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.7.56
GlitnirRanking.dll
Decompiled 13 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.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 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.UI; [assembly: Guid("72b7e2b4-c4d2-4b2f-91db-99d0311d97bc")] [assembly: ComVisible(false)] [assembly: AssemblyTrademark("")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyProduct("GlitnirRanking")] [assembly: AssemblyCompany("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyDescription("")] [assembly: AssemblyTitle("GlitnirRanking")] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace Glitnir.Ranking { internal static class GlitnirRankingServerSync { internal static readonly ConfigSync ConfigSync = new ConfigSync("com.glitnir.ranking") { DisplayName = "Glitnir Ranking", CurrentVersion = "0.7.55", MinimumRequiredVersion = "0.7.55" }; internal static ConfigEntry<T> BindConfig<T>(this ConfigFile config, string group, string name, T value, string description, bool synced = true) { ConfigEntry<T> val = config.Bind<T>(group, name, value, description); if (synced) { ConfigSync.AddConfigEntry<T>(val); } return val; } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.glitnir.ranking", "Glitnir Ranking", "0.7.55")] 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 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 KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; 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 PendingKillCheck { public string PrefabName = ""; public float FirstSeenTime; public float LastHitTime; } private sealed class RankRewardInfo { public int Rank; public int MinPoints; public string Label = ""; public string PrefabName = ""; public int Amount; } 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); } } public const string ModGuid = "com.glitnir.ranking"; public const string ModName = "Glitnir Ranking"; public const string ModVersion = "0.7.55"; 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 RpcReportHit = "glitnir.ranking.reporthit"; private const string RpcReportKill = "glitnir.ranking.reportkill"; 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 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 LocalRecentHitLifetimeSeconds = 15f; private const float ServerPendingKillDelaySeconds = 0.25f; private const float ServerPendingKillTimeoutSeconds = 12f; private const float ServerPendingKillCheckInterval = 0.25f; private const int MaxHudChars = 3000; private const int MaxPlayerNameLength = 32; private const int MaxReasonLength = 64; private const float ClientRefreshInterval = 1f; 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 MethodInfo _hudLocalizationLocalizeMethod; private bool _hudLocalizationReflectionReady; private Harmony _harmony; private bool _rpcsRegistered = false; private string _rulesFilePath; private string _uiFolderPath; private string _uiFilePath; private string _databaseFilePath; private string _legacyDatabaseFilePath; private ConfigFile _rulesConfig; private FileSystemWatcher _rulesConfigWatcher; 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<string> _cfgUniqueCraftJackpotRules; 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<int> _cfgPointsExchangeCoinsPerPoint; 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 readonly Dictionary<string, PendingKillCheck> _pendingKillChecks = new Dictionary<string, PendingKillCheck>(StringComparer.Ordinal); private float _serverPendingKillCheckAt; private bool _hudVisible; private float _hudOpenTime; private Rect _windowRect = new Rect(660f, 56f, 548f, 804f); private Rect _iconRect = new Rect(24f, 180f, 56f, 56f); 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 = 0; private float _hudTabSwitchTime = 0f; 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 _explorationReportTimer; private float _lastReportedMapExplorePercent = -1f; private long _lastKnownServerPeerUid; private bool _requestedInitialServerSnapshot; private string _cachedTopText = "Carregando ranking..."; private string _cachedPlayerText = "Aguardando dados do servidor..."; private string _statusText = "Sincronizando..."; private float _statusUntil = 0f; private bool _rewardClaimRequestPending; private string _rewardClaimPendingCycleId = ""; private int _rewardClaimPendingRank = 0; 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 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 string _uiTitleFontNames = "Cinzel Decorative|Cinzel|Trajan Pro|Palatino Linotype|Georgia|Times New Roman"; private string _uiBodyFontNames = "Cormorant Garamond|Cormorant SC|Palatino Linotype|Georgia|Garamond|Arial"; private string _uiAccentFontNames = "Cinzel|Palatino Linotype|Georgia|Arial"; private bool _uiUseSystemFonts = false; private KeyCode _uiToggleKey = (KeyCode)121; 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 bool _uiTexturesLoaded; 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", "Fish3" }, { "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" }, { "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", "Fish3" }, { "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 const float ActionPlayerCollapsedHeight = 58f; private const float ActionPlayerExpandedHeight = 650f; 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 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); foreach (string text in array) { string text2 = text.Trim(); if (string.IsNullOrWhiteSpace(text2) || text2.StartsWith("#") || text2.StartsWith(";") || text2.StartsWith("//")) { continue; } int num = text2.IndexOf('='); if (num <= 0) { continue; } string text3 = text2.Substring(0, num).Trim(); string text4 = text2.Substring(num + 1).Trim(); if (text3.Equals("RankingEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RankingEnabled = ParseBool(text4, _rules.RankingEnabled); } else if (text3.Equals("EnableKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableKillPoints = ParseBool(text4, _rules.EnableKillPoints); } else if (text3.Equals("EnableBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableBossPoints = ParseBool(text4, _rules.EnableBossPoints); } else if (text3.Equals("DefaultKillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.DefaultKillPoints = ParseInt(text4, _rules.DefaultKillPoints); } else if (text3.Equals("TopCount", StringComparison.OrdinalIgnoreCase)) { _rules.TopCount = Mathf.Clamp(ParseInt(text4, _rules.TopCount), 1, 50); } else if (text3.Equals("AllowRepeatedBossPoints", StringComparison.OrdinalIgnoreCase)) { _rules.AllowRepeatedBossPoints = ParseBool(text4, _rules.AllowRepeatedBossPoints); } else if (text3.Equals("DebugLogging", StringComparison.OrdinalIgnoreCase)) { _rules.DebugLogging = ParseBool(text4, _rules.DebugLogging); } else if (text3.Equals("LogHitReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogHitReports = ParseBool(text4, _rules.LogHitReports); } else if (text3.Equals("LogKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogKillReports = ParseBool(text4, _rules.LogKillReports); } else if (text3.Equals("LogPendingKillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogPendingKillReports = ParseBool(text4, _rules.LogPendingKillReports); } else if (text3.Equals("LogSnapshotRequests", StringComparison.OrdinalIgnoreCase)) { _rules.LogSnapshotRequests = ParseBool(text4, _rules.LogSnapshotRequests); } else if (text3.Equals("LogPointsChanges", StringComparison.OrdinalIgnoreCase)) { _rules.LogPointsChanges = ParseBool(text4, _rules.LogPointsChanges); } else if (text3.Equals("EnableSkillPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableSkillPoints = ParseBool(text4, _rules.EnableSkillPoints); } else if (text3.Equals("LogSkillReports", StringComparison.OrdinalIgnoreCase)) { _rules.LogSkillReports = ParseBool(text4, _rules.LogSkillReports); } else if (text3.Equals("EnableFishingPoints", StringComparison.OrdinalIgnoreCase)) { _rules.EnableFishingPoints = ParseBool(text4, _rules.EnableFishingPoints); } else if (text3.Equals("EnableDeathPenalty", StringComparison.OrdinalIgnoreCase)) { _rules.EnableDeathPenalty = ParseBool(text4, _rules.EnableDeathPenalty); } else if (text3.Equals("DeathPenalty.UseMultiplierMode", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyUseMultiplier", StringComparison.OrdinalIgnoreCase) || text3.Equals("UseMultiplierMode", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyUseMultiplier = ParseBool(text4, _rules.DeathPenaltyUseMultiplier); } else if (text3.Equals("DeathPenalty.PenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyPerDeath", StringComparison.OrdinalIgnoreCase) || text3.Equals("PenaltyPerDeath", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyPerDeath = Mathf.Max(0, ParseInt(text4, _rules.DeathPenaltyPerDeath)); } else if (text3.Equals("DeathPenalty.Rules", StringComparison.OrdinalIgnoreCase) || text3.Equals("DeathPenaltyRules", StringComparison.OrdinalIgnoreCase) || text3.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { _rules.DeathPenaltyRules = ParseDeathPenaltyRules(text4); } else if (text3.Equals("RewardClaimsEnabled", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimsEnabled = ParseBool(text4, _rules.RewardClaimsEnabled); } else if (text3.Equals("RewardClaimCycleId", StringComparison.OrdinalIgnoreCase)) { _rules.RewardClaimCycleId = SafeLimit(text4, 64); } else if (text3.Equals("RewardTop1MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop1MinPoints)); } else if (text3.Equals("RewardTop1Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop1Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop1Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop1Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop1Amount)); } else if (text3.Equals("RewardTop2MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop2MinPoints)); } else if (text3.Equals("RewardTop2Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop2Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop2Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop2Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop2Amount)); } else if (text3.Equals("RewardTop3MinPoints", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3MinPoints = Mathf.Max(0, ParseInt(text4, _rules.RewardTop3MinPoints)); } else if (text3.Equals("RewardTop3Label", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Label = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop3Prefab", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Prefab = SafeLimit(text4, 96); } else if (text3.Equals("RewardTop3Amount", StringComparison.OrdinalIgnoreCase)) { _rules.RewardTop3Amount = Mathf.Max(0, ParseInt(text4, _rules.RewardTop3Amount)); } else if (text3.StartsWith("KillPoints.", StringComparison.OrdinalIgnoreCase)) { string text5 = SafeKey(text3.Substring("KillPoints.".Length)); if (!IsFishPrefab(text5)) { _rules.KillPoints[text5] = ParseInt(text4, 0); } } else if (text3.StartsWith("BossPoints.", StringComparison.OrdinalIgnoreCase)) { _rules.BossPoints[NormalizeBossPrefabName(text3.Substring("BossPoints.".Length))] = ParseInt(text4, 0); } else if (text3.StartsWith("FishingPoints.", StringComparison.OrdinalIgnoreCase)) { string text6 = NormalizeFishPrefabName(text3.Substring("FishingPoints.".Length)); if (IsFishPrefab(text6)) { _rules.FishingPoints[text6] = ParseInt(text4, 0); } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao carregar regras do ranking: " + ex)); } } private bool ParseBool(string raw, bool fallback) { bool result; return bool.TryParse(raw, out result) ? result : fallback; } private int ParseInt(string raw, int fallback) { int result; return int.TryParse(raw, out result) ? result : fallback; } 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; BindSyncedRulesConfigEntries(dictionary); 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); 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 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); foreach (string text in array) { string text2 = text.Trim(); if (text2.StartsWith("[") && text2.EndsWith("]")) { flag = true; break; } } if (flag) { return dictionary; } string[] array2 = File.ReadAllLines(filePath, Encoding.UTF8); foreach (string text3 in array2) { string text4 = text3.Trim(); if (string.IsNullOrWhiteSpace(text4) || text4.StartsWith("#") || text4.StartsWith(";") || text4.StartsWith("//")) { continue; } int num = text4.IndexOf('='); if (num > 0) { string text5 = text4.Substring(0, num).Trim(); string value = text4.Substring(num + 1).Trim(); if (!string.IsNullOrWhiteSpace(text5)) { dictionary[text5] = 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); foreach (string text in array) { string text2 = text.Trim(); if (!string.IsNullOrWhiteSpace(text2) && !text2.StartsWith("#") && !text2.StartsWith(";") && !text2.StartsWith("//")) { return !text2.StartsWith("[") || !text2.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."); _cfgPointsExchangeCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "CoinsPerPoint", ReadLegacyInt(legacyOverrides, "PointsExchangeCoinsPerPoint", rankingRules.PointsExchangeCoinsPerPoint), "Quantidade de moedas entregues por cada ponto trocado."); _cfgPointsExchangeMinPoints = _rulesConfig.BindConfig("PointsExchange", "MinPoints", ReadLegacyInt(legacyOverrides, "PointsExchangeMinPoints", rankingRules.PointsExchangeMinPoints), "Valor legado. O câmbio não exige requisito mínimo; basta o jogador ter pontos disponíveis."); _cfgPointsExchangeMaxPointsPerRequest = _rulesConfig.BindConfig("PointsExchange", "MaxPointsPerRequest", ReadLegacyInt(legacyOverrides, "PointsExchangeMaxPointsPerRequest", rankingRules.PointsExchangeMaxPointsPerRequest), "Máximo de pontos convertidos por clique. Use 0 para trocar todos."); _cfgMarketplaceQuestPointMap = _rulesConfig.BindConfig("MarketplaceQuestPoints", "QuestPointMap", BuildMarketplaceQuestPointMapDefault(legacyOverrides), "Formato: questId:pontos separados por vírgula. Ex: ccq_b21:450,ccq_b22:150"); _cfgDeathPenaltyRules = _rulesConfig.BindConfig("DeathPenalty", "Rules", ReadLegacyString(legacyOverrides, "DeathPenalty.Rules", ReadLegacyString(legacyOverrides, "DeathPenaltyRules", "1:0,2:100,5:300,10:800")), "Penalidade por morte em marcos. Formato: mortes:pontosPerdidos,mortes:pontosPerdidos. Exemplo: 1:0,2:100,5:300,10:800"); _cfgDeathPenaltyUseMultiplier = _rulesConfig.BindConfig("DeathPenalty", "UseMultiplierMode", ReadLegacyBool(legacyOverrides, "DeathPenalty.UseMultiplierMode", ReadLegacyBool(legacyOverrides, "DeathPenaltyUseMultiplier", fallback: false)), "Se true, ignora a tabela Rules e aplica PenaltyPerDeath a cada morte. Exemplo: 15 mortes x 100 = -1500."); _cfgDeathPenaltyPerDeath = _rulesConfig.BindConfig("DeathPenalty", "PenaltyPerDeath", Mathf.Max(0, ReadLegacyInt(legacyOverrides, "DeathPenalty.PenaltyPerDeath", ReadLegacyInt(legacyOverrides, "DeathPenaltyPerDeath", 100))), "Valor perdido por cada morte quando UseMultiplierMode=true."); _cfgEnableExplorationJackpots = _rulesConfig.BindConfig("ExplorationMapJackpots", "Enabled", ReadLegacyBool(legacyOverrides, "ExplorationMapJackpots.Enabled", fallback: true), "Ativa jackpots por porcentagem de mapa revelado."); _cfgExplorationMapJackpotRules = _rulesConfig.BindConfig("ExplorationMapJackpots", "Rules", ReadLegacyString(legacyOverrides, "ExplorationMapJackpots.Rules", ReadLegacyString(legacyOverrides, "ExplorationMapJackpotRules", "5:25;10:50;25:150;50:400;75:800;100:1500")), "Jackpots por porcentagem do mapa revelado. Formato: porcentagem:pontos;porcentagem:pontos. Exemplo: 5:25;10:50;25:150"); BindCombatBiomeRuleEntries(legacyOverrides); _cfgProductionCategoryRules = _rulesConfig.BindConfig("HudCategories", "Production", ReadLegacyString(legacyOverrides, "HudCategories.Production", "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"), "Producao/Conquistas por categoria COM pontos. O nome da categoria do config vira a aba do HUD. Formato obrigatorio: Categoria:Prefab=pontos,Prefab=pontos;Outra:Prefab=pontos. Exemplo: Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25. Tambem organiza FarmJackpots e UniqueCraftJackpots."); BindPointSectionEntries("BossPoints", "Pontos por bosses e minibosses.", legacyOverrides, _cfgBossPointEntries); BindPointSectionEntries("FishingPoints", "Pontos por peixe pescado.", legacyOverrides, _cfgFishingPointEntries); BindStringPointSectionEntries("SkillMilestoneJackpotPoints", "Pontos únicos por marco de skill. Formato: 20:500,40:1000,60:2000,80:3000,100:4000.", legacyOverrides, _cfgSkillMilestoneJackpotPointEntries); _cfgFarmJackpotRules = _rulesConfig.BindConfig("FarmJackpots", "Rules", ReadGroupedRulesDefault(legacyOverrides, "FarmJackpots", "Carrot:200:1000;Turnip:200:1200;Onion:200:1500;Barley:500:2000;Flax:500:2000"), "Jackpots de colheita. Edite somente esta linha. Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos. Exemplo: Carrot:200:1000;Barley:500:2000."); _cfgUniqueCraftJackpotRules = _rulesConfig.BindConfig("UniqueCraftJackpots", "Rules", ReadGroupedRulesDefault(legacyOverrides, "UniqueCraftJackpots", "SwordIron:1:800;SwordSilver:1:1500;SwordBlackmetal:1:2500;ArmorWolfChest:1:2000;ArmorCarapaceChest:1:3500"), "Jackpot único por craft. Edite somente esta linha. Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos. Exemplo: SwordIron:1:800;ArmorWolfChest:1:2000."); } } private void BindCombatBiomeRuleEntries(Dictionary<string, string> legacyOverrides) { if (_cfgCombatBiomeRuleEntries != null) { _cfgCombatBiomeRuleEntries.Clear(); Dictionary<string, string> defaults = ParseCombatBiomeDefaults("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"); BindSingleCombatBiome("Prados", defaults, legacyOverrides); BindSingleCombatBiome("Floresta Negra", defaults, legacyOverrides); BindSingleCombatBiome("Pântano", defaults, legacyOverrides); BindSingleCombatBiome("Montanha", defaults, legacyOverrides); BindSingleCombatBiome("Planícies", defaults, legacyOverrides); BindSingleCombatBiome("Oceano", defaults, legacyOverrides); BindSingleCombatBiome("Mistlands", defaults, legacyOverrides); BindSingleCombatBiome("Ashlands", defaults, legacyOverrides); BindSingleCombatBiome("Especiais", defaults, legacyOverrides); } } private void BindSingleCombatBiome(string category, Dictionary<string, string> defaults, Dictionary<string, string> legacyOverrides) { if (defaults == null || !defaults.TryGetValue(category, out var value)) { value = string.Empty; } string value2 = ReadLegacyString(legacyOverrides, "Combat." + category, value); _cfgCombatBiomeRuleEntries[category] = _rulesConfig.BindConfig("Combat", category, value2, "Mobs e pontos para esta aba do HUD. Formato: Prefab;Pontos,OutroPrefab;Pontos. Exemplo: Boar;2,Deer;3. Aceita qualquer prefab, inclusive de mods. O nome desta chave vira a categoria no HUD."); } private Dictionary<string, string> ParseCombatBiomeDefaults(string raw) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string text3 = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); string text4 = text2.Substring(num + 1).Trim(); if (string.IsNullOrWhiteSpace(text3) || string.IsNullOrWhiteSpace(text4)) { continue; } List<string> list = new List<string>(); string[] array3 = text4.Split(new char[2] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries); string[] array4 = array3; foreach (string itemRaw in array4) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ";" + Mathf.Max(0, points)); } } dictionary[text3] = string.Join(",", list.ToArray()); } return dictionary; } private string BuildCombatCategoryRulesFromBiomeEntries() { if (_cfgCombatBiomeRuleEntries == null || _cfgCombatBiomeRuleEntries.Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, ConfigEntry<string>> cfgCombatBiomeRuleEntry in _cfgCombatBiomeRuleEntries) { if (cfgCombatBiomeRuleEntry.Value == null) { continue; } string value = NormalizeHudCategoryName(cfgCombatBiomeRuleEntry.Key); if (string.IsNullOrWhiteSpace(value)) { continue; } string value2 = ConvertCombatBiomeItemsToHudCategoryItems(cfgCombatBiomeRuleEntry.Value.Value); if (!string.IsNullOrWhiteSpace(value2)) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } stringBuilder.Append(value); stringBuilder.Append(':'); stringBuilder.Append(value2); } } return stringBuilder.ToString(); } private string ConvertCombatBiomeItemsToHudCategoryItems(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return string.Empty; } List<string> list = new List<string>(); string[] array = raw.Split(new char[4] { ',', '|', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string itemRaw in array2) { if (TryParseCombatBiomeItem(itemRaw, out var prefab, out var points)) { list.Add(prefab + ":" + Mathf.Max(0, points)); } } return string.Join(",", list.ToArray()); } private bool TryParseCombatBiomeItem(string itemRaw, out string prefab, out int points) { prefab = string.Empty; points = 0; if (string.IsNullOrWhiteSpace(itemRaw)) { return false; } string text = itemRaw.Trim(); int num = text.LastIndexOf(';'); if (num <= 0) { num = text.LastIndexOf('='); } if (num <= 0) { num = text.LastIndexOf(':'); } if (num <= 0 || num >= text.Length - 1) { return false; } string value = text.Substring(0, num).Trim(); string s = text.Substring(num + 1).Trim(); if (!int.TryParse(s, out var result)) { return false; } prefab = SafeKey(value); points = Mathf.Max(0, result); return !string.IsNullOrWhiteSpace(prefab); } private void RewriteRulesConfigHeaderAndGroupedSections() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List<string> list = new List<string>(array.Length + 40); string a = string.Empty; bool flag = false; HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "DeathPenalty", "ExplorationMapJackpots" }; list.Add("## Glitnir Ranking - Regras de pontuação"); list.Add("##"); list.Add("## Edite este arquivo no servidor. As regras marcadas como synced são enviadas aos clientes."); list.Add("##"); list.Add("## Combate fica em [Combat], uma chave por bioma/categoria, exatamente igual ao HUD."); list.Add("## [Combat]"); list.Add("## Prados = Boar;2,Deer;3"); list.Add("## Floresta Negra = Troll;20,Greydwarf;1"); list.Add("## Pântano = Draugr;2,Blob;2"); list.Add("## [UniqueCraftJackpots] Rules = Prefab:quantidade:pontos;Prefab:quantidade:pontos"); list.Add("## [DeathPenalty] Rules = mortes:pontosPerdidos,mortes:pontosPerdidos"); list.Add("## [ExplorationMapJackpots] Rules = porcentagem:pontos;porcentagem:pontos"); list.Add("##"); list.Add("## Exemplos de prefabs reais comuns:"); list.Add("## Carrot:200:1000"); list.Add("## SwordIron:1:800"); list.Add("## ArmorWolfChest:1:2000"); list.Add("## 1:0,2:100,5:300,10:800"); list.Add("##"); list.Add("## Importante: se um item não pontuar, ative DebugLogging/LogPointsChanges e veja no log"); list.Add("## o prefab detectado pelo ranking no momento do kill, craft ou colheita."); list.Add(""); bool flag2 = false; bool flag3 = false; bool flag4 = false; bool flag5 = false; string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (text2.StartsWith("## Glitnir Ranking - Regras de pontuação", StringComparison.OrdinalIgnoreCase) || (string.Equals(a, "HudCategories", StringComparison.OrdinalIgnoreCase) && text2.StartsWith("Combat", StringComparison.OrdinalIgnoreCase) && text2.IndexOf('=') > 0)) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { string text3 = text2.Substring(1, text2.Length - 2).Trim(); flag = hashSet.Contains(text3); a = text3; if (flag) { if (text3.Equals("FarmJackpots", StringComparison.OrdinalIgnoreCase) && !flag2) { AppendGroupedFarmSection(list); flag2 = true; } else if (text3.Equals("UniqueCraftJackpots", StringComparison.OrdinalIgnoreCase) && !flag3) { AppendGroupedUniqueCraftSection(list); flag3 = true; } else if (text3.Equals("DeathPenalty", StringComparison.OrdinalIgnoreCase) && !flag4) { AppendGroupedDeathPenaltySection(list); flag4 = true; } else if (text3.Equals("ExplorationMapJackpots", StringComparison.OrdinalIgnoreCase) && !flag5) { AppendGroupedExplorationMapSection(list); flag5 = true; } } else { list.Add(text); } } else if (!flag) { list.Add(text); } } if (!flag2) { AppendGroupedFarmSection(list); } if (!flag3) { AppendGroupedUniqueCraftSection(list); } if (!flag4) { AppendGroupedDeathPenaltySection(list); } if (!flag5) { AppendGroupedExplorationMapSection(list); } File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao organizar config do ranking: " + ex.Message)); } } private void AppendGroupedFarmSection(List<string> lines) { lines.Add(""); lines.Add("[FarmJackpots]"); lines.Add(""); lines.Add("## Jackpot de colheita por prefab configurado."); lines.Add("## Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos"); lines.Add("## Exemplo: Carrot:200:1000;Barley:500:2000"); lines.Add("Rules = " + ((_cfgFarmJackpotRules != null) ? SanitizeDelimitedJackpotRules(_cfgFarmJackpotRules.Value) : "Carrot:200:1000;Barley:500:2000")); } private void AppendGroupedUniqueCraftSection(List<string> lines) { lines.Add(""); lines.Add("[UniqueCraftJackpots]"); lines.Add(""); lines.Add("## Jackpot único por craft de qualquer prefab configurado."); lines.Add("## Formato: Prefab:quantidade:pontos;Prefab:quantidade:pontos"); lines.Add("## Exemplo: SwordIron:1:800;ArmorWolfChest:1:2000"); lines.Add("Rules = " + ((_cfgUniqueCraftJackpotRules != null) ? SanitizeDelimitedJackpotRules(_cfgUniqueCraftJackpotRules.Value) : "SwordIron:1:800;ArmorWolfChest:1:2000")); } private void AppendGroupedDeathPenaltySection(List<string> lines) { lines.Add(""); lines.Add("[DeathPenalty]"); lines.Add(""); lines.Add("## Penalidade por morte."); lines.Add("## UseMultiplierMode=false usa a tabela Rules abaixo."); lines.Add("## UseMultiplierMode=true ignora Rules e aplica PenaltyPerDeath para cada morte."); lines.Add("## Exemplo multiplicador: PenaltyPerDeath=100 e 15 mortes = -1500 pontos."); lines.Add("UseMultiplierMode = " + ((_cfgDeathPenaltyUseMultiplier != null) ? _cfgDeathPenaltyUseMultiplier.Value.ToString() : "false")); lines.Add("PenaltyPerDeath = " + ((_cfgDeathPenaltyPerDeath != null) ? Mathf.Max(0, _cfgDeathPenaltyPerDeath.Value).ToString() : "100")); lines.Add(""); lines.Add("## Formato do modo tabela: mortes:pontosPerdidos,mortes:pontosPerdidos"); lines.Add("## Exemplo: 1:0,2:100,5:300,10:800"); lines.Add("Rules = " + ((_cfgDeathPenaltyRules != null) ? SanitizeDeathPenaltyRules(_cfgDeathPenaltyRules.Value) : "1:0,2:100,5:300,10:800")); } private void AppendGroupedExplorationMapSection(List<string> lines) { lines.Add(""); lines.Add("[ExplorationMapJackpots]"); lines.Add(""); lines.Add("## Jackpot por porcentagem de mapa revelado."); lines.Add("## Formato: porcentagem:pontos;porcentagem:pontos"); lines.Add("## Exemplo: 5:25;10:50;25:150;50:400;75:800;100:1500"); lines.Add("Enabled = " + ((_cfgEnableExplorationJackpots != null) ? _cfgEnableExplorationJackpots.Value.ToString() : "true")); lines.Add("Rules = " + ((_cfgExplorationMapJackpotRules != null) ? _cfgExplorationMapJackpotRules.Value : "5:25;10:50;25:150;50:400;75:800;100:1500")); } private void CleanupFishKillPointConfigEntries() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List<string> list = new List<string>(array.Length); bool flag = false; bool flag2 = false; string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (text2.StartsWith("[") && text2.EndsWith("]")) { flag = text2.Equals("[KillPoints]", StringComparison.OrdinalIgnoreCase); } int num = text2.IndexOf('='); if (num > 0) { string text3 = text2.Substring(0, num).Trim(); string text4 = text3; if (text3.StartsWith("KillPoints.", StringComparison.OrdinalIgnoreCase)) { text4 = text3.Substring("KillPoints.".Length).Trim(); } else if (!flag) { text4 = null; } if (!string.IsNullOrWhiteSpace(text4) && IsFishPrefab(text4)) { flag2 = true; continue; } } list.Add(text); } if (flag2) { File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao limpar prefabs de peixe do config: " + ex.Message)); } } private void CleanupGroupedProductionConfigEntries() { try { if (string.IsNullOrWhiteSpace(_rulesFilePath) || !File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); List<string> list = new List<string>(array.Length); string text = string.Empty; bool flag = false; HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "KillPoints", "CraftPoints", "FarmJackpots", "UniqueCraftJackpots", "DeathPenalty", "ExplorationMapJackpots" }; string[] array2 = array; foreach (string text2 in array2) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (text3.StartsWith("[") && text3.EndsWith("]")) { text = text3.Substring(1, text3.Length - 2).Trim(); list.Add(text2); continue; } int num = text3.IndexOf('='); if (num > 0 && hashSet.Contains(text)) { string text4 = text3.Substring(0, num).Trim(); bool flag2 = text4.Equals("Rules", StringComparison.OrdinalIgnoreCase); if (text.Equals("DeathPenalty", StringComparison.OrdinalIgnoreCase)) { flag2 = flag2 || text4.Equals("UseMultiplierMode", StringComparison.OrdinalIgnoreCase) || text4.Equals("PenaltyPerDeath", StringComparison.OrdinalIgnoreCase); } else if (text.Equals("ExplorationMapJackpots", StringComparison.OrdinalIgnoreCase)) { flag2 = flag2 || text4.Equals("Enabled", StringComparison.OrdinalIgnoreCase); } if (!flag2) { flag = true; continue; } } list.Add(text2); } if (flag) { File.WriteAllLines(_rulesFilePath, list.ToArray(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[Ranking] Falha ao reorganizar config de produção: " + ex.Message)); } } private string BuildMarketplaceQuestPointMapDefault(Dictionary<string, string> legacyOverrides) { string text = ReadLegacyString(legacyOverrides, "MarketplaceQuestPoints.QuestPointMap", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeMarketplaceQuestPointMap(text); } text = ReadLegacyString(legacyOverrides, "QuestPointMap", null); if (!string.IsNullOrWhiteSpace(text)) { return SanitizeMarketplaceQuestPointMap(text); } Dictionary<string, int> mergedDefaultPointEntries = GetMergedDefaultPointEntries("MarketplaceQuestPoints", legacyOverrides); if (mergedDefaultPointEntries.Count <= 0) { return "ccq_b21:450,ccq_b22:150"; } return string.Join(",", from p in mergedDefaultPointEntries.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.OrdinalIgnoreCase) select SafeMarketplaceQuestKey(p.Key) + ":" + Mathf.Max(0, p.Value)); } private Dictionary<string, int> ParseMarketplaceQuestPointMap(string raw) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2) { string text3 = SafeMarketplaceQuestKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result)) { dictionary[text3] = result; } } } return dictionary; } private string SanitizeMarketplaceQuestPointMap(string raw) { Dictionary<string, int> dictionary = ParseMarketplaceQuestPointMap(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.OrdinalIgnoreCase) select p.Key + ":" + Mathf.Max(0, p.Value)); } private Dictionary<int, int> ParseSkillMilestoneMap(string raw) { Dictionary<int, int> dictionary = new Dictionary<int, int>(); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text2)) { string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2 && int.TryParse(array3[0].Trim(), out var result) && int.TryParse(array3[1].Trim(), out var result2)) { result = Mathf.Clamp(result, 1, 100); dictionary[result] = Mathf.Max(0, result2); } } } return dictionary; } private string SanitizeSkillMilestoneMap(string raw) { Dictionary<int, int> dictionary = ParseSkillMilestoneMap(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary orderby p.Key select p.Key + ":" + Mathf.Max(0, p.Value)); } private JackpotRule ParseJackpotRule(string raw) { JackpotRule jackpotRule = new JackpotRule(); if (string.IsNullOrWhiteSpace(raw)) { return jackpotRule; } string[] array = raw.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array.Length != 2) { return jackpotRule; } if (!int.TryParse(array[0].Trim(), out var result)) { return jackpotRule; } if (!int.TryParse(array[1].Trim(), out var result2)) { return jackpotRule; } jackpotRule.RequiredAmount = Mathf.Max(1, result); jackpotRule.Points = Mathf.Max(0, result2); return jackpotRule; } private string SanitizeJackpotRule(string raw) { JackpotRule jackpotRule = ParseJackpotRule(raw); if (jackpotRule == null || jackpotRule.RequiredAmount <= 0) { return "1:0"; } return jackpotRule.RequiredAmount + ":" + Mathf.Max(0, jackpotRule.Points); } private string StripHudCategoryPointPart(string itemRaw) { if (string.IsNullOrWhiteSpace(itemRaw)) { return ""; } string text = itemRaw.Trim(); int num = text.IndexOf('='); if (num > 0) { text = text.Substring(0, num).Trim(); } int num2 = text.LastIndexOf(':'); if (num2 > 0 && num2 < text.Length - 1) { string s = text.Substring(num2 + 1).Trim(); if (int.TryParse(s, out var _)) { text = text.Substring(0, num2).Trim(); } } return SafeKey(text); } private bool TryParseHudCategoryPointItem(string itemRaw, out string prefab, out int points) { prefab = ""; points = 0; if (string.IsNullOrWhiteSpace(itemRaw)) { return false; } string text = itemRaw.Trim(); int num = text.LastIndexOf('='); if (num <= 0) { num = text.LastIndexOf(':'); } if (num <= 0 || num >= text.Length - 1) { return false; } string value = text.Substring(0, num).Trim(); string s = text.Substring(num + 1).Trim(); if (!int.TryParse(s, out var result)) { return false; } prefab = SafeKey(value); points = Mathf.Max(0, result); return !string.IsNullOrWhiteSpace(prefab); } private Dictionary<string, string> ParseHudCategoryRules(string raw) { ParseHudCategorizedPointRules(raw, out var categories); return categories; } private static string NormalizeHudCategoryName(string category) { if (string.IsNullOrWhiteSpace(category)) { return category; } string text = category.Trim(); if (text.Equals("Arcos e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Arcos e Municoes", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas, Armaduras e Munições", StringComparison.OrdinalIgnoreCase) || text.Equals("Armas, Armaduras e Municoes", StringComparison.OrdinalIgnoreCase)) { return "Armas"; } if (text.Equals("Comidas e Consumíveis", StringComparison.OrdinalIgnoreCase) || text.Equals("Comidas e Consumiveis", StringComparison.OrdinalIgnoreCase) || text.Equals("Comidas e bebidas", StringComparison.OrdinalIgnoreCase)) { return "Comidas"; } return text; } private Dictionary<string, int> ParseHudCategorizedPointRules(string raw, out Dictionary<string, string> categories) { categories = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[3] { ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : ""); if (string.IsNullOrWhiteSpace(text2)) { continue; } int num = text2.IndexOf(':'); if (num <= 0 || num >= text2.Length - 1) { continue; } string value = NormalizeHudCategoryName(text2.Substring(0, num).Trim()); if (string.IsNullOrWhiteSpace(value)) { continue; } string text3 = text2.Substring(num + 1); string[] array3 = text3.Split(new char[2] { ',', '|' }, StringSplitOptions.RemoveEmptyEntries); string[] array4 = array3; foreach (string itemRaw in array4) { string text4 = StripHudCategoryPointPart(itemRaw); if (!string.IsNullOrWhiteSpace(text4)) { categories[text4] = value; if (TryParseHudCategoryPointItem(itemRaw, out var prefab, out var points)) { dictionary[prefab] = points; } } } } return dictionary; } private Dictionary<string, int> ParseDelimitedPointRules(string raw) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2) { string text3 = SafeKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result)) { dictionary[text3] = Mathf.Max(0, result); } } } return dictionary; } private Dictionary<string, JackpotRule> ParseDelimitedJackpotRules(string raw) { Dictionary<string, JackpotRule> dictionary = new Dictionary<string, JackpotRule>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(text2)) { continue; } string[] array3 = text2.Split(new char[1] { ':' }, 3, StringSplitOptions.None); if (array3.Length == 3) { string text3 = SafeKey(array3[0]); if (!string.IsNullOrWhiteSpace(text3) && int.TryParse(array3[1].Trim(), out var result) && int.TryParse(array3[2].Trim(), out var result2)) { dictionary[text3] = new JackpotRule { RequiredAmount = Mathf.Max(1, result), Points = Mathf.Max(0, result2) }; } } } return dictionary; } private Dictionary<int, int> ParseDeathPenaltyRules(string raw) { Dictionary<int, int> dictionary = new Dictionary<int, int>(); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string[] array = raw.Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); string[] array2 = array; foreach (string text in array2) { string text2 = ((text != null) ? text.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text2)) { string[] array3 = text2.Split(new char[1] { ':' }, 2, StringSplitOptions.None); if (array3.Length == 2 && int.TryParse(array3[0].Trim(), out var result) && int.TryParse(array3[1].Trim(), out var result2)) { result = Mathf.Max(1, result); result2 = (dictionary[result] = Mathf.Max(0, result2)); } } } return dictionary; } private string SanitizeDeathPenaltyRules(string raw) { Dictionary<int, int> dictionary = ParseDeathPenaltyRules(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(",", from p in dictionary orderby p.Key select Mathf.Max(1, p.Key) + ":" + Mathf.Max(0, p.Value)); } private string SanitizeDelimitedPointRules(string raw) { return SanitizeDelimitedPointRules(raw, ";"); } private string SanitizeDelimitedPointRules(string raw, string delimiter) { Dictionary<string, int> dictionary = ParseDelimitedPointRules(raw); if (dictionary.Count <= 0) { return string.Empty; } if (string.IsNullOrEmpty(delimiter)) { delimiter = ";"; } return string.Join(delimiter, from p in dictionary.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.OrdinalIgnoreCase) where !IsFishPrefab(p.Key) select p.Key + ":" + Mathf.Max(0, p.Value)); } private string SanitizeDelimitedJackpotRules(string raw) { Dictionary<string, JackpotRule> dictionary = ParseDelimitedJackpotRules(raw); if (dictionary.Count <= 0) { return string.Empty; } return string.Join(";", from p in dictionary.OrderBy<KeyValuePair<string, JackpotRule>, string>((KeyValuePair<string, JackpotRule> p) => p.Key, StringComparer.OrdinalIgnoreCase) select p.Key + ":" + Mathf.Max(1, p.Value.RequiredAmount) + ":" + Mathf.Max(0, p.Value.Points)); } private string ReadGroupedRul