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 Travs Valheim Server NPCs and Marketplace v1.3.2
plugins/TravsServerNPCs/TravsServerNPCs.dll
Decompiled a day 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 System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Splatform; using TravsServerNPCs.Client; using TravsServerNPCs.Commands; using TravsServerNPCs.Net; using TravsServerNPCs.Npc; using TravsServerNPCs.Server; using TravsServerNPCs.Server.Services; using TravsServerNPCs.Shared; using TravsServerNPCs.UI; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.UI; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_guiutils")] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("TravsServerNPCs")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.3.2.0")] [assembly: AssemblyInformationalVersion("1.3.2+068d7908c6715039c3d257402de4657d1eaac834")] [assembly: AssemblyProduct("TravsServerNPCs")] [assembly: AssemblyTitle("TravsServerNPCs")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.3.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TravsServerNPCs { internal static class Log { private static ManualLogSource _log; public static void Init(ManualLogSource log) { _log = log; } public static void Info(string msg) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)msg); } } public static void Warn(string msg) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)msg); } } public static void Error(string msg) { ManualLogSource log = _log; if (log != null) { log.LogError((object)msg); } } public static void Debug(string msg) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)msg); } } } [BepInPlugin("trav.travsservernpcs", "Trav's Valheim Server NPCs and Marketplace Mod", "1.3.2")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { public const string Guid = "trav.travsservernpcs"; public const string Name = "Trav's Valheim Server NPCs and Marketplace Mod"; public const string Version = "1.3.2"; public const string FolderName = "TravsServerNPCs"; private Harmony _harmony; public static Plugin Instance { get; private set; } private void Awake() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown Instance = this; Log.Init(((BaseUnityPlugin)this).Logger); NetChannel.Register(); NpcPrefabs.Register(); CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new TsnCommand()); _harmony = new Harmony("trav.travsservernpcs"); _harmony.PatchAll(typeof(Plugin).Assembly); Log.Info("Trav's Valheim Server NPCs and Marketplace Mod 1.3.2 loaded"); } private void Update() { ServerHost.Tick(); ClientState.Tick(); } private void OnDestroy() { ServerHost.Shutdown(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace TravsServerNPCs.UI { internal static class ChroniclerView { private static WritStateView _state; public static void OnOpen() { _state = null; ClientState.Send("writ.state", null, delegate(Response r) { if (r.State != null) { _state = ((JToken)r.State).ToObject<WritStateView>(); } if (!string.IsNullOrEmpty(r.Message)) { ClientState.Notify(r.Message); } NpcPanel.Refresh(); }); } public static string[] Tabs() { return new string[2] { "Boss Writs", "Chronicle" }; } public static void Rows(int tab, List<Row> rows, Action<int, Action<int, bool>> pay) { if (_state == null) { rows.Add(new Row { Label = "Consulting the chronicle..." }); } else if (tab == 0) { Writs(rows, pay); } else { Chronicle(rows); } } private static void Writs(List<Row> rows, Action<int, Action<int, bool>> pay) { float num = ClientState.Config?.Settings.Writs.DaysAfterFirstKill ?? 3f; rows.Add(new Row { Header = true, Label = $"Missed a boss night? A writ counts the boss as defeated for your character, {num:0.#} days after the world first beat it." }); if (!_state.YnwInstalled) { rows.Add(new Row { Label = " (You Are Not Worthy isn't running, so keys are recorded but don't gate items.)" }); } foreach (BossView boss2 in _state.Bosses) { BossView boss = boss2; bool flag = (Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).HaveUniqueKey(boss2.Key); bool flag2 = boss2.HaveKey || flag; string text = boss2.Price + " coins" + ((boss2.ItemAmount > 0 && !string.IsNullOrEmpty(boss2.Item)) ? $" + {boss2.ItemAmount}x {InventoryHelper.DisplayName(boss2.Item)}" : ""); bool flag3 = !flag2 && string.IsNullOrEmpty(boss2.Blocked); rows.Add(new Row { Icon = (string.IsNullOrEmpty(boss2.Item) ? null : InventoryHelper.Icon(boss2.Item)), Label = (flag2 ? (boss2.Name + " ✓ defeated") : (boss2.Name + " — " + text + (flag3 ? "" : (" (" + boss2.Blocked + ")")))), ButtonText = (flag3 ? "Buy writ" : null), OnClick = (flag3 ? ((Action<int>)delegate { Buy(boss, pay); }) : null) }); } } private static void Buy(BossView boss, Action<int, Action<int, bool>> pay) { List<ItemRecord> given = new List<ItemRecord>(); if (boss.ItemAmount > 0 && !string.IsNullOrEmpty(boss.Item)) { if (InventoryHelper.Count(boss.Item) < boss.ItemAmount) { ClientState.Notify($"You also need {boss.ItemAmount}x {InventoryHelper.DisplayName(boss.Item)}."); return; } int num = InventoryHelper.Take(boss.Item, boss.ItemAmount); if (num > 0) { given.Add(ItemRecord.Plain(boss.Item, num)); } } pay(boss.Price, delegate(int paid, bool bank) { ClientState.Send("writ.buy", new WritBuyRequest { Key = boss.Key, PaidCoins = paid, FromBank = bank, Given = given }, delegate(Response r) { ClientState.Notify(r.Message); OnOpen(); }); }); } private static void Chronicle(List<Row> rows) { rows.Add(new Row { Header = true, Label = "This world's boss history, and where you stand." }); foreach (BossView boss in _state.Bosses) { bool flag = (Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).HaveUniqueKey(boss.Key); string text = ((boss.HaveKey || flag) ? "✓ you" : "✗ you"); string text2 = (boss.FirstKillUtc.HasValue ? ($"first defeated {boss.FirstKillUtc.Value.ToLocalTime():d MMM yyyy}" + ((boss.FirstPlayers.Count > 0) ? (" by " + string.Join(", ", boss.FirstPlayers.Take(4))) : "")) : "not yet defeated on this world"); rows.Add(new Row { Label = boss.Name + " " + text + " · " + text2 }); } } } internal static class ConfigEditor { private const float Width = 900f; private const float Height = 720f; private static GameObject _window; private static NpcProfile _profile; private static List<TradeEntry> _stock; private static List<Destination> _dests; private static readonly List<Dictionary<string, InputField>> Rows = new List<Dictionary<string, InputField>>(); private static int _closedFrame = -1; public static bool IsOpen => (Object)(object)_window != (Object)null; public static bool ClosedThisFrame => _closedFrame == Time.frameCount; public static void Open(NpcProfile profile) { if (!((Object)(object)GUIManager.CustomGUIFront == (Object)null) && profile != null) { NpcProfile npcProfile = ClientState.Config?.GetProfile(profile.Id); if (npcProfile == null) { ClientState.Notify("Profile '" + profile.Id + "' isn't a config file on the server, so there's nothing to edit."); return; } NpcPanel.Close(); NpcEditor.Close(); Close(); _profile = npcProfile; _stock = npcProfile.Stock.Select(Copy).ToList(); _dests = npcProfile.Destinations.Select(Copy).ToList(); GUIManager.BlockInput(true); Build(); } } public static void Close() { if (!((Object)(object)_window == (Object)null)) { Object.Destroy((Object)(object)_window); _window = null; Rows.Clear(); _closedFrame = Time.frameCount; GUIManager.BlockInput(false); } } public static void Tick() { if (IsOpen && ((Object)(object)Player.m_localPlayer == (Object)null || ZInput.GetKeyDown((KeyCode)27, true))) { Close(); } } private static void Build() { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_window != (Object)null) { Object.Destroy((Object)(object)_window); } Rows.Clear(); _window = PanelBuilder.Window(900f, 720f); ((Object)_window).name = "TSN_ConfigEditor"; Transform transform = _window.transform; bool flag = _profile.Role == "trader"; PanelBuilder.Label(transform, "Edit " + (flag ? "stock" : "destinations") + ": " + _profile.Id, 20f, 18f, 780f, 40f, 26, GUIManager.Instance.ValheimOrange, (TextAnchor)4); PanelBuilder.Button(transform, "X", 830f, 18f, 46f, 38f, Close); float contentWidth; RectTransform content = PanelBuilder.ScrollList(transform, 20f, 66f, 860f, 584f, out contentWidth); if (flag) { PanelBuilder.AddRow(content, contentWidth, new Row { Header = true, Label = "item | amount | sell price | buy price | stock (-1 = unlimited) | restock h | requires key | daily limit" }); for (int i = 0; i < _stock.Count; i++) { TradeEntry tradeEntry = _stock[i]; int index = i; Dictionary<string, InputField> item = FieldRow(content, contentWidth, new string[8] { tradeEntry.Item, tradeEntry.Amount.ToString(), tradeEntry.Sell.ToString(), tradeEntry.Buy.ToString(), tradeEntry.Stock.ToString(), F(tradeEntry.RestockHours), tradeEntry.Requires, tradeEntry.DailyLimit.ToString() }, delegate { Collect(); _stock.RemoveAt(index); Build(); }); Rows.Add(item); } PanelBuilder.AddRow(content, contentWidth, new Row { Label = "Add a stock line", ButtonText = "Add", OnClick = delegate { Collect(); _stock.Add(new TradeEntry { Item = "Wood", Amount = 10, Sell = 10 }); Build(); } }); } else { PanelBuilder.AddRow(content, contentWidth, new Row { Header = true, Label = "name | x | y | z | cost | requires key | cooldown s | allow all items (true/false)" }); for (int num = 0; num < _dests.Count; num++) { Destination destination = _dests[num]; int index2 = num; Dictionary<string, InputField> item2 = FieldRow(content, contentWidth, new string[8] { destination.Name, F(destination.X), F(destination.Y), F(destination.Z), destination.Cost.ToString(), destination.Requires, F(destination.CooldownSeconds), destination.AllowAllItems ? "true" : "false" }, delegate { Collect(); _dests.RemoveAt(index2); Build(); }); Rows.Add(item2); } PanelBuilder.AddRow(content, contentWidth, new Row { Label = "Add a destination at my position", ButtonText = "Add here", OnClick = delegate { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) Collect(); Vector3 position = ((Component)Player.m_localPlayer).transform.position; _dests.Add(new Destination { Name = "New place", X = Mathf.Round(position.x), Y = Mathf.Round(position.y), Z = Mathf.Round(position.z) }); Build(); } }); } PanelBuilder.Button(transform, "Save to server", 20f, 658f, 200f, 40f, Save); PanelBuilder.Button(transform, "Cancel", 230f, 658f, 120f, 40f, Close); } private static Dictionary<string, InputField> FieldRow(RectTransform content, float width, string[] values, Action remove) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0064: 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_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Row", new Type[3] { typeof(RectTransform), typeof(LayoutElement), typeof(Image) }); val.transform.SetParent((Transform)(object)content, false); val.GetComponent<RectTransform>().sizeDelta = new Vector2(width - 8f, 40f); LayoutElement component = val.GetComponent<LayoutElement>(); float minHeight = (component.preferredHeight = 40f); component.minHeight = minHeight; ((Graphic)val.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.25f); Dictionary<string, InputField> dictionary = new Dictionary<string, InputField>(); float num2 = (width - 8f - 90f) / (float)values.Length; for (int i = 0; i < values.Length; i++) { float num3 = ((i == 0) ? (num2 * 1.6f) : (num2 * ((float)values.Length - 1.6f) / (float)(values.Length - 1))); float num4 = ((i == 0) ? 4f : (4f + num2 * 1.6f + (float)(i - 1) * num3)); InputField component2 = GUIManager.Instance.CreateInputField(val.transform, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(num4 + num3 / 2f, 0f), (ContentType)0, "", 13, num3 - 4f, 30f).GetComponent<InputField>(); component2.text = values[i] ?? ""; dictionary[i.ToString()] = component2; } ((UnityEvent)GUIManager.Instance.CreateButton("Remove", val.transform, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-46f, 0f), 80f, 30f).GetComponent<Button>().onClick).AddListener((UnityAction)delegate { remove(); }); return dictionary; } private static string V(Dictionary<string, InputField> r, int i) { if (!r.TryGetValue(i.ToString(), out var value)) { return ""; } return (value.text ?? "").Trim(); } private static float N(Dictionary<string, InputField> r, int i, float d) { if (!float.TryParse(V(r, i), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return d; } return result; } private static string F(float v) { return v.ToString("0.##", CultureInfo.InvariantCulture); } private static void Collect() { if (_profile.Role == "trader") { for (int i = 0; i < Rows.Count && i < _stock.Count; i++) { Dictionary<string, InputField> r = Rows[i]; _stock[i] = new TradeEntry { Item = V(r, 0), Amount = (int)N(r, 1, 1f), Sell = (int)N(r, 2, 0f), Buy = (int)N(r, 3, 0f), Stock = (int)N(r, 4, -1f), RestockHours = N(r, 5, 24f), Requires = V(r, 6), DailyLimit = (int)N(r, 7, 0f) }; } } else { for (int j = 0; j < Rows.Count && j < _dests.Count; j++) { Dictionary<string, InputField> r2 = Rows[j]; _dests[j] = new Destination { Name = V(r2, 0), X = N(r2, 1, 0f), Y = N(r2, 2, 0f), Z = N(r2, 3, 0f), Cost = (int)N(r2, 4, 0f), Requires = V(r2, 5), CooldownSeconds = N(r2, 6, 0f), AllowAllItems = V(r2, 7).Equals("true", StringComparison.OrdinalIgnoreCase) }; } } } private static void Save() { Collect(); if (_stock.Where((TradeEntry e) => string.IsNullOrEmpty(e.Item) || e.Amount < 1).ToList().Count > 0) { ClientState.Notify("Every stock line needs an item and an amount of 1 or more."); return; } if (_dests.Any((Destination d) => string.IsNullOrEmpty(d.Name))) { ClientState.Notify("Every destination needs a name."); return; } ClientState.Send("admin.saveProfile", new AdminSaveProfileRequest { ProfileId = _profile.Id, Stock = _stock, Destinations = _dests }, delegate(Response r) { ClientState.Notify(r.Message); if (r.Ok) { Close(); } }); } private static TradeEntry Copy(TradeEntry e) { return new TradeEntry { Item = e.Item, Amount = e.Amount, Sell = e.Sell, Buy = e.Buy, Stock = e.Stock, RestockHours = e.RestockHours, Requires = e.Requires, DailyLimit = e.DailyLimit }; } private static Destination Copy(Destination d) { return new Destination { Name = d.Name, X = d.X, Y = d.Y, Z = d.Z, Cost = d.Cost, Requires = d.Requires, CooldownSeconds = d.CooldownSeconds, AllowAllItems = d.AllowAllItems }; } } internal static class DialoguePanel { private const float Width = 720f; private const float Height = 520f; private const float MaxDistance = 8f; private static GameObject _window; private static NpcComponent _npc; private static NpcProfile _profile; private static string _nodeId; private static int _closedFrame = -1; public static bool IsOpen => (Object)(object)_window != (Object)null; public static bool ClosedThisFrame => _closedFrame == Time.frameCount; public static void Open(NpcComponent npc, NpcProfile profile) { if (!((Object)(object)GUIManager.CustomGUIFront == (Object)null) && profile?.Dialogue != null) { NpcPanel.Close(); Close(); _npc = npc; _profile = profile; GUIManager.BlockInput(true); Show(profile.Dialogue.Start); } } public static void Close() { if (!((Object)(object)_window == (Object)null)) { Object.Destroy((Object)(object)_window); _window = null; _closedFrame = Time.frameCount; GUIManager.BlockInput(false); } } public static void OpenShop() { NpcComponent npc = _npc; NpcProfile npcProfile = (((Object)(object)npc != (Object)null) ? npc.Profile : null); Close(); if (npcProfile != null && npcProfile.Role != "decoration") { NpcPanel.Open(npc, npcProfile); } } public static void Tick() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (Object)(object)_npc == (Object)null || Vector3.Distance(((Component)localPlayer).transform.position, ((Component)_npc).transform.position) > 8f || ZInput.GetKeyDown((KeyCode)27, true)) { Close(); } } } private static void Show(string nodeId) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) DialogueNode dialogueNode = _profile.Dialogue.Node(nodeId); if (dialogueNode == null) { Close(); return; } _nodeId = nodeId; if ((Object)(object)_window != (Object)null) { Object.Destroy((Object)(object)_window); } _window = PanelBuilder.Window(720f, 520f); ((Object)_window).name = "TSN_Dialogue"; Transform transform = _window.transform; PanelBuilder.Label(transform, ((Object)(object)_npc != (Object)null) ? _npc.DisplayName : _profile.Name, 20f, 18f, 600f, 40f, 26, GUIManager.Instance.ValheimOrange, (TextAnchor)4); PanelBuilder.Button(transform, "X", 650f, 18f, 46f, 38f, Close); PanelBuilder.Label(transform, dialogueNode.Text, 30f, 64f, 660f, 150f, 18, GUIManager.Instance.ValheimBeige, (TextAnchor)0); float contentWidth; RectTransform content = PanelBuilder.ScrollList(transform, 20f, 224f, 680f, 276f, out contentWidth); ConditionContext c = QuestClient.Conditions(); int num = 0; for (int i = 0; i < dialogueNode.Choices.Count; i++) { DialogueChoice choice = dialogueNode.Choices[i]; if (Conditions.Check(choice.Requires, c)) { int index = i; num++; PanelBuilder.AddRow(content, contentWidth, new Row { Label = $"{num}. {choice.Text}", ButtonText = "Say", OnClick = delegate { Pick(index, choice); } }); } } if (num == 0) { PanelBuilder.AddRow(content, contentWidth, new Row { Label = "...", ButtonText = "Leave", OnClick = delegate { Close(); } }); } } private static void Pick(int index, DialogueChoice choice) { if (!choice.Actions.Any((Dictionary<string, object> action) => !ActionRunnerNames.IsClientOnly(Arg.Name(action)))) { foreach (Dictionary<string, object> action in choice.Actions) { if (Arg.Name(action).Equals("openShop", StringComparison.OrdinalIgnoreCase)) { OpenShop(); return; } } Advance(choice.Next); return; } List<ItemRecord> list = new List<ItemRecord>(); foreach (Dictionary<string, object> a in choice.Actions) { if (Arg.Name(a).Equals("completeQuest", StringComparison.OrdinalIgnoreCase)) { ActiveQuestView activeQuestView = QuestClient.State.Active.FirstOrDefault((ActiveQuestView x) => string.Equals(x.Id, Arg.Scalar(Arg.Value(a)), StringComparison.OrdinalIgnoreCase)); if (activeQuestView != null) { list.AddRange(QuestClient.CollectFor(activeQuestView)); } } } string nodeId = _nodeId; ClientState.Send("dialogue.choose", new DialogueChooseRequest { ProfileId = _profile.Id, Node = nodeId, Choice = index, Given = list }, delegate(Response r) { if (!string.IsNullOrEmpty(r.Message)) { ClientState.Notify(r.Message); } if (IsOpen) { if (!r.Ok) { Show(_nodeId); } else if (!r.Actions.Any((ClientAction clientAction) => clientAction.Kind == "openShop")) { Advance(string.IsNullOrEmpty(r.Next) ? choice.Next : r.Next); } } }); } private static void Advance(string next) { if (string.IsNullOrEmpty(next)) { Close(); } else { Show(next); } } } internal static class ActionRunnerNames { public static bool IsClientOnly(string name) { return string.Equals(name, "openShop", StringComparison.OrdinalIgnoreCase); } } internal static class NpcEditor { private const float Width = 840f; private const float Height = 720f; private static GameObject _window; private static NpcComponent _npc; private static readonly Dictionary<string, InputField> Fields = new Dictionary<string, InputField>(); private static string _role; private static string _pin; private static int _closedFrame = -1; public static bool IsOpen => (Object)(object)_window != (Object)null; public static bool ClosedThisFrame => _closedFrame == Time.frameCount; public static void Open(NpcComponent npc) { if (!((Object)(object)GUIManager.CustomGUIFront == (Object)null) && !((Object)(object)npc == (Object)null)) { NpcPanel.Close(); Close(); _npc = npc; NpcProfile obj = npc.Profile ?? new NpcProfile { Id = npc.ProfileId }; obj.FillDefaults(); _role = obj.Role; _pin = obj.Behaviour.MapPin; Build(obj); GUIManager.BlockInput(true); } } public static void Close() { if (!((Object)(object)_window == (Object)null)) { Object.Destroy((Object)(object)_window); _window = null; _npc = null; Fields.Clear(); _closedFrame = Time.frameCount; GUIManager.BlockInput(false); } } public static void Tick() { if (IsOpen) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (Object)(object)_npc == (Object)null || ZInput.GetKeyDown((KeyCode)27, true)) { Close(); } } } private static void Build(NpcProfile eff) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) _window = PanelBuilder.Window(840f, 720f); ((Object)_window).name = "TSN_Editor"; Transform transform = _window.transform; PanelBuilder.Label(transform, "Edit NPC", 20f, 18f, 720f, 40f, 28, GUIManager.Instance.ValheimOrange, (TextAnchor)4); PanelBuilder.Button(transform, "X", 770f, 18f, 46f, 38f, Close); float contentWidth; RectTransform content = PanelBuilder.ScrollList(transform, 20f, 66f, 800f, 584f, out contentWidth); Header(content, contentWidth, "Identity"); Field(content, contentWidth, "profile", "Profile id (which config file)", eff.Id); Field(content, contentWidth, "name", "Name", eff.Name); PanelBuilder.AddRow(content, contentWidth, new Row { Label = "Role: " + _role, ButtonText = "Change", OnClick = delegate { _role = Next(NpcRoles.All, _role); Reopen(); } }); Field(content, contentWidth, "model", "Model (Haldor, Hildir, BogWitch, Player, PlayerFemale, Troll, ...)", eff.Model); Field(content, contentWidth, "greeting", "Greeting", eff.Greeting); Field(content, contentWidth, "goodbye", "Goodbye", eff.Behaviour.Goodbye); Header(content, contentWidth, "Look (gear, hair and colours need the Player or PlayerFemale model)"); Appearance look = eff.Look; Field(content, contentWidth, "helmet", "Helmet", look.Helmet); Field(content, contentWidth, "chest", "Chest", look.Chest); Field(content, contentWidth, "legs", "Legs", look.Legs); Field(content, contentWidth, "cape", "Cape", look.Cape); Field(content, contentWidth, "leftHand", "Left hand", look.LeftHand); Field(content, contentWidth, "rightHand", "Right hand", look.RightHand); Field(content, contentWidth, "leftBack", "Left back", look.LeftBack); Field(content, contentWidth, "rightBack", "Right back", look.RightBack); Field(content, contentWidth, "hair", "Hair (Hair1 .. Hair23)", look.Hair); Field(content, contentWidth, "beard", "Beard (Beard1 .. Beard21)", look.Beard); Field(content, contentWidth, "hairColor", "Hair colour (#hex)", look.HairColor); Field(content, contentWidth, "skinColor", "Skin colour (#hex)", look.SkinColor); Field(content, contentWidth, "scale", "Size (0.5 - 3)", F(look.Scale)); Header(content, contentWidth, "Behaviour"); NpcBehaviour behaviour = eff.Behaviour; Field(content, contentWidth, "greetRange", "Greet range (m)", F(behaviour.GreetRange)); Field(content, contentWidth, "greetAnimation", "Greet animation (emote_wave ...)", behaviour.GreetAnimation); Field(content, contentWidth, "goodbyeAnimation", "Goodbye animation", behaviour.GoodbyeAnimation); Field(content, contentWidth, "interactAnimation", "Interact animation", behaviour.InteractAnimation); Field(content, contentWidth, "idleAnimation", "Idle animation", behaviour.IdleAnimation); Field(content, contentWidth, "idleEvery", "Idle animation every (s)", F(behaviour.IdleEverySeconds)); Field(content, contentWidth, "interactSound", "Interact sound (sfx_...)", behaviour.InteractSound); Field(content, contentWidth, "idleSound", "Idle sound", behaviour.IdleSound); Field(content, contentWidth, "idleSoundEvery", "Idle sound every (s, 0 = off)", F(behaviour.IdleSoundEverySeconds)); Field(content, contentWidth, "craftingPose", "Crafting pose (-1 none, 0-3)", behaviour.CraftingPose.ToString()); PanelBuilder.AddRow(content, contentWidth, new Row { Label = "Map pin: " + _pin, ButtonText = "Change", OnClick = delegate { _pin = Next(MapPinModes.All, _pin); Reopen(); } }); Field(content, contentWidth, "patrol", "Patrol points x,y,z ; x,y,z (empty = stand still)", string.Join(" ; ", behaviour.Patrol.Points.Select((PatrolPoint p) => F(p.X) + "," + F(p.Y) + "," + F(p.Z)))); Field(content, contentWidth, "patrolSpeed", "Patrol speed (m/s)", F(behaviour.Patrol.Speed)); Field(content, contentWidth, "patrolPause", "Pause at each point (s)", F(behaviour.Patrol.PauseSeconds)); Header(content, contentWidth, "Preset (saves everything above as a new profile file on the server)"); Field(content, contentWidth, "preset", "Preset id (letters, numbers, - and _)", ""); PanelBuilder.AddRow(content, contentWidth, new Row { Label = "Save this NPC as a preset", ButtonText = "Save preset", OnClick = delegate { SavePreset(); } }); float y = 658f; PanelBuilder.Button(transform, "Apply", 20f, y, 150f, 40f, delegate { Send(move: false); }); PanelBuilder.Button(transform, "Snap to ground + face me", 180f, y, 230f, 40f, delegate { Send(move: true); }); PanelBuilder.Button(transform, "Use profile defaults", 420f, y, 200f, 40f, UseDefaults); PanelBuilder.Button(transform, "Remove NPC", 630f, y, 150f, 40f, Remove); if (eff.Role == "trader" || eff.Role == "teleporter") { PanelBuilder.Button(transform, (eff.Role == "trader") ? "Edit stock" : "Edit destinations", 610f, 18f, 150f, 38f, delegate { ConfigEditor.Open(_npc?.Profile); }); } if (eff.Role == "quest") { PanelBuilder.Button(transform, "Quest Designer", 610f, 18f, 150f, 38f, delegate { QuestDesigner.Open(); }); } } private static void Reopen() { Dictionary<string, string> dictionary = Fields.ToDictionary((KeyValuePair<string, InputField> k) => k.Key, (KeyValuePair<string, InputField> k) => k.Value.text); NpcComponent npc = _npc; NpcProfile eff = Collect(); Object.Destroy((Object)(object)_window); _window = null; Fields.Clear(); _npc = npc; Build(eff); foreach (KeyValuePair<string, string> item in dictionary) { if (Fields.TryGetValue(item.Key, out var value)) { value.text = item.Value; } } } private static void Header(RectTransform content, float w, string text) { PanelBuilder.AddRow(content, w, new Row { Header = true, Label = text }); } private static void Field(RectTransform content, float w, string key, string label, string value) { Fields[key] = PanelBuilder.AddTextRow(content, w, label, value ?? ""); } private static string V(string key) { if (!Fields.TryGetValue(key, out var value)) { return ""; } return (value.text ?? "").Trim(); } private static float Num(string key, float fallback) { if (!float.TryParse(V(key), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return fallback; } return result; } private static string F(float v) { return v.ToString("0.##", CultureInfo.InvariantCulture); } private static string Next(string[] options, string current) { int num = Array.IndexOf(options, current); return options[(num + 1) % options.Length]; } private static NpcProfile Collect() { NpcProfile npcProfile = new NpcProfile(); npcProfile.Id = V("profile"); npcProfile.Name = V("name"); npcProfile.Role = _role; npcProfile.Model = V("model"); npcProfile.Greeting = V("greeting"); npcProfile.Look = new Appearance { Helmet = V("helmet"), Chest = V("chest"), Legs = V("legs"), Cape = V("cape"), LeftHand = V("leftHand"), RightHand = V("rightHand"), LeftBack = V("leftBack"), RightBack = V("rightBack"), Hair = V("hair"), Beard = V("beard"), HairColor = V("hairColor"), SkinColor = V("skinColor"), Scale = Mathf.Clamp(Num("scale", 1f), 0.5f, 3f) }; npcProfile.Behaviour = new NpcBehaviour { Goodbye = V("goodbye"), GreetRange = Num("greetRange", 5f), GreetAnimation = V("greetAnimation"), GoodbyeAnimation = V("goodbyeAnimation"), InteractAnimation = V("interactAnimation"), IdleAnimation = V("idleAnimation"), IdleEverySeconds = Num("idleEvery", 60f), InteractSound = V("interactSound"), IdleSound = V("idleSound"), IdleSoundEverySeconds = Num("idleSoundEvery", 0f), CraftingPose = (int)Num("craftingPose", -1f), MapPin = _pin, Patrol = new Patrol { Speed = Num("patrolSpeed", 1.5f), PauseSeconds = Num("patrolPause", 3f), Points = ParsePoints(V("patrol")) } }; npcProfile.FillDefaults(); return npcProfile; } private static List<PatrolPoint> ParsePoints(string text) { List<PatrolPoint> list = new List<PatrolPoint>(); string[] array = text.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { string[] array2 = (from s in array[i].Split(new char[1] { ',' }) select s.Trim() into s where s.Length > 0 select s).ToArray(); if (array2.Length == 3 && float.TryParse(array2[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && float.TryParse(array2[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && float.TryParse(array2[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { list.Add(new PatrolPoint { X = result, Y = result2, Z = result3 }); } } return list; } private static AdminEditRequest Request(NpcProfile over) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) ZDOID uid = _npc.Zdo.m_uid; return new AdminEditRequest { ZdoUser = ((ZDOID)(ref uid)).UserID, ZdoId = ((ZDOID)(ref uid)).ID, ProfileId = V("profile"), Name = V("name"), Override = over }; } private static void Send(bool move) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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) if ((Object)(object)_npc == (Object)null || _npc.Zdo == null) { Close(); return; } AdminEditRequest adminEditRequest = Request(Collect()); if (move) { Player localPlayer = Player.m_localPlayer; Vector3 position = ((Component)_npc).transform.position; float y = default(float); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGroundHeight(position, ref y)) { position.y = y; } Vector3 val = ((Component)localPlayer).transform.position - position; val.y = 0f; adminEditRequest.Move = true; adminEditRequest.X = position.x; adminEditRequest.Y = position.y; adminEditRequest.Z = position.z; float y2; if (!(((Vector3)(ref val)).sqrMagnitude > 0.01f)) { y2 = ((Component)_npc).transform.eulerAngles.y; } else { Quaternion val2 = Quaternion.LookRotation(val); y2 = ((Quaternion)(ref val2)).eulerAngles.y; } adminEditRequest.RotY = y2; } ClientState.Send("admin.edit", adminEditRequest, delegate(Response r) { ClientState.Notify(r.Message); if (r.Ok) { Close(); } }); } private static void UseDefaults() { if ((Object)(object)_npc == (Object)null || _npc.Zdo == null) { Close(); return; } ClientState.Send("admin.edit", Request(null), delegate(Response r) { ClientState.Notify(r.Message); if (r.Ok) { Close(); } }); } private static void SavePreset() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_npc == (Object)null || _npc.Zdo == null) { Close(); return; } ZDOID id = _npc.Zdo.m_uid; ClientState.Send("admin.edit", Request(Collect()), delegate(Response r) { if (!r.Ok) { ClientState.Notify(r.Message); } else { ClientState.Send("admin.savePreset", new AdminSavePresetRequest { ZdoUser = ((ZDOID)(ref id)).UserID, ZdoId = ((ZDOID)(ref id)).ID, PresetId = V("preset") }, delegate(Response response) { ClientState.Notify(response.Message); if (response.Ok) { Close(); } }); } }); } private static void Remove() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_npc == (Object)null || _npc.Zdo == null) { Close(); return; } ZDOID uid = _npc.Zdo.m_uid; ClientState.Send("admin.remove", new NpcRef { ZdoUser = ((ZDOID)(ref uid)).UserID, ZdoId = ((ZDOID)(ref uid)).ID }, delegate(Response r) { ClientState.Notify(r.Message); }); Close(); } } internal static class NpcPanel { private const float Width = 760f; private const float Height = 620f; private const float MaxDistance = 8f; private static GameObject _window; private static GameObject _body; private static int _closedFrame = -1; public static NpcComponent Npc { get; private set; } public static NpcProfile Profile { get; private set; } public static int Tab { get; set; } public static bool PayFromBank { get; set; } public static bool IsOpen => (Object)(object)_window != (Object)null; public static bool ClosedThisFrame => _closedFrame == Time.frameCount; public static void Open(NpcComponent npc, NpcProfile profile) { if (!((Object)(object)GUIManager.CustomGUIFront == (Object)null)) { Close(); Npc = npc; Profile = profile; Tab = 0; _window = PanelBuilder.Window(760f, 620f); ((Object)_window).name = "TSN_Panel"; GUIManager.BlockInput(true); Views.OnOpen(profile); Refresh(); } } public static void Close() { if (!((Object)(object)_window == (Object)null)) { Object.Destroy((Object)(object)_window); _window = null; _body = null; Npc = null; Profile = null; _closedFrame = Time.frameCount; GUIManager.BlockInput(false); } } public static void Tick() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (IsOpen) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (Object)(object)Npc == (Object)null || Vector3.Distance(((Component)localPlayer).transform.position, ((Component)Npc).transform.position) > 8f || ZInput.GetKeyDown((KeyCode)27, true)) { Close(); } } } public static void OnConfigChanged() { if (IsOpen) { NpcProfile npcProfile = (((Object)(object)Npc != (Object)null) ? Npc.Profile : null); if (npcProfile == null) { Close(); return; } Profile = npcProfile; Refresh(); } } public static void Refresh() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_0066: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_015a: 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) if (!IsOpen) { return; } if ((Object)(object)_body != (Object)null) { Object.Destroy((Object)(object)_body); } _body = new GameObject("Body", new Type[1] { typeof(RectTransform) }); _body.transform.SetParent(_window.transform, false); RectTransform component = _body.GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; Vector2 offsetMin = (component.offsetMax = Vector2.zero); component.offsetMin = offsetMin; Transform transform = _body.transform; string text = (((Object)(object)Npc != (Object)null) ? Npc.DisplayName : Profile.Name); PanelBuilder.Label(transform, text, 20f, 18f, 640f, 40f, 28, GUIManager.Instance.ValheimOrange, (TextAnchor)4); PanelBuilder.Button(transform, "X", 690f, 18f, 46f, 38f, Close); if (!string.IsNullOrEmpty(Profile.Greeting)) { PanelBuilder.Label(transform, Profile.Greeting, 20f, 58f, 720f, 28f, 16, GUIManager.Instance.ValheimBeige, (TextAnchor)4); } string[] array = Views.Tabs(Profile); for (int i = 0; i < array.Length; i++) { int index = i; string text2 = ((i == Tab) ? ("> " + array[i] + " <") : array[i]); PanelBuilder.Button(transform, text2, 20f + (float)i * 158f, 94f, 150f, 34f, delegate { Tab = index; Refresh(); }); } if (Views.UsesPayment(Profile) && ClientState.Config.Settings.Bank.Enabled) { PanelBuilder.Button(transform, PayFromBank ? "Pay from: Bank" : "Pay from: Inventory", 530f, 94f, 210f, 34f, delegate { PayFromBank = !PayFromBank; Refresh(); }); } PanelBuilder.Label(transform, Views.Status(Profile), 20f, 134f, 720f, 26f, 16, Color.white, (TextAnchor)3); float contentWidth; RectTransform content = PanelBuilder.ScrollList(transform, 20f, 166f, 720f, 434f, out contentWidth); List<Row> list = new List<Row>(); Views.Rows(Profile, Tab, list); if (list.Count == 0) { list.Add(new Row { Label = "Nothing here yet." }); } foreach (Row item in list) { PanelBuilder.AddRow(content, contentWidth, item); } } } [HarmonyPatch(typeof(Menu), "Update")] internal static class MenuUpdatePatch { private static bool Prefix() { if (!NpcPanel.IsOpen && !NpcPanel.ClosedThisFrame && !NpcEditor.IsOpen && !NpcEditor.ClosedThisFrame && !ConfigEditor.IsOpen && !ConfigEditor.ClosedThisFrame && !QuestDesigner.IsOpen && !QuestDesigner.ClosedThisFrame && !DialoguePanel.IsOpen && !DialoguePanel.ClosedThisFrame && !QuestJournal.IsOpen) { return !QuestJournal.ClosedThisFrame; } return false; } } internal class Row { public Sprite Icon; public string Label; public string ButtonText; public Action<int> OnClick; public string InputPlaceholder; public int InputDefault; public bool Header; public Action<string> OnText; } internal static class PanelBuilder { private static readonly Vector2 Center = new Vector2(0.5f, 0.5f); private static readonly Vector2 MidLeft = new Vector2(0f, 0.5f); private static readonly Vector2 TopLeft = new Vector2(0f, 1f); private const float RowHeight = 40f; public static GameObject Window(float width, float height) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) return GUIManager.Instance.CreateWoodpanel(GUIManager.CustomGUIFront.transform, Center, Center, Vector2.zero, width, height, true); } public static Text Label(Transform parent, string text, float x, float y, float width, float height, int size, Color color, TextAnchor align = (TextAnchor)3) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) Text component = GUIManager.Instance.CreateText(text, parent, TopLeft, TopLeft, new Vector2(x + width / 2f, 0f - (y + height / 2f)), GUIManager.Instance.AveriaSerifBold, size, color, true, Color.black, width, height, false).GetComponent<Text>(); component.alignment = align; component.horizontalOverflow = (HorizontalWrapMode)0; component.verticalOverflow = (VerticalWrapMode)0; return component; } public static Button Button(Transform parent, string text, float x, float y, float width, float height, Action onClick) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown Button component = GUIManager.Instance.CreateButton(text, parent, TopLeft, TopLeft, new Vector2(x + width / 2f, 0f - (y + height / 2f)), width, height).GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener((UnityAction)delegate { onClick(); }); return component; } public static RectTransform ScrollList(Transform parent, float x, float y, float width, float height, out float contentWidth) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Expected O, but got Unknown ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock; ((ColorBlock)(ref defaultColorBlock)).normalColor = new Color(0.82f, 0.55f, 0.22f, 1f); ((ColorBlock)(ref defaultColorBlock)).highlightedColor = new Color(1f, 0.7f, 0.3f, 1f); ((ColorBlock)(ref defaultColorBlock)).pressedColor = new Color(0.6f, 0.4f, 0.15f, 1f); GameObject obj = GUIManager.Instance.CreateScrollView(parent, false, true, 10f, 4f, defaultColorBlock, new Color(0f, 0f, 0f, 0.35f), width, height); RectTransform component = obj.GetComponent<RectTransform>(); Vector2 anchorMin = (component.anchorMax = TopLeft); component.anchorMin = anchorMin; component.anchoredPosition = new Vector2(x + width / 2f, 0f - (y + height / 2f)); contentWidth = width - 8f - 10f; ScrollRect componentInChildren = obj.GetComponentInChildren<ScrollRect>(); VerticalLayoutGroup component2 = ((Component)componentInChildren.content).GetComponent<VerticalLayoutGroup>(); ((HorizontalOrVerticalLayoutGroup)component2).spacing = 4f; ((LayoutGroup)component2).padding = new RectOffset(4, 4, 4, 4); return componentInChildren.content; } private static GameObject NewRow(RectTransform content, float width, bool header) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown GameObject val = new GameObject("Row", new Type[3] { typeof(RectTransform), typeof(LayoutElement), typeof(Image) }); val.transform.SetParent((Transform)(object)content, false); val.GetComponent<RectTransform>().sizeDelta = new Vector2(width - 8f, 40f); LayoutElement component = val.GetComponent<LayoutElement>(); float minHeight = (component.preferredHeight = 40f); component.minHeight = minHeight; ((Graphic)val.GetComponent<Image>()).color = (header ? new Color(0.35f, 0.25f, 0.1f, 0.5f) : new Color(0f, 0f, 0f, 0.25f)); return val; } private static Text RowText(Transform row, string text, float x, float width, bool header) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0045: 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) Text component = GUIManager.Instance.CreateText(text ?? "", row, MidLeft, MidLeft, new Vector2(x + width / 2f, 0f), GUIManager.Instance.AveriaSerifBold, header ? 18 : 16, header ? GUIManager.Instance.ValheimOrange : Color.white, true, Color.black, width, 40f, false).GetComponent<Text>(); component.alignment = (TextAnchor)3; component.horizontalOverflow = (HorizontalWrapMode)0; component.verticalOverflow = (VerticalWrapMode)0; component.resizeTextForBestFit = true; component.resizeTextMinSize = 11; component.resizeTextMaxSize = (header ? 18 : 16); return component; } public static void AddRow(RectTransform content, float width, Row row) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Expected O, but got Unknown //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) if (row.OnText != null) { ((UnityEvent<string>)(object)AddTextRow(content, width, row.Label, "").onValueChanged).AddListener((UnityAction<string>)delegate(string v) { row.OnText(v); }); return; } GameObject val = NewRow(content, width, row.Header); float num = 6f; if (Object.op_Implicit((Object)(object)row.Icon)) { GameObject val2 = new GameObject("Icon", new Type[2] { typeof(RectTransform), typeof(Image) }); val2.transform.SetParent(val.transform, false); RectTransform component = val2.GetComponent<RectTransform>(); Vector2 anchorMin = (component.anchorMax = MidLeft); component.anchorMin = anchorMin; component.sizeDelta = new Vector2(32f, 32f); component.anchoredPosition = new Vector2(num + 16f, 0f); Image component2 = val2.GetComponent<Image>(); component2.sprite = row.Icon; component2.preserveAspect = true; num += 40f; } bool flag = row.OnClick != null && !string.IsNullOrEmpty(row.ButtonText); bool flag2 = flag && row.InputPlaceholder != null; float num2 = width - 8f - 6f - (flag ? 126f : 0f) - (flag2 ? 86f : 0f); RowText(val.transform, row.Label, num, num2 - num, row.Header); if (!flag) { return; } InputField input = null; float num3 = width - 8f - 6f - 120f; if (flag2) { float num4 = num3 - 6f - 80f; input = GUIManager.Instance.CreateInputField(val.transform, MidLeft, MidLeft, new Vector2(num4 + 40f, 0f), (ContentType)2, row.InputPlaceholder, 16, 80f, 30f).GetComponent<InputField>(); if (row.InputDefault > 0) { input.text = row.InputDefault.ToString(); } } ((UnityEvent)GUIManager.Instance.CreateButton(row.ButtonText, val.transform, MidLeft, MidLeft, new Vector2(num3 + 60f, 0f), 120f, 32f).GetComponent<Button>().onClick).AddListener((UnityAction)delegate { int result = 0; if ((Object)(object)input != (Object)null) { int.TryParse(input.text, out result); } row.OnClick(result); }); } public static InputField[] AddFieldsRow(RectTransform content, float width, string[] values, string buttonText = null, Action onButton = null) { //IL_010c: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) GameObject val = NewRow(content, width, header: false); InputField[] array = (InputField[])(object)new InputField[values.Length]; float num = (string.IsNullOrEmpty(buttonText) ? 0f : 90f); float num2 = (width - 8f - num - 8f) / ((float)values.Length + 0.8f); float num3 = 4f; for (int i = 0; i < values.Length; i++) { float num4 = ((i == 0) ? (num2 * 1.8f) : num2); InputField component = GUIManager.Instance.CreateInputField(val.transform, MidLeft, MidLeft, new Vector2(num3 + num4 / 2f, 0f), (ContentType)0, "", 13, num4 - 4f, 30f).GetComponent<InputField>(); component.text = values[i] ?? ""; array[i] = component; num3 += num4; } if (num > 0f) { ((UnityEvent)GUIManager.Instance.CreateButton(buttonText, val.transform, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(0f - num / 2f - 4f, 0f), num - 6f, 30f).GetComponent<Button>().onClick).AddListener((UnityAction)delegate { onButton?.Invoke(); }); } return array; } public static InputField AddTextRow(RectTransform content, float width, string label, string value) { //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_006c: Unknown result type (might be due to invalid IL or missing references) GameObject val = NewRow(content, width, header: false); float num = (width - 8f) * 0.48f; RowText(val.transform, label, 6f, num - 6f, header: false); float num2 = num + 4f; float num3 = width - 8f - num2 - 6f; InputField component = GUIManager.Instance.CreateInputField(val.transform, MidLeft, MidLeft, new Vector2(num2 + num3 / 2f, 0f), (ContentType)0, "", 15, num3, 30f).GetComponent<InputField>(); component.text = value ?? ""; return component; } } internal static class QuestDesigner { private const float Width = 960f; private const float Height = 740f; private static GameObject _window; private static QuestDef _quest; private static readonly Dictionary<string, InputField> F = new Dictionary<string, InputField>(); private static readonly List<InputField[]> StepFields = new List<InputField[]>(); private static readonly List<InputField[]> ItemFields = new List<InputField[]>(); private static int _closedFrame = -1; public static bool IsOpen => (Object)(object)_window != (Object)null; public static bool ClosedThisFrame => _closedFrame == Time.frameCount; public static void Open(string editQuestId = null) { if (!((Object)(object)GUIManager.CustomGUIFront == (Object)null) && ClientState.Config != null) { if (!ClientState.IsLocalAdmin) { ClientState.Notify("Only admins can design quests."); return; } NpcPanel.Close(); NpcEditor.Close(); Close(); _quest = ((editQuestId == null) ? null : Clone(ClientState.Config.GetQuest(editQuestId))); GUIManager.BlockInput(true); Build(); } } public static void Close() { if (!((Object)(object)_window == (Object)null)) { Object.Destroy((Object)(object)_window); _window = null; _quest = null; F.Clear(); StepFields.Clear(); ItemFields.Clear(); _closedFrame = Time.frameCount; GUIManager.BlockInput(false); } } public static void Tick() { if (IsOpen && ((Object)(object)Player.m_localPlayer == (Object)null || ZInput.GetKeyDown((KeyCode)27, true))) { Close(); } } public static void OnConfigChanged() { if (IsOpen && _quest == null) { Build(); } } private static void Build() { if ((Object)(object)_window != (Object)null) { Object.Destroy((Object)(object)_window); } F.Clear(); StepFields.Clear(); ItemFields.Clear(); _window = PanelBuilder.Window(960f, 740f); ((Object)_window).name = "TSN_QuestDesigner"; Transform transform = _window.transform; PanelBuilder.Button(transform, "X", 890f, 18f, 46f, 38f, Close); if (_quest == null) { BuildList(transform); } else { BuildForm(transform); } } private static void BuildList(Transform t) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) PanelBuilder.Label(t, "Quest Designer", 20f, 18f, 840f, 40f, 28, GUIManager.Instance.ValheimOrange, (TextAnchor)4); float contentWidth; RectTransform content = PanelBuilder.ScrollList(t, 20f, 66f, 920f, 604f, out contentWidth); PanelBuilder.AddRow(content, contentWidth, new Row { Header = true, Label = "Start a new quest", ButtonText = "New quest", OnClick = delegate { _quest = NewQuest(); Build(); } }); PanelBuilder.AddRow(content, contentWidth, new Row { Label = "Quests are files in quests/ on the server. Edit here or in any text editor; both reload live." }); foreach (QuestDef item in from q in ClientState.Config.Quests orderby q.Giver, q.Name select q) { QuestDef quest = item; string text = (string.IsNullOrEmpty(item.Giver) ? "dialogue only" : (ClientState.Config.GetProfile(item.Giver)?.Name ?? item.Giver)); PanelBuilder.AddRow(content, contentWidth, new Row { Label = $"{item.Name} ({item.Id}) giver: {text} · {item.Steps.Count} step(s), {item.Repeat}", ButtonText = "Edit", OnClick = delegate { _quest = Clone(quest); Build(); } }); } PanelBuilder.Button(t, "Close", 20f, 678f, 120f, 40f, Close); } private static void BuildForm(Transform t) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) QuestDef q = _quest; PanelBuilder.Label(t, string.IsNullOrEmpty(q.Id) ? "New quest" : ("Quest: " + q.Id), 20f, 18f, 840f, 40f, 26, GUIManager.Instance.ValheimOrange, (TextAnchor)4); float contentWidth; RectTransform val = PanelBuilder.ScrollList(t, 20f, 66f, 920f, 604f, out contentWidth); Header(val, contentWidth, "Basics"); Field(val, contentWidth, "id", "Id (letters, numbers, _ ; becomes the file name)", q.Id); Field(val, contentWidth, "name", "Name shown to players", q.Name); Field(val, contentWidth, "description", "Description", q.Description); List<string> list = (from p in ClientState.Config.Profiles where p.Role == "quest" select p.Id).ToList(); Field(val, contentWidth, "giver", "Giver profile id (quest NPCs: " + ((list.Count == 0) ? "none yet" : string.Join(", ", list)) + "; empty = dialogue only)", q.Giver); Field(val, contentWidth, "turnIn", "Hand in at (profile id, empty = the giver)", q.TurnIn); PanelBuilder.AddRow(val, contentWidth, new Row { Label = "Repeat: " + q.Repeat, ButtonText = "Change", OnClick = delegate { Collect(); q.Repeat = Next(QuestRepeat.All, q.Repeat); Build(); } }); Field(val, contentWidth, "cooldownHours", "Cooldown hours (repeat: cooldown)", N(q.CooldownHours)); Field(val, contentWidth, "timeLimitHours", "Time limit hours (0 = none)", N(q.TimeLimitHours)); PanelBuilder.AddRow(val, contentWidth, new Row { Label = "Hidden until unlocked by another quest: " + (q.Hidden ? "yes" : "no"), ButtonText = "Toggle", OnClick = delegate { Collect(); q.Hidden = !q.Hidden; Build(); } }); Field(val, contentWidth, "requiresQuests", "Requires quests done first (ids, comma separated)", string.Join(", ", q.RequiresQuests)); Field(val, contentWidth, "requiresKey", "Requires global key (e.g. defeated_eikthyr)", q.RequiresKey); Header(val, contentWidth, "Steps — type | target / npc | amount | min stars+1 | x | z | radius | text shown"); for (int num = 0; num < q.Steps.Count; num++) { QuestStep s = q.Steps[num]; int index = num; PanelBuilder.AddRow(val, contentWidth, new Row { Label = $"Step {num + 1}: {s.Type} ({Hint(s.Type)})", ButtonText = "Change type", OnClick = delegate { Collect(); s.Type = Next(QuestStepTypes.All, s.Type); Build(); } }); InputField[] item = PanelBuilder.AddFieldsRow(val, contentWidth, new string[7] { (s.Type == "talk") ? s.Npc : s.Target, s.Amount.ToString(), s.MinLevel.ToString(), N(s.X), N(s.Z), N(s.Radius), s.Text }, "Remove", delegate { Collect(); q.Steps.RemoveAt(index); Build(); }); StepFields.Add(item); if (s.Type == "explore") { PanelBuilder.AddRow(val, contentWidth, new Row { Label = " Use my current position for this step", ButtonText = "Here", OnClick = delegate { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) Collect(); Vector3 position = ((Component)Player.m_localPlayer).transform.position; s.X = Mathf.Round(position.x); s.Z = Mathf.Round(position.z); Build(); } }); } } PanelBuilder.AddRow(val, contentWidth, new Row { Label = "Add a step", ButtonText = "Add step", OnClick = delegate { Collect(); q.Steps.Add(new QuestStep { Type = "kill", Target = "Greydwarf", Amount = 5 }); Build(); } }); Header(val, contentWidth, "Rewards"); Field(val, contentWidth, "coins", "Coins", q.Rewards.Coins.ToString()); PanelBuilder.AddRow(val, contentWidth, new Row { Label = "Coins go to: " + (q.Rewards.CoinsToBank ? "bank" : "inventory"), ButtonText = "Toggle", OnClick = delegate { Collect(); q.Rewards.CoinsToBank = !q.Rewards.CoinsToBank; Build(); } }); PanelBuilder.AddRow(val, contentWidth, new Row { Label = "Items — prefab | amount | quality" }); for (int num2 = 0; num2 < q.Rewards.Items.Count; num2++) { int index2 = num2; RewardItem rewardItem = q.Rewards.Items[num2]; ItemFields.Add(PanelBuilder.AddFieldsRow(val, contentWidth, new string[3] { rewardItem.Item, rewardItem.Amount.ToString(), rewardItem.Quality.ToString() }, "Remove", delegate { Collect(); q.Rewards.Items.RemoveAt(index2); Build(); })); } PanelBuilder.AddRow(val, contentWidth, new Row { Label = "Add a reward item", ButtonText = "Add item", OnClick = delegate { Collect(); q.Rewards.Items.Add(new RewardItem { Item = "Coal", Amount = 10 }); Build(); } }); Field(val, contentWidth, "skill", "Skill to train (Swords, Bows, Run ...)", q.Rewards.Skill); Field(val, contentWidth, "skillXp", "Skill experience", N(q.Rewards.SkillXp)); Field(val, contentWidth, "buff", "Buff id or status effect name", q.Rewards.Buff); Field(val, contentWidth, "buffSeconds", "Buff seconds (0 = its default)", N(q.Rewards.BuffSeconds)); Field(val, contentWidth, "unlockQuests", "Unlocks hidden quests (ids, comma separated)", string.Join(", ", q.Rewards.UnlockQuests)); Header(val, contentWidth, "Messages (more event actions can be added in the YAML file)"); Field(val, contentWidth, "onAccept", "Said when accepted", EventMessage(q, "onAccept")); Field(val, contentWidth, "onComplete", "Said when completed", EventMessage(q, "onComplete")); Field(val, contentWidth, "onTimeout", "Said when it times out", EventMessage(q, "onTimeout")); float y = 678f; PanelBuilder.Button(t, "Save (goes live)", 20f, y, 190f, 40f, Save); PanelBuilder.Button(t, "Back to list", 220f, y, 150f, 40f, delegate { _quest = null; Build(); }); if (ClientState.Config.GetQuest(q.Id) != null) { PanelBuilder.Button(t, "Delete quest", 780f, y, 160f, 40f, Delete); } } private static void Header(RectTransform c, float w, string text) { PanelBuilder.AddRow(c, w, new Row { Header = true, Label = text }); } private static void Field(RectTransform c, float w, string key, string label, string value) { F[key] = PanelBuilder.AddTextRow(c, w, label, value ?? ""); } private static string V(string key) { if (!F.TryGetValue(key, out var value)) { return ""; } return (value.text ?? "").Trim(); } private static float Num(string s, float d) { if (!float.TryParse((s ?? "").Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return d; } return result; } private static string N(float v) { return v.ToString("0.##", CultureInfo.InvariantCulture); } private static string Next(string[] options, string current) { return options[(Array.IndexOf(options, current) + 1) % options.Length]; } private static List<string> Csv(string s) { return (from x in s.Split(new char[1] { ',' }) select x.Trim() into x where x.Length > 0 select x).ToList(); } private static string Hint(string type) { return type switch { "kill" => "creature prefab, e.g. Greydwarf; min stars+1: 1 = any, 2 = one star", "gather" => "item prefab; taken from the player when handed in", "deliver" => "item prefab brought to the hand-in NPC", "craft" => "item prefab crafted", "build" => "piece prefab placed, e.g. fire_pit", "explore" => "reach x, z within radius", "talk" => "profile id of the NPC to speak to", _ => "", }; } private static string EventMessage(QuestDef q, string ev) { foreach (Dictionary<string, object> item in q.EventActions(ev)) { if (Arg.Name(item).Equals("message", StringComparison.OrdinalIgnoreCase)) { return Arg.Scalar(Arg.Value(item)); } } return ""; } private static void SetEventMessage(QuestDef q, string ev, string text) { List<Dictionary<string, object>> list = (from a in q.EventActions(ev) where !Arg.Name(a).Equals("message", StringComparison.OrdinalIgnoreCase) select a).ToList(); if (!string.IsNullOrWhiteSpace(text)) { list.Insert(0, new Dictionary<string, object> { { "message", text.Trim() } }); } string key = q.Events.Keys.FirstOrDefault((string k) => string.Equals(k, ev, StringComparison.OrdinalIgnoreCase)) ?? ev; if (list.Count == 0) { q.Events.Remove(key); } else { q.Events[key] = list; } } private static QuestDef NewQuest() { string giver = ClientState.Config.Profiles.FirstOrDefault((NpcProfile p) => p.Role == "quest")?.Id ?? ""; QuestDef questDef = new QuestDef(); questDef.Id = ""; questDef.Name = "New quest"; questDef.Description = ""; questDef.Giver = giver; questDef.Repeat = "once"; questDef.Steps.Add(new QuestStep { Type = "kill", Target = "Greydwarf", Amount = 5 }); questDef.Rewards.Coins = 50; questDef.FillDefaults(); return questDef; } private static QuestDef Clone(QuestDef q) { if (q == null) { return NewQuest(); } QuestDef questDef = JsonConvert.DeserializeObject<QuestDef>(JsonConvert.SerializeObject((object)q)); questDef.FillDefaults(); return questDef; } private static void Collect() { QuestDef quest = _quest; if (quest == null || F.Count == 0) { return; } quest.Id = V("id"); quest.Name = V("name"); quest.Description = V("description"); quest.Giver = V("giver"); quest.TurnIn = V("turnIn"); quest.CooldownHours = Num(V("cooldownHours"), 24f); quest.TimeLimitHours = Num(V("timeLimitHours"), 0f); quest.RequiresQuests = Csv(V("requiresQuests")); quest.RequiresKey = V("requiresKey"); for (int i = 0; i < StepFields.Count && i < quest.Steps.Count; i++) { InputField[] array = StepFields[i]; QuestStep questStep = quest.Steps[i]; string text = array[0].text.Trim(); if (questStep.Type == "talk") { questStep.Npc = text; } else { questStep.Target = text; } questStep.Amount = Math.Max(1, (int)Num(array[1].text, 1f)); questStep.MinLevel = Math.Max(1, (int)Num(array[2].text, 1f)); questStep.X = Num(array[3].text, 0f); questStep.Z = Num(array[4].text, 0f); questStep.Radius = Math.Max(2f, Num(array[5].text, 10f)); questStep.Text = array[6].text.Trim(); } quest.Rewards.Coins = Math.Max(0, (int)Num(V("coins"), 0f)); for (int j = 0; j < ItemFields.Count && j < quest.Rewards.Items.Count; j++) { InputField[] array2 = ItemFields[j]; quest.Rewards.Items[j] = new RewardItem { Item = array2[0].text.Trim(), Amount = Math.Max(1, (int)Num(array2[1].text, 1f)), Quality = Math.Max(1, (int)Num(array2[2].text, 1f)) }; } quest.Rewards.Skill = V("skill"); quest.Rewards.SkillXp = Num(V("skillXp"), 0f); quest.Rewards.Buff = V("buff"); quest.Rewards.BuffSeconds = Num(V("buffSeconds"), 0f); quest.Rewards.UnlockQuests = Csv(V("unlockQuests")); SetEventMessage(quest, "onAccept", V("onAccept")); SetEventMessage(quest, "onComplete", V("onComplete")); SetEventMessage(quest, "onTimeout", V("onTimeout")); } private static void Save() { Collect(); QuestDef quest = _quest; if (string.IsNullOrWhiteSpace(quest.Id)) { ClientState.Notify("Give the quest an id first."); return; } if (quest.Steps.Count == 0) { ClientState.Notify("Add at least one step."); return; } quest.Rewards.Items.RemoveAll((RewardItem i) => string.IsNullOrEmpty(i.Item)); ClientState.Send("admin.saveQuest", new AdminSaveQuestRequest { Quest = quest }, delegate(Response r) { ClientState.Notify(r.Message); if (r.Ok && IsOpen) { _quest = null; Build(); } }); } private static void Delete() { string text = _quest?.Id; if (string.IsNullOrEmpty(text)) { return; } ClientState.Send("admin.deleteQuest", new QuestIdRequest { QuestId = text }, delegate(Response r) { ClientState.Notify(r.Message); if (r.Ok && IsOpen) { _quest = null; Build(); } }); } } internal static class QuestJournal { private const float Width = 760f; private const float Height = 620f; private static GameObject _window; private static GameObject _tracker; private static Text _trackerText; private static int _closedFrame = -1; public static bool IsOpen => (Object)(object)_window != (Object)null; public static bool ClosedThisFrame => _closedFrame == Time.frameCount; public static void Tick() { //IL_0079: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } if (IsOpen && (((Character)localPlayer).IsDead() || ZInput.GetKeyDown((KeyCode)27, true))) { Close(); return; } if (!TextFocused() && !NpcPanel.IsOpen && !NpcEditor.IsOpen && !DialoguePanel.IsOpen && Enum.TryParse<KeyCode>(ClientState.Config?.Settings.Quests.JournalKey ?? "J", ignoreCase: true, out KeyCode result) && ZInput.GetKeyDown(result, true)) { if (IsOpen) { Close(); } else { Open(); } } EnsureTracker(); } private static bool TextFocused() { if (!Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible()) { if ((Object)(object)Chat.instance != (Object)null) { return Chat.instance.HasFocus(); } return false; } return true; } public static void Open() { if (!((Object)(object)GUIManager.CustomGUIFront == (Object)null)) { Close(); _window = PanelBuilder.Window(760f, 620f); ((Object)_window).name = "TSN_Journal"; GUIManager.BlockInput(true); Draw(); } } public static void Close() { if (!((Object)(object)_window == (Object)null)) { Object.Destroy((Object)(object)_window); _window = null; _closedFrame = Time.frameCount; GUIManager.BlockInput(false); } } public static void Refresh() { if (IsOpen) { Draw(); } UpdateTracker(); } private static void Draw() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) foreach (Transform item in _window.transform) { Transform val = item; if (((Object)val).name == "Body") { Object.Destroy((Object)(object)((Component)val).gameObject); } } GameObject val2 = new GameObject("Body", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(_window.transform, false); RectTransform component = val2.GetComponent<RectTransform>(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; Vector2 offsetMin = (component.offsetMax = Vector2.zero); component.offsetMin = offsetMin; Transform transform = val2.transform; PlayerStateView state = QuestClient.State; PanelBuilder.Label(transform, $"Quest Journal ({state.Active.Count}/{state.MaxActive})", 20f, 18f, 640f, 40f, 28, GUIManager.Instance.ValheimOrange, (TextAnchor)4); PanelBuilder.Button(transform, "X", 690f, 18f, 46f, 38f, Close); float contentWidth; RectTransform content = PanelBuilder.ScrollList(transform, 20f, 70f, 720f, 530f, out contentWidth); if (state.Active.Count == 0) { PanelBuilder.AddRow(content, contentWidth, new Row { Label = "No active quests. Talk to a quest giver." }); } foreach (ActiveQuestView item2 in state.Active) { ActiveQuestView quest = item2; string text = (item2.ExpiresUtc.HasValue ? (" · " + Left(item2.ExpiresUtc.Value)) : ""); PanelBuilder.AddRow(content, contentWidth, new Row { Header = true, Label = item2.Name + text, ButtonText = "Abandon", OnClick = delegate { ClientState.Send("quest.abandon", new QuestIdRequest { QuestId = quest.Id }, delegate(Response r) { ClientState.Notify(r.Message); }); } }); if (!string.IsNullOrEmpty(item2.Description)) { PanelBuilder.AddRow(content, contentWidth, new Row { Label = " " + item2.Description }); } foreach (StepView step in item2.Steps) { int num = (QuestStepTypes.IsLive(step.Type) ? step.Have : Mathf.Min(step.Need, InventoryHelper.Count(step.Target))); bool flag = (QuestStepTypes.IsLive(step.Type) ? step.Done : (num >= step.Need)); PanelBuilder.AddRow(content, contentWidth, new Row { Label = string.Format(" {0} {1} {2}/{3}", flag ? "✓" : "•", step.Text, num, step.Need) }); } string text2 = ClientState.Config?.GetProfile(item2.TurnIn)?.Name ?? item2.TurnIn; if (!string.IsNullOrEmpty(text2)) { PanelBuilder.AddRow(content, contentWidth, new Row { Label = " Hand in to " + text2 }); } } if (state.Completed.Count <= 0) { return; } PanelBuilder.AddRow(content, contentWidth, new Row { Header = true, Label = $"Completed ({state.Completed.Count})" }); foreach (KeyValuePair<string, DateTime> item3 in state.Completed.OrderByDescending((KeyValuePair<string, DateTime> k) => k.Value)) { string arg = ClientState.Config?.GetQuest(item3.Key)?.Name ?? item3.Key; PanelBuilder.AddRow(content, contentWidth, new Row { Label = $" {arg} · {item3.Value.ToLocalTime():d MMM}" }); } } private static string Left(DateTime expiresUtc) { TimeSpan timeSpan = expiresUtc - DateTime.UtcNow; if (timeSpan.TotalSeconds <= 0.0) { return "expired"; } if (!(timeSpan.TotalHours >= 1.0)) { return $"{timeSpan.Minutes}m left"; } return $"{(int)timeSpan.TotalHours}h {timeSpan.Minutes}m left"; } private static void EnsureTracker() { //IL_0039: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_tracker != (Object)null) && !((Object)(object)GUIManager.CustomGUIBack == (Object)null)) { _tracker = GUIManager.Instance.CreateText("", GUIManager.CustomGUIBack.transform, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-170f, -120f), GUIManager.Instance.AveriaSerifBold, 15, GUIManager.Instance.ValheimBeige, true, Color.black, 320f, 200f, false); ((Object)_tracker).name = "TSN_Tracker"; _trackerText = _tracker.GetComponent<Text>(); _trackerText.alignment = (TextAnchor)0; ((Graphic)_trackerText).raycastTarget = false; UpdateTracker(); } } private static void UpdateTracker() { if ((Object)(object)_trackerText == (Object)null) { return; } StringBuilder stringBuilder = new StringBuilder(); string text = FactionClient.Names(); if (text.Length > 0) { stringBuilder.AppendLine("<color=#9ad0ff>" + text + "</color>"); } foreach (ActiveQuestView item in QuestClient.State.Active.Take(3)) { stringBuilder.AppendLine("<color=#ffb84d>" + item.Name + "</color>"); foreach (StepView step in item.Steps) { int num = (QuestStepTypes.IsLive(step.Type) ? step.Have : Mathf.Min(step.Need, InventoryHelper.Count(step.Target))); stringBuilder.AppendLine(string.Format(" {0} {1} {2}/{3}", (num >= step.Need) ? "✓" : "•", step.Text, num, step.Need)); } } _trackerText.text = stringBuilder.ToString(); } } internal static class ServiceViews { public static string[] Tabs(NpcProfile p) { switch (p.Role) { case "gambler": return new string[1] { "Roll" }; case "buffer": return new string[1] { "Blessings" }; case "info": if (p.Pages.Count != 0) { return p.Pages.Select(Title).ToArray(); } return new string[1] { "Info" }; case "feedback": return new string[1] { "Feedback" }; default: return null; } } public static void Rows(NpcProfile p, int tab, List<Row> rows, Action<int, bool, Action<int, bool>> pay) { switch (p.Role) { case "gambler": Gambler(p, rows, pay); break; case "buffer": Buffer(p, rows, pay); break; case "info": Info(p, tab, rows); break; case "feedback": Feedback(rows); break; } } private static void Refresh(Response r) { if (!string.IsNullOrEmpty(r.Message)) { ClientState.Notify(r.Message); } NpcPanel.Refresh(); } private static void Gambler(NpcProfile p, List<Row> rows, Action<int, bool, Action<int, bool>> pay) { GamblerTable t = p.Gambler; List<LootEntry> list = t.Loot.Where((LootEntry l) => !string.IsNullOrEmpty(l.Item) && l.Weight > 0f).ToList(); float num = list.Sum((LootEntry l) => l.Weight); rows.Add(new Row { Header = true, Label = $"One roll costs {t.Price} coins" + ((t.DailyLimit > 0) ? $" · {t.DailyLimit} rolls a day" : ""), ButtonText = "Roll", OnClick = delegate { pay(t.Price, arg2: false, delegate(int paid, bool bank) { ClientState.Send("gambler.roll", new GamblerRollRequest { ProfileId = p.Id, PaidCoins = paid, FromBank = bank }, Refresh); }); } }); foreach (LootEntry item in list.OrderByDescending((LootEntry l) => l.Weight)) { rows.Add(new Row { Icon = InventoryHelper.Icon(item.Item), Label = $"{item.Amount}x {InventoryHelper.DisplayName(item.Item)}" + ((item.Quality > 1) ? $" ★{item.Quality}" : "") + $" {item.Weight / num * 100f:0.#}%" }); } if (list.Count == 0) { rows.Add(new Row { Label = "The prize table is empty." }); } } private static void Buffer(NpcProfile p, List<Row> rows, Action<int, bool, Action<int, bool>> pay) { ConfigBundle config = ClientState.Config; for (int i = 0; i < p.Buffs.Count; i++) { BuffOffer o = p.Buffs[i]; int index = i; BuffDef buff = config.GetBuff(o.Buff); bool flag = !string.IsNullOrWhiteSpace(o.Requires) && (!((Object)(object)ZoneSystem.instance != (Object)null) || !ZoneSystem.instance.GetGlobalKey(o.Requires.Trim())); string text = buff?.Name ?? o.Buff; string text2 = ((o.Seconds >= 3600f) ? $"{o.Seconds / 3600f:0.#} h" : $"{o.Seconds / 60f:0} min"); rows.Add(new Row { Icon = ((buff != null) ? InventoryHelper.Icon(buff.Icon) : null), Label = text + " — " + text2 + " — " + ((o.Price > 0) ? (o.Price + " coins") : "free") + (flag ? " (locked)" : ""), ButtonText = (flag ? null : "Bless me"), OnClick = (flag ? null : ((Action<int>)delegate { pay(o.Price, arg2: false, delegate(int paid, bool bank) { ClientState.Send("buffer.buy", new BufferBuyRequest { ProfileId = p.Id, Index = index, PaidCoins = paid, FromBank = bank }, Refresh); }); })) }); if (buff != null && !string.IsNullOrEmpty(buff.Description)) { rows.Add(new Row { Label = " " + buff.Description }); } } if (p.Buffs.Count == 0) { rows.Add(new Row { Label = "No blessings on offer." }); } } private static void Info(NpcProfile p, int tab, List<Row> rows) { Dictionary<string, string> source = ClientState.Config?.InfoPages ?? new Dictionary<string, string>(); if (p.Pages.Count == 0 || tab < 0 || tab >= p.Pages.Count) { rows.Add(new Row { Label = "This NPC has no pages set. Add 'pages: [name]' to its profile and info/name.md on the server." }); return; } string key = p.Pages[tab]; string value = source.FirstOrDefault((KeyValuePair<string, string> kv) => string.Equals(kv.Key, key, StringComparison.OrdinalIgnoreCase)).Value; if (string.IsNullOrEmpty(value)) { rows.Add(new Row { Label = "Page '" + key + "' wasn't found in the server's info folder." }); return; } string[] array = value.Replace("\r", "").Split(new char[1] { '\n' }); for (int num = 0; num < array.Length; num++) { string text = array[num].TrimEnd(Array.Empty<char>()); if (text.Length == 0) { rows.Add(new Row { Label = "" }); continue; } bool flag = text.StartsWith("#"); if (flag) { text = text.TrimStart(new char[1] { '#' }).Trim(); } foreach (string item in Wrap(text, 95)) { rows.Add(new Row { Label = item, Header = flag }); } } } private static void Feedback(List<Row> rows) { List<string> obj = ClientState.Config?.Settings.Feedback.Categories ?? new List<string> { "Bug", "Player report", "Idea" }; rows.Add(new Row { Header = true, Label = "Tell the admins what happened. Type below, then pick a category to send." }); string text = ""; rows.Add(new Row { Label = "Your message", OnText = delegate(string t) { text = t; } }); foreach (string item in obj) { string category = item; rows.Add(new Row { Label = "Send as: " + item, ButtonText = "Send", OnClick = delegate { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_004b: 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) Vector3 val = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero); ClientState.Send("feedback.submit", new FeedbackRequest { Category = category, Text = text, X = val.x, Z = val.z }, delegate(Response r) { ClientState.Notify(r.Message); if (r.Ok) { NpcPanel.Close(); } }); } }); } } private static string Title(string page) { if (!string.IsNullOrEmpty(page)) { return char.ToUpper(page[0]) + page.Substring(1).Replace('_', ' '); } return "Info"; } private static IEnumerable<string> Wrap(string line, int width) { if (line.Length <= width) { yield return line; yield break; } string[] array = line.Split(new char[1] { ' ' }); string text = ""; string[] array2 = array; foreach (string w in array2) { if (text.Length + w.Length + 1 > width && text.Length > 0) { yield return text; text = ""; } text = ((text.Length == 0) ? w : (text + " " + w)); } if (text.Length > 0) { yield return text; } } } internal static class Views { private static MarketState _market; private static BankStateView _bank; public static void OnOpen(NpcProfile p) { _market = null; _bank = null; if (p.Role == "marketplace") { ClientState.Send("market.browse", null, OnMarket); } if (p.Role == "banker") { ClientState.Send("bank.state", null, OnBank); } WorldViews.OnOpen(p); if (p.Role == "chronicler") { ChroniclerView.OnOpen(); } } public static string[] Tabs(NpcProfile p) { return p.Role switch { "marketplace" => new string[3] { "Browse", "Sell", "My listings" }, "trader" => new string[2] { "Buy", "Sell" }, "banker" => new string[2] { "Coins", "Vault" }, "teleporter" => new string[1] { "Destinations" }, "quest" => new string[2] { "Quests", "In progress" }, "chronicler" => ChroniclerView.Tabs(), _ => ServiceViews.Tabs(p) ?? WorldViews.Tabs(p) ?? new string[1] { "Talk" }, }; } public static bool UsesPayment(NpcProfile p) { if (!(p.Role == "marketplace") && !(p.Role == "trader") && !(p.Role == "teleporter") && !(p.Role == "gambler") && !(p.Role == "buffer") && !(p.Role == "transmog")) { return p.Role == "chronicler"; } return true; } public static string Status(NpcProfile p) { if (p.Role == "quest") { return $"Journal: {QuestClient.State.Active.Count}/{QuestClient.State.MaxActive} quests"; } string text = $"You carry {InventoryHelper.CountCoins()} coins"; BankStateView bank = _bank; int? num = ((bank != null) ? new int?(bank.Coins) : _market?.BankCoins); if (num.HasValue) { text += $" · Bank: {num.Value} coins"; } if (p.Role == "marketplace" && _market != null) { text += $" · Sale tax {_market.SaleTaxPercent:0.#}%"; } return text; } public static void Rows(NpcProfile p, int tab, List<Row> rows) { switch (p.Role) { case "marketplace": switch (tab) { case 0: MarketBrowse(rows); break; case 1: MarketSell(rows); break; default: MarketMine(rows); break; } break; case "trader": if (tab == 0) { TraderBuy(p, rows); } else { TraderSell(p, rows); } break; case "banker": if (tab == 0) { BankCoins(rows); } else { BankVault(rows); } break; case "teleporter": Teleports(p, rows); break; case "quest": if (tab == 0) { QuestsAvailable(p, rows); } else { QuestsActive(p, rows); } break; case "leaderboard": case "transmog": WorldViews.Rows(p, tab, rows, Pay); break; case "chronicler": ChroniclerView.Rows(tab, rows, Pay); break; default: ServiceViews.Rows(p, tab, rows, delegate(int price, bool _, Action<int, bool> send) { Pay(price, send); }); break; } } private static void QuestsAvailable(NpcProfile p, List<Row> rows) { ConfigBundle config = ClientState.Config; PlayerStateView state = QuestClient.State; ConditionContext conditionContext = QuestClient.Conditions(); if (ClientState.IsLocalAdmin) { rows.Add(new Row { Header = true, Label = "Admin: make or edit this NPC's quests", ButtonText = "Quest Designer", OnClick = delegate { QuestDesigner.Open(); } }); } foreach (QuestDef item in config.Quests.Where((QuestDef q) => string.Equals(q.Giver, p.Id, StringComparison.OrdinalIgnoreCase))) { if (state.IsActive(item.Id)) { continue; } string text = QuestRules.Availability(item, state, conditionContext.HasKey, DateTime.UtcNow); if (text == "Already completed." || text == "Not unlocked yet.") { continue; } QuestDef quest = item; rows.Add(new Row { Header = true, Label = item.Name + ((text != null) ? (" (" + text + ")") : ""), ButtonText = ((text == null) ? "Accept" : null), OnClick = ((text == null) ? ((Action<int>)delegate { ClientState.Send("quest.accept", new QuestAcceptRequest { QuestId = quest.Id, ProfileId = p.Id }, OnQuest); }) : null) }); if (!string.IsNullOrEmpty(item.Description)) { rows.Add(new Row { Label = " " + item.Description }); } foreach (QuestStep step in item.Steps) { rows.Add(new Row { Label = " • " + QuestRules.Describe(step) }); } string text2 = RewardText(item.Rewards); if (text2.Length > 0) { rows.Add(new Row { Label = " Reward: " + text2 }); } } if (!rows.Any((Row r) => r.Header && r.ButtonText != "Quest Designer")) { rows.Add(new Row { Label = "Nothing to offer right now." }); } } private static void QuestsActive(NpcProfile p, List<Row> rows) { foreach (ActiveQuestView item in QuestClient.State.Active) { ActiveQuestView quest = item; bool flag = string.IsNullOrEmpty(item.TurnIn) || string.Equals(item.TurnIn, p.Id, StringComparison.OrdinalIgnoreCase); bool ready = flag && QuestClient.CanTurnIn(item); rows.Add(new Row { Header = true, Label = item.Name + (flag ? "" : " (hand in elsewhere)"), ButtonText = (ready ? "Turn in" : "Abandon"), OnClick = delegate { if (ready) { QuestClient.TurnIn(quest, p.Id, OnQuest); } else { ClientState.Send("quest.abandon", new QuestIdRequest { QuestId = quest.Id }, OnQuest); } } }); foreach (StepView step in item.Steps) { int num = (QuestStepTypes.IsLive(step.Type) ? step.Have : Mathf.Min(step.Need, InventoryHelper.Count(step.Target))); rows.Add(new Row { Label = string.Format(" {0} {1} {2}/{3}", (num >= step.Need) ? "✓" : "•", step.Text, num, step.Need) }); } } if (rows.Count == 0) { rows.Add(new Row { Label = "No quests in progress." }); } } private static void OnQuest(Response r) { if (!string.IsNullOrEmpty(r.Message)) { ClientState.Notify(r.Message); } NpcPanel.Refresh(); } private static string RewardText(QuestRewards rw) { List<string> list = new List<string>(); foreach (RewardItem item in rw.Items) { list.Add($"{item.Amount}x {InventoryHelper.DisplayName(item.Item)}"); } if (rw.Coins > 0) { list.Add($"{rw.Coins} coins" + (rw.CoinsToBank ? " (to bank)" : "")); } if (!string.IsNullOrEmpty(rw.Skill) && rw.SkillXp > 0f) { list.Add(rw.Skill + " experience"); } if (!string.IsNullOrEmpty(rw.Buff)) { list.Add(rw.Buff); } return string.Join(", ", list); } private static void OnMarket(Response r) { if (r.State != null) { _market = ((JToken)r.State).ToObject<MarketState>(); } if (!string.IsNullOrEmpty(r.Message)) { ClientState.Notify(r.Message); } NpcPanel.Refresh(); } private static void PayoutRow(List<Row> rows) { if (_market != null && (_market.PayoutCoins != 0 || _market.PayoutItems != 0)) { rows.Add(new Row { Header = true, Label = $"Waiting for you: {_market.PayoutCoins} coins, {_market.PayoutItems} returned item(s)", ButtonText = "Collect", OnClick = delegate { ClientState.Send("market.collect", null, OnMarket); } }); } } private static void MarketBrowse(List<Row> rows) { if (_market == null) { rows.Add(new Row { Label = "Loading listings..." }); return; } PayoutRow(rows); foreach (ListingView listing2 in _market.Listings) { ListingView listing = listing2; rows.Add(new Row { Icon = InventoryHelper.Icon(listing2.Item.Prefab), Label = $"{ItemLabel(listing2.Item)} — {listing2.Price} coins · {listing2.SellerName} · {TimeLeft(listing2.HoursLeft)}", ButtonText = (listing2.Mine ? "Cancel" : "Buy"), OnClick = delegate { if (listing.Mine) { ClientState.Send("market.cancel", new MarketIdRequest { ListingId = listing.Id }, OnMarket); } else { Pay(listing.Price, delegate(int paid, bool bank) { ClientState.Send("market.buy", new MarketBuyRequest { ListingId = listing.Id, Price = listing.Price, PaidCoins = paid, FromBank = bank }, OnMarket); }); } } }); } } private static void MarketSell(List<Row> rows) { float feePct = ClientState.Config.Settings.Marketplace.ListingFeePercent; rows.Add(new Row { Header = true, Label = $"Type a price and press List. Fee {feePct:0.#}% now, tax {ClientState.Config.Settings.Marketplace.SaleTaxPercent:0.#}% when it sells." }); Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null) { return; } string currency = InventoryHelper.Currency; foreach (ItemData item in (from i in val.GetAllItems() where !i.m_equipped orderby i.m_shared.m_name select i).ToList()) { if (string.Equals(InventoryHelper.PrefabName(item), currency, StringComparison.OrdinalIgnoreCase)) { continue; } ItemData target = item; rows.Add(new Row { Icon = item.GetIcon(), Label = ItemLabel(InventoryHelper.ToRecord(item)), InputPlaceholder = "price", ButtonText = "List", OnClick = delegate(int price) { if (price < 1) { ClientState.Notify("Type a price first."); } else { int num = (int)Math.Ceiling((float)price * feePct / 100f); if (!InventoryHelper.TakeCoins(num)) { ClientState.Notify($"Listing costs a {num} coin fee."); } else { ItemRecord itemRecord = InventoryHelper.TakeStack(target); if (itemRecord == null) { InventoryHelper.Give(ItemRecord.Plain(currency, num)); ClientState.Notify("That item is no longer in your inventory."); NpcPanel.Refresh(); } else { ClientState.Send("market.list", new MarketListRequest { Item = itemRecord, Price = price, PaidFee = num }, OnMarket); } } } } }); } } private static void MarketMine(List<Row> rows) { if (_market == null) { rows.Add(new Row { Label = "Loading listings..." }); return;