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.67
GlitnirRanking.dll
Decompiled a week 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 Glitnir.Ranking.Patches; using HarmonyLib; using JetBrains.Annotations; using LiteDB; using LiteDB.Engine; using LiteDB.Utils; using LiteDB.Utils.Extensions; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; using UnityEngine.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.66", MinimumRequiredVersion = "0.7.66" }; 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; } } [BepInPlugin("com.glitnir.ranking", "Glitnir Ranking", "0.7.66")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class GlitnirRankingPlugin : BaseUnityPlugin { private enum DebugCategory { General, Hit, Kill, Skill, PendingKill, Snapshot, Points } private sealed class SnapshotTopEntryData { public int Position; public string PlayerName = ""; public int Points; public bool IsLocalPlayer; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; } private sealed class SnapshotPlayerData { public bool HasData; public int Position; public string PlayerName = ""; public int Points; public int TotalKillsPontuadas; public int TotalBossesPontuadas; public int TotalSkillLevelUpsPontuados; public int TotalMarketplaceQuestsPontuadas; public int KillPointsTotal; public int BossPointsTotal; public int SkillPointsTotal; public int MarketplaceQuestPointsTotal; public int TotalFishingPontuadas; public int TotalCraftPontuadas; public int TotalFarmJackpotsPontuados; public int TotalUniqueCraftJackpotsPontuados; public int TotalDeaths; public int FishingPointsTotal; public int CraftPointsTotal; public int FarmJackpotPointsTotal; public int UniqueCraftJackpotPointsTotal; public int DeathPenaltyPointsTotal; public int TotalPointsExchanges; public int PointsExchangePenaltyTotal; public int PointsExchangeCoinsTotal; public int ExplorationMapJackpotPointsTotal; public string LastReason = ""; public string LastUpdateUtc = ""; public bool RankingEnabled; public bool EnableKillPoints; public bool EnableBossPoints; public bool EnableSkillPoints; public bool EnableMarketplaceQuestPoints; public int TopCount; public bool RewardClaimsEnabled; public bool RewardCanClaim; public bool RewardAlreadyClaimed; public int RewardRank; public int RewardMinPoints; public string RewardLabel = ""; public string RewardPrefabName = ""; public int RewardAmount; public string RewardBlockReason = ""; public string RewardClaimCycleId = ""; public string HudKillRules = ""; public string HudBossRules = ""; public string HudSkillJackpotRules = ""; public string HudMarketplaceQuestRules = ""; public string HudFishingRules = ""; public string HudCraftRules = ""; public string HudFarmJackpotRules = ""; public string HudUniqueCraftJackpotRules = ""; public string HudDeathPenaltyRules = ""; public bool EnableDeathPenalty; public bool DeathPenaltyUseMultiplier; public int DeathPenaltyPerDeath; public string HudExplorationMapJackpotRules = ""; public Dictionary<string, int> ProgressCounters = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); public Dictionary<string, float> FloatProgressCounters = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase); } private sealed class 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); } } [CompilerGenerated] private sealed class <GetPreferredCategoryOrder>d__45 : IEnumerable<string>, IEnumerable, IEnumerator<string>, IDisposable, IEnumerator { private int <>1__state; private string <>2__current; private int <>l__initialThreadId; private Dictionary<string, string> categories; public Dictionary<string, string> <>3__categories; private string[] preferredOrder; public string[] <>3__preferredOrder; public GlitnirRankingPlugin <>4__this; private HashSet<string> <emitted>5__1; private string[] <>s__2; private int <>s__3; private string <preferred>5__4; private string <category>5__5; private IEnumerator<string> <>s__6; private string <category>5__7; private string <clean>5__8; string IEnumerator<string>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <GetPreferredCategoryOrder>d__45(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 2) { try { } finally { <>m__Finally1(); } } <emitted>5__1 = null; <>s__2 = null; <preferred>5__4 = null; <category>5__5 = null; <>s__6 = null; <category>5__7 = null; <clean>5__8 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <emitted>5__1 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); if (preferredOrder != null) { <>s__2 = preferredOrder; <>s__3 = 0; goto IL_0119; } goto IL_0134; case 1: <>1__state = -1; goto IL_00fc; case 2: { <>1__state = -3; goto IL_0200; } IL_0119: if (<>s__3 < <>s__2.Length) { <preferred>5__4 = <>s__2[<>s__3]; <category>5__5 = NormalizeHudCategoryName(<preferred>5__4); if (string.IsNullOrWhiteSpace(<category>5__5)) { goto IL_010b; } if (categories != null && categories.ContainsKey(<category>5__5) && <emitted>5__1.Add(<category>5__5)) { <>2__current = <category>5__5; <>1__state = 1; return true; } goto IL_00fc; } <>s__2 = null; goto IL_0134; IL_00fc: <category>5__5 = null; <preferred>5__4 = null; goto IL_010b; IL_010b: <>s__3++; goto IL_0119; IL_0134: if (categories == null) { return false; } <>s__6 = categories.Keys.OrderBy<string, string>((string k) => k, StringComparer.OrdinalIgnoreCase).GetEnumerator(); <>1__state = -3; goto IL_020f; IL_0200: <clean>5__8 = null; <category>5__7 = null; goto IL_020f; IL_020f: do { if (<>s__6.MoveNext()) { <category>5__7 = <>s__6.Current; <clean>5__8 = NormalizeHudCategoryName(<category>5__7); continue; } <>m__Finally1(); <>s__6 = null; return false; } while (string.IsNullOrWhiteSpace(<clean>5__8)); if (<emitted>5__1.Add(<clean>5__8)) { <>2__current = <clean>5__8; <>1__state = 2; return true; } goto IL_0200; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>s__6 != null) { <>s__6.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<string> IEnumerable<string>.GetEnumerator() { <GetPreferredCategoryOrder>d__45 <GetPreferredCategoryOrder>d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; <GetPreferredCategoryOrder>d__ = this; } else { <GetPreferredCategoryOrder>d__ = new <GetPreferredCategoryOrder>d__45(0) { <>4__this = <>4__this }; } <GetPreferredCategoryOrder>d__.categories = <>3__categories; <GetPreferredCategoryOrder>d__.preferredOrder = <>3__preferredOrder; return <GetPreferredCategoryOrder>d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<string>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class <VerifyCraftCompletedAndReport>d__638 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public Recipe recipe; public string prefabName; public string sharedName; public int beforeCount; public GlitnirRankingPlugin <>4__this; private float <start>5__1; private int <lastCount>5__2; private int <currentCount>5__3; private int <gained>5__4; private int <amountToReport>5__5; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <VerifyCraftCompletedAndReport>d__638(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <start>5__1 = Time.time; <lastCount>5__2 = beforeCount; break; case 1: <>1__state = -1; if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } <currentCount>5__3 = <>4__this.CountLocalInventoryItemsByPrefabOrSharedName(prefabName, sharedName); <lastCount>5__2 = <currentCount>5__3; <gained>5__4 = <currentCount>5__3 - beforeCount; if (<gained>5__4 <= 0) { break; } <amountToReport>5__5 = Mathf.Clamp(<gained>5__4, 1, 100); <>4__this.NotifyLocalCraftedItem(prefabName, <amountToReport>5__5); <>4__this.DebugLog(DebugCategory.Points, "Craft confirmado por inventário: item=" + prefabName + " antes=" + beforeCount + " depois=" + <currentCount>5__3 + " ganho=" + <amountToReport>5__5); return false; } if (Time.time - <start>5__1 <= 12f) { <>2__current = (object)new WaitForSeconds(0.25f); <>1__state = 1; return true; } <>4__this.DebugLog(DebugCategory.Points, "Craft não pontuado: item não entrou no inventário após início do craft. item=" + prefabName + " antes=" + beforeCount + " ultimo=" + <lastCount>5__2); return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private readonly Dictionary<string, ConfigEntry<string>> _cfgProductionCategoryRuleEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgUniqueCraftJackpotCategoryEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); public const string ModGuid = "com.glitnir.ranking"; public const string ModName = "Glitnir Ranking"; public const string ModVersion = "0.7.66"; 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 PropertyInfo _hudLocalizationInstanceProperty; private MethodInfo _hudLocalizationLocalizeMethod; private bool _hudLocalizationReflectionReady; private Harmony _harmony; private bool _rpcsRegistered = false; private ZRoutedRpc _registeredRoutedRpcInstance; private readonly object _databaseSaveLock = new object(); 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<bool> _cfgRewardClaimsEnabled; private ConfigEntry<string> _cfgRewardClaimCycleId; private ConfigEntry<int> _cfgRewardTop1MinPoints; private ConfigEntry<string> _cfgRewardTop1Label; private ConfigEntry<string> _cfgRewardTop1Prefab; private ConfigEntry<int> _cfgRewardTop1Amount; private ConfigEntry<int> _cfgRewardTop2MinPoints; private ConfigEntry<string> _cfgRewardTop2Label; private ConfigEntry<string> _cfgRewardTop2Prefab; private ConfigEntry<int> _cfgRewardTop2Amount; private ConfigEntry<int> _cfgRewardTop3MinPoints; private ConfigEntry<string> _cfgRewardTop3Label; private ConfigEntry<string> _cfgRewardTop3Prefab; private ConfigEntry<int> _cfgRewardTop3Amount; private ConfigEntry<bool> _cfgPointsExchangeEnabled; private ConfigEntry<string> _cfgPointsExchangePrefab; private ConfigEntry<bool> _cfgPointsExchangeUseCoinsPerPoint; private ConfigEntry<int> _cfgPointsExchangeCoinsPerPoint; private ConfigEntry<bool> _cfgPointsExchangeUsePointsPerCoin; private ConfigEntry<int> _cfgPointsExchangePointsPerCoin; private ConfigEntry<int> _cfgPointsExchangeMinPoints; private ConfigEntry<int> _cfgPointsExchangeMaxPointsPerRequest; private ConfigEntry<bool> _cfgDiscordTop3WebhookEnabled; private ConfigEntry<string> _cfgDiscordTop3WebhookUrl; private ConfigEntry<bool> _cfgIgnoreAdminsInRanking; private ConfigEntry<string> _cfgIgnoredAdminNames; private readonly Dictionary<string, ConfigEntry<int>> _cfgKillPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<int>> _cfgBossPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<int>> _cfgFishingPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<int>> _cfgCraftPointEntries = new Dictionary<string, ConfigEntry<int>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgFarmJackpotEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgUniqueCraftJackpotEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, ConfigEntry<string>> _cfgSkillMilestoneJackpotPointEntries = new Dictionary<string, ConfigEntry<string>>(StringComparer.OrdinalIgnoreCase); private RankingDatabase _database = new RankingDatabase(); private RankingRules _rules = new RankingRules(); private readonly Dictionary<int, MarketplaceQuestPointRule> _marketplaceQuestPointsByUid = new Dictionary<int, MarketplaceQuestPointRule>(); private readonly Dictionary<string, Dictionary<string, float>> _damageCredits = new Dictionary<string, Dictionary<string, float>>(StringComparer.Ordinal); private readonly Dictionary<string, float> _processedKills = new Dictionary<string, float>(StringComparer.Ordinal); private readonly Dictionary<string, float> _ignoredTamedKills = new Dictionary<string, float>(StringComparer.Ordinal); private readonly Dictionary<string, float> _localRecentHits = new Dictionary<string, float>(StringComparer.Ordinal); private 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 _lastSnapshotSyncPlayerName = ""; private bool _lastSnapshotHadLocalPlayer; 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", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" } }; private static readonly Dictionary<string, string> FishPortugueseToPrefab = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Perca", "Fish1" }, { "Lúcio", "Fish2" }, { "Lucio", "Fish2" }, { "Fish3", "Fish3" }, { "Atum", "Fish3" }, { "Tetra", "Fish4_cave" }, { "Peixe Troll", "Fish5" }, { "Arenque gigante", "Fish6" }, { "Garoupa", "Fish7" }, { "Garoupa estrelada", "Fish8" }, { "Peixe-pescador", "Fish9" }, { "Peixe pescador", "Fish9" }, { "Salmão do norte", "Fish10" }, { "Salmao do norte", "Fish10" }, { "Peixe magma", "Fish11" }, { "Baiacu", "Fish12" } }; private static readonly Dictionary<string, string> HudFriendlyNames = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Boar", "Javali" }, { "Neck", "Nixe" }, { "Greyling", "Greydwarf Jovem" }, { "Greydwarf", "Greydwarf" }, { "Greydwarf_Elite", "Greydwarf Elite" }, { "Greydwarf_Shaman", "Xamã Greydwarf" }, { "Troll", "Troll" }, { "Skeleton", "Esqueleto" }, { "Skeleton_Poison", "Esqueleto Venenoso" }, { "Draugr", "Draugr" }, { "Draugr_Elite", "Draugr Elite" }, { "Blob", "Gosma" }, { "BlobElite", "Gosma Elite" }, { "Leech", "Sanguessuga" }, { "Surtling", "Surtling" }, { "Abomination", "Abominação" }, { "Wolf", "Lobo" }, { "Hatchling", "Draco" }, { "Fenring", "Fenring" }, { "StoneGolem", "Golem de Pedra" }, { "Goblin", "Fuling" }, { "GoblinArcher", "Fuling Arqueiro" }, { "GoblinBrute", "Fuling Berserker" }, { "GoblinShaman", "Xamã Fuling" }, { "Lox", "Lox" }, { "Deathsquito", "Mosquito da Morte" }, { "Serpent", "Serpente Marinha" }, { "Bat", "Morcego" }, { "Ulv", "Ulv" }, { "Hare", "Lebre" }, { "Tick", "Carrapato" }, { "Seeker", "Seeker" }, { "SeekerBrute", "Seeker Brutamontes" }, { "Gjall", "Gjall" }, { "Dverger", "Dvergr" }, { "Charred_Melee", "Carbonizado Guerreiro" }, { "Charred_Archer", "Carbonizado Arqueiro" }, { "Charred_Mage", "Carbonizado Mago" }, { "Morgen", "Morgen" }, { "Asksvin", "Asksvin" }, { "Volture", "Abutre das Cinzas" }, { "Eikthyr", "Eikthyr" }, { "gd_king", "O Ancião" }, { "Bonemass", "Massa Óssea" }, { "Dragon", "Moder" }, { "GoblinKing", "Yagluth" }, { "SeekerQueen", "Rainha Seeker" }, { "Fader", "Fader" }, { "Fish1", "Perca" }, { "Fish2", "Lúcio" }, { "Fish3", "Atum" }, { "Fish4_cave", "Tetra" }, { "Fish5", "Peixe Troll" }, { "Fish6", "Arenque gigante" }, { "Fish7", "Garoupa" }, { "Fish8", "Garoupa estrelada" }, { "Fish9", "Peixe-pescador" }, { "Fish10", "Salmão do norte" }, { "Fish11", "Peixe magma" }, { "Fish12", "Baiacu" }, { "Carrot", "Cenoura" }, { "Turnip", "Nabo" }, { "Onion", "Cebola" }, { "Barley", "Cevada" }, { "Flax", "Linho" }, { "Sap", "Seiva" }, { "JotunPuffs", "Cogumelo Jotun" }, { "Magecap", "Chapéu de Mago" }, { "Vineberry", "Baga de Videira" }, { "Fiddleheadfern", "Samambaia" }, { "Run", "Corrida" }, { "Jump", "Salto" }, { "Swords", "Espadas" }, { "Knives", "Facas" }, { "Clubs", "Porretes" }, { "Polearms", "Armas de Haste" }, { "Spears", "Lanças" }, { "Blocking", "Bloqueio" }, { "Axes", "Machados" }, { "Bows", "Arcos" }, { "Crossbows", "Bestas" }, { "ElementalMagic", "Magia Elemental" }, { "BloodMagic", "Magia de Sangue" }, { "Fishing", "Pesca" }, { "Cooking", "Culinária" }, { "Deer", "Cervo" }, { "EvilHeart_Forest", "Coração Maligno da Floresta" }, { "Ghost", "Fantasma" }, { "Wraith", "Aparição" }, { "Fenring_Cultist", "Cultista Fenring" }, { "SeekerBrood", "Cria de Seeker" }, { "DvergerMage", "Mago Dvergr" }, { "DvergerMageFire", "Mago Dvergr de Fogo" }, { "DvergerMageIce", "Mago Dvergr de Gelo" }, { "DvergerMageSupport", "Mago Dvergr de Suporte" }, { "DvergerRogue", "Ladino Dvergr" }, { "Charred_Twitcher", "Carbonizado Retorcido" }, { "Charred_Melee_Dyrnwyn", "Guerreiro Carbonizado Dyrnwyn" }, { "GoblinBruteBros", "Irmãos Berserker Fuling" }, { "GoblinBrute_Hildir", "Berserker Fuling da Hildir" }, { "GoblinShaman_Hildir", "Xamã Fuling da Hildir" }, { "Fenring_Cultist_Hildir", "Cultista Fenring da Hildir" }, { "Skeleton_Hildir", "Esqueleto da Hildir" }, { "ExploreBlackForest", "Explorador da Floresta Negra" }, { "ExploreSwamp", "Explorador do Pântano" }, { "ExploreMountain", "Explorador das Montanhas" }, { "ExplorePlains", "Explorador das Planícies" }, { "ExploreMistlands", "Explorador das Terras Nebulosas" }, { "ExploreAshlands", "Explorador das Terras das Cinzas" }, { "Kill100Enemies", "Exterminador" }, { "CraftMaster", "Mestre Artesão" }, { "Mapa 5%", "Mapa 5%" } }; private const float ActionPlayerCollapsedHeight = 58f; private const float ActionPlayerExpandedHeight = 738f; private const string HudColorPoints = "#00FF7F"; private const string HudColorDanger = "#FF5555"; private const string HudColorProgress = "#4FC3F7"; private const string HudColorHighlight = "#FFD700"; private const string HudColorMuted = "#D7DCE5"; private const string HudColorName = "#FFFFFF"; private const string HudColorSeparator = "#8F6E38"; private const float CraftRankingVerifyIntervalSeconds = 0.25f; private const float CraftRankingVerifyTimeoutSeconds = 12f; private readonly Dictionary<string, RankingEntry> _entriesByPlayerName = new Dictionary<string, RankingEntry>(StringComparer.OrdinalIgnoreCase); private List<RankingEntry> _orderedRankingCache = new List<RankingEntry>(); private Dictionary<string, int> _rankingSnapshotCache = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private bool _rankingCacheDirty = true; private readonly HashSet<string> _marketplaceQuestCreditKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _fishingCatchCreditKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, List<MarketplaceQuestCreditRecord>> _marketplaceQuestCreditsByPlayer = new Dictionary<string, List<MarketplaceQuestCreditRecord>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, List<FishingCatchCreditRecord>> _fishingCreditsByPlayer = new Dictionary<string, List<FishingCatchCreditRecord>>(StringComparer.OrdinalIgnoreCase); private void EnsureRulesFileExists() { try { if (!File.Exists(_rulesFilePath)) { File.WriteAllText(_rulesFilePath, BuildDefaultRulesFileContents(), Encoding.UTF8); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Erro ao criar arquivo de regras: " + ex)); } } private string BuildDefaultRulesFileContents() { StringBuilder stringBuilder = new StringBuilder(16384); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# GLITNIR RANKING - CONFIGURACAO DE PONTUACAO"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine("# Este arquivo controla as regras do ranking do servidor Glitnir."); stringBuilder.AppendLine("# Prefab = nome interno do item/criatura/colheita no Valheim."); stringBuilder.AppendLine("# Pontos = valor entregue ao jogador quando a regra for validada."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Organizacao principal:"); stringBuilder.AppendLine("# - KillPoints: pontos por criatura normal. Peixes NAO ficam aqui."); stringBuilder.AppendLine("# - FishingPoints: somente pontos por peixe realmente pescado."); stringBuilder.AppendLine("# - BossPoints: pontos por bosses e minibosses."); stringBuilder.AppendLine("# - CraftPoints.Rules: pontos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - FarmJackpots.Rules: jackpots de colheita, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("# - UniqueCraftJackpots.Rules: jackpots unicos por craft, em uma linha limpa separada por ponto e virgula."); stringBuilder.AppendLine("#"); stringBuilder.AppendLine("# Nao use virgulas nas listas de producao/jackpot. Use ;"); stringBuilder.AppendLine("# Exemplos:"); stringBuilder.AppendLine("# CraftPoints.Rules=MeadHealthMinor:20;ArrowNeedle:2"); stringBuilder.AppendLine("# FarmJackpots.Rules=Carrot:200:1000"); stringBuilder.AppendLine("# UniqueCraftJackpots.Rules=IronSword:1:800"); stringBuilder.AppendLine("# ======================================================================="); stringBuilder.AppendLine(); stringBuilder.AppendLine("RankingEnabled=true"); stringBuilder.AppendLine("EnableKillPoints=true"); stringBuilder.AppendLine("EnableBossPoints=true"); stringBuilder.AppendLine("DefaultKillPoints=1"); stringBuilder.AppendLine("TopCount=10"); stringBuilder.AppendLine("AllowRepeatedBossPoints=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Logging"); stringBuilder.AppendLine("DebugLogging=false"); stringBuilder.AppendLine("LogHitReports=false"); stringBuilder.AppendLine("LogKillReports=false"); stringBuilder.AppendLine("LogSkillReports=false"); stringBuilder.AppendLine("LogPendingKillReports=false"); stringBuilder.AppendLine("LogSnapshotRequests=false"); stringBuilder.AppendLine("LogPointsChanges=false"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Skill milestone jackpot points"); stringBuilder.AppendLine("EnableSkillPoints=true"); stringBuilder.AppendLine("IgnoreTamedKills=true"); stringBuilder.AppendLine("EnableMarketplaceQuestPoints=true"); stringBuilder.AppendLine("EnableFishingPoints=true"); stringBuilder.AppendLine("EnableDeathPenalty=true"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Marketplace quest points"); stringBuilder.AppendLine("[MarketplaceQuestPoints]"); stringBuilder.AppendLine("QuestPointMap=ccq_b21:450,ccq_b22:150"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Penalidade por morte"); stringBuilder.AppendLine("[DeathPenalty]"); stringBuilder.AppendLine("# UseMultiplierMode=false usa a tabela Rules abaixo."); stringBuilder.AppendLine("# UseMultiplierMode=true ignora Rules e usa PenaltyPerDeath por morte."); stringBuilder.AppendLine("# Exemplo multiplicador: PenaltyPerDeath=100 e 15 mortes = -1500."); stringBuilder.AppendLine("UseMultiplierMode=false"); stringBuilder.AppendLine("PenaltyPerDeath=100"); stringBuilder.AppendLine("# Formato do modo tabela: quantidadeDeMortes:pontosPerdidos,quantidadeDeMortes:pontosPerdidos"); stringBuilder.AppendLine("# Exemplo: 1:0,2:100,5:300,10:800"); stringBuilder.AppendLine("Rules=1:0,2:100,5:300,10:800"); stringBuilder.AppendLine(); stringBuilder.AppendLine("# Reward claims"); stringBuilder.AppendLine("RewardClaimsEnabled=true"); stringBuilder.AppendLine("RewardClaimCycleId=temporada_001"); stringBuilder.AppendLine("RewardTop1MinPoints=300"); stringBuilder.AppendLine("RewardTop1Label=Recompensa do 1 lugar"); stringBuilder.AppendLine("RewardTop1Prefab=Coins"); stringBuilder.AppendLine("RewardTop1Amount=500"); stringBuilder.AppendLine("RewardTop2MinPoints=200"); stringBuilder.AppendLine("RewardTop2Label=Recompensa do 2 lugar"); stringBuilder.AppendLine("RewardTop2Prefab=AmberPearl"); stringBuilder.AppendLine("RewardTop2Amount=10"); stringBuilder.AppendLine("RewardTop3MinPoints=100"); stringBuilder.AppendLine("RewardTop3Label=Recompensa do 3 lugar"); stringBuilder.AppendLine("RewardTop3Prefab=Ruby"); stringBuilder.AppendLine("RewardTop3Amount=5"); stringBuilder.AppendLine(); AppendBossPoints(stringBuilder); AppendKillPoints(stringBuilder); AppendFishingPoints(stringBuilder); AppendSkillMilestoneJackpotPoints(stringBuilder); AppendProductionRankingRules(stringBuilder); return stringBuilder.ToString(); } private void AppendBossPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# BOSS POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Cada boss/miniboss fica em uma chave própria."); sb.AppendLine("BossPoints.Bonemass=2200"); sb.AppendLine("BossPoints.Charred_Melee_Dyrnwyn=40"); sb.AppendLine("BossPoints.Dragon=3000"); sb.AppendLine("BossPoints.Eikthyr=1200"); sb.AppendLine("BossPoints.Fader=10000"); sb.AppendLine("BossPoints.Fenring_Cultist_Hildir=30"); sb.AppendLine("BossPoints.gd_king=4200"); sb.AppendLine("BossPoints.GoblinBruteBros=35"); sb.AppendLine("BossPoints.GoblinBrute_Hildir=18"); sb.AppendLine("BossPoints.GoblinKing=6000"); sb.AppendLine("BossPoints.GoblinShaman_Hildir=18"); sb.AppendLine("BossPoints.SeekerQueen=8600"); sb.AppendLine("BossPoints.Skeleton_Hildir=25"); sb.AppendLine(); } private void AppendPoint(StringBuilder sb, string section, string key, int points) { sb.AppendLine(section + "." + key + "=" + points); } private void AppendKillPoint(StringBuilder sb, string prefabName, int points, string comment = null) { } private void AppendKillPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# KILL POINTS / COMBATE"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por criaturas normais. Peixes nao devem ficar aqui; use [FishingPoints]."); sb.AppendLine("# Formato gerado pelo BepInEx: [KillPoints] Prefab = pontos."); sb.AppendLine("# Mantido no modo performance: o mod NAO envia RPC por hit; pontua apenas kill confirmada."); sb.AppendLine("KillPoints.Boar=1"); sb.AppendLine("KillPoints.Neck=1"); sb.AppendLine("KillPoints.Greyling=1"); sb.AppendLine("KillPoints.Greydwarf=1"); sb.AppendLine("KillPoints.Greydwarf_Elite=2"); sb.AppendLine("KillPoints.Greydwarf_Shaman=2"); sb.AppendLine("KillPoints.Skeleton=1"); sb.AppendLine("KillPoints.Troll=5"); sb.AppendLine("KillPoints.Draugr=2"); sb.AppendLine("KillPoints.Draugr_Elite=3"); sb.AppendLine("KillPoints.Blob=2"); sb.AppendLine("KillPoints.Wolf=2"); sb.AppendLine("KillPoints.Hatchling=2"); sb.AppendLine("KillPoints.StoneGolem=8"); sb.AppendLine("KillPoints.Deathsquito=3"); sb.AppendLine("KillPoints.Goblin=3"); sb.AppendLine("KillPoints.GoblinBrute=5"); sb.AppendLine("KillPoints.GoblinShaman=4"); sb.AppendLine("KillPoints.Lox=6"); sb.AppendLine("KillPoints.Serpent=6"); sb.AppendLine("KillPoints.Seeker=4"); sb.AppendLine("KillPoints.SeekerBrute=8"); sb.AppendLine("KillPoints.Gjall=10"); sb.AppendLine("KillPoints.Charred_Melee=4"); sb.AppendLine("KillPoints.Charred_Archer=4"); sb.AppendLine("KillPoints.Morgen=12"); sb.AppendLine(); } private void AppendFishingPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# FISHING POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# As chaves podem ficar em português; o código converte para o prefab real internamente."); sb.AppendLine("# Mapeamento: Perca=Fish1, Lúcio=Fish2, Fish3=Fish3, Tetra=Fish4_cave, Peixe Troll=Fish5, Arenque gigante=Fish6, Garoupa=Fish7, Garoupa estrelada=Fish8, Peixe-pescador=Fish9, Salmão do norte=Fish10, Peixe magma=Fish11, Baiacu=Fish12"); AppendFishingPoint(sb, "Perca", 1); AppendFishingPoint(sb, "Lúcio", 1); AppendFishingPoint(sb, "Fish3", 2); AppendFishingPoint(sb, "Tetra", 3); AppendFishingPoint(sb, "Peixe Troll", 2); AppendFishingPoint(sb, "Arenque gigante", 3); AppendFishingPoint(sb, "Garoupa", 3000); AppendFishingPoint(sb, "Garoupa estrelada", 4); AppendFishingPoint(sb, "Peixe-pescador", 4); AppendFishingPoint(sb, "Salmão do norte", 5); AppendFishingPoint(sb, "Peixe magma", 5); AppendFishingPoint(sb, "Baiacu", 6); sb.AppendLine(); } private void AppendFishingPoint(StringBuilder sb, string prefabName, int points) { sb.AppendLine("FishingPoints." + prefabName + "=" + points); } private void AppendSkillPoint(StringBuilder sb, string skillKey, int points, string comment = null) { if (!string.IsNullOrWhiteSpace(comment)) { sb.AppendLine(comment); } sb.AppendLine("SkillPoints." + skillKey + "=" + points); } private void AppendSkillMilestoneJackpotPoints(StringBuilder sb) { sb.AppendLine("# ========================="); sb.AppendLine("# SKILL MILESTONE JACKPOT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos únicos por marco de skill. Formato: SkillMilestoneJackpotPoints.Skill=20:pontos,40:pontos,60:pontos,80:pontos,100:pontos"); sb.AppendLine("# Exemplo: Bows 20/40/60/80/100. Cada marco pontua uma única vez por jogador/ciclo."); AppendSkillMilestoneJackpotPoint(sb, "BloodMagic", "20:1500,40:3000,60:5000,80:7500,100:12000"); AppendSkillMilestoneJackpotPoint(sb, "ElementalMagic", "20:1000,40:2000,60:3500,80:5500,100:8000"); AppendSkillMilestoneJackpotPoint(sb, "Bows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Crossbows", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Swords", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Axes", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Clubs", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Spears", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Polearms", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Knives", "20:500,40:1000,60:2000,80:3000,100:4000"); AppendSkillMilestoneJackpotPoint(sb, "Blocking", "20:400,40:800,60:1500,80:2500,100:3500"); AppendSkillMilestoneJackpotPoint(sb, "Run", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "Jump", "20:300,40:700,60:1200,80:2000,100:3000"); AppendSkillMilestoneJackpotPoint(sb, "WoodCutting", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Pickaxes", "20:300,40:700,60:1200,80:1800,100:2500"); AppendSkillMilestoneJackpotPoint(sb, "Farming", "20:0,40:0,60:0,80:0,100:0"); sb.AppendLine(); } private void AppendSkillMilestoneJackpotPoint(StringBuilder sb, string skillKey, string milestones) { sb.AppendLine("SkillMilestoneJackpotPoints." + skillKey + "=" + milestones); } private void AppendProductionRankingRules(StringBuilder sb) { sb.AppendLine("# ======================================================================="); sb.AppendLine("# PRODUCAO E JACKPOTS - GLITNIR RANKING"); sb.AppendLine("# ======================================================================="); sb.AppendLine("# Estes blocos usam UMA entrada por sistema para manter o arquivo limpo."); sb.AppendLine("# Nao use virgulas. Separe cada regra com ponto e virgula (;)."); sb.AppendLine("# Formatos:"); sb.AppendLine("# CraftPoints.Rules = Prefab:pontos;OutroPrefab:pontos"); sb.AppendLine("# FarmJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# UniqueCraftJackpots.Rules = Prefab:quantidade:pontos;OutroPrefab:quantidade:pontos"); sb.AppendLine("# Exemplos reais:"); sb.AppendLine("# MeadHealthMinor:20"); sb.AppendLine("# Carrot:200:1000"); sb.AppendLine("# IronSword:1:800"); sb.AppendLine("# ======================================================================="); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# CRAFT POINTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Pontos por craft de qualquer prefab configurado."); sb.AppendLine("# Exemplo de item: MeadHealthMinor:20"); sb.AppendLine("CraftPoints.Rules=MeadHealthMinor:20"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# FARM JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot por colheita. Premia uma vez por jogador/ciclo quando bater a quantidade."); sb.AppendLine("# Exemplo de item: Carrot:200:1000"); sb.AppendLine("FarmJackpots.Rules=Carrot:200:1000"); sb.AppendLine(); sb.AppendLine("# ========================="); sb.AppendLine("# UNIQUE CRAFT JACKPOTS"); sb.AppendLine("# ========================="); sb.AppendLine("# Jackpot unico por craft. Ideal para armas, armaduras e marcos especiais."); sb.AppendLine("# Exemplo de item: IronSword:1:800"); sb.AppendLine("UniqueCraftJackpots.Rules=IronSword:1:800"); sb.AppendLine(); } private void AppendStringPoint(StringBuilder sb, string section, string key, string value) { sb.AppendLine(section + "." + key + "=" + value); } private void LoadRules() { _rules = new RankingRules(); try { if (!File.Exists(_rulesFilePath)) { return; } string[] array = File.ReadAllLines(_rulesFilePath, Encoding.UTF8); 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."); _cfgPointsExchangeUseCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "UseCoinsPerPoint", ReadLegacyBool(legacyOverrides, "PointsExchangeUseCoinsPerPoint", rankingRules.PointsExchangeUseCoinsPerPoint), "Ativa o modo antigo: moedas por ponto. Por padrão fica false."); _cfgPointsExchangeCoinsPerPoint = _rulesConfig.BindConfig("PointsExchange", "CoinsPerPoint", ReadLegacyInt(legacyOverrides, "PointsExchangeCoinsPerPoint", rankingRules.PointsExchangeCoinsPerPoint), "Quantidade de moedas entregues por cada ponto trocado. Só é usado se UseCoinsPerPoint=true."); _cfgPointsExchangeUsePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "UsePointsPerCoin", ReadLegacyBool(legacyOverrides, "PointsExchangeUsePointsPerCoin", rankingRules.PointsExchangeUsePointsPerCoin), "Ativa o modo recomendado: pontos necessários para receber 1 moeda."); _cfgPointsExchangePointsPerCoin = _rulesConfig.BindConfig("PointsExchange", "PointsPerCoin", ReadLegacyInt(legacyOverrides, "PointsExchangePointsPerCoin", rankingRules.PointsExchangePointsPerCoin), "Quantidade de pontos necessários para receber 1 moeda. Exemplo: 100 = a cada 100 pontos, 1 coin."); _cfgPointsExchangeMinPoints = _rulesConfig.BindConfig("PointsExchange", "MinPoints", ReadLegacyInt(legacyOverrides, "PointsExchangeMinPoints", rankingRules.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); BindProductionCategoryEntries(legacyOverrides); 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."); BindUniqueCraftJackpotCategoryEntries(legacyOverrides); } } private void BindUniqueCraftJackpotCategoryEntries(Dictionary<string, string> legacyOverrides) { if (_cfgUniqueCraftJackpotCategoryEntries == null) { return; } _cfgUniqueCraftJackpotCategoryEntries.Clear(); Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { { "Armas", "SwordIron;1;800,SwordSilver;1;1500,SwordBlackmetal;1;2500" }, { "Armaduras", "ArmorWolfChest;1;2000,ArmorCarapaceChest;1;3500" }, { "Outros", "" } }; string text = ReadLegacyString(legacyOverrides, "UniqueCraftJackpots.Rules", null); if (!string.IsNullOrWhiteSpace(text)) { foreach (KeyValuePair<string, string> item in SplitUniqueCraftJackpotsIntoCategories(text)) { dictionary[item.Key] = MergeCommaRules(dictionary.ContainsKey(item.Key) ? dictionary[item.Key] : string.Empty, item.Value); } } if (legacyOverrides != null) { foreach (KeyValuePair<string, string> legacyOverride in legacyOverrides) { if (string.IsNullOrWhiteSpace(legacyOverride.Key) || !legacyOverride.Key.StartsWith("UniqueCraftJackpots.", StringComparison.OrdinalIgnoreCase)) { continue; } string text2 = NormalizeHudCategoryName(legacyOverride.Key.Substring("UniqueCraftJackpots.".Length)); if (!string.IsNullOrWhiteSpace(text2) && !text2.Equals("Rules", StringComparison.OrdinalIgnoreCase)) { string value = ConvertUniqueCraftJackpotItemsToConfigValue(legacyOverride.Value); if (!string.IsNullOrWhiteSpace(value) || !dictionary.ContainsKey(text2)) { dictionary[text2] = value; } } } } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } foreach (string item2 in GetPreferredCategoryOrder(dictionary, new string[5] { "Armas", "Armaduras", "Ferramentas", "Comidas", "Outros" })) { BindSingleUniqueCraftJackpotCategory(item2, legacyOverrides, dictionary[item2]); } } private void BindSingleUniqueCraftJackpotCategory(string category, Dictionary<string, string> legacyOverrides, string defaultValue) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { string raw = ReadLegacyString(legacyOverrides, "UniqueCraftJackpots." + category, defaultValue); raw = ConvertUniqueCraftJackpotItemsToConfigValue(raw); ConfigEntry<string> value = _rulesConfig.BindConfig("UniqueCraftJackpots", category, raw, "Jackpot único por craft para esta categoria. Formato: Prefab;Quantidade;Pontos,OutroPrefab;Quantidade;Pontos. Exemplo: SwordIron;1;800,ArmorWolfChest;1;2000. Aceita qualquer prefab, inclusive de mods. O nome desta chave vira a categoria da config."); _cfgUniqueCraftJackpotCategoryEntries[category] = value; } } private Dictionary<string, string> SplitUniqueCraftJackpotsIntoCategories(string raw) { Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase) { { "Armas", new List<string>() }, { "Armaduras", new List<string>() }, { "Outros", new List<string>() } }; foreach (KeyValuePair<string, JackpotRule> item in ParseFlexibleJackpotRules(raw)) { string key = GuessUniqueCraftJackpotCategory(item.Key); if (!dictionary.ContainsKey(key)) { dictionary[key] = new List<string>(); } JackpotRule value = item.Value; dictionary[key].Add(item.Key + ";" + Mathf.Max(1, value.RequiredAmount) + ";" + Mathf.Max(0, value.Points)); } Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, List<string>> item2 in dictionary) { dictionary2[item2.Key] = string.Join(",", item2.Value.ToArray()); } return dictionary2; } private string GuessUniqueCraftJackpotCategory(string prefab) { string text = SafeKey(prefab); if (string.IsNullOrWhiteSpace(text)) { return "Outros"; } if (text.StartsWith("Sword", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Axe", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Mace", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Knife", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Atgeir", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Spear", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Bow", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Crossbow", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Shield", StringComparison.OrdinalIgnoreCase)) { return "Armas"; } if (text.StartsWith("Armor", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Helmet", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Cape", StringComparison.OrdinalIgnoreCase)) { return "Armaduras"; } return "Outros"; } private string MergeCommaRules(string current, string extra) { current = ((current != null) ? current.Trim() : string.Empty); extra = ((extra != null) ? extra.Trim() : string.Empty); if (string.IsNullOrWhiteSpace(current)) { return extra; } if (string.IsNullOrWhiteSpace(extra)) { return current; } return current + "," + extra; } private string BuildUniqueCraftJackpotRulesFromCategoryEntries() { if (_cfgUniqueCraftJackpotCategoryEntries == null || _cfgUniqueCraftJackpotCategoryEntries.Count == 0) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, ConfigEntry<string>> cfgUniqueCraftJackpotCategoryEntry in _cfgUniqueCraftJackpotCategoryEntries) { if (cfgUniqueCraftJackpotCategoryEntry.Value == null) { continue; } foreach (KeyValuePair<string, JackpotRule> item in ParseFlexibleJackpotRules(cfgUniqueCraftJackpotCategoryEntry.Value.Value)) { if (stringBuilder.Length > 0) { stringBuilder.Append(';'); } JackpotRule value = item.Value; stringBuilder.Append(SafeKey(item.Key)); stringBuilder.Append(':'); stringBuilder.Append(Mathf.Max(1, value.RequiredAmount)); stringBuilder.Append(':'); stringBuilder.Append(Mathf.Max(0, value.Points)); } } return stringBuilder.ToString(); } private string ConvertUniqueCraftJackpotItemsToConfigValue(string raw) { List<string> list = new List<string>(); foreach (KeyValuePair<string, JackpotRule> item in ParseFlexibleJackpotRules(raw)) { JackpotRule value = item.Value; list.Add(SafeKey(item.Key) + ";" + Mathf.Max(1, value.RequiredAmount) + ";" + Mathf.Max(0, value.Points)); } return string.Join(",", list.ToArray()); } private Dictionary<string, JackpotRule> ParseFlexibleJackpotRules(string raw) { Dictionary<string, JackpotRule> dictionary = new Dictionary<string, JackpotRule>(StringComparer.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(raw)) { return dictionary; } string text = raw.Replace("\r", "\n").Replace("|", ","); List<string> list = new List<string>(); if (text.IndexOf(',') >= 0 || text.IndexOf('\n') >= 0) { string[] array = text.Split(new char[2] { ',', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { string text3 = ((text2 != null) ? text2.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text3)) { list.Add(text3); } } } else if (text.IndexOf(':') >= 0) { string[] array2 = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text4 in array2) { string text5 = ((text4 != null) ? text4.Trim() : string.Empty); if (!string.IsNullOrWhiteSpace(text5)) { list.Add(text5); } } } else { string[] array3 = text.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int k = 0; k + 2 < array3.Length; k += 3) { list.Add(array3[k].Trim() + ";" + array3[k + 1].Trim() + ";" + array3[k + 2].Trim()); } } foreach (string item in list) { if (TryParseFlexibleJackpotItem(item, out var prefab, out var amount, out var points)) { dictionary[prefab] = new JackpotRule { RequiredAmount = Mathf.Max(1, amount), Points = Mathf.Max(0, points) }; } } return dictionary; } private bool TryParseFlexibleJackpotItem(string entry, out string prefab, out int amount, out int points) { prefab = string.Empty; amount = 0; points = 0; if (string.IsNullOrWhiteSpace(entry)) { return false; } string[] array = ((entry.IndexOf(';') >= 0) ? entry.Split(new char[1] { ';' }, 3, StringSplitOptions.None) : entry.Split(new char[1] { ':' }, 3, StringSplitOptions.None)); if (array.Length != 3) { return false; } prefab = SafeKey(array[0]); if (string.IsNullOrWhiteSpace(prefab)) { return false; } if (!int.TryParse(array[1].Trim(), out amount)) { return false; } if (!int.TryParse(array[2].Trim(), out points)) { return false; } amount = Mathf.Max(1, amount); points = Mathf.Max(0, points); return true; } private void BindProductionCategoryEntries(Dictionary<string, string> legacyOverrides) { if (_cfgProductionCategoryRuleEntries == null) { return; } _cfgProductionCategoryRuleEntries.Clear(); Dictionary<string, string> dictionary = ParseProductionCategoryDefaults("Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"); string text = ReadLegacyString(legacyOverrides, "HudCategories.Production", null); if (!string.IsNullOrWhiteSpace(text)) { foreach (KeyValuePair<string, string> item in ParseProductionCategoryDefaults(text)) { dictionary[item.Key] = item.Value; } } if (legacyOverrides != null) { foreach (KeyValuePair<string, string> legacyOverride in legacyOverrides) { if (string.IsNullOrWhiteSpace(legacyOverride.Key) || !legacyOverride.Key.StartsWith("HudCategories.", StringComparison.OrdinalIgnoreCase)) { continue; } string text2 = NormalizeHudCategoryName(legacyOverride.Key.Substring("HudCategories.".Length)); if (!string.IsNullOrWhiteSpace(text2) && !text2.Equals("Production", StringComparison.OrdinalIgnoreCase) && !text2.Equals("Combat", StringComparison.OrdinalIgnoreCase)) { string value = ConvertProductionCategoryItemsToConfigValue(legacyOverride.Value); if (!string.IsNullOrWhiteSpace(value)) { dictionary[text2] = value; } } } } if (dictionary.Count <= 0) { dictionary["Armas"] = "SwordIron;800"; dictionary["Armaduras"] = "HelmetBronze;800"; dictionary["Comidas"] = "DeerStew;25"; } if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } foreach (string item2 in GetPreferredCategoryOrder(dictionary, new string[6] { "Armas", "Armaduras", "Comidas", "Ferramentas", "Materiais", "Outros" })) { BindSingleProductionCategory(item2, legacyOverrides, dictionary[item2]); } } private void BindSingleProductionCategory(string category, Dictionary<string, string> legacyOverrides, string defaultValue) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { string raw = ReadLegacyString(legacyOverrides, "HudCategories." + category, defaultValue); raw = ConvertProductionCategoryItemsToConfigValue(raw); ConfigEntry<string> val = _rulesConfig.BindConfig("HudCategories", category, raw, "Produção/Conquistas para esta aba do HUD. Formato: Prefab;Pontos,OutroPrefab;Pontos. Exemplo: SwordIron;800,HelmetBronze;800,DeerStew;25. Aceita prefabs de mods. O nome desta chave vira a categoria no HUD."); _cfgProductionCategoryRuleEntries[category] = val; if (_cfgProductionCategoryRules == null) { _cfgProductionCategoryRules = val; } } } private Dictionary<string, string> ParseProductionCategoryDefaults(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)) { string value = ConvertProductionCategoryItemsToConfigValue(text4); if (!string.IsNullOrWhiteSpace(value)) { dictionary[text3] = value; } } } return dictionary; } private string BuildProductionCategoryRules() { if (_cfgProductionCategoryRuleEntries == null || _cfgProductionCategoryRuleEntries.Count == 0) { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } try { List<string> list = new List<string>(); foreach (KeyValuePair<string, ConfigEntry<string>> cfgProductionCategoryRuleEntry in _cfgProductionCategoryRuleEntries) { if (cfgProductionCategoryRuleEntry.Value == null) { continue; } string text = NormalizeHudCategoryName(cfgProductionCategoryRuleEntry.Key); if (!string.IsNullOrWhiteSpace(text)) { string text2 = ConvertProductionCategoryItemsToHudCategoryItems(cfgProductionCategoryRuleEntry.Value.Value); if (!string.IsNullOrWhiteSpace(text2)) { list.Add(text + ":" + text2); } } } if (list.Count <= 0) { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } return string.Join(";", list.ToArray()); } catch { return "Armas:SwordIron=800;Armaduras:HelmetBronze=800;Comidas:DeerStew=25"; } } private string ConvertProductionCategoryItemsToConfigValue(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 string ConvertProductionCategoryItemsToHudCategoryItems(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 void BindCombatBiomeRuleEntries(Dictionary<string, string> legacyOverrides) { if (_cfgCombatBiomeRuleEntries == null) { return; } _cfgCombatBiomeRuleEntries.Clear(); Dictionary<string, string> dictionary = 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"); if (!dictionary.ContainsKey("Outros")) { dictionary["Outros"] = string.Empty; } if (legacyOverrides != null) { foreach (KeyValuePair<string, string> legacyOverride in legacyOverrides) { if (!string.IsNullOrWhiteSpace(legacyOverride.Key) && legacyOverride.Key.StartsWith("Combat.", StringComparison.OrdinalIgnoreCase)) { string text = NormalizeHudCategoryName(legacyOverride.Key.Substring("Combat.".Length)); if (!string.IsNullOrWhiteSpace(text)) { string value = ConvertProductionCategoryItemsToConfigValue(legacyOverride.Value); dictionary[text] = value; } } } } foreach (string item in GetPreferredCategoryOrder(dictionary, new string[10] { "Prados", "Floresta Negra", "Pântano", "Montanha", "Planícies", "Oceano", "Mistlands", "Ashlands", "Especiais", "Outros" })) { BindSingleCombatBiome(item, dictionary, legacyOverrides); } } private void BindSingleCombatBiome(string category, Dictionary<string, string> defaults, Dictionary<string, string> legacyOverrides) { category = NormalizeHudCategoryName(category); if (!string.IsNullOrWhiteSpace(category)) { if (defaults == null || !defaults.TryGetValue(category, out var value)) { value = string.Empty; } string raw = ReadLegacyString(legacyOverrides, "Combat." + category, value); raw = ConvertProductionCategoryItemsToConfigValue(raw); _cfgCombatBiomeRuleEntries[category] = _rulesConfig.BindConfig("Combat", category, raw, "Mobs e pontos para esta aba do HUD. Formato: Prefab;Pontos,OutroPrefab;Pontos. Exemplo: Boar;2,Deer;3. Aceita qualquer quantidade de prefabs, inclusive de mods. O nome desta chave vira a categoria no HUD."); } } [IteratorStateMachine(typeof(<GetPreferredCategoryOrder>d__45))] private IEnumerable<string> GetPreferredCategoryOrder(Dictionary<string, string> categories, string[] preferredOrder) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <GetPreferredCategoryOrder>d__45(-2) { <>4__this = this, <>3__categories = cate