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 CarlosMMOUnderground v1.1.3
plugins/CarlosMMOUnderground.dll
Decompiled a month agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("CarlosMMOUnderground")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("CarlosMMOUnderground")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("2d4c38b1-694e-417f-b093-13494f2d9fe5")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace CarlosMMO; [BepInPlugin("carlos.mmo.underground", "CarlosMMOUnderground", "1.1.1")] public class CarlosMMOUnderground : BaseUnityPlugin { public const string ModGuid = "carlos.mmo.underground"; public const string ModName = "CarlosMMOUnderground"; public const string ModVersion = "1.1.1"; internal static ManualLogSource Log; internal static Harmony Harmony; internal static ConfigEntry<bool> ModEnabled; internal static ConfigEntry<bool> ForceAllowPiecesInDungeons; internal static ConfigEntry<bool> IgnoreNoBuildChecks; internal static ConfigEntry<bool> VerboseLogging; internal static ConfigEntry<bool> LogDungeonEntry; internal static ConfigEntry<bool> LogBuildUnlock; internal static ConfigEntry<bool> LogPieceAllowance; internal static ConfigEntry<bool> EnableDungeonClaim; internal static ConfigEntry<bool> LogDungeonClaim; internal static ConfigEntry<string> ClaimedDungeonsData; private static bool _lastDungeonState = false; private static float _nextDungeonCheckTime = 0f; private static string _lastDungeonType = "Unknown"; private static readonly HashSet<string> _loggedPieces = new HashSet<string>(); private static readonly HashSet<string> _claimedDungeons = new HashSet<string>(); private static readonly HashSet<string> _loggedKnownClaims = new HashSet<string>(); private static float _nextClaimEntryLogTime = 0f; private static bool _pendingClaim = false; private static float _pendingClaimExpireTime = 0f; private static string _pendingPieceName = "UnknownPiece"; private void Awake() { //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Habilita o mod."); ForceAllowPiecesInDungeons = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ForceAllowPiecesInDungeons", true, "Força peças a funcionarem em dungeon/interior."); IgnoreNoBuildChecks = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "IgnoreNoBuildChecks", true, "Ignora bloqueios de construção em dungeon/interior."); EnableDungeonClaim = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Claim", "EnableDungeonClaim", true, "Ativa o sistema simples de claim de dungeon."); ClaimedDungeonsData = ((BaseUnityPlugin)this).Config.Bind<string>("1 - Claim", "ClaimedDungeonsData", "", "Dados serializados das dungeons claimadas. Não editar manualmente."); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Debug", "VerboseLogging", true, "Logs detalhados gerais."); LogDungeonEntry = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Debug", "LogDungeonEntry", true, "Mostra log ao entrar/sair de dungeon ou interior."); LogBuildUnlock = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Debug", "LogBuildUnlock", true, "Mostra log quando o bloqueio de construção é ignorado."); LogPieceAllowance = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Debug", "LogPieceAllowance", false, "Mostra log quando uma peça é liberada para dungeon. Pode gerar muito log."); LogDungeonClaim = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Debug", "LogDungeonClaim", true, "Mostra log quando uma dungeon é claimada ou reencontrada."); LoadClaims(); Harmony = new Harmony("carlos.mmo.underground"); try { Harmony.PatchAll(typeof(CarlosMMOUnderground).Assembly); PatchOptionalTargets(Harmony); Log.LogInfo((object)"CarlosMMOUnderground 1.1.1 carregado."); Log.LogInfo((object)"Modo atual: Dungeon Livre + Claim simples com confirmação atrasada."); Log.LogInfo((object)("Claims carregados: " + _claimedDungeons.Count)); } catch (Exception ex) { Log.LogError((object)"Erro ao aplicar patches:"); Log.LogError((object)ex); } } private void Update() { if (IsEnabled()) { TryTrackDungeonState(); TryResolvePendingClaim(); } } private static void PatchOptionalTargets(Harmony harmony) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown TryPatchAllMethodsNamed(harmony, typeof(Player), "PlacePiece", null, new HarmonyMethod(typeof(PlayerPlacePieceClaimPatch).GetMethod("Postfix", BindingFlags.Static | BindingFlags.Public))); } private static void TryPatchAllMethodsNamed(Harmony harmony, Type type, string methodName, HarmonyMethod prefix = null, HarmonyMethod postfix = null) { try { List<MethodInfo> declaredMethods = AccessTools.GetDeclaredMethods(type); int num = 0; for (int i = 0; i < declaredMethods.Count; i++) { MethodInfo methodInfo = declaredMethods[i]; if (methodInfo == null || methodInfo.Name != methodName) { continue; } try { harmony.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; if (VerboseLogging.Value) { Log.LogInfo((object)$"Patch: {type.Name}.{methodInfo.Name}"); } } catch (Exception ex) { Log.LogWarning((object)$"Falha patch {type.Name}.{methodInfo.Name}: {ex.Message}"); } } if (VerboseLogging.Value) { Log.LogInfo((object)$"Total patch {type.Name}.{methodName}: {num}"); } } catch (Exception ex2) { Log.LogWarning((object)$"Erro ao buscar métodos {type.Name}: {ex2.Message}"); } } internal static bool IsEnabled() { return ModEnabled != null && ModEnabled.Value; } internal static bool ShouldIgnoreNoBuild() { return IsEnabled() && IgnoreNoBuildChecks != null && IgnoreNoBuildChecks.Value; } internal static bool ShouldForcePieces() { return IsEnabled() && ForceAllowPiecesInDungeons != null && ForceAllowPiecesInDungeons.Value; } internal static bool ShouldUseClaim() { return IsEnabled() && EnableDungeonClaim != null && EnableDungeonClaim.Value; } internal static bool IsInDungeon() { string dungeonType; return DetectDungeonState(out dungeonType); } internal static bool DetectDungeonState(out string dungeonType) { dungeonType = "Unknown"; try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } MethodInfo method = ((object)localPlayer).GetType().GetMethod("InInterior", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.Invoke(localPlayer, null) is int num && num != 0) { dungeonType = DetectCurrentDungeonType(localPlayer); return true; } MethodInfo method2 = ((object)localPlayer).GetType().GetMethod("IsInsideDungeon", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { object obj = method2.Invoke(localPlayer, null); if (obj is bool && (bool)obj) { dungeonType = DetectCurrentDungeonType(localPlayer); return true; } } } catch (Exception ex) { if (VerboseLogging.Value) { Log.LogWarning((object)("Falha ao detectar dungeon/interior: " + ex.Message)); } } return false; } private static string DetectCurrentDungeonType(Player player) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)player == (Object)null) { return "Unknown"; } string text = string.Empty; if ((Object)(object)((Component)player).gameObject != (Object)null) { Scene scene = ((Component)player).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { scene = ((Component)player).gameObject.scene; text = ((Scene)(ref scene)).name; } } string text2 = (string.IsNullOrEmpty(text) ? string.Empty : text.ToLowerInvariant()); if (text2.Contains("troll")) { return "TrollCave"; } if (text2.Contains("crypt")) { return "Crypt"; } if (text2.Contains("sunken")) { return "SunkenCrypt"; } if (text2.Contains("burial")) { return "BurialChamber"; } if (text2.Contains("cave")) { return "Cave"; } if (!string.IsNullOrEmpty(text)) { return text; } } catch { } return "InteriorOrDungeon"; } private void TryTrackDungeonState() { if (Time.time < _nextDungeonCheckTime) { return; } _nextDungeonCheckTime = Time.time + 1f; string dungeonType; bool flag = DetectDungeonState(out dungeonType); if (flag != _lastDungeonState || dungeonType != _lastDungeonType) { if (LogDungeonEntry.Value) { if (flag) { Log.LogInfo((object)("[DungeonLivre] Jogador entrou em dungeon/interior: " + dungeonType)); } else { Log.LogInfo((object)"[DungeonLivre] Jogador saiu de dungeon/interior."); } } _lastDungeonState = flag; _lastDungeonType = dungeonType; } if (!flag || !ShouldUseClaim() || !(Time.time >= _nextClaimEntryLogTime)) { return; } _nextClaimEntryLogTime = Time.time + 5f; string text = BuildCurrentDungeonClaimKey(); if (!string.IsNullOrEmpty(text) && _claimedDungeons.Contains(text) && !_loggedKnownClaims.Contains(text)) { _loggedKnownClaims.Add(text); if (LogDungeonClaim.Value) { Log.LogInfo((object)("[DungeonLivre] Dungeon já claimada detectada: " + text)); } } } private void TryResolvePendingClaim() { if (!_pendingClaim) { return; } if (!ShouldUseClaim()) { _pendingClaim = false; } else if (Time.time > _pendingClaimExpireTime) { if (VerboseLogging.Value) { Log.LogInfo((object)"[DungeonLivre] Claim pendente expirou sem confirmação."); } _pendingClaim = false; } else if (IsInDungeon()) { TryClaimCurrentDungeon(_pendingPieceName); _pendingClaim = false; } } internal static void QueuePendingClaim(string pieceName) { if (ShouldUseClaim()) { _pendingClaim = true; _pendingClaimExpireTime = Time.time + 3f; _pendingPieceName = (string.IsNullOrEmpty(pieceName) ? "UnknownPiece" : pieceName); if (VerboseLogging.Value) { Log.LogInfo((object)("[DungeonLivre] Claim pendente armado para peça: " + _pendingPieceName)); } } } internal static void ForcePiece(Piece piece) { if ((Object)(object)piece == (Object)null || !ShouldForcePieces()) { return; } try { FieldInfo fieldInfo = AccessTools.Field(((object)piece).GetType(), "m_allowedInDungeons"); if (fieldInfo == null) { return; } if (!(fieldInfo.GetValue(piece) is int num) || num == 0) { fieldInfo.SetValue(piece, true); } if (LogPieceAllowance.Value) { string name = ((Object)piece).name; if (!string.IsNullOrEmpty(name) && !_loggedPieces.Contains(name)) { _loggedPieces.Add(name); Log.LogInfo((object)("[DungeonLivre] Peça liberada para dungeon: " + name)); } } } catch (Exception ex) { if (VerboseLogging.Value) { Log.LogWarning((object)("Falha ao liberar peça em dungeon: " + ex.Message)); } } } internal static void FixGhost(Player player) { if ((Object)(object)player == (Object)null || !ShouldForcePieces()) { return; } try { FieldInfo fieldInfo = AccessTools.Field(((object)player).GetType(), "m_placementGhost"); if (fieldInfo == null) { return; } object? value = fieldInfo.GetValue(player); GameObject val = (GameObject)((value is GameObject) ? value : null); if (!((Object)(object)val == (Object)null)) { Piece component = val.GetComponent<Piece>(); if ((Object)(object)component != (Object)null) { ForcePiece(component); } } } catch (Exception ex) { if (VerboseLogging.Value) { Log.LogWarning((object)("Falha ao ajustar placement ghost: " + ex.Message)); } } } internal static void LogBuildUnlockOnce(string sourceName) { if (IsEnabled() && LogBuildUnlock != null && LogBuildUnlock.Value && IsInDungeon()) { Log.LogInfo((object)("[DungeonLivre] Bloqueio de construção ignorado em dungeon/interior via " + sourceName + ".")); } } internal static void TryClaimCurrentDungeon(string pieceName) { if (!ShouldUseClaim() || !IsInDungeon()) { return; } string text = BuildCurrentDungeonClaimKey(); if (!string.IsNullOrEmpty(text) && !_claimedDungeons.Contains(text)) { _claimedDungeons.Add(text); SaveClaims(); if (LogDungeonClaim.Value) { Log.LogInfo((object)("[DungeonLivre] Dungeon claimada com sucesso: " + text + " | peça inicial: " + pieceName)); } } } private static string BuildCurrentDungeonClaimKey() { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return string.Empty; } if (!DetectDungeonState(out var dungeonType)) { return string.Empty; } Vector3 position = ((Component)localPlayer).transform.position; int num = Mathf.RoundToInt(position.x / 10f); int num2 = Mathf.RoundToInt(position.y / 10f); int num3 = Mathf.RoundToInt(position.z / 10f); string worldNameSafe = GetWorldNameSafe(); return $"{worldNameSafe}|{dungeonType}|{num.ToString(CultureInfo.InvariantCulture)}|{num2.ToString(CultureInfo.InvariantCulture)}|{num3.ToString(CultureInfo.InvariantCulture)}"; } catch (Exception ex) { if (VerboseLogging.Value) { Log.LogWarning((object)("Falha ao montar claim key: " + ex.Message)); } } return string.Empty; } private static string GetWorldNameSafe() { try { if ((Object)(object)ZNet.instance != (Object)null) { MethodInfo methodInfo = AccessTools.Method(typeof(ZNet), "GetWorldName", (Type[])null, (Type[])null); if (methodInfo != null) { object obj = methodInfo.Invoke(ZNet.instance, null); if (obj is string && !string.IsNullOrEmpty((string)obj)) { return (string)obj; } } } } catch { } return "UnknownWorld"; } private void LoadClaims() { _claimedDungeons.Clear(); try { string value = ClaimedDungeonsData.Value; if (string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new string[1] { ";;" }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrEmpty(text)) { _claimedDungeons.Add(text); } } } catch (Exception ex) { Log.LogWarning((object)("Falha ao carregar claims: " + ex.Message)); } } private static void SaveClaims() { try { if (ClaimedDungeonsData != null) { string[] array = new string[_claimedDungeons.Count]; _claimedDungeons.CopyTo(array); ClaimedDungeonsData.Value = string.Join(";;", array); } } catch (Exception ex) { Log.LogWarning((object)("Falha ao salvar claims: " + ex.Message)); } } } [HarmonyPatch(typeof(Piece), "Awake")] public static class PiecePatch { public static void Postfix(Piece __instance) { if (CarlosMMOUnderground.IsEnabled()) { CarlosMMOUnderground.ForcePiece(__instance); } } } public static class PlayerPlacePieceClaimPatch { public static void Postfix(Player __instance, Piece piece, Vector3 pos, Quaternion rot, bool doAttack) { if (CarlosMMOUnderground.IsEnabled() && CarlosMMOUnderground.ShouldUseClaim()) { string pieceName = (((Object)(object)piece != (Object)null) ? ((Object)piece).name : "UnknownPiece"); CarlosMMOUnderground.QueuePendingClaim(pieceName); } } }