Decompiled source of CarlosMMOUnderground v1.1.3

plugins/CarlosMMOUnderground.dll

Decompiled a month ago
using 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);
		}
	}
}