Please disclose if your mod was created primarily 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 SkaldSaga v0.1.1
BepInEx/plugins/SkaldSaga/SkaldSaga.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = "")] [assembly: AssemblyCompany("NomadicWar")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Skald — A Valheim event broadcaster. Boss kills, deaths, biome discoveries, joins and departures announced in real-time. Install on server and clients.")] [assembly: AssemblyFileVersion("0.1.0.0")] [assembly: AssemblyInformationalVersion("0.1.0")] [assembly: AssemblyProduct("Skald")] [assembly: AssemblyTitle("Skald")] [assembly: AssemblyVersion("0.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SkaldSaga { [BepInPlugin("com.nomadicwar.skaldsaga", "SkaldSaga", "0.1.1")] public class Plugin : BaseUnityPlugin { private struct BossKillRecord { public string prefab; public Vector3 position; public float time; } public const string PluginGUID = "com.nomadicwar.skaldsaga"; public const string PluginName = "SkaldSaga"; public const string PluginVersion = "0.1.1"; internal static ManualLogSource Log; private readonly Harmony _harmony = new Harmony("com.nomadicwar.skaldsaga"); public static ConfigEntry<bool> EnablePlayerDeath; public static ConfigEntry<bool> EnableBossKill; public static ConfigEntry<bool> EnablePlayerJoin; public static ConfigEntry<bool> EnablePlayerLeave; public static ConfigEntry<bool> EnableBiomeDiscovery; public static ConfigEntry<bool> EnableKillMilestone; public static ConfigEntry<bool> EnableDeathMilestone; public static ConfigEntry<bool> EnableGearTier; public static ConfigEntry<bool> EnableTitleEarned; public static ConfigEntry<bool> ShowDayNumber; public static ConfigEntry<bool> ShowOnlineCount; public static ConfigEntry<string> MessagePrefix; public static ConfigEntry<bool> EnableChronicleLog; public static ConfigEntry<bool> LogServerStartStop; public static ConfigEntry<bool> ShowInChat; public static ConfigEntry<bool> ShowOnHud; public static ConfigEntry<KeyCode> PanelToggleKey; public static ConfigEntry<KeyCode> TitleMenuKey; public static ConfigEntry<KeyCode> StatsMenuKey; public static ConfigEntry<float> HudScale; public static ConfigEntry<float> BossCreditRadius; private static ConfigFile _configFile = null; private static string _configFilePath = null; private static DateTime _lastConfigWrite = DateTime.MinValue; private const float CONFIG_CHECK_INTERVAL = 5f; private static float _configCheckTimer = 0f; public static readonly Queue<string> PendingMessages = new Queue<string>(); private static readonly List<BossKillRecord> _recentBossKills = new List<BossKillRecord>(); private const float BOSS_KILL_TTL = 10f; private const float BOSS_KILL_POS_TOL = 20f; private static readonly HashSet<string> _processedConfirmations = new HashSet<string>(); private void Awake() { Log = ((BaseUnityPlugin)this).Logger; EnablePlayerDeath = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnablePlayerDeath", true, "Broadcast when a player dies"); EnableBossKill = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnableBossKill", true, "Broadcast when a boss is killed"); EnablePlayerJoin = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnablePlayerJoin", true, "Broadcast when a player joins"); EnablePlayerLeave = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnablePlayerLeave", true, "Broadcast when a player leaves"); EnableBiomeDiscovery = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnableBiomeDiscovery", true, "Broadcast when a player discovers a new biome"); EnableKillMilestone = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnableKillMilestone", true, "Broadcast enemy kill milestones"); EnableDeathMilestone = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnableDeathMilestone", true, "Broadcast death milestones"); EnableGearTier = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnableGearTier", true, "Broadcast new gear tier"); EnableTitleEarned = ((BaseUnityPlugin)this).Config.Bind<bool>("Events", "EnableTitleEarned", true, "Broadcast when a title is earned"); ShowDayNumber = ((BaseUnityPlugin)this).Config.Bind<bool>("Format", "ShowDayNumber", true, "Append day number to messages"); ShowOnlineCount = ((BaseUnityPlugin)this).Config.Bind<bool>("Format", "ShowOnlineCount", true, "Append online count to messages"); MessagePrefix = ((BaseUnityPlugin)this).Config.Bind<string>("Format", "MessagePrefix", "⚔ ", "Prefix for all Skald messages"); EnableChronicleLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "EnableChronicleLog", true, "Write SkaldSaga_Chronicle.log"); LogServerStartStop = ((BaseUnityPlugin)this).Config.Bind<bool>("Log", "LogServerStartStop", true, "Log server start/stop"); ShowInChat = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowInChat", false, "Show messages in chat"); ShowOnHud = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowOnHud", true, "Show messages on HUD"); PanelToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Display", "PanelToggleKey", (KeyCode)92, "Toggle event panel"); TitleMenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Display", "TitleMenuKey", (KeyCode)93, "Toggle title menu"); StatsMenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Display", "StatsMenuKey", (KeyCode)91, "Toggle stats panel"); HudScale = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "HudScale", 1f, "HUD scale (0.5-2.0)"); BossCreditRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Bosses", "BossCreditRadius", 100f, "Radius in meters in which players receive boss kill credit."); Log.LogInfo((object)"SkaldSaga 0.1.1 awakens."); try { _harmony.PatchAll(); Log.LogInfo((object)"[SkaldSaga] All patches applied."); } catch (Exception ex) { Log.LogError((object)("[SkaldSaga] PatchAll failed: " + ex.Message)); } InitConfigReload(((BaseUnityPlugin)this).Config); } private void OnDestroy() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && LogServerStartStop.Value) { Chronicle.Write("SERVER", "shutdown", "Server shutting down.", null); } Chronicle.Close(); _harmony.UnpatchSelf(); } public static bool IsServer() { return (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer(); } public static void InitConfigReload(ConfigFile cfg) { _configFile = cfg; _configFilePath = cfg.ConfigFilePath; if (File.Exists(_configFilePath)) { _lastConfigWrite = File.GetLastWriteTimeUtc(_configFilePath); } } public static void TickConfigReload(float dt) { if (_configFile == null || _configFilePath == null) { return; } _configCheckTimer += dt; if (_configCheckTimer < 5f) { return; } _configCheckTimer = 0f; try { if (!File.Exists(_configFilePath)) { return; } DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(_configFilePath); if (!(lastWriteTimeUtc <= _lastConfigWrite)) { _lastConfigWrite = lastWriteTimeUtc; _configFile.Reload(); Log.LogInfo((object)"[SkaldSaga] Config reloaded from disk."); if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, "SkaldSaga config reloaded.", 0, (Sprite)null, false); } } } catch (Exception ex) { Log.LogWarning((object)("[SkaldSaga] Config reload failed: " + ex.Message)); } } public static void Broadcast(string message, string eventType = "event", string player = null, string detail = null) { try { string text = MessagePrefix.Value ?? "⚔ "; int num = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetDay() : 0); int num2 = (((Object)(object)ZNet.instance != (Object)null) ? (ZNet.instance.GetConnectedPeers().Count + 1) : 0); string text2 = ((ShowDayNumber.Value && num > 0) ? $" [Day {num}]" : ""); string text3 = ((ShowOnlineCount.Value && num2 > 0) ? $" ({num2} online)" : ""); string text4 = text + message + text2 + text3; if ((Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { try { ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "SkaldBroadcast", new object[1] { text4 }); } catch { } } } Log.LogInfo((object)("[SkaldSaga] " + text4)); Chronicle.Write(player ?? "world", eventType, message, new Dictionary<string, string> { { "day", num.ToString() }, { "online", num2.ToString() }, { "detail", detail ?? "" } }); } catch (Exception ex) { Log.LogWarning((object)("[SkaldSaga] Broadcast failed: " + ex.Message)); } } public static void OnSkaldEvent(long senderID, string eventType, string playerName, string message, string detail) { try { if (1 == 0) { } bool flag = eventType switch { "player_death" => EnablePlayerDeath.Value, "boss_kill" => EnableBossKill.Value, "biome_discovery" => EnableBiomeDiscovery.Value, "kill_milestone" => EnableKillMilestone.Value, "death_milestone" => EnableDeathMilestone.Value, "gear_tier" => EnableGearTier.Value, "title_earned" => EnableTitleEarned.Value, _ => true, }; if (1 == 0) { } if (flag) { Broadcast(message, eventType, playerName, detail); } } catch (Exception ex) { Log.LogWarning((object)("[SkaldSaga] OnSkaldEvent error: " + ex.Message)); } } public static void SendEvent(string eventType, string playerName, string message, string detail = "") { try { if (ZRoutedRpc.instance != null) { ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetServerPeer() : null); if (val != null) { ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, "SkaldEvent", new object[4] { eventType, playerName, message, detail }); Log.LogInfo((object)("[SkaldSaga] Sent " + eventType + ": " + message)); } } } catch (Exception ex) { Log.LogWarning((object)("[SkaldSaga] SendEvent failed: " + ex.Message)); } } public static void OnSkaldBroadcast(long senderID, string message) { try { Log.LogInfo((object)("[SkaldSaga] Broadcast received: " + message)); PendingMessages.Enqueue(message); SkaldPanel.AddEvent(message); FlushMessages(); } catch (Exception ex) { Log.LogWarning((object)("[SkaldSaga] OnSkaldBroadcast failed: " + ex.Message)); } } public static void FlushMessages() { bool flag = (Object)(object)Chat.instance != (Object)null; bool flag2 = (Object)(object)MessageHud.instance != (Object)null; if (!flag && !flag2) { return; } while (PendingMessages.Count > 0) { string text = PendingMessages.Dequeue(); if (flag && ShowInChat.Value) { ((Terminal)Chat.instance).AddString("SkaldSaga", text, (Type)2, false); } if (flag2 && ShowOnHud.Value) { MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false); } } } public static void OnSkaldBossDetected(long senderID, string prefab, Vector3 position, string bossDisplayName) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) try { if (!IsServer()) { return; } float now = Time.realtimeSinceStartup; _recentBossKills.RemoveAll((BossKillRecord r) => now - r.time > 10f); int num = Mathf.RoundToInt(position.x / 10f); int num2 = Mathf.RoundToInt(position.y / 10f); int num3 = Mathf.RoundToInt(position.z / 10f); foreach (BossKillRecord recentBossKill in _recentBossKills) { int num4 = Mathf.RoundToInt(recentBossKill.position.x / 10f); int num5 = Mathf.RoundToInt(recentBossKill.position.y / 10f); int num6 = Mathf.RoundToInt(recentBossKill.position.z / 10f); if (recentBossKill.prefab == prefab && num4 == num && num5 == num2 && num6 == num3) { Log.LogInfo((object)("[SkaldSaga] BossDetected duplicate suppressed: " + prefab)); return; } } _recentBossKills.Add(new BossKillRecord { prefab = prefab, position = position, time = now }); Log.LogInfo((object)("[SkaldSaga] BossDetected confirmed: " + prefab + " (" + bossDisplayName + ") — broadcasting")); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { try { ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "SkaldBossConfirmed", new object[3] { prefab, position, bossDisplayName }); } catch { } } OnSkaldBossConfirmed(0L, prefab, position, bossDisplayName); } catch (Exception ex) { Log.LogWarning((object)("[SkaldSaga] OnSkaldBossDetected error: " + ex.Message)); } } public static void OnSkaldBossConfirmed(long senderID, string prefab, Vector3 position, string bossDisplayName) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !SkaldData.IsLoaded) { return; } string text = $"{prefab}:{Mathf.RoundToInt(position.x / 10f)}:{Mathf.RoundToInt(position.y / 10f)}:{Mathf.RoundToInt(position.z / 10f)}"; if (_processedConfirmations.Contains(text)) { Log.LogInfo((object)("[SkaldSaga] BossConfirmed already processed: " + text)); return; } _processedConfirmations.Add(text); string text2 = (string.IsNullOrEmpty(bossDisplayName) ? TitleSystem.GetBossDisplayName(prefab) : bossDisplayName); float num = Mathf.Clamp(BossCreditRadius.Value, 5f, 500f); foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer == (Object)null || Vector3.Distance(((Component)allPlayer).transform.position, position) > num) { continue; } string playerName = allPlayer.GetPlayerName(); TitleSystem.OnBossKill(playerName, prefab); Log.LogInfo((object)("[SkaldSaga] BossConfirmed credit: " + playerName + " for " + prefab + " (" + text2 + ")")); if ((Object)(object)allPlayer == (Object)(object)localPlayer) { string displayName = TitleSystem.GetDisplayName(playerName); SendEvent("boss_kill", playerName, displayName + " helped defeat " + text2 + "!", prefab); if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, text2 + " defeated! Title credit awarded.", 0, (Sprite)null, false); } } } } catch (Exception ex) { Log.LogWarning((object)("[SkaldSaga] OnSkaldBossConfirmed error: " + ex.Message)); } } public static string GetDeathCause(Player player) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) try { object? obj = AccessTools.Field(typeof(Character), "m_lastHit")?.GetValue(player); HitData val = (HitData)((obj is HitData) ? obj : null); if (val == null) { return ((Character)player).IsSwimming() ? "drowning" : "a fall"; } if (val.m_attacker != ZDOID.None) { ZDOMan instance = ZDOMan.instance; ZDO val2 = ((instance != null) ? instance.GetZDO(val.m_attacker) : null); if (val2 != null) { ZNetScene instance2 = ZNetScene.instance; GameObject val3 = ((instance2 != null) ? instance2.GetPrefab(val2.GetPrefab()) : null); if ((Object)(object)val3 != (Object)null) { Character component = val3.GetComponent<Character>(); if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.m_name)) { string text = Localization.instance.Localize(component.m_name); return component.IsBoss() ? text : ("a " + text); } return ((Object)val3).name.Replace("(Clone)", "").Trim(); } } } DamageTypes damage = val.m_damage; if (damage.m_fire > 0f) { return "burning"; } if (damage.m_frost > 0f) { return "freezing"; } if (damage.m_poison > 0f) { return "poison"; } if (damage.m_spirit > 0f) { return "spirit damage"; } if (damage.m_blunt > 0f || damage.m_slash > 0f || damage.m_pierce > 0f) { return ((Character)player).IsSwimming() ? "drowning" : "combat"; } return ((Character)player).IsSwimming() ? "drowning" : "a fall"; } catch { return "unknown causes"; } } } [HarmonyPatch(typeof(ZNet), "Awake")] public static class Patch_ZNetAwake { private static void Postfix() { try { if (ZNet.instance.IsServer()) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.Register<string, string, string, string>("SkaldEvent", (Method<string, string, string, string>)Plugin.OnSkaldEvent); } ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.Register<string, Vector3, string>("SkaldBossDetected", (Action<long, string, Vector3, string>)Plugin.OnSkaldBossDetected); } Chronicle.Init(); if (Plugin.LogServerStartStop.Value) { Chronicle.Write("SERVER", "startup", "Skald is listening.", null); } Plugin.Log.LogInfo((object)"[SkaldSaga] Server RPC registered."); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] ZNet.Awake error: " + ex.Message)); } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class Patch_PlayerJoin { private static void Postfix(ZRpc rpc) { try { if (Plugin.IsServer() && Plugin.EnablePlayerJoin.Value) { ZNet instance = ZNet.instance; string text = ((instance == null) ? null : instance.GetPeers()?.Find((ZNetPeer p) => p.m_rpc == rpc))?.m_playerName; if (string.IsNullOrEmpty(text)) { text = "A Viking"; } Plugin.Broadcast(text + " has entered the world.", "player_join", text); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] PlayerJoin error: " + ex.Message)); } } } [HarmonyPatch(typeof(ZNet), "RPC_Disconnect")] public static class Patch_PlayerLeave { [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class TrackJoin { private static void Postfix(ZRpc rpc) { if (!Plugin.IsServer()) { return; } ZNet instance = ZNet.instance; ZNetPeer val = ((instance == null) ? null : instance.GetPeers()?.Find((ZNetPeer p) => p.m_rpc == rpc)); if (val != null) { JoinTimes[val.m_uid] = DateTime.UtcNow; if (!string.IsNullOrEmpty(val.m_playerName)) { JoinNames[val.m_uid] = val.m_playerName; } } } } private static readonly Dictionary<long, DateTime> JoinTimes = new Dictionary<long, DateTime>(); private static readonly Dictionary<long, string> JoinNames = new Dictionary<long, string>(); internal static void FireLeave(ZRpc rpc) { try { if (Plugin.IsServer() && Plugin.EnablePlayerLeave.Value) { ZNet instance = ZNet.instance; ZNetPeer val = ((instance == null) ? null : instance.GetPeers()?.Find((ZNetPeer p) => p.m_rpc == rpc)); long num = val?.m_uid ?? 0; string value = val?.m_playerName; if (string.IsNullOrEmpty(value) && num != 0) { JoinNames.TryGetValue(num, out value); } if (string.IsNullOrEmpty(value)) { value = "A Viking"; } string text = ""; if (num != 0L && JoinTimes.TryGetValue(num, out var value2)) { TimeSpan timeSpan = DateTime.UtcNow - value2; text = ((timeSpan.TotalHours >= 1.0) ? $" (played {(int)timeSpan.TotalHours}h {timeSpan.Minutes}m)" : $" (played {timeSpan.Minutes}m)"); JoinTimes.Remove(num); JoinNames.Remove(num); } Plugin.Broadcast(value + " has left the world" + text + ".", "player_leave", value, text.Trim()); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] PlayerLeave error: " + ex.Message)); } } private static void Prefix(ZRpc rpc) { FireLeave(rpc); } } [HarmonyPatch(typeof(ZNet), "SendDisconnect", new Type[] { typeof(ZNetPeer) })] public static class Patch_PlayerLeave_Send { private static void Prefix(ZNetPeer peer) { try { if (Plugin.IsServer() && peer?.m_rpc != null) { Patch_PlayerLeave.FireLeave(peer.m_rpc); } } catch { } } } [HarmonyPatch(typeof(Game), "Start")] public static class Patch_GameStart { private static void Postfix() { try { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.Register<string>("SkaldBroadcast", (Action<long, string>)Plugin.OnSkaldBroadcast); } ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.Register<string, Vector3, string>("SkaldBossConfirmed", (Action<long, string, Vector3, string>)Plugin.OnSkaldBossConfirmed); } Plugin.Log.LogInfo((object)"[SkaldSaga] Client RPC registered."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] Game.Start error: " + ex.Message)); } } } [HarmonyPatch(typeof(Chat), "Awake")] public static class Patch_ChatAwake { private static void Postfix() { Plugin.FlushMessages(); } } [HarmonyPatch(typeof(MessageHud), "Awake")] public static class Patch_MessageHudAwake { private static void Postfix() { Plugin.FlushMessages(); } } [HarmonyPatch(typeof(Player), "OnSpawned")] public static class Patch_DataLoad { private static void Postfix(Player __instance) { if (((Character)__instance).IsOwner()) { SkaldData.Load(__instance); } } } [HarmonyPatch(typeof(Player), "OnDestroy")] public static class Patch_DataSave { private static void Prefix(Player __instance) { if (((Character)__instance).IsOwner()) { SkaldData.Save(); } } } [HarmonyPatch(typeof(Player), "Update")] public static class Patch_DataTick { private static void Postfix(Player __instance) { if (((Character)__instance).IsOwner()) { SkaldData.Tick(Time.deltaTime); Plugin.TickConfigReload(Time.deltaTime); } } } [HarmonyPatch(typeof(ZNet), "SendDisconnect", new Type[] { typeof(ZNetPeer) })] public static class Patch_SaveOnLogout { private static void Prefix() { SkaldData.Save(); } } [HarmonyPatch(typeof(Player), "CreateTombStone")] public static class Patch_PlayerDeath { private static void Postfix(Player __instance) { try { if (((Character)__instance).IsOwner()) { string playerName = __instance.GetPlayerName(); string deathCause = Plugin.GetDeathCause(__instance); string activeTitle = TitleSystem.GetActiveTitle(); string text = (string.IsNullOrEmpty(activeTitle) ? playerName : (playerName + " the " + activeTitle)); string message = ((deathCause == "a fall") ? (text + " fell to their death.") : (text + " has fallen to " + deathCause + ".")); MilestoneTracker.OnDeath(playerName); Plugin.SendEvent("player_death", playerName, message, deathCause); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] PlayerDeath error: " + ex.Message)); } } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class Patch_CreatureKill { private static readonly HashSet<int> _processedBosses = new HashSet<int>(); private static void Prefix(Character __instance) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) try { if (__instance is Player) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !SkaldData.IsLoaded) { return; } string text = ((Object)((Component)__instance).gameObject).name.Replace("(Clone)", "").Trim(); string playerName = localPlayer.GetPlayerName(); if (!TitleSystem.BossTitles.ContainsKey(text)) { MilestoneTracker.OnKill(playerName); TitleSystem.OnCreatureKill(playerName, text); return; } int instanceID = ((Object)__instance).GetInstanceID(); if (_processedBosses.Contains(instanceID)) { return; } _processedBosses.Add(instanceID); Vector3 position = ((Component)__instance).transform.position; string text2 = Localization.instance.Localize(__instance.m_name); if (string.IsNullOrEmpty(text2)) { text2 = TitleSystem.GetBossDisplayName(text); } Plugin.Log.LogInfo((object)$"[SkaldSaga] BossDetected: {text} ({text2}) at {position}"); ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetServerPeer() : null); if (val != null) { ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC(val.m_uid, "SkaldBossDetected", new object[3] { text, position, text2 }); } } else { Plugin.OnSkaldBossDetected(0L, text, position, text2); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] CreatureKill error: " + ex.Message)); } } } [HarmonyPatch(typeof(MessageHud), "ShowBiomeFoundMsg")] public static class Patch_BiomeDiscovery { private static string _lastBiome = ""; private static float _lastBiomeTime = -999f; private static void Prefix(string text, bool playStinger) { try { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { string playerName = localPlayer.GetPlayerName(); string text2 = "the " + Localization.instance.Localize(text); float realtimeSinceStartup = Time.realtimeSinceStartup; if (!(text2 == _lastBiome) || !(realtimeSinceStartup - _lastBiomeTime < 30f)) { _lastBiome = text2; _lastBiomeTime = realtimeSinceStartup; string displayName = TitleSystem.GetDisplayName(playerName); Plugin.SendEvent("biome_discovery", playerName, displayName + " has discovered " + text2 + " for the first time.", text2); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] BiomeDiscovery error: " + ex.Message)); } } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] public static class Patch_GearTier { private static readonly Dictionary<string, (int tier, string label)> TierMap = new Dictionary<string, (int, string)> { { "ArmorLeatherChest", (1, "Leather") }, { "ArmorLeatherLegs", (1, "Leather") }, { "HelmetLeather", (1, "Leather") }, { "AxeFlint", (1, "Flint") }, { "KnifeFlint", (1, "Flint") }, { "SpearFlint", (1, "Flint") }, { "HelmetBronze", (2, "Bronze") }, { "ArmorBronzeChest", (2, "Bronze") }, { "ArmorBronzeLegs", (2, "Bronze") }, { "AtgeirBronze", (2, "Bronze") }, { "AxeBronze", (2, "Bronze") }, { "SwordBronze", (2, "Bronze") }, { "SpearBronze", (2, "Bronze") }, { "MaceBronze", (2, "Bronze") }, { "ShieldBronze", (2, "Bronze") }, { "ShieldBronzeBuckler", (2, "Bronze") }, { "HelmetIron", (3, "Iron") }, { "ArmorIronChest", (3, "Iron") }, { "ArmorIronLegs", (3, "Iron") }, { "SwordIron", (3, "Iron") }, { "AtgeirIron", (3, "Iron") }, { "AxeIron", (3, "Iron") }, { "MaceIron", (3, "Iron") }, { "SledgeIron", (3, "Iron") }, { "ShieldIron", (3, "Iron") }, { "ShieldIronSquare", (3, "Iron") }, { "ShieldIronTower", (3, "Iron") }, { "SpearElderbark", (3, "Iron") }, { "SwordSilver", (4, "Silver") }, { "Frostner", (4, "Silver") }, { "KnifeSilver", (4, "Silver") }, { "ShieldSilver", (4, "Silver") }, { "ArmorWolfChest", (4, "Wolf") }, { "ArmorWolfLegs", (4, "Wolf") }, { "HelmetDrake", (4, "Wolf") }, { "CapeWolf", (4, "Wolf") }, { "ArmorWolf", (4, "Wolf") }, { "SwordBlackmetal", (5, "Blackmetal") }, { "AtgeirBlackmetal", (5, "Blackmetal") }, { "AxeBlackmetal", (5, "Blackmetal") }, { "KnifeBlackmetal", (5, "Blackmetal") }, { "ShieldBlackmetal", (5, "Blackmetal") }, { "ShieldBlackmetalTower", (5, "Blackmetal") }, { "SwordBlackMetal", (5, "Blackmetal") }, { "AtgeirBlackMetal", (5, "Blackmetal") }, { "AxeBlackMetal", (5, "Blackmetal") }, { "KnifeBlackMetal", (5, "Blackmetal") }, { "ShieldBlackMetal", (5, "Blackmetal") }, { "ShieldBlackMetalTower", (5, "Blackmetal") }, { "ArmorPaddedCuirass", (5, "Padded") }, { "ArmorPaddedGreaves", (5, "Padded") }, { "HelmetPadded", (5, "Padded") }, { "ArmorCarapaceChest", (6, "Carapace") }, { "ArmorCarapaceLegs", (6, "Carapace") }, { "HelmetCarapace", (6, "Carapace") }, { "SwordCarapace", (6, "Carapace") }, { "ShieldCarapace", (6, "Carapace") }, { "SwordFlametal", (7, "Flametal") }, { "AtgeirFlametal", (7, "Flametal") }, { "AxeFlametal", (7, "Flametal") }, { "ArmorFlametalChest", (7, "Flametal") }, { "ArmorFlametalLegs", (7, "Flametal") }, { "HelmetFlametal", (7, "Flametal") } }; private static void Postfix(Humanoid __instance, ItemData item, bool __result) { try { if (!__result) { return; } Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val == null || !((Character)val).IsOwner() || item?.m_shared == null || !SkaldData.IsLoaded) { return; } GameObject dropPrefab = item.m_dropPrefab; string text = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? item.m_shared.m_name?.Replace("$item_", "").Replace("$", ""); if (!string.IsNullOrEmpty(text) && TierMap.TryGetValue(text, out (int, string) value)) { int @int = SkaldData.GetInt("gear_tier"); if (value.Item1 > @int) { SkaldData.SetInt("gear_tier", value.Item1); string playerName = val.GetPlayerName(); string displayName = TitleSystem.GetDisplayName(playerName); Plugin.SendEvent("gear_tier", playerName, displayName + " has equipped " + value.Item2 + " gear for the first time!", value.Item2); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] GearTier error: " + ex.Message)); } } } public static class SkaldData { private static Dictionary<string, string> _data = new Dictionary<string, string>(); private static bool _dirty = false; private static string _charID = null; private static float _saveTimer = 0f; private const float SAVE_INTERVAL = 60f; private static string StableDir => Path.Combine(Paths.ConfigPath, "SkaldSaga"); public static bool IsLoaded => _charID != null; private static string GetPath(string charID) { return Path.Combine(StableDir, "skaldsaga_" + charID + ".json"); } private static void TryMigrate(string charID) { string path = GetPath(charID); if (File.Exists(path)) { return; } string[] array = new string[2] { Path.Combine(Paths.PluginPath, "SkaldSaga"), Path.Combine(Paths.PluginPath, "Skald") }; string[] array2 = new string[2] { "skaldsaga_", "skald_" }; string[] array3 = array; foreach (string path2 in array3) { string[] array4 = array2; foreach (string text in array4) { string text2 = Path.Combine(path2, text + charID + ".json"); if (File.Exists(text2)) { try { File.Copy(text2, path, overwrite: false); Plugin.Log.LogInfo((object)("[SkaldSaga] Migrated save: " + text2 + " -> " + path)); return; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] Migration failed: " + ex.Message)); return; } } } } } public static void Load(Player player) { try { string text = player.GetPlayerID().ToString(); if (_charID == text && IsLoaded) { return; } _charID = text; Directory.CreateDirectory(StableDir); TryMigrate(text); _data = new Dictionary<string, string>(); string path = GetPath(text); if (File.Exists(path)) { string text2 = File.ReadAllText(path).Trim().TrimStart(new char[1] { '{' }) .TrimEnd(new char[1] { '}' }); string[] array = text2.Split(new char[1] { ',' }); foreach (string text3 in array) { string[] array2 = text3.Trim().Split(new char[1] { ':' }, 2); if (array2.Length == 2) { _data[array2[0].Trim().Trim(new char[1] { '"' })] = array2[1].Trim().Trim(new char[1] { '"' }); } } } _dirty = false; Plugin.Log.LogInfo((object)$"[SkaldSaga] Data loaded for {player.GetPlayerName()}: {_data.Count} entries from {path}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] Load failed: " + ex.Message)); } } public static void Save() { if (!_dirty || _charID == null) { return; } try { StringBuilder stringBuilder = new StringBuilder("{"); bool flag = true; foreach (KeyValuePair<string, string> datum in _data) { if (!flag) { stringBuilder.Append(","); } stringBuilder.Append("\"" + datum.Key + "\":\"" + datum.Value + "\""); flag = false; } stringBuilder.Append("}"); File.WriteAllText(GetPath(_charID), stringBuilder.ToString()); _dirty = false; Plugin.Log.LogInfo((object)"[SkaldSaga] Data saved."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] Save failed: " + ex.Message)); } } public static void Tick(float dt) { if (_dirty) { _saveTimer += dt; if (_saveTimer >= 60f) { _saveTimer = 0f; Save(); } } } public static int GetInt(string key) { if (_data.TryGetValue(key, out var value) && int.TryParse(value, out var result)) { return result; } return 0; } public static void SetInt(string key, int val) { _data[key] = val.ToString(); _dirty = true; } public static string GetString(string key) { _data.TryGetValue(key, out var value); return value ?? ""; } public static void SetString(string key, string val) { _data[key] = val ?? ""; _dirty = true; } public static HashSet<string> GetSet(string key) { if (_data.TryGetValue(key, out var value) && !string.IsNullOrEmpty(value)) { return new HashSet<string>(value.Split(new char[1] { ',' })); } return new HashSet<string>(); } public static void SetSet(string key, HashSet<string> set) { _data[key] = string.Join(",", set); _dirty = true; } } public static class MilestoneTracker { private static readonly int[] KillMilestones = new int[7] { 1, 10, 100, 500, 1000, 5000, 10000 }; private static readonly int[] DeathMilestones = new int[8] { 1, 5, 10, 25, 50, 100, 500, 1000 }; public static void OnKill(string playerName) { if (!Plugin.EnableKillMilestone.Value || !SkaldData.IsLoaded) { return; } int num = SkaldData.GetInt("kills") + 1; SkaldData.SetInt("kills", num); string displayName = TitleSystem.GetDisplayName(playerName); int[] killMilestones = KillMilestones; for (int i = 0; i < killMilestones.Length; i++) { int num2 = killMilestones[i]; if (num == num2) { Plugin.SendEvent("kill_milestone", playerName, $"{displayName} has slain {num2} enemies!", num2.ToString()); break; } } } public static void OnDeath(string playerName) { if (!Plugin.EnableDeathMilestone.Value || !SkaldData.IsLoaded) { return; } int num = SkaldData.GetInt("deaths") + 1; SkaldData.SetInt("deaths", num); string displayName = TitleSystem.GetDisplayName(playerName); int[] deathMilestones = DeathMilestones; for (int i = 0; i < deathMilestones.Length; i++) { int num2 = deathMilestones[i]; if (num == num2) { string message = ((num2 == 1) ? (displayName + " has died for the first time. Welcome to Valheim.") : $"{displayName} has died {num2} times. The saga continues."); Plugin.SendEvent("death_milestone", playerName, message, num2.ToString()); break; } } } } public static class TitleSystem { public static readonly Dictionary<string, (int kills, string title)[]> CreatureTitles = new Dictionary<string, (int, string)[]> { { "Boar", new(int, string)[3] { (100, "Boar Hunter"), (500, "Boar Slayer"), (1000, "Swine Reaper") } }, { "Deer", new(int, string)[3] { (100, "Deer Stalker"), (500, "The Huntsman"), (1000, "Ghost of the Meadows") } }, { "Neck", new(int, string)[3] { (100, "Neck Breaker"), (500, "Swamp Wader"), (1000, "Neck's Nightmare") } }, { "Greyling", new(int, string)[3] { (100, "Greyling Crusher"), (500, "Ash Maker"), (1000, "Ember of Yggdrasil") } }, { "Greydwarf", new(int, string)[3] { (100, "Greydwarf Hunter"), (500, "Greydwarf Bane"), (1000, "Terror of the Forest") } }, { "Troll", new(int, string)[3] { (100, "Troll Fighter"), (500, "Troll Breaker"), (1000, "Giant Slayer") } }, { "Draugr", new(int, string)[3] { (100, "Draugr Slayer"), (500, "Death Walker"), (1000, "Bane of the Fallen") } }, { "Skeleton", new(int, string)[3] { (100, "Bone Breaker"), (500, "The Undying"), (1000, "Lord of Ash") } }, { "Blob", new(int, string)[3] { (100, "Blob Stomper"), (500, "Ooze Wader"), (1000, "Plague Walker") } }, { "Wolf", new(int, string)[3] { (100, "Wolf Hunter"), (500, "Pack Breaker"), (1000, "Alpha of Alphas") } }, { "Drake", new(int, string)[3] { (100, "Drake Hunter"), (500, "Dragonslayer"), (1000, "Wyrm's Bane") } }, { "StoneGolem", new(int, string)[3] { (100, "Stone Breaker"), (500, "Mountain Crusher"), (1000, "Wrath of the Peak") } }, { "GoblinBrute", new(int, string)[3] { (100, "Fuling Fighter"), (500, "Plains Reaper"), (1000, "Goblin King's Dread") } }, { "Goblin", new(int, string)[3] { (100, "Fuling Fighter"), (500, "Plains Reaper"), (1000, "Goblin King's Dread") } }, { "Deathsquito", new(int, string)[3] { (100, "Bug Swatter"), (500, "Needle Dancer"), (1000, "Lord of Needles") } }, { "Lox", new(int, string)[3] { (100, "Lox Tamer"), (500, "Lox Breaker"), (1000, "Plains Titan") } }, { "Leech", new(int, string)[3] { (100, "Leech Stomper"), (500, "Bloodless Wader"), (1000, "The Undrained") } }, { "Wraith", new(int, string)[3] { (100, "Wraith Breaker"), (500, "Ghost Render"), (1000, "Banisher of Souls") } }, { "Abomination", new(int, string)[3] { (100, "Root Render"), (500, "Swamp Titan"), (1000, "The Unmade") } }, { "Fenring", new(int, string)[3] { (100, "Fenring Slayer"), (500, "Moon Breaker"), (1000, "The Unwolfed") } }, { "Bat", new(int, string)[3] { (100, "Bat Swatter"), (500, "Cave Clearer"), (1000, "Darkness Render") } }, { "Cultist", new(int, string)[3] { (100, "Cultist Slayer"), (500, "Heretic Breaker"), (1000, "The Unconverted") } }, { "Serpent", new(int, string)[3] { (100, "Serpent Hunter"), (500, "Sea Slayer"), (1000, "Leviathan's Bane") } }, { "Seeker", new(int, string)[3] { (100, "Seeker Slayer"), (500, "Mistwalker"), (1000, "Bug Crusher") } }, { "SeekerBrute", new(int, string)[3] { (100, "Brute Breaker"), (500, "Carapace Crusher"), (1000, "Titan of the Mist") } }, { "SeekerSoldier", new(int, string)[3] { (100, "Soldier Slayer"), (500, "Legion Breaker"), (1000, "Mist Commander") } }, { "Tick", new(int, string)[3] { (100, "Tick Flicker"), (500, "Bloodless"), (1000, "The Untapped") } }, { "Gjall", new(int, string)[3] { (100, "Gjall Hunter"), (500, "Sky Render"), (1000, "The Unscreamed") } }, { "Dvergr", new(int, string)[3] { (100, "Dvergr Duelist"), (500, "Rune Breaker"), (1000, "Slayer of Ancients") } }, { "Charred", new(int, string)[3] { (100, "Ash Fighter"), (500, "Ember Breaker"), (1000, "Lord of Cinders") } }, { "Morgen", new(int, string)[3] { (100, "Morgen Slayer"), (500, "Ashen Reaper"), (1000, "Wrath of the Ashlands") } }, { "Volture", new(int, string)[3] { (100, "Volture Hunter"), (500, "Sky Scourge"), (1000, "Wings of Ash") } }, { "FallenValkyrie", new(int, string)[3] { (100, "Valkyrie Breaker"), (500, "Heaven's Bane"), (1000, "The Unchosen") } }, { "Ulv", new(int, string)[3] { (100, "Ulv Hunter"), (500, "Pack Render"), (1000, "Alpha of the North") } }, { "Hare", new(int, string)[3] { (100, "Hare Hunter"), (500, "Swift Slayer"), (1000, "The Unbounding") } } }; private static readonly Dictionary<string, string> CreatureKeyMap = new Dictionary<string, string> { { "Boar", "Boar" }, { "Boar_piggy", "Boar" }, { "Deer", "Deer" }, { "Neck", "Neck" }, { "Greyling", "Greyling" }, { "Greydwarf", "Greydwarf" }, { "Greydwarf_elite", "Greydwarf" }, { "Greydwarf_shaman", "Greydwarf" }, { "Troll", "Troll" }, { "Draugr", "Draugr" }, { "Draugr_Elite", "Draugr" }, { "Skeleton", "Skeleton" }, { "Skeleton_NoArcher", "Skeleton" }, { "Blob", "Blob" }, { "BlobElite", "Blob" }, { "Wolf", "Wolf" }, { "Wolf_cub", "Wolf" }, { "Drake", "Drake" }, { "Hatchling", "Drake" }, { "DrakeHatchling", "Drake" }, { "StoneGolem", "StoneGolem" }, { "Goblin", "Goblin" }, { "GoblinArcher", "Goblin" }, { "GoblinShaman", "Goblin" }, { "GoblinBrute", "GoblinBrute" }, { "Deathsquito", "Deathsquito" }, { "Lox", "Lox" }, { "Leech", "Leech" }, { "Wraith", "Wraith" }, { "Abomination", "Abomination" }, { "Fenring", "Fenring" }, { "Fenring_CultistStar", "Fenring" }, { "Bat", "Bat" }, { "Cultist", "Cultist" }, { "CultistStar", "Cultist" }, { "Serpent", "Serpent" }, { "Seeker", "Seeker" }, { "SeekerStar", "Seeker" }, { "SeekerBrute", "SeekerBrute" }, { "SeekerSoldier", "SeekerSoldier" }, { "Tick", "Tick" }, { "Gjall", "Gjall" }, { "Dvergr", "Dvergr" }, { "DvergrMage", "Dvergr" }, { "DvergrRogue", "Dvergr" }, { "Charred", "Charred" }, { "CharredMage", "Charred" }, { "CharredArcher", "Charred" }, { "CharredTwitcher", "Charred" }, { "Morgen", "Morgen" }, { "MorgenStar", "Morgen" }, { "Volture", "Volture" }, { "FallenValkyrie", "FallenValkyrie" }, { "Ulv", "Ulv" }, { "Hare", "Hare" } }; public static readonly Dictionary<string, string> BossTitles = new Dictionary<string, string> { { "Eikthyr", "Stag Breaker" }, { "gd_king", "Voice of the Ancient" }, { "Bonemass", "Bane of the Swamp" }, { "Dragon", "Veil of the Frost" }, { "GoblinKing", "Ender of Giants" }, { "SeekerQueen", "Hive Ender" }, { "Fader", "The Ashen" } }; private static readonly int[] BossKillMilestones = new int[3] { 3, 10, 25 }; private static readonly string[] BossKillTitles = new string[3] { "Boss Hunter", "World Breaker", "God Ender" }; public static readonly Dictionary<string, string> BossDisplayNames = new Dictionary<string, string> { { "Eikthyr", "Eikthyr" }, { "gd_king", "The Elder" }, { "Bonemass", "Bonemass" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "The Queen" }, { "Fader", "Fader" } }; public static string GetActiveTitle() { return SkaldData.GetString("title"); } public static void SetActiveTitle(string t) { SkaldData.SetString("title", t); } public static List<string> GetEarnedTitles() { return new List<string>(SkaldData.GetSet("earned")); } public static string GetDisplayName(string playerName) { string activeTitle = GetActiveTitle(); return string.IsNullOrEmpty(activeTitle) ? playerName : (playerName + " the " + activeTitle); } private static void EarnTitle(string playerName, string title) { if (string.IsNullOrEmpty(title) || !SkaldData.IsLoaded) { return; } HashSet<string> set = SkaldData.GetSet("earned"); if (!set.Contains(title)) { set.Add(title); SkaldData.SetSet("earned", set); if (string.IsNullOrEmpty(GetActiveTitle())) { SetActiveTitle(title); } string displayName = GetDisplayName(playerName); Plugin.SendEvent("title_earned", playerName, displayName + " has earned the title: " + title + "!", title); } } public static void OnCreatureKill(string playerName, string prefab) { if (!SkaldData.IsLoaded) { return; } string value = null; if (!CreatureKeyMap.TryGetValue(prefab, out value)) { foreach (KeyValuePair<string, string> item in CreatureKeyMap) { if (prefab.StartsWith(item.Key, StringComparison.OrdinalIgnoreCase)) { value = item.Value; Plugin.Log.LogInfo((object)("[Skald AutoMap] " + prefab + " -> " + value + " (startsWith)")); break; } } if (value == null) { foreach (KeyValuePair<string, string> item2 in CreatureKeyMap) { if (prefab.IndexOf(item2.Key, StringComparison.OrdinalIgnoreCase) >= 0) { value = item2.Value; Plugin.Log.LogInfo((object)("[Skald AutoMap] " + prefab + " -> " + value + " (substring)")); break; } } } if (value == null) { value = prefab; Plugin.Log.LogInfo((object)("[Skald AutoMap] " + prefab + " -> NEW CATEGORY (" + value + ")")); } } string key = "c_" + value; int num = SkaldData.GetInt(key) + 1; SkaldData.SetInt(key, num); if (!CreatureTitles.TryGetValue(value, out (int, string)[] value2)) { return; } (int, string)[] array = value2; for (int i = 0; i < array.Length; i++) { var (num2, title) = array[i]; if (num == num2) { EarnTitle(playerName, title); break; } } } public static string GetBossDisplayName(string prefab) { string value; return BossDisplayNames.TryGetValue(prefab, out value) ? value : prefab; } public static void OnBossKill(string playerName, string prefab) { if (!SkaldData.IsLoaded) { return; } if (BossTitles.TryGetValue(prefab, out var value)) { EarnTitle(playerName, value); } HashSet<string> set = SkaldData.GetSet("earned"); bool flag = true; foreach (string value2 in BossTitles.Values) { if (!set.Contains(value2)) { flag = false; break; } } if (flag) { EarnTitle(playerName, "Slayer of Gods"); } int num = SkaldData.GetInt("boss_kills") + 1; SkaldData.SetInt("boss_kills", num); for (int i = 0; i < BossKillMilestones.Length; i++) { if (num == BossKillMilestones[i]) { EarnTitle(playerName, BossKillTitles[i]); } } } } [HarmonyPatch(typeof(InventoryGui), "IsVisible")] public static class Patch_CursorUnlock { private static void Postfix(ref bool __result) { if ((Object)(object)Player.m_localPlayer != (Object)null && (TitleMenu.IsVisible || StatsPanel.IsVisible)) { __result = true; } } } [HarmonyPatch(typeof(Hud), "Awake")] public static class Patch_HudAwake { private static void Postfix(Hud __instance) { try { SkaldPanel.Build(((Component)__instance).transform); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] SkaldPanel.Build failed: " + ex.Message)); } try { TitleMenu.Build(((Component)__instance).transform); Plugin.Log.LogInfo((object)"[SkaldSaga] TitleMenu built."); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[SkaldSaga] TitleMenu.Build failed: " + ex2.Message)); } try { StatsPanel.Build(((Component)__instance).transform); Plugin.Log.LogInfo((object)"[SkaldSaga] StatsPanel built."); } catch (Exception ex3) { Plugin.Log.LogWarning((object)("[SkaldSaga] StatsPanel.Build failed: " + ex3.Message)); } } } [HarmonyPatch(typeof(Hud), "Update")] public static class Patch_HudUpdate { private static void Postfix() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) try { SkaldPanel.ApplyScale(); if (Input.GetKeyDown(Plugin.PanelToggleKey.Value)) { SkaldPanel.Toggle(); } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { if (Input.GetKeyDown(Plugin.TitleMenuKey.Value)) { TitleMenu.Toggle(localPlayer); } if (Input.GetKeyDown(Plugin.StatsMenuKey.Value)) { StatsPanel.Toggle(localPlayer); } } if (Input.GetKeyDown((KeyCode)27)) { if (TitleMenu.IsVisible) { TitleMenu.Hide(); } if (StatsPanel.IsVisible) { StatsPanel.Hide(); } } } catch { } } } public static class SkaldPanel { private static GameObject _root; private static GameObject _panel; private static Text[] _lines; private static bool _visible = false; private const int MaxLines = 5; private static readonly Queue<string> _events = new Queue<string>(); public static void AddEvent(string msg) { _events.Enqueue(msg); while (_events.Count > 5) { _events.Dequeue(); } Refresh(); } public static void Toggle() { _visible = !_visible; if ((Object)(object)_panel != (Object)null) { _panel.SetActive(_visible); } } public static void Build(Transform hudRoot) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0056: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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_00b7: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Expected O, but got Unknown //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01de: 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_023b: 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_028b: Expected O, but got Unknown //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("SkaldPanel"); _root.transform.SetParent(hudRoot, false); RectTransform val = _root.AddComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(1f, 1f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(1f, 1f); val.anchoredPosition = new Vector2(-210f, -180f); val.sizeDelta = new Vector2(340f, 200f); _panel = new GameObject("BG"); _panel.transform.SetParent(_root.transform, false); RectTransform val3 = _panel.AddComponent<RectTransform>(); val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val2 = (val3.offsetMin = (val3.offsetMax = Vector2.zero)); ((Graphic)_panel.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.55f); _lines = (Text[])(object)new Text[5]; for (int i = 0; i < 5; i++) { GameObject val5 = new GameObject($"L{i}"); val5.transform.SetParent(_panel.transform, false); RectTransform val6 = val5.AddComponent<RectTransform>(); val6.anchorMin = new Vector2(0f, 1f); val6.anchorMax = new Vector2(1f, 1f); val6.pivot = new Vector2(0f, 1f); val6.anchoredPosition = new Vector2(6f, -6f - (float)i * 36f); val6.sizeDelta = new Vector2(-12f, 34f); Text val7 = val5.AddComponent<Text>(); val7.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); val7.fontSize = 13; ((Graphic)val7).color = new Color(0.9f, 0.85f, 0.7f, 1f); val7.alignment = (TextAnchor)0; val7.horizontalOverflow = (HorizontalWrapMode)0; val7.verticalOverflow = (VerticalWrapMode)1; _lines[i] = val7; } GameObject val8 = new GameObject("DH"); val8.transform.SetParent(_root.transform, false); RectTransform val9 = val8.AddComponent<RectTransform>(); val9.anchorMin = Vector2.zero; val9.anchorMax = Vector2.one; val2 = (val9.offsetMin = (val9.offsetMax = Vector2.zero)); ((Graphic)val8.AddComponent<Image>()).color = Color.clear; val8.AddComponent<SkaldDragHandler>(); _panel.SetActive(_visible); ApplyScale(); Plugin.Log.LogInfo((object)"[SkaldSaga] Panel built."); } } public static void ApplyScale() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root == (Object)null)) { float num = Mathf.Clamp(Plugin.HudScale.Value, 0.5f, 2f); _root.transform.localScale = new Vector3(num, num, 1f); } } private static void Refresh() { if (_lines == null) { return; } string[] array = _events.ToArray(); for (int i = 0; i < 5; i++) { if (!((Object)(object)_lines[i] == (Object)null)) { int num = array.Length - 1 - i; _lines[i].text = ((num >= 0) ? array[num] : ""); } } } } public class SkaldDragHandler : MonoBehaviour, IDragHandler, IEventSystemHandler, IBeginDragHandler { private RectTransform _rt; private Vector2 _offset; private void Awake() { _rt = ((Component)((Component)this).transform.parent).GetComponent<RectTransform>(); } public void OnBeginDrag(PointerEventData e) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) RectTransformUtility.ScreenPointToLocalPointInRectangle(_rt, e.position, e.pressEventCamera, ref _offset); } public void OnDrag(PointerEventData e) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Vector2 val = default(Vector2); if (!((Object)(object)_rt == (Object)null) && RectTransformUtility.ScreenPointToLocalPointInRectangle((RectTransform)/*isinst with value type is only supported in some contexts*/, e.position, e.pressEventCamera, ref val)) { ((Transform)_rt).localPosition = Vector2.op_Implicit(val - _offset); } } } public static class StatsPanel { private static GameObject _root; private static bool _visible; public static bool IsVisible => _visible; public static void Toggle(Player p) { if (!((Object)(object)_root == (Object)null)) { _visible = !_visible; if (_visible) { Refresh(p); } _root.SetActive(_visible); } } public static void Hide() { _visible = false; if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } } public static void Build(Transform hudRoot) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0056: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009d: 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_00b3: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("SkaldStats"); _root.transform.SetParent(hudRoot, false); RectTransform val = _root.AddComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(0.5f, 0.5f); val.anchoredPosition = new Vector2(220f, 0f); val.sizeDelta = new Vector2(340f, 480f); GameObject val3 = new GameObject("BG"); val3.transform.SetParent(_root.transform, false); RectTransform val4 = val3.AddComponent<RectTransform>(); val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; val2 = (val4.offsetMin = (val4.offsetMax = Vector2.zero)); ((Graphic)val3.AddComponent<Image>()).color = new Color(0.05f, 0.05f, 0.05f, 0.92f); GameObject val6 = new GameObject("Content"); val6.transform.SetParent(_root.transform, false); RectTransform val7 = val6.AddComponent<RectTransform>(); val7.anchorMin = Vector2.zero; val7.anchorMax = Vector2.one; val7.offsetMin = new Vector2(10f, 10f); val7.offsetMax = new Vector2(-10f, -10f); _root.AddComponent<SkaldRef>().ContentRoot = val6.transform; Canvas val8 = _root.AddComponent<Canvas>(); val8.renderMode = (RenderMode)0; val8.overrideSorting = true; val8.sortingOrder = 200; _root.AddComponent<GraphicRaycaster>(); GameObject val9 = new GameObject("DH"); val9.transform.SetParent(_root.transform, false); RectTransform val10 = val9.AddComponent<RectTransform>(); val10.anchorMin = new Vector2(0f, 1f); val10.anchorMax = new Vector2(1f, 1f); val10.pivot = new Vector2(0.5f, 1f); val10.sizeDelta = new Vector2(0f, 40f); ((Graphic)val9.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.01f); val9.AddComponent<SkaldDragHandler>(); _root.SetActive(false); } } public static void Refresh(Player player) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Expected O, but got Unknown //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Unknown result type (might be due to invalid IL or missing references) GameObject root = _root; SkaldRef skaldRef = ((root != null) ? root.GetComponent<SkaldRef>() : null); if ((Object)(object)skaldRef?.ContentRoot == (Object)null) { return; } foreach (Transform item in skaldRef.ContentRoot) { Transform val = item; Object.Destroy((Object)(object)((Component)val).gameObject); } List<string> list = new List<string> { "--- Skald Stats ---", "", string.Format("Total Kills: {0}", SkaldData.GetInt("kills")), string.Format("Deaths: {0}", SkaldData.GetInt("deaths")), "", "--- Creature Kills ---" }; List<(string, int)> list2 = new List<(string, int)>(); foreach (string key in TitleSystem.CreatureTitles.Keys) { int @int = SkaldData.GetInt("c_" + key); if (@int > 0) { list2.Add((key, @int)); } } list2.Sort(((string name, int count) a, (string name, int count) b) => b.count.CompareTo(a.count)); foreach (var (arg, num) in list2) { list.Add($" {arg}: {num}"); } list.Add(""); list.Add("--- Titles ---"); string activeTitle = TitleSystem.GetActiveTitle(); foreach (string earnedTitle in TitleSystem.GetEarnedTitles()) { list.Add((earnedTitle == activeTitle) ? (" * " + earnedTitle) : (" " + earnedTitle)); } float num2 = -8f; foreach (string item2 in list) { GameObject val2 = new GameObject("L"); val2.transform.SetParent(skaldRef.ContentRoot, false); RectTransform val3 = val2.AddComponent<RectTransform>(); val3.anchorMin = new Vector2(0f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(0.5f, 1f); val3.anchoredPosition = new Vector2(0f, num2); bool flag = string.IsNullOrEmpty(item2); val3.sizeDelta = new Vector2(0f, flag ? 8f : 22f); num2 -= (flag ? 8f : 24f); if (!flag) { Text val4 = val2.AddComponent<Text>(); val4.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); val4.fontSize = (item2.StartsWith("---") ? 14 : 13); val4.fontStyle = (FontStyle)(item2.StartsWith("---") ? 1 : 0); ((Graphic)val4).color = (item2.StartsWith(" *") ? new Color(0.95f, 0.85f, 0.4f, 1f) : (item2.StartsWith("---") ? new Color(0.9f, 0.8f, 0.4f, 1f) : new Color(0.85f, 0.85f, 0.85f, 1f))); val4.alignment = (TextAnchor)3; val4.text = item2; } } } } public static class TitleMenu { private static GameObject _root; private static bool _visible = false; private static List<GameObject> _buttons = new List<GameObject>(); public static bool IsVisible => _visible; public static void Toggle(Player p) { if (!((Object)(object)_root == (Object)null)) { _visible = !_visible; if (_visible) { Refresh(p); } _root.SetActive(_visible); } } public static void Hide() { _visible = false; if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } } public static void Build(Transform hudRoot) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0056: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: 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_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Expected O, but got Unknown //IL_0240: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_027b: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Expected O, but got Unknown //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_root != (Object)null)) { _root = new GameObject("SkaldTitleMenu"); _root.transform.SetParent(hudRoot, false); RectTransform val = _root.AddComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(0.5f, 0.5f); val.sizeDelta = new Vector2(380f, 480f); GameObject val3 = new GameObject("BG"); val3.transform.SetParent(_root.transform, false); RectTransform val4 = val3.AddComponent<RectTransform>(); val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; val2 = (val4.offsetMin = (val4.offsetMax = Vector2.zero)); ((Graphic)val3.AddComponent<Image>()).color = new Color(0.05f, 0.05f, 0.05f, 0.92f); GameObject val6 = new GameObject("Header"); val6.transform.SetParent(_root.transform, false); RectTransform val7 = val6.AddComponent<RectTransform>(); val7.anchorMin = new Vector2(0f, 1f); val7.anchorMax = new Vector2(1f, 1f); val7.pivot = new Vector2(0.5f, 1f); val7.anchoredPosition = new Vector2(0f, -10f); val7.sizeDelta = new Vector2(0f, 40f); Text val8 = val6.AddComponent<Text>(); val8.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); val8.fontSize = 18; val8.fontStyle = (FontStyle)1; ((Graphic)val8).color = new Color(0.9f, 0.8f, 0.4f, 1f); val8.alignment = (TextAnchor)4; val8.text = "— Choose Your Title —"; GameObject val9 = new GameObject("Content"); val9.transform.SetParent(_root.transform, false); RectTransform val10 = val9.AddComponent<RectTransform>(); val10.anchorMin = Vector2.zero; val10.anchorMax = Vector2.one; val10.offsetMin = new Vector2(10f, 10f); val10.offsetMax = new Vector2(-10f, -60f); _root.AddComponent<SkaldRef>().ContentRoot = val9.transform; Canvas val11 = _root.AddComponent<Canvas>(); val11.renderMode = (RenderMode)0; val11.overrideSorting = true; val11.sortingOrder = 200; _root.AddComponent<GraphicRaycaster>(); GameObject val12 = new GameObject("DH"); val12.transform.SetParent(_root.transform, false); RectTransform val13 = val12.AddComponent<RectTransform>(); val13.anchorMin = new Vector2(0f, 1f); val13.anchorMax = new Vector2(1f, 1f); val13.pivot = new Vector2(0.5f, 1f); val13.sizeDelta = new Vector2(0f, 40f); ((Graphic)val12.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.01f); val12.AddComponent<SkaldDragHandler>(); _root.SetActive(false); } } public static void Refresh(Player player) { GameObject root = _root; SkaldRef skaldRef = ((root != null) ? root.GetComponent<SkaldRef>() : null); if ((Object)(object)skaldRef?.ContentRoot == (Object)null) { return; } foreach (GameObject button in _buttons) { if ((Object)(object)button != (Object)null) { Object.Destroy((Object)(object)button); } } _buttons.Clear(); List<string> earnedTitles = TitleSystem.GetEarnedTitles(); string activeTitle = TitleSystem.GetActiveTitle(); List<string> list = ((earnedTitles.Count == 0) ? new List<string> { "(No titles earned yet)" } : new List<string> { "(No Title)" }); if (earnedTitles.Count > 0) { list.AddRange(earnedTitles); } float yPos = -8f; foreach (string item in list) { bool isActive = (item == "(No Title)" && activeTitle == "") || item == activeTitle; bool flag = item != "(No titles earned yet)"; AddButton(skaldRef.ContentRoot, item, isActive, flag ? player : null, ref yPos); } Canvas.ForceUpdateCanvases(); } private static void AddButton(Transform content, string title, bool isActive, Player player, ref float yPos) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0199: 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_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown GameObject val = new GameObject("B_" + title); val.transform.SetParent(content, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(0f, 1f); val2.anchorMax = new Vector2(1f, 1f); val2.pivot = new Vector2(0.5f, 1f); val2.anchoredPosition = new Vector2(0f, yPos); val2.sizeDelta = new Vector2(0f, 40f); yPos -= 46f; ((Graphic)val.AddComponent<Image>()).color = (isActive ? new Color(0.3f, 0.25f, 0.05f, 0.9f) : new Color(0.15f, 0.15f, 0.15f, 0.8f)); GameObject val3 = new GameObject("L"); val3.transform.SetParent(val.transform, false); RectTransform val4 = val3.AddComponent<RectTransform>(); val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; val4.offsetMin = new Vector2(12f, 0f); val4.offsetMax = Vector2.zero; Text val5 = val3.AddComponent<Text>(); val5.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); val5.fontSize = 15; ((Graphic)val5).color = (isActive ? new Color(0.95f, 0.85f, 0.4f, 1f) : new Color(0.85f, 0.85f, 0.85f, 1f)); val5.alignment = (TextAnchor)3; val5.text = (isActive ? ("* " + title) : title); if ((Object)(object)player != (Object)null) { Button val6 = val.AddComponent<Button>(); string ct = ((title == "(No Title)") ? "" : title); Player cp = player; ((UnityEvent)val6.onClick).AddListener((UnityAction)delegate { TitleSystem.SetActiveTitle(ct); Refresh(cp); }); } _buttons.Add(val); } } public static class Chronicle { private static StreamWriter _writer; private static readonly object _lock = new object(); private static string _logPath; public static void Init() { try { string text = Path.Combine(Paths.PluginPath, "SkaldSaga"); Directory.CreateDirectory(text); string text2 = DateTime.UtcNow.ToString("yyyy-MM-dd"); _logPath = Path.Combine(text, "SkaldSaga_Chronicle_" + text2 + ".log"); _writer = new StreamWriter(_logPath, append: true) { AutoFlush = true }; if (new FileInfo(_logPath).Length == 0) { _writer.WriteLine("TIMESTAMP_UTC\t\t\tDAY\tONLINE\tEVENT\t\t\tPLAYER\t\t\tMESSAGE\t\t\t\t\tDETAIL"); } Plugin.Log.LogInfo((object)("[SkaldSaga] Chronicle: " + _logPath)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] Chronicle init failed: " + ex.Message)); } } public static void Write(string player, string eventType, string message, Dictionary<string, string> data) { if (Plugin.EnableChronicleLog != null && !Plugin.EnableChronicleLog.Value) { return; } lock (_lock) { try { string value = DateTime.UtcNow.ToString("yyyy-MM-dd"); if (_logPath != null && !_logPath.Contains(value)) { _writer?.Close(); Init(); } string text = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"); string text2 = ((data != null && data.ContainsKey("day")) ? data["day"] : "-"); string text3 = ((data != null && data.ContainsKey("online")) ? data["online"] : "-"); string text4 = ((data != null && data.ContainsKey("detail")) ? data["detail"] : ""); _writer?.WriteLine($"{text}\t{text2}\t{text3}\t{eventType,-16}\t{player,-20}\t{message,-40}\t{text4}"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[SkaldSaga] Chronicle write failed: " + ex.Message)); } } } public static void Close() { lock (_lock) { try { _writer?.Close(); } catch { } } } } public class SkaldRef : MonoBehaviour { public Transform ContentRoot; } }