using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; using UnityEngine.UI; using ValheimMMO.Cities; using ValheimMMO.Config; using ValheimMMO.Drops; using ValheimMMO.Dungeons; using ValheimMMO.Hunger; using ValheimMMO.InventorySlots; using ValheimMMO.Items; using ValheimMMO.Merchant; using ValheimMMO.Progression; using ValheimMMO.Pvp; using ValheimMMO.Quests; using ValheimMMO.UI; [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(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ValheimMMO")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.4.0")] [assembly: AssemblyInformationalVersion("0.3.4")] [assembly: AssemblyProduct("ValheimMMO")] [assembly: AssemblyTitle("ValheimMMO")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.3.4.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 ValheimMMO { [BepInPlugin("com.lucas.valheimmmo", "Mayheim", "0.3.4")] public class Plugin : BaseUnityPlugin { public const string PluginId = "com.lucas.valheimmmo"; public const string PluginName = "Mayheim"; public const string PluginVersion = "0.3.4"; internal static ManualLogSource Log; internal static Plugin Instance; private Harmony _harmony; private void Awake() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; ModConfig.Bind(((BaseUnityPlugin)this).Config); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); ((Component)this).gameObject.AddComponent(); _harmony = new Harmony("com.lucas.valheimmmo"); PatchIsolated(); Log.LogInfo((object)"Mayheim v0.3.4 carregado."); } private void PatchIsolated() { int num = 0; int num2 = 0; Type[] typesFromAssembly = AccessTools.GetTypesFromAssembly(typeof(Plugin).Assembly); foreach (Type type in typesFromAssembly) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { try { _harmony.CreateClassProcessor(type).Patch(); num++; } catch (Exception ex) { num2++; Log.LogError((object)("Falha ao aplicar patch " + type.FullName + ": " + ex.Message)); } } } Log.LogInfo((object)($"Patches aplicados: {num} classes" + ((num2 > 0) ? $", {num2} com falha (ver acima)" : "."))); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace ValheimMMO.UI { [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] internal static class GameCamera_UpdateMouseCapture_Patch { private static void Postfix() { if (QuestNpcUI.IsOpen || MmoUI.PanelOpen) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } } } internal sealed class ClonedBar { public GameObject Root; public RectTransform Rect; public GuiBar[] Bars; public TMP_Text Text; public Animator Animator; public bool Alive => (Object)(object)Root != (Object)null; public static ClonedBar From(RectTransform source, string name, bool fixedText) { //IL_00b3: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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_0105: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null) { return null; } GameObject val = Object.Instantiate(((Component)source).gameObject, ((Transform)source).parent); ((Object)val).name = name; ClonedBar clonedBar = new ClonedBar { Root = val, Rect = val.GetComponent(), Bars = val.GetComponentsInChildren(true), Text = val.GetComponentInChildren(true), Animator = (val.GetComponent() ?? val.GetComponentInChildren(true)) }; if ((Object)(object)clonedBar.Text != (Object)null && fixedText) { TMP_Text text = clonedBar.Text; text.enableAutoSizing = false; text.fontSize = 15f; text.textWrappingMode = (TextWrappingModes)0; text.overflowMode = (TextOverflowModes)0; text.alignment = (TextAlignmentOptions)514; ((Graphic)text).color = Color.white; text.fontStyle = (FontStyles)(text.fontStyle | 1); RectTransform rectTransform = text.rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = new Vector2(8f, 0f); rectTransform.offsetMax = new Vector2(-8f, 0f); } val.SetActive(true); return clonedBar; } public void SetVisible(bool visible) { if (Alive) { if (Root.activeSelf != visible) { Root.SetActive(visible); } if (visible && (Object)(object)Animator != (Object)null) { Animator.SetBool("Visible", true); } } } public void SetLength(float size, float borderBuffer) { if (Alive) { size = Mathf.Ceil(size); Rect.SetSizeWithCurrentAnchors((Axis)0, size + borderBuffer); GuiBar[] bars = Bars; for (int i = 0; i < bars.Length; i++) { bars[i].SetWidth(size); } } } public void SetValue(float value, float max, Color color) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (Alive) { for (int i = 0; i < Bars.Length; i++) { Bars[i].SetMaxValue(max); Bars[i].SetValue(Mathf.Clamp(value, 0f, max)); Bars[i].SetColor((i == 0) ? color : (color * new Color(0.55f, 0.55f, 0.55f, 1f))); } } } } internal static class HudBars { private static ClonedBar _xp; private static readonly Color XpColor = new Color(0.42f, 0.55f, 0.95f); internal static void Update(Hud hud) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)hud == (Object)null || (Object)(object)localPlayer == (Object)null) { return; } PlayerProgress playerProgress = PlayerProgress.Get(localPlayer); if (playerProgress == null) { return; } bool flag = ModConfig.ShowXpBar.Value && !InventoryGui.IsVisible(); if (_xp == null || !_xp.Alive) { if (!flag) { return; } _xp = ClonedBar.From(hud.m_staminaBar2Root, "MMO_XpBar", fixedText: true); if (_xp == null) { return; } } _xp.SetVisible(flag); if (flag) { _xp.Rect.anchoredPosition = new Vector2(ModConfig.XpBarOffsetX.Value, ModConfig.XpBarOffsetY.Value); _xp.SetLength(Mathf.Max(120f, ModConfig.XpBarWidth.Value), 16f); bool flag2 = playerProgress.Level >= ModConfig.MaxLevel.Value; float num = (flag2 ? 1f : playerProgress.LevelProgress); _xp.SetValue(num, 1f, XpColor); if ((Object)(object)_xp.Text != (Object)null) { _xp.Text.text = (flag2 ? $"Lv {playerProgress.Level} MAX" : $"Lv {playerProgress.Level} {playerProgress.Xp} / {playerProgress.XpForNextLevel} {num * 100f:0}%"); } } } } internal static class HudHungerBar { private static ClonedBar _bar; internal static void Update(Hud hud, Player player) { //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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) if ((Object)(object)hud == (Object)null || (Object)(object)player == (Object)null || (Object)(object)hud.m_healthBarRoot == (Object)null) { return; } PlayerProgress playerProgress = PlayerProgress.Get(player); if (!ModConfig.ShowHungerBar.Value || !ModConfig.HungerEnabled.Value || playerProgress == null || !(playerProgress.Satiety >= 0f)) { if (_bar != null) { _bar.SetVisible(visible: false); } return; } HideFoodUi(hud); if (_bar == null || !_bar.Alive) { _bar = ClonedBar.From(hud.m_healthBarRoot, "MMO_HungerBar", fixedText: false); if (_bar == null) { return; } } _bar.SetVisible(visible: true); if ((Object)(object)hud.m_foodBarRoot != (Object)null) { ((Transform)_bar.Rect).position = ((Transform)hud.m_foodBarRoot).position; ((Transform)_bar.Rect).rotation = ((Transform)hud.m_foodBarRoot).rotation; } float max = HungerSystem.Max; HungerState s = HungerSystem.StateOf(playerProgress.Satiety); _bar.SetLength(max / 25f * 32f, 0f); _bar.SetValue(playerProgress.Satiety, max, HungerSystem.ColorOf(s)); if ((Object)(object)_bar.Text != (Object)null) { _bar.Text.text = Mathf.CeilToInt(playerProgress.Satiety).ToString(); } if ((Object)(object)hud.m_foodIcon != (Object)null) { ((Graphic)hud.m_foodIcon).color = HungerSystem.ColorOf(s); } } private static void HideFoodUi(Hud hud) { if (hud.m_foodBars != null) { Image[] foodBars = hud.m_foodBars; foreach (Image val in foodBars) { if ((Object)(object)val == (Object)null) { continue; } if (((Behaviour)val).enabled) { ((Behaviour)val).enabled = false; } Graphic[] componentsInChildren = ((Component)val).GetComponentsInChildren(true); foreach (Graphic val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null && ((Behaviour)val2).enabled) { ((Behaviour)val2).enabled = false; } } } } if (hud.m_foodIcons != null) { Image[] foodBars = hud.m_foodIcons; foreach (Image val3 in foodBars) { if ((Object)(object)val3 != (Object)null && ((Component)val3).gameObject.activeSelf) { ((Component)val3).gameObject.SetActive(false); } } } if (hud.m_foodTime != null) { TMP_Text[] foodTime = hud.m_foodTime; foreach (TMP_Text val4 in foodTime) { if ((Object)(object)val4 != (Object)null && ((Component)val4).gameObject.activeSelf) { ((Component)val4).gameObject.SetActive(false); } } } if ((Object)(object)hud.m_foodBaseBar != (Object)null && ((Component)hud.m_foodBaseBar).gameObject.activeSelf) { ((Component)hud.m_foodBaseBar).gameObject.SetActive(false); } if ((Object)(object)hud.m_foodText != (Object)null && ((Component)hud.m_foodText).gameObject.activeSelf) { ((Component)hud.m_foodText).gameObject.SetActive(false); } if ((Object)(object)hud.m_foodBarRoot != (Object)null && ((Component)hud.m_foodBarRoot).gameObject.activeSelf) { ((Component)hud.m_foodBarRoot).gameObject.SetActive(false); } object obj2; if (!((Object)(object)hud.m_healthPanel != (Object)null)) { RectTransform healthBarRoot = hud.m_healthBarRoot; Transform obj = ((healthBarRoot != null) ? ((Transform)healthBarRoot).parent : null); obj2 = ((obj is RectTransform) ? obj : null); } else { obj2 = hud.m_healthPanel; } RectTransform val5 = (RectTransform)obj2; if ((Object)(object)val5 != (Object)null) { for (int k = 0; k < ((Transform)val5).childCount; k++) { Transform child = ((Transform)val5).GetChild(k); string name = ((Object)child).name; bool num = name.Length == 5 && name.StartsWith("food", StringComparison.OrdinalIgnoreCase) && char.IsDigit(name[4]); bool flag = name.StartsWith("foodicon (", StringComparison.OrdinalIgnoreCase); if ((num || flag) && ((Component)child).gameObject.activeSelf) { ((Component)child).gameObject.SetActive(false); } } } if ((Object)(object)hud.m_foodIcon != (Object)null) { if (!((Component)hud.m_foodIcon).gameObject.activeSelf) { ((Component)hud.m_foodIcon).gameObject.SetActive(true); } if (!((Behaviour)hud.m_foodIcon).enabled) { ((Behaviour)hud.m_foodIcon).enabled = true; } } } } [HarmonyPatch(typeof(Hud), "Update")] internal static class Hud_Update_BarsPatch { private static void Postfix(Hud __instance) { HudBars.Update(__instance); } } [HarmonyPatch(typeof(Hud), "UpdateFood")] internal static class Hud_UpdateFood_HungerPatch { private static void Postfix(Hud __instance, Player player) { HudHungerBar.Update(__instance, player); } } public class MmoUI : MonoBehaviour { internal static MmoUI Instance; internal static bool PanelOpen; private const float PanelWidth = 470f; private Rect _window = new Rect(80f, 80f, 470f, 0f); private Vector2 _equipScroll; private bool _showEquipList = true; private string _respecReason; private static readonly Color BgColor = new Color(0.075f, 0.06f, 0.045f, 0.94f); private static readonly Color BorderColor = new Color(0.72f, 0.58f, 0.3f, 1f); private static readonly Color TextColor = new Color(0.92f, 0.86f, 0.7f, 1f); private static readonly Color GoldColor = new Color(0.95f, 0.78f, 0.4f, 1f); private static readonly Color MutedColor = new Color(0.65f, 0.6f, 0.5f, 1f); private static readonly Color ItemBonusColor = new Color(1f, 0.62f, 0.2f, 1f); private static readonly Color ButtonColor = new Color(0.22f, 0.17f, 0.11f, 1f); private static readonly Color ButtonHover = new Color(0.32f, 0.25f, 0.15f, 1f); private static readonly Color BarBg = new Color(0f, 0f, 0f, 0.6f); private static readonly Color XpFill = new Color(0.42f, 0.55f, 0.95f, 1f); private static GUISkin _skin; private static Texture2D _white; private static Font _norse; private static bool _fontSearched; private static string Gold => ColorUtility.ToHtmlStringRGB(GoldColor); private static string Muted => ColorUtility.ToHtmlStringRGB(MutedColor); private static string Bonus => ColorUtility.ToHtmlStringRGB(ItemBonusColor); private static Texture2D White { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_white == (Object)null) { _white = Solid(Color.white); } return _white; } } private static Font NorseFont { get { if (_fontSearched) { return _norse; } _fontSearched = true; try { Font[] array = Resources.FindObjectsOfTypeAll(); foreach (Font val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("norse", StringComparison.OrdinalIgnoreCase) >= 0) { _norse = val; break; } } } catch { } return _norse; } } internal static Texture2D WhiteTex => White; internal static Color GoldC => GoldColor; internal static Color MutedC => MutedColor; internal static Color BorderC => BorderColor; internal static Color BonusC => ItemBonusColor; internal static GUISkin Skin { get { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_004c: 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_0060: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: 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_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Expected O, but got Unknown //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Expected O, but got Unknown //IL_025e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_skin != (Object)null) { return _skin; } GUISkin val = ScriptableObject.CreateInstance(); ((Object)val).hideFlags = (HideFlags)61; GUISkin skin = GUI.skin; Font norseFont = NorseFont; val.window = new GUIStyle(skin.window) { padding = new RectOffset(16, 16, 28, 14), fontSize = 16, alignment = (TextAnchor)1 }; Texture2D background = Bordered(BgColor, BorderColor); val.window.normal.background = background; val.window.onNormal.background = background; val.window.normal.textColor = GoldColor; val.window.onNormal.textColor = GoldColor; val.window.border = new RectOffset(3, 3, 3, 3); if ((Object)(object)norseFont != (Object)null) { val.window.font = norseFont; } val.label = new GUIStyle(skin.label) { fontSize = 14, richText = true }; val.label.normal.textColor = TextColor; if ((Object)(object)norseFont != (Object)null) { val.label.font = norseFont; } val.button = new GUIStyle(skin.button) { fontSize = 14, richText = true }; val.button.normal.background = Bordered(ButtonColor, BorderColor, 12, 1); val.button.hover.background = Bordered(ButtonHover, GoldColor, 12, 1); val.button.active.background = Bordered(ButtonHover, GoldColor, 12, 1); val.button.normal.textColor = TextColor; val.button.hover.textColor = GoldColor; val.button.active.textColor = GoldColor; val.button.border = new RectOffset(2, 2, 2, 2); if ((Object)(object)norseFont != (Object)null) { val.button.font = norseFont; } val.scrollView = new GUIStyle(skin.scrollView); val.verticalScrollbar = new GUIStyle(skin.verticalScrollbar); val.verticalScrollbarThumb = new GUIStyle(skin.verticalScrollbarThumb); val.verticalScrollbarThumb.normal.background = Solid(BorderColor); _skin = val; return _skin; } } private static bool AttachedToInventory { get { if (ModConfig.PanelOpensWithInventory.Value && InventoryGui.IsVisible()) { return !PanelOpen; } return false; } } internal static bool Visible { get { if ((Object)(object)Player.m_localPlayer != (Object)null) { if (!PanelOpen) { return AttachedToInventory; } return true; } return false; } } private static Texture2D Solid(Color c) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: 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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, c); val.Apply(); ((Object)val).hideFlags = (HideFlags)61; return val; } private static Texture2D Bordered(Color fill, Color border, int size = 12, int thickness = 2) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(size, size, (TextureFormat)4, false); for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { bool flag = j < thickness || i < thickness || j >= size - thickness || i >= size - thickness; val.SetPixel(j, i, flag ? border : fill); } } val.Apply(); ((Object)val).hideFlags = (HideFlags)61; return val; } private void Awake() { Instance = this; } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = ModConfig.OpenPanelKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && CanShowUi()) { PanelOpen = !PanelOpen; } if (PanelOpen && (Object)(object)Player.m_localPlayer == (Object)null) { PanelOpen = false; } } private static bool CanShowUi() { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return false; } if (Console.IsVisible() || TextInput.IsVisible()) { return false; } if (Menu.IsVisible() || InventoryGui.IsVisible()) { return false; } return true; } private void OnGUI() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: 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_00f7: Expected O, but got Unknown //IL_00f2: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_00b9: 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) if (Visible) { GUISkin skin = GUI.skin; Matrix4x4 matrix = GUI.matrix; GUI.skin = Skin; float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 2.5f); GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(num, num, 1f)); float num2 = (float)Screen.width / num; if (AttachedToInventory) { Rect val = default(Rect); ((Rect)(ref val))..ctor((num2 - 470f) * 0.5f, 34f, 470f, 0f); GUILayout.Window(((Object)this).GetInstanceID(), val, new WindowFunction(DrawPanel), "PERSONAGEM", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(470f) }); } else { _window = GUILayout.Window(((Object)this).GetInstanceID(), _window, new WindowFunction(DrawPanel), "PERSONAGEM", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(470f) }); } GUI.matrix = matrix; GUI.skin = skin; } } private void DrawPanel(int id) { //IL_027e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; PlayerProgress playerProgress = PlayerProgress.Get(localPlayer); if (playerProgress == null) { return; } bool flag = playerProgress.Level >= ModConfig.MaxLevel.Value; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + localPlayer.GetPlayerName() + "", Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label($"Level {playerProgress.Level}" + (flag ? "" : $" / {ModConfig.MaxLevel.Value}"), Array.Empty()); GUILayout.EndHorizontal(); GUILayout.Space(6f); DrawXpBar(playerProgress, flag); if (ModConfig.PvpEnabled.Value) { float num = Karma.Local(localPlayer); string arg = ((num >= ModConfig.WantedThreshold.Value) ? "E04A3A" : Muted); GUILayout.Label($"Karma {num:0.0} · {Karma.StatusName(num)}", Array.Empty()); } GUILayout.Space(12f); Separator(); GUILayout.Space(8f); DrawAttributes(localPlayer, playerProgress); GUILayout.Space(10f); Separator(); GUILayout.Space(8f); DrawEquipment(localPlayer); GUILayout.Space(10f); Separator(); GUILayout.Space(8f); string reason; bool flag2 = PlayerProgress.CanRespec(localPlayer, out reason); GUI.enabled = ModConfig.RespecEnabled.Value && playerProgress.SpentPoints > 0 && flag2; if (GUILayout.Button("Redistribuir todos os pontos", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) })) { if (playerProgress.RespecAll(localPlayer, out _respecReason)) { RefreshStats(localPlayer); ((Character)localPlayer).Message((MessageType)2, "Pontos redistribuidos", 0, (Sprite)null); } else { ((Character)localPlayer).Message((MessageType)2, _respecReason, 0, (Sprite)null); } } GUI.enabled = true; if (!flag2 && ModConfig.RespecEnabled.Value) { GUILayout.Label("" + reason + "", Array.Empty()); } if (PanelOpen) { GUILayout.Space(6f); if (GUILayout.Button("Fechar", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { PanelOpen = false; } GUI.DragWindow(new Rect(0f, 0f, 10000f, 26f)); } } private void DrawAttributes(Player player, PlayerProgress prog) { int unspentPoints = prog.UnspentPoints; GUILayout.Label((unspentPoints > 0) ? string.Format("{1} ponto{2} para distribuir", Gold, unspentPoints, (unspentPoints == 1) ? "" : "s") : ("Nenhum ponto disponivel"), Array.Empty()); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("ATRIBUTO", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(126f) }); GUILayout.Label("PONTOS", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); GUILayout.Space(62f); GUILayout.Label("ITENS", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); GUILayout.FlexibleSpace(); GUILayout.Label("EFEITO", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); GUILayout.EndHorizontal(); prog.ComputeStats(player, out var hp, out var stamina, out var eitr); float maxCarryWeight = player.GetMaxCarryWeight(); int[] array = ItemBonuses.TotalEquipped((Humanoid)(object)player); Attr[] all = AttrInfo.All; foreach (Attr attr in all) { int num = (int)attr; GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); GUILayout.Label("" + AttrInfo.DisplayName(attr) + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(126f) }); GUILayout.Label($"{prog.Spent[num]}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); GUI.enabled = unspentPoints > 0; if (GUILayout.Button("+", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(28f), GUILayout.Height(24f) })) { prog.TrySpend(attr, player); RefreshStats(player); } GUI.enabled = prog.Spent[num] > 0 && ModConfig.RespecEnabled.Value; if (GUILayout.Button("-", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(28f), GUILayout.Height(24f) })) { if (!prog.TryRefund(attr, player, out _respecReason)) { ((Character)player).Message((MessageType)2, _respecReason, 0, (Sprite)null); } else { RefreshStats(player); } } GUI.enabled = true; GUILayout.Label((array[num] > 0) ? $"+{array[num]}" : ("-"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); GUILayout.FlexibleSpace(); GUILayout.Label("" + AttrInfo.Effect(attr) + " " + ValueFor(attr, hp, stamina, eitr, maxCarryWeight) + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); GUILayout.EndHorizontal(); } } private void DrawEquipment(Player player) { //IL_04f5: Unknown result type (might be due to invalid IL or missing references) //IL_0525: Unknown result type (might be due to invalid IL or missing references) //IL_052a: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } List equippedItems = inventory.GetEquippedItems(); GUILayout.Label(string.Format("Equipamento {1} peca{2}", Muted, equippedItems.Count, (equippedItems.Count == 1) ? "" : "s"), Array.Empty()); GUILayout.Space(2f); EquipmentRow.Slot[] slots = EquipmentRow.Slots; foreach (EquipmentRow.Slot slot in slots) { ItemData val = EquipmentRow.ItemIn(inventory, slot); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }); GUILayout.Label("" + slot.Name + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(78f) }); if (val == null) { GUILayout.Label("vazio", Array.Empty()); GUILayout.EndHorizontal(); continue; } DrawIcon(val, 24f); GUILayout.Space(6f); string text = Localization.instance.Localize(val.m_shared.m_name); string text2 = ((val.m_shared.m_maxQuality > 1) ? $" q{val.m_quality}" : ""); string text3 = ((val.m_stack > 1) ? $" x{val.m_stack}" : ""); GUILayout.Label(text + text2 + text3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); int[] bonuses = ItemBonuses.For(val); GUILayout.Label(ItemBonuses.Any(bonuses) ? ("" + ItemBonuses.Describe(bonuses) + "") : ("-"), Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Tirar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(52f), GUILayout.Height(22f) })) { ((Humanoid)player).UnequipItem(val, true); RefreshStats(player); } GUILayout.EndHorizontal(); } List list = EquipmentRow.OverflowItems(inventory); if (list.Count > 0) { GUILayout.Space(4f); GUILayout.Label($"Sem espaco na mochila ({list.Count}): abra espaco e clique em Guardar.", Array.Empty()); foreach (ItemData item in list) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }); DrawIcon(item, 22f); GUILayout.Space(6f); GUILayout.Label(Localization.instance.Localize(item.m_shared.m_name), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); GUILayout.FlexibleSpace(); if (GUILayout.Button("Guardar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(70f), GUILayout.Height(22f) }) && !EquipmentRow.TryStore(inventory, item)) { ((Character)player).Message((MessageType)2, "Mochila cheia.", 0, (Sprite)null); } if (GUILayout.Button("Equipar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(70f), GUILayout.Height(22f) })) { ((Humanoid)player).EquipItem(item, true); RefreshStats(player); } GUILayout.EndHorizontal(); } } GUILayout.Space(6f); List list2 = new List(); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && allItem.IsEquipable() && !allItem.m_equipped) { list2.Add(allItem); } } list2.Sort(delegate(ItemData a, ItemData b) { int num = SlotOrder(a).CompareTo(SlotOrder(b)); return (num == 0) ? string.Compare(a.m_shared.m_name, b.m_shared.m_name, StringComparison.Ordinal) : num; }); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(_showEquipList ? "▾ Equipar da mochila" : "▸ Equipar da mochila", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { _showEquipList = !_showEquipList; } GUILayout.Label(string.Format("{1} disponivel{2}", Muted, list2.Count, (list2.Count == 1) ? "" : "is"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUILayout.EndHorizontal(); if (!_showEquipList || list2.Count == 0) { return; } _equipScroll = GUILayout.BeginScrollView(_equipScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Min(160f, 28f * (float)list2.Count + 6f)) }); foreach (ItemData item2 in list2) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }); DrawIcon(item2, 22f); GUILayout.Space(6f); string text4 = Localization.instance.Localize(item2.m_shared.m_name); string text5 = ((item2.m_shared.m_maxQuality > 1) ? $" q{item2.m_quality}" : ""); int[] bonuses2 = ItemBonuses.For(item2); string text6 = (ItemBonuses.Any(bonuses2) ? (" " + ItemBonuses.Describe(bonuses2) + "") : ""); if (GUILayout.Button("" + text4 + text5 + text6 + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { ((Humanoid)player).EquipItem(item2, true); RefreshStats(player); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private static void DrawXpBar(PlayerProgress prog, bool atCap) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_00ff: 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) Rect rect = GUILayoutUtility.GetRect(1f, 20f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.color = BorderColor; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x - 1f, ((Rect)(ref rect)).y - 1f, ((Rect)(ref rect)).width + 2f, ((Rect)(ref rect)).height + 2f), (Texture)(object)White); GUI.color = BarBg; GUI.DrawTexture(rect, (Texture)(object)White); float num = (atCap ? 1f : prog.LevelProgress); GUI.color = XpFill; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width * num, ((Rect)(ref rect)).height), (Texture)(object)White); GUI.color = Color.white; GUIStyle val = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4, fontSize = 12, fontStyle = (FontStyle)1 }; val.normal.textColor = Color.white; string text = (atCap ? "LEVEL MAXIMO" : $"{prog.Xp} / {prog.XpForNextLevel} XP ({num * 100f:0}%)"); GUI.Label(rect, text, val); } private static void Separator() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(1f, 1f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.color = new Color(BorderColor.r, BorderColor.g, BorderColor.b, 0.5f); GUI.DrawTexture(rect, (Texture)(object)White); GUI.color = Color.white; } private static void DrawIcon(ItemData item, float size) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(size, size, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(size), GUILayout.Height(size) }); Sprite val = null; try { val = item.GetIcon(); } catch { } if (!((Object)(object)val == (Object)null) && !((Object)(object)val.texture == (Object)null)) { Texture2D texture = val.texture; Rect textureRect = default(Rect); try { textureRect = val.textureRect; } catch { ((Rect)(ref textureRect))..ctor(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height); } Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height); GUI.DrawTextureWithTexCoords(rect, (Texture)(object)texture, val2, true); } } private static int SlotOrder(ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected I4, but got Unknown ItemType itemType = item.m_shared.m_itemType; return (itemType - 3) switch { 0 => 0, 11 => 0, 19 => 0, 1 => 0, 2 => 1, 3 => 2, 4 => 3, 8 => 4, 14 => 5, 15 => 6, 21 => 7, 12 => 8, 16 => 9, _ => 20, }; } private static string SlotName(ItemData item) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected I4, but got Unknown ItemType itemType = item.m_shared.m_itemType; return (itemType - 3) switch { 0 => "Arma", 11 => "Arma 2M", 19 => "Arma 2M", 1 => "Arco", 2 => "Escudo", 3 => "Cabeca", 4 => "Peito", 8 => "Pernas", 14 => "Capa", 15 => "Utilidade", 21 => "Amuleto", 12 => "Tocha", 16 => "Ferramenta", 6 => "Municao", _ => ((object)Unsafe.As(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString(), }; } private static string ValueFor(Attr attr, float hp, float stam, float eitr, float carry) { return attr switch { Attr.Vitality => $"{hp:0}", Attr.Endurance => $"{stam:0}", Attr.Spirit => $"{eitr:0}", Attr.Conditioning => $"{carry:0} kg", _ => "", }; } internal static void RefreshStats(Player player) { if (!((Object)(object)player == (Object)null)) { player.UpdateFood(0f, true); } } } [HarmonyPatch(typeof(Player), "TakeInput")] internal static class Player_TakeInput_Patch { private static void Postfix(ref bool __result) { if (MmoUI.PanelOpen) { __result = false; } } } } namespace ValheimMMO.Quests { internal static class QuestListUI { public static void Draw(Player player, int tier, string header) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) string text = ColorUtility.ToHtmlStringRGB(MmoUI.MutedC); string text2 = ColorUtility.ToHtmlStringRGB(MmoUI.GoldC); string text3 = ColorUtility.ToHtmlStringRGB(MmoUI.BonusC); GUILayout.Label("" + header + "", Array.Empty()); GUILayout.Space(6f); List list = QuestSystem.TodayFor(player, tier); if (list.Count == 0) { GUILayout.Label("Sem missoes hoje.", Array.Empty()); } foreach (Quest item in list) { GUILayout.BeginVertical(Array.Empty()); string text4 = (item.Claimed ? ("feita hoje") : ((!item.Accepted) ? ("disponivel") : (item.Complete ? ("completa - fale comigo") : $"{item.Progress}/{item.Required}"))); GUILayout.Label("" + item.Title + " " + text4 + "", Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label($"Recompensa: {item.GoldReward} moedas, {item.XpReward} XP", Array.Empty()); GUILayout.FlexibleSpace(); string message = null; if (item.Claimed) { GUI.enabled = false; GUILayout.Button("Feita hoje", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(110f), GUILayout.Height(22f) }); GUI.enabled = true; } else if (!item.Accepted) { if (GUILayout.Button("Aceitar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(110f), GUILayout.Height(22f) })) { QuestSystem.Accept(player, item, out message); } } else if (item.Complete) { if (GUILayout.Button("Concluir", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(110f), GUILayout.Height(22f) })) { QuestSystem.Claim(player, item, out message); } } else { if (item.Kind == QuestKind.Deliver && GUILayout.Button("Entregar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(110f), GUILayout.Height(22f) })) { QuestSystem.Deliver(player, item, out message); } if (GUILayout.Button("Abandonar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(90f), GUILayout.Height(22f) })) { QuestSystem.Abandon(player, item, out message); } } if (message != null) { ((Character)player).Message((MessageType)2, message, 0, (Sprite)null); } GUILayout.EndHorizontal(); GUILayout.Space(8f); GUILayout.EndVertical(); } } } public class QuestNpcUI : MonoBehaviour { internal static bool IsOpen; private static int _tier; private const float W = 560f; public static void Open(int tier) { _tier = tier; IsOpen = true; } private void Update() { if (IsOpen && ((Object)(object)Player.m_localPlayer == (Object)null || Input.GetKeyDown((KeyCode)27))) { IsOpen = false; } } private void OnGUI() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Expected O, but got Unknown //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (IsOpen && !((Object)(object)Player.m_localPlayer == (Object)null)) { GUISkin skin = GUI.skin; Matrix4x4 matrix = GUI.matrix; GUI.skin = MmoUI.Skin; float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 2.5f); GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(num, num, 1f)); float num2 = (float)Screen.width / num; CityDef cityDef = CitySystem.ByTier(_tier); string text = ((cityDef != null) ? ("MISSOES - " + cityDef.Name.ToUpperInvariant()) : "MISSOES"); GUILayout.Window(((Object)this).GetInstanceID(), new Rect((num2 - 560f) * 0.5f, 120f, 560f, 0f), new WindowFunction(Draw), text, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(560f) }); GUI.matrix = matrix; GUI.skin = skin; } } private void Draw(int id) { QuestListUI.Draw(Player.m_localPlayer, _tier, $"Dia {QuestSystem.CurrentDay()} · aceite, cumpra e volte aqui para concluir. Cada missao vale uma vez por dia; amanha troca. Recompensa sempre em ouro."); GUILayout.Space(4f); if (GUILayout.Button("Fechar", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { IsOpen = false; } } } [HarmonyPatch(typeof(Player), "TakeInput")] internal static class Player_TakeInput_QuestNpcPatch { private static void Postfix(ref bool __result) { if (QuestNpcUI.IsOpen) { __result = false; } } } internal enum QuestKind { Kill, Deliver, DungeonBoss } internal sealed class Quest { public int Tier; public int Index; public QuestKind Kind; public string Target; public string TargetName; public int Required; public int Progress; public bool Accepted; public bool Claimed; public int GoldReward; public long XpReward; public bool Complete => Progress >= Required; public bool Active { get { if (Accepted) { return !Claimed; } return false; } } public string Title => Kind switch { QuestKind.Kill => $"Mate {Required}x {TargetName}", QuestKind.Deliver => $"Entregue {Required}x {TargetName}", QuestKind.DungeonBoss => "Derrote o boss de dungeon: " + TargetName, _ => "", }; public string ShortTitle => Kind switch { QuestKind.Kill => TargetName ?? "", QuestKind.Deliver => "Entregar " + TargetName, QuestKind.DungeonBoss => "Boss: " + TargetName, _ => "", }; } internal static class QuestSystem { private sealed class DayCache { public int Day; public List List; } public const int MaxTier = 7; private static readonly string[][] KillPools = new string[7][] { new string[4] { "Greyling", "Boar", "Neck", "Deer" }, new string[5] { "Greydwarf", "Skeleton", "Greydwarf_Shaman", "Troll", "Ghost" }, new string[6] { "Draugr", "Blob", "Leech", "Wraith", "Abomination", "Surtling" }, new string[5] { "Wolf", "Fenring", "Hatchling", "StoneGolem", "Ulv" }, new string[5] { "Goblin", "Deathsquito", "Lox", "GoblinShaman", "GoblinBrute" }, new string[5] { "Seeker", "Tick", "Gjall", "Dverger", "SeekerBrute" }, new string[5] { "Charred_Melee", "Charred_Archer", "Asksvin", "Morgen", "Volture" } }; private static readonly string[][] DeliverPools = new string[7][] { new string[6] { "Wood", "Stone", "Resin", "LeatherScraps", "RawMeat", "Raspberry" }, new string[6] { "FineWood", "CopperOre", "TinOre", "Bronze", "TrollHide", "Coal" }, new string[6] { "IronScrap", "Guck", "Bloodbag", "Entrails", "Turnip", "Ooze" }, new string[6] { "WolfPelt", "Silver", "Obsidian", "FreezeGland", "Crystal", "Onion" }, new string[6] { "BlackMetalScrap", "Barley", "Flax", "LoxPelt", "Needle", "Tar" }, new string[6] { "Carapace", "YggdrasilWood", "Sap", "Softtissue", "BlackMarble", "Mandible" }, new string[5] { "FlametalOreNew", "CharredBone", "Grausten", "Blackwood", "AskHide" } }; private static readonly Dictionary Cache = new Dictionary(); public static int CurrentDay() { if (!((Object)(object)EnvMan.instance != (Object)null)) { return 0; } return EnvMan.instance.GetDay(); } private static string KeyFor(int tier) { return $"mmo.quests.{tier}"; } public static List TodayFor(Player player, int tier) { if (!ModConfig.QuestsEnabled.Value || (Object)(object)player == (Object)null) { return new List(); } tier = Mathf.Clamp(tier, 1, KillPools.Length); int num = tier - 1; int num2 = CurrentDay(); if (Cache.TryGetValue(tier, out var value) && value.Day == num2) { if (value.List.Count > 0 && value.List[0].Progress == 0 && !value.List[0].Accepted) { LoadProgress(player, tier, value.List); } return value.List; } Random random = new Random((int)((((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldUID() : 0) ^ ((long)num2 * 7919L) ^ ((long)tier * 104729L))); List list = new List(); string text = PickExisting(random, KillPools[num], IsCreature); if (text != null) { Character component = ZNetScene.instance.GetPrefab(text).GetComponent(); float num3 = Mathf.Max(1f, component.m_health); int num4 = 8 + random.Next(0, 8); int num5 = Mathf.CeilToInt(Mathf.Pow(num3, 0.6f) * (float)num4 * 1.5f * ModConfig.QuestRewardMultiplier.Value); list.Add(new Quest { Tier = tier, Index = 0, Kind = QuestKind.Kill, Target = text, TargetName = Localization.instance.Localize(component.m_name), Required = num4, GoldReward = num5, XpReward = (long)num5 * 3L }); } string text2 = PickExisting(random, DeliverPools[num], IsItem); if (text2 != null) { int num6 = 10 + random.Next(0, 11); int num7 = Mathf.CeilToInt((float)(MerchantPricing.PriceOf(text2) * num6) * 1.5f * ModConfig.QuestRewardMultiplier.Value); list.Add(new Quest { Tier = tier, Index = 1, Kind = QuestKind.Deliver, Target = text2, TargetName = Localization.instance.Localize(ObjectDB.instance.GetItemPrefab(text2).GetComponent().m_itemData.m_shared.m_name), Required = num6, GoldReward = num7, XpReward = (long)num7 * 3L }); } DungeonTier dungeonTier = DungeonBosses.TierById(Mathf.Clamp(tier, 1, DungeonBosses.Tiers.Length)); if (dungeonTier != null) { int num8 = Mathf.CeilToInt((float)DungeonBosses.GoldFor(dungeonTier) * 0.5f * ModConfig.QuestRewardMultiplier.Value); list.Add(new Quest { Tier = tier, Index = 2, Kind = QuestKind.DungeonBoss, Target = dungeonTier.Id.ToString(), TargetName = dungeonTier.Title, Required = 1, GoldReward = num8, XpReward = (long)num8 * 3L }); } Cache[tier] = new DayCache { Day = num2, List = list }; LoadProgress(player, tier, list); return list; } public static List Active(Player player) { List list = new List(); if ((Object)(object)player == (Object)null || !ModConfig.QuestsEnabled.Value) { return list; } for (int i = 1; i <= 7; i++) { foreach (Quest item in TodayFor(player, i)) { if (item.Active) { list.Add(item); } } } return list; } public static void ClearCache() { Cache.Clear(); } private static bool IsCreature(string name) { ZNetScene instance = ZNetScene.instance; object obj; if (instance == null) { obj = null; } else { GameObject prefab = instance.GetPrefab(name); obj = ((prefab != null) ? prefab.GetComponent() : null); } return (Object)obj != (Object)null; } private static bool IsItem(string name) { ObjectDB instance = ObjectDB.instance; object obj; if (instance == null) { obj = null; } else { GameObject itemPrefab = instance.GetItemPrefab(name); obj = ((itemPrefab != null) ? itemPrefab.GetComponent() : null); } return (Object)obj != (Object)null; } private static string PickExisting(Random rng, string[] pool, Func exists) { List list = new List(); foreach (string text in pool) { if (exists(text)) { list.Add(text); } } if (list.Count != 0) { return list[rng.Next(list.Count)]; } return null; } private static void LoadProgress(Player player, int tier, List quests) { foreach (Quest quest in quests) { quest.Progress = 0; quest.Claimed = false; quest.Accepted = false; } if (!player.m_customData.TryGetValue(KeyFor(tier), out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split('|'); if (array.Length < 1 || !int.TryParse(array[0], out var result) || result != CurrentDay()) { return; } foreach (Quest quest2 in quests) { int num = 1 + quest2.Index * 3; if (num + 2 < array.Length) { quest2.Accepted = array[num] == "1"; int.TryParse(array[num + 1], out quest2.Progress); quest2.Claimed = array[num + 2] == "1"; } } } private static void SaveProgress(Player player, int tier) { if (!Cache.TryGetValue(tier, out var value)) { return; } List list = value.List; List list2 = new List { CurrentDay().ToString(CultureInfo.InvariantCulture) }; int idx; for (idx = 0; idx < 3; idx++) { Quest quest = list.Find((Quest x) => x.Index == idx); list2.Add((quest != null && quest.Accepted) ? "1" : "0"); list2.Add((quest?.Progress ?? 0).ToString(CultureInfo.InvariantCulture)); list2.Add((quest != null && quest.Claimed) ? "1" : "0"); } player.m_customData[KeyFor(tier)] = string.Join("|", list2); } public static bool Accept(Player player, Quest q, out string message) { message = null; if (q.Accepted || q.Claimed) { return false; } int count = Active(player).Count; int num = Mathf.Max(1, ModConfig.QuestMaxActive.Value); if (count >= num) { message = $"Voce ja tem {count} missoes em andamento (maximo {num})."; return false; } q.Accepted = true; SaveProgress(player, q.Tier); message = "Missao aceita: " + q.Title; return true; } public static bool Abandon(Player player, Quest q, out string message) { message = null; if (!q.Active) { return false; } q.Accepted = false; q.Progress = 0; SaveProgress(player, q.Tier); message = "Missao abandonada."; return true; } public static void OnKill(string victimPrefab, int dungeonTier) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !ModConfig.QuestsEnabled.Value) { return; } for (int i = 1; i <= KillPools.Length; i++) { List list = TodayFor(localPlayer, i); bool flag = false; foreach (Quest item in list) { if (item.Active && !item.Complete && ((item.Kind == QuestKind.Kill && item.Target == victimPrefab) || (item.Kind == QuestKind.DungeonBoss && dungeonTier > 0 && item.Target == dungeonTier.ToString()))) { item.Progress++; flag = true; if (item.Complete) { ((Character)localPlayer).Message((MessageType)2, "Missao completa: " + item.Title + ". Volte ao emissario.", 0, (Sprite)null); } else { ((Character)localPlayer).Message((MessageType)1, $"{item.ShortTitle}: {item.Progress}/{item.Required}", 0, (Sprite)null); } } } if (flag) { SaveProgress(localPlayer, i); } } } public static bool Deliver(Player player, Quest q, out string message) { message = null; if (q.Kind != QuestKind.Deliver || !q.Active) { return false; } Inventory inventory = ((Humanoid)player).GetInventory(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(q.Target); ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent() : null); if ((Object)(object)val == (Object)null) { return false; } string name = val.m_itemData.m_shared.m_name; int num = Mathf.Min(inventory.CountItems(name, -1, true), q.Required - q.Progress); if (num <= 0) { message = "Voce nao tem esse item na mochila."; return false; } inventory.RemoveItem(name, num, -1, true); q.Progress += num; SaveProgress(player, q.Tier); if (q.Complete) { return Claim(player, q, out message); } message = $"Entregue {num}. Faltam {q.Required - q.Progress}."; return true; } public static bool Claim(Player player, Quest q, out string message) { message = null; if (q.Claimed || !q.Accepted || !q.Complete) { message = "Missao ainda nao concluida."; return false; } Inventory inventory = ((Humanoid)player).GetInventory(); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Coins"); ItemDrop val = ((itemPrefab != null) ? itemPrefab.GetComponent() : null); if ((Object)(object)val == (Object)null) { message = "Moeda indisponivel."; return false; } int num = q.GoldReward; int num2 = Mathf.Max(1, val.m_itemData.m_shared.m_maxStackSize); while (num > 0) { int num3 = Mathf.Min(num2, num); if (inventory.AddItem(((Object)((Component)val).gameObject).name, num3, val.m_itemData.m_quality, val.m_itemData.m_variant, 0L, "", false) == null) { message = "Sem espaco na mochila para a recompensa."; return false; } num -= num3; } KillXpNetwork.Award(player, q.XpReward, null); q.Claimed = true; SaveProgress(player, q.Tier); message = $"Missao concluida! {q.GoldReward} moedas e {q.XpReward} XP"; return true; } } [HarmonyPatch(typeof(Player), "Load")] internal static class Player_Load_QuestCachePatch { private static void Postfix() { QuestSystem.ClearCache(); } } public class QuestTracker : MonoBehaviour { private const float W = 250f; private float _refresh; private List _active = new List(); private void Update() { _refresh -= Time.unscaledDeltaTime; if (!(_refresh > 0f)) { _refresh = 1f; Player localPlayer = Player.m_localPlayer; _active = (((Object)(object)localPlayer != (Object)null && ModConfig.QuestTrackerEnabled.Value) ? QuestSystem.Active(localPlayer) : new List()); } } private void OnGUI() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.QuestTrackerEnabled.Value || _active.Count == 0 || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Hud.instance == (Object)null || !Hud.instance.m_rootObject.activeSelf || Menu.IsVisible() || InventoryGui.IsVisible() || StoreGui.IsVisible() || Minimap.IsOpen() || Console.IsVisible()) { return; } GUISkin skin = GUI.skin; Matrix4x4 matrix = GUI.matrix; GUI.skin = MmoUI.Skin; float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 2.5f); GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(num, num, 1f)); float num2 = (float)Screen.width / num; string text = ColorUtility.ToHtmlStringRGB(MmoUI.MutedC); string text2 = ColorUtility.ToHtmlStringRGB(MmoUI.GoldC); GUILayout.BeginArea(new Rect(num2 - 250f - ModConfig.QuestTrackerOffsetX.Value, ModConfig.QuestTrackerOffsetY.Value, 250f, 0f)); GUILayout.Label("MISSOES", Array.Empty()); foreach (Quest item in _active) { string text3 = CitySystem.ByTier(item.Tier)?.Name ?? BiomeTiers.Name(item.Tier); string text4 = (item.Complete ? ("completa") : $"{item.Progress}/{item.Required}"); GUILayout.Label("" + item.ShortTitle + " " + text4 + "\n" + text3 + "", Array.Empty()); } GUILayout.EndArea(); GUI.matrix = matrix; GUI.skin = skin; } } } namespace ValheimMMO.Pvp { internal static class Karma { private const string Key = "mmo.karma"; public const string ZdoKey = "mmo_karma"; private const string RpcKill = "ValheimMMO_KarmaKill"; private static float _syncTimer; private static float _lastSynced = -1f; public static float Of(Player p) { if ((Object)(object)p == (Object)null) { return 0f; } if ((Object)(object)p == (Object)(object)Player.m_localPlayer) { return Local(p); } ZDO obj = (((Object)(object)((Character)p).m_nview != (Object)null && ((Character)p).m_nview.IsValid()) ? ((Character)p).m_nview.GetZDO() : null); if (obj == null) { return 0f; } return obj.GetFloat("mmo_karma", 0f); } public static float Local(Player p) { if (p.m_customData.TryGetValue("mmo.karma", out var value) && float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return 0f; } public static bool IsWanted(Player p) { if (ModConfig.PvpEnabled.Value) { return Of(p) >= ModConfig.WantedThreshold.Value; } return false; } public static bool IsOutlaw(Player p) { if (ModConfig.PvpEnabled.Value) { return Of(p) >= ModConfig.OutlawThreshold.Value; } return false; } public static string StatusName(float k) { if (k >= ModConfig.OutlawThreshold.Value) { return "Fora da lei"; } if (k >= ModConfig.WantedThreshold.Value) { return "Procurado"; } return "Limpo"; } public static void Set(Player p, float value, bool announce = true) { if ((Object)(object)p == (Object)null) { return; } float num = Local(p); value = Mathf.Clamp(value, 0f, ModConfig.KarmaMax.Value); p.m_customData["mmo.karma"] = value.ToString("R", CultureInfo.InvariantCulture); Sync(p, force: true); if (announce) { bool num2 = num >= ModConfig.WantedThreshold.Value; bool flag = value >= ModConfig.WantedThreshold.Value; if (!num2 && flag) { ((Character)p).Message((MessageType)2, "Voce agora e PROCURADO. Guardas vao atacar e as cidades fecham para voce.", 0, (Sprite)null); } if (num2 && !flag) { ((Character)p).Message((MessageType)2, "Seu nome foi limpo.", 0, (Sprite)null); } } } public static void Add(Player p, float delta, bool announce = true) { Set(p, Local(p) + delta, announce); } private static void Sync(Player p, bool force) { if (!((Object)(object)((Character)p).m_nview == (Object)null) && ((Character)p).m_nview.IsValid() && ((Character)p).m_nview.IsOwner()) { float num = Local(p); if (force || !(Mathf.Abs(num - _lastSynced) < 0.05f)) { ((Character)p).m_nview.GetZDO().Set("mmo_karma", num); _lastSynced = num; } } } internal static void Tick(Player p, float dt) { if (!ModConfig.PvpEnabled.Value || (Object)(object)p == (Object)null) { return; } float num = Local(p); if (num > 0f) { float num2 = Mathf.Max(0f, num - ModConfig.KarmaDecayPerMinute.Value / 60f * dt); if (num2 != num) { bool num3 = num >= ModConfig.WantedThreshold.Value && num2 < ModConfig.WantedThreshold.Value; p.m_customData["mmo.karma"] = num2.ToString("R", CultureInfo.InvariantCulture); if (num3) { ((Character)p).Message((MessageType)2, "Seu nome foi limpo.", 0, (Sprite)null); } } } _syncTimer += dt; if (_syncTimer >= 2f) { _syncTimer = 0f; Sync(p, force: false); } } internal static void OnMonsterKill(float victimMaxHp, bool boss) { Player localPlayer = Player.m_localPlayer; if (ModConfig.PvpEnabled.Value && !((Object)(object)localPlayer == (Object)null) && !(Local(localPlayer) <= 0f)) { float num = ModConfig.KarmaMonsterFactor.Value * Mathf.Pow(Mathf.Max(1f, victimMaxHp), 0.6f) * (boss ? 5f : 1f); Add(localPlayer, 0f - num); } } internal static void Register() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("ValheimMMO_KarmaKill", (Action)OnKillCredit); } } internal static void OnPlayerDied(Player victim) { if (!ModConfig.PvpEnabled.Value || (Object)(object)victim == (Object)null) { return; } float num = Local(victim); if (num > 0f) { Add(victim, 0f - ModConfig.KarmaDeathLoss.Value, announce: false); } HitData lastHit = ((Character)victim).m_lastHit; Character obj = ((lastHit != null) ? lastHit.GetAttacker() : null); Player val = (Player)(object)((obj is Player) ? obj : null); if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)victim) { return; } ZDO val2 = (((Object)(object)((Character)val).m_nview != (Object)null && ((Character)val).m_nview.IsValid()) ? ((Character)val).m_nview.GetZDO() : null); if (val2 == null) { return; } long owner = val2.GetOwner(); if ((Object)(object)val == (Object)(object)Player.m_localPlayer) { OnKillCredit(0L, victim.GetPlayerName(), num); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(owner, "ValheimMMO_KarmaKill", new object[2] { victim.GetPlayerName(), num }); } } private static void OnKillCredit(long sender, string victimName, float victimKarma) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !ModConfig.PvpEnabled.Value) { return; } if (victimKarma >= ModConfig.WantedThreshold.Value) { int num = Mathf.RoundToInt(victimKarma * ModConfig.BountyPerKarma.Value); if (num > 0) { KillXpNetwork.GiveGoldPublic(localPlayer, num, $"Recompensa por {victimName}: {num} moedas"); } } else { Add(localPlayer, ModConfig.KarmaPerKill.Value); ((Character)localPlayer).Message((MessageType)1, $"Assassinato de {victimName}: +{ModConfig.KarmaPerKill.Value:0} karma", 0, (Sprite)null); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class Game_Start_KarmaPatch { private static void Postfix() { Karma.Register(); } } [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class Player_UpdateStats_KarmaPatch { private static void Postfix(Player __instance, float dt) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Karma.Tick(__instance, dt); } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class Player_OnDeath_KarmaPatch { private static void Prefix(Player __instance) { if ((Object)(object)__instance != (Object)null && (Object)(object)((Character)__instance).m_nview != (Object)null && ((Character)__instance).m_nview.IsOwner()) { Karma.OnPlayerDied(__instance); } } } [HarmonyPatch(typeof(Player), "GetHoverName")] [HarmonyPriority(200)] internal static class Player_GetHoverName_KarmaPatch { private static void Postfix(Player __instance, ref string __result) { if (ModConfig.PvpEnabled.Value && Karma.IsWanted(__instance)) { __result = "☠ " + __result + ""; } } } [HarmonyPatch(typeof(StoreGui), "Show")] internal static class StoreGui_Show_KarmaPatch { private static bool Prefix(Trader trader) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !Karma.IsWanted(localPlayer)) { return true; } if (!MerchantSpawner.IsMerchant(trader)) { return true; } ((Character)localPlayer).Message((MessageType)2, "O mercador nao negocia com procurados.", 0, (Sprite)null); return false; } } [HarmonyPatch(typeof(TeleportWorld), "Teleport")] internal static class TeleportWorld_Teleport_KarmaPatch { private static bool Prefix(TeleportWorld __instance, Player player) { if ((Object)(object)player == (Object)null || !Karma.IsWanted(player)) { return true; } ZNetView component = ((Component)__instance).GetComponent(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val == null || !val.GetBool("mmo_city_portal", false)) { return true; } ((Character)player).Message((MessageType)2, "O portal da cidade rejeita procurados.", 0, (Sprite)null); return false; } } [HarmonyPatch(typeof(EnemyHud), "TestShow")] internal static class EnemyHud_TestShow_StealthPatch { private static void Postfix(Character c, ref bool __result) { if (__result && ModConfig.StealthHidesName.Value) { Player val = (Player)(object)((c is Player) ? c : null); if (val != null && (Object)(object)val != (Object)(object)Player.m_localPlayer && ((Character)val).IsCrouching()) { __result = false; } } } } } namespace ValheimMMO.Progression { public enum Attr { Vitality, Endurance, Spirit, Conditioning } public static class AttrInfo { public const int Count = 4; public static readonly Attr[] All = new Attr[4] { Attr.Vitality, Attr.Endurance, Attr.Spirit, Attr.Conditioning }; public static string DisplayName(Attr a) { return a switch { Attr.Vitality => "Vitalidade", Attr.Endurance => "Resistencia", Attr.Spirit => "Espirito", Attr.Conditioning => "Condicionamento", _ => a.ToString(), }; } public static string Short(Attr a) { return a switch { Attr.Vitality => "VIT", Attr.Endurance => "RES", Attr.Spirit => "ESP", Attr.Conditioning => "CON", _ => "?", }; } public static string Effect(Attr a) { return a switch { Attr.Vitality => "Vida maxima", Attr.Endurance => "Stamina maxima", Attr.Spirit => "Eitr maximo", Attr.Conditioning => "Peso maximo", _ => "", }; } } public static class ItemBonuses { public static bool Enabled => ModConfig.ItemBonusesEnabled.Value; public static int[] For(ItemData item) { if (item != null) { return For(item, item.m_quality, item.m_worldLevel); } return new int[4]; } public static int[] For(ItemData item, int quality, float worldLevel) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected I4, but got Unknown //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Invalid comparison between Unknown and I4 //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Invalid comparison between Unknown and I4 //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Invalid comparison between Unknown and I4 //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Invalid comparison between Unknown and I4 //IL_013c: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Invalid comparison between Unknown and I4 //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) int[] result = new int[4]; if (!Enabled || item?.m_shared == null) { return result; } SharedData shared = item.m_shared; quality = Mathf.Max(1, quality); ItemType itemType = shared.m_itemType; switch (itemType - 3) { default: if ((int)itemType != 22) { break; } goto case 0; case 3: case 4: case 8: { float armor2 = item.GetArmor(quality, worldLevel); Add(result, ArmorAttribute(shared), armor2 * ModConfig.ArmorToAttribute.Value); break; } case 14: { float armor = item.GetArmor(quality, worldLevel); Add(result, Attr.Conditioning, armor * ModConfig.ArmorToAttribute.Value); break; } case 2: { float blockPower = item.GetBlockPower(quality, 0f); Add(result, Attr.Vitality, blockPower * ModConfig.BlockToAttribute.Value); break; } case 0: case 1: case 11: { SkillType skillType = shared.m_skillType; DamageTypes damage; if ((int)skillType == 9 || (int)skillType == 10) { float value = ModConfig.MagicStaffBase.Value + ModConfig.MagicStaffPerQuality.Value * (float)(quality - 1); Add(result, Attr.Spirit, value); } else if ((int)skillType == 8 || (int)skillType == 14) { damage = item.GetDamage(quality, worldLevel); float totalDamage = ((DamageTypes)(ref damage)).GetTotalDamage(); Add(result, Attr.Endurance, totalDamage * ModConfig.RangedDamageToAttribute.Value); } else { damage = item.GetDamage(quality, worldLevel); float totalDamage2 = ((DamageTypes)(ref damage)).GetTotalDamage(); Add(result, Attr.Endurance, totalDamage2 * ModConfig.MeleeDamageToAttribute.Value); } break; } case 5: case 6: case 7: case 9: case 10: case 12: case 13: break; } return result; } private static void Add(int[] result, Attr attr, float value) { int num = Mathf.RoundToInt(value); if (num > 0) { result[(int)attr] += num; } } public static Attr ArmorAttribute(SharedData shared) { if (shared.m_eitrRegenModifier > 0f) { return Attr.Spirit; } if (shared.m_movementModifier <= ModConfig.HeavyArmorMovementThreshold.Value) { return Attr.Vitality; } return Attr.Endurance; } public static int[] TotalEquipped(Humanoid humanoid) { int[] array = new int[4]; if (!Enabled || (Object)(object)humanoid == (Object)null) { return array; } Inventory inventory = humanoid.GetInventory(); if (inventory == null) { return array; } foreach (ItemData equippedItem in inventory.GetEquippedItems()) { int[] array2 = For(equippedItem); for (int i = 0; i < array.Length; i++) { array[i] += array2[i]; } } return array; } public static bool Any(int[] bonuses) { if (bonuses == null) { return false; } for (int i = 0; i < bonuses.Length; i++) { if (bonuses[i] != 0) { return true; } } return false; } public static string Describe(int[] bonuses, bool shortNames = true) { List list = new List(); for (int i = 0; i < bonuses.Length; i++) { if (bonuses[i] != 0) { Attr a = (Attr)i; list.Add($"+{bonuses[i]} {(shortNames ? AttrInfo.Short(a) : AttrInfo.DisplayName(a))}"); } } return string.Join(", ", list); } } [HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(ItemData), typeof(int), typeof(bool), typeof(float), typeof(int) })] internal static class ItemData_GetTooltip_Patch { private static void Postfix(ItemData item, int qualityLevel, float worldLevel, ref string __result) { if (!ModConfig.ShowItemBonusTooltip.Value || !ItemBonuses.Enabled) { return; } int[] array = ItemBonuses.For(item, qualityLevel, worldLevel); if (!ItemBonuses.Any(array)) { return; } StringBuilder stringBuilder = new StringBuilder(__result); stringBuilder.Append("\n"); for (int i = 0; i < array.Length; i++) { if (array[i] != 0) { Attr a = (Attr)i; stringBuilder.Append($"\n{AttrInfo.DisplayName(a)}: +{array[i]}"); } } __result = stringBuilder.ToString(); } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] internal static class Humanoid_EquipItem_Patch { private static void Postfix(Humanoid __instance, bool __result) { if (__result) { RefreshIfLocal(__instance); } } internal static void RefreshIfLocal(Humanoid humanoid) { Player val = (Player)(object)((humanoid is Player) ? humanoid : null); if (val != null && (Object)(object)val == (Object)(object)Player.m_localPlayer) { val.UpdateFood(0f, true); } } } [HarmonyPatch(typeof(Humanoid), "UnequipItem")] internal static class Humanoid_UnequipItem_Patch { private static void Postfix(Humanoid __instance) { Humanoid_EquipItem_Patch.RefreshIfLocal(__instance); } } [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] internal static class Player_GetMaxCarryWeight_Patch { private static void Postfix(Player __instance, ref float __result) { PlayerProgress playerProgress = PlayerProgress.Get(__instance); if (playerProgress != null) { __result += playerProgress.CarryBonusKg(__instance); } } } public static class LevelCurve { public static long XpToNext(int level) { if (level < 1) { level = 1; } if (level >= ModConfig.MaxLevel.Value) { return 0L; } return (long)Math.Round((double)ModConfig.XpCurveFactor.Value * Math.Pow(level, ModConfig.XpCurveExponent.Value)); } public static long TotalXpForLevel(int level) { long num = 0L; for (int i = 1; i < level; i++) { num += XpToNext(i); } return num; } public static int PointsGrantedAt(int level) { return Math.Max(0, (level - 1) * ModConfig.PointsPerLevel.Value); } public static long XpForKill(Character victim) { if ((Object)(object)victim == (Object)null) { return 0L; } float num = 0f; try { num = victim.GetMaxHealth(); } catch { } if (num <= 0f) { num = victim.GetMaxHealthBase(); } if (num <= 0f) { return 0L; } int num2 = Math.Max(0, victim.GetLevel() - 1); double num3 = 1.0 + (double)(ModConfig.XpStarBonus.Value * (float)num2); double num4 = 1.0; if (victim.IsBoss()) { num4 = (DungeonBosses.IsDungeonBoss(victim, out var _) ? ModConfig.XpDungeonBossMultiplier.Value : ModConfig.XpBossMultiplier.Value); } return (long)Math.Ceiling((double)ModConfig.XpKillFactor.Value * Math.Pow(num, ModConfig.XpKillExponent.Value) * num3 * num4); } } internal static class LevelDisplay { public const string ZdoKey = "mmo_level"; public static void Sync(Player player, int level) { if (!((Object)(object)player == (Object)null) && !((Object)(object)((Character)player).m_nview == (Object)null) && ((Character)player).m_nview.IsValid() && ((Character)player).m_nview.IsOwner()) { ZDO zDO = ((Character)player).m_nview.GetZDO(); if (zDO.GetInt("mmo_level", -1) != level) { zDO.Set("mmo_level", level); } } } public static int LevelOf(Player player) { if ((Object)(object)player == (Object)null) { return 0; } if ((Object)(object)player == (Object)(object)Player.m_localPlayer) { return PlayerProgress.Get(player)?.Level ?? 0; } ZDO obj = (((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid()) ? ((Character)player).m_nview.GetZDO() : null); if (obj == null) { return 0; } return obj.GetInt("mmo_level", 0); } } [HarmonyPatch(typeof(Player), "GetHoverName")] internal static class Player_GetHoverName_LevelPatch { private static void Postfix(Player __instance, ref string __result) { if (ModConfig.ShowPlayerLevels.Value) { int num = LevelDisplay.LevelOf(__instance); if (num > 0) { __result = $"{__result} Lv {num}"; } } } } public class PlayerProgress { private const string CustomDataKey = "mmo.progression"; private const int Format = 2; public int Level = 1; public long Xp; public readonly int[] Spent = new int[4]; public float Satiety = -1f; private static readonly ConditionalWeakTable Cache = new ConditionalWeakTable(); public static PlayerProgress Local { get { if (!((Object)(object)Player.m_localPlayer != (Object)null)) { return null; } return Get(Player.m_localPlayer); } } public int TotalPoints => LevelCurve.PointsGrantedAt(Level); public int SpentPoints { get { int num = 0; for (int i = 0; i < Spent.Length; i++) { num += Spent[i]; } return num; } } public int UnspentPoints => Math.Max(0, TotalPoints - SpentPoints); public long XpForNextLevel => LevelCurve.XpToNext(Level); public float LevelProgress { get { long xpForNextLevel = XpForNextLevel; if (xpForNextLevel > 0) { return Math.Min(1f, (float)Xp / (float)xpForNextLevel); } return 1f; } } public static PlayerProgress Get(Player player) { if ((Object)(object)player == (Object)null) { return null; } if (Cache.TryGetValue(player, out var value)) { return value; } PlayerProgress playerProgress = new PlayerProgress(); playerProgress.LoadFrom(player); Cache.Add(player, playerProgress); return playerProgress; } public static void ClearCache(Player player) { if ((Object)(object)player != (Object)null) { Cache.Remove(player); } } public bool TrySpend(Attr attr, Player owner) { if (UnspentPoints <= 0) { return false; } Spent[(int)attr]++; SaveTo(owner); return true; } public bool TryRefund(Attr attr, Player owner, out string reason) { reason = null; if (!ModConfig.RespecEnabled.Value) { reason = "Respec esta desativado neste servidor."; return false; } if (Spent[(int)attr] <= 0) { reason = "Nenhum ponto investido nesse atributo."; return false; } if (!CanRespec(owner, out reason)) { return false; } Spent[(int)attr]--; SaveTo(owner); return true; } public bool RespecAll(Player owner, out string reason) { reason = null; if (!ModConfig.RespecEnabled.Value) { reason = "Respec esta desativado neste servidor."; return false; } if (SpentPoints == 0) { reason = "Nao ha pontos para redistribuir."; return false; } if (!CanRespec(owner, out reason)) { return false; } Array.Clear(Spent, 0, Spent.Length); SaveTo(owner); return true; } public static bool CanRespec(Player player, out string reason) { reason = null; if ((Object)(object)player == (Object)null) { reason = "Jogador invalido."; return false; } if (ModConfig.RespecRequiresShelter.Value && !player.InShelter()) { reason = "Voce precisa estar sob abrigo para redistribuir pontos."; return false; } int value = ModConfig.RespecMinComfort.Value; if (value > 0 && player.GetComfortLevel() < value) { reason = $"Conforto insuficiente ({player.GetComfortLevel()}/{value}). " + "Aproxime-se de uma fogueira e de moveis."; return false; } return true; } public int AddXp(long amount, Player owner) { if (amount <= 0) { return 0; } int value = ModConfig.MaxLevel.Value; if (Level >= value) { return 0; } Xp += amount; int num = 0; while (Level < value) { long num2 = LevelCurve.XpToNext(Level); if (num2 <= 0 || Xp < num2) { break; } Xp -= num2; Level++; num++; } if (Level >= value) { Xp = 0L; } SaveTo(owner); return num; } public int ApplyDeathPenalty(Player owner) { float num = ModConfig.DeathXpLossPercent.Value / 100f; if (num <= 0f) { return 0; } long num2 = (long)Math.Round((float)LevelCurve.XpToNext(Level) * num); if (num2 <= 0) { return 0; } Xp -= num2; int num3 = 0; if (ModConfig.DeathCanDelevel.Value) { while (Xp < 0 && Level > 1) { Level--; num3++; Xp += LevelCurve.XpToNext(Level); } } if (Xp < 0) { Xp = 0L; } if (num3 > 0) { ReclaimExcessPoints(); } SaveTo(owner); return num3; } private void ReclaimExcessPoints() { int num = SpentPoints - TotalPoints; while (num > 0) { int num2 = 0; for (int i = 1; i < Spent.Length; i++) { if (Spent[i] > Spent[num2]) { num2 = i; } } if (Spent[num2] > 0) { Spent[num2]--; num--; continue; } break; } } public int EffectivePoints(Attr attr, Player player) { int num = ItemBonuses.TotalEquipped((Humanoid)(object)player)[(int)attr]; return Spent[(int)attr] + num; } public void ComputeStats(Player player, out float hp, out float stamina, out float eitr) { int num = Math.Max(0, Level - 1); float num2 = (((Object)(object)player != (Object)null) ? player.m_baseHP : 25f); float num3 = (((Object)(object)player != (Object)null) ? player.m_baseStamina : 50f); int[] array = ItemBonuses.TotalEquipped((Humanoid)(object)player); hp = num2 + (float)num * ModConfig.GrowthHealthPerLevel.Value + (float)(Spent[0] + array[0]) * ModConfig.HealthPerVitality.Value; stamina = num3 + (float)num * ModConfig.GrowthStaminaPerLevel.Value + (float)(Spent[1] + array[1]) * ModConfig.StaminaPerEndurance.Value; eitr = (float)num * ModConfig.GrowthEitrPerLevel.Value + (float)(Spent[2] + array[2]) * ModConfig.EitrPerSpirit.Value; if (hp < 1f) { hp = 1f; } if (stamina < 1f) { stamina = 1f; } if (eitr < 0f) { eitr = 0f; } } public float CarryBonusKg(Player player) { int num = Math.Max(0, Level - 1); int num2 = Spent[3] + ItemBonuses.TotalEquipped((Humanoid)(object)player)[3]; return (float)num * ModConfig.GrowthCarryPerLevel.Value + (float)num2 * ModConfig.CarryPerConditioning.Value; } public void SaveTo(Player player) { if (!((Object)(object)player == (Object)null)) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; List list = new List { 2.ToString(invariantCulture), Level.ToString(invariantCulture), Xp.ToString(invariantCulture) }; for (int i = 0; i < 4; i++) { list.Add(Spent[i].ToString(invariantCulture)); } list.Add(Satiety.ToString("R", invariantCulture)); player.m_customData["mmo.progression"] = string.Join("|", list); LevelDisplay.Sync(player, Level); } } public void LoadFrom(Player player) { if ((Object)(object)player == (Object)null) { return; } if (!player.m_customData.TryGetValue("mmo.progression", out var value) || string.IsNullOrEmpty(value)) { Reset(); return; } try { CultureInfo invariantCulture = CultureInfo.InvariantCulture; string[] array = value.Split('|'); int num = int.Parse(array[0], invariantCulture); Level = Math.Max(1, int.Parse(array[1], invariantCulture)); Xp = Math.Max(0L, long.Parse(array[2], invariantCulture)); int num2 = ((num >= 2) ? 4 : 3); Array.Clear(Spent, 0, Spent.Length); for (int i = 0; i < num2 && i < 4; i++) { Spent[i] = Math.Max(0, int.Parse(array[3 + i], invariantCulture)); } int num3 = 3 + num2; Satiety = ((array.Length > num3) ? float.Parse(array[num3], NumberStyles.Float, invariantCulture) : (-1f)); Clamp(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Nao foi possivel ler a progressao de " + player.GetPlayerName() + ", resetando. Dado bruto: '" + value + "'. Erro: " + ex.Message)); Reset(); } } private void Clamp() { if (Level > ModConfig.MaxLevel.Value) { Level = ModConfig.MaxLevel.Value; } if (SpentPoints > TotalPoints) { ReclaimExcessPoints(); } } public void Reset() { Level = 1; Xp = 0L; Array.Clear(Spent, 0, Spent.Length); Satiety = -1f; } public override string ToString() { return $"lvl {Level} ({Xp}/{XpForNextLevel} xp) " + $"VIT {Spent[0]} RES {Spent[1]} ESP {Spent[2]} CON {Spent[3]} " + $"livres {UnspentPoints}"; } } [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] internal static class Player_GetTotalFoodValue_Patch { private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr) { PlayerProgress playerProgress = PlayerProgress.Get(__instance); if (playerProgress != null) { playerProgress.ComputeStats(__instance, out var hp2, out var stamina2, out var eitr2); if (ModConfig.FoodGivesNoStats.Value) { hp = hp2; stamina = stamina2; eitr = eitr2; } else { hp += hp2 - __instance.m_baseHP; stamina += stamina2 - __instance.m_baseStamina; eitr += eitr2; } } } } internal static class KillXpNetwork { private const string RpcName = "ValheimMMO_KillXp"; private static readonly ConditionalWeakTable> Contributors = new ConditionalWeakTable>(); internal static void Register() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("ValheimMMO_KillXp", (Action)OnKillXp); Plugin.Log.LogInfo((object)"RPC de XP registrado."); } } internal static void RecordHit(Character victim, HitData hit) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)victim == (Object)null) && hit != null && !victim.IsPlayer() && !((ZDOID)(ref hit.m_attacker)).IsNone() && hit.GetAttacker() is Player && !(hit.GetTotalDamage() <= 0f)) { Contributors.GetOrCreateValue(victim).Add(hit.m_attacker); } } internal static void BroadcastKill(Character victim) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_00a3: 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) if ((Object)(object)victim == (Object)null || ZRoutedRpc.instance == null) { return; } HashSet value; bool flag = Contributors.TryGetValue(victim, out value) && value.Count > 0; bool flag2 = !ModConfig.XpRequiresOwnDamage.Value; if (!flag && !flag2) { Contributors.Remove(victim); return; } long num = LevelCurve.XpForKill(victim); num = (long)((float)num * ModConfig.XpGlobalRate.Value); if (num <= 0) { Contributors.Remove(victim); return; } ZPackage val = new ZPackage(); val.Write(num); val.Write(victim.m_name ?? ""); val.Write(((Component)victim).transform.position); val.Write(flag2); val.Write(flag ? value.Count : 0); if (flag) { foreach (ZDOID item in value) { val.Write(item); } } val.Write(((Object)((Component)victim).gameObject).name.Replace("(Clone)", "")); val.Write(DungeonBosses.IsDungeonBoss(victim, out var tier) ? tier.Id : 0); val.Write(WorldBoss.IsWorldBoss(victim, out var def) ? WorldBoss.GoldFor(def) : 0); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ValheimMMO_KillXp", new object[1] { val }); Contributors.Remove(victim); } private static void OnKillXp(long sender, ZPackage pkg) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || pkg == null) { return; } long num = pkg.ReadLong(); string victimName = pkg.ReadString(); Vector3 val = pkg.ReadVector3(); bool flag = pkg.ReadBool(); int num2 = pkg.ReadInt(); ZDOID zDOID = ((Character)localPlayer).GetZDOID(); bool flag2 = false; for (int i = 0; i < num2; i++) { if (pkg.ReadZDOID() == zDOID) { flag2 = true; } } string victimPrefab = pkg.ReadString(); int num3 = pkg.ReadInt(); int num4 = pkg.ReadInt(); if (!flag2 && flag) { float num5 = Mathf.Max(1f, ModConfig.XpNearbyRadius.Value); flag2 = Vector3.Distance(((Component)localPlayer).transform.position, val) <= num5; } if (flag2) { Award(localPlayer, num, victimName); QuestSystem.OnKill(victimPrefab, num3); if (num4 > 0) { GiveGold(localPlayer, num4); } Karma.OnMonsterKill(Mathf.Pow(Mathf.Max(1f, (float)num / Mathf.Max(0.1f, ModConfig.XpKillFactor.Value * ModConfig.XpGlobalRate.Value)), 1.3333334f), num3 > 0 || num4 > 0); } } public static void GiveGoldPublic(Player player, int amount, string message) { GiveGold(player, amount, message); } private static void GiveGold(Player player, int amount, string message = null) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) ObjectDB instance = ObjectDB.instance; object obj; if (instance == null) { obj = null; } else { GameObject itemPrefab = instance.GetItemPrefab("Coins"); obj = ((itemPrefab != null) ? itemPrefab.GetComponent() : null); } ItemDrop val = (ItemDrop)obj; if ((Object)(object)val == (Object)null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); int num = Mathf.Max(1, val.m_itemData.m_shared.m_maxStackSize); int num2 = amount; while (num2 > 0) { int num3 = Mathf.Min(num, num2); if (inventory.AddItem(((Object)((Component)val).gameObject).name, num3, val.m_itemData.m_quality, val.m_itemData.m_variant, 0L, "", false) == null) { CharacterDrop.DropItems(new List> { new KeyValuePair(((Component)val).gameObject, 1) }, ((Component)player).transform.position + Vector3.up, 0.3f); break; } num2 -= num3; } ((Character)player).Message((MessageType)2, message ?? $"Recompensa do chefe mundial: {amount} moedas", 0, (Sprite)null); } internal static void Award(Player player, long xp, string victimName) { PlayerProgress playerProgress = PlayerProgress.Get(player); if (playerProgress != null && xp > 0) { int level = playerProgress.Level; int num = playerProgress.AddXp(xp, player); string arg = (string.IsNullOrEmpty(victimName) ? "" : (" (" + Localization.instance.Localize(victimName) + ")")); if (num > 0) { ((Character)player).Message((MessageType)2, $"Level {playerProgress.Level}! +{num * ModConfig.PointsPerLevel.Value} pontos", 0, (Sprite)null); Plugin.Log.LogInfo((object)$"Level up: {level} -> {playerProgress.Level} ({playerProgress})"); } else if (ModConfig.ShowXpGainMessages.Value) { ((Character)player).Message((MessageType)1, $"+{xp} XP{arg}", 0, (Sprite)null); } } } } [HarmonyPatch(typeof(Game), "Start")] internal static class Game_Start_RegisterRpcPatch { private static void Postfix() { KillXpNetwork.Register(); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] internal static class Character_RPC_Damage_TrackPatch { private static void Prefix(Character __instance, HitData hit) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsOwner()) { KillXpNetwork.RecordHit(__instance, hit); } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class Character_OnDeath_XpPatch { private static void Prefix(Character __instance) { if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && __instance.m_nview.IsOwner()) { KillXpNetwork.BroadcastKill(__instance); } } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class Player_OnDeath_PenaltyPatch { private static void Prefix(Player __instance) { if ((Object)(object)__instance == (Object)null || !((Character)__instance).m_nview.IsOwner()) { return; } PlayerProgress playerProgress = PlayerProgress.Get(__instance); if (playerProgress != null) { long xp = playerProgress.Xp; int level = playerProgress.Level; if (playerProgress.ApplyDeathPenalty(__instance) > 0) { ((Character)__instance).Message((MessageType)2, $"Voce regrediu para o level {playerProgress.Level}", 0, (Sprite)null); Plugin.Log.LogInfo((object)$"Morte: level {level} -> {playerProgress.Level}, pontos devolvidos automaticamente."); } else if (xp != playerProgress.Xp) { ((Character)__instance).Message((MessageType)1, $"-{xp - playerProgress.Xp} XP", 0, (Sprite)null); } } } } [HarmonyPatch(typeof(Player), "Load")] internal static class Player_Load_Patch { private static void Postfix(Player __instance) { PlayerProgress.ClearCache(__instance); PlayerProgress playerProgress = PlayerProgress.Get(__instance); if (playerProgress != null) { LevelDisplay.Sync(__instance, playerProgress.Level); } Plugin.Log.LogInfo((object)$"Progressao carregada para {__instance.GetPlayerName()}: {playerProgress}"); } } } namespace ValheimMMO.Network { internal static class ConfigSync { private const string Rpc = "ValheimMMO_Config"; private static readonly string[] LocalOnlySections = new string[2] { "10 - Interface", "18 - Rede" }; private static Dictionary _snapshot; public static bool Synced { get; private set; } internal static void OnNewConnection(ZNetPeer peer) { if (peer?.m_rpc != null) { peer.m_rpc.Register("ValheimMMO_Config", (Action)OnConfig); } } private static bool IsLocalOnly(string section) { string[] localOnlySections = LocalOnlySections; foreach (string text in localOnlySections) { if (section == text) { return true; } } return false; } internal static void SendTo(ZRpc rpc) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown if (!ModConfig.ConfigSyncEnabled.Value || rpc == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ConfigFile config = ((BaseUnityPlugin)Plugin.Instance).Config; List> list = new List>(); foreach (KeyValuePair item in config) { if (!IsLocalOnly(item.Key.Section)) { list.Add(item); } } ZPackage val = new ZPackage(); val.Write(list.Count); foreach (KeyValuePair item2 in list) { val.Write(item2.Key.Section); val.Write(item2.Key.Key); val.Write(item2.Value.GetSerializedValue()); } rpc.Invoke("ValheimMMO_Config", new object[1] { val }); Plugin.Log.LogInfo((object)$"[Config] {list.Count} entradas enviadas ao cliente."); } private static void OnConfig(ZRpc rpc, ZPackage pkg) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return; } ConfigFile config = ((BaseUnityPlugin)Plugin.Instance).Config; if (_snapshot == null) { _snapshot = new Dictionary(); foreach (KeyValuePair item in config) { _snapshot[item.Key] = item.Value.GetSerializedValue(); } } bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; int num = 0; int num2 = 0; try { int num3 = pkg.ReadInt(); for (int i = 0; i < num3; i++) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); string serializedValue = pkg.ReadString(); if (IsLocalOnly(text)) { continue; } ConfigDefinition val = new ConfigDefinition(text, text2); if (config.ContainsKey(val)) { ConfigEntryBase val2 = config[val]; try { val2.SetSerializedValue(serializedValue); num++; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Config] " + text + "/" + text2 + ": " + ex.Message)); } } else { num2++; } } } finally { config.SaveOnConfigSet = saveOnConfigSet; } Synced = true; MerchantItems.Invalidate(); Plugin.Log.LogInfo((object)($"[Config] Sincronizada do servidor: {num} entradas aplicadas" + ((num2 > 0) ? $", {num2} desconhecidas" : "."))); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, "Configuracao do servidor aplicada.", 0, (Sprite)null); } } internal static void Restore() { if (_snapshot == null) { return; } ConfigFile config = ((BaseUnityPlugin)Plugin.Instance).Config; bool saveOnConfigSet = config.SaveOnConfigSet; config.SaveOnConfigSet = false; try { foreach (KeyValuePair item in _snapshot) { if (config.ContainsKey(item.Key)) { ConfigEntryBase val = config[item.Key]; try { val.SetSerializedValue(item.Value); } catch { } } } } finally { config.SaveOnConfigSet = saveOnConfigSet; } _snapshot = null; Synced = false; MerchantItems.Invalidate(); Plugin.Log.LogInfo((object)"[Config] Config local restaurada."); } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal static class ZNet_OnNewConnection_ConfigPatch { private static void Postfix(ZNetPeer peer) { ConfigSync.OnNewConnection(peer); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class ZNet_RPC_PeerInfo_ConfigPatch { private static void Postfix(ZRpc rpc) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && VersionCheck.Allow(rpc, out var _)) { ConfigSync.SendTo(rpc); } } } [HarmonyPatch(typeof(ZNet), "OnDestroy")] internal static class ZNet_OnDestroy_ConfigPatch { private static void Prefix() { ConfigSync.Restore(); } } internal static class VersionCheck { private const string Rpc = "ValheimMMO_Version"; private static readonly Dictionary Received = new Dictionary(); internal static void OnNewConnection(ZNetPeer peer) { if (peer?.m_rpc != null) { peer.m_rpc.Register("ValheimMMO_Version", (Action)OnVersion); if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { peer.m_rpc.Invoke("ValheimMMO_Version", new object[1] { "0.3.4" }); } } } private static void OnVersion(ZRpc rpc, string version) { Received[rpc] = version; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { rpc.Invoke("ValheimMMO_Version", new object[1] { "0.3.4" }); } else if (version != "0.3.4") { Plugin.Log.LogWarning((object)("[Versao] Servidor tem ValheimMMO " + version + ", voce tem 0.3.4.")); } } internal static bool Allow(ZRpc rpc, out string reason) { reason = null; if (!ModConfig.VersionCheckEnabled.Value) { return true; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return true; } if (!Received.TryGetValue(rpc, out var value)) { reason = "cliente sem o mod ValheimMMO"; return false; } if (ModConfig.VersionCheckExact.Value && value != "0.3.4") { reason = "versao do mod diferente (cliente " + value + ", servidor 0.3.4)"; return false; } return true; } internal static void Forget(ZNetPeer peer) { if (peer?.m_rpc != null) { Received.Remove(peer.m_rpc); } } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] internal static class ZNet_OnNewConnection_VersionPatch { private static void Postfix(ZNetPeer peer) { VersionCheck.OnNewConnection(peer); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] internal static class ZNet_RPC_PeerInfo_VersionPatch { private static bool Prefix(ZRpc rpc) { if (VersionCheck.Allow(rpc, out var reason)) { return true; } Plugin.Log.LogWarning((object)("[Versao] Conexao recusada: " + reason + ".")); rpc.Invoke("Error", new object[1] { 3 }); return false; } } [HarmonyPatch(typeof(ZNet), "Disconnect")] internal static class ZNet_Disconnect_VersionPatch { private static void Prefix(ZNetPeer peer) { VersionCheck.Forget(peer); } } } namespace ValheimMMO.Merchant { internal static class MerchantItems { private sealed class TierCache { public ObjectDB Db; public int Bucket; public int Discount; public List List; } internal struct GameObject_ItemDrop { public string Name; public ItemDrop Drop; } private static List _list; private static ObjectDB _builtFor; private static int _builtBucket = -1; private static readonly Dictionary TierLists = new Dictionary(); public static List List { get { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return new List(); } int num = CurrentBucket(); if (_list != null && (Object)(object)_builtFor == (Object)(object)instance && _builtBucket == num) { return _list; } _builtFor = instance; _builtBucket = num; _list = Build(instance, 0, 0f); Plugin.Log.LogInfo((object)$"[Mercador] Catalogo montado: {_list.Count} itens, dia {CurrentDay()}, inflacao x{InflationMultiplier():0.00}."); return _list; } } public static void Invalidate() { _list = null; _builtFor = null; _builtBucket = -1; TierLists.Clear(); MerchantPricing.Invalidate(); BiomeTiers.Invalidate(); } public static int CurrentDay() { if (!((Object)(object)EnvMan.instance != (Object)null)) { return 0; } return EnvMan.instance.GetDay(); } public static int CurrentBucket() { int value = ModConfig.MerchantInflationEveryDays.Value; if (value <= 0) { return 0; } return CurrentDay() / value; } public static float InflationMultiplier() { int num = CurrentBucket(); if (num <= 0) { return 1f; } float num2 = Mathf.Pow(1f + ModConfig.MerchantInflationPercent.Value / 100f, (float)num); float num3 = Mathf.Max(1f, ModConfig.MerchantInflationMaxMultiplier.Value); return Mathf.Min(num2, num3); } public static List ListFor(int tier, float discount) { if (tier <= 0 && discount <= 0f) { return List; } ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null) { return new List(); } int num = CurrentBucket(); int num2 = Mathf.RoundToInt(discount * 100f); if (TierLists.TryGetValue(tier, out var value) && (Object)(object)value.Db == (Object)(object)instance && value.Bucket == num && value.Discount == num2) { return value.List; } List list = Build(instance, tier, discount); TierLists[tier] = new TierCache { Db = instance, Bucket = num, Discount = num2, List = list }; Plugin.Log.LogInfo((object)$"[Mercador] Catalogo tier {tier}: {list.Count} itens (desconto {num2}%)."); return list; } public static bool IsSellable(GameObject_ItemDrop item, out string why) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Invalid comparison between Unknown and I4 why = null; SharedData val = item.Drop?.m_itemData?.m_shared; if (val == null) { why = "sem ItemData"; return false; } string name = item.Name; if (name == "Coins") { why = "moeda"; return false; } if (ModConfig.MerchantExcludeQuestItems.Value && val.m_questItem) { why = "item de missao"; return false; } if (val.m_icons == null || val.m_icons.Length == 0 || (Object)(object)val.m_icons[0] == (Object)null) { why = "sem icone"; return false; } if ((int)val.m_itemType == 10) { why = "cosmetico"; return false; } if (!EquipmentDrops.IsDroppable(name)) { why = "sem ZNetView (interno)"; return false; } if (string.IsNullOrEmpty(val.m_name)) { why = "sem nome"; return false; } string[] array = (ModConfig.MerchantExcludePattern.Value ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && ((text[0] == '=') ? string.Equals(name, text.Substring(1), StringComparison.OrdinalIgnoreCase) : (name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0))) { why = "padrao '" + text + "'"; return false; } } return true; } public static int StackFor(SharedData shared) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Invalid comparison between Unknown and I4 if (shared.m_name == "Mapa do Explorador") { return 1; } if (shared.m_name == "Pedra de Retorno") { return 1; } int val = Math.Max(1, shared.m_maxStackSize); ItemType itemType = shared.m_itemType; if ((int)itemType <= 2) { if ((int)itemType == 1) { goto IL_0054; } if ((int)itemType == 2) { return Math.Min(val, Math.Max(1, ModConfig.MerchantConsumableStack.Value)); } } else if ((int)itemType == 9 || (int)itemType == 23) { goto IL_0054; } return 1; IL_0054: return Math.Min(val, Math.Max(1, ModConfig.MerchantMaterialStack.Value)); } private static List Build(ObjectDB db, int tier, float discount) { //IL_0179: 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_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown List list = new List(); float num = InflationMultiplier(); bool flag = tier > 0 && ModConfig.CitiesEnabled.Value; foreach (GameObject item2 in db.m_items) { if ((Object)(object)item2 == (Object)null) { continue; } ItemDrop component = item2.GetComponent(); if ((Object)(object)component == (Object)null) { continue; } GameObject_ItemDrop item = new GameObject_ItemDrop { Name = ((Object)item2).name, Drop = component }; bool flag2 = CustomItems.IsCustom(((Object)item2).name) || MapItem.IsMap(((Object)item2).name); if (!IsSellable(item, out var why)) { if (flag2) { Plugin.Log.LogWarning((object)("[Mercador] item do mod '" + ((Object)item2).name + "' fora do catalogo: " + why)); } continue; } float num2 = 1f; if (flag) { int num3 = BiomeTiers.TierOf(((Object)item2).name); if (num3 != 0) { if (num3 > tier || (num3 < tier && !ModConfig.CitySellsLowerTiers.Value)) { continue; } if (num3 < tier) { num2 = 1f + ModConfig.ImportMarkupPerTier.Value * (float)(tier - num3); } } } int num4 = StackFor(component.m_itemData.m_shared); long num5 = (long)Math.Ceiling((float)MerchantPricing.PriceOf(((Object)item2).name) * num * num2 * (1f - Mathf.Clamp01(discount))) * num4; if (num5 > int.MaxValue) { num5 = 2147483647L; } list.Add(new TradeItem { m_prefab = component, m_stack = num4, m_price = (int)Math.Max(1L, num5), m_requiredGlobalKey = "" }); } list.Sort(delegate(TradeItem a, TradeItem b) { int num6 = GroupOf(a); int num7 = GroupOf(b); if (num6 != num7) { return num6.CompareTo(num7); } int num8 = a.m_price / Math.Max(1, a.m_stack); int num9 = b.m_price / Math.Max(1, b.m_stack); return (num8 != num9) ? num8.CompareTo(num9) : string.Compare(((Object)((Component)a.m_prefab).gameObject).name, ((Object)((Component)b.m_prefab).gameObject).name, StringComparison.OrdinalIgnoreCase); }); return list; } private static int GroupOf(TradeItem t) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) string name = ((Object)((Component)t.m_prefab).gameObject).name; if (CustomItems.IsCustom(name) || MapItem.IsMap(name)) { return -1; } return Group(t.m_prefab.m_itemData.m_shared.m_itemType); } private static int Group(ItemType t) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected I4, but got Unknown return (t - 1) switch { 2 => 0, 13 => 0, 21 => 0, 3 => 1, 4 => 2, 5 => 3, 6 => 3, 10 => 3, 16 => 3, 17 => 4, 23 => 4, 18 => 5, 14 => 5, 8 => 6, 22 => 6, 1 => 7, 0 => 8, 12 => 9, _ => 10, }; } } internal static class MerchantSpawner { public const string GlobalKey = "mmo_merchant_spawned"; public const string ZdoTag = "mmo_merchant"; private static float _timer; public static bool IsMerchant(Trader trader) { if ((Object)(object)trader == (Object)null) { return false; } ZNetView component = ((Component)trader).GetComponent(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val == null) { return false; } if (!val.GetBool("mmo_merchant", false)) { return CitySystem.IsCityMerchant(trader); } return true; } public static int TierOf(Trader trader) { if ((Object)(object)trader == (Object)null) { return -1; } if (CitySystem.IsCityMerchant(trader)) { return CitySystem.CityTierOf((Component)(object)trader); } ZNetView component = ((Component)trader).GetComponent(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val != null && val.GetBool("mmo_merchant", false)) { if (!ModConfig.CitiesEnabled.Value) { return 0; } return ModConfig.SpawnMerchantTier.Value; } return -1; } public static void Tick(Player player, float dt) { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.MerchantEnabled.Value || (Object)(object)player == (Object)null) { return; } _timer += dt; if (_timer < 5f) { return; } _timer = 0f; Vector3 val = default(Vector3); if ((Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ZoneSystem.instance.GetGlobalKey("mmo_merchant_spawned") || !ZoneSystem.instance.GetLocationIcon("StartTemple", ref val) || Vector3.Distance(((Component)player).transform.position, val) > 40f) { return; } Trader[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (Trader val2 in array) { if (IsMerchant(val2) && Vector3.Distance(((Component)val2).transform.position, val) < 60f) { ZoneSystem.instance.SetGlobalKey("mmo_merchant_spawned"); return; } } Vector3 val3 = val + new Vector3(ModConfig.MerchantOffsetX.Value, 0f, ModConfig.MerchantOffsetZ.Value); if (Spawn(val3, val, out var error)) { ZoneSystem.instance.SetGlobalKey("mmo_merchant_spawned"); ((Character)player).Message((MessageType)1, "Um mercador se instalou perto das pedras iniciais.", 0, (Sprite)null); Plugin.Log.LogInfo((object)$"[Mercador] Spawnado em {val3}."); } else { Plugin.Log.LogWarning((object)("[Mercador] Falha ao spawnar: " + error)); ZoneSystem.instance.SetGlobalKey("mmo_merchant_spawned"); } } public static bool Spawn(Vector3 pos, Vector3 lookAt, out string error) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) error = null; ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(ModConfig.MerchantPrefab.Value) : null); if ((Object)(object)val == (Object)null) { error = "prefab '" + ModConfig.MerchantPrefab.Value + "' nao encontrado no ZNetScene"; return false; } if ((Object)(object)val.GetComponent() == (Object)null) { error = "prefab nao tem componente Trader"; return false; } if ((Object)(object)val.GetComponent() == (Object)null) { error = "prefab nao tem ZNetView"; return false; } float y = default(float); if (ZoneSystem.instance.GetGroundHeight(pos, ref y)) { pos.y = y; } Vector3 val2 = lookAt - pos; val2.y = 0f; Quaternion val3 = ((((Vector3)(ref val2)).sqrMagnitude > 0.01f) ? Quaternion.LookRotation(((Vector3)(ref val2)).normalized) : Quaternion.identity); GameObject val4 = Object.Instantiate(val, pos, val3); ZNetView component = val4.GetComponent(); if ((Object)(object)component == (Object)null || !component.IsValid()) { error = "ZNetView invalido apos instanciar"; return false; } component.GetZDO().Set("mmo_merchant", true); component.GetZDO().Persistent = true; Trader component2 = val4.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.m_name = ModConfig.MerchantName.Value; } return true; } } [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class Player_UpdateStats_MerchantPatch { private static void Postfix(Player __instance, float dt) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { MerchantSpawner.Tick(__instance, dt); } } } [HarmonyPatch(typeof(Trader), "Start")] internal static class Trader_Start_NamePatch { private static void Postfix(Trader __instance) { if (MerchantSpawner.IsMerchant(__instance)) { __instance.m_name = ModConfig.MerchantName.Value; } } } [HarmonyPatch(typeof(Trader), "GetAvailableItems")] internal static class Trader_GetAvailableItems_Patch { internal static bool InStore; private static void Postfix(Trader __instance, ref List __result) { if (ModConfig.MerchantEnabled.Value && (InStore || StoreGui.IsVisible())) { int num = MerchantSpawner.TierOf(__instance); if (num >= 0) { __result = MerchantItems.ListFor(num, 0f); } } } } [HarmonyPatch(typeof(StoreGui), "FillList")] internal static class StoreGui_FillList_Patch { private static void Prefix() { Trader_GetAvailableItems_Patch.InStore = true; } private static void Postfix() { Trader_GetAvailableItems_Patch.InStore = false; } } [HarmonyPatch(typeof(StoreGui), "Show")] internal static class StoreGui_Show_InflationPatch { private static void Postfix(Trader trader) { if (!ModConfig.MerchantEnabled.Value || !MerchantSpawner.IsMerchant(trader)) { return; } Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { float num = MerchantItems.InflationMultiplier(); int value = ModConfig.MerchantInflationEveryDays.Value; if (num > 1f) { ((Character)localPlayer).Message((MessageType)1, $"Dia {MerchantItems.CurrentDay()}: precos {(num - 1f) * 100f:+0}% (sobem {ModConfig.MerchantInflationPercent.Value:0}% a cada {value} dias)", 0, (Sprite)null); } else if (value > 0) { ((Character)localPlayer).Message((MessageType)1, $"Dia {MerchantItems.CurrentDay()}: precos base (sobem {ModConfig.MerchantInflationPercent.Value:0}% a cada {value} dias)", 0, (Sprite)null); } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDB_Awake_MerchantPatch { private static void Postfix() { MerchantItems.Invalidate(); } } internal static class MerchantPricing { private enum Source { Override, Base, Recipe, Conversion, CreatureDrop, Gathered, Mined, VanillaValue, Unknown, Cycle } private sealed class Conversion { public string From; public float Markup; public float Fee; public float Divide = 1f; public string Station; } private static readonly Dictionary Cache = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary Sources = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary Explain = new Dictionary(StringComparer.Ordinal); private static readonly HashSet Visiting = new HashSet(StringComparer.Ordinal); private static Dictionary> _recipes; private static Dictionary> _conversions; private static Dictionary _creatureHp; private static HashSet _gathered; private static HashSet _mined; private static Dictionary _basePrices; private static Dictionary _overrides; private static Dictionary _traderPrices; private static ObjectDB _builtFor; public static void Invalidate() { Cache.Clear(); Sources.Clear(); Explain.Clear(); Visiting.Clear(); _recipes = null; _conversions = null; _creatureHp = null; _gathered = null; _mined = null; _basePrices = null; _overrides = null; _builtFor = null; } private static void EnsureBuilt() { ObjectDB instance = ObjectDB.instance; if ((Object)(object)instance == (Object)null || (_recipes != null && (Object)(object)_builtFor == (Object)(object)instance)) { return; } Invalidate(); _builtFor = instance; _basePrices = ParseTable(ModConfig.MerchantBasePrices.Value); _overrides = ParseTable(ModConfig.MerchantPriceOverrides.Value); _recipes = new Dictionary>(StringComparer.Ordinal); foreach (Recipe recipe in instance.m_recipes) { if (!((Object)(object)recipe == (Object)null) && recipe.m_enabled && !((Object)(object)recipe.m_item == (Object)null)) { string name = ((Object)((Component)recipe.m_item).gameObject).name; if (!_recipes.TryGetValue(name, out var value)) { value = (_recipes[name] = new List()); } value.Add(recipe); } } _conversions = new Dictionary>(StringComparer.Ordinal); _creatureHp = new Dictionary(StringComparer.Ordinal); _gathered = new HashSet(StringComparer.Ordinal); _mined = new HashSet(StringComparer.Ordinal); _traderPrices = new Dictionary(StringComparer.Ordinal); if (!((Object)(object)ZNetScene.instance != (Object)null)) { return; } foreach (GameObject prefab in ZNetScene.instance.m_prefabs) { if ((Object)(object)prefab == (Object)null) { continue; } Trader component = prefab.GetComponent(); if ((Object)(object)component != (Object)null && component.m_items != null) { foreach (TradeItem item in component.m_items) { if (!((Object)(object)item?.m_prefab == (Object)null) && item.m_price > 0) { int num = Mathf.Max(1, Mathf.CeilToInt((float)item.m_price / (float)Mathf.Max(1, item.m_stack))); string name2 = ((Object)((Component)item.m_prefab).gameObject).name; if (!_traderPrices.TryGetValue(name2, out var value2) || num < value2) { _traderPrices[name2] = num; } } } } Smelter component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null && component2.m_conversion != null) { float num2 = 0f; if ((Object)(object)component2.m_fuelItem != (Object)null) { num2 = component2.m_fuelPerProduct * Mathf.Max(1, PriceOf(((Object)((Component)component2.m_fuelItem).gameObject).name)); } foreach (ItemConversion item2 in component2.m_conversion) { AddConversion(item2?.m_to, item2?.m_from, ModConfig.MerchantConversionMarkup.Value, num2 + 2f, 1f, component2.m_name); } } CookingStation component3 = prefab.GetComponent(); if ((Object)(object)component3 != (Object)null && component3.m_conversion != null) { foreach (ItemConversion item3 in component3.m_conversion) { AddConversion(item3?.m_to, item3?.m_from, 1.2f, 1f, 1f, "cooking"); } } Fermenter component4 = prefab.GetComponent(); if ((Object)(object)component4 != (Object)null && component4.m_conversion != null) { foreach (ItemConversion item4 in component4.m_conversion) { AddConversion(item4?.m_to, item4?.m_from, 1.2f, 2f, Mathf.Max(1, item4?.m_producedItems ?? 1), "fermenter"); } } CharacterDrop component5 = prefab.GetComponent(); Character component6 = prefab.GetComponent(); if ((Object)(object)component5 != (Object)null && (Object)(object)component6 != (Object)null && component5.m_drops != null) { float num3 = Mathf.Max(1f, component6.m_health); foreach (Drop drop in component5.m_drops) { if (!((Object)(object)drop?.m_prefab == (Object)null)) { string name3 = ((Object)drop.m_prefab).name; if (!_creatureHp.TryGetValue(name3, out var value3) || num3 < value3) { _creatureHp[name3] = num3; } } } } Pickable component7 = prefab.GetComponent(); if ((Object)(object)component7 != (Object)null) { if ((Object)(object)component7.m_itemPrefab != (Object)null) { _gathered.Add(((Object)component7.m_itemPrefab).name); } AddTable(component7.m_extraDrops, _gathered); } AddTable(prefab.GetComponent()?.m_dropWhenDestroyed, _gathered); AddTable(prefab.GetComponent()?.m_dropWhenDestroyed, _gathered); AddTable(prefab.GetComponent()?.m_dropWhenDestroyed, _gathered); AddTable(prefab.GetComponent()?.m_dropItems, _mined); AddTable(prefab.GetComponent()?.m_dropItems, _mined); } } private static void AddConversion(ItemDrop to, ItemDrop from, float markup, float fee, float divide, string station) { if (!((Object)(object)to == (Object)null) && !((Object)(object)from == (Object)null)) { string name = ((Object)((Component)to).gameObject).name; if (!_conversions.TryGetValue(name, out var value)) { value = (_conversions[name] = new List()); } value.Add(new Conversion { From = ((Object)((Component)from).gameObject).name, Markup = markup, Fee = fee, Divide = divide, Station = station }); } } private static void AddTable(DropTable table, HashSet into) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (table?.m_drops == null) { return; } foreach (DropData drop in table.m_drops) { if ((Object)(object)drop.m_item != (Object)null) { into.Add(((Object)drop.m_item).name); } } } private static Dictionary ParseTable(string raw) { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrEmpty(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { int num = text.IndexOf('='); if (num > 0) { string text2 = text.Substring(0, num).Trim(); if (int.TryParse(text.Substring(num + 1).Trim(), out var result) && text2.Length > 0) { dictionary[text2] = result; } } } return dictionary; } public static int PriceOf(string prefabName) { //IL_048d: Unknown result type (might be due to invalid IL or missing references) //IL_0494: Invalid comparison between Unknown and I4 EnsureBuilt(); if (string.IsNullOrEmpty(prefabName)) { return 1; } if (Cache.TryGetValue(prefabName, out var value)) { return value; } if (_overrides != null && _overrides.TryGetValue(prefabName, out var value2)) { return Store(prefabName, value2, Source.Override, "override na config"); } if (MapItem.IsMap(prefabName)) { return Store(prefabName, Mathf.Max(1, ModConfig.MapPrice.Value), Source.Override, "item do mod (MapPrice)"); } if (CustomItems.IsCustom(prefabName)) { return Store(prefabName, Mathf.Max(1, CustomItems.PriceOf(prefabName)), Source.Override, "item do mod"); } if (Visiting.Contains(prefabName)) { Sources[prefabName] = Source.Cycle; return Mathf.Max(1, Mathf.RoundToInt(ModConfig.MerchantUnknownBase.Value)); } Visiting.Add(prefabName); try { if (_basePrices != null && _basePrices.TryGetValue(prefabName, out var value3)) { return Store(prefabName, Scaled(value3), Source.Base, "materia-prima (BasePrices)"); } if (_traderPrices != null && _traderPrices.TryGetValue(prefabName, out var value4)) { return Store(prefabName, Scaled(value4), Source.VanillaValue, $"preco de vendedor vanilla {value4}"); } float num = float.MaxValue; Source source = Source.Unknown; string why = ""; if (_recipes != null && _recipes.TryGetValue(prefabName, out var value5)) { foreach (Recipe item in value5) { float num2 = 0f; List list = new List(); Requirement[] resources = item.m_resources; foreach (Requirement val in resources) { if (!((Object)(object)val?.m_resItem == (Object)null) && val.m_amount > 0) { string name = ((Object)((Component)val.m_resItem).gameObject).name; int num3 = PriceOf(name); num2 += (float)(num3 * val.m_amount); list.Add($"{val.m_amount}x {name}@{num3}"); } } int num4 = Mathf.Max(1, item.m_amount); float num5 = ModConfig.MerchantCraftBaseFee.Value + (float)Mathf.Max(0, item.m_minStationLevel - 1) * ModConfig.MerchantStationLevelFee.Value; float num6 = (num2 * ModConfig.MerchantCraftMarkup.Value + num5) / (float)num4; if (num6 < num) { num = num6; source = Source.Recipe; why = string.Format("receita [{0}] /{1}", string.Join(", ", list), num4) + (((Object)(object)item.m_craftingStation != (Object)null) ? $" @ {item.m_craftingStation.m_name} nv{item.m_minStationLevel}" : ""); } } } if (_conversions != null && _conversions.TryGetValue(prefabName, out var value6)) { foreach (Conversion item2 in value6) { int num7 = PriceOf(item2.From); float num8 = (float)num7 / item2.Divide * item2.Markup + item2.Fee; if (num8 < num) { num = num8; source = Source.Conversion; why = $"{item2.Station}: {item2.From}@{num7}" + ((item2.Divide > 1f) ? $" /{item2.Divide:0}" : ""); } } } if (source != Source.Unknown) { return Store(prefabName, Mathf.Max(1, Mathf.CeilToInt(num)), source, why); } ObjectDB instance = ObjectDB.instance; object obj; if (instance == null) { obj = null; } else { GameObject itemPrefab = instance.GetItemPrefab(prefabName); obj = ((itemPrefab != null) ? itemPrefab.GetComponent() : null); } SharedData val2 = ((ItemDrop)(obj?)).m_itemData?.m_shared; if (val2 != null && val2.m_value > 0) { return Store(prefabName, Scaled(val2.m_value * 2), Source.VanillaValue, $"valor vanilla {val2.m_value} x2"); } if (_creatureHp != null && _creatureHp.TryGetValue(prefabName, out var value7)) { float num9 = Mathf.Pow(value7, ModConfig.MerchantCreatureDropExponent.Value); if (val2 != null && (int)val2.m_itemType == 13) { num9 *= ModConfig.MerchantTrophyMultiplier.Value; } return Store(prefabName, Scaled(Mathf.CeilToInt(num9)), Source.CreatureDrop, $"drop de criatura com {value7:0} de vida"); } if (_mined != null && _mined.Contains(prefabName)) { return Store(prefabName, Scaled(Mathf.RoundToInt(ModConfig.MerchantMinedBase.Value)), Source.Mined, "minerado"); } if (_gathered != null && _gathered.Contains(prefabName)) { return Store(prefabName, Scaled(Mathf.RoundToInt(ModConfig.MerchantGatheredBase.Value)), Source.Gathered, "coletado"); } return Store(prefabName, Scaled(Mathf.RoundToInt(ModConfig.MerchantUnknownBase.Value)), Source.Unknown, "SEM ORIGEM CONHECIDA (revisar)"); } finally { Visiting.Remove(prefabName); } } private static int Scaled(int v) { return Mathf.Max(1, Mathf.RoundToInt((float)v * ModConfig.MerchantPriceMultiplier.Value)); } private static int Store(string name, int price, Source source, string why) { if (source == Source.Recipe || source == Source.Conversion) { price = Mathf.Max(1, price); } Cache[name] = price; Sources[name] = source; Explain[name] = why; return price; } public static string SourceOf(string name) { if (!Sources.TryGetValue(name, out var value)) { return "?"; } return value.ToString(); } public static string WhyOf(string name) { if (!Explain.TryGetValue(name, out var value)) { return ""; } return value; } } public class MerchantSellUI : MonoBehaviour { private enum Tab { Sell, Repair, Quests, Bank } private const float PanelWidth = 540f; private Tab _tab; private Vector2 _scroll; private static bool Active { get { if (ModConfig.MerchantEnabled.Value && (Object)(object)StoreGui.instance != (Object)null && StoreGui.IsVisible() && MerchantSpawner.IsMerchant(StoreGui.instance.m_trader)) { return (Object)(object)Player.m_localPlayer != (Object)null; } return false; } } private static int CurrentTier { get { if (!((Object)(object)StoreGui.instance != (Object)null)) { return -1; } return MerchantSpawner.TierOf(StoreGui.instance.m_trader); } } public static int UnitBuyPrice(ItemData item) { if (item?.m_shared == null) { return 0; } if (item.m_shared.m_value > 0) { return item.m_shared.m_value; } string prefabName = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name); int num = MerchantPricing.PriceOf(prefabName); float value = ModConfig.MerchantBuyRatio.Value; int currentTier = CurrentTier; if (ModConfig.CitiesEnabled.Value && currentTier > 0 && BiomeTiers.TierOf(prefabName) == currentTier) { value = ModConfig.LocalBuyRatio.Value; } return Mathf.Max(1, Mathf.FloorToInt((float)num * Mathf.Clamp01(value))); } private static int BasePriceOf(ItemData item) { if (!((Object)(object)item?.m_dropPrefab != (Object)null)) { return 0; } return MerchantPricing.PriceOf(((Object)item.m_dropPrefab).name); } public static int RepairCost(ItemData item) { float maxDurability = item.GetMaxDurability(); if (maxDurability <= 0f) { return 0; } float num = Mathf.Clamp01(1f - item.m_durability / maxDurability); if (num <= 0.001f) { return 0; } return Mathf.Max(1, Mathf.CeilToInt((float)BasePriceOf(item) * num * ModConfig.RepairCostRatio.Value)); } public static int UpgradeCost(ItemData item) { return Mathf.Max(1, Mathf.CeilToInt((float)(BasePriceOf(item) * item.m_quality) * ModConfig.UpgradeCostFactor.Value)); } public static int MaxUpgradeQuality(ItemData item) { return item.m_shared.m_maxQuality + Mathf.Max(0, ModConfig.ExtraQualityLevels.Value); } private static bool CanSell(ItemData item) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Invalid comparison between Unknown and I4 if (item?.m_shared == null || (Object)(object)item.m_dropPrefab == (Object)null) { return false; } if (item.m_equipped) { return false; } if (item.m_shared.m_questItem) { return false; } if (((Object)item.m_dropPrefab).name == "Coins") { return false; } if (item.m_shared.m_value > 0) { return true; } if (!ModConfig.MerchantBuysMaterials.Value && (int)item.m_shared.m_itemType == 1) { return false; } string[] array = (ModConfig.MerchantNoBuyPattern.Value ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0 && ((text[0] == '=') ? string.Equals(((Object)item.m_dropPrefab).name, text.Substring(1), StringComparison.OrdinalIgnoreCase) : (((Object)item.m_dropPrefab).name.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0))) { return false; } } return true; } private static bool IsEquipment(ItemData item) { //IL_0030: 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_004e: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Invalid comparison between Unknown and I4 if (item?.m_shared != null && (Object)(object)item.m_dropPrefab != (Object)null && item.m_shared.m_maxQuality > 1 && EquipmentDrops.ChanceFor(item.m_shared.m_itemType) > 0f && (int)item.m_shared.m_itemType != 9) { return (int)item.m_shared.m_itemType != 23; } return false; } private static int Coins(Player p) { return ((Humanoid)p).GetInventory().CountItems(StoreGui.instance.m_coinPrefab.m_itemData.m_shared.m_name, -1, true); } private static bool Pay(Player p, int amount) { if (amount <= 0) { return true; } Inventory inventory = ((Humanoid)p).GetInventory(); string name = StoreGui.instance.m_coinPrefab.m_itemData.m_shared.m_name; if (inventory.CountItems(name, -1, true) < amount) { ((Character)p).Message((MessageType)2, "Moedas insuficientes.", 0, (Sprite)null); return false; } inventory.RemoveItem(name, amount, -1, true); return true; } private void OnGUI() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: 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_00dd: Expected O, but got Unknown //IL_00d8: 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) if (Active) { GUISkin skin = GUI.skin; Matrix4x4 matrix = GUI.matrix; GUI.skin = MmoUI.Skin; float num = Mathf.Clamp((float)Screen.height / 1080f, 0.75f, 2.5f); GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(num, num, 1f)); float num2 = (float)Screen.width / num; float num3 = (float)Screen.height / num; Rect val = default(Rect); ((Rect)(ref val))..ctor(num2 - 540f - 24f, 90f, 540f, Mathf.Min(780f, num3 - 180f)); GUILayout.Window(((Object)this).GetInstanceID(), val, new WindowFunction(Draw), "MERCADOR", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(540f), GUILayout.Height(((Rect)(ref val)).height) }); GUI.matrix = matrix; GUI.skin = skin; } } private void Draw(int id) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null) != null && !((Object)(object)StoreGui.instance == (Object)null)) { string arg = ColorUtility.ToHtmlStringRGB(MmoUI.GoldC); GUILayout.BeginHorizontal(Array.Empty()); TabButton("VENDER", Tab.Sell); TabButton("REPARAR / MELHORAR", Tab.Repair); if (ModConfig.QuestsEnabled.Value && !ModConfig.CitiesEnabled.Value) { TabButton("MISSOES", Tab.Quests); } if (ModConfig.BankEnabled.Value) { TabButton("COFRE", Tab.Bank); } GUILayout.FlexibleSpace(); GUILayout.Label($"{Coins(localPlayer)} moedas", Array.Empty()); GUILayout.EndHorizontal(); GUILayout.Space(6f); switch (_tab) { case Tab.Sell: DrawSell(localPlayer); break; case Tab.Repair: DrawRepair(localPlayer); break; case Tab.Quests: DrawQuests(localPlayer); break; case Tab.Bank: DrawBank(localPlayer); break; } } } private void DrawBank(Player player) { //IL_0000: 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_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) string text = ColorUtility.ToHtmlStringRGB(MmoUI.MutedC); Inventory val = PlayerBank.Get(player); Inventory inventory = ((Humanoid)player).GetInventory(); if (val == null || inventory == null) { return; } GUILayout.Label($"Cofre pessoal: {val.NrOfItems()}/{val.GetWidth() * val.GetHeight()} slots. O que esta aqui nao pesa e fica salvo no seu personagem.", Array.Empty()); GUILayout.Space(4f); _scroll = GUILayout.BeginScrollView(_scroll, Array.Empty()); GUILayout.Label("No cofre", Array.Empty()); List allItems = val.GetAllItems(); if (allItems.Count == 0) { GUILayout.Label("Vazio.", Array.Empty()); } foreach (ItemData item in new List(allItems)) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); DrawIcon(item, 24f); GUILayout.Space(6f); GUILayout.Label("" + NameOf(item) + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.FlexibleSpace(); if (GUILayout.Button("Retirar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(100f), GUILayout.Height(24f) }) && !PlayerBank.Withdraw(player, item, out var message) && message != null) { ((Character)player).Message((MessageType)2, message, 0, (Sprite)null); } GUILayout.EndHorizontal(); } GUILayout.Space(10f); GUILayout.Label("Na mochila", Array.Empty()); List list = new List(); foreach (ItemData allItem in inventory.GetAllItems()) { if (!allItem.m_equipped) { list.Add(allItem); } } list.Sort((ItemData a, ItemData b) => string.Compare(Localization.instance.Localize(a.m_shared.m_name), Localization.instance.Localize(b.m_shared.m_name), StringComparison.OrdinalIgnoreCase)); if (list.Count == 0) { GUILayout.Label("Nada para guardar.", Array.Empty()); } foreach (ItemData item2 in list) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); DrawIcon(item2, 24f); GUILayout.Space(6f); GUILayout.Label("" + NameOf(item2) + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.FlexibleSpace(); if (GUILayout.Button("Guardar", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(100f), GUILayout.Height(24f) }) && !PlayerBank.Deposit(player, item2, out var message2) && message2 != null) { ((Character)player).Message((MessageType)2, message2, 0, (Sprite)null); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private void TabButton(string label, Tab tab) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) bool num = _tab == tab; string text = ColorUtility.ToHtmlStringRGB(MmoUI.GoldC); if (GUILayout.Button(num ? ("" + label + "") : label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { _tab = tab; _scroll = Vector2.zero; } } private void DrawSell(Player player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); string text = ColorUtility.ToHtmlStringRGB(MmoUI.MutedC); string arg = ColorUtility.ToHtmlStringRGB(MmoUI.BonusC); int num = Mathf.RoundToInt(Mathf.Clamp01(ModConfig.MerchantBuyRatio.Value) * 100f); GUILayout.Label(string.Format(arg2: ModConfig.MerchantBuysMaterials.Value ? "" : " Nao compra materia-prima (joias sim, pelo valor cheio).", format: "O mercador paga {1}% do valor base. Itens equipados nao aparecem.{2}", arg0: text, arg1: num), Array.Empty()); GUILayout.Space(4f); List list = new List(); foreach (ItemData allItem in inventory.GetAllItems()) { if (CanSell(allItem)) { list.Add(allItem); } } list.Sort((ItemData a, ItemData b) => string.Compare(Localization.instance.Localize(a.m_shared.m_name), Localization.instance.Localize(b.m_shared.m_name), StringComparison.OrdinalIgnoreCase)); if (list.Count == 0) { GUILayout.Label("Nada para vender.", Array.Empty()); return; } _scroll = GUILayout.BeginScrollView(_scroll, Array.Empty()); foreach (ItemData item in list) { int num2 = UnitBuyPrice(item); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); DrawIcon(item, 24f); GUILayout.Space(6f); GUILayout.Label("" + NameOf(item) + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(236f) }); GUILayout.Label($"{num2} /un", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); GUILayout.FlexibleSpace(); if (GUILayout.Button("1", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(32f), GUILayout.Height(24f) })) { Sell(player, item, 1); } GUI.enabled = item.m_stack > 1; if (GUILayout.Button($"Tudo ({num2 * item.m_stack})", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(116f), GUILayout.Height(24f) })) { Sell(player, item, item.m_stack); } GUI.enabled = true; GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private static void Sell(Player player, ItemData item, int amount) { //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) StoreGui instance = StoreGui.instance; Inventory inventory = ((Humanoid)player).GetInventory(); if ((Object)(object)instance == (Object)null || inventory == null || item == null) { return; } amount = Mathf.Clamp(amount, 1, item.m_stack); int num = UnitBuyPrice(item) * amount; ItemDrop coinPrefab = instance.m_coinPrefab; string name = ((Object)((Component)coinPrefab).gameObject).name; int num2 = Mathf.Max(1, coinPrefab.m_itemData.m_shared.m_maxStackSize); string name2 = ((Object)item.m_dropPrefab).name; int quality = item.m_quality; int variant = item.m_variant; float durability = item.m_durability; long crafterID = item.m_crafterID; string crafterName = item.m_crafterName; string arg = ((amount > 1) ? $"{amount}x {Localization.instance.Localize(item.m_shared.m_name)}" : Localization.instance.Localize(item.m_shared.m_name)); Sprite val = ((item.m_shared.m_icons.Length != 0) ? item.m_shared.m_icons[0] : null); inventory.RemoveItem(item, amount); int num3 = num; int num4 = 0; while (num3 > 0) { int num5 = Mathf.Min(num2, num3); if (inventory.AddItem(name, num5, coinPrefab.m_itemData.m_quality, coinPrefab.m_itemData.m_variant, 0L, "", false) == null) { break; } num3 -= num5; num4 += num5; } if (num3 > 0) { if (num4 > 0) { inventory.RemoveItem(name, num4, -1, true); } ItemData val2 = inventory.AddItem(name2, amount, quality, variant, crafterID, crafterName, false); if (val2 != null) { val2.m_durability = durability; } ((Character)player).Message((MessageType)2, "Sem espaco na mochila para as moedas.", 0, (Sprite)null); return; } Trader trader = instance.m_trader; if (trader != null) { trader.OnSold(); } EffectList sellEffects = instance.m_sellEffects; if (sellEffects != null) { sellEffects.Create(((Component)instance).transform.position, Quaternion.identity, (Transform)null, 1f, -1); } ((Character)player).Message((MessageType)1, $"Vendido: {arg} por {num} moedas", 0, val); instance.FillList(); } private void DrawRepair(Player player) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) Inventory inventory = ((Humanoid)player).GetInventory(); string text = ColorUtility.ToHtmlStringRGB(MmoUI.MutedC); ColorUtility.ToHtmlStringRGB(MmoUI.BonusC); GUILayout.Label($"Reparo: preco x desgaste x {ModConfig.RepairCostRatio.Value:0.##}. " + $"Melhoria: ate q{{max}}+{ModConfig.ExtraQualityLevels.Value}, preco x qualidade x {ModConfig.UpgradeCostFactor.Value:0.##}. " + "O bonus de atributo do item sobe com a qualidade.", Array.Empty()); GUILayout.Space(4f); List list = new List(); foreach (ItemData allItem in inventory.GetAllItems()) { if (IsEquipment(allItem)) { list.Add(allItem); } } list.Sort((ItemData a, ItemData b) => (b.m_equipped ? 1 : 0) - (a.m_equipped ? 1 : 0)); if (list.Count == 0) { GUILayout.Label("Nenhum equipamento na mochila.", Array.Empty()); return; } _scroll = GUILayout.BeginScrollView(_scroll, Array.Empty()); foreach (ItemData item in list) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); DrawIcon(item, 24f); GUILayout.Space(6f); string text2 = (item.m_equipped ? (" (equipado)") : ""); float maxDurability = item.GetMaxDurability(); string text3 = ((item.m_shared.m_useDurability && maxDurability > 0f) ? $" {item.m_durability / maxDurability * 100f:0}%" : ""); GUILayout.Label("" + NameOf(item) + text2 + text3 + "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); GUILayout.FlexibleSpace(); int num = (item.m_shared.m_useDurability ? RepairCost(item) : 0); GUI.enabled = num > 0; if (GUILayout.Button((num > 0) ? $"Reparar ({num})" : "Inteiro", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(110f), GUILayout.Height(24f) }) && Pay(player, num)) { item.m_durability = item.GetMaxDurability(); ((Character)player).Message((MessageType)1, $"Reparado: {NameOf(item)} por {num} moedas", 0, (Sprite)null); StoreGui.instance.FillList(); } int num2 = MaxUpgradeQuality(item); GUI.enabled = item.m_quality < num2; int num3 = UpgradeCost(item); if (GUILayout.Button((item.m_quality < num2) ? $"q{item.m_quality + 1} ({num3})" : $"q{item.m_quality} max", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(104f), GUILayout.Height(24f) }) && Pay(player, num3)) { item.m_quality++; item.m_durability = item.GetMaxDurability(); MmoUI.RefreshStats(player); ((Character)player).Message((MessageType)1, $"{NameOf(item)} melhorado para q{item.m_quality} por {num3} moedas", 0, (Sprite)null); StoreGui.instance.FillList(); } GUI.enabled = true; GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private void DrawQuests(Player player) { int tier = Mathf.Clamp((PlayerProgress.Get(player)?.Level ?? 1) / 10 + 1, 1, 7); QuestListUI.Draw(player, tier, $"Dia {QuestSystem.CurrentDay()}. Aceite, cumpra e volte para concluir. Trocam a cada dia do mundo e seguem a sua faixa de level."); } private static string NameOf(ItemData item) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) string arg = ColorUtility.ToHtmlStringRGB(MmoUI.MutedC); string text = Localization.instance.Localize(item.m_shared.m_name); string text2 = ((item.m_shared.m_maxQuality > 1) ? $" q{item.m_quality}" : ""); string text3 = ((item.m_stack > 1) ? $" x{item.m_stack}" : ""); return text + text2 + text3; } private static void DrawIcon(ItemData item, float size) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(size, size, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(size), GUILayout.Height(size) }); Sprite val = null; try { val = item.GetIcon(); } catch { } if (!((Object)(object)val == (Object)null) && !((Object)(object)val.texture == (Object)null)) { Texture2D texture = val.texture; Rect textureRect = default(Rect); try { textureRect = val.textureRect; } catch { ((Rect)(ref textureRect))..ctor(0f, 0f, (float)((Texture)texture).width, (float)((Texture)texture).height); } Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height); GUI.DrawTextureWithTexCoords(rect, (Texture)(object)texture, val2, true); } } } [HarmonyPatch(typeof(StoreGui), "UpdateSellButton")] internal static class StoreGui_UpdateSellButton_Patch { private static void Postfix(StoreGui __instance) { if (ModConfig.MerchantEnabled.Value && !((Object)(object)__instance?.m_sellButton == (Object)null) && MerchantSpawner.IsMerchant(__instance.m_trader)) { ((Selectable)__instance.m_sellButton).interactable = false; } } } internal static class PlayerBank { private const string Key = "mmo.bank"; private static readonly ConditionalWeakTable Cache = new ConditionalWeakTable(); public static int Rows => Mathf.Clamp(ModConfig.BankRows.Value, 1, 12); public static Inventory Get(Player player) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown if ((Object)(object)player == (Object)null) { return null; } if (Cache.TryGetValue(player, out var value)) { return value; } value = new Inventory("MMO_Cofre", (Sprite)null, 8, Rows); if (player.m_customData.TryGetValue("mmo.bank", out var value2) && !string.IsNullOrEmpty(value2)) { try { value.Load(new ZPackage(value2)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Cofre] falha ao carregar: " + ex.Message)); } } Cache.Add(player, value); return value; } public static void Save(Player player) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (!((Object)(object)player == (Object)null) && Cache.TryGetValue(player, out var value)) { ZPackage val = new ZPackage(); value.Save(val); player.m_customData["mmo.bank"] = val.GetBase64(); } } public static bool Deposit(Player player, ItemData item, out string message) { message = null; Inventory val = Get(player); Inventory inventory = ((Humanoid)player).GetInventory(); if (val == null || inventory == null || item == null) { return false; } if (item.m_equipped) { message = "Desequipe o item antes de guardar."; return false; } if (!val.CanAddItem(item, -1)) { message = "O cofre esta cheio."; return false; } val.MoveItemToThis(inventory, item); Save(player); return true; } public static bool Withdraw(Player player, ItemData item, out string message) { message = null; Inventory val = Get(player); Inventory inventory = ((Humanoid)player).GetInventory(); if (val == null || inventory == null || item == null) { return false; } if (!inventory.CanAddItem(item, -1)) { message = "Sem espaco na mochila."; return false; } inventory.MoveItemToThis(val, item); Save(player); return true; } } } namespace ValheimMMO.Items { internal static class DropPending { private sealed class Pending { public string Prefab; public float Durability; public int Quality; public float Time; } private static readonly List Queue = new List(); public static void Enqueue(string prefabName, float durabilityFraction, int quality) { Queue.Add(new Pending { Prefab = prefabName, Durability = durabilityFraction, Quality = quality, Time = Time.time }); } internal static void Apply(ItemDrop drop) { if (Queue.Count == 0 || drop?.m_itemData == null) { return; } float now = Time.time; Queue.RemoveAll((Pending p) => now - p.Time > 240f); string name = ((Object)((Component)drop).gameObject).name.Replace("(Clone)", ""); int num = Queue.FindIndex((Pending p) => p.Prefab == name); if (num >= 0) { Pending pending = Queue[num]; Queue.RemoveAt(num); ItemData itemData = drop.m_itemData; if (pending.Quality > 1) { itemData.m_quality = Mathf.Min(pending.Quality, itemData.m_shared.m_maxQuality + Mathf.Max(0, ModConfig.ExtraQualityLevels.Value)); } if (pending.Durability < 0.999f && itemData.m_shared.m_useDurability) { itemData.m_durability = Mathf.Max(1f, itemData.GetMaxDurability() * Mathf.Clamp01(pending.Durability)); } if ((Object)(object)drop.m_nview != (Object)null && drop.m_nview.IsValid() && drop.m_nview.IsOwner()) { drop.Save(); } } } } [HarmonyPatch(typeof(ItemDrop), "Awake")] internal static class ItemDrop_Awake_DropPendingPatch { private static void Postfix(ItemDrop __instance) { DropPending.Apply(__instance); } } internal sealed class CustomItemDef { public string Prefab; public string Name; public string Description; public string Source; public string IconFrom; public int MaxStack = 10; public float Weight = 0.3f; public int Price = 50; public string Effect; public float Amount; public bool Consumed = true; } internal static class CustomItems { internal static readonly CustomItemDef[] Defs; private static readonly Dictionary Prefabs; private static readonly Dictionary ByName; static CustomItems() { Defs = new CustomItemDef[6] { new CustomItemDef { Prefab = "MMO_PocaoVidaMenor", Name = "Pocao de Vida Menor", Description = "Cura 30% da vida maxima na hora. Compartilha recarga com as outras pocoes.", Source = "MeadHealthMinor", IconFrom = "MeadHealthMinor", Price = 40, Effect = "heal", Amount = 0.3f }, new CustomItemDef { Prefab = "MMO_PocaoVida", Name = "Pocao de Vida", Description = "Cura 50% da vida maxima na hora. Compartilha recarga com as outras pocoes.", Source = "MeadHealthMedium", IconFrom = "MeadHealthMedium", Price = 120, Effect = "heal", Amount = 0.5f }, new CustomItemDef { Prefab = "MMO_PocaoVidaGrande", Name = "Pocao de Vida Grande", Description = "Cura 80% da vida maxima na hora. Compartilha recarga com as outras pocoes.", Source = "MeadHealthMajor", IconFrom = "MeadHealthMajor", Price = 350, Effect = "heal", Amount = 0.8f }, new CustomItemDef { Prefab = "MMO_PocaoStamina", Name = "Pocao de Vigor", Description = "Recupera 50% da stamina maxima na hora. Compartilha recarga com as outras pocoes.", Source = "MeadStaminaMinor", IconFrom = "MeadStaminaMinor", Price = 80, Effect = "stamina", Amount = 0.5f }, new CustomItemDef { Prefab = "MMO_PocaoEitr", Name = "Pocao de Eitr", Description = "Recupera 50% do eitr maximo na hora. Compartilha recarga com as outras pocoes.", Source = "MeadEitrMinor", IconFrom = "MeadEitrMinor", Price = 120, Effect = "eitr", Amount = 0.5f }, new CustomItemDef { Prefab = "MMO_PedraRetorno", Name = "Pedra de Retorno", Description = "Ao ser usada, teleporta voce para a sua cama. Nao e consumida. Recarga longa. Respeita as regras de portal para minerios.", Source = "MeadHealthMinor", IconFrom = "Ruby", MaxStack = 1, Weight = 1f, Price = 300, Effect = "return", Amount = 1800f, Consumed = false } }; Prefabs = new Dictionary(StringComparer.Ordinal); ByName = new Dictionary(StringComparer.Ordinal); CustomItemDef[] defs = Defs; foreach (CustomItemDef customItemDef in defs) { ByName[customItemDef.Prefab] = customItemDef; } } public static CustomItemDef DefFor(string prefabName) { if (prefabName == null || !ByName.TryGetValue(prefabName, out var value)) { return null; } return value; } public static CustomItemDef DefFor(ItemData item) { if (!((Object)(object)item?.m_dropPrefab != (Object)null)) { return null; } return DefFor(((Object)item.m_dropPrefab).name); } public static bool IsCustom(string prefabName) { if (prefabName != null) { return ByName.ContainsKey(prefabName); } return false; } public static int PriceOf(string prefabName) { return DefFor(prefabName)?.Price ?? 0; } private static GameObject Ensure(ObjectDB db, CustomItemDef def) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) if (Prefabs.TryGetValue(def.Prefab, out var value) && (Object)(object)value != (Object)null) { return value; } if ((Object)(object)db == (Object)null) { return null; } GameObject itemPrefab = db.GetItemPrefab(def.Source); if ((Object)(object)itemPrefab == (Object)null) { Plugin.Log.LogWarning((object)("[Itens] prefab base '" + def.Source + "' nao encontrado para " + def.Prefab + ".")); return null; } GameObject val = Object.Instantiate(itemPrefab, PrefabHolder.Root); ((Object)val).name = def.Prefab; ItemDrop component = val.GetComponent(); SharedData shared = component.m_itemData.m_shared; shared.m_name = def.Name; shared.m_description = def.Description; shared.m_itemType = (ItemType)2; shared.m_maxStackSize = def.MaxStack; shared.m_weight = def.Weight; shared.m_value = 0; shared.m_questItem = false; shared.m_teleportable = true; shared.m_food = 0f; shared.m_foodStamina = 0f; shared.m_foodEitr = 0f; shared.m_foodRegen = 0f; shared.m_foodBurnTime = 0f; shared.m_consumeStatusEffect = null; shared.m_maxQuality = 1; if (!string.IsNullOrEmpty(def.IconFrom)) { GameObject itemPrefab2 = db.GetItemPrefab(def.IconFrom); Sprite[] array = ((itemPrefab2 == null) ? null : itemPrefab2.GetComponent()?.m_itemData?.m_shared?.m_icons); if (array != null && array.Length != 0 && (Object)(object)array[0] != (Object)null) { shared.m_icons = (Sprite[])(object)new Sprite[1] { array[0] }; } } component.m_itemData.m_quality = 1; component.m_itemData.m_stack = 1; component.m_itemData.m_dropPrefab = val; Prefabs[def.Prefab] = val; return val; } internal static void RegisterInDb(ObjectDB db) { if ((Object)(object)db == (Object)null) { return; } int num = 0; CustomItemDef[] defs = Defs; foreach (CustomItemDef customItemDef in defs) { GameObject val = Ensure(db, customItemDef); if (!((Object)(object)val == (Object)null)) { if (!db.m_items.Contains(val)) { db.m_items.Add(val); } db.m_itemByHash[StringExtensionMethods.GetStableHashCode(customItemDef.Prefab)] = val; num++; } } if ((Object)(object)ZNetScene.instance != (Object)null) { RegisterInScene(ZNetScene.instance); } if (num > 0) { Plugin.Log.LogInfo((object)$"[Itens] {num} itens do mod registrados."); } } internal static void RegisterInScene(ZNetScene scene) { if ((Object)(object)scene == (Object)null) { return; } CustomItemDef[] defs = Defs; foreach (CustomItemDef customItemDef in defs) { GameObject val = Ensure(ObjectDB.instance, customItemDef); if (!((Object)(object)val == (Object)null)) { if (!scene.m_prefabs.Contains(val)) { scene.m_prefabs.Add(val); } scene.m_namedPrefabs[StringExtensionMethods.GetStableHashCode(customItemDef.Prefab)] = val; } } } } internal static class ConsumableEffects { private const string PotionCdKey = "mmo.cd.potion"; private const string ReturnCdKey = "mmo.cd.return"; private static double Now() { if (!((Object)(object)ZNet.instance != (Object)null)) { return Time.timeAsDouble; } return ZNet.instance.GetTimeSeconds(); } private static double ReadCd(Player p, string key) { if (p.m_customData.TryGetValue(key, out var value) && double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return 0.0; } private static void WriteCd(Player p, string key, double readyAt) { p.m_customData[key] = readyAt.ToString("R", CultureInfo.InvariantCulture); } public static float PotionCooldownLeft(Player p) { return (float)Math.Max(0.0, ReadCd(p, "mmo.cd.potion") - Now()); } public static float ReturnCooldownLeft(Player p) { return (float)Math.Max(0.0, ReadCd(p, "mmo.cd.return") - Now()); } public static bool TryUse(Player player, ItemData item, out bool consumed) { //IL_020f: 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_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) consumed = false; CustomItemDef customItemDef = CustomItems.DefFor(item); if (customItemDef == null) { return false; } switch (customItemDef.Effect) { case "heal": case "stamina": case "eitr": { float num2 = PotionCooldownLeft(player); if (num2 > 0f) { ((Character)player).Message((MessageType)2, $"Pocao em recarga ({num2:0}s)", 0, (Sprite)null); return true; } if (customItemDef.Effect == "heal") { float num3 = ((Character)player).GetMaxHealth() * customItemDef.Amount; ((Character)player).Heal(num3, true); ((Character)player).Message((MessageType)1, $"+{num3:0} vida", 0, (Sprite)null); } else if (customItemDef.Effect == "stamina") { float num4 = ((Character)player).GetMaxStamina() * customItemDef.Amount; ((Character)player).AddStamina(num4); ((Character)player).Message((MessageType)1, $"+{num4:0} stamina", 0, (Sprite)null); } else { float num5 = ((Character)player).GetMaxEitr() * customItemDef.Amount; if (num5 <= 0f) { ((Character)player).Message((MessageType)2, "Voce nao tem eitr para recuperar.", 0, (Sprite)null); return true; } ((Character)player).AddEitr(num5); ((Character)player).Message((MessageType)1, $"+{num5:0} eitr", 0, (Sprite)null); } WriteCd(player, "mmo.cd.potion", Now() + (double)Mathf.Max(1f, ModConfig.PotionCooldown.Value)); consumed = customItemDef.Consumed; return true; } case "return": { float num = ReturnCooldownLeft(player); if (num > 0f) { ((Character)player).Message((MessageType)2, $"Pedra de Retorno em recarga ({num / 60f:0} min)", 0, (Sprite)null); return true; } Game instance = Game.instance; PlayerProfile val = ((instance != null) ? instance.GetPlayerProfile() : null); if (val == null || !val.HaveCustomSpawnPoint()) { ((Character)player).Message((MessageType)2, "Voce nao tem uma cama reivindicada.", 0, (Sprite)null); return true; } if (!ModConfig.ReturnStoneIgnoresOre.Value && !((Humanoid)player).GetInventory().IsTeleportable()) { ((Character)player).Message((MessageType)2, "$msg_noteleport", 0, (Sprite)null); return true; } Vector3 customSpawnPoint = val.GetCustomSpawnPoint(); ((Character)player).TeleportTo(customSpawnPoint, ((Component)player).transform.rotation, true); WriteCd(player, "mmo.cd.return", Now() + (double)Mathf.Max(10f, customItemDef.Amount * ModConfig.ReturnStoneCooldownMultiplier.Value)); ((Character)player).Message((MessageType)2, "A pedra o leva para casa.", 0, (Sprite)null); consumed = false; return true; } default: return false; } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDB_Awake_CustomItemsPatch { private static void Postfix(ObjectDB __instance) { CustomItems.RegisterInDb(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDB_CopyOtherDB_CustomItemsPatch { private static void Postfix(ObjectDB __instance) { CustomItems.RegisterInDb(__instance); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetScene_Awake_CustomItemsPatch { private static void Postfix(ZNetScene __instance) { CustomItems.RegisterInScene(__instance); } } [HarmonyPatch(typeof(Player), "ConsumeItem")] internal static class Player_ConsumeItem_CustomItemsPatch { private static bool Prefix(Player __instance, Inventory inventory, ItemData item, ref bool __result) { if (!ConsumableEffects.TryUse(__instance, item, out var consumed)) { return true; } if (consumed) { inventory.RemoveOneItem(item); } __result = consumed; return false; } } internal static class MapItem { public const string PrefabName = "MMO_MapaExplorador"; private const string SourcePrefab = "MeadHealthMinor"; private const string IconPiece = "piece_cartographytable"; private static GameObject _prefab; private static readonly MethodInfo ExploreMethod = AccessTools.Method(typeof(Minimap), "Explore", new Type[2] { typeof(Vector3), typeof(float) }, (Type[])null); private static readonly MethodInfo IsExploredMethod = AccessTools.Method(typeof(Minimap), "IsExplored", new Type[1] { typeof(Vector3) }, (Type[])null); public static bool IsMap(ItemData item) { if ((Object)(object)item?.m_dropPrefab != (Object)null) { return ((Object)item.m_dropPrefab).name == "MMO_MapaExplorador"; } return false; } public static bool IsMap(string prefabName) { return prefabName == "MMO_MapaExplorador"; } private static GameObject EnsurePrefab(ObjectDB db) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_prefab != (Object)null) { return _prefab; } if ((Object)(object)db == (Object)null) { return null; } GameObject itemPrefab = db.GetItemPrefab("MeadHealthMinor"); if ((Object)(object)itemPrefab == (Object)null) { Plugin.Log.LogWarning((object)"[Mapa] prefab base 'MeadHealthMinor' nao encontrado; item nao criado."); return null; } GameObject val = Object.Instantiate(itemPrefab, PrefabHolder.Root); ((Object)val).name = "MMO_MapaExplorador"; ItemDrop component = val.GetComponent(); SharedData shared = component.m_itemData.m_shared; shared.m_name = "Mapa do Explorador"; shared.m_description = "Um mapa antigo e rabiscado. Ao ser lido, revela uma regiao ainda desconhecida do mundo e marca o local."; shared.m_itemType = (ItemType)2; shared.m_maxStackSize = 10; shared.m_weight = 0.3f; shared.m_value = 0; shared.m_questItem = false; shared.m_teleportable = true; shared.m_food = 0f; shared.m_foodStamina = 0f; shared.m_foodEitr = 0f; shared.m_foodRegen = 0f; shared.m_foodBurnTime = 0f; shared.m_consumeStatusEffect = null; shared.m_maxQuality = 1; ZNetScene instance = ZNetScene.instance; object obj; if (instance == null) { obj = null; } else { GameObject prefab = instance.GetPrefab("piece_cartographytable"); obj = ((prefab == null) ? null : prefab.GetComponent()?.m_icon); } Sprite val2 = (Sprite)obj; if ((Object)(object)val2 != (Object)null) { shared.m_icons = (Sprite[])(object)new Sprite[1] { val2 }; } component.m_itemData.m_quality = 1; component.m_itemData.m_stack = 1; component.m_itemData.m_dropPrefab = val; _prefab = val; Plugin.Log.LogInfo((object)"[Mapa] Item criado."); return _prefab; } internal static void RegisterInDb(ObjectDB db) { GameObject val = EnsurePrefab(db); if (!((Object)(object)val == (Object)null) && !((Object)(object)db == (Object)null)) { if (!db.m_items.Contains(val)) { db.m_items.Add(val); } db.m_itemByHash[StringExtensionMethods.GetStableHashCode("MMO_MapaExplorador")] = val; if ((Object)(object)ZNetScene.instance != (Object)null) { RegisterInScene(ZNetScene.instance); } } } internal static void RegisterInScene(ZNetScene scene) { GameObject val = EnsurePrefab(ObjectDB.instance); if (!((Object)(object)val == (Object)null) && !((Object)(object)scene == (Object)null)) { if (!scene.m_prefabs.Contains(val)) { scene.m_prefabs.Add(val); } scene.m_namedPrefabs[StringExtensionMethods.GetStableHashCode("MMO_MapaExplorador")] = val; } } private static bool IsExplored(Minimap mm, Vector3 p) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (IsExploredMethod == null) { return false; } try { return (bool)IsExploredMethod.Invoke(mm, new object[1] { p }); } catch { return false; } } public static bool Use(Player player) { //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Invalid comparison between Unknown and I4 Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || ExploreMethod == null) { ((Character)player).Message((MessageType)2, "O mapa esta ilegivel aqui.", 0, (Sprite)null); return false; } float num = Mathf.Max(500f, ModConfig.MapWorldRadius.Value); float num2 = Mathf.Max(50f, ModConfig.MapRevealRadius.Value); Vector3 val = Vector3.zero; bool flag = false; WorldGenerator instance2 = WorldGenerator.instance; Vector3 val2 = default(Vector3); for (int i = 0; i < 2; i++) { if (flag) { break; } for (int j = 0; j < 60; j++) { float num3 = Random.value * MathF.PI * 2f; float num4 = Mathf.Sqrt(Random.value) * num; ((Vector3)(ref val2))..ctor(Mathf.Cos(num3) * num4, 0f, Mathf.Sin(num3) * num4); if (!IsExplored(instance, val2) && (i != 0 || instance2 == null || (int)instance2.GetBiome(val2) != 256)) { val = val2; flag = true; break; } } } if (!flag) { float num5 = Random.value * MathF.PI * 2f; float num6 = Mathf.Sqrt(Random.value) * num; ((Vector3)(ref val))..ctor(Mathf.Cos(num5) * num6, 0f, Mathf.Sin(num5) * num6); } try { ExploreMethod.Invoke(instance, new object[2] { val, num2 }); } catch (Exception ex) { Plugin.Log.LogError((object)("[Mapa] Explore falhou: " + ex.Message)); ((Character)player).Message((MessageType)2, "O mapa esta ilegivel aqui.", 0, (Sprite)null); return false; } try { instance.AddPin(val, (PinType)3, "Mapa do Explorador", true, false, 0L, default(PlatformUserID)); } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[Mapa] Pin falhou: " + ex2.Message)); } Vector3 val3 = val - ((Component)player).transform.position; Vector2 val4 = new Vector2(val3.x, val3.z); float num7 = ((Vector2)(ref val4)).magnitude / 1000f; ((Character)player).Message((MessageType)2, $"O mapa revela uma regiao a {num7:0.0} km {Compass(val3)}", 0, (Sprite)null); return true; } private static string Compass(Vector3 d) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Atan2(d.x, d.z) * 57.29578f; if (num < 0f) { num += 360f; } string[] obj = new string[8] { "ao norte", "a nordeste", "a leste", "a sudeste", "ao sul", "a sudoeste", "a oeste", "a noroeste" }; int num2 = Mathf.RoundToInt(num / 45f) % 8; return obj[num2]; } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDB_Awake_MapItemPatch { private static void Postfix(ObjectDB __instance) { MapItem.RegisterInDb(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDB_CopyOtherDB_MapItemPatch { private static void Postfix(ObjectDB __instance) { MapItem.RegisterInDb(__instance); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class ZNetScene_Awake_MapItemPatch { private static void Postfix(ZNetScene __instance) { MapItem.RegisterInScene(__instance); } } [HarmonyPatch(typeof(Player), "ConsumeItem")] internal static class Player_ConsumeItem_MapPatch { private static bool Prefix(Player __instance, Inventory inventory, ItemData item, ref bool __result) { if (!MapItem.IsMap(item)) { return true; } __result = MapItem.Use(__instance); if (__result) { inventory.RemoveOneItem(item); } return false; } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] internal static class CharacterDrop_GenerateDropList_MapPatch { private static void Postfix(CharacterDrop __instance, ref List> __result) { if (__result != null && !((Object)(object)__instance?.m_character == (Object)null) && __instance.m_character.IsBoss() && (WorldBoss.IsWorldBoss(__instance.m_character, out var _) || !(Random.value > ModConfig.MapBossDropChance.Value))) { ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab("MMO_MapaExplorador") : null); if (!((Object)(object)val == (Object)null)) { __result.Add(new KeyValuePair(val, 1)); } } } } internal static class PrefabHolder { private static GameObject _root; public static Transform Root { get { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)_root == (Object)null) { _root = new GameObject("ValheimMMO_Prefabs"); _root.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_root); } return _root.transform; } } } } namespace ValheimMMO.InventorySlots { internal static class CarrySlots { public const int BaseRows = 4; public const float BaseCarryKg = 300f; private static float _lastWarn; public static int DesiredRows(Player player) { if (!ModConfig.SlotsEnabled.Value || (Object)(object)player == (Object)null) { return 4; } float num = Mathf.Max(8f, ModConfig.SlotsKgPerRow.Value); float num2 = player.GetMaxCarryWeight() - 300f; int num3 = ((!(num2 <= 0f)) ? Mathf.FloorToInt(num2 / num) : 0); return Mathf.Clamp(4 + num3, 4, Mathf.Max(4, ModConfig.SlotsMaxRows.Value)); } public static void Apply(Player player) { if ((Object)(object)player == (Object)null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } int num = DesiredRows(player); int height = inventory.m_height; if (height == num) { return; } if (num < height) { foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem.m_gridPos.y >= num && !EquipmentRow.IsVirtual(allItem)) { if (Time.time - _lastWarn > 30f && (Object)(object)player == (Object)(object)Player.m_localPlayer) { _lastWarn = Time.time; ((Character)player).Message((MessageType)1, "Peso maximo caiu: esvazie as ultimas linhas da mochila para elas fecharem.", 0, (Sprite)null); } return; } } } inventory.m_height = num; inventory.Changed(); if ((Object)(object)player == (Object)(object)Player.m_localPlayer && num > height) { ((Character)player).Message((MessageType)1, $"Mochila: +{(num - height) * inventory.m_width} slots", 0, (Sprite)null); } } } [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class Player_UpdateStats_SlotsPatch { private static float _timer; private static void Postfix(Player __instance, float dt) { _timer += dt; if (!(_timer < 1f)) { _timer = 0f; CarrySlots.Apply(__instance); } } } [HarmonyPatch(typeof(Player), "Load")] internal static class Player_Load_SlotsPatch { private static void Postfix(Player __instance) { Inventory val = ((__instance != null) ? ((Humanoid)__instance).GetInventory() : null); if (val == null) { return; } int num = val.m_height - 1; foreach (ItemData allItem in val.GetAllItems()) { if (allItem.m_gridPos.y > num && !EquipmentRow.IsVirtual(allItem)) { num = allItem.m_gridPos.y; } } int num2 = Mathf.Max(val.m_height, num + 1); int num3 = CarrySlots.DesiredRows(__instance); int num4 = Mathf.Max(num2, num3); if (num4 != val.m_height) { val.m_height = num4; if ((Object)(object)Game.instance != (Object)null) { val.Changed(); } } } } internal static class EquipmentRow { public sealed class Slot { public string Name; public int X; public int Y; public Slot(string n, int x, int y) { Name = n; X = x; Y = y; } } public const int VirtualRow = 100; public const int OverflowRow = 102; public static readonly Slot[] Slots = new Slot[10] { new Slot("Arma", 0, 100), new Slot("Mao esq.", 1, 100), new Slot("Arco", 2, 100), new Slot("Flechas", 3, 100), new Slot("Capacete", 4, 100), new Slot("Peito", 5, 100), new Slot("Pernas", 6, 100), new Slot("Capa", 7, 100), new Slot("Utilitario", 0, 101), new Slot("Amuleto", 1, 101) }; public static bool Enabled => ModConfig.EquipmentRowEnabled.Value; public static bool IsVirtual(ItemData item) { if (item != null) { return item.m_gridPos.y >= 100; } return false; } public static bool IsPlayerInventory(Inventory inv) { if (inv != null && (Object)(object)Player.m_localPlayer != (Object)null) { return inv == ((Humanoid)Player.m_localPlayer).GetInventory(); } return false; } public static Slot SlotFor(ItemData item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected I4, but got Unknown //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 if (item?.m_shared == null) { return null; } ItemType itemType = item.m_shared.m_itemType; switch (itemType - 3) { case 1: return Slots[2]; case 0: case 11: case 19: if ((int)item.m_shared.m_skillType != 14) { return Slots[0]; } return Slots[2]; case 2: case 12: return Slots[1]; case 6: return Slots[3]; case 3: return Slots[4]; case 4: return Slots[5]; case 8: return Slots[6]; case 14: return Slots[7]; case 15: return Slots[8]; case 21: return Slots[9]; default: return null; } } public static ItemData ItemIn(Inventory inv, Slot slot) { if (inv == null) { return null; } return inv.GetItemAt(slot.X, slot.Y); } public static Vector2i FindBagSlot(Inventory inv) { //IL_0036: 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) for (int i = 0; i < inv.m_height; i++) { for (int j = 0; j < inv.m_width; j++) { if (inv.GetItemAt(j, i) == null) { return new Vector2i(j, i); } } } return new Vector2i(-1, -1); } private static Vector2i FindOverflowSlot(Inventory inv) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < inv.m_width; i++) { if (inv.GetItemAt(i, 102) == null) { return new Vector2i(i, 102); } } return new Vector2i(inv.m_width, 102); } public static void PlaceInSlot(Inventory inv, ItemData item) { //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) if (!Enabled || inv == null || item == null || !IsPlayerInventory(inv)) { return; } Slot slot = SlotFor(item); if (slot == null || (item.m_gridPos.x == slot.X && item.m_gridPos.y == slot.Y)) { return; } ItemData val = ItemIn(inv, slot); if (val != null && val != item) { if (val.m_equipped) { return; } MoveToBagOrOverflow(inv, val); } item.m_gridPos = new Vector2i(slot.X, slot.Y); inv.Changed(); } public static void ReturnToBag(Inventory inv, ItemData item) { if (Enabled && inv != null && item != null && IsPlayerInventory(inv) && IsVirtual(item)) { MoveToBagOrOverflow(inv, item); inv.Changed(); } } private static void MoveToBagOrOverflow(Inventory inv, ItemData item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Vector2i val = FindBagSlot(inv); if (val.x >= 0) { item.m_gridPos = val; } else if (item.m_gridPos.y != 102) { item.m_gridPos = FindOverflowSlot(inv); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, "Mochila cheia: " + Localization.instance.Localize(item.m_shared.m_name) + " ficou no painel de equipamento.", 0, (Sprite)null); } } } public static bool TryStore(Inventory inv, ItemData item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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) Vector2i val = FindBagSlot(inv); if (val.x < 0) { return false; } item.m_gridPos = val; inv.Changed(); return true; } public static void Validate(Player player) { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) if (!Enabled || (Object)(object)player == (Object)null) { return; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return; } bool flag = false; foreach (ItemData item in new List(inventory.GetAllItems())) { if (item.m_equipped) { Slot slot = SlotFor(item); if (slot == null || (item.m_gridPos.x == slot.X && item.m_gridPos.y == slot.Y)) { continue; } ItemData val = ItemIn(inventory, slot); if (val == null || !val.m_equipped) { if (val != null) { MoveToBagOrOverflow(inventory, val); } item.m_gridPos = new Vector2i(slot.X, slot.Y); flag = true; } } else if (IsVirtual(item)) { Vector2i val2 = FindBagSlot(inventory); if (val2.x >= 0) { item.m_gridPos = val2; flag = true; } else if (item.m_gridPos.y != 102) { item.m_gridPos = FindOverflowSlot(inventory); flag = true; } } } if (flag) { inventory.Changed(); } } public static List OverflowItems(Inventory inv) { List list = new List(); if (inv == null) { return list; } foreach (ItemData allItem in inv.GetAllItems()) { if (IsVirtual(allItem) && !allItem.m_equipped) { list.Add(allItem); } } return list; } } [HarmonyPatch(typeof(Humanoid), "EquipItem")] internal static class Humanoid_EquipItem_RowPatch { private static void Postfix(Humanoid __instance, ItemData item, bool __result) { if (__result) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer)) { EquipmentRow.PlaceInSlot(((Humanoid)val).GetInventory(), item); } } } } [HarmonyPatch(typeof(Humanoid), "UnequipItem")] internal static class Humanoid_UnequipItem_RowPatch { private static void Postfix(Humanoid __instance, ItemData item) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && item != null && !item.m_equipped) { EquipmentRow.ReturnToBag(((Humanoid)val).GetInventory(), item); } } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] internal static class InventoryGrid_UpdateGui_RowPatch { private static readonly List Hidden = new List(); private static Inventory _inv; private static void Prefix(InventoryGrid __instance) { _inv = null; Inventory inventory = __instance.m_inventory; if (inventory == null || !EquipmentRow.Enabled) { return; } Hidden.Clear(); foreach (ItemData item in inventory.m_inventory) { if (EquipmentRow.IsVirtual(item)) { Hidden.Add(item); } } if (Hidden.Count == 0) { return; } _inv = inventory; foreach (ItemData item2 in Hidden) { inventory.m_inventory.Remove(item2); } } private static void Postfix() { if (_inv == null) { return; } foreach (ItemData item in Hidden) { if (!_inv.m_inventory.Contains(item)) { _inv.m_inventory.Add(item); } } Hidden.Clear(); _inv = null; } private static Exception Finalizer(Exception __exception) { Postfix(); return __exception; } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData), typeof(int), typeof(int), typeof(int) })] internal static class Inventory_AddItemAt_RowPatch { private static bool Prefix(Inventory __instance, ItemData item, int amount, int x, int y, ref bool __result) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (item == null) { return true; } if (y < 100) { if (y >= __instance.m_height && x >= 0 && x < __instance.m_width && y < 40) { __instance.m_height = y + 1; } return true; } amount = Mathf.Min(amount, item.m_stack); if (__instance.GetItemAt(x, y) != null) { y = 102; x = 0; while (__instance.GetItemAt(x, y) != null) { x++; } } ItemData val = item.Clone(); val.m_stack = amount; val.m_gridPos = new Vector2i(x, y); __instance.m_inventory.Add(val); item.m_stack -= amount; __instance.Changed(); __result = true; return false; } } [HarmonyPatch(typeof(Inventory), "GetEmptySlots")] internal static class Inventory_GetEmptySlots_RowPatch { private static void Postfix(Inventory __instance, ref int __result) { if (!EquipmentRow.Enabled) { return; } int num = 0; foreach (ItemData item in __instance.m_inventory) { if (EquipmentRow.IsVirtual(item)) { num++; } } if (num > 0) { __result = Mathf.Min(__instance.m_width * __instance.m_height, __result + num); } } } [HarmonyPatch(typeof(Inventory), "HaveEmptySlot")] internal static class Inventory_HaveEmptySlot_RowPatch { private static void Postfix(Inventory __instance, ref bool __result) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!__result && EquipmentRow.Enabled) { __result = EquipmentRow.FindBagSlot(__instance).x >= 0; } } } [HarmonyPatch(typeof(Inventory), "MoveInventoryToGrave")] internal static class Inventory_MoveInventoryToGrave_RowPatch { private static void Postfix(Inventory __instance) { //IL_00b7: 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_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (ItemData item in __instance.m_inventory) { if (EquipmentRow.IsVirtual(item)) { list.Add(item); } } if (list.Count == 0) { return; } Vector2i val = default(Vector2i); foreach (ItemData item2 in list) { ((Vector2i)(ref val))..ctor(-1, -1); for (int i = 0; i < __instance.m_height; i++) { if (val.x >= 0) { break; } for (int j = 0; j < __instance.m_width; j++) { if (__instance.GetItemAt(j, i) == null) { ((Vector2i)(ref val))..ctor(j, i); break; } } } if (val.x < 0) { __instance.m_height++; ((Vector2i)(ref val))..ctor(0, __instance.m_height - 1); } item2.m_gridPos = val; } } } [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class Player_UpdateStats_RowPatch { private static float _timer; private static void Postfix(Player __instance, float dt) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { _timer += dt; if (!(_timer < 1f)) { _timer = 0f; EquipmentRow.Validate(__instance); } } } } [HarmonyPatch(typeof(InventoryGui), "UpdateInventory")] internal static class InventoryGui_UpdateInventory_ResizePatch { private static float _basePanelHeight = -1f; private static float _baseGridHeight = -1f; private static int _lastRows = -1; private static InventoryGui _gui; private static void Postfix(InventoryGui __instance, Player player) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) Inventory val = ((player != null) ? ((Humanoid)player).GetInventory() : null); RectTransform val2 = __instance?.m_player; InventoryGrid val3 = __instance?.m_playerGrid; if (val == null || (Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null) { return; } Transform transform = ((Component)val3).transform; RectTransform val4 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if (!((Object)(object)val4 == (Object)null)) { if ((Object)(object)_gui != (Object)(object)__instance) { _gui = __instance; _lastRows = -1; Rect rect = val2.rect; _basePanelHeight = ((Rect)(ref rect)).height; rect = val4.rect; _baseGridHeight = ((Rect)(ref rect)).height; } int height = val.GetHeight(); if (height != _lastRows) { _lastRows = height; float num = (float)Mathf.Max(0, height - 4) * val3.m_elementSpace; val2.SetSizeWithCurrentAnchors((Axis)1, _basePanelHeight + num); val4.SetSizeWithCurrentAnchors((Axis)1, _baseGridHeight + num); val3.ResetView(); } } } } } namespace ValheimMMO.Hunger { [HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })] internal static class Player_UpdateStats_HungerPatch { private static void Postfix(Player __instance, float dt) { HungerSystem.Tick(__instance, dt); } } [HarmonyPatch(typeof(Player), "EatFood")] internal static class Player_EatFood_Patch { private static bool Prefix(Player __instance, ItemData item, ref bool __result) { if (!ModConfig.HungerEnabled.Value) { return true; } __result = HungerSystem.CanEatNow(__instance, item, showMessage: true); return false; } private static void Postfix(Player __instance, ItemData item, bool __result) { if (__result) { HungerSystem.Eat(__instance, item); } } } [HarmonyPatch(typeof(Player), "CanEat")] internal static class Player_CanEat_Patch { private static bool Prefix(Player __instance, ItemData item, bool showMessages, ref bool __result) { if (!ModConfig.HungerEnabled.Value) { return true; } __result = HungerSystem.CanEatNow(__instance, item, showMessages); return false; } } [HarmonyPatch(typeof(SEMan), "ModifyHealthRegen")] internal static class SEMan_ModifyHealthRegen_Patch { private static void Postfix(SEMan __instance, ref float regenMultiplier) { if (TryGetTrackedPlayer(__instance, out var player)) { PlayerProgress playerProgress = PlayerProgress.Get(player); if (playerProgress != null && !(playerProgress.Satiety < 0f)) { regenMultiplier *= HungerSystem.HealthRegenMultiplier(HungerSystem.StateOf(playerProgress.Satiety)); } } } internal static bool TryGetTrackedPlayer(SEMan seman, out Player player) { player = null; if (!ModConfig.HungerEnabled.Value) { return false; } Character obj = seman?.m_character; Player val = (Player)(object)((obj is Player) ? obj : null); if (val != null && (Object)(object)val == (Object)(object)Player.m_localPlayer) { player = val; return true; } return false; } } [HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")] internal static class SEMan_ModifyStaminaRegen_Patch { private static void Postfix(SEMan __instance, ref float staminaMultiplier) { if (SEMan_ModifyHealthRegen_Patch.TryGetTrackedPlayer(__instance, out var player)) { PlayerProgress playerProgress = PlayerProgress.Get(player); if (playerProgress != null && !(playerProgress.Satiety < 0f)) { staminaMultiplier *= HungerSystem.StaminaRegenMultiplier(HungerSystem.StateOf(playerProgress.Satiety)); } } } } [HarmonyPatch(typeof(SEMan), "ModifyEitrRegen")] internal static class SEMan_ModifyEitrRegen_Patch { private static void Postfix(SEMan __instance, ref float eitrMultiplier) { if (SEMan_ModifyHealthRegen_Patch.TryGetTrackedPlayer(__instance, out var player)) { PlayerProgress playerProgress = PlayerProgress.Get(player); if (playerProgress != null && !(playerProgress.Satiety < 0f)) { eitrMultiplier *= HungerSystem.EitrRegenMultiplier(HungerSystem.StateOf(playerProgress.Satiety)); } } } } internal static class HungerStatusIcon { private const string BuffName = "MMO_BemAlimentado"; private const string DebuffName = "MMO_Fome"; private static SE_Stats _buff; private static SE_Stats _debuff; private static bool _iconsSet; private static SE_Stats Make(string name, string display, string tooltip) { SE_Stats obj = ScriptableObject.CreateInstance(); ((Object)obj).name = name; ((StatusEffect)obj).m_name = display; ((StatusEffect)obj).m_tooltip = tooltip; ((StatusEffect)obj).m_ttl = 0f; ((StatusEffect)obj).m_category = "mmo_hunger"; return obj; } internal static void Register(ObjectDB db) { if (!((Object)(object)db == (Object)null)) { if ((Object)(object)_buff == (Object)null) { _buff = Make("MMO_BemAlimentado", "Bem alimentado", $"Saciedade alta. +{(ModConfig.WellFedRegenMult.Value - 1f) * 100f:0}% de regeneracao de vida e stamina."); } if ((Object)(object)_debuff == (Object)null) { _debuff = Make("MMO_Fome", "Com fome", "Regeneracao reduzida. Coma algo."); } if (!db.m_StatusEffects.Contains((StatusEffect)(object)_buff)) { db.m_StatusEffects.Add((StatusEffect)(object)_buff); } if (!db.m_StatusEffects.Contains((StatusEffect)(object)_debuff)) { db.m_StatusEffects.Add((StatusEffect)(object)_debuff); } } } private static void EnsureIcons() { if (_iconsSet) { return; } Hud instance = Hud.instance; object obj; if (instance == null) { obj = null; } else { Image foodIcon = instance.m_foodIcon; obj = ((foodIcon != null) ? foodIcon.sprite : null); } if (!((Object)obj == (Object)null)) { Sprite sprite = Hud.instance.m_foodIcon.sprite; if ((Object)(object)_buff != (Object)null) { ((StatusEffect)_buff).m_icon = sprite; } if ((Object)(object)_debuff != (Object)null) { ((StatusEffect)_debuff).m_icon = sprite; } _iconsSet = true; } } internal static void Apply(Player player, HungerState state) { if (!ModConfig.HungerStatusIcons.Value || (Object)(object)player == (Object)null || (Object)(object)_buff == (Object)null || (Object)(object)_debuff == (Object)null) { return; } EnsureIcons(); SEMan sEMan = ((Character)player).GetSEMan(); if (sEMan == null) { return; } bool num = state == HungerState.WellFed; bool flag = state == HungerState.Hungry || state == HungerState.Starving || state == HungerState.Starved; int num2 = ((StatusEffect)_buff).NameHash(); int num3 = ((StatusEffect)_debuff).NameHash(); if (num && !sEMan.HaveStatusEffect(num2)) { sEMan.AddStatusEffect(num2, false, 0, 0f); } if (!num && sEMan.HaveStatusEffect(num2)) { sEMan.RemoveStatusEffect(num2, true); } if (flag && !sEMan.HaveStatusEffect(num3)) { sEMan.AddStatusEffect(num3, false, 0, 0f); } if (!flag && sEMan.HaveStatusEffect(num3)) { sEMan.RemoveStatusEffect(num3, true); } if (flag) { StatusEffect statusEffect = sEMan.GetStatusEffect(num3); if ((Object)(object)statusEffect != (Object)null) { statusEffect.m_name = HungerSystem.DisplayName(state); StatusEffect val = statusEffect; val.m_tooltip = state switch { HungerState.Hungry => "Regeneracao de vida e stamina reduzida. Coma algo.", HungerState.Starving => "Sem regeneracao de vida. Stamina e eitr lentos. Coma agora.", _ => "Voce esta morrendo de fome. Perdendo vida.", }; } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDB_Awake_HungerIconPatch { private static void Postfix(ObjectDB __instance) { HungerStatusIcon.Register(__instance); } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDB_CopyOtherDB_HungerIconPatch { private static void Postfix(ObjectDB __instance) { HungerStatusIcon.Register(__instance); } } public enum HungerState { WellFed, Fed, Hungry, Starving, Starved } public static class HungerSystem { private static float _damageTimer; private static float _saveTimer; private static HungerState _lastState = HungerState.Fed; public static float Max => Mathf.Max(1f, ModConfig.HungerMax.Value); public static HungerState StateOf(float satiety) { if (satiety <= 0f) { return HungerState.Starved; } if (satiety < ModConfig.StarvingThreshold.Value) { return HungerState.Starving; } if (satiety < ModConfig.HungryThreshold.Value) { return HungerState.Hungry; } if (satiety >= ModConfig.WellFedThreshold.Value) { return HungerState.WellFed; } return HungerState.Fed; } public static string DisplayName(HungerState s) { return s switch { HungerState.WellFed => "Bem alimentado", HungerState.Fed => "Saciado", HungerState.Hungry => "Com fome", HungerState.Starving => "Faminto", HungerState.Starved => "Inanicao", _ => "", }; } public static Color ColorOf(HungerState s) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) return (Color)(s switch { HungerState.WellFed => new Color(0.45f, 0.9f, 0.4f), HungerState.Fed => new Color(0.55f, 0.78f, 0.35f), HungerState.Hungry => new Color(0.9f, 0.75f, 0.25f), HungerState.Starving => new Color(0.88f, 0.42f, 0.15f), _ => new Color(0.8f, 0.18f, 0.18f), }); } public static void Tick(Player player, float dt) { if (!ModConfig.HungerEnabled.Value || (Object)(object)player == (Object)null || dt <= 0f) { return; } PlayerProgress playerProgress = PlayerProgress.Get(player); if (playerProgress != null) { if (playerProgress.Satiety < 0f) { playerProgress.Satiety = Max; } float num = Max / Mathf.Max(1f, ModConfig.HungerDrainMinutes.Value * 60f); playerProgress.Satiety = Mathf.Clamp(playerProgress.Satiety - num * dt, 0f, Max); HungerState hungerState = StateOf(playerProgress.Satiety); if (hungerState != _lastState) { AnnounceStateChange(player, _lastState, hungerState); _lastState = hungerState; } HungerStatusIcon.Apply(player, hungerState); if (hungerState == HungerState.Starved) { ApplyStarvationDamage(player, dt); } else { _damageTimer = 0f; } _saveTimer += dt; if (_saveTimer >= 5f) { _saveTimer = 0f; playerProgress.SaveTo(player); } } } private static void AnnounceStateChange(Player player, HungerState from, HungerState to) { if (to > from) { ((Character)player).Message((MessageType)2, to switch { HungerState.Hungry => "Voce esta com fome", HungerState.Starving => "Voce esta faminto", HungerState.Starved => "Voce esta morrendo de fome", _ => "", }, 0, (Sprite)null); } } private static void ApplyStarvationDamage(Player player, float dt) { //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_008d: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown float num = Mathf.Max(0.5f, ModConfig.StarvationDamageInterval.Value); _damageTimer += dt; if (_damageTimer < num) { return; } _damageTimer = 0f; float value = ModConfig.StarvationDamage.Value; if (value <= 0f) { return; } if (!ModConfig.StarvationCanKill.Value && ((Character)player).GetHealth() - value <= 1f) { if (((Character)player).GetHealth() > 1f) { ((Character)player).SetHealth(1f); } return; } HitData val = new HitData { m_damage = { m_damage = value }, m_point = ((Character)player).GetCenterPoint(), m_dir = Vector3.up, m_hitType = (HitType)14 }; ((Character)player).Damage(val); } public static float SatietyFrom(ItemData item) { if (item?.m_shared == null) { return 0f; } return item.m_shared.m_foodBurnTime * ModConfig.SatietyPerBurnSecond.Value; } public static bool CanEatNow(Player player, ItemData item, bool showMessage) { if (!ModConfig.HungerEnabled.Value || (Object)(object)player == (Object)null || item == null) { return true; } PlayerProgress playerProgress = PlayerProgress.Get(player); if (playerProgress == null) { return true; } if (playerProgress.Satiety < Max - 0.5f) { return true; } if (showMessage) { ((Character)player).Message((MessageType)2, "Voce esta saciado.", 0, (Sprite)null); } return false; } public static void Eat(Player player, ItemData item) { if (!ModConfig.HungerEnabled.Value) { return; } PlayerProgress playerProgress = PlayerProgress.Get(player); if (playerProgress != null) { if (playerProgress.Satiety < 0f) { playerProgress.Satiety = 0f; } float num = SatietyFrom(item); float satiety = playerProgress.Satiety; playerProgress.Satiety = Mathf.Clamp(playerProgress.Satiety + num, 0f, Max); playerProgress.SaveTo(player); float num2 = playerProgress.Satiety - satiety; ((Character)player).Message((MessageType)2, $"+{num2:0} saciedade ({playerProgress.Satiety:0}/{Max:0})", 0, (Sprite)null); } } public static float HealthRegenMultiplier(HungerState s) { return s switch { HungerState.WellFed => ModConfig.WellFedRegenMult.Value, HungerState.Hungry => ModConfig.HungryHealthRegenMult.Value, HungerState.Starving => ModConfig.StarvingHealthRegenMult.Value, HungerState.Starved => 0f, _ => 1f, }; } public static float StaminaRegenMultiplier(HungerState s) { return s switch { HungerState.WellFed => ModConfig.WellFedRegenMult.Value, HungerState.Hungry => ModConfig.HungryStaminaRegenMult.Value, HungerState.Starving => ModConfig.StarvingStaminaRegenMult.Value, HungerState.Starved => ModConfig.StarvingStaminaRegenMult.Value * 0.5f, _ => 1f, }; } public static float EitrRegenMultiplier(HungerState s) { return s switch { HungerState.Starving => ModConfig.StarvingEitrRegenMult.Value, HungerState.Starved => ModConfig.StarvingEitrRegenMult.Value * 0.5f, _ => 1f, }; } } } namespace ValheimMMO.Dungeons { internal static class BossRaids { private sealed class BossRef { public string Key; public string Prefab; public string Title; public int Order; } private const string Prefix = "mmo_bossraid_"; private const string CooldownKey = "mmo_bossraid_cd"; private static readonly BossRef[] Bosses = new BossRef[7] { new BossRef { Key = "defeated_eikthyr", Prefab = "Eikthyr", Title = "Eikthyr", Order = 1 }, new BossRef { Key = "defeated_gdking", Prefab = "gd_king", Title = "O Anciao", Order = 2 }, new BossRef { Key = "defeated_bonemass", Prefab = "Bonemass", Title = "Bonemass", Order = 3 }, new BossRef { Key = "defeated_dragon", Prefab = "Dragon", Title = "Moder", Order = 4 }, new BossRef { Key = "defeated_goblinking", Prefab = "GoblinKing", Title = "Yagluth", Order = 5 }, new BossRef { Key = "defeated_queen", Prefab = "SeekerQueen", Title = "A Rainha", Order = 6 }, new BossRef { Key = "defeated_fader", Prefab = "Fader", Title = "Fader", Order = 7 } }; private static float _timer; private const string RpcKilled = "ValheimMMO_BossRaidKilled"; internal static void Inject(RandEventSystem sys) { //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Expected O, but got Unknown if (!ModConfig.BossRaidEnabled.Value || (Object)(object)sys == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } foreach (RandomEvent @event in sys.m_events) { if (@event != null && @event.m_name.StartsWith("mmo_bossraid_")) { return; } } List list = new List(); HashSet hashSet = new HashSet(); foreach (RandomEvent event2 in sys.m_events) { if (event2 == null || !event2.m_enabled || !event2.m_random || !event2.m_name.StartsWith("army_")) { continue; } BossRef bossRef = null; foreach (string requiredGlobalKey in event2.m_requiredGlobalKeys) { BossRef[] bosses = Bosses; foreach (BossRef bossRef2 in bosses) { if (string.Equals(bossRef2.Key, requiredGlobalKey, StringComparison.OrdinalIgnoreCase) && (bossRef == null || bossRef2.Order > bossRef.Order)) { bossRef = bossRef2; } } } if (bossRef != null && !hashSet.Contains(bossRef.Prefab)) { GameObject prefab = ZNetScene.instance.GetPrefab(bossRef.Prefab); if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab.GetComponent() == (Object)null)) { RandomEvent val = event2.Clone(); val.m_name = "mmo_bossraid_" + bossRef.Prefab; val.m_startMessage = bossRef.Title + " retorna para se vingar!"; val.m_endMessage = ""; val.m_duration = Mathf.Max(event2.m_duration, ModConfig.BossRaidMinDuration.Value); val.m_notRequiredGlobalKeys = new List(event2.m_notRequiredGlobalKeys) { "mmo_bossraid_cd" }; val.m_biome = (Biome)(-1); SpawnData val2 = ((event2.m_spawn.Count > 0) ? event2.m_spawn[0] : null); SpawnData item = new SpawnData { m_name = "MMO " + bossRef.Title, m_enabled = true, m_prefab = prefab, m_maxSpawned = 1, m_groupSizeMin = 1, m_groupSizeMax = 1, m_spawnChance = 100f, m_spawnInterval = 1f, m_spawnDistance = 40f, m_spawnRadiusMin = (val2?.m_spawnRadiusMin ?? 0f), m_spawnRadiusMax = (val2?.m_spawnRadiusMax ?? 0f), m_biome = (Biome)(-1), m_biomeArea = (BiomeArea)3, m_spawnAtDay = true, m_spawnAtNight = true, m_minAltitude = -1000f, m_maxAltitude = 1000f, m_minTilt = 0f, m_maxTilt = 60f, m_groupRadius = 5f }; val.m_spawn = new List(event2.m_spawn) { item }; list.Add(val); hashSet.Add(bossRef.Prefab); } } } foreach (RandomEvent item2 in list) { sys.m_events.Add(item2); } if (list.Count > 0) { Plugin.Log.LogInfo((object)string.Format("[Raid de boss] {0} eventos criados: {1}", list.Count, string.Join(", ", list.ConvertAll((RandomEvent c) => c.m_name)))); } } internal static void OnEventStarted(RandomEvent ev) { if (ev != null && ev.m_name.StartsWith("mmo_bossraid_") && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)ZoneSystem.instance == (Object)null) && !((Object)(object)EnvMan.instance == (Object)null)) { ZoneSystem.instance.SetGlobalKey(string.Format("{0} {1}", "mmo_bossraid_cd", EnvMan.instance.GetDay())); Plugin.Log.LogInfo((object)$"[Raid de boss] {ev.m_name} iniciado; proximo possivel em {ModConfig.BossRaidCooldownDays.Value} dia(s)."); } } internal static void Register() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.Register("ValheimMMO_BossRaidKilled", (Action)OnBossKilledServer); } } internal static void OnCharacterDeath(Character c) { if ((Object)(object)c == (Object)null || c.IsPlayer() || (Object)(object)RandEventSystem.instance == (Object)null) { return; } RandomEvent currentRandomEvent = RandEventSystem.instance.GetCurrentRandomEvent(); if (currentRandomEvent == null || !currentRandomEvent.m_name.StartsWith("mmo_bossraid_")) { return; } string text = ((Object)((Component)c).gameObject).name.Replace("(Clone)", ""); if (string.Equals(currentRandomEvent.m_name, "mmo_bossraid_" + text, StringComparison.OrdinalIgnoreCase)) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC("ValheimMMO_BossRaidKilled", new object[1] { currentRandomEvent.m_name }); } } } private static void OnBossKilledServer(long sender, string eventName) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)RandEventSystem.instance == (Object)null) { return; } RandomEvent currentRandomEvent = RandEventSystem.instance.GetCurrentRandomEvent(); if (currentRandomEvent != null && !(currentRandomEvent.m_name != eventName)) { RandEventSystem.instance.ResetRandomEvent(); Plugin.Log.LogInfo((object)("[Raid de boss] " + eventName + ": boss derrotado, evento encerrado.")); MessageHud instance = MessageHud.instance; if (instance != null) { instance.ShowMessage((MessageType)2, "A vinganca foi contida!", 0, (Sprite)null, false); } } } internal static void ServerTick(float dt) { if (!ModConfig.BossRaidEnabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null) { return; } _timer += dt; if (!(_timer < 60f)) { _timer = 0f; string s = default(string); if (ZoneSystem.instance.GetGlobalKey("mmo_bossraid_cd", ref s) && int.TryParse(s, out var result) && EnvMan.instance.GetDay() >= result + Mathf.Max(0, ModConfig.BossRaidCooldownDays.Value)) { ZoneSystem.instance.RemoveGlobalKey("mmo_bossraid_cd"); } } } } [HarmonyPatch(typeof(RandEventSystem), "Start")] internal static class RandEventSystem_Start_BossRaidPatch { private static void Postfix(RandEventSystem __instance) { BossRaids.Inject(__instance); } } [HarmonyPatch(typeof(Game), "Start")] internal static class Game_Start_BossRaidPatch { private static void Postfix() { BossRaids.Register(); } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class Character_OnDeath_BossRaidPatch { private static void Prefix(Character __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && __instance.m_nview.IsOwner()) { BossRaids.OnCharacterDeath(__instance); } } } [HarmonyPatch(typeof(RandEventSystem), "SetRandomEvent")] internal static class RandEventSystem_SetRandomEvent_BossRaidPatch { private static void Postfix(RandomEvent ev) { BossRaids.OnEventStarted(ev); } } [HarmonyPatch(typeof(ZNet), "Update")] internal static class ZNet_Update_BossRaidPatch { private static void Postfix() { BossRaids.ServerTick(Time.deltaTime); } } internal sealed class BossScaling : MonoBehaviour { private const string ZdoBaseHp = "mmo_boss_basehp"; private const string ZdoPlayers = "mmo_boss_players"; private Character _c; private float _timer; internal static void Attach(Character c) { if (!((Object)(object)c == (Object)null) && ModConfig.BossScalingEnabled.Value && (Object)(object)((Component)c).GetComponent() == (Object)null) { ((Component)c).gameObject.AddComponent(); } } private void Awake() { _c = ((Component)this).GetComponent(); } private void Update() { //IL_00c7: Unknown result type (might be due to invalid IL or missing references) _timer += Time.deltaTime; if (_timer < 5f) { return; } _timer = 0f; if ((Object)(object)_c == (Object)null || (Object)(object)_c.m_nview == (Object)null || !_c.m_nview.IsValid() || !_c.m_nview.IsOwner() || _c.IsDead()) { return; } ZDO zDO = _c.m_nview.GetZDO(); float num = zDO.GetFloat("mmo_boss_basehp", 0f); if (num <= 0f) { num = _c.GetMaxHealth(); zDO.Set("mmo_boss_basehp", num); } int num2 = CountPlayersNear(((Component)this).transform.position, ModConfig.BossScaleRadius.Value); if (num2 < 1) { num2 = 1; } float num3 = num * (1f + ModConfig.BossScalePerPlayer.Value * (float)(num2 - 1)); float maxHealth = _c.GetMaxHealth(); if (!(Mathf.Abs(num3 - maxHealth) < 1f)) { float num4 = ((maxHealth > 0f) ? Mathf.Clamp01(_c.GetHealth() / maxHealth) : 1f); _c.SetMaxHealth(num3); _c.SetHealth(num3 * num4); int num5 = zDO.GetInt("mmo_boss_players", 1); zDO.Set("mmo_boss_players", num2); if (num5 != num2) { Plugin.Log.LogInfo((object)$"[Boss] {_c.m_name}: {num2} jogador(es) no raio, vida maxima {maxHealth:0} -> {num3:0}."); } } } private static int CountPlayersNear(Vector3 pos, float radius) { //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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) int num = 0; float num2 = radius * radius; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && !((Character)allPlayer).IsDead()) { Vector3 val = ((Component)allPlayer).transform.position - pos; if (((Vector3)(ref val)).sqrMagnitude <= num2) { num++; } } } return num; } } [HarmonyPatch(typeof(Character), "Awake")] internal static class Character_Awake_BossScalingPatch { [HarmonyPriority(0)] private static void Postfix(Character __instance) { if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && __instance.m_boss && (ModConfig.BossScaleVanilla.Value || DungeonBosses.IsDungeonBoss(__instance, out var _) || WorldBoss.IsWorldBoss(__instance, out var _))) { BossScaling.Attach(__instance); } } } internal sealed class DungeonTier { public int Id; public string Title; public Theme Themes; public string Creature; public bool EquipsItems; public string[] WeaponChoices; public string Shield; public string BossEvent; public float Health; public string GoldReferenceItem; public bool IsCamp; } internal static class DungeonBosses { internal sealed class DeferredEquip : MonoBehaviour { public string Items; private void Start() { Humanoid component = ((Component)this).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)((Character)component).m_nview != (Object)null && ((Character)component).m_nview.IsValid()) { GiveItems(component, Items); } Object.Destroy((Object)(object)this); } } [HarmonyPatch(typeof(ItemDrop), "Awake")] private static class ItemDrop_Awake_CoinStackPatch { private static void Postfix(ItemDrop __instance) { if (_pendingStacks.Count != 0 && !((Object)(object)__instance == (Object)null) && !(((Object)((Component)__instance).gameObject).name.Replace("(Clone)", "") != "Coins")) { int stack = _pendingStacks.Dequeue(); __instance.m_itemData.m_stack = stack; if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid()) { __instance.Save(); } } } } public const string ZdoTier = "mmo_dboss"; public const string ZdoItems = "mmo_dboss_items"; public const string ZdoDungeon = "mmo_dboss_dungeon"; public const string DngTier = "mmo_dtier"; public const string DngPos = "mmo_dboss_pos"; public const string DngItems = "mmo_dboss_items"; public const string DngDeadDay = "mmo_dboss_dead_day"; internal static readonly DungeonTier[] Tiers = new DungeonTier[6] { new DungeonTier { Id = 1, Title = "Senhor da Cripta", Themes = (Theme)9, Creature = "Skeleton", EquipsItems = true, WeaponChoices = new string[2] { "SwordBronze", "MaceBronze" }, Shield = "ShieldBronzeBuckler", BossEvent = "boss_gdking", Health = 800f, GoldReferenceItem = "SwordBronze" }, new DungeonTier { Id = 2, Title = "Rei Afogado", Themes = (Theme)2, Creature = "Draugr_Elite", EquipsItems = true, WeaponChoices = new string[2] { "SwordIron", "AxeIron" }, Shield = "ShieldBanded", BossEvent = "boss_bonemass", Health = 1600f, GoldReferenceItem = "SwordIron" }, new DungeonTier { Id = 3, Title = "Anciao do Gelo", Themes = (Theme)4, Creature = "Fenring_Cultist", EquipsItems = false, WeaponChoices = Array.Empty(), BossEvent = "boss_moder", Health = 2500f, GoldReferenceItem = "ArmorWolfChest" }, new DungeonTier { Id = 4, Title = "Chefe Fuling", Themes = (Theme)16, Creature = "Goblin", EquipsItems = true, WeaponChoices = new string[2] { "SwordBlackmetal", "MaceNeedle" }, Shield = "ShieldBlackmetal", BossEvent = "boss_goblinking", Health = 3500f, GoldReferenceItem = "SwordBlackmetal", IsCamp = true }, new DungeonTier { Id = 5, Title = "Mestre de Forja", Themes = (Theme)128, Creature = "Dverger", EquipsItems = true, WeaponChoices = new string[2] { "CrossbowArbalest", "StaffFireball" }, BossEvent = "boss_seekerqueen", Health = 4500f, GoldReferenceItem = "StaffFireball" }, new DungeonTier { Id = 6, Title = "Campeao Carbonizado", Themes = (Theme)12288, Creature = "Charred_Melee", EquipsItems = true, WeaponChoices = new string[1] { "THSwordSlayer" }, BossEvent = "boss_fader", Health = 5500f, GoldReferenceItem = "THSwordSlayer", IsCamp = true } }; private static readonly Queue _pendingStacks = new Queue(); public static DungeonTier TierFor(Theme themes) { //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_0015: Unknown result type (might be due to invalid IL or missing references) DungeonTier[] tiers = Tiers; foreach (DungeonTier dungeonTier in tiers) { if ((dungeonTier.Themes & themes) != 0) { return dungeonTier; } } return null; } public static DungeonTier TierById(int id) { DungeonTier[] tiers = Tiers; foreach (DungeonTier dungeonTier in tiers) { if (dungeonTier.Id == id) { return dungeonTier; } } return null; } public static bool IsDungeonBoss(Character c, out DungeonTier tier) { tier = null; ZDO val = (((Object)(object)c?.m_nview != (Object)null && c.m_nview.IsValid()) ? c.m_nview.GetZDO() : null); if (val == null) { return false; } int num = val.GetInt("mmo_dboss", 0); if (num <= 0) { return false; } tier = TierById(num); return tier != null; } public static bool SpawnFor(DungeonGenerator gen, List rooms) { //IL_0023: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: 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_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: 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) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.DungeonBossesEnabled.Value || (Object)(object)gen == (Object)null || rooms == null || rooms.Count == 0) { return false; } DungeonTier dungeonTier = TierFor(gen.m_themes); if (dungeonTier == null) { return false; } if (dungeonTier.IsCamp && !ModConfig.DungeonIncludeCamps.Value) { return false; } Room val = null; foreach (Room room in rooms) { if ((Object)(object)room != (Object)null && room.m_entrance) { val = room; break; } } Vector3 val2 = (((Object)(object)val != (Object)null) ? ((Component)val).transform.position : ((Component)rooms[0]).transform.position); Room val3 = null; float num = -1f; foreach (Room room2 in rooms) { if (!((Object)(object)room2 == (Object)null) && !room2.m_entrance && !room2.m_endCap) { Vector3 val4 = ((Component)room2).transform.position - val2; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (sqrMagnitude > num) { num = sqrMagnitude; val3 = room2; } } } if ((Object)(object)val3 == (Object)null) { val3 = rooms[rooms.Count - 1]; } Vector3 val5 = ((Component)val3).transform.position + Vector3.up * 1.5f; RaycastHit val6 = default(RaycastHit); if (Physics.Raycast(val5 + Vector3.up * 2f, Vector3.down, ref val6, 12f, LayerMask.GetMask(new string[4] { "piece", "terrain", "static_solid", "Default" }))) { val5 = ((RaycastHit)(ref val6)).point + Vector3.up * 0.2f; } string error; return Spawn(dungeonTier, val5, out error, null, gen.m_nview); } public static bool Spawn(DungeonTier tier, Vector3 pos, out string error, string presetItems = null, ZNetView dungeonView = null) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) error = null; ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab(tier.Creature) : null); if ((Object)(object)val == (Object)null) { error = "criatura '" + tier.Creature + "' nao encontrada"; return false; } GameObject val2 = Object.Instantiate(val, pos, Quaternion.Euler(0f, Random.Range(0f, 360f), 0f)); Character component = val2.GetComponent(); ZNetView component2 = val2.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid()) { error = "prefab sem Character/ZNetView"; Object.Destroy((Object)(object)val2); return false; } string text = presetItems; if (text == null) { text = ""; if (tier.EquipsItems && tier.WeaponChoices.Length != 0) { string text2 = tier.WeaponChoices[Random.Range(0, tier.WeaponChoices.Length)]; text = (string.IsNullOrEmpty(tier.Shield) ? text2 : (text2 + "|" + tier.Shield)); } } ZDO zDO = component2.GetZDO(); zDO.Set("mmo_dboss", tier.Id); zDO.Set("mmo_dboss_items", text); zDO.Persistent = true; if ((Object)(object)dungeonView != (Object)null && dungeonView.IsValid()) { ZDO zDO2 = dungeonView.GetZDO(); zDO.Set("mmo_dboss_dungeon", zDO2.m_uid); zDO2.Set("mmo_dtier", tier.Id); zDO2.Set("mmo_dboss_pos", pos); zDO2.Set("mmo_dboss_items", text); zDO2.Set("mmo_dboss_dead_day", 0); if ((Object)(object)((Component)dungeonView).GetComponent() == (Object)null) { ((Component)dungeonView).gameObject.AddComponent(); } } int num = Mathf.Clamp(ModConfig.DungeonBossStars.Value, 0, 2); component.SetLevel(num + 1); float num2 = tier.Health * Mathf.Max(0.1f, ModConfig.DungeonBossHpMultiplier.Value); component.SetMaxHealth(num2); component.SetHealth(num2); ApplyPresentation(component, tier); Humanoid val3 = (Humanoid)(object)((component is Humanoid) ? component : null); if (val3 != null) { GiveItems(val3, text); } Plugin.Log.LogInfo((object)$"[Dungeon] Boss '{tier.Title}' ({tier.Creature}) em {pos}, vida {num2:0}, itens '{text}'."); return true; } internal static void ApplyPresentation(Character c, DungeonTier tier) { c.m_boss = true; c.m_name = tier.Title; c.m_bossEvent = tier.BossEvent; BossScaling.Attach(c); } internal static void GiveItems(Humanoid h, string items) { if ((Object)(object)h == (Object)null || string.IsNullOrEmpty(items)) { return; } Inventory inventory = h.GetInventory(); if (inventory == null) { return; } int num = Mathf.Max(1, ModConfig.DungeonBossItemQuality.Value); string[] array = items.Split('|'); foreach (string text in array) { if (string.IsNullOrEmpty(text) || inventory.ContainsItemByName(text)) { continue; } ItemData val = inventory.AddItem(text, 1, num, 0, 0L, "", false); if (val == null) { Plugin.Log.LogWarning((object)("[Dungeon] Nao foi possivel dar '" + text + "' ao boss.")); continue; } try { h.EquipItem(val, false); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Dungeon] Equipar '" + text + "' em " + ((Character)h).m_name + ": " + ex.Message)); } } } internal static void GiveItemsDeferred(Humanoid h, string items) { if (!((Object)(object)h == (Object)null) && !string.IsNullOrEmpty(items)) { (((Component)h).gameObject.GetComponent() ?? ((Component)h).gameObject.AddComponent()).Items = items; } } public static int GoldFor(DungeonTier tier) { int num = MerchantPricing.PriceOf(tier.GoldReferenceItem); return Mathf.Max(10, Mathf.RoundToInt((float)num * ModConfig.DungeonBossGoldRatio.Value)); } internal static void DropGold(Character c, DungeonTier tier) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) ZNetScene instance = ZNetScene.instance; GameObject val = ((instance != null) ? instance.GetPrefab("Coins") : null); if (!((Object)(object)val == (Object)null)) { int num = GoldFor(tier); int num2 = Mathf.Max(1, val.GetComponent()?.m_itemData.m_shared.m_maxStackSize ?? 999); List> list = new List>(); while (num > 0) { int num3 = Mathf.Min(num2, num); list.Add(new KeyValuePair(val, 1)); num -= num3; _pendingStacks.Enqueue(num3); } CharacterDrop.DropItems(list, c.GetCenterPoint(), 0.7f); } } } [HarmonyPatch(typeof(DungeonGenerator), "Generate", new Type[] { typeof(int), typeof(SpawnMode) })] internal static class DungeonGenerator_Generate_Patch { internal static SpawnMode CurrentMode; internal static bool Generating; private static void Prefix(SpawnMode mode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) CurrentMode = mode; Generating = true; } private static void Postfix() { Generating = false; } } [HarmonyPatch(typeof(DungeonGenerator), "Save")] internal static class DungeonGenerator_Save_Patch { private static void Postfix(DungeonGenerator __instance) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (!DungeonGenerator_Generate_Patch.Generating || (int)DungeonGenerator_Generate_Patch.CurrentMode != 0) { return; } try { DungeonBosses.SpawnFor(__instance, new List(DungeonGenerator.m_placedRooms)); } catch (Exception arg) { Plugin.Log.LogError((object)$"[Dungeon] Falha ao spawnar boss: {arg}"); } } } [HarmonyPatch(typeof(Character), "Awake")] internal static class Character_Awake_DungeonBossPatch { private static void Postfix(Character __instance) { if (DungeonBosses.IsDungeonBoss(__instance, out var tier)) { DungeonBosses.ApplyPresentation(__instance, tier); } } } [HarmonyPatch(typeof(Humanoid), "Awake")] internal static class Humanoid_Awake_DungeonBossPatch { private static void Postfix(Humanoid __instance) { if (DungeonBosses.IsDungeonBoss((Character)(object)__instance, out var _)) { string items = ((Character)__instance).m_nview.GetZDO().GetString("mmo_dboss_items", ""); DungeonBosses.GiveItemsDeferred(__instance, items); } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class Character_OnDeath_DungeonBossPatch { private static void Prefix(Character __instance) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid() || !__instance.m_nview.IsOwner() || !DungeonBosses.IsDungeonBoss(__instance, out var tier)) { return; } if (!tier.EquipsItems) { DungeonBosses.DropGold(__instance, tier); } ZDOID zDOID = __instance.m_nview.GetZDO().GetZDOID("mmo_dboss_dungeon"); if (((ZDOID)(ref zDOID)).IsNone()) { return; } DungeonGenerator[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { ZNetView nview = array[i].m_nview; if (!((Object)(object)nview == (Object)null) && nview.IsValid() && !(nview.GetZDO().m_uid != zDOID)) { nview.GetZDO().Set("mmo_dboss_dead_day", Mathf.Max(1, (!((Object)(object)EnvMan.instance != (Object)null)) ? 1 : EnvMan.instance.GetDay())); Plugin.Log.LogInfo((object)$"[Dungeon] Boss '{tier.Title}' morto; reset em {ModConfig.DungeonResetDays.Value} dia(s)."); break; } } } } internal sealed class DungeonResetter : MonoBehaviour { private DungeonGenerator _gen; private float _timer; private void Awake() { _gen = ((Component)this).GetComponent(); } private void Update() { //IL_00d2: 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_00f2: Unknown result type (might be due to invalid IL or missing references) _timer += Time.deltaTime; if (_timer < 15f) { return; } _timer = 0f; int value = ModConfig.DungeonResetDays.Value; if (value <= 0 || (Object)(object)_gen == (Object)null) { return; } ZNetView nview = _gen.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return; } ZDO zDO = nview.GetZDO(); int num = zDO.GetInt("mmo_dtier", 0); int num2 = zDO.GetInt("mmo_dboss_dead_day", 0); if (num <= 0 || num2 <= 0 || (Object)(object)EnvMan.instance == (Object)null || EnvMan.instance.GetDay() < num2 + value) { return; } DungeonTier dungeonTier = DungeonBosses.TierById(num); if (dungeonTier != null) { Vector3 vec = zDO.GetVec3("mmo_dboss_pos", ((Component)this).transform.position); string presetItems = zDO.GetString("mmo_dboss_items", ""); if (DungeonBosses.Spawn(dungeonTier, vec, out var error, presetItems, nview)) { zDO.Set("mmo_dboss_dead_day", 0); Plugin.Log.LogInfo((object)("[Dungeon] Boss '" + dungeonTier.Title + "' renasceu.")); } else { Plugin.Log.LogWarning((object)("[Dungeon] Reset falhou: " + error)); zDO.Set("mmo_dboss_dead_day", 0); } } } } [HarmonyPatch(typeof(DungeonGenerator), "Load")] internal static class DungeonGenerator_Load_ResetPatch { private static void Postfix(DungeonGenerator __instance) { ZNetView val = __instance?.m_nview; if (!((Object)(object)val == (Object)null) && val.IsValid() && val.GetZDO().GetInt("mmo_dtier", 0) > 0 && (Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } [HarmonyPatch(typeof(DungeonGenerator), "Spawn")] internal static class DungeonGenerator_Spawn_LateBossPatch { private static void Postfix(DungeonGenerator __instance) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (!ModConfig.DungeonBossesEnabled.Value) { return; } ZNetView val = __instance?.m_nview; if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner() || val.GetZDO().GetInt("mmo_dtier", 0) > 0) { return; } List list = new List(((Component)__instance).GetComponentsInChildren()); if (list.Count != 0 && DungeonBosses.SpawnFor(__instance, list)) { Plugin.Log.LogInfo((object)$"[Dungeon] Boss adicionado a dungeon antiga ({list.Count} salas) em {((Component)__instance).transform.position}."); if ((Object)(object)((Component)__instance).GetComponent() == (Object)null) { ((Component)__instance).gameObject.AddComponent(); } } } } internal sealed class WorldBossDef { public int Tier; public string Title; public string Creature; public string BossEvent; public float Health; public Biome Biomes; } internal static class WorldBoss { public const string ZdoTag = "mmo_wboss"; private const string KeyNext = "mmo_wboss_next"; private const string KeyAlive = "mmo_wboss_alive"; private const string RpcSpawn = "ValheimMMO_WorldBossSpawn"; private const string RpcAnnounce = "ValheimMMO_WorldBossAnnounce"; private const string RpcDead = "ValheimMMO_WorldBossDead"; internal static readonly WorldBossDef[] Defs = new WorldBossDef[6] { new WorldBossDef { Tier = 1, Title = "Troll Anciao", Creature = "Troll", BossEvent = "boss_gdking", Health = 3000f, Biomes = (Biome)9 }, new WorldBossDef { Tier = 2, Title = "Abominacao Primordial", Creature = "Abomination", BossEvent = "boss_bonemass", Health = 6000f, Biomes = (Biome)2 }, new WorldBossDef { Tier = 3, Title = "Colosso de Pedra", Creature = "StoneGolem", BossEvent = "boss_moder", Health = 8000f, Biomes = (Biome)4 }, new WorldBossDef { Tier = 4, Title = "Berserker Fuling", Creature = "GoblinBrute", BossEvent = "boss_goblinking", Health = 10000f, Biomes = (Biome)16 }, new WorldBossDef { Tier = 5, Title = "Devorador da Nevoa", Creature = "SeekerBrute", BossEvent = "boss_seekerqueen", Health = 12000f, Biomes = (Biome)512 }, new WorldBossDef { Tier = 6, Title = "Morgen Ancia", Creature = "Morgen", BossEvent = "boss_fader", Health = 15000f, Biomes = (Biome)32 } }; private static PinData _pin; private static float _timer; public static WorldBossDef ForBiome(Biome biome) { //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_0015: Unknown result type (might be due to invalid IL or missing references) WorldBossDef[] defs = Defs; foreach (WorldBossDef worldBossDef in defs) { if ((worldBossDef.Biomes & biome) != 0) { return worldBossDef; } } return Defs[0]; } public static WorldBossDef ByTier(int tier) { WorldBossDef[] defs = Defs; foreach (WorldBossDef worldBossDef in defs) { if (worldBossDef.Tier == tier) { return worldBossDef; } } return null; } public static bool IsWorldBoss(Character c, out WorldBossDef def) { def = null; ZDO val = (((Object)(object)c?.m_nview != (Object)null && c.m_nview.IsValid()) ? c.m_nview.GetZDO() : null); if (val == null) { return false; } int num = val.GetInt("mmo_wboss", 0); if (num <= 0) { return false; } def = ByTier(num); return def != null; } public static int GoldFor(WorldBossDef def) { return Mathf.Max(1, Mathf.RoundToInt((float)(ModConfig.WorldBossGoldBase.Value * def.Tier))); } internal static void Register() { if (ZRoutedRpc.instance != null) { ZRoutedRpc.instance.Register("ValheimMMO_WorldBossSpawn", (Action)OnSpawnRequest); ZRoutedRpc.instance.Register("ValheimMMO_WorldBossAnnounce", (Action)OnAnnounce); ZRoutedRpc.instance.Register("ValheimMMO_WorldBossDead", (Action)OnDead); } } private static int Today() { if (!((Object)(object)EnvMan.instance != (Object)null)) { return 0; } return EnvMan.instance.GetDay(); } private static int ReadDayKey(string key) { string s = default(string); if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(key, ref s) && int.TryParse(s, out var result)) { return result; } return -1; } private static void WriteDayKey(string key, int day) { ZoneSystem instance = ZoneSystem.instance; if (instance != null) { instance.SetGlobalKey($"{key} {day}"); } } internal static void ServerTick(float dt) { if (!ModConfig.WorldBossEnabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)EnvMan.instance == (Object)null) { return; } _timer += dt; if (_timer < 30f) { return; } _timer = 0f; int num = Mathf.Max(1, ModConfig.WorldBossIntervalDays.Value); int num2 = Today(); int num3 = ReadDayKey("mmo_wboss_next"); if (num3 < 0) { WriteDayKey("mmo_wboss_next", num2 + num); return; } int num4 = ReadDayKey("mmo_wboss_alive"); if ((num4 >= 0 && num2 < num4 + num * 2) || num2 < num3) { return; } List peers = ZNet.instance.GetPeers(); List list = new List(); foreach (ZNetPeer item in peers) { if (item != null && item.IsReady()) { list.Add(item.m_uid); } } if (!ZNet.instance.IsDedicated() && (Object)(object)Player.m_localPlayer != (Object)null) { list.Add(ZRoutedRpc.instance.GetServerPeerID()); } if (list.Count != 0) { long num5 = list[Random.Range(0, list.Count)]; WriteDayKey("mmo_wboss_next", num2 + num); WriteDayKey("mmo_wboss_alive", num2); if (num5 == ZRoutedRpc.instance.GetServerPeerID()) { OnSpawnRequest(0L); } else { ZRoutedRpc.instance.InvokeRoutedRPC(num5, "ValheimMMO_WorldBossSpawn", Array.Empty()); } } } internal static void SpawnNow() { OnSpawnRequest(0L); } private static void OnSpawnRequest(long sender) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } float num = Random.value * MathF.PI * 2f; float num2 = Random.Range(150f, 200f); Vector3 val = ((Component)localPlayer).transform.position + new Vector3(Mathf.Cos(num) * num2, 0f, Mathf.Sin(num) * num2); float num3 = default(float); if (ZoneSystem.instance.GetGroundHeight(val, ref num3)) { val.y = num3 + 0.5f; } WorldBossDef worldBossDef = ForBiome((Biome)((WorldGenerator.instance == null) ? 1 : ((int)WorldGenerator.instance.GetBiome(val)))); GameObject prefab = ZNetScene.instance.GetPrefab(worldBossDef.Creature); if ((Object)(object)prefab == (Object)null) { Plugin.Log.LogWarning((object)("[Chefe mundial] criatura '" + worldBossDef.Creature + "' nao encontrada.")); return; } GameObject val2 = Object.Instantiate(prefab, val, Quaternion.identity); Character component = val2.GetComponent(); ZNetView component2 = val2.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid()) { Object.Destroy((Object)(object)val2); return; } component2.GetZDO().Set("mmo_wboss", worldBossDef.Tier); component2.GetZDO().Persistent = true; component.SetLevel(3); float num4 = worldBossDef.Health * Mathf.Max(0.1f, ModConfig.WorldBossHpMultiplier.Value); component.SetMaxHealth(num4); component.SetHealth(num4); ApplyPresentation(component, worldBossDef); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ValheimMMO_WorldBossAnnounce", new object[2] { worldBossDef.Title, val }); Plugin.Log.LogInfo((object)$"[Chefe mundial] {worldBossDef.Title} ({worldBossDef.Creature}) em {val}, vida {num4:0}."); } internal static void ApplyPresentation(Character c, WorldBossDef def) { c.m_boss = true; c.m_name = def.Title; c.m_bossEvent = def.BossEvent; BossScaling.Attach(c); } private static void OnAnnounce(long sender, string title, Vector3 pos) { //IL_004c: 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_005c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { ((Character)localPlayer).Message((MessageType)2, "Um chefe mundial despertou: " + title + "!", 0, (Sprite)null); } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { return; } try { if (_pin != null) { instance.RemovePin(_pin); } _pin = instance.AddPin(pos, (PinType)9, title, false, false, 0L, default(PlatformUserID)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[Chefe mundial] pin: " + ex.Message)); } } private static void OnDead(long sender, string title) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, title + " foi derrotado!", 0, (Sprite)null); } Minimap instance = Minimap.instance; if ((Object)(object)instance != (Object)null && _pin != null) { try { instance.RemovePin(_pin); } catch { } _pin = null; } } internal static void OnKilled(Character c, WorldBossDef def) { ZoneSystem instance = ZoneSystem.instance; if (instance != null) { instance.RemoveGlobalKey("mmo_wboss_alive"); } ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ValheimMMO_WorldBossDead", new object[1] { def.Title }); } } } [HarmonyPatch(typeof(Game), "Start")] internal static class Game_Start_WorldBossPatch { private static void Postfix() { WorldBoss.Register(); } } [HarmonyPatch(typeof(ZNet), "Update")] internal static class ZNet_Update_WorldBossPatch { private static void Postfix() { WorldBoss.ServerTick(Time.deltaTime); } } [HarmonyPatch(typeof(Character), "Awake")] internal static class Character_Awake_WorldBossPatch { private static void Postfix(Character __instance) { if (WorldBoss.IsWorldBoss(__instance, out var def)) { WorldBoss.ApplyPresentation(__instance, def); } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class Character_OnDeath_WorldBossPatch { private static void Prefix(Character __instance) { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && __instance.m_nview.IsOwner() && WorldBoss.IsWorldBoss(__instance, out var def)) { WorldBoss.OnKilled(__instance, def); } } } } namespace ValheimMMO.Drops { public static class EquipmentDrops { private static string[] _blacklistCache = Array.Empty(); private static string _blacklistRaw; private static Dictionary _remap; private static string _remapRaw; private static readonly HashSet _loggedSkips = new HashSet(StringComparer.OrdinalIgnoreCase); private static Dictionary Remap { get { string text = ModConfig.CreatureItemRemap.Value ?? ""; if (_remap == null || text != _remapRaw) { _remapRaw = text; _remap = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] array = text.Split(new char[3] { ',', ';', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string text4 = text2.Substring(num + 1).Trim(); if (text3.Length > 0 && text4.Length > 0) { _remap[text3] = text4; } } } } return _remap; } } public static float ChanceFor(ItemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected I4, but got Unknown switch (type - 3) { case 0: case 1: case 11: case 19: return ModConfig.WeaponDropChance.Value; case 2: return ModConfig.ShieldDropChance.Value; case 3: case 4: case 8: case 9: case 14: return ModConfig.ArmorDropChance.Value; case 12: case 16: return ModConfig.ToolDropChance.Value; case 6: case 20: return ModConfig.AmmoDropChance.Value; default: return 0f; } } private static bool IsBlacklisted(string prefabName) { string text = ModConfig.EquipDropPrefabBlacklist.Value ?? ""; if (text != _blacklistRaw) { _blacklistRaw = text; _blacklistCache = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < _blacklistCache.Length; i++) { _blacklistCache[i] = _blacklistCache[i].Trim(); } } string[] blacklistCache = _blacklistCache; foreach (string text2 in blacklistCache) { if (text2.Length > 0 && string.Equals(text2, prefabName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } public static bool IsDroppable(string prefabName) { return (Object)(object)DroppablePrefab(prefabName) != (Object)null; } public static GameObject DroppablePrefab(string prefabName) { if (string.IsNullOrEmpty(prefabName) || (Object)(object)ZNetScene.instance == (Object)null) { return null; } GameObject prefab = ZNetScene.instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { return null; } if ((Object)(object)prefab.GetComponent() == (Object)null) { return null; } if ((Object)(object)prefab.GetComponent() == (Object)null) { return null; } return prefab; } public static string RemapFor(string prefabName) { if (prefabName == null || !Remap.TryGetValue(prefabName, out var value)) { return null; } return value; } public static List> RemapChoices(string prefabName) { List> list = new List>(); string text = RemapFor(prefabName); if (text == null) { return list; } string[] array = text.Split('|'); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length == 0) { continue; } float result = 1f; int num = text2.IndexOf('*'); if (num > 0) { if (!float.TryParse(text2.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { result = 1f; } text2 = text2.Substring(0, num).Trim(); } if (text2.Length > 0) { list.Add(new KeyValuePair(text2, Mathf.Max(0f, result))); } } return list; } public static string PickRemap(string prefabName, int stars) { List> list = RemapChoices(prefabName); if (list.Count == 0) { return null; } if (list.Count == 1) { return list[0].Key; } float num = 0f; foreach (KeyValuePair item in list) { num += item.Value; } if (num <= 0f) { return list[0].Key; } float num2 = list[0].Value / num + (float)Mathf.Max(0, stars) * ModConfig.RemapStarBias.Value; num2 = Mathf.Clamp01(num2); float value = Random.value; if (value < num2) { return list[0].Key; } float num3 = num - list[0].Value; float num4 = (value - num2) / Mathf.Max(0.0001f, 1f - num2) * num3; for (int i = 1; i < list.Count; i++) { num4 -= list[i].Value; if (num4 <= 0f) { return list[i].Key; } } return list[list.Count - 1].Key; } public static GameObject ResolveDroppable(GameObject dropPrefab, int stars = 0) { if ((Object)(object)dropPrefab == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return null; } if (RemapFor(((Object)dropPrefab).name) != null) { string text = PickRemap(((Object)dropPrefab).name, stars); GameObject val = DroppablePrefab(text); if ((Object)(object)val != (Object)null) { return val; } LogSkipOnce(((Object)dropPrefab).name + ":" + text, "remap para '" + text + "' nao e um item de jogador valido (sem ZNetView/ItemDrop ou inexistente)"); return null; } GameObject val2 = DroppablePrefab(((Object)dropPrefab).name); if ((Object)(object)val2 != (Object)null) { if (!ModConfig.DropOnlyPlayerItems.Value || IsPlayerItem(val2)) { return val2; } LogSkipOnce(((Object)dropPrefab).name, "item de criatura sem receita -- para um de-para, adicione em CreatureItemRemap"); return null; } LogSkipOnce(((Object)dropPrefab).name, "item interno da criatura sem entrada em CreatureItemRemap"); return null; } public static bool IsPlayerItem(GameObject prefab) { if ((Object)(object)prefab == (Object)null) { return false; } string name = ((Object)prefab).name; if (CustomItems.IsCustom(name) || MapItem.IsMap(name)) { return true; } ItemDrop component = prefab.GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } if ((Object)(object)ObjectDB.instance.GetRecipe(component.m_itemData) != (Object)null) { return true; } Trader[] array = Resources.FindObjectsOfTypeAll(); foreach (Trader val in array) { if (val.m_items == null) { continue; } foreach (TradeItem item in val.m_items) { if ((Object)(object)item?.m_prefab != (Object)null && ((Object)((Component)item.m_prefab).gameObject).name == name) { return true; } } } return false; } private static void LogSkipOnce(string name, string why) { if (ModConfig.LogSkippedInternalItems.Value && _loggedSkips.Add(name)) { Plugin.Log.LogInfo((object)("[Drops] Ignorando '" + name + "': " + why + ".")); } } public static List> BuildFor(Character character, List> existing) { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Invalid comparison between Unknown and I4 //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Invalid comparison between Unknown and I4 List> list = new List>(); if (!ModConfig.EquipmentDropsEnabled.Value) { return list; } if ((Object)(object)character == (Object)null || character.IsPlayer()) { return list; } DungeonTier tier; bool flag = DungeonBosses.IsDungeonBoss(character, out tier); if (!flag && ModConfig.ExcludeBossesFromEquipDrops.Value && character.IsBoss()) { return list; } Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null); if (val == null) { return list; } Inventory inventory = val.GetInventory(); if (inventory == null) { return list; } int num = Math.Max(0, character.GetLevel() - 1); int num2 = ((!ModConfig.DropsScaleWithStars.Value) ? 1 : (1 + num)); foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem?.m_shared == null || (Object)(object)allItem.m_dropPrefab == (Object)null) { continue; } float num3 = ChanceFor(allItem.m_shared.m_itemType); if (num3 <= 0f || IsBlacklisted(((Object)allItem.m_dropPrefab).name)) { continue; } GameObject val2 = ResolveDroppable(allItem.m_dropPrefab, num); if ((Object)(object)val2 == (Object)null || IsBlacklisted(((Object)val2).name) || (!flag && Random.value > num3) || AlreadyListed(existing, val2) || AlreadyListed(list, val2)) { continue; } int num4; int num5; if ((int)allItem.m_shared.m_itemType != 9) { num4 = (((int)allItem.m_shared.m_itemType == 23) ? 1 : 0); if (num4 == 0) { num5 = num2; goto IL_0189; } } else { num4 = 1; } num5 = Math.Max(1, allItem.m_stack); goto IL_0189; IL_0189: int num6 = num5; list.Add(new KeyValuePair(val2, num6)); if (num4 == 0) { for (int i = 0; i < num6; i++) { float durabilityFraction = (flag ? 1f : Random.Range(ModConfig.DropDurabilityMin.Value, ModConfig.DropDurabilityMax.Value)); DropPending.Enqueue(((Object)val2).name, durabilityFraction, (!flag) ? 1 : allItem.m_quality); } } } return list; } private static bool AlreadyListed(List> list, GameObject prefab) { if (list == null) { return false; } foreach (KeyValuePair item in list) { if ((Object)(object)item.Key == (Object)(object)prefab) { return true; } } return false; } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] internal static class CharacterDrop_GenerateDropList_Patch { private static void Postfix(CharacterDrop __instance, ref List> __result) { if (__result != null) { List> list = EquipmentDrops.BuildFor(__instance.m_character, __result); if (list.Count > 0) { __result.AddRange(list); } } } } [HarmonyPatch(typeof(Character), "OnDeath")] internal static class Character_OnDeath_DropFallbackPatch { private static void Prefix(Character __instance) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer() && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsOwner() && !((Object)(object)((Component)__instance).GetComponent() != (Object)null)) { List> list = EquipmentDrops.BuildFor(__instance, null); if (list.Count != 0) { CharacterDrop.DropItems(list, __instance.GetCenterPoint(), 0.5f); } } } } } namespace ValheimMMO.Config { public static class ModConfig { public static ConfigEntry MaxLevel; public static ConfigEntry PointsPerLevel; public static ConfigEntry XpCurveFactor; public static ConfigEntry XpCurveExponent; public static ConfigEntry XpKillFactor; public static ConfigEntry XpKillExponent; public static ConfigEntry XpStarBonus; public static ConfigEntry XpBossMultiplier; public static ConfigEntry XpRequiresOwnDamage; public static ConfigEntry XpNearbyRadius; public static ConfigEntry XpGlobalRate; public static ConfigEntry GrowthHealthPerLevel; public static ConfigEntry GrowthStaminaPerLevel; public static ConfigEntry GrowthEitrPerLevel; public static ConfigEntry HealthPerVitality; public static ConfigEntry StaminaPerEndurance; public static ConfigEntry EitrPerSpirit; public static ConfigEntry CarryPerConditioning; public static ConfigEntry GrowthCarryPerLevel; public static ConfigEntry ItemBonusesEnabled; public static ConfigEntry ShowItemBonusTooltip; public static ConfigEntry ArmorToAttribute; public static ConfigEntry BlockToAttribute; public static ConfigEntry MeleeDamageToAttribute; public static ConfigEntry RangedDamageToAttribute; public static ConfigEntry MagicStaffBase; public static ConfigEntry MagicStaffPerQuality; public static ConfigEntry HeavyArmorMovementThreshold; public static ConfigEntry FoodGivesNoStats; public static ConfigEntry DeathXpLossPercent; public static ConfigEntry DeathCanDelevel; public static ConfigEntry RespecEnabled; public static ConfigEntry RespecMinComfort; public static ConfigEntry RespecRequiresShelter; public static ConfigEntry HungerEnabled; public static ConfigEntry HungerMax; public static ConfigEntry HungerDrainMinutes; public static ConfigEntry SatietyPerBurnSecond; public static ConfigEntry HungryThreshold; public static ConfigEntry StarvingThreshold; public static ConfigEntry HungryHealthRegenMult; public static ConfigEntry HungryStaminaRegenMult; public static ConfigEntry StarvingHealthRegenMult; public static ConfigEntry StarvingStaminaRegenMult; public static ConfigEntry StarvingEitrRegenMult; public static ConfigEntry WellFedThreshold; public static ConfigEntry WellFedRegenMult; public static ConfigEntry StarvationDamage; public static ConfigEntry StarvationDamageInterval; public static ConfigEntry StarvationCanKill; public static ConfigEntry EquipmentDropsEnabled; public static ConfigEntry WeaponDropChance; public static ConfigEntry ShieldDropChance; public static ConfigEntry ArmorDropChance; public static ConfigEntry ToolDropChance; public static ConfigEntry AmmoDropChance; public static ConfigEntry DropsScaleWithStars; public static ConfigEntry ExcludeBossesFromEquipDrops; public static ConfigEntry EquipDropPrefabBlacklist; public static ConfigEntry CreatureItemRemap; public static ConfigEntry LogSkippedInternalItems; public static ConfigEntry DropOnlyPlayerItems; public static ConfigEntry RemapStarBias; public static ConfigEntry OpenPanelKey; public static ConfigEntry ShowHungerBar; public static ConfigEntry ShowXpBar; public static ConfigEntry ShowXpGainMessages; public static ConfigEntry HungerBarOffsetX; public static ConfigEntry HungerBarOffsetY; public static ConfigEntry HungerBarWidth; public static ConfigEntry XpBarOffsetX; public static ConfigEntry XpBarOffsetY; public static ConfigEntry XpBarWidth; public static ConfigEntry PanelOpensWithInventory; public static ConfigEntry SlotsEnabled; public static ConfigEntry SlotsKgPerRow; public static ConfigEntry SlotsMaxRows; public static ConfigEntry MerchantEnabled; public static ConfigEntry MerchantPrefab; public static ConfigEntry MerchantName; public static ConfigEntry MerchantOffsetX; public static ConfigEntry MerchantOffsetZ; public static ConfigEntry MerchantPriceMultiplier; public static ConfigEntry MerchantCraftMarkup; public static ConfigEntry MerchantCraftBaseFee; public static ConfigEntry MerchantStationLevelFee; public static ConfigEntry MerchantConversionMarkup; public static ConfigEntry MerchantCreatureDropExponent; public static ConfigEntry MerchantTrophyMultiplier; public static ConfigEntry MerchantGatheredBase; public static ConfigEntry MerchantMinedBase; public static ConfigEntry MerchantUnknownBase; public static ConfigEntry MerchantMaterialStack; public static ConfigEntry MerchantConsumableStack; public static ConfigEntry MerchantExcludeQuestItems; public static ConfigEntry MerchantExcludePattern; public static ConfigEntry MerchantBasePrices; public static ConfigEntry MerchantPriceOverrides; public static ConfigEntry MerchantBuysItems; public static ConfigEntry MerchantBuyRatio; public static ConfigEntry MerchantBuysMaterials; public static ConfigEntry MerchantNoBuyPattern; public static ConfigEntry MerchantInflationEveryDays; public static ConfigEntry MerchantInflationPercent; public static ConfigEntry MerchantInflationMaxMultiplier; public static ConfigEntry DungeonBossesEnabled; public static ConfigEntry DungeonIncludeCamps; public static ConfigEntry DungeonBossHpMultiplier; public static ConfigEntry DungeonBossStars; public static ConfigEntry DungeonBossItemQuality; public static ConfigEntry DungeonBossGoldRatio; public static ConfigEntry XpDungeonBossMultiplier; public static ConfigEntry DungeonBossMusicRange; public static ConfigEntry PotionCooldown; public static ConfigEntry ReturnStoneCooldownMultiplier; public static ConfigEntry ReturnStoneIgnoresOre; public static ConfigEntry DungeonResetDays; public static ConfigEntry ShowPlayerLevels; public static ConfigEntry EquipmentRowEnabled; public static ConfigEntry VersionCheckEnabled; public static ConfigEntry VersionCheckExact; public static ConfigEntry ConfigSyncEnabled; public static ConfigEntry CityBuildings; public static ConfigEntry CityRoofYawFix; public static ConfigEntry CityMinDistance; public static ConfigEntry CityMaxDistance; public static ConfigEntry CityKeepAwayFromSpawn; public static ConfigEntry PvpEnabled; public static ConfigEntry StealthHidesName; public static ConfigEntry KarmaPerKill; public static ConfigEntry KarmaDeathLoss; public static ConfigEntry KarmaDecayPerMinute; public static ConfigEntry KarmaMonsterFactor; public static ConfigEntry WantedThreshold; public static ConfigEntry OutlawThreshold; public static ConfigEntry BountyPerKarma; public static ConfigEntry KarmaMax; public static ConfigEntry GuardsEnabled; public static ConfigEntry GuardsPerCity; public static ConfigEntry GuardStars; public static ConfigEntry GuardHpMultiplier; public static ConfigEntry GuardScaleWithPlayers; public static ConfigEntry CitiesEnabled; public static ConfigEntry CityAnchors; public static ConfigEntry SpawnMerchantTier; public static ConfigEntry CitySellsLowerTiers; public static ConfigEntry ImportMarkupPerTier; public static ConfigEntry LocalBuyRatio; public static ConfigEntry CityPlaza; public static ConfigEntry CitySafeZone; public static ConfigEntry CitySafeRadius; public static ConfigEntry CityPortals; public static ConfigEntry CityLevelTerrain; public static ConfigEntry CityLevelRadius; public static ConfigEntry MaterialTiers; public static ConfigEntry WorldBossEnabled; public static ConfigEntry WorldBossIntervalDays; public static ConfigEntry WorldBossHpMultiplier; public static ConfigEntry WorldBossGoldBase; public static ConfigEntry BankEnabled; public static ConfigEntry BankRows; public static ConfigEntry BossRaidEnabled; public static ConfigEntry BossRaidCooldownDays; public static ConfigEntry BossRaidMinDuration; public static ConfigEntry BossScalingEnabled; public static ConfigEntry BossScalePerPlayer; public static ConfigEntry BossScaleRadius; public static ConfigEntry BossScaleVanilla; public static ConfigEntry DropDurabilityMin; public static ConfigEntry DropDurabilityMax; public static ConfigEntry HungerStatusIcons; public static ConfigEntry RepairCostRatio; public static ConfigEntry UpgradeCostFactor; public static ConfigEntry ExtraQualityLevels; public static ConfigEntry QuestsEnabled; public static ConfigEntry QuestRewardMultiplier; public static ConfigEntry QuestMaxActive; public static ConfigEntry QuestTrackerEnabled; public static ConfigEntry QuestTrackerOffsetX; public static ConfigEntry QuestTrackerOffsetY; public static ConfigEntry MapPrice; public static ConfigEntry MapRevealRadius; public static ConfigEntry MapWorldRadius; public static ConfigEntry MapBossDropChance; public static ConfigEntry BowTweaksEnabled; public static ConfigEntry BowDrawTimeMultiplier; public static ConfigEntry BowDrawStaminaMultiplier; public static ConfigEntry BowAttackStaminaMultiplier; public static ConfigEntry BowProjectileSpeedMultiplier; public static ConfigEntry BowDamageMultiplier; public static ConfigEntry CrossbowReloadMultiplier; public static void Bind(ConfigFile cfg) { //IL_0770: Unknown result type (might be due to invalid IL or missing references) MaxLevel = cfg.Bind("1 - Progressao", "MaxLevel", 60, "Level maximo alcancavel."); PointsPerLevel = cfg.Bind("1 - Progressao", "PointsPerLevel", 3, "Pontos de atributo concedidos por level."); XpCurveFactor = cfg.Bind("1 - Progressao", "XpCurveFactor", 50f, "A na formula: XP_para_proximo_level = A * level^B."); XpCurveExponent = cfg.Bind("1 - Progressao", "XpCurveExponent", 1.5f, "B na formula: XP_para_proximo_level = A * level^B."); XpKillFactor = cfg.Bind("2 - XP por abate", "XpKillFactor", 2f, "K na formula: XP = K * vidaBase^E * multEstrela * multBoss."); XpKillExponent = cfg.Bind("2 - XP por abate", "XpKillExponent", 0.75f, "E na formula de XP. Abaixo de 1 impede que criaturas gigantes dominem a curva."); XpStarBonus = cfg.Bind("2 - XP por abate", "XpStarBonus", 0.5f, "Bonus de XP por estrela da criatura. 0.5 = mais 50 por cento por estrela."); XpBossMultiplier = cfg.Bind("2 - XP por abate", "XpBossMultiplier", 5f, "Multiplicador de XP para bosses."); XpRequiresOwnDamage = cfg.Bind("2 - XP por abate", "XpRequiresOwnDamage", true, "Exige que voce tenha causado dano na criatura para receber XP. Se false, todo jogador dentro de XpNearbyRadius da morte recebe."); XpNearbyRadius = cfg.Bind("2 - XP por abate", "XpNearbyRadius", 60f, "Raio em metros para XP de proximidade, usado apenas quando XpRequiresOwnDamage = false."); XpGlobalRate = cfg.Bind("2 - XP por abate", "XpGlobalRate", 1f, "Multiplicador global de XP. Use para acelerar ou desacelerar o servidor inteiro."); GrowthHealthPerLevel = cfg.Bind("3 - Crescimento automatico por level", "HealthPerLevel", 1.5f, "Vida maxima ganha por level, sem gastar ponto."); GrowthStaminaPerLevel = cfg.Bind("3 - Crescimento automatico por level", "StaminaPerLevel", 1.6f, "Stamina maxima ganha por level, sem gastar ponto."); GrowthEitrPerLevel = cfg.Bind("3 - Crescimento automatico por level", "EitrPerLevel", 0.8f, "Eitr maximo ganho por level, sem gastar ponto."); HealthPerVitality = cfg.Bind("4 - Ganho por ponto distribuido", "HealthPerVitality", 1f, "Vida maxima por ponto de Vitalidade."); StaminaPerEndurance = cfg.Bind("4 - Ganho por ponto distribuido", "StaminaPerEndurance", 0.9f, "Stamina maxima por ponto de Resistencia."); EitrPerSpirit = cfg.Bind("4 - Ganho por ponto distribuido", "EitrPerSpirit", 1f, "Eitr maximo por ponto de Espirito."); CarryPerConditioning = cfg.Bind("4 - Ganho por ponto distribuido", "CarryKgPerConditioning", 4f, "Quilos de carga por ponto de Condicionamento. Base do jogo: 300 kg; cinto Megingjord: +150."); GrowthCarryPerLevel = cfg.Bind("3 - Crescimento automatico por level", "CarryKgPerLevel", 1f, "Quilos de carga ganhos por level, sem gastar ponto."); ItemBonusesEnabled = cfg.Bind("12 - Atributos em itens", "Enabled", true, "Itens equipados concedem pontos de atributo derivados dos proprios stats (armor, bloqueio, dano)."); ShowItemBonusTooltip = cfg.Bind("12 - Atributos em itens", "ShowInTooltip", true, "Mostra o bonus no tooltip do item."); ArmorToAttribute = cfg.Bind("12 - Atributos em itens", "ArmorToAttribute", 0.25f, "Pontos por unidade de armor em capacete/peito/pernas/capa. Ex.: carapaca 32 -> 8."); BlockToAttribute = cfg.Bind("12 - Atributos em itens", "BlockToAttribute", 0.1f, "Pontos de Vitalidade por unidade de bloqueio de escudos. Ex.: metal negro 78 -> 8."); MeleeDamageToAttribute = cfg.Bind("12 - Atributos em itens", "MeleeDamageToAttribute", 0.08f, "Pontos de Resistencia por unidade de dano total de armas corpo a corpo. Ex.: espada de bronze 35 -> 3."); RangedDamageToAttribute = cfg.Bind("12 - Atributos em itens", "RangedDamageToAttribute", 0.06f, "Pontos de Resistencia por unidade de dano de arcos e bestas. Menor porque besta tem dano por tiro alto."); MagicStaffBase = cfg.Bind("12 - Atributos em itens", "MagicStaffBase", 6f, "Pontos de Espirito de um cajado na qualidade 1. O dano do cajado esta no projetil, entao nao serve de base."); MagicStaffPerQuality = cfg.Bind("12 - Atributos em itens", "MagicStaffPerQuality", 2f, "Pontos de Espirito extras por nivel de qualidade do cajado."); HeavyArmorMovementThreshold = cfg.Bind("12 - Atributos em itens", "HeavyArmorMovementThreshold", -0.05f, "Armadura com penalidade de movimento igual ou pior que isto conta como pesada (Vitalidade). Acima disso, leve (Resistencia)."); FoodGivesNoStats = cfg.Bind("5 - Comida", "FoodGivesNoStats", true, "Comida deixa de dar vida, stamina e eitr maximos. Desligue para rodar o mod em modo aditivo."); DeathXpLossPercent = cfg.Bind("6 - Morte", "XpLossPercent", 10f, "Percentual do XP do level atual perdido ao morrer."); DeathCanDelevel = cfg.Bind("6 - Morte", "CanDelevel", true, "Se a perda de XP pode regredir o level. Pontos excedentes sao devolvidos automaticamente."); RespecEnabled = cfg.Bind("7 - Respec", "Enabled", true, "Permite redistribuir pontos ja gastos."); RespecRequiresShelter = cfg.Bind("7 - Respec", "RequiresShelter", true, "Respec exige estar sob abrigo."); RespecMinComfort = cfg.Bind("7 - Respec", "MinComfort", 1, "Nivel minimo de conforto para dar respec. 0 desativa a exigencia."); HungerEnabled = cfg.Bind("8 - Fome", "Enabled", true, "Ativa o sistema de fome."); HungerMax = cfg.Bind("8 - Fome", "Max", 100f, "Saciedade maxima."); HungerDrainMinutes = cfg.Bind("8 - Fome", "DrainMinutes", 30f, "Minutos para ir de saciedade cheia a zero."); SatietyPerBurnSecond = cfg.Bind("8 - Fome", "SatietyPerBurnSecond", 0.05f, "Saciedade ganha por segundo de m_foodBurnTime da comida. Comida melhor dura mais, logo enche mais."); HungryThreshold = cfg.Bind("8 - Fome", "HungryThreshold", 60f, "Abaixo desta saciedade entra o estado Com Fome."); StarvingThreshold = cfg.Bind("8 - Fome", "StarvingThreshold", 30f, "Abaixo desta saciedade entra o estado Faminto."); HungryHealthRegenMult = cfg.Bind("8 - Fome", "HungryHealthRegen", 0.5f, "Multiplicador de regeneracao de vida no estado Com Fome."); HungryStaminaRegenMult = cfg.Bind("8 - Fome", "HungryStaminaRegen", 0.8f, "Multiplicador de regeneracao de stamina no estado Com Fome."); StarvingHealthRegenMult = cfg.Bind("8 - Fome", "StarvingHealthRegen", 0f, "Multiplicador de regeneracao de vida no estado Faminto."); StarvingStaminaRegenMult = cfg.Bind("8 - Fome", "StarvingStaminaRegen", 0.5f, "Multiplicador de regeneracao de stamina no estado Faminto."); StarvingEitrRegenMult = cfg.Bind("8 - Fome", "StarvingEitrRegen", 0.5f, "Multiplicador de regeneracao de eitr no estado Faminto."); WellFedThreshold = cfg.Bind("8 - Fome", "WellFedThreshold", 90f, "Saciedade a partir da qual o estado Bem Alimentado da bonus de regeneracao."); WellFedRegenMult = cfg.Bind("8 - Fome", "WellFedRegen", 1.1f, "Multiplicador de regeneracao de vida e stamina no estado Bem Alimentado."); StarvationDamage = cfg.Bind("8 - Fome", "StarvationDamage", 1f, "Dano por tick com a saciedade zerada."); StarvationDamageInterval = cfg.Bind("8 - Fome", "StarvationDamageInterval", 5f, "Segundos entre ticks de dano de inanicao."); StarvationCanKill = cfg.Bind("8 - Fome", "CanKill", true, "Se a inanicao pode matar o personagem."); EquipmentDropsEnabled = cfg.Bind("9 - Drop de equipamento", "Enabled", true, "Criaturas dropam o equipamento que estavam efetivamente usando."); WeaponDropChance = cfg.Bind("9 - Drop de equipamento", "WeaponDropChance", 1f, "Chance de dropar a arma equipada. 1.0 = sempre."); ShieldDropChance = cfg.Bind("9 - Drop de equipamento", "ShieldDropChance", 1f, "Chance de dropar o escudo equipado."); ArmorDropChance = cfg.Bind("9 - Drop de equipamento", "ArmorDropChance", 1f, "Chance de dropar cada peca de armadura equipada."); ToolDropChance = cfg.Bind("9 - Drop de equipamento", "ToolDropChance", 1f, "Chance de dropar ferramentas e tochas equipadas."); AmmoDropChance = cfg.Bind("9 - Drop de equipamento", "AmmoDropChance", 0.5f, "Chance de dropar municao, como flechas."); DropsScaleWithStars = cfg.Bind("9 - Drop de equipamento", "ScaleWithStars", false, "Criaturas com estrela dropam multiplas copias do equipamento."); ExcludeBossesFromEquipDrops = cfg.Bind("9 - Drop de equipamento", "ExcludeBosses", true, "Bosses ficam de fora desta regra e mantem apenas a drop table original."); EquipDropPrefabBlacklist = cfg.Bind("9 - Drop de equipamento", "PrefabBlacklist", "", "Nomes de prefab de item que nunca devem dropar, separados por virgula."); CreatureItemRemap = cfg.Bind("9 - Drop de equipamento", "CreatureItemRemap", "skeleton_sword=SwordBronze*70|Club*30,skeleton_sword2=SwordBronze*70|Club*30,skeleton_mace=MaceBronze*70|Club*30,skeleton_bow=BowFineWood*70|Bow*30,skeleton_bow2=BowFineWood*70|Bow*30,draugr_sword=SwordIron*70|SwordBronze*30,draugr_axe=AxeIron*60|AxeBronze*30|AxeStone*10,draugr_bow=BowHuntsman*70|BowFineWood*30,GoblinSword=SwordBlackmetal*70|SwordIron*30,GoblinClub=MaceNeedle*70|MaceIron*30,GoblinSpear=SpearWolfFang*70|SpearElderbark*30,GoblinTorch=Torch,DvergerArbalest=CrossbowArbalest,DvergerStaffFire=StaffFireball,DvergerStaffIce=StaffIceShards,GoblinShield=ShieldBlackmetal,GoblinShoulders=ArmorPaddedCuirass,GoblinLoin=ArmorPaddedGreaves,GoblinHelmet=HelmetPadded,GoblinBrute_Attack=AtgeirBlackmetal*70|SledgeIron*30,GoblinShaman_attack_poke=StaffFireball,Fenring_attack_claw=FistFenrirClaw,Fenring_attack_fireclaw=FistFenrirClaw,Fenring_attack_iceclaw=FistFenrirClaw,DvergerStaffHeal=StaffShield,DvergerStaffNova=StaffIceShards,DvergerSuitArbalest=ArmorCarapaceChest,DvergerSuitFire=ArmorMageChest,DvergerSuitIce=ArmorMageChest,DvergerSuitSupport=ArmorMageChest,charred_greatsword_swing=THSwordSlayer,charred_bow=BowAshlands,charred_magestaff_fire=StaffClusterbomb,Charred_Helmet=HelmetFlametal,Charred_Breastplate=ArmorFlametalChest", "Armas internas de criatura nao sao itens de jogador (sem ZNetView), entao nao podem cair no chao. Este mapa traduz 'prefab_da_criatura=PrefabDoJogador'. Itens internos sem entrada sao ignorados. Nomes de criatura vem do mmo_dumpequip; nomes de item de jogador podem ser conferidos com mmo_dumpitems. O proprio mmo_dumpequip marca '(remap invalido!)' quando o alvo nao existe."); LogSkippedInternalItems = cfg.Bind("9 - Drop de equipamento", "LogSkippedInternalItems", true, "Loga uma vez cada item interno ignorado por falta de remap, para ajudar a preencher o mapa."); RemapStarBias = cfg.Bind("9 - Drop de equipamento", "RemapStarBias", 0.15f, "Quanto cada estrela da criatura soma a chance do PRIMEIRO alvo do remap (o tier mais alto da linha). 0.15: esqueleto 2* dropa sempre bronze."); DropOnlyPlayerItems = cfg.Bind("9 - Drop de equipamento", "DropOnlyPlayerItems", true, "So dropa itens que o jogador consegue usar (com receita, do mod ou vendido por mercador vanilla). Item de criatura com rede mas sem receita (GoblinArmor, GoblinShield...) e ignorado, a menos que tenha um de-para em CreatureItemRemap."); OpenPanelKey = cfg.Bind("10 - Interface", "OpenPanelKey", new KeyboardShortcut((KeyCode)112, Array.Empty()), "Tecla que abre o painel de atributos."); ShowHungerBar = cfg.Bind("10 - Interface", "ShowHungerBar", true, "Desenha a barra de saciedade no HUD, no estilo da barra de stamina do jogo."); ShowXpBar = cfg.Bind("10 - Interface", "ShowXpBar", true, "Desenha a barra de XP no HUD."); ShowXpGainMessages = cfg.Bind("10 - Interface", "ShowXpGainMessages", true, "Mostra o XP ganho no canto da tela ao matar criaturas."); HungerBarOffsetX = cfg.Bind("10 - Interface", "HungerBarOffsetX", 0f, "Deslocamento horizontal da barra de saciedade em relacao a barra de stamina."); HungerBarOffsetY = cfg.Bind("10 - Interface", "HungerBarOffsetY", 42f, "Deslocamento vertical da barra de saciedade em relacao a barra de stamina. Positivo = acima."); HungerBarWidth = cfg.Bind("10 - Interface", "HungerBarWidth", 160f, "Largura da barra de saciedade em pixels."); XpBarOffsetX = cfg.Bind("10 - Interface", "XpBarOffsetX", 0f, "Deslocamento horizontal da barra de XP (0 = centro)."); XpBarOffsetY = cfg.Bind("10 - Interface", "XpBarOffsetY", 82f, "Altura da barra de XP a partir da base da tela. Fica entre a barra de dicas e a de stamina."); XpBarWidth = cfg.Bind("10 - Interface", "XpBarWidth", 440f, "Largura da barra de XP em pixels."); PanelOpensWithInventory = cfg.Bind("10 - Interface", "PanelOpensWithInventory", true, "O painel de atributos aparece junto com o inventario, no espaco central. A tecla OpenPanelKey continua funcionando fora do inventario."); SlotsEnabled = cfg.Bind("13 - Slots por peso", "Enabled", true, "Linhas extras de mochila conforme o peso maximo. 300 kg = 24 slots uteis (12,5 kg/slot)."); SlotsKgPerRow = cfg.Bind("13 - Slots por peso", "KgPerRow", 96f, "Quilos acima de 300 para ganhar uma linha (8 slots). 96 kg = 12 kg por slot."); SlotsMaxRows = cfg.Bind("13 - Slots por peso", "MaxRows", 8, "Total maximo de linhas do inventario (vanilla = 4). O grid rola quando passa do painel."); MerchantEnabled = cfg.Bind("14 - Mercador", "Enabled", true, "Spawna um mercador ao lado das pedras iniciais (StartTemple) que vende todos os itens por moedas."); MerchantPrefab = cfg.Bind("14 - Mercador", "Prefab", "Haldor", "Prefab de Trader usado como mercador."); MerchantName = cfg.Bind("14 - Mercador", "Name", "Mercador", "Nome exibido do mercador."); MerchantOffsetX = cfg.Bind("14 - Mercador", "OffsetX", 9f, "Deslocamento em X a partir do centro do StartTemple."); MerchantOffsetZ = cfg.Bind("14 - Mercador", "OffsetZ", 0f, "Deslocamento em Z a partir do centro do StartTemple."); MerchantPriceMultiplier = cfg.Bind("14 - Mercador", "PriceMultiplier", 1f, "Multiplicador global aplicado as materias-primas (propaga pela cadeia de receitas)."); MerchantCraftMarkup = cfg.Bind("14 - Mercador", "CraftMarkup", 1.15f, "Multiplicador sobre a soma dos insumos de uma receita (tempo de craft)."); MerchantCraftBaseFee = cfg.Bind("14 - Mercador", "CraftBaseFee", 5f, "Taxa fixa por item craftado."); MerchantStationLevelFee = cfg.Bind("14 - Mercador", "StationLevelFee", 5f, "Taxa extra por nivel de estacao exigido acima de 1."); MerchantConversionMarkup = cfg.Bind("14 - Mercador", "ConversionMarkup", 1.3f, "Multiplicador em conversoes de fornalha/alto-forno (alem do combustivel)."); MerchantCreatureDropExponent = cfg.Bind("14 - Mercador", "CreatureDropExponent", 0.6f, "Preco de drop de criatura = vida^expoente. 0.6: javali 10->4, greydwarf 40->9, troll 600->46, lox 1000->63."); MerchantTrophyMultiplier = cfg.Bind("14 - Mercador", "TrophyMultiplier", 3f, "Trofeus custam este multiplo do drop comum da criatura."); MerchantGatheredBase = cfg.Bind("14 - Mercador", "GatheredBase", 3f, "Preco de itens coletados (Pickable, arvores, destrutiveis) sem preco explicito."); MerchantMinedBase = cfg.Bind("14 - Mercador", "MinedBase", 8f, "Preco de itens minerados sem preco explicito."); MerchantUnknownBase = cfg.Bind("14 - Mercador", "UnknownBase", 25f, "Preco de itens sem origem conhecida. Aparecem marcados no mmo_dumpprices."); MerchantMaterialStack = cfg.Bind("14 - Mercador", "MaterialStack", 10, "Materiais e municao sao vendidos em lotes deste tamanho."); MerchantConsumableStack = cfg.Bind("14 - Mercador", "ConsumableStack", 5, "Comidas e pocoes sao vendidas em lotes deste tamanho."); MerchantExcludeQuestItems = cfg.Bind("14 - Mercador", "ExcludeQuestItems", true, "Nao vende itens de missao (m_questItem)."); MerchantExcludePattern = cfg.Bind("14 - Mercador", "ExcludePattern", "Cheat,Coins,PlayerUnarmed,CapeTest,ShieldKnight,ShieldIronSquare,_shoot,TurretBoltBone,TrophyDraugrFem,TrophyForestTroll,HildirKey,DvergrKeyFragment,VegvisirShard,Chest_hildir,=AtgeirWood,=AxeWood,=BattleaxeWood,=KnifeWood,=MaceWood,=SledgeWood,=SpearWood,=THSwordWood,=PickaxeStone,=Flametal,=FlametalOre,=SwordIronFire,=DvergerArbalest,Pot_Shard", "Itens que nunca sao vendidos, separados por virgula. Sem '=' compara trecho do nome; com '=' exige nome exato (ex.: '=Flametal' exclui o item legado sem excluir FlametalNew). Padrao: itens de dev e chaves de missao."); MerchantBasePrices = cfg.Bind("14 - Mercador", "BasePrices", "Wood=2,Stone=1,Resin=2,Flint=2,Feathers=3,LeatherScraps=3,DeerHide=4,BoneFragments=3,Honey=4,Raspberry=2,Blueberries=2,Mushroom=2,MushroomYellow=4,MushroomBlue=6,Cloudberry=6,Dandelion=2,Thistle=4,Carrot=3,Turnip=4,Onion=5,Barley=8,Flax=8,CarrotSeeds=2,TurnipSeeds=3,OnionSeeds=4,FineWood=4,RoundLog=4,ElderBark=8,YggdrasilWood=12,Blackwood=16,CopperOre=6,TinOre=6,IronScrap=15,SilverOre=40,BlackMetalScrap=60,FlametalOreNew=120,Coal=3,Tar=15,Sap=20,Guck=8,Chitin=20,Crystal=25,Obsidian=12,BlackMarble=8,Grausten=10,TrollHide=12,WolfPelt=30,LoxPelt=45,Bloodbag=10,Entrails=10,Ooze=12,FreezeGland=25,WolfFang=25,Needle=40,ScaleHide=45,Carapace=55,Mandible=50,RoyalJelly=60,Softtissue=25,MushroomMagecap=30,MushroomJotunPuffs=30,JuteRed=20,JuteBlue=45,CharredBone=70,MoltenCore=140,CelestialFeather=150,Bonemawtooth=120,Amber=10,AmberPearl=40,Ruby=60,SilverNecklace=90,Coins=1,BronzeScrap=20,IronOre=15,DragonEgg=300,ChickenEgg=60,AsksvinEgg=150,Fish1=10,Fish2=15,Fish3=20,Fish4_cave=25,Fish5=30,Fish6=35,Fish7=40,Fish8=45,Fish9=50,Fish10=55,Fish11=60,Fish12=80,FeastMeadows=60,FeastBlackforest=90,FeastSwamps=130,FeastMountains=170,FeastPlains=220,FeastOceans=250,FeastMistlands=300,FeastAshlands=400,HelmetMidsummerCrown=50,HelmetPointyHat=50,TorchMist=40,AxeHead1=25,AxeHead2=25,BonemawSerpentScale=80,FishAnglerRaw=60,Larva=15,WolfClaw=30", "Precos de materias-primas (prefab=moedas). Tudo o que deriva delas via receita e calculado automaticamente. Nomes nao existentes nesta build sao ignorados."); MerchantPriceOverrides = cfg.Bind("14 - Mercador", "PriceOverrides", "", "Preco final forcado por item (prefab=moedas). Ganha de qualquer calculo."); MerchantBuysItems = cfg.Bind("14 - Mercador", "BuysItems", true, "O mercador compra qualquer item da mochila (painel VENDER ao lado da loja)."); MerchantBuyRatio = cfg.Bind("14 - Mercador", "BuyRatio", 0.2f, "Fracao do valor BASE (sem inflacao) que o mercador paga. 0.2 = paga 20 por cento (80 por cento a menos)."); MerchantBuysMaterials = cfg.Bind("14 - Mercador", "BuysMaterials", false, "Se o mercador compra itens do tipo Material (madeira, pedra, minerios, couros, lingotes). Desligado por padrao: coleta nao pode virar ouro infinito."); MerchantNoBuyPattern = cfg.Bind("14 - Mercador", "NoBuyPattern", "", "Itens adicionais que o mercador nunca compra (trecho do nome, ou '=Nome' exato), separados por virgula."); MerchantInflationEveryDays = cfg.Bind("14 - Mercador", "InflationEveryDays", 10, "Inflacao: a cada tantos dias do mundo os precos sobem InflationPercent. 0 desliga."); MerchantInflationPercent = cfg.Bind("14 - Mercador", "InflationPercent", 5f, "Percentual de aumento por periodo. Composto: multiplicador = (1 + p)^(dia / InflationEveryDays)."); MerchantInflationMaxMultiplier = cfg.Bind("14 - Mercador", "InflationMaxMultiplier", 4f, "Teto do multiplicador de inflacao. Com 5 por cento a cada 10 dias, 4x e atingido por volta do dia 285."); DungeonBossesEnabled = cfg.Bind("16 - Dungeons", "Enabled", true, "Toda dungeon gerada recebe um boss na sala mais funda."); DungeonIncludeCamps = cfg.Bind("16 - Dungeons", "IncludeCamps", true, "Aldeias Fuling e ruinas das Cinzas (geradas pelo mesmo sistema) tambem recebem boss."); DungeonBossHpMultiplier = cfg.Bind("16 - Dungeons", "HpMultiplier", 1f, "Multiplicador sobre a vida alvo por tier (800 / 1600 / 2500 / 3500 / 4500 / 5500)."); DungeonBossStars = cfg.Bind("16 - Dungeons", "Stars", 2, "Estrelas do boss (0-2). Cada estrela aumenta o dano como no jogo."); DungeonBossItemQuality = cfg.Bind("16 - Dungeons", "ItemQuality", 2, "Qualidade do item que o boss humanoide equipa e dropa. 2 = um passo acima do craftavel."); DungeonBossGoldRatio = cfg.Bind("16 - Dungeons", "GoldRatio", 0.4f, "Boss que nao equipa item dropa ouro = preco do item de referencia do tier x este valor."); XpDungeonBossMultiplier = cfg.Bind("16 - Dungeons", "XpMultiplier", 2f, "Multiplicador de XP de boss de dungeon (bosses invocados usam XpBossMultiplier)."); DungeonBossMusicRange = cfg.Bind("16 - Dungeons", "MusicRange", 45f, "Distancia em metros para o evento de boss (musica/clima) ligar."); PotionCooldown = cfg.Bind("17 - Pocoes e utilitarios", "PotionCooldown", 20f, "Segundos de recarga compartilhada entre todas as pocoes (vida, vigor, eitr)."); ReturnStoneCooldownMultiplier = cfg.Bind("17 - Pocoes e utilitarios", "ReturnStoneCooldownMultiplier", 1f, "Multiplica a recarga base da Pedra de Retorno (30 min)."); ReturnStoneIgnoresOre = cfg.Bind("17 - Pocoes e utilitarios", "ReturnStoneIgnoresOre", false, "Se true, a Pedra de Retorno teleporta mesmo carregando minerios (ignora regra de portal)."); DungeonResetDays = cfg.Bind("16 - Dungeons", "ResetDays", 5, "Dias do mundo apos a morte para o boss de dungeon renascer na mesma sala. 0 = nunca."); ShowPlayerLevels = cfg.Bind("10 - Interface", "ShowPlayerLevels", true, "Mostra 'Lv N' ao lado do nome dos jogadores."); EquipmentRowEnabled = cfg.Bind("13 - Slots por peso", "EquipmentRow", true, "A ultima linha da mochila e reservada para o equipamento vestido: equipar move a peca para la e libera o slot. Armas/escudos equipados a partir da hotbar ficam na hotbar (troca por tecla continua funcionando)."); VersionCheckEnabled = cfg.Bind("18 - Rede", "VersionCheck", true, "Servidor recusa clientes sem o mod (mostra 'versao incompativel' para eles)."); VersionCheckExact = cfg.Bind("18 - Rede", "VersionCheckExact", true, "Exige a MESMA versao do mod no cliente e no servidor."); ConfigSyncEnabled = cfg.Bind("18 - Rede", "ConfigSync", true, "Servidor envia sua config aos clientes na conexao (exceto secoes de interface e rede). Aplicada so em memoria; o .cfg local do cliente nao e alterado."); PvpEnabled = cfg.Bind("22 - PvP e karma", "Enabled", true, "Karma por matar jogadores, nome de procurado, guardas hostis a procurados e cidades fechadas para eles."); StealthHidesName = cfg.Bind("22 - PvP e karma", "StealthHidesName", true, "Jogador agachado nao mostra a barra de nome/vida para os outros."); KarmaPerKill = cfg.Bind("22 - PvP e karma", "KarmaPerKill", 10f, "Karma ganho ao matar um jogador limpo (karma abaixo de WantedThreshold)."); KarmaDeathLoss = cfg.Bind("22 - PvP e karma", "KarmaDeathLoss", 10f, "Karma perdido ao morrer (nao zera)."); KarmaDecayPerMinute = cfg.Bind("22 - PvP e karma", "KarmaDecayPerMinute", 0.5f, "Karma perdido por minuto ONLINE. 0.5: um assassinato limpa em 20 min."); KarmaMonsterFactor = cfg.Bind("22 - PvP e karma", "KarmaMonsterFactor", 0.02f, "Karma perdido por monstro = fator x vida^0.6 (boss x5). 0.02: greydwarf 0.18, troll 0.93, lox 1.26."); WantedThreshold = cfg.Bind("22 - PvP e karma", "WantedThreshold", 10f, "A partir deste karma o jogador e PROCURADO: nome vermelho, guardas atacam, cidade fecha, mata-lo nao da karma."); OutlawThreshold = cfg.Bind("22 - PvP e karma", "OutlawThreshold", 30f, "A partir deste karma o jogador e FORA DA LEI (so muda o rotulo; recompensa ja cresce com o karma)."); BountyPerKarma = cfg.Bind("22 - PvP e karma", "BountyPerKarma", 5f, "Ouro pago a quem mata um procurado = karma da vitima x este valor."); KarmaMax = cfg.Bind("22 - PvP e karma", "KarmaMax", 100f, "Teto do karma."); GuardsEnabled = cfg.Bind("22 - PvP e karma", "Guards", true, "Guardas nas cidades: criatura humanoide do bioma com faccao de jogador, arma do tier, posto fixo. Atacam procurados e monstros."); GuardsPerCity = cfg.Bind("22 - PvP e karma", "GuardsPerCity", 4, "Guardas por cidade. Repostos no dia seguinte a morte."); GuardStars = cfg.Bind("22 - PvP e karma", "GuardStars", 3, "Estrelas do guarda (0-3)."); GuardHpMultiplier = cfg.Bind("22 - PvP e karma", "GuardHpMultiplier", 1f, "Vida do guarda = vida do boss de dungeon do tier (800/1600/2500/3500/4500/5500 x HpMultiplier das dungeons) x este valor. 1 = cada guarda e um boss."); GuardScaleWithPlayers = cfg.Bind("22 - PvP e karma", "GuardScaleWithPlayers", true, "Vida do guarda tambem escala com o numero de jogadores proximos, como os bosses (secao Dungeons)."); CitiesEnabled = cfg.Bind("21 - Cidades", "Enabled", true, "Uma cidade por bioma ao lado do altar do boss: mercador do bioma, emissario de missoes, praca e zona segura. Com cidades, o mercador do spawn vende so o tier SpawnMerchantTier e itens do mod."); CityAnchors = cfg.Bind("21 - Cidades", "Anchors", "1=Eikthyrnir,2=GDKing,3=Bonemass,4=Dragonqueen,5=GoblinKing,6=Mistlands_DvergrBossEntrance1,7=FaderLocation", "tier=nome da location do altar do boss (ZoneSystem). Use mmo_dumplocations para conferir os nomes desta build."); SpawnMerchantTier = cfg.Bind("21 - Cidades", "SpawnMerchantTier", 1, "Tier maximo vendido pelo mercador do spawn quando cidades estao ativas."); CitySellsLowerTiers = cfg.Bind("21 - Cidades", "SellsLowerTiers", true, "Cidade vende itens de biomas anteriores com sobretaxa de importacao. Se false, so o proprio bioma."); ImportMarkupPerTier = cfg.Bind("21 - Cidades", "ImportMarkupPerTier", 0.4f, "Sobretaxa por tier de distancia para itens importados. 0.4: item de 2 biomas atras custa +80%."); LocalBuyRatio = cfg.Bind("21 - Cidades", "LocalBuyRatio", 0.3f, "Fracao do valor base que a cidade paga por itens do PROPRIO bioma (os demais usam BuyRatio)."); CityPlaza = cfg.Bind("21 - Cidades", "Plaza", true, "Instancia a praca minima: fogueira, estacao do bioma e 4 banners."); CityBuildings = cfg.Bind("21 - Cidades", "Buildings", true, "Constroi casas e cerca com pecas do bioma (plantas em BepInEx/config/ValheimMMO_cityplan.txt; mmo_cityplan)."); CityRoofYawFix = cfg.Bind("21 - Cidades", "RoofYawFix", 0f, "Correcao de rotacao (graus) das pecas de telhado, se sairem viradas ao contrario: tente 180."); CityMinDistance = cfg.Bind("21 - Cidades", "MinDistanceFromAltar", 70f, "Distancia minima (m) entre o altar do boss e o centro da cidade."); CityKeepAwayFromSpawn = cfg.Bind("21 - Cidades", "KeepAwayFromSpawn", 80f, "Distancia minima (m) entre o centro da cidade e as pedras iniciais (StartTemple). A cidade nunca nasce em cima do respawn."); CityMaxDistance = cfg.Bind("21 - Cidades", "MaxDistanceFromAltar", 140f, "Distancia maxima (m). A cidade nasce no ponto mais plano e seco desse anel que ja estiver carregado."); CitySafeZone = cfg.Bind("21 - Cidades", "SafeZone", true, "Zona sem spawn de monstros ao redor do mercador da cidade (EffectArea NoMonsters)."); CitySafeRadius = cfg.Bind("21 - Cidades", "SafeRadius", 40f, "Raio da zona segura em metros."); CityPortals = cfg.Bind("21 - Cidades", "Portals", true, "Rede de portais: a cidade de tier 1 e o hub com um portal para cada outra cidade; cada cidade tem um portal de volta. Tags fixas, nao editaveis. A regra vanilla de minerio nao teleportar continua valendo."); CityLevelTerrain = cfg.Bind("21 - Cidades", "LevelTerrain", true, "Terraplana a praca (nivela e alisa) antes de colocar as pecas, com a mesma operacao da enxada."); CityLevelRadius = cfg.Bind("21 - Cidades", "LevelRadius", 16f, "Raio da terraplanagem em metros."); MaterialTiers = cfg.Bind("21 - Cidades", "MaterialTiers", "Wood=1,Stone=1,Resin=1,Flint=1,Feathers=1,LeatherScraps=1,DeerHide=1,Honey=1,Raspberry=1,Mushroom=1,Dandelion=1,RawMeat=1,DeerMeat=1,NeckTail=1,HardAntler=1,Amber=1,Fish1=1,BoneFragments=2,Blueberries=2,MushroomYellow=2,Thistle=2,Carrot=2,CarrotSeeds=2,FineWood=2,RoundLog=2,CopperOre=2,TinOre=2,Coal=2,TrollHide=2,AmberPearl=2,Ruby=2,SurtlingCore=2,AncientSeed=2,BronzeScrap=2,GreydwarfEye=2,Fish2=2,MushroomBlue=3,Turnip=3,TurnipSeeds=3,ElderBark=3,IronScrap=3,IronOre=3,Guck=3,Chitin=3,Bloodbag=3,Entrails=3,Ooze=3,SilverNecklace=3,Chain=3,WitheredBone=3,Wishbone=3,SerpentMeat=3,SerpentScale=3,Fish3=3,Onion=4,OnionSeeds=4,SilverOre=4,Crystal=4,Obsidian=4,WolfPelt=4,FreezeGland=4,WolfFang=4,DragonEgg=4,WolfMeat=4,Fish4_cave=4,Cloudberry=5,Barley=5,Flax=5,BlackMetalScrap=5,Tar=5,LoxPelt=5,Needle=5,LoxMeat=5,ChickenEgg=5,Fish5=5,Fish6=5,YggdrasilWood=6,Sap=6,BlackMarble=6,ScaleHide=6,Carapace=6,Mandible=6,RoyalJelly=6,Softtissue=6,MushroomMagecap=6,MushroomJotunPuffs=6,JuteRed=6,JuteBlue=6,Eitr=6,SeekerMeat=6,HareMeat=6,BugMeat=6,Fish7=6,Fish8=6,FlametalOreNew=7,Blackwood=7,Grausten=7,CharredBone=7,MoltenCore=7,CelestialFeather=7,Bonemawtooth=7,AskHide=7,AsksvinEgg=7,Fish9=7,Fish10=7,Fish11=7,Fish12=7", "Bioma (1-7) das materias-primas. Tudo que deriva delas por receita herda o tier do insumo mais avancado. O que nao estiver aqui e detectado por spawn/vegetacao ou por faixa de preco. mmo_dumptiers mostra o resultado."); WorldBossEnabled = cfg.Bind("19 - Chefes mundiais", "Enabled", true, "A cada tantos dias do mundo, um chefe mundial surge perto de um jogador aleatorio e todos sao avisados."); WorldBossIntervalDays = cfg.Bind("19 - Chefes mundiais", "IntervalDays", 3, "Dias do mundo entre chefes mundiais."); WorldBossHpMultiplier = cfg.Bind("19 - Chefes mundiais", "HpMultiplier", 1f, "Multiplicador sobre a vida por bioma (3000 / 6000 / 8000 / 10000 / 12000 / 15000)."); WorldBossGoldBase = cfg.Bind("19 - Chefes mundiais", "GoldBase", 300, "Ouro para CADA contribuinte = GoldBase x tier do bioma (1-6)."); BossRaidEnabled = cfg.Bind("20 - Raids de boss", "Enabled", true, "Depois de derrotado, um boss invocado pode voltar como raid: clone do raid vanilla do bioma (mesma frequencia, duracao, perto da base, musica) com o proprio boss no lugar dos monstros."); BossRaidCooldownDays = cfg.Bind("20 - Raids de boss", "CooldownDays", 4, "Dias do mundo entre raids de boss (qualquer boss). Os raids comuns continuam normais."); BossRaidMinDuration = cfg.Bind("20 - Raids de boss", "MinDurationSeconds", 90f, "Duracao minima do evento. O boss continua vivo depois que o evento termina."); BossScalingEnabled = cfg.Bind("16 - Dungeons", "ScaleWithPlayers", true, "Vida maxima do boss escala com jogadores no raio: base x (1 + PerPlayer x (n-1)), mantendo a fracao de vida atual."); BossScalePerPlayer = cfg.Bind("16 - Dungeons", "ScalePerPlayer", 0.6f, "Acrescimo de vida por jogador adicional. 0.6: 2 jogadores = 160%, 3 = 220%, 4 = 280%."); BossScaleRadius = cfg.Bind("16 - Dungeons", "ScaleRadius", 80f, "Raio em metros para contar jogadores na luta."); BossScaleVanilla = cfg.Bind("16 - Dungeons", "ScaleVanillaBosses", true, "Aplica o escalonamento tambem aos bosses invocados do jogo (Eikthyr, Elder...)."); BankEnabled = cfg.Bind("14 - Mercador", "BankEnabled", true, "Aba COFRE no mercador: cofre pessoal salvo no personagem, sem peso."); BankRows = cfg.Bind("14 - Mercador", "BankRows", 6, "Linhas do cofre (8 slots por linha)."); DropDurabilityMin = cfg.Bind("9 - Drop de equipamento", "DropDurabilityMin", 0.2f, "Durabilidade minima (fracao) do equipamento dropado por mob comum."); DropDurabilityMax = cfg.Bind("9 - Drop de equipamento", "DropDurabilityMax", 0.6f, "Durabilidade maxima (fracao) do equipamento dropado por mob comum. Boss de dungeon dropa intacto."); HungerStatusIcons = cfg.Bind("8 - Fome", "StatusIcons", true, "Mostra 'Bem alimentado' / 'Com fome' na lista de efeitos do HUD."); RepairCostRatio = cfg.Bind("14 - Mercador", "RepairCostRatio", 0.1f, "Reparo no mercador custa preco do item x (1 - durabilidade) x este valor."); UpgradeCostFactor = cfg.Bind("14 - Mercador", "UpgradeCostFactor", 0.6f, "Melhorar qualidade no mercador custa preco do item x qualidade atual x este valor."); ExtraQualityLevels = cfg.Bind("14 - Mercador", "ExtraQualityLevels", 2, "Quantos niveis de qualidade ALEM do maximo craftavel o mercador pode dar (o bonus de atributo do item escala junto)."); QuestsEnabled = cfg.Bind("14 - Mercador", "QuestsEnabled", true, "Missoes diarias: no emissario de cada cidade (ou na aba MISSOES do mercador, sem cidades)."); QuestMaxActive = cfg.Bind("14 - Mercador", "QuestMaxActive", 5, "Quantas missoes aceitas ao mesmo tempo (somando todas as cidades)."); QuestTrackerEnabled = cfg.Bind("14 - Mercador", "QuestTracker", true, "Mostra as missoes aceitas com progresso no HUD, abaixo do minimapa."); QuestTrackerOffsetX = cfg.Bind("14 - Mercador", "QuestTrackerOffsetX", 20f, "Distancia da borda direita (px em 1080p)."); QuestTrackerOffsetY = cfg.Bind("14 - Mercador", "QuestTrackerOffsetY", 300f, "Distancia do topo (px em 1080p). Minimapa termina em ~280."); QuestRewardMultiplier = cfg.Bind("14 - Mercador", "QuestRewardMultiplier", 1f, "Multiplicador das recompensas de missao (ouro; o XP e 3x o ouro)."); MapPrice = cfg.Bind("15 - Mapa do Explorador", "Price", 150, "Preco base do Mapa do Explorador no mercador (a inflacao aplica por cima). Vendido por unidade."); MapRevealRadius = cfg.Bind("15 - Mapa do Explorador", "RevealRadius", 300f, "Raio em metros da regiao revelada. 300 m ~ 4-5 zonas de 64 m em cada direcao."); MapWorldRadius = cfg.Bind("15 - Mapa do Explorador", "WorldRadius", 9000f, "Raio do mundo dentro do qual a regiao e sorteada. O mundo tem ~10.500 m; alem de ~9.000 e so mar/borda."); MapBossDropChance = cfg.Bind("15 - Mapa do Explorador", "BossDropChance", 0.35f, "Chance de qualquer boss (vanilla ou de dungeon) dropar um mapa. 1.0 = sempre."); BowTweaksEnabled = cfg.Bind("11 - Arquearia", "Enabled", true, "Aplica os ajustes de arco e besta abaixo a todos os itens dessas skills (vanilla e de mods)."); BowDrawTimeMultiplier = cfg.Bind("11 - Arquearia", "DrawTimeMultiplier", 0.7f, "Multiplicador do tempo minimo para puxar o arco por completo. 0.7 = 30 por cento mais rapido."); BowDrawStaminaMultiplier = cfg.Bind("11 - Arquearia", "DrawStaminaMultiplier", 0.6f, "Multiplicador da stamina drenada enquanto segura o arco puxado."); BowAttackStaminaMultiplier = cfg.Bind("11 - Arquearia", "AttackStaminaMultiplier", 0.8f, "Multiplicador da stamina gasta ao disparar."); BowProjectileSpeedMultiplier = cfg.Bind("11 - Arquearia", "ProjectileSpeedMultiplier", 1.3f, "Multiplicador da velocidade da flecha. Maior = trajetoria mais reta e menos queda."); BowDamageMultiplier = cfg.Bind("11 - Arquearia", "DamageMultiplier", 1f, "Multiplicador de dano de arcos e bestas. 1.0 = sem alteracao."); CrossbowReloadMultiplier = cfg.Bind("11 - Arquearia", "CrossbowReloadMultiplier", 0.75f, "Multiplicador do tempo de recarga das bestas."); } } } namespace ValheimMMO.Commands { [HarmonyPatch(typeof(Terminal), "InitTerminal")] internal static class Terminal_InitTerminal_Patch { private static bool _registered; private static void Postfix() { if (!_registered) { _registered = true; ConsoleCommands.Register(); Plugin.Log.LogInfo((object)"Comandos de console registrados."); } } } internal static class ConsoleCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; public static ConsoleEvent <>9__0_1; public static ConsoleEvent <>9__0_2; public static ConsoleEvent <>9__0_3; public static ConsoleEvent <>9__0_4; public static ConsoleEvent <>9__0_5; public static ConsoleEvent <>9__0_6; public static ConsoleEvent <>9__0_7; public static ConsoleEvent <>9__0_8; public static ConsoleEvent <>9__0_9; public static ConsoleEvent <>9__0_10; public static ConsoleEvent <>9__0_11; public static ConsoleEvent <>9__0_12; public static ConsoleEvent <>9__0_13; public static ConsoleEvent <>9__0_14; public static ConsoleEvent <>9__0_15; public static ConsoleEvent <>9__0_16; public static ConsoleEvent <>9__0_17; public static ConsoleEvent <>9__0_18; public static ConsoleEvent <>9__0_19; public static ConsoleEvent <>9__0_20; public static Comparison<(string type, string row)> <>9__2_0; public static Converter <>9__3_0; internal void b__0_0(ConsoleEventArgs args) { PlayerProgress local = PlayerProgress.Local; if (local == null) { args.Context.AddString("Nenhum personagem ativo."); return; } Player localPlayer = Player.m_localPlayer; local.ComputeStats(localPlayer, out var hp, out var stamina, out var eitr); args.Context.AddString($"Level {local.Level}/{ModConfig.MaxLevel.Value} " + $"XP {local.Xp}/{local.XpForNextLevel}"); int[] array = ItemBonuses.TotalEquipped((Humanoid)(object)localPlayer); args.Context.AddString($"Vitalidade {local.Spent[0]} (+{array[0]} itens) " + $"Resistencia {local.Spent[1]} (+{array[1]}) " + $"Espirito {local.Spent[2]} (+{array[2]}) " + $"Condicionamento {local.Spent[3]} (+{array[3]}) " + $"(livres: {local.UnspentPoints})"); args.Context.AddString($"Vida {hp:0} Stamina {stamina:0} Eitr {eitr:0} Peso {localPlayer.GetMaxCarryWeight():0} kg"); if (ModConfig.HungerEnabled.Value) { HungerState s = HungerSystem.StateOf(local.Satiety); args.Context.AddString($"Saciedade {local.Satiety:0}/{HungerSystem.Max:0} ({HungerSystem.DisplayName(s)})"); } } internal void b__0_1(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; PlayerProgress local = PlayerProgress.Local; string reason; if (local == null) { args.Context.AddString("Nenhum personagem ativo."); } else if (local.RespecAll(localPlayer, out reason)) { MmoUI.RefreshStats(localPlayer); args.Context.AddString($"Pontos redistribuidos. {local.UnspentPoints} disponiveis."); } else { args.Context.AddString(reason); } } internal void b__0_2(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; PlayerProgress local = PlayerProgress.Local; if (local == null) { args.Context.AddString("Nenhum personagem ativo."); return; } if (args.Length < 2 || !long.TryParse(args[1], out var result)) { args.Context.AddString("Uso: mmo_addxp "); return; } int num = local.AddXp(result, localPlayer); MmoUI.RefreshStats(localPlayer); args.Context.AddString($"+{result} XP. Level {local.Level} (subiu {num})."); } internal void b__0_3(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; PlayerProgress local = PlayerProgress.Local; if (local == null) { args.Context.AddString("Nenhum personagem ativo."); return; } if (args.Length < 2 || !int.TryParse(args[1], out var result)) { args.Context.AddString("Uso: mmo_setlevel "); return; } local.Level = Mathf.Clamp(result, 1, ModConfig.MaxLevel.Value); local.Xp = 0L; local.SaveTo(localPlayer); MmoUI.RefreshStats(localPlayer); args.Context.AddString($"Level definido para {local.Level}. " + $"{local.UnspentPoints} pontos disponiveis."); } internal void b__0_4(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; PlayerProgress local = PlayerProgress.Local; if (local == null) { args.Context.AddString("Nenhum personagem ativo."); return; } if (args.Length < 2 || !float.TryParse(args[1], out var result)) { args.Context.AddString("Uso: mmo_satiety <0-100>"); return; } local.Satiety = Mathf.Clamp(result, 0f, HungerSystem.Max); local.SaveTo(localPlayer); args.Context.AddString($"Saciedade = {local.Satiety:0}"); } internal void b__0_5(ConsoleEventArgs args) { Player localPlayer = Player.m_localPlayer; PlayerProgress local = PlayerProgress.Local; if (local == null) { args.Context.AddString("Nenhum personagem ativo."); return; } local.Reset(); local.SaveTo(localPlayer); MmoUI.RefreshStats(localPlayer); args.Context.AddString("Progressao zerada."); } internal void b__0_6(ConsoleEventArgs args) { DumpEquipmentMap(args.Context); } internal void b__0_7(ConsoleEventArgs args) { DumpItems(args.Context); } internal void b__0_8(ConsoleEventArgs args) { <>c__DisplayClass0_0 <>c__DisplayClass0_ = default(<>c__DisplayClass0_0); <>c__DisplayClass0_.hud = Hud.instance; if ((Object)(object)<>c__DisplayClass0_.hud == (Object)null) { args.Context.AddString("HUD nao carregado."); return; } object obj; if (!((Object)(object)<>c__DisplayClass0_.hud.m_healthPanel != (Object)null)) { RectTransform healthBarRoot = <>c__DisplayClass0_.hud.m_healthBarRoot; obj = ((healthBarRoot != null) ? ((Transform)healthBarRoot).parent : null); } else { obj = ((Component)<>c__DisplayClass0_.hud.m_healthPanel).transform; } Transform val = (Transform)obj; if ((Object)(object)val == (Object)null) { args.Context.AddString("Painel de vida nao encontrado."); return; } <>c__DisplayClass0_.sb = new StringBuilder(); <>c__DisplayClass0_.sb.AppendLine("# Hierarquia de " + ((Object)val).name); <>c__DisplayClass0_.sb.AppendLine(); <>c__DisplayClass0_.sb.AppendLine("Marcadores: [healthBarRoot] [foodBarRoot] [foodBaseBar] [foodIcon] [foodText]"); <>c__DisplayClass0_.sb.AppendLine(); g__Walk|0_21(val, 0, ref <>c__DisplayClass0_); string text = Path.Combine(Paths.ConfigPath, "ValheimMMO_hud.md"); File.WriteAllText(text, <>c__DisplayClass0_.sb.ToString(), Encoding.UTF8); args.Context.AddString("Arquivo: " + text); } internal void b__0_9(ConsoleEventArgs args) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("Nenhum personagem ativo."); return; } int result; DungeonTier dungeonTier = DungeonBosses.TierById((args.Length <= 1 || !int.TryParse(args[1], out result)) ? 1 : result); if (dungeonTier == null) { args.Context.AddString("Tier invalido (1-6)."); return; } Vector3 pos = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 6f; if (DungeonBosses.Spawn(dungeonTier, pos, out var error)) { args.Context.AddString("Boss '" + dungeonTier.Title + "' spawnado."); } else { args.Context.AddString("Falha: " + error); } } internal void b__0_10(ConsoleEventArgs args) { if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { args.Context.AddString("Entre em um mundo primeiro."); return; } WorldBoss.SpawnNow(); args.Context.AddString("Chefe mundial invocado. Siga o pin no mapa."); } internal void b__0_11(ConsoleEventArgs args) { RandEventSystem instance = RandEventSystem.instance; if ((Object)(object)instance == (Object)null) { args.Context.AddString("RandEventSystem nao carregado."); return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("| Evento | Musica | Ambiente | Duracao | Spawns |"); stringBuilder.AppendLine("|---|---|---|---|---|"); foreach (RandomEvent @event in instance.m_events) { stringBuilder.AppendLine($"| `{@event.m_name}` | {@event.m_forceMusic} | {@event.m_forceEnvironment} | {@event.m_duration} | {@event.m_spawn?.Count ?? 0} |"); } string text = Path.Combine(Paths.ConfigPath, "ValheimMMO_events.md"); File.WriteAllText(text, stringBuilder.ToString(), Encoding.UTF8); args.Context.AddString($"{instance.m_events.Count} eventos. Arquivo: {text}"); } internal void b__0_12(ConsoleEventArgs args) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZoneSystem.instance == (Object)null) { args.Context.AddString("Entre em um mundo primeiro."); return; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("| Location | Bioma | Unica | Icone |"); stringBuilder.AppendLine("|---|---|---|---|"); foreach (ZoneLocation location in ZoneSystem.instance.m_locations) { if (location != null) { stringBuilder.AppendLine(string.Format("| `{0}` | {1} | {2} | {3} |", location.m_prefabName, location.m_biome, location.m_unique ? "sim" : "", (location.m_iconAlways || location.m_iconPlaced) ? "sim" : "")); } } string text = Path.Combine(Paths.ConfigPath, "ValheimMMO_locations.md"); File.WriteAllText(text, stringBuilder.ToString(), Encoding.UTF8); args.Context.AddString($"{ZoneSystem.instance.m_locations.Count} locations. Arquivo: {text}"); } internal void b__0_13(ConsoleEventArgs args) { if ((Object)(object)ObjectDB.instance == (Object)null) { args.Context.AddString("Entre em um mundo primeiro."); return; } MerchantItems.Invalidate(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("| Prefab | Tier | Bioma | Como |"); stringBuilder.AppendLine("|---|---|---|---|"); int[] array = new int[8]; foreach (TradeItem item in MerchantItems.List) { string name = ((Object)((Component)item.m_prefab).gameObject).name; int num = BiomeTiers.TierOf(name); array[Mathf.Clamp(num, 0, 7)]++; stringBuilder.AppendLine($"| `{name}` | {num} | {BiomeTiers.Name(num)} | {BiomeTiers.WhyOf(name)} |"); } stringBuilder.AppendLine(); for (int i = 0; i <= 7; i++) { stringBuilder.AppendLine($"- Tier {i} ({BiomeTiers.Name(i)}): {array[i]} itens"); } string text = Path.Combine(Paths.ConfigPath, "ValheimMMO_tiers.md"); File.WriteAllText(text, stringBuilder.ToString(), Encoding.UTF8); args.Context.AddString("Arquivo: " + text); } internal void b__0_14(ConsoleEventArgs args) { DumpPrices(args.Context); } internal void b__0_15(ConsoleEventArgs args) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; string error; if ((Object)(object)localPlayer == (Object)null) { args.Context.AddString("Nenhum personagem ativo."); } else if (MerchantSpawner.Spawn(((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 3f, ((Component)localPlayer).transform.position, out error)) { ZoneSystem instance = ZoneSystem.instance; if (instance != null) { instance.SetGlobalKey("mmo_merchant_spawned"); } args.Context.AddString("Mercador spawnado."); } else { args.Context.AddString("Falha: " + error); } } internal void b__0_16(ConsoleEventArgs args) { if ((Object)(object)ZoneSystem.instance == (Object)null) { args.Context.AddString("Entre em um mundo primeiro."); return; } ZoneSystem.instance.RemoveGlobalKey("mmo_merchant_spawned"); MerchantItems.Invalidate(); args.Context.AddString("Marca removida. Va ate as pedras iniciais para ele reaparecer."); } internal void b__0_17(ConsoleEventArgs args) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: 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_0240: 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_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)ZNet.instance == (Object)null) { args.Context.AddString("Entre em um mundo primeiro."); return; } if (!ZNet.instance.IsServer()) { args.Context.AddString("So funciona no host/servidor (a lista de locations vive la)."); return; } Vector3 val = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : Vector3.zero); int result2; if (args.Length >= 3 && args[1].ToLowerInvariant() == "demolish" && int.TryParse(args[2], out var result)) { int num = 0; ZNetView[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (ZNetView val2 in array) { if (val2.IsValid()) { ZDO zDO = val2.GetZDO(); if ((zDO.GetBool("mmo_city_piece", false) || zDO.GetBool("mmo_city_portal", false) || zDO.GetInt("mmo_city", 0) == result || zDO.GetInt("mmo_guard", 0) == result || zDO.GetBool("mmo_city_test", false)) && !(Vector3.Distance(((Component)val2).transform.position, val) > 120f)) { val2.ClaimOwnership(); ZNetScene.instance.Destroy(((Component)val2).gameObject); num++; } } } CitySystem.ResetFounded(result); args.Context.AddString($"{num} objetos removidos; tier {result} liberado. Ela renasce (no anel novo) quando alguem chegar perto do altar."); } else if (args.Length >= 2 && int.TryParse(args[1], out result2)) { CityDef cityDef = CitySystem.ByTier(result2); if (cityDef == null) { args.Context.AddString("Tier invalido (1-7)."); return; } string anchorName; Vector3? val3 = CitySystem.AnchorPositionFor(result2, val, out anchorName); if (!val3.HasValue) { args.Context.AddString("Altar '" + anchorName + "' nao existe neste mundo."); return; } float num2 = Vector3.Distance(val, val3.Value); if (num2 > 60f) { args.Context.AddString($"Voce esta a {num2:0} m do altar '{anchorName}'. O terreno so existe carregado a ate ~60 m; chegue mais perto (ou espere: ela nasce sozinha)."); return; } CitySystem.ResetFounded(result2); string error; bool flag = CitySystem.Found(cityDef, val3.Value, Player.m_localPlayer, out error); args.Context.AddString(flag ? $"{cityDef.Name} fundada em {val3.Value} (altar a {num2:0} m de voce)." : ("Falha ao fundar " + cityDef.Name + ": " + error)); } else { CityDef[] defs = CitySystem.Defs; foreach (CityDef cityDef2 in defs) { string anchorName2; Vector3? val4 = CitySystem.AnchorPositionFor(cityDef2.Tier, val, out anchorName2); string text = ((!val4.HasValue) ? "altar NAO encontrado" : $"altar a {Vector3.Distance(val, val4.Value):0} m"); args.Context.AddString(string.Format("[{0}] {1} <- {2}: {3}; {4}", cityDef2.Tier, cityDef2.Name, anchorName2, text, CitySystem.IsFounded(cityDef2.Tier) ? "FUNDADA" : "nao fundada")); } args.Context.AddString("Fundacao automatica: servidor, quando alguem chega a 100 m do altar. Forcar: mmo_city ."); } } internal void b__0_18(ConsoleEventArgs args) { //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: 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_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0159: 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_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { args.Context.AddString("Entre em um mundo primeiro."); return; } Player localPlayer = Player.m_localPlayer; string text = ((args.Length >= 2) ? args[1].ToLowerInvariant() : "help"); int result = 1; switch (text) { case "reload": args.Context.AddString("Plano: " + CityBuilder.Reload()); break; case "export": args.Context.AddString(CityBuilder.ExportBuiltin() + " -- edite e use mmo_cityplan reload"); break; case "build": { if (args.Length < 3) { args.Context.AddString("mmo_cityplan build