Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of GlitnirRanking v0.9.71
GlitnirRanking.dll
Decompiled 11 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Buffers; using System.Buffers.Binary; using System.Buffers.Text; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.Tracing; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Net; using System.Numerics; using System.Numerics.Hashing; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security; using System.Security.AccessControl; using System.Security.Cryptography; using System.Security.Permissions; using System.Security.Principal; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FxResources.System.Buffers; using FxResources.System.Memory; using Glitnir.Ranking.Patches; using HarmonyLib; using JetBrains.Annotations; using LiteDB; using LiteDB.Engine; using LiteDB.Utils; using LiteDB.Utils.Extensions; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: Guid("72b7e2b4-c4d2-4b2f-91db-99d0311d97bc")] [assembly: ComVisible(false)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyProduct("GlitnirRanking")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyDescription("")] [assembly: AssemblyTitle("GlitnirRanking")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: <f9d3fb25-9f77-4562-8d1a-2929ece7ad42>RefSafetyRules(11)] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [<6bddd229-4dc5-4486-9a3f-6f793cdb59ca>Embedded] internal sealed class <6bddd229-4dc5-4486-9a3f-6f793cdb59ca>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [<6bddd229-4dc5-4486-9a3f-6f793cdb59ca>Embedded] [CompilerGenerated] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class <f9d3fb25-9f77-4562-8d1a-2929ece7ad42>RefSafetyRulesAttribute : Attribute { public readonly int Version; public <f9d3fb25-9f77-4562-8d1a-2929ece7ad42>RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Glitnir.Ranking { [HarmonyPatch] internal static class CraftRankingPatches { [HarmonyTargetMethods] private static IEnumerable<MethodBase> TargetMethods() { Type type = AccessTools.TypeByName("InventoryGui"); if (type == null) { yield break; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (string.Equals(methodInfo.Name, "DoCrafting", StringComparison.OrdinalIgnoreCase)) { yield return methodInfo; } } } [HarmonyPostfix] private static void Postfix(object __instance, object[] __args) { try { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { Recipe val = ExtractRecipe(__args); if ((Object)(object)val == (Object)null && __instance != null) { val = ExtractRecipeFromInventoryGui(__instance); } if (!((Object)(object)val == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalCraftingStarted(val); } } } catch (Exception ex) { GlitnirRankingPlugin.Log.LogError((object)("Erro no patch de craft: " + ex)); } } private static Recipe ExtractRecipe(object[] args) { if (args == null) { return null; } foreach (object obj in args) { Recipe val = (Recipe)((obj is Recipe) ? obj : null); if ((Object)(object)val != (Object)null) { return val; } } return null; } private static Recipe ExtractRecipeFromInventoryGui(object inventoryGui) { try { FieldInfo field = inventoryGui.GetType().GetField("m_craftRecipe", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object? value = field.GetValue(inventoryGui); return (Recipe)((value is Recipe) ? value : null); } } catch { } return null; } } [HarmonyPatch] internal static class FarmHarvestRankingPatches { [HarmonyTargetMethods] private static IEnumerable<MethodBase> TargetMethods() { Type type = AccessTools.TypeByName("Pickable"); if (type == null) { yield break; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (string.Equals(methodInfo.Name, "Interact", StringComparison.OrdinalIgnoreCase)) { yield return methodInfo; } } } [HarmonyPostfix] private static void Postfix(object __instance, object[] __args, object __result) { try { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && __instance != null && (!(__result is bool) || (bool)__result)) { Player val = ExtractPlayer(__args); if ((!((Object)(object)val != (Object)null) || !((Object)(object)val != (Object)(object)Player.m_localPlayer)) && TryExtractPickableDrop(__instance, out var prefabName, out var amount)) { GlitnirRankingPlugin.Instance.NotifyLocalFarmHarvest(prefabName, amount); } } } catch (Exception ex) { GlitnirRankingPlugin.Log.LogError((object)("Erro no patch de colheita: " + ex)); } } private static Player ExtractPlayer(object[] args) { if (args == null) { return null; } for (int i = 0; i < args.Length; i++) { object obj = args[i]; Player val = (Player)((obj is Player) ? obj : null); if ((Object)(object)val != (Object)null) { return val; } object obj2 = args[i]; Humanoid val2 = (Humanoid)((obj2 is Humanoid) ? obj2 : null); if (val2 is Player) { return (Player)(object)((val2 is Player) ? val2 : null); } } return null; } private static bool TryExtractPickableDrop(object pickable, out string prefabName, out int amount) { prefabName = ""; amount = 1; try { Type type = pickable.GetType(); FieldInfo field = type.GetField("m_itemPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); GameObject val = (GameObject)((field != null) ? /*isinst with value type is only supported in some contexts*/: null); if ((Object)(object)val == (Object)null) { return false; } prefabName = ((Object)val).name.Replace("(Clone)", "").Trim(); FieldInfo field2 = type.GetField("m_amount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 != null && field2.GetValue(pickable) is int) { amount = Mathf.Max(1, (int)field2.GetValue(pickable)); } return !string.IsNullOrWhiteSpace(prefabName); } catch { return false; } } } public class FishingCatchCreditDocument { public string PlayerName { get; set; } public string FishPrefab { get; set; } [BsonId] public string ZdoKey { get; set; } public string GrantedAtUtc { get; set; } } [Serializable] public class FishingCatchCreditRecord { public string PlayerName = ""; public string FishPrefab = ""; public string ZdoKey = ""; public string GrantedAtUtc = ""; } [HarmonyPatch(typeof(WearNTear), "RPC_Damage")] internal static class FishingChestProtectionPatches { [HarmonyPrefix] private static bool WearNTearRpcDamagePrefix(WearNTear __instance, long sender) { try { if ((Object)(object)__instance == (Object)null || !TryFindContainer(__instance, out var container) || !ContainsFish(container)) { return true; } GlitnirRankingPlugin.Instance?.NotifyFishChestBreakBlocked(sender); ManualLogSource log = GlitnirRankingPlugin.Log; if (log != null) { log.LogInfo((object)"[Ranking] Dano bloqueado: bau contem peixe e precisa ser esvaziado antes de ser destruido."); } return false; } catch (Exception ex) { ManualLogSource log2 = GlitnirRankingPlugin.Log; if (log2 != null) { log2.LogWarning((object)("[Ranking] Falha ao verificar peixe no bau antes do dano: " + ex.Message)); } return true; } } private static bool TryFindContainer(WearNTear wearNTear, out Container container) { container = ((Component)wearNTear).GetComponent<Container>(); if ((Object)(object)container == (Object)null) { container = ((Component)wearNTear).GetComponentInParent<Container>(); } if ((Object)(object)container == (Object)null) { container = ((Component)wearNTear).GetComponentInChildren<Container>(true); } return (Object)(object)container != (Object)null; } internal static bool ContainsFish(Container container) { Inventory inventory = container.GetInventory(); if (inventory == null) { return false; } foreach (ItemData allItem in inventory.GetAllItems()) { if (FishingDropBlockPatches.IsFishItem(allItem)) { return true; } } return false; } } [HarmonyPatch(typeof(Piece), "CanBeRemoved")] internal static class FishingChestHammerProtectionPatch { private static bool _blockedCurrentRemoveAttempt; internal static void BeginRemoveAttempt() { _blockedCurrentRemoveAttempt = false; } internal static bool ConsumeBlockedRemoveAttempt() { bool blockedCurrentRemoveAttempt = _blockedCurrentRemoveAttempt; _blockedCurrentRemoveAttempt = false; return blockedCurrentRemoveAttempt; } [HarmonyPostfix] private static void PieceCanBeRemovedPostfix(Piece __instance, ref bool __result) { try { if (!((Object)(object)__instance == (Object)null)) { Container componentInChildren = ((Component)__instance).GetComponentInChildren<Container>(true); if (!((Object)(object)componentInChildren == (Object)null) && FishingChestProtectionPatches.ContainsFish(componentInChildren)) { __result = false; _blockedCurrentRemoveAttempt = true; } } } catch (Exception ex) { ManualLogSource log = GlitnirRankingPlugin.Log; if (log != null) { log.LogWarning((object)("[Ranking] Falha ao verificar peixe no bau antes da remocao com martelo: " + ex.Message)); } } } } [HarmonyPatch(typeof(Player), "RemovePiece")] internal static class FishingChestHammerMessagePatch { [HarmonyPrefix] private static void PlayerRemovePiecePrefix() { FishingChestHammerProtectionPatch.BeginRemoveAttempt(); } [HarmonyPostfix] private static void PlayerRemovePiecePostfix() { if (FishingChestHammerProtectionPatch.ConsumeBlockedRemoveAttempt()) { GlitnirRankingPlugin.Instance?.ShowFishingProtectionMessage("Sjar está furioso: baú não é aquário!"); } } } [HarmonyPatch] internal static class FishingDropBlockPatches { [HarmonyPrepare] private static bool Prepare() { try { if (AccessTools.Method(typeof(Player), "DropItem", (Type[])null, (Type[])null) == null) { GlitnirRankingPlugin.Log.LogWarning((object)"[Ranking] Bloqueio de drop de peixe nao aplicado: Player.DropItem nao encontrado."); return false; } return true; } catch { return false; } } [HarmonyTargetMethods] private static IEnumerable<MethodBase> TargetMethods() { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "DropItem", (Type[])null, (Type[])null); if (methodInfo != null) { yield return methodInfo; } } [HarmonyPrefix] private static bool Prefix(object[] __args) { try { if (!IsFishItem(TryExtractItemData(__args))) { return true; } ShowBlockedDropMessage(); GlitnirRankingPlugin.Log.LogInfo((object)"[Ranking] Drop de peixe bloqueado para evitar farm infinito de pontos."); return false; } catch (Exception ex) { GlitnirRankingPlugin.Log.LogWarning((object)("[Ranking] Erro ao validar bloqueio de drop de peixe: " + ex.Message)); return true; } } private static ItemData TryExtractItemData(object[] args) { if (args == null) { return null; } foreach (object obj in args) { ItemData val = (ItemData)((obj is ItemData) ? obj : null); if (val != null) { return val; } } return null; } internal static bool IsFishItem(ItemData item) { if (item == null) { return false; } try { GameObject dropPrefab = item.m_dropPrefab; if ((Object)(object)dropPrefab != (Object)null && GetCleanName(((Object)dropPrefab).name).StartsWith("Fish", StringComparison.OrdinalIgnoreCase)) { return true; } } catch { } try { if (item.m_shared != null) { string text = (item.m_shared.m_name ?? "").ToLowerInvariant(); if (text.Contains("fish")) { return true; } if (text.Contains("$item_fish")) { return true; } } } catch { } return false; } private static string GetCleanName(string rawName) { if (string.IsNullOrWhiteSpace(rawName)) { return ""; } string text = rawName; int num = text.IndexOf('('); if (num > 0) { text = text.Substring(0, num); } return text.Trim(); } private static void ShowBlockedDropMessage() { try { if ((Object)(object)GlitnirRankingPlugin.Instance != (Object)null) { GlitnirRankingPlugin.Instance.ShowFishingProtectionMessage("Necromancia aquática detectada! Sjar está decepcionado."); } } catch { } } } [HarmonyPatch] internal static class FishingRankingPatches { [HarmonyPrepare] private static bool Prepare() { try { Type type = AccessTools.TypeByName("FishingFloat"); if (type == null) { GlitnirRankingPlugin.Log.LogWarning((object)"[Ranking] Patch de pesca não aplicado: classe FishingFloat não encontrada."); return false; } if (AccessTools.Method(type, "SetCatch", (Type[])null, (Type[])null) == null) { GlitnirRankingPlugin.Log.LogWarning((object)"[Ranking] Patch de pesca não aplicado: FishingFloat.SetCatch não encontrado."); return false; } return true; } catch { return false; } } [HarmonyTargetMethods] private static IEnumerable<MethodBase> TargetMethods() { Type type = AccessTools.TypeByName("FishingFloat"); if (!(type == null)) { MethodInfo methodInfo = AccessTools.Method(type, "SetCatch", (Type[])null, (Type[])null); if (methodInfo != null) { yield return methodInfo; } } } [HarmonyPostfix] private static void Postfix(MethodBase __originalMethod, object __instance, object[] __args) { try { if (!((Object)(object)GlitnirRankingPlugin.Instance == (Object)null)) { if (__originalMethod != null) { _ = __originalMethod.Name; } GameObject val = TryExtractFishGameObject(__instance, __args); if (!((Object)(object)val == (Object)null)) { GlitnirRankingPlugin.Instance.NotifyLocalFishCaught(val.transform, Player.m_localPlayer); } } } catch (Exception ex) { GlitnirRankingPlugin.Log.LogError((object)("Erro no patch FishingFloat de pesca: " + ex)); } } private static GameObject TryExtractFishGameObject(object instance, object[] args) { try { GameObject val = FindGameObjectRecursive(args, 0); if ((Object)(object)val != (Object)null) { return val; } Component val2 = (Component)((instance is Component) ? instance : null); if ((Object)(object)val2 == (Object)null) { return null; } GameObject val3 = FindGameObjectInFields(val2, 0); if ((Object)(object)val3 != (Object)null) { return val3; } Transform transform = val2.transform; if ((Object)(object)transform != (Object)null) { for (int i = 0; i < transform.childCount; i++) { Transform child = transform.GetChild(i); if (!((Object)(object)child == (Object)null) && IsFishObject(((Component)child).gameObject)) { return ((Component)child).gameObject; } } } } catch { } return null; } private static GameObject FindGameObjectRecursive(object value, int depth) { if (value == null || depth > 2) { return null; } if (value is object[] array) { for (int i = 0; i < array.Length; i++) { GameObject val = FindGameObjectRecursive(array[i], depth + 1); if ((Object)(object)val != (Object)null) { return val; } } return null; } GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val2 != (Object)null && IsFishObject(val2)) { return val2; } Component val3 = (Component)((value is Component) ? value : null); if ((Object)(object)val3 != (Object)null && (Object)(object)val3.gameObject != (Object)null && IsFishObject(val3.gameObject)) { return val3.gameObject; } return null; } private static GameObject FindGameObjectInFields(Component component, int depth) { if ((Object)(object)component == (Object)null || depth > 2) { return null; } FieldInfo[] fields = ((object)component).GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo == null) { continue; } string text = fieldInfo.Name ?? ""; if (text.IndexOf("fish", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("catch", StringComparison.OrdinalIgnoreCase) >= 0) { object value = null; try { value = fieldInfo.GetValue(component); } catch { } GameObject val = FindGameObjectRecursive(value, depth + 1); if ((Object)(object)val != (Object)null) { return val; } } } return null; } private static bool IsFishObject(GameObject go) { if ((Object)(object)go == (Object)null) { return false; } if ((((Object)go).name ?? "").StartsWith("Fish", StringComparison.OrdinalIgnoreCase)) { return true; } try { ZNetView component = go.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.GetZDO() != null) { string text = (((Object)(object)GlitnirRankingPlugin.Instance != (Object)null) ? GlitnirRankingPlugin.Instance.GetPrefabName(go) : ""); return !string.IsNullOrWhiteSpace(text) && text.StartsWith("Fish", StringComparison.OrdinalIgnoreCase); } } catch { } return false; } } [HarmonyPatch(typeof(InventoryGui))] public static class GlitnirRankingInventoryGuiPatch { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AwakePostfix(InventoryGui __instance) { GlitnirRankingPlugin.Instance?.EnsureInventoryRankingGui(__instance); } [HarmonyPatch("Update")] [HarmonyPostfix] private static void UpdatePostfix(InventoryGui __instance) { GlitnirRankingPlugin.Instance?.UpdateInventoryRankingGui(__instance); } } [HarmonyPatch(typeof(PlayerController), "TakeInput")] internal static class GlitnirRankingPlayerControllerInputPatch { private static bool Prefix(ref bool __result) { if ((Object)(object)GlitnirRankingPlugin.Instance != (Object)null && GlitnirRankingPlugin.Instance.ShouldRankingBlockGameInput()) { __result = false; return false; } return true; } } [Serializable] [HarmonyPatch(typeof(Player), "OnDeath")] public static class GlitnirRankingPlayerDeathPatch { private static float _lastLocalDeathReportAt; private static void Postfix(Player __instance) { try { if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(Time.realtimeSinceStartup - _lastLocalDeathReportAt < 2f)) { _lastLocalDeathReportAt = Time.realtimeSinceStartup; GlitnirRankingPlugin.Instance?.ReportLocalPlayerDeathToServer(); } } catch (Exception ex) { ManualLogSource log = GlitnirRankingPlugin.Log; if (log != null) { log.LogWarning((object)("[Ranking] Falha ao reportar morte do jogador: " + ex.Message)); } } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.glitnir.ranking", "Glitnir Ranking", "0.7.89")] public class GlitnirRankingPlugin : BaseUnityPlugin { private enum DebugCategory { General, Hit, Kill, Skill, PendingKill, Snapshot, Points } private sealed class SnapshotTopEntryData { public int Position; public string PlayerName = ""; public int Points; public bool IsLocalPlayer; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; } private sealed class SnapshotPlayerData { public bool HasData; public int Position; public string PlayerName = ""; public int Points; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; public bool RankingEnabled; public bool EnableKillPoints; public bool EnableBossPoints; public bool EnableSkillPoints; public bool EnableMarketplaceQuestPoints; public int TopCount; public bool RewardClaimsEnabled; public bool RewardCanClaim; public bool RewardAlreadyClaimed; public int RewardRank; public int RewardMinPoints; public string RewardLabel = ""; public string RewardPrefabName = ""; public int RewardAmount; public string RewardBlockReason = ""; public string RewardClaimCycleId = ""; public string HudKillRules = ""; public string HudBossRules = ""; public string HudSkillJackpotRules = ""; public string HudMarketplaceQuestRules = ""; public string HudFishingRules = ""; public string HudCraftRules = ""; public string HudFarmJackpotRules = ""; public string HudUniqueCraftJackpotRules = ""; public string HudDeathPenaltyRules = ""; public bool EnableDeathPenalty; public bool DeathPenaltyUseMultiplier; public int DeathPenaltyPerDeath; public string HudExplorationMapJackpotRules = ""; public Dictionary<string, int> ProgressCounters = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); public Dictionary<string, float> FloatProgressCounters = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); } private sealed class RankRewardInfo { public int Rank; public int MinPoints; public string Label = ""; public string PrefabName = ""; public int Amount; } private sealed class InventoryGuideRuleGroup { public readonly string Name; public readonly List<string> Rows = new List<string>(); public InventoryGuideRuleGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Todos" : name); } } private struct RectOffsetSpec { public float Left; public float Bottom; public float Right; public float Top; } private sealed class RuleGuideGroup { public readonly string Name; public readonly List<string> Rows = new List<string>(); public RuleGuideGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Outros" : name); } } private class SkillGuideGroup { public string Name; public List<SkillGuideMilestone> Milestones; public SkillGuideGroup(string name) { Name = (string.IsNullOrWhiteSpace(name) ? "Habilidade" : name); Milestones = new List<SkillGuideMilestone>(); } } private class SkillGuideMilestone { public string Level; public string Points; public SkillGuideMilestone(string level, string points) { Level = (string.IsNullOrWhiteSpace(level) ? "?" : level); Points = (string.IsNullOrWhiteSpace(points) ? "0" : points); } } private struct UnityGuideCategory { public readonly string Key; public readonly string Label; public readonly string Title; public readonly string Description; public UnityGuideCategory(string key, string label, string title, string description) { Key = key; Label = label; Title = title; Description = description; } } private struct UnityGuideRuleLine { public readonly string Action; public readonly string Points; public readonly string Category; public readonly string RawKey; public UnityGuideRuleLine(string action, string points, string category = "", string rawKey = "") { Action = action ?? ""; Points = points ?? ""; Category = category ?? ""; RawKey = rawKey ?? action ?? ""; } } private sealed class GlitnirButtonFx : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler { private RectTransform _rect; private GameObject _hoverGlow; private GameObject _selectedGlow; private Vector3 _baseScale = Vector3.one; private Vector3 _targetScale = Vector3.one; private bool _hovered; private bool _pressed; private bool _selected; private void Awake() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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_0029: Unknown result type (might be due to invalid IL or missing references) ref RectTransform rect = ref _rect; Transform transform = ((Component)this).transform; rect = (RectTransform)(object)((transform is RectTransform) ? transform : null); _baseScale = ((Component)this).transform.localScale; _targetScale = _baseScale; Transform val = ((Component)this).transform.Find("HoverGlow"); Transform val2 = ((Component)this).transform.Find("SelectedGlow"); _hoverGlow = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null); _selectedGlow = (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : null); RefreshGlow(); } private void OnEnable() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) _pressed = false; _hovered = false; _targetScale = _baseScale; RefreshGlow(); } private void Update() { //IL_001a: 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_0030: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_rect == (Object)null)) { ((Transform)_rect).localScale = Vector3.Lerp(((Transform)_rect).localScale, _targetScale, Time.unscaledDeltaTime * 14f); } } public void SetSelected(bool selected) { _selected = selected; RefreshGlow(); } public void OnPointerEnter(PointerEventData eventData) { //IL_0009: 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_0018: Unknown result type (might be due to invalid IL or missing references) _hovered = true; _targetScale = _baseScale * 1.035f; RefreshGlow(); } public void OnPointerExit(PointerEventData eventData) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) _hovered = false; _pressed = false; _targetScale = _baseScale; RefreshGlow(); } public void OnPointerDown(PointerEventData eventData) { //IL_0009: 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_0018: Unknown result type (might be due to invalid IL or missing references) _pressed = true; _targetScale = _baseScale * 0.975f; RefreshGlow(); } public void OnPointerUp(PointerEventData eventData) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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_0028: Unknown result type (might be due to invalid IL or missing references) _pressed = false; _targetScale = (_hovered ? (_baseScale * 1.035f) : _baseScale); RefreshGlow(); } private void RefreshGlow() { if ((Object)(object)_hoverGlow != (Object)null) { _hoverGlow.SetActive(_hovered || _pressed); } if ((Object)(object)_selectedGlow != (Object)null) { _selectedGlow.SetActive(_selected); } } } private struct RectSpec { public Vector2 AnchorMin; public Vector2 AnchorMax; public Vector2 Pivot; public Vector2 OffsetMin; public Vector2 OffsetMax; public Vector2 AnchoredPosition; public Vector2 SizeDelta; public bool UseOffsets; } private readonly Dictionary<string, ConfigEntry<string>> _cfgProductionCategoryRuleEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgUniqueCraftJackpotCategoryEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); public const string ModGuid = "com.glitnir.ranking"; public const string ModName = "Glitnir Ranking"; public const string ModVersion = "0.7.89"; internal static GlitnirRankingPlugin Instance; internal static ManualLogSource Log; private const string RpcRequestSnapshot = "glitnir.ranking.requestsnapshot"; private const string RpcReceiveSnapshot = "glitnir.ranking.receivesnapshot"; private const string RpcReportKill = "glitnir.ranking.reportkill"; private const string RpcAuthoritativeKillCredit = "glitnir.ranking.killcredit"; private const string RpcReportSkillGain = "glitnir.ranking.reportskillgain"; private const string RpcRequestRewardClaim = "glitnir.ranking.requestrewardclaim"; private const string RpcGrantRewardItem = "glitnir.ranking.grantrewarditem"; private const string RpcFinalizeRewardClaim = "glitnir.ranking.finalizerewardclaim"; private const string RpcRewardClaimFeedback = "glitnir.ranking.rewardclaimfeedback"; private const string RpcRequestPointsExchange = "glitnir.ranking.requestpointsexchange"; private const string RpcGrantExchangeCoins = "glitnir.ranking.grantexchangecoins"; private const string RpcFinalizePointsExchange = "glitnir.ranking.finalizepointsexchange"; private const string RpcPointsExchangeFeedback = "glitnir.ranking.pointsexchangefeedback"; private const string RpcReportMarketplaceQuestComplete = "glitnir.ranking.marketquestcomplete"; private const string RpcReportFishCaught = "glitnir.ranking.reportfishcaught"; private const string RpcFishChestBreakBlocked = "glitnir.ranking.fishchestblocked"; private const string RpcReportCraftedItem = "glitnir.ranking.reportcrafteditem"; private const string RpcReportFarmHarvest = "glitnir.ranking.reportfarmharvest"; private const string RpcReportPlayerDeath = "glitnir.ranking.reportplayerdeath"; private const string RpcReportExplorationMap = "glitnir.ranking.reportexplorationmap"; private const float DamageCreditLifetimeSeconds = 180f; private const float ProcessedKillLifetimeSeconds = 180f; private const float ProcessedKillCleanupIntervalSeconds = 15f; private const float LocalRecentHitLifetimeSeconds = 15f; private const float AdminIdentifierRefreshIntervalSeconds = 30f; private const float ExplorationReportIntervalSeconds = 180f; private const double DatabaseSaveIntervalSeconds = 30.0; private const int MaxHudChars = 3000; private const int MaxPlayerNameLength = 32; private const int MaxReasonLength = 64; private const float ClientRefreshInterval = 300f; private readonly Dictionary<string, string> _hudPrefabDisplayNameCache = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, GameObject> _hudPrefabObjectCache = new Dictionary<string, GameObject>(StringComparer.OrdinalIgnoreCase); private int _hudObjectDbItemCountCached = -1; private int _hudZNetScenePrefabCountCached = -1; private Type _hudLocalizationType; private FieldInfo _hudLocalizationInstanceField; private PropertyInfo _hudLocalizationInstanceProperty; private MethodInfo _hudLocalizationLocalizeMethod; private bool _hudLocalizationReflectionReady; private FieldInfo _minimapExploredField; private Harmony _harmony; private bool _rpcsRegistered; private ZRoutedRpc _registeredRoutedRpcInstance; private readonly object _databaseSaveLock = new object(); private bool _databaseSavePending; private bool _serverDatabaseInitialized; private ZNet _databaseServerInstance; private DateTime _nextDatabaseSaveUtc = DateTime.MinValue; private float _nextProcessedKillCleanupAt; private string _rulesFilePath; private string _uiFolderPath; private string _uiFilePath; private string _databaseFilePath; private string _legacyDatabaseFilePath; private ConfigFile _rulesConfig; private FileSystemWatcher _rulesConfigWatcher; private ConfigEntry<bool> _cfgLockConfiguration; private bool _rulesConfigEventsBound; private bool _rulesConfigReloadQueued; private float _rulesConfigReloadAt; private float _rulesConfigApplyAt; private const float RulesConfigApplyInterval = 1f; private ConfigEntry<bool> _cfgRankingEnabled; private ConfigEntry<bool> _cfgEnableKillPoints; private ConfigEntry<bool> _cfgEnableBossPoints; private ConfigEntry<int> _cfgDefaultKillPoints; private ConfigEntry<int> _cfgTopCount; private ConfigEntry<bool> _cfgAllowRepeatedBossPoints; private ConfigEntry<bool> _cfgDebugLogging; private ConfigEntry<bool> _cfgLogHitReports; private ConfigEntry<bool> _cfgLogKillReports; private ConfigEntry<bool> _cfgLogSkillReports; private ConfigEntry<bool> _cfgLogPendingKillReports; private ConfigEntry<bool> _cfgLogSnapshotRequests; private ConfigEntry<bool> _cfgLogPointsChanges; private ConfigEntry<bool> _cfgEnableSkillPoints; private ConfigEntry<bool> _cfgIgnoreTamedKills; private ConfigEntry<bool> _cfgEnableMarketplaceQuestPoints; private ConfigEntry<bool> _cfgEnableFishingPoints; private ConfigEntry<bool> _cfgEnableDeathPenalty; private ConfigEntry<bool> _cfgEnableExplorationJackpots; private ConfigEntry<string> _cfgExplorationMapJackpotRules; private ConfigEntry<string> _cfgDeathPenaltyRules; private ConfigEntry<bool> _cfgDeathPenaltyUseMultiplier; private ConfigEntry<int> _cfgDeathPenaltyPerDeath; private readonly Dictionary<string, ConfigEntry<string>> _cfgCombatBiomeRuleEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private ConfigEntry<string> _cfgProductionCategoryRules; private ConfigEntry<string> _cfgMarketplaceQuestPointMap; private ConfigEntry<string> _cfgFarmJackpotRules; private ConfigEntry<bool> _cfgRewardClaimsEnabled; private ConfigEntry<string> _cfgRewardClaimCycleId; private ConfigEntry<int> _cfgRewardTop1MinPoints; private ConfigEntry<string> _cfgRewardTop1Label; private ConfigEntry<string> _cfgRewardTop1Prefab; private ConfigEntry<int> _cfgRewardTop1Amount; private ConfigEntry<int> _cfgRewardTop2MinPoints; private ConfigEntry<string> _cfgRewardTop2Label; private ConfigEntry<string> _cfgRewardTop2Prefab; private ConfigEntry<int> _cfgRewardTop2Amount; private ConfigEntry<int> _cfgRewardTop3MinPoints; private ConfigEntry<string> _cfgRewardTop3Label; private ConfigEntry<string> _cfgRewardTop3Prefab; private ConfigEntry<int> _cfgRewardTop3Amount; private ConfigEntry<bool> _cfgPointsExchangeEnabled; private ConfigEntry<string> _cfgPointsExchangePrefab; private ConfigEntry<bool> _cfgPointsExchangeUseCoinsPerPoint; private ConfigEntry<int> _cfgPointsExchangeCoinsPerPoint; private ConfigEntry<bool> _cfgPointsExchangeUsePointsPerCoin; private ConfigEntry<int> _cfgPointsExchangePointsPerCoin; private ConfigEntry<int> _cfgPointsExchangeMinPoints; private ConfigEntry<int> _cfgPointsExchangeMaxPointsPerRequest; private ConfigEntry<bool> _cfgDiscordTop3WebhookEnabled; private ConfigEntry<string> _cfgDiscordTop3WebhookUrl; private ConfigEntry<bool> _cfgIgnoreAdminsInRanking; private ConfigEntry<string> _cfgIgnoredAdminNames; private readonly Dictionary<string, ConfigEntry<int>> _cfgKillPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<int>> _cfgBossPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<int>> _cfgFishingPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<int>> _cfgCraftPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgFarmJackpotEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgUniqueCraftJackpotEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgSkillMilestoneJackpotPointEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private RankingDatabase _database = new RankingDatabase(); private RankingRules _rules = new RankingRules(); private readonly Dictionary<int, MarketplaceQuestPointRule> _marketplaceQuestPointsByUid = new Dictionary<int, MarketplaceQuestPointRule>(); private readonly Dictionary<string, Dictionary<string, float>> _damageCredits = new Dictionary<string, Dictionary<string, float>>(StringComparer.Ordinal); private readonly Dictionary<string, float> _processedKills = new Dictionary<string, float>(StringComparer.Ordinal); private readonly Dictionary<string, float> _ignoredTamedKills = new Dictionary<string, float>(StringComparer.Ordinal); private readonly Dictionary<string, float> _localRecentHits = new Dictionary<string, float>(StringComparer.Ordinal); private HashSet<string> _cachedAdminIdentifiers = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private float _nextAdminIdentifierRefreshAt; private bool _adminIdentifiersCacheReady; private bool _hudVisible; private float _hudOpenTime; private Rect _windowRect = new Rect(660f, 56f, 548f, 804f); private Rect _iconRect = new Rect(36f, 596f, 50f, 50f); private GameObject _rankingInputBlockerObject; private Canvas _rankingInputBlockerCanvas; private RectTransform _rankingInputBlockerRect; private Vector2 _rankingScrollPosition = Vector2.zero; private Vector2 _playerInfoScrollPosition = Vector2.zero; private Vector2 _rulesInfoScrollPosition = Vector2.zero; private Vector2 _actionsScrollPosition = Vector2.zero; private readonly HashSet<string> _expandedActionPlayers = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private int _hudTabIndex; private float _hudTabSwitchTime; private readonly HashSet<string> _expandedRuleCategories = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _expandedGuideSkillRows = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _expandedRuleSubCategories = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool _ruleCategoriesInitialized; private readonly HashSet<string> _expandedPerformanceCategories = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool _performanceCategoriesInitialized; private float _clientRefreshTimer; private float _lastSnapshotRequestTime = -9999f; private float _explorationReportTimer; private float _lastReportedMapExplorePercent = -1f; private long _lastKnownServerPeerUid; private bool _requestedInitialServerSnapshot; private string _lastSnapshotSyncPlayerName = ""; private bool _lastSnapshotHadLocalPlayer; private string _cachedTopText = "Carregando ranking..."; private string _cachedPlayerText = "Aguardando dados do servidor..."; private string _statusText = "Sincronizando..."; private float _statusUntil; private float _rankingHudTransparentUntil; private bool _rewardClaimRequestPending; private string _rewardClaimPendingCycleId = ""; private int _rewardClaimPendingRank; private readonly HashSet<string> _pendingRewardClaims = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _pendingPointsExchanges = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private bool _pointsExchangeRequestPending; private string _pointsExchangeAmountInput = ""; private bool _iconDragging; private Vector2 _iconDragOffset = Vector2.zero; private Vector2 _iconMouseDownPosition = Vector2.zero; private bool _iconMovedDuringDrag; private bool _rankingHudToggleQueued; private readonly List<SnapshotTopEntryData> _cachedTopEntries = new List<SnapshotTopEntryData>(); private SnapshotPlayerData _cachedPlayerData = new SnapshotPlayerData(); private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _sectionTitleStyle; private GUIStyle _rankingTextStyle; private GUIStyle _playerTextStyle; private GUIStyle _footerStyle; private GUIStyle _statusStyle; private GUIStyle _iconButtonStyle; private GUIStyle _panelHeadingStyle; private GUIStyle _cardLabelStyle; private GUIStyle _cardValueStyle; private GUIStyle _bodyRowStyle; private GUIStyle _bodyValueStyle; private GUIStyle _mutedBodyStyle; private GUIStyle _rankIndexStyle; private GUIStyle _rankNameStyle; private GUIStyle _rankPointsStyle; private GUIStyle _youTagStyle; private GUIStyle _emptyStateStyle; private GUIStyle _configStyle; private bool _stylesReady; private float _uiIconZoom = 0.96f; private Font _uiTitleFont; private Font _uiBodyFont; private Font _uiAccentFont; private Texture2D _uiTransparentTexture; private Texture2D _uiWhiteTexture; private Texture2D _uiPanelTexture; private Texture2D _uiBackgroundTexture; private Texture2D _uiRankingIconTexture; private Texture2D _uiTitleRankingTexture; private Texture2D _uiTitleTopTexture; private Texture2D _uiTitleGuideTexture; private Texture2D _uiTitlePlayerTexture; private Texture2D _uiTitleActionsTexture; private readonly Dictionary<string, Texture2D> _uiRuleCategoryIcons = new Dictionary<string, Texture2D>(StringComparer.OrdinalIgnoreCase); private const float RuleCategoryIconSize = 24f; private Texture2D _uiRank1IconTexture; private Texture2D _uiRank2IconTexture; private Texture2D _uiRank3IconTexture; private readonly HashSet<string> _missingUiTextureWarnings = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private const string DefaultKillPointRules = "Boar:1"; private const string DefaultCombatCategoryRules = "Prados:Boar:1,Deer:1,Neck:1;Floresta Negra:Greydwarf:1,Greydwarf_Elite:2,Greydwarf_Shaman:2,Troll:5;Pântano:Draugr:2,Draugr_Elite:3,Blob:2,Abomination:8;Montanha:Wolf:2,Hatchling:2,StoneGolem:8;Planícies:Goblin:3,GoblinBrute:5,Lox:6,Deathsquito:3;Oceano:Serpent:6;Mistlands:Seeker:4,SeekerBrute:8,Gjall:10;Ashlands:Charred_Melee:4,Charred_Archer:4,Volture:4;Especiais:Haldor:0"; private const string DefaultProductionCategoryRules = "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; private static readonly Dictionary<string, string> ManualPortuguesePrefabNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "deerstew", "Ensopado de cervo" }, { "honeysglazedchicken", "Frango glaceado com mel" }, { "mushroomjotun", "Cogumelo Jotun" }, { "helmetbronze", "Elmo de bronze" }, { "helmetcarapace", "Elmo de carapaça" }, { "helmetdrake", "Elmo de draco" }, { "helmetdverger", "Tiara Dvergr" }, { "helmetfenris", "Capuz de Fenris" }, { "helmetiron", "Elmo de ferro" }, { "helmetleather", "Elmo de couro" }, { "helmetmage", "Capuz de mago" }, { "helmetpadded", "Elmo acolchoado" }, { "helmetroot", "Máscara de raiz" }, { "helmettrollleather", "Capuz de couro de troll" }, { "helmetflametal", "Elmo de flametal" }, { "armorbronzechest", "Peitoral de bronze" }, { "armorbronzelegs", "Perneiras de bronze" }, { "armorcarapacechest", "Peitoral de carapaça" }, { "armorcarapacelegs", "Perneiras de carapaça" }, { "armorfenringchest", "Peitoral de Fenris" }, { "armorfenringlegs", "Perneiras de Fenris" }, { "armorironchest", "Peitoral de ferro" }, { "armorironlegs", "Perneiras de ferro" }, { "armorleatherchest", "Túnica de couro" }, { "armorleatherlegs", "Calças de couro" }, { "armormagechest", "Manto de mago" }, { "armormagelegs", "Calças de mago" }, { "armorpaddedcuirass", "Couraça acolchoada" }, { "armorpaddedgreaves", "Grevas acolchoadas" }, { "armorragschest", "Túnica de trapos" }, { "armorragslegs", "Calças de trapos" }, { "armorrootchest", "Armadura de raiz" }, { "armorrootlegs", "Perneiras de raiz" }, { "armortrollleatherchest", "Túnica de couro de troll" }, { "armortrollleatherlegs", "Calças de couro de troll" }, { "knifeblackmetal", "Faca de metal negro" }, { "knifecopper", "Faca de cobre" }, { "knifeflint", "Faca de sílex" }, { "knifesilver", "Faca de prata" }, { "swordiron", "Espada de ferro" }, { "swordsilver", "Espada de prata" }, { "swordblackmetal", "Espada de metal negro" }, { "swordbronze", "Espada de bronze" }, { "swordmistwalker", "Mistwalker" }, { "swordflametal", "Espada de flametal" }, { "axeiron", "Machado de ferro" }, { "axebronze", "Machado de bronze" }, { "axeblackmetal", "Machado de metal negro" }, { "maceiron", "Maça de ferro" }, { "macebronze", "Maça de bronze" }, { "macesilver", "Frostner" }, { "sledgeiron", "Marreta de ferro" }, { "sledge_demolisher", "Demolidor" }, { "bow", "Arco bruto" }, { "bowfinewood", "Arco de madeira fina" }, { "bowhuntsman", "Arco do caçador" }, { "bowdraugrfang", "Presa de Draugr" }, { "bowspine", "Estilhaçador de espinha" }, { "crossbowarbalest", "Arbalest" }, { "crossbowripper", "Ripper" }, { "shieldwood", "Escudo de madeira" }, { "shieldbronze buckler", "Broquel de bronze" }, { "shieldironbuckler", "Broquel de ferro" }, { "shieldblackmetal", "Escudo de metal negro" }, { "shieldcarapace", "Escudo de carapaça" }, { "shieldflametal", "Escudo de flametal" }, { "atgierbronze", "Atgeir de bronze" }, { "atgieriron", "Atgeir de ferro" }, { "atgierblackmetal", "Atgeir de metal negro" }, { "spearflint", "Lança de sílex" }, { "spearbronze", "Lança de bronze" }, { "spearwolffang", "Lança de presas" }, { "carrot", "Cenoura" }, { "turnip", "Nabo" }, { "onion", "Cebola" }, { "barley", "Cevada" }, { "flax", "Linho" }, { "sap", "Seiva" }, { "jotunpuffs", "Cogumelo Jotun" }, { "magecap", "Chapéu de Mago" }, { "vineberry", "Baga de Videira" } }; private static readonly Dictionary<string, string> BossDisplayNamesPtBr = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Eikthyr", "Eikthyr" }, { "gd_king", "O Ancião" }, { "Bonemass", "Massa Óssea" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "Rainha Seeker" }, { "Fader", "Fader" }, { "Charred_Melee_Dyrnwyn", "Guerreiro Carbonizado Dyrnwyn" }, { "Fenring_Cultist_Hildir", "Cultista Fenring da Hildir" }, { "GoblinBruteBros", "Irmãos Berserker Fuling" }, { "GoblinBrute_Hildir", "Berserker Fuling da Hildir" }, { "GoblinShaman_Hildir", "Xamã Fuling da Hildir" }, { "Skeleton_Hildir", "Esqueleto da Hildir" } }; private static readonly Dictionary<string, string> BossPortugueseToPrefab = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Eikthyr", "Eikthyr" }, { "O Ancião", "gd_king" }, { "Ancião", "gd_king" }, { "O Anciao", "gd_king" }, { "Anciao", "gd_king" }, { "Massa Óssea", "Bonemass" }, { "Massa Ossea", "Bonemass" }, { "Bonemass", "Bonemass" }, { "Moder", "Dragon" }, { "Yagluth", "GoblinKing" }, { "Rainha Seeker", "SeekerQueen" }, { "Rainha dos Seeker", "SeekerQueen" }, { "Seeker Queen", "SeekerQueen" }, { "Fader", "Fader" }, { "Guerreiro Carbonizado Dyrnwyn", "Charred_Melee_Dyrnwyn" }, { "Dyrnwyn", "Charred_Melee_Dyrnwyn" }, { "Cultista Fenring da Hildir", "Fenring_Cultist_Hildir" }, { "Irmãos Berserker Fuling", "GoblinBruteBros" }, { "Irmaos Berserker Fuling", "GoblinBruteBros" }, { "Berserker Fuling da Hildir", "GoblinBrute_Hildir" }, { "Xamã Fuling da Hildir", "GoblinShaman_Hildir" }, { "Xama Fuling da Hildir", "GoblinShaman_Hildir" }, { "Esqueleto da Hildir", "Skeleton_Hildir" } }; private static readonly Dictionary<string, string> FishDisplayNamesPtBr = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Fish1", "Perca" }, { "Fish2", "Lúcio" }, { "Fish3", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" } }; private static readonly Dictionary<string, string> FishPortugueseToPrefab = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Perca", "Fish1" }, { "Lúcio", "Fish2" }, { "Lucio", "Fish2" }, { "Fish3", "Fish3" }, { "Atum", "Fish3" }, { "Tetra", "Fish4_cave" }, { "Peixe Troll", "Fish5" }, { "Arenque gigante", "Fish6" }, { "Garoupa", "Fish7" }, { "Garoupa estrelada", "Fish8" }, { "Peixe-pescador", "Fish9" }, { "Peixe pescador", "Fish9" }, { "Salmão do norte", "Fish10" }, { "Salmao do norte", "Fish10" }, { "Peixe magma", "Fish11" }, { "Baiacu", "Fish12" } }; private static readonly Dictionary<string, string> HudFriendlyNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Boar", "Javali" }, { "Neck", "Nixe" }, { "Greyling", "Greydwarf Jovem" }, { "Greydwarf", "Greydwarf" }, { "Greydwarf_Elite", "Greydwarf Elite" }, { "Greydwarf_Shaman", "Xamã Greydwarf" }, { "Troll", "Troll" }, { "Skeleton", "Esqueleto" }, { "Skeleton_Poison", "Esqueleto Venenoso" }, { "Draugr", "Draugr" }, { "Draugr_Elite", "Draugr Elite" }, { "Blob", "Gosma" }, { "BlobElite", "Gosma Elite" }, { "Leech", "Sanguessuga" }, { "Surtling", "Surtling" }, { "Abomination", "Abominação" }, { "Wolf", "Lobo" }, { "Hatchling", "Draco" }, { "Fenring", "Fenring" }, { "StoneGolem", "Golem de Pedra" }, { "Goblin", "Fuling" }, { "GoblinArcher", "Fuling Arqueiro" }, { "GoblinBrute", "Fuling Berserker" }, { "GoblinShaman", "Xamã Fuling" }, { "Lox", "Lox" }, { "Deathsquito", "Mosquito da Morte" }, { "Serpent", "Serpente Marinha" }, { "Bat", "Morcego" }, { "Ulv", "Ulv" }, { "Hare", "Lebre" }, { "Tick", "Carrapato" }, { "Seeker", "Seeker" }, { "SeekerBrute", "Seeker Brutamontes" }, { "Gjall", "Gjall" }, { "Dverger", "Dvergr" }, { "Charred_Melee", "Carbonizado Guerreiro" }, { "Charred_Archer", "Carbonizado Arqueiro" }, { "Charred_Mage", "Carbonizado Mago" }, { "Morgen", "Morgen" }, { "Asksvin", "Asksvin" }, { "Volture", "Abutre das Cinzas" }, { "Eikthyr", "Eikthyr" }, { "gd_king", "O Ancião" }, { "Bonemass", "Massa Óssea" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "Rainha Seeker" }, { "Fader", "Fader" }, { "Fish1", "Perca" }, { "Fish2", "Lúcio" }, { "Fish3", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" }, { "Carrot", "Cenoura" }, { "Turnip", "Nabo" }, { "Onion", "Cebola" }, { "Barley", "Cevada" }, { "Flax", "Linho" }, { "Sap", "Seiva" }, { "JotunPuffs", "Cogumelo Jotun" }, { "Magecap", "Chapéu de Mago" }, { "Vineberry", "Baga de Videira" }, { "Fiddleheadfern", "Samambaia" }, { "Run", "Corrida" }, { "Jump", "Salto" }, { "Swords", "Espadas" }, { "Knives", "Facas" }, { "Clubs", "Porretes" }, { "Polearms", "Armas de Haste" }, { "Spears", "Lanças" }, { "Blocking", "Bloqueio" }, { "Axes", "Machados" }, { "Bows", "Arcos" }, { "Crossbows", "Bestas" }, { "ElementalMagic", "Magia Elemental" }, { "BloodMagic", "Magia de Sangue" }, { "Fishing", "Pesca" }, { "Cooking", "Culinária" }, { "Deer", "Cervo" }, { "EvilHeart_Forest", "Coração Maligno da Floresta" }, { "Ghost", "Fantasma" }, { "Wraith", "Aparição" }, { "Fenring_Cultist", "Cultista Fenring" }, { "SeekerBrood", "Cria de Seeker" }, { "DvergerMage", "Mago Dvergr" }, { "DvergerMageFire", "Mago Dvergr de Fogo" }, { "DvergerMageIce", "Mago Dvergr de Gelo" }, { "DvergerMageSupport", "Mago Dvergr de Suporte" }, { "DvergerRogue", "Ladino Dvergr" }, { "Charred_Twitcher", "Carbonizado Retorcido" }, { "Charred_Melee_Dyrnwyn", "Guerreiro Carbonizado Dyrnwyn" }, { "GoblinBruteBros", "Irmãos Berserker Fuling" }, { "GoblinBrute_Hildir", "Berserker Fuling da Hildir" }, { "GoblinShaman_Hildir", "Xamã Fuling da Hildir" }, { "Fenring_Cultist_Hildir", "Cultista Fenring da Hildir" }, { "Skeleton_Hildir", "Esqueleto da Hildir" }, { "ExploreBlackForest", "Explorador da Floresta Negra" }, { "ExploreSwamp", "Explorador do Pântano" }, { "ExploreMountain", "Explorador das Montanhas" }, { "ExplorePlains", "Explorador das Planícies" }, { "ExploreMistlands", "Explorador das Terras Nebulosas" }, { "ExploreAshlands", "Explorador das Terras das Cinzas" }, { "Kill100Enemies", "Exterminador" }, { "CraftMaster", "Mestre Artesão" }, { "Mapa 5%", "Mapa 5%" } }; private GameObject _inventoryRankingButton; private GameObject _inventoryRankingPanel; private Text _inventoryRankingTitle; private Text _inventoryRankingBody; private RectTransform _inventoryRankingBodyRect; private ScrollRect _inventoryRankingScrollRect; private GameObject _inventoryGuideSidebar; private GameObject _inventoryGuideSubSidebar; private readonly List<GameObject> _inventoryGuideCategoryButtons = new List<GameObject>(); private readonly List<GameObject> _inventoryGuideSubCategoryButtons = new List<GameObject>(); private int _inventoryGuideCategoryIndex; private string _inventoryGuideSubCategory = ""; private string[] _inventoryGuideVisibleSubCategories = new string[0]; private readonly HashSet<string> _inventoryGuideExpandedBlocks = new HashSet<string>(); private GameObject _inventoryRankingPrimaryAction; private Text _inventoryRankingPrimaryActionLabel; private GameObject _inventoryRankingSecondaryAction; private Text _inventoryRankingSecondaryActionLabel; private GameObject _inventoryExchangeMinusAction; private Text _inventoryExchangeMinusLabel; private GameObject _inventoryExchangePlusAction; private Text _inventoryExchangePlusLabel; private GameObject _inventoryExchangeMaxAction; private Text _inventoryExchangeMaxLabel; private Text _inventoryExchangeAmountLabel; private bool _inventoryRankingOpen; private float _inventoryRankingNextRefresh; private int _inventoryRankingTab; private int _inventoryExchangePoints; private const float ActionPlayerCollapsedHeight = 58f; private const float ActionPlayerExpandedHeight = 738f; private const string HudColorPoints = "#00FF7F"; private const string HudColorDanger = "#FF5555"; private const string HudColorProgress = "#4FC3F7"; private const string HudColorHighlight = "#FFD700"; private const string HudColorMuted = "#D7DCE5"; private const string HudColorName = "#FFFFFF"; private const string HudColorSeparator = "#8F6E38"; private const string UnityHudBundleResourceSuffix = "glitnirrankinghud"; private AssetBundle _unityHudBundle; private GameObject _unityHudCanvas; private readonly Dictionary<string, Transform> _unityHudTransforms = new Dictionary<string, Transform>(StringComparer.OrdinalIgnoreCase); private bool _unityHudLoadAttempted; private bool _unityHudAvailable; private bool _unityHudButtonsBound; private float _unityHudNextRefresh; private int _unityHudTabIndex; private int _unityHudGuideCategoryIndex; private int _unityHudExchangePoints; private int _unityHudLeaderboardScrollIndex; private int _unityHudGuideScrollIndex; private int _unityHudPlayersScrollIndex; private int _unityHudGuideSubCategoryIndex; private InputField _unityHudExchangeInputField; private float _unityHudExchangePendingSince; private GameObject _unityHudInputShield; private GameObject _unityShortcutIconObject; private RawImage _unityShortcutIconImage; private static readonly UnityGuideCategory[] UnityGuideCategories = new UnityGuideCategory[9] { new UnityGuideCategory("COMBAT", "COMBATE", "COMBATE [ATIVO]", "Derrote criaturas configuradas para ganhar pontos."), new UnityGuideCategory("BOSSES", "CHEFES", "CHEFES [ATIVO]", "Vitórias contra chefes e encontros especiais."), new UnityGuideCategory("SKILLS", "HABILID.", "HABILIDADES [ATIVO]", "Marcos de progressao de habilidade."), new UnityGuideCategory("FISHING", "PESCA", "PESCA [ATIVO]", "Peixes e capturas que geram honra."), new UnityGuideCategory("FARMING", "CULTIVO", "CULTIVO [ATIVO]", "Colheitas e jackpots de producao."), new UnityGuideCategory("CRAFTING", "PRODUCAO", "PRODUCAO [ATIVO]", "Itens criados, raridades e bonus unicos."), new UnityGuideCategory("QUESTS", "QUESTS", "QUESTS [ATIVO]", "Missoes do Marketplace que contam para o ranking."), new UnityGuideCategory("EXPLORATION", "EXPLOR.", "EXPLORACAO [ATIVO]", "Revele o mapa para ativar marcos de exploracao."), new UnityGuideCategory("PENALTIES", "PENAL.", "PENALIDADES", "Mortes e ajustes que removem pontos.") }; private Font _runtimeUnityHudFont; private Font _rankingNorseFont; private bool _rankingNorseFontLogged; private float _lastFishingProtectionMessageTime = -9999f; private const float FishingProtectionMessageDuration = 8f; private const float CraftRankingVerifyIntervalSeconds = 0.25f; private const float CraftRankingVerifyTimeoutSeconds = 12f; private readonly Dictionary<string, RankingEntry> _entriesByPlayerName = new Dictionary<string, RankingEntry>(StringComparer.OrdinalIgnoreCase); private List<RankingEntry> _orderedRankingCache = new List<RankingEntry>(); private Dictionary<string, int> _rankingSnapshotCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private bool _rankingCacheDirty = true; private readonly HashSet<string> _marketplaceQuestCreditKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _fishingCatchCreditKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, List<MarketplaceQuestCreditRecord>> _marketplaceQuestCreditsByPlayer = new Dictionary<string, List<MarketplaceQuestCreditRecord>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, List<FishingCatchCreditRecord>> _fishingCreditsByPlayer = new Dictionary<string, List<FishingCatchCreditRecord>>(StringComparer.OrdinalIgnoreCase); private void EnsureRulesFileExists() { try { if (!File.Exists(_rulesFilePath)) { File.WriteAllText(_rulesFilePath, BuildDefaultRulesFileContents(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao criar arquivo de regras: " + ex)); } } private string BuildDefaultRulesFileContents() { StringBuilder stringBuilder = new StringBuilder(16384); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# GLITNIR RANKING - CONFIGURACAO DE PONTUACAO"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# Este arquivo controla as regras do ranking do servidor Glitnir."); stringBuilder.AppendLine("# Prefab = nome interno do item/criatura/colheita no Valheim."); stringBuilder.AppendLine("# Pontos = valor entregue ao jogador quando a regra for validada."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Organizacao principal:"); stringBuilder.AppendLine("# - KillPoints: pontos por criatura normal. Peixes NAO ficam aqui."); stringBuilder.AppendLine("# - FishingPoints: somente pontos por peixe realmente pescado."); stringBuilder.AppendLine("# - BossPoints: pontos por bosses e minibosses."); stringBuilder.AppendLine("# - CraftPoints.Rules: pontos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - FarmJackpots.Rules: jackpots de colheita, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - UniqueCraftJackpots.Rules: jackpots unicos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Nao use virgulas nas listas de producao/jackpot. Use ;"); stringBuilder.AppendLine("# Exemplos:"); stringBuilder.AppendLine("# CraftPoints.Rules=MeadHealthMinor:20;ArrowNeedle:2"); stringBuilder.AppendLine("# FarmJackpots.Rules=Carrot:200:1000"); stringBuilder.AppendLine("# UniqueCraftJackpots.Rules=IronSword:1:800"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine(); stringBuilder.AppendLine("RankingEnabled=true"); stringBuilder.AppendLine("EnableKillPoints=true"); stringBuilder.AppendLine("EnableBossPoints=true"); stringBuilder.AppendLine("DefaultKillPoints=1"); stringBuilder.AppendLine("TopCount=10"); stringBuilder.AppendLine("AllowRepeatedBossPoints=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Logging"); stringBuilder.AppendLine("DebugLogging=false"); stringBuilder.AppendLine("LogHitReports=false"); stringBuilder.AppendLine("LogKillReports=false"); stringBuilder.AppendLine("LogSkillReports=false"); stringBuilder.AppendLine("LogPendingKillReports=false"); stringBuilder.AppendLine("LogSnapshotRequests=false"); stringBuilder.AppendLine("LogPointsChanges=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Skill milestone jackpot points"); stringBuilder.AppendLine("EnableSkillPoints=true"); stringBuilder.AppendLine("IgnoreTamedKills=true"); stringBuilder.AppendLine("EnableMarketplaceQuestPoints=true"); stringBuilder.AppendLine("EnableFishingPoints=true"); stringBuilder.AppendLine("EnableDeathPenalty=true"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Marketplace quest points"); stringBuilder.AppendLine("[MarketplaceQuestPoints]"); stringBuilder.AppendLine("QuestPointMap=ccq_b21:450,ccq_b22:150"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Penalidade por morte"); stringBuilder.AppendLine("[DeathPenalty]"); stringBuilder.AppendLine("# UseMultiplierMode=false usa a tabela Rules abaixo."); stringBuilder.AppendLine("# UseMultiplierMode=true ignora Rules e usa PenaltyPerDeath por morte."); stringBuilder.AppendLine("# Exemplo multiplicador: PenaltyPerDeath=100 e 15 mortes = -1500."); stringBuilder.AppendLine("UseMultiplierMode=false"); stringBuilder.AppendLine("PenaltyPerDeath=100"); stringBuilder.AppendLine("# Formato do modo tabela: quantidadeDeMortes:pontosPerdidos,quantidadeDeMortes:pontosPerdidos"); stringBuilder.AppendLine("# Exemplo: 1:0,2:100,5:300,10:800"); stringBuilder.AppendLine("Rules=1:0,2:100,5:300,10:800"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Reward claims"); stringBuilder.AppendLine("RewardClaimsEnabled=true"); stringBuilder.AppendLine("RewardClaimCycleId=temporada_001"); stringBuilder.AppendLine("RewardTop1MinPoints=300"); stringBuilder.AppendLine("RewardTop1Label=Recompensa do 1 lugar"); stringBuilder.AppendLine("RewardTop1Prefab=Coins"); stringBuilder.AppendLine("RewardTop1Amount=500"); stringBuilder.AppendLine("RewardTop2MinPoints=200"); stringBuilder.AppendLine("RewardTop2Label=Recompensa do 2 lugar"); stringBuilder.AppendLine("RewardTop2Prefab=AmberPearl"); stringBuilder.AppendLine("RewardTop2Amount=10"); stringBuilder.AppendLine("RewardTop3MinPoints=100"); stringBuilder.AppendLine("RewardTop3Label=Recompensa do 3 lugar"); stringBuilder.AppendLine("RewardTop3Prefab=Ruby"); stringBuilder.AppendLine("RewardTop3Amount=5"); stringBuilder.AppendLine(); AppendBossPoints(stringBuilder); AppendKillPoints(stringBuilder); AppendFishingPoints(stringBuilder); AppendSkillMilestoneJackpotPoints(stringBuilder); AppendProductionRankingRules(stringBuilder); return stringBuilder.ToString(); } private void AppendBossPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# BOSS POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Cada boss/miniboss fica em uma chave própria."); sb.AppendLine("BossPoints.Bonemass=2200"); sb.AppendLine("BossPoints.Charred_Melee_Dyrnwyn=40"); sb.AppendLine("BossPoints.Dragon=3000"); sb.AppendLine("BossPoints.Eikthyr=1200"); sb.AppendLine("BossPoints.Fader=10000"); sb.AppendLine("BossPoints.Fenring_Cultist_Hildir=30"); sb.AppendLine("BossPoints.gd_king=4200"); sb.AppendLine("BossPoints.GoblinBruteBros=35"); sb.AppendLine("BossPoints.GoblinBrute_Hildir=18"); sb.AppendLine("BossPoints.GoblinKing=6000"); sb.AppendLine("BossPoints.GoblinShaman_Hildir=18"); sb.AppendLine("BossPoints.SeekerQueen=8600"); sb.AppendLine("BossPoints.Skeleton_Hildir=25"); sb.AppendLine(); } private void AppendPoint(StringBuilder sb, string section, string key, int points) { sb.AppendLine(section + "." + key + "=" + points); } private void AppendKillPoint(StringBuilder sb, string prefabName, int points, string comment = null) { } private void AppendKillPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# KILL POINTS / COMBATE"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por criaturas normais. Peixes nao devem ficar aqui; use [FishingPoints]."); sb.AppendLine("# Formato gerado pelo BepInEx: [KillPoints] Prefab = pontos."); sb.AppendLine("# Mantido no modo performance: o mod NAO envia RPC por hit; pontua apenas kill confirmada."); sb.AppendLine("KillPoints.Boar=1"); sb.AppendLine("KillPoints.Neck=1"); sb.AppendLine("KillPoints.Greyling=1"); sb.AppendLine("KillPoints.Greydwarf=1"); sb.AppendLine("KillPoints.Greydwarf_Elite=2"); sb.AppendLine("KillPoints.Greydwarf_Shaman=2"); sb.AppendLine("KillPoints.Skeleton=1"); sb.AppendLine("KillPoints.Troll=5"); sb.AppendLine("KillPoints.Draugr=2"); sb.AppendLine("KillPoints.Draugr_Elite=3"); sb.AppendLine("KillPoints.Blob=2"); sb.AppendLine("KillPoints.Wolf=2"); sb.AppendLine("KillPoints.Hatchling=2"); sb.AppendLine("KillPoints.StoneGolem=8"); sb.AppendLine("KillPoints.Deathsquito=3"); sb.AppendLine("KillPoints.Goblin=3"); sb.AppendLine("KillPoints.GoblinBrute=5"); sb.AppendLine("KillPoints.GoblinShaman=4"); sb.AppendLine("KillPoints.Lox=6"); sb.AppendLine("KillPoints.Serpent=6"); sb.AppendLine("KillPoints.Seeker=4"); sb.AppendLine("KillPoints.SeekerBrute=8"); sb.AppendLine("KillPoints.Gjall=10"); sb.AppendLine("KillPoints.Charred_Melee=4"); sb.AppendLine("KillPoints.Charred_Archer=4"); sb.AppendLine("KillPoints.Morgen=12"); sb.AppendLine(); } private void AppendFishingPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# FISHING POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# As chaves podem ficar em português; o código converte para o prefab real internamente."); sb.AppendLine("# Mapeamento: Perca=Fish1, Lúcio=Fish2, Fish3=Fish3, Tetra=Fish4_cave, Peixe Troll=Fish5, Arenque gigante=Fish6, Garoupa=Fish7, Garoupa estrelada=Fish8, Peixe-pescador=Fish9, Salmão do norte=Fish10, Peixe magma=Fish11, Baiacu=Fish12"); AppendFishingPoint(sb, "Perca", 1); AppendFishingPoint(sb, "Lúcio", 1); AppendFishingPoint(sb, "Fish3", 2); AppendFishingPoint(sb, "Tetra", 3); AppendFishingPoint(sb, "Peixe Troll", 2); AppendFishingPoint(sb, "Arenque gigante", 3); AppendFishingPoint(sb, "Garoupa", 3000); AppendFishingPoint(sb, "Garoupa estrelada", 4); AppendFishingPoint(sb, "Peixe-pescador", 4); AppendFishingPoint(sb, "Salmão do norte", 5); AppendFishingPoint(sb, "Peixe magma", 5); AppendFishingPoint(sb, "Baiacu", 6); sb.AppendLine(); } private void AppendFishingPoint(StringBuilder sb, string prefabName, int points) { sb.AppendLine("FishingPoints." + prefabName + "=" + points); } private void AppendSkillPoint(StringBuilder sb, string skillKey, int points, string comment = null) { if (!string.IsNullOrWhiteSpace(comment)) { sb.AppendLine(comment); } sb.AppendLine("SkillPoints." + skillKey + "=" + points); } private void AppendSkillMilestoneJackpotPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# SKILL MILESTONE JACKPOT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos únicos por marco de skill. Formato: SkillMilestoneJackpotPoints.Skill=20:pontos,40:pontos,60:pontos,80:pontos,100:pontos"); sb.AppendLine("# Exemplo: Bows 20/40/60/80/100. Cada marco pontua uma única vez por jogador/ciclo."); AppendSkillMilestoneJackpotPoint(sb, "BloodMagic", "20:1500,40:3000,60:5000,80:7500,100:12000"); AppendSkillMilestoneJackpotPoint(sb, "ElementalMagic", "20:1000,40:2000,60:3500,80:5500,100:8000"); AppendSkillMilestoneJackpotPoint(sb, "Bows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Crossbows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Swords", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Axes", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Clubs", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Spears", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Polearms", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Knives", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Blocking", "20:400,40:800,60:1500,80:2500,100:3500"); AppendSkillMilestoneJackpotPoint(sb, "Run", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "Jump", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "WoodCutting", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Pickaxes", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Farming", "20:0,40:0,60:0,80:0,100:0"); sb.AppendLine(); } private void AppendSkillMilestoneJackpotPoint(StringBuilder sb, string skillKey, string milestones) { sb.AppendLine("SkillMilestoneJackpotPoints." + skillKey + "=" + milestones); } private void AppendProductionRankingRules(StringBuilder sb) { sb.AppendLine("# ======================================================================="); sb.AppendLine("# PRODUCAO E JACKPOTS - GLITNIR RANKING"); sb.AppendLine("# ======================================================================="); sb.AppendLine("# Estes blocos usam UMA entrada por sistema para manter o arquivo limpo."); sb.AppendLine("# Nao use virgulas. Separe cada regra com ponto e virgula (;)."); sb.AppendLine("# Formatos:"); sb.AppendLine("# CraftPoints.Rules = Prefab:pontos;OutroPrefab:pontos"); sb.AppendLine("# FarmJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# UniqueCraftJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# Exemplos reais:"); sb.AppendLine("# MeadHealthMinor:20"); sb.AppendLine("# Carrot:200:1000"); sb.AppendLine("# IronSword:1:800"); sb.AppendLine("# ======================================================================="); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# CRAFT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por craft de qualquer prefab configurado."); sb.AppendLine("# Exemplo de item: MeadHealthMinor:20"); sb.AppendLine("CraftPoints.Rules=MeadHealthMinor:20"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# FARM JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot por colheita. Premia uma vez por jogador/ciclo quando bater a quantidade."); sb.AppendLine("# Exemplo de item: Carrot:200:1000"); sb.AppendLine("FarmJackpots.Rules=Carrot:200:1000"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# UNIQUE CRAFT JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot unico por craft. Ideal para armas, armaduras e marcos especiais."); sb.AppendLine("# Exemplo de item: IronSword:1:800"); sb.AppendLine("UniqueCraftJackpots.Rules=IronSword:1:800"); sb.AppendLine(); } private void AppendStringPoint(StringBuilder sb, string section, string key, string value) { sb.AppendLine(section + "." + key + "=" + value); } private void LoadRules() { _rules = new RankingRules(); try { if (!File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (string.IsNullOrWhiteSpace(text) || text.StartsWith("#") || text.StartsWith(";") || text.StartsWith("//")) { continue; } int num = text.IndexOf('='); if (num <= 0) { continue; } string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 1).Trim(); if (text2.Equals("RankingEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RankingEnabled = ParseBool(text3, _rules.RankingEnabled); } else if (text2.Equals("EnableKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableKillPoints = ParseBool(text3, _rules.EnableKillPoints); } else if (text2.Equals("EnableBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableBossPoints = ParseBool(text3, _rules.EnableBossPoints); } else if (text2.Equals("DefaultKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.DefaultKillPoints = ParseInt(text3, _rules.DefaultKillPoints); } else if (text2.Equals("TopCount", StringComparison.OrdinalIgnoreCase)) { _rules.TopCount = Mathf.Clamp(ParseInt(text3, _rules.TopCount), 1, 50); } else if (text2.Equals("AllowRepeatedBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.AllowRepeatedBossPoints = ParseBool(text3, _rules.AllowRepeatedBossPoints); } else if (text2.Equals("DebugLogging", StringComparison.OrdinalIgnoreCase)) { _rules.DebugLogging = ParseBool(text3, _rules.DebugLogging); } else if (text2.Equals("LogHitReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogHitReports = ParseBool(text3, _rules.LogHitReports); } else if (text2.Equals("LogKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogKillReports = ParseBool(text3, _rules.LogKillReports); } else if (text2.Equals("LogPendingKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogPendingKillReports = ParseBool(text3, _rules.LogPendingKillReports); } else if (text2.Equals("LogSnapshotRequests", StringComparison.OrdinalIgnoreCase)) { _rules.LogSnapshotRequests = ParseBool(text3, _rules.LogSnapshotRequests); } else if (text2.Equals("LogPointsChanges", StringComparison.OrdinalIgnoreCase)) { _rules.LogPointsChanges = ParseBool(text3, _rules.LogPointsChanges); } else if (text2.Equals("EnableSkillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableSkillPoints = ParseBool(text3, _rules.EnableSkillPoints); } else if (text2.Equals("LogSkillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogSkillReports = ParseBool(text3, _rules.LogSkillReports); } else if (text2.Equals("EnableFishingPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableFishingPoints = ParseBool(text3, _rules.EnableFishingPoints); } else if (text2.Equals("EnableDeathPenalty", StringComparison.OrdinalIgnoreCase)) { _rules.EnableDeathPenalty = ParseBool(text3, _rules.EnableDeathPenalty); } else if (text2.Equals("DeathPenalty.UseMultiplierMode", StringComparison.OrdinalIgnoreCase) || text2.Equals("DeathPenaltyUseMultiplier", StringComparison.OrdinalIgnoreCase) || text2.Equals("UseMultiplierMode", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyUseMultiplier = ParseBool(text3, _rules.DeathPenaltyUseMultiplier); } else if (text2.Equals("DeathPenalty.PenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text2.Equals("DeathPenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text2.Equals("PenaltyPerDeath", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyPerDeath = Mathf.Max(0, ParseInt(text3, _rules.DeathPenaltyPerDeath)); } else if (text2.Equals("DeathPenalty.Rules", StringComparison.OrdinalIgnoreCase) || text2.Equals("DeathPenaltyRules", StringComparison.OrdinalIgnoreCase) || text2.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyRules = ParseDeathPenaltyRules(text3); } else if (text2.Equals("RewardClaimsEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimsEnabled = ParseBool(text3, _rules.RewardClaimsEnabled); } else if (text2.Equals("RewardClaimCycleId", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimCycleId = SafeLimit(text3, 64); } else if (text2.Equals("RewardTop1MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1MinPoints = Mathf.Max(0, ParseInt(text3, _rules.RewardTop1MinPoints)); } else if (text2.Equals("RewardTop1Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Label = SafeLimit(text3, 96); } else if (text2.Equals("RewardTop1Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Prefab = SafeLimit(text3, 96); } else if (text2.Equals("RewardTop1Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Amount = Mathf.Max(0, ParseInt(text3, _rules.RewardTop1Amount)); } else if (text2.Equals("RewardTop2MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2MinPoints = Mathf.Max(0, ParseInt(text3, _rules.RewardTop2MinPoints)); } else if (text2.Equals("RewardTop2Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Label = SafeLimit(text3, 96); } else if (text2.Equals("RewardTop2Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Prefab = SafeLimit(text3, 96); } else if (text2.Equals("RewardTop2Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Amount = Mathf.Max(0, ParseInt(text3, _rules.RewardTop2Amount)); } else if (text2.Equals("RewardTop3MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3MinPoints = Mathf.Max(0, ParseInt(text3, _rules.RewardTop3MinPoints)); } else if (text2.Equals("RewardTop3Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Label = SafeLimit(text3, 96); } else if (text2.Equals("RewardTop3Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Prefab = SafeLimit(text3, 96); } else if (text2.Equals("RewardTop3Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Amount = Mathf.Max(0, ParseInt(text3, _rules.RewardTop3Amount)); } else if (text2.StartsWith("KillPoints.", StringComparison.OrdinalIgnoreCase)) { string text4 = SafeKey(text2.Substring("KillPoints.".Length)); if (!IsFishPrefab(text4)) { _rules.KillPoints[text4] = ParseInt(text3, 0); } } else if (text2.StartsWith("BossPoints.", StringComparison.OrdinalIgnoreCase)) { _rules.BossPoints[NormalizeBossPrefabName(text2.Substring("BossPoints.".Length))] = ParseInt(text3, 0); } else if (text2.StartsWith("FishingPoints.", StringComparison.OrdinalIgnoreCase)) { string text5 = NormalizeFishPrefabName(text2.Substring("FishingPoints.".Length)); if (IsFishPrefab(text5)) { _rules.FishingPoints[text5] = ParseInt(text3, 0); } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar regras do ranking: " + ex)); } } private bool ParseBool(string raw, bool fallback) { if (!bool.TryParse(raw, out var result)) { return fallback; } return result; } private int ParseInt(string raw, int fallback) { if (!int.TryParse(raw, out var result)) { return fallback; } return result; } private void InitializeSyncedRulesConfig() { try { string filePath = Path.Combine(Paths.ConfigPath, "glitnir.ranking.rules.cfg"); Dictionary<string, string> dictionary = LoadLegacyRulesOverrides(filePath); MergeSectionOverrides(dictionary, LoadSectionRulesOverrides(filePath)); _rulesConfig = ((BaseUnityPlugin)this).Config; _cfgLockConfiguration = _rulesConfig.BindLockingConfig(); BindSyncedRulesConfigEntries(dictionary); BindRulesConfigEvents(); ApplyRulesFromSyncedConfig(); _rulesConfig.Save(); CleanupFishKillPointConfigEntries(); RewriteRulesConfigHeaderAndGroupedSections(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao inicializar config sincronizada do ranking: " + ex)); EnsureRulesFileExists(); LoadRules(); } } private void SetupRulesConfigWatcher() { try { if (!string.IsNullOrWhiteSpace(_rulesFilePath)) { string directoryName = Path.GetDirectoryName(_rulesFilePath); string fileName = Path.GetFileName(_rulesFilePath); if (!string.IsNullOrWhiteSpace(directoryName) && !string.IsNullOrWhiteSpace(fileName)) { _rulesConfigWatcher = new FileSystemWatcher(directoryName, fileName); _rulesConfigWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime; _rulesConfigWatcher.Changed += OnRulesConfigFileChanged; _rulesConfigWatcher.Created += OnRulesConfigFileChanged; _rulesConfigWatcher.Renamed += OnRulesConfigFileChanged; _rulesConfigWatcher.EnableRaisingEvents = true; } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Não foi possível iniciar watcher da config do ranking: " + ex.Message)); } } private void OnRulesConfigFileChanged(object sender, FileSystemEventArgs args) { _rulesConfigReloadQueued = true; _rulesConfigReloadAt = Time.realtimeSinceStartup + 0.5f; } private void ProcessRulesConfigReloadIfNeeded() { if (!_rulesConfigReloadQueued || Time.realtimeSinceStartup < _rulesConfigReloadAt) { return; } _rulesConfigReloadQueued = false; try { ConfigFile rulesConfig = _rulesConfig; if (rulesConfig != null) { rulesConfig.Reload(); } BindSyncedRulesConfigEntries(null); BindRulesConfigEvents(); ApplyRulesFromSyncedConfig(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao recarregar config do ranking: " + ex)); } } private void RefreshRulesFromSyncedConfigIfNeeded() { if (!(Time.realtimeSinceStartup < _rulesConfigApplyAt)) { _rulesConfigApplyAt = Time.realtimeSinceStartup + 1f; ApplyRulesFromSyncedConfig(); } } private void BindRulesConfigEvents() { if (_rulesConfig != null && !_rulesConfigEventsBound) { _rulesConfig.SettingChanged += OnRulesConfigSettingChanged; _rulesConfig.ConfigReloaded += OnRulesConfigReloaded; _rulesConfigEventsBound = true; } } private void OnRulesConfigSettingChanged(object sender, SettingChangedEventArgs args) { QueueRulesConfigApply(); } private void OnRulesConfigReloaded(object sender, EventArgs args) { QueueRulesConfigApply(); } private void QueueRulesConfigApply() { _rulesConfigReloadQueued = false; _rulesConfigApplyAt = Time.realtimeSinceStartup; } private Dictionary<string, string> LoadLegacyRulesOverrides(string filePath) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return dictionary; } bool flag = false; string[] array = File.ReadAllLines(filePath, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.StartsWith("[") && text.EndsWith("]")) { flag = true; break; } } if (flag) { return dictionary; } array = File.ReadAllLines(filePath, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#") || text2.StartsWith(";") || text2.StartsWith("//")) { continue; } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string value = text2.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text3)) { dictionary[text3] = value; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao ler overrides legados da config do ranking: " + ex.Message)); } return dictionary; } private void MergeSectionOverrides(Dictionary<string, string> target, Dictionary<string, string> source) { if (target == null || source == null) { return; } foreach (KeyValuePair<string, string> item in source) { if (!string.IsNullOrWhiteSpace(item.Key)) { target[item.Key] = item.Value; } } } private Dictionary<string, string> LoadSectionRulesOverrides(string filePath) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return dictionary; } string text = string.Empty; HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "BossPoints", "FishingPoints", "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "SkillMilestoneJackpotPoints", "DeathPenalty", "ExplorationMapJackpots" }; string[] array = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text2 in array) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text3) || text3.StartsWith("#") || text3.StartsWith(";") || text3.StartsWith("//")) { continue; } if (text3.StartsWith("[") && text3.EndsWith("]")) { text = SafeKey(text3.Substring(1, text3.Length - 2)); continue; } int num = text3.IndexOf('='); if (num <= 0) { continue; } string text4 = text3.Substring(0, num).Trim(); string value = text3.Substring(num + 1).Trim(); if (string.IsNullOrWhiteSpace(text4)) { continue; } string text5 = text4; if (!text4.Contains(".") && !string.IsNullOrWhiteSpace(text)) { text5 = text + "." + text4; } int num2 = text5.IndexOf('.'); if (num2 > 0) { string text6 = text5.Substring(0, num2).Trim(); string text7 = SafeKey(text5.Substring(num2 + 1)); if (hashSet.Contains(text6) && !string.IsNullOrWhiteSpace(text7) && (!text6.Equals("KillPoints", StringComparison.OrdinalIgnoreCase) || !IsFishPrefab(text7))) { dictionary[text6 + "." + text7] = value; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao ler entradas dinâmicas da config do ranking: " + ex.Message)); } return dictionary; } private bool IsLegacyRulesFileFormat(string filePath) { try { if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath)) { return false; } string[] array = File.ReadAllLines(filePath, Encoding.UTF8); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrWhiteSpace(text) && !text.StartsWith("#") && !text.StartsWith(";") && !text.StartsWith("//")) { return !text.StartsWith("[") || !text.EndsWith("]"); } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Falha ao detectar formato legado da config do ranking: " + ex.Message)); } return false; } private void BindSyncedRulesConfigEntries(Dictionary<string, string> legacyOverrides) { if (_rulesConfig != null) { legacyOverrides = legacyOverrides ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); RankingRules rankingRules = new RankingRules(); _cfgRankingEnabled = _rulesConfig.BindConfig("General", "RankingEnabled", ReadLegacyBool(legacyOverrides, "RankingEnabled", rankingRules.RankingEnabled), "Ativa o sistema de ranking."); _cfgEnableKillPoints = _rulesConfig.BindConfig("General", "EnableKillPoints", ReadLegacyBool(legacyOverrides, "EnableKillPoints", rankingRules.EnableKillPoints), "Ativa a pontuação por criaturas normais."); _cfgEnableBossPoints = _rulesConfig.BindConfig("General", "EnableBossPoints", ReadLegacyBool(legacyOverrides, "EnableBossPoints", rankingRules.EnableBossPoints), "Ativa a pontuação por bosses e minibosses."); _cfgDefaultKillPoints = _rulesConfig.BindConfig("General", "DefaultKillPoints", ReadLegacyInt(legacyOverrides, "DefaultKillPoints", rankingRules.DefaultKillPoints), "Pontos padrão para kills sem regra específica."); _cfgTopCount = _rulesConfig.BindConfig("General", "TopCount", ReadLegacyInt(legacyOverrides, "TopCount", rankingRules.TopCount), "Quantidade de jogadores exibidos no topo do ranking."); _cfgAllowRepeatedBossPoints = _rulesConfig.BindConfig("General", "AllowRepeatedBossPoints", ReadLegacyBool(legacyOverrides, "AllowRepeatedBossPoints", rankingRules.AllowRepeatedBossPoints), "Permite pontuar boss repetido."); _cfgEnableSkillPoints = _rulesConfig.BindConfig("General", "EnableSkillPoints", ReadLegacyBool(legacyOverrides, "EnableSkillPoints", rankingRules.EnableSkillPoints), "Ativa jackpots por marcos de skill."); _cfgIgnoreTamedKills = _rulesConfig.BindConfig("General", "IgnoreTamedKills", ReadLegacyBool(legacyOverrides, "IgnoreTamedKills", rankingRules.IgnoreTamedKills), "Ignora pontos por matar criaturas domadas."); _cfgEnableMarketplaceQuestPoints = _rulesConfig.BindConfig("General", "EnableMarketplaceQuestPoints", ReadLegacyBool(legacyOverrides, "EnableMarketplaceQuestPoints", rankingRules.EnableMarketplaceQuestPoints), "Ativa pontuação por conclusão de quests do Marketplace."); _cfgEnableFishingPoints = _rulesConfig.BindConfig("General", "EnableFishingPoints", ReadLegacyBool(legacyOverrides, "EnableFishingPoints", rankingRules.EnableFishingPoints), "Ativa pontuação própria por pesca."); _cfgEnableDeathPenalty = _rulesConfig.BindConfig("General", "EnableDeathPenalty", ReadLegacyBool(legacyOverrides, "EnableDeathPenalty", rankingRules.EnableDeathPenalty), "Ativa penalidade por morte no ranking."); _cfgDebugLogging = _rulesConfig.BindConfig("Logging", "DebugLogging", ReadLegacyBool(legacyOverrides, "DebugLogging", rankingRules.DebugLogging), "Ativa logs de depuração."); _cfgLogHitReports = _rulesConfig.BindConfig("Logging", "LogHitReports", ReadLegacyBool(legacyOverrides, "LogHitReports", rankingRules.LogHitReports), "Ativa logs de hit reports."); _cfgLogKillReports = _rulesConfig.BindConfig("Logging", "LogKillReports", ReadLegacyBool(legacyOverrides, "LogKillReports", rankingRules.LogKillReports), "Ativa logs de kill reports."); _cfgLogSkillReports = _rulesConfig.BindConfig("Logging", "LogSkillReports", ReadLegacyBool(legacyOverrides, "LogSkillReports", rankingRules.LogSkillReports), "Ativa logs de skill reports."); _cfgLogPendingKillReports = _rulesConfig.BindConfig("Logging", "LogPendingKillReports", ReadLegacyBool(legacyOverrides, "LogPendingKillReports", rankingRules.LogPendingKillReports), "Ativa logs das checagens pendentes de kill."); _cfgLogSnapshotRequests = _rulesConfig.BindConfig("Logging", "LogSnapshotRequests", ReadLegacyBool(legacyOverrides, "LogSnapshotRequests", rankingRules.LogSnapshotRequests), "Ativa logs de snapshots do ranking."); _cfgLogPointsChanges = _rulesConfig.BindConfig("Logging", "LogPointsChanges", ReadLegacyBool(legacyOverrides, "LogPointsChanges", rankingRules.LogPointsChanges), "Ativa logs de alterações de pontos."); _cfgRewardClaimsEnabled = _rulesConfig.BindConfig("Rewards", "RewardClaimsEnabled", ReadLegacyBool(legacyOverrides, "RewardClaimsEnabled", rankingRules.RewardClaimsEnabled), "Ativa o resgate de recompensas no HUD."); _cfgRewardClaimCycleId = _rulesConfig.BindConfig("Rewards", "RewardClaimCycleId", ReadLegacyString(legacyOverrides, "RewardClaimCycleId", rankingRules.RewardClaimCycleId), "Identificador do ciclo atual de recompensas."); _cfgRewardTop1MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop1MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop1MinPoints", rankingRules.RewardTop1MinPoints), "Pontos mínimos para o Top 1 resgatar."); _cfgRewardTop1Label = _rulesConfig.BindConfig("Rewards", "RewardTop1Label", ReadLegacyString(legacyOverrides, "RewardTop1Label", rankingRules.RewardTop1Label), "Texto exibido para a recompensa do Top 1."); _cfgRewardTop1Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop1Prefab", ReadLegacyString(legacyOverrides, "RewardTop1Prefab", rankingRules.RewardTop1Prefab), "Prefab do item entregue ao Top 1."); _cfgRewardTop1Amount = _rulesConfig.BindConfig("Rewards", "RewardTop1Amount", ReadLegacyInt(legacyOverrides, "RewardTop1Amount", rankingRules.RewardTop1Amount), "Quantidade do item entregue ao Top 1."); _cfgRewardTop2MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop2MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop2MinPoints", rankingRules.RewardTop2MinPoints), "Pontos mínimos para o Top 2 resgatar."); _cfgRewardTop2Label = _rulesConfig.BindConfig("Rewards", "RewardTop2Label", ReadLegacyString(legacyOverrides, "RewardTop2Label", rankingRules.RewardTop2Label), "Texto exibido para a recompensa do Top 2."); _cfgRewardTop2Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop2Prefab", ReadLegacyString(legacyOverrides, "RewardTop2Prefab", rankingRules.RewardTop2Prefab), "Prefab do item entregue ao Top 2."); _cfgRewardTop2Amount = _rulesConfig.BindConfig("Rewards", "RewardTop2Amount", ReadLegacyInt(legacyOverrides, "RewardTop2Amount", rankingRules.RewardTop2Amount), "Quantidade do item entregue ao Top 2."); _cfgRewardTop3MinPoints = _rulesConfig.BindConfig("Rewards", "RewardTop3MinPoints", ReadLegacyInt(legacyOverrides, "RewardTop3MinPoints", rankingRules.RewardTop3MinPoints), "Pontos mínimos para o Top 3 resgatar."); _cfgRewardTop3Label = _rulesConfig.BindConfig("Rewards", "RewardTop3Label", ReadLegacyString(legacyOverrides, "RewardTop3Label", rankingRules.RewardTop3Label), "Texto exibido para a recompensa do Top 3."); _cfgRewardTop3Prefab = _rulesConfig.BindConfig("Rewards", "RewardTop3Prefab", ReadLegacyString(legacyOverrides, "RewardTop3Prefab", rankingRules.RewardTop3Prefab), "Prefab do item entregue ao Top 3."); _cfgRewardTop3Amount = _rulesConfig.BindConfig("Rewards", "RewardTop3Amount", ReadLegacyInt(legacyOverrides, "RewardTop3Amount", rankingRules.RewardTop3Amount), "Quantidade do item entregue ao Top 3."); _cfgPointsExchangeEnabled = _rulesConfig.BindConfig("PointsExchange", "Enabled", ReadLegacyBool(legacyOverrides, "PointsExchangeEnabled", rankingRules.PointsExchangeEnabled), "Ativa o botão de câmbio de pontos por moedas no HUD."); _cfgPointsExchangePrefab = _rulesConfig.BindConfig("PointsExchange", "Prefab", ReadLegacyString(legacyOverrides, "PointsExchangePrefab", rankingRules.PointsExchangePrefab), "Prefab entregue no câmbio de pontos. Exemplo: Coins."); _cfgPointsExchangeUseCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "UseCoinsPerPoint", ReadLegacyBool(legacyOverrides, "PointsExchangeUseCoinsPerPoint", rankingRules.PointsExchangeUseCoinsPerPoint), "Ativa o modo antigo: moedas por ponto. Por padrão fica false."); _cfgPointsExchangeCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "CoinsPerPoint", ReadLegacyInt(legacyOverrides, "PointsExchangeCoinsPerPoint", rankingRules.PointsExchangeCoinsPerPoint), "Quantidade de moedas entregues por cada ponto trocado. Só é usado se UseCoinsPerPoint=true."); _cfgPointsExchangeUsePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "UsePointsPerCoin", ReadLegacyBool(legacyOverrides, "PointsExchangeUsePointsPerCoin", rankingRules.PointsExchangeUsePointsPerCoin), "Ativa o modo recomendado: pontos necessários para receber 1 moeda."); _cfgPointsExchangePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "PointsPerCoin", ReadLegacyInt(legacyOverrides, "PointsExchangePointsPerCoin", rankingRules.PointsExchangePointsPerCoin), "Quantidade de pontos necessários para receber 1 moeda. Exemplo: 100 = a cada 100 pontos, 1 coin."); _cfgPointsExchangeMinPoints = _rulesConfig.BindConfig("PointsExchange", "MinPoints", ReadLegacyInt(legacyOverrides, "PointsExchangeMinPoints", rankingRules.PointsExchangeMinPoin