Decompiled source of RepoAmigos v1.5.0

RepoAmigos.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using RepoAmigos.Patches;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Utilities;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RepoAmigos")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.0.0")]
[assembly: AssemblyInformationalVersion("1.5.0+e3104b6eec231ad272c5f572b8d3df08ee8dbf9b")]
[assembly: AssemblyProduct("RepoAmigos")]
[assembly: AssemblyTitle("RepoAmigos")]
[assembly: AssemblyVersion("1.5.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RepoAmigos
{
	[BepInPlugin("com.usuario.repoamigos", "RepoAmigos", "1.5.0")]
	[BepInProcess("REPO.exe")]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.usuario.repoamigos";

		public const string PluginName = "RepoAmigos";

		public const string PluginVersion = "1.5.0";

		private Harmony _harmony;

		internal static ConfigEntry<bool> VerboseLogging;

		internal static ManualLogSource Log { get; private set; }

		internal static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			ExtractionOnDemand.BindConfig(((BaseUnityPlugin)this).Config);
			ReviveEnExtractor.BindConfig(((BaseUnityPlugin)this).Config);
			GeneradorDePruebas.BindConfig(((BaseUnityPlugin)this).Config);
			CarritoEncoge.BindConfig(((BaseUnityPlugin)this).Config);
			ComprobacionDeVersion.BindConfig(((BaseUnityPlugin)this).Config);
			Roles.BindConfig(((BaseUnityPlugin)this).Config);
			RolMedico.BindConfig(((BaseUnityPlugin)this).Config);
			RolIngeniero.BindConfig(((BaseUnityPlugin)this).Config);
			RolSaboteador.BindConfig(((BaseUnityPlugin)this).Config);
			RolRastreador.BindConfig(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.usuario.repoamigos");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			Log.LogInfo((object)("RepoAmigos v1.5.0 cargado. " + $"Metodos parcheados: {_harmony.GetPatchedMethods().Count()}"));
			Log.LogInfo((object)("Extraccion manual: " + (ExtractionOnDemand.Enabled.Value ? "ACTIVADA" : "desactivada")));
			Log.LogInfo((object)("Revivir en extractor: " + (ReviveEnExtractor.Enabled.Value ? $"ACTIVADO ({ReviveEnExtractor.Segundos.Value}s)" : "desactivado")));
			Log.LogInfo((object)($"Pruebas: Activado={GeneradorDePruebas.Enabled.Value}, " + $"Tecla={GeneradorDePruebas.Tecla.Value}, " + $"Cantidad={GeneradorDePruebas.Cantidad.Value}"));
			Log.LogInfo((object)(Roles.Enabled.Value ? ($"Roles: ACTIVADOS  (minimo {Roles.MinJugadores.Value} jugadores)  " + $"medico[{RolMedico.Tecla.Value}]={RolMedico.Enabled.Value} " + $"ingeniero={RolIngeniero.Enabled.Value} " + $"saboteador[{RolSaboteador.Tecla.Value}]={RolSaboteador.Enabled.Value} " + $"rastreador={RolRastreador.Enabled.Value}") : "Roles: desactivados"));
		}

		private void Update()
		{
			GeneradorDePruebas.Tick("Plugin");
		}

		private void OnDestroy()
		{
			Log.LogWarning((object)"OnDestroy: el componente del plugin se ha destruido. Los parches Harmony se mantienen a proposito.");
			ExtractionOnDemand.DesconectarRed();
			Roles.DesconectarRed();
		}

		private void BindConfig()
		{
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogDetallado", false, "Escribe informacion extra en la consola de BepInEx. Util para depurar, ruidoso para jugar.");
		}

		internal static void Debug(string mensaje)
		{
			if (VerboseLogging != null && VerboseLogging.Value)
			{
				Log.LogInfo((object)("[debug] " + mensaje));
			}
		}
	}
}
namespace RepoAmigos.Patches
{
	internal static class CarritoEncoge
	{
		private sealed class Encogido
		{
			public Vector3 TamanoOriginal;

			public float VistoPorUltimaVez;
		}

		[HarmonyPatch(typeof(PhysGrabCart), "ObjectsInCart")]
		private static class ReconciliarContenido
		{
			private static void Prefix(out float __state, float ___objectInCartCheckTimer)
			{
				__state = ___objectInCartCheckTimer;
			}

			private static void Postfix(float __state, float ___objectInCartCheckTimer, List<PhysGrabObject> ___itemsInCart)
			{
				if (Enabled == null || !Enabled.Value || ___objectInCartCheckTimer <= __state)
				{
					return;
				}
				if (___itemsInCart != null)
				{
					foreach (PhysGrabObject item in ___itemsInCart)
					{
						Marcar(item);
					}
				}
				DevolverALosQueSalieron();
			}
		}

		[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
		private static class SoltarTodoAlCambiarDeNivel
		{
			private static void Prefix()
			{
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				if (_dentro.Count == 0)
				{
					return;
				}
				int num = 0;
				foreach (KeyValuePair<PhysGrabObject, Encogido> item in _dentro)
				{
					if (!((Object)(object)item.Key == (Object)null))
					{
						((Component)item.Key).transform.localScale = item.Value.TamanoOriginal;
						num++;
					}
				}
				Plugin.Debug($"Carrito: cambio de nivel, {num} objetos devueltos a su tamano.");
				_dentro.Clear();
			}
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> Escala;

		internal static ConfigEntry<bool> SoloValiosos;

		private static readonly Dictionary<PhysGrabObject, Encogido> _dentro = new Dictionary<PhysGrabObject, Encogido>();

		private const float MargenSalida = 1.6f;

		private static readonly FieldInfo _campoValioso = AccessTools.Field(typeof(PhysGrabObject), "isValuable");

		private static readonly FieldInfo _campoEnemigo = AccessTools.Field(typeof(PhysGrabObject), "isEnemy");

		private static readonly FieldInfo _campoJugador = AccessTools.Field(typeof(PhysGrabObject), "isPlayer");

		internal static void BindConfig(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Carrito", "Activado", true, "Encoge los objetos mientras estan dentro del C.A.R.T. y les devuelve su tamano al sacarlos.");
			Escala = config.Bind<float>("Carrito", "EscalaDentro", 0.75f, new ConfigDescription("Tamano dentro del carrito respecto al original. 0.75 los deja al 75%; 0.25 seria encogerlos un 75%.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 1f), Array.Empty<object>()));
			SoloValiosos = config.Bind<bool>("Carrito", "SoloValiosos", true, "true: solo se encoge el botin, que es lo que de verdad ocupa sitio. false: tambien objetos normales. Aun en false hay cosas que NUNCA se tocan (armas equipables, jugadores, enemigos, puertas y bisagras), porque encogerlas rompia el nivel o el propio objeto.");
		}

		private static bool LaBanderaDice(FieldInfo campo, PhysGrabObject objeto, bool siNoSePuede)
		{
			if (campo == null)
			{
				return siNoSePuede;
			}
			object value = campo.GetValue(objeto);
			if (!(value is bool))
			{
				return siNoSePuede;
			}
			return (bool)value;
		}

		private static bool SePuedeEncoger(PhysGrabObject objeto)
		{
			if ((Object)(object)objeto == (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)objeto).GetComponent<PhysGrabCart>() != (Object)null)
			{
				return false;
			}
			if (LaBanderaDice(_campoEnemigo, objeto, siNoSePuede: true))
			{
				return false;
			}
			if (LaBanderaDice(_campoJugador, objeto, siNoSePuede: true))
			{
				return false;
			}
			if ((Object)(object)((Component)objeto).GetComponentInChildren<ItemEquippable>(true) != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)objeto).GetComponentInChildren<PlayerDeathHead>(true) != (Object)null)
			{
				return false;
			}
			if ((Object)(object)((Component)objeto).GetComponentInChildren<PhysGrabHinge>(true) != (Object)null)
			{
				return false;
			}
			if (SoloValiosos.Value && !LaBanderaDice(_campoValioso, objeto, siNoSePuede: false))
			{
				return false;
			}
			return true;
		}

		private static void Marcar(PhysGrabObject objeto)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)objeto == (Object)null)
			{
				return;
			}
			if (_dentro.TryGetValue(objeto, out var value))
			{
				value.VistoPorUltimaVez = Time.time;
			}
			else if (SePuedeEncoger(objeto))
			{
				Vector3 localScale = ((Component)objeto).transform.localScale;
				if (!(((Vector3)(ref localScale)).sqrMagnitude < 0.01f))
				{
					_dentro[objeto] = new Encogido
					{
						TamanoOriginal = localScale,
						VistoPorUltimaVez = Time.time
					};
					((Component)objeto).transform.localScale = localScale * Escala.Value;
					Plugin.Debug($"Carrito: '{((Object)objeto).name}' encogido al {Escala.Value:P0}.");
				}
			}
		}

		private static void DevolverALosQueSalieron()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			if (_dentro.Count == 0)
			{
				return;
			}
			List<PhysGrabObject> list = null;
			foreach (KeyValuePair<PhysGrabObject, Encogido> item in _dentro)
			{
				if ((Object)(object)item.Key == (Object)null)
				{
					(list ?? (list = new List<PhysGrabObject>())).Add(item.Key);
				}
				else if (!(Time.time - item.Value.VistoPorUltimaVez < 1.6f))
				{
					((Component)item.Key).transform.localScale = item.Value.TamanoOriginal;
					(list ?? (list = new List<PhysGrabObject>())).Add(item.Key);
					Plugin.Debug("Carrito: '" + ((Object)item.Key).name + "' recupera su tamano.");
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (PhysGrabObject item2 in list)
			{
				_dentro.Remove(item2);
			}
		}
	}
	internal static class ComprobacionDeVersion
	{
		internal static ConfigEntry<bool> Enabled;

		private static readonly Dictionary<string, string> _versiones = new Dictionary<string, string>();

		private static readonly Dictionary<string, float> _vistoDesde = new Dictionary<string, float>();

		private static float _proximoAnuncio;

		private static float _proximaRevision;

		private static string _ultimoProblema = "";

		private const float MargenDeGracia = 25f;

		private const float CadaCuantoAnuncio = 15f;

		private const float CadaCuantoRevision = 5f;

		private static readonly FieldInfo _campoNombre = AccessTools.Field(typeof(PlayerAvatar), "playerName");

		internal static void BindConfig(ConfigFile config)
		{
			Enabled = config.Bind<bool>("Version", "AvisarDesajuste", true, "Avisa en pantalla si algun jugador de la sala lleva otra version del mod o no lo lleva. Muy recomendable dejarlo activado: un desajuste de version no da ningun error, simplemente hace que los roles no funcionen.");
		}

		internal static void AlCambiarNivel()
		{
			_versiones.Clear();
			_vistoDesde.Clear();
			_proximoAnuncio = 2f;
			_proximaRevision = 25f;
			_ultimoProblema = "";
		}

		internal static void Tick()
		{
			if (Enabled != null && Enabled.Value && Roles.EnPartida())
			{
				_proximoAnuncio -= Time.deltaTime;
				if (_proximoAnuncio <= 0f)
				{
					_proximoAnuncio = 15f;
					Anunciar();
				}
				_proximaRevision -= Time.deltaTime;
				if (_proximaRevision <= 0f)
				{
					_proximaRevision = 5f;
					Revisar();
				}
			}
		}

		private static void Anunciar()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			PlayerAvatar instance = PlayerAvatar.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			string text = SemiFunc.PlayerGetSteamID(instance);
			if (!string.IsNullOrEmpty(text))
			{
				_versiones[text] = "1.5.0";
				if (SemiFunc.IsMultiplayer() && PhotonNetwork.NetworkingClient != null && PhotonNetwork.InRoom)
				{
					PhotonNetwork.RaiseEvent((byte)178, (object)(text + "|1.5.0"), new RaiseEventOptions
					{
						Receivers = (ReceiverGroup)0
					}, SendOptions.SendReliable);
				}
			}
		}

		internal static void Recibir(object datos)
		{
			if (datos is string text)
			{
				int num = text.IndexOf('|');
				if (num > 0 && num != text.Length - 1)
				{
					_versiones[text.Substring(0, num)] = text.Substring(num + 1);
				}
			}
		}

		private static string Nombre(PlayerAvatar avatar)
		{
			if (_campoNombre == null)
			{
				return "Alguien";
			}
			string text = _campoNombre.GetValue(avatar) as string;
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "Alguien";
		}

		private static void Revisar()
		{
			if (!SemiFunc.IsMultiplayer())
			{
				return;
			}
			List<PlayerAvatar> list = SemiFunc.PlayerGetAll();
			if (list == null || list.Count <= 1)
			{
				return;
			}
			List<string> list2 = new List<string>();
			List<string> list3 = new List<string>();
			foreach (PlayerAvatar item in list)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				string text = SemiFunc.PlayerGetSteamID(item);
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				if (!_vistoDesde.ContainsKey(text))
				{
					_vistoDesde[text] = Time.time;
				}
				if (_versiones.TryGetValue(text, out var value))
				{
					if (value != "1.5.0")
					{
						list2.Add(Nombre(item) + " lleva la " + value);
					}
				}
				else if (Time.time - _vistoDesde[text] >= 25f)
				{
					list3.Add(Nombre(item));
				}
			}
			if (list2.Count == 0 && list3.Count == 0)
			{
				_ultimoProblema = "";
				return;
			}
			string text2 = string.Join(",", list2.ToArray()) + "//" + string.Join(",", list3.ToArray());
			if (!(text2 == _ultimoProblema))
			{
				_ultimoProblema = text2;
				Avisar(list2, list3);
			}
		}

		private static void Avisar(List<string> distintos, List<string> ausentes)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			SemiFunc.UIBigMessage("MOD DESINCRONIZADO", "!", 35f, new Color(1f, 0.4f, 0.2f), Color.white);
			string text = ((distintos.Count > 0 && ausentes.Count > 0) ? (string.Join(", ", distintos.ToArray()) + "  -  sin el mod: " + string.Join(", ", ausentes.ToArray())) : ((distintos.Count <= 0) ? ("Sin el mod (o version anterior a la 1.3.0): " + string.Join(", ", ausentes.ToArray())) : (string.Join(", ", distintos.ToArray()) + "  -  tu la 1.5.0")));
			SemiFunc.UIFocusText(text, new Color(1f, 0.4f, 0.2f), Color.white, 8f);
			Plugin.Log.LogWarning((object)("DESAJUSTE DE VERSION. Yo llevo la 1.5.0. " + text + ". Los roles no van a funcionar bien hasta que todos tengan la misma DLL. Recordad que arrancar desde Steam corre el juego en vanilla sin avisar."));
		}
	}
	internal static class ExtractionOnDemand
	{
		private sealed class Estado
		{
			public bool Esperando;

			public bool Armado;

			public bool AvisoMostrado;

			public float LuzIntensidad = -1f;

			public Color LuzColor;

			public Transform Estacion;

			public bool YaCompletado;

			public PhysGrabObject Pato;

			public Vector3 PatoPosicion;

			public Quaternion PatoRotacion;

			public bool Activo;

			public bool MetaConocida;

			public int FaltaBotin;
		}

		[HarmonyPatch(typeof(ExtractionPoint), "Start")]
		private static class ConservarBotonDeTienda
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instrucciones)
			{
				MethodInfo destroy = AccessTools.Method(typeof(Object), "Destroy", new Type[1] { typeof(Object) }, (Type[])null);
				MethodInfo reemplazo = AccessTools.Method(typeof(ExtractionOnDemand), "DestruirSalvoQueLoConservemos", (Type[])null, (Type[])null);
				foreach (CodeInstruction instruccione in instrucciones)
				{
					if (destroy != null && CodeInstructionExtensions.Calls(instruccione, destroy))
					{
						yield return new CodeInstruction(OpCodes.Call, (object)reemplazo);
					}
					else
					{
						yield return instruccione;
					}
				}
			}

			private static void Postfix(ExtractionPoint __instance, bool ___isShop)
			{
				if (Enabled.Value && BotonGrande.Value && !___isShop)
				{
					Transform shopStation = __instance.shopStation;
					if (!((Object)(object)shopStation == (Object)null))
					{
						EstadoDe(__instance).Estacion = shopStation;
						__instance.shopStation = null;
						((Component)shopStation).gameObject.SetActive(true);
						DesplegarEstacion(shopStation, __instance);
					}
				}
			}
		}

		private sealed class Vinculo
		{
			public ExtractionPoint Punto;
		}

		[HarmonyPatch(typeof(PhysGrabObject), "GrabStarted")]
		private static class PatoNoSeLevanta
		{
			private static bool Prefix(PhysGrabObject __instance)
			{
				if (!Enabled.Value || !UsarPato.Value)
				{
					return true;
				}
				if (!_patos.TryGetValue(__instance, out var value))
				{
					return true;
				}
				if ((Object)(object)value.Punto != (Object)null)
				{
					if (EstadoDe(value.Punto).Esperando)
					{
						object? obj = _photonViewExtractor?.GetValue(value.Punto);
						PhotonView view = (PhotonView)((obj is PhotonView) ? obj : null);
						Plugin.Debug("Pato pulsado: lanzando la extraccion.");
						Solicitar(value.Punto, view);
					}
					else
					{
						AvisarPorQueNoSePuede(EstadoDe(value.Punto));
					}
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(ItemEquippable), "Update")]
		private static class SilenciarEquipableDelPato
		{
			private static bool Prefix(ItemEquippable __instance)
			{
				if (!Enabled.Value || _equipablesDelPato.Count == 0)
				{
					return true;
				}
				return !_equipablesDelPato.Contains(__instance);
			}
		}

		[HarmonyPatch(typeof(ItemRubberDuck), "Start")]
		private static class DesnudarPatoEnCadaCliente
		{
			private static void Postfix(ItemRubberDuck __instance)
			{
				PhotonView component = ((Component)__instance).GetComponent<PhotonView>();
				object[] array = (((Object)(object)component != (Object)null) ? component.InstantiationData : null);
				if (array != null && array.Length != 0 && array[0] is string text && !(text != "RepoAmigos:BotonPato"))
				{
					DesnudarPato(((Component)__instance).gameObject);
				}
			}
		}

		[HarmonyPatch(typeof(ExtractionPoint), "StateSetRPC")]
		private static class SonidoAlPulsar
		{
			private static void Prefix(ExtractionPoint __instance, State state, bool ___isShop)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Invalid comparison between Unknown and I4
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: 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_0059: Unknown result type (might be due to invalid IL or missing references)
				if (!Enabled.Value || ___isShop || (int)state != 3)
				{
					return;
				}
				try
				{
					Estado estado = EstadoDe(__instance);
					Vector3 val = (((Object)(object)estado.Pato != (Object)null) ? ((Component)estado.Pato).transform.position : ((Component)__instance).transform.position);
					if (__instance.soundButton != null)
					{
						__instance.soundButton.Play(val, 1f, 1f, 1f, 1f);
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPatch(typeof(ExtractionPoint), "StateSet")]
		private static class BloquearSuccessAutomatico
		{
			private static bool Prefix(ExtractionPoint __instance, State newState, bool ___isShop)
			{
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Invalid comparison between Unknown and I4
				if (!Enabled.Value)
				{
					return true;
				}
				if ((int)newState != 3)
				{
					return true;
				}
				if (___isShop)
				{
					return true;
				}
				Estado estado = EstadoDe(__instance);
				if (estado.Armado)
				{
					estado.Armado = false;
					estado.Esperando = false;
					estado.AvisoMostrado = false;
					Plugin.Debug("Extraccion autorizada por pulsacion del boton.");
					return true;
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(ExtractionPoint), "Update")]
		private static class MantenerBotonUsable
		{
			private static void Postfix(ExtractionPoint __instance, bool ___isShop, bool ___haulGoalFetched, int ___haulCurrent, State ___currentState)
			{
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Invalid comparison between Unknown and I4
				//IL_0044: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: Invalid comparison between Unknown and I4
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_0066: Invalid comparison between Unknown and I4
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
				//IL_00de: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
				//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
				//IL_0128: Unknown result type (might be due to invalid IL or missing references)
				//IL_0134: Unknown result type (might be due to invalid IL or missing references)
				//IL_0319: Unknown result type (might be due to invalid IL or missing references)
				if (!Enabled.Value)
				{
					return;
				}
				AsegurarRed();
				Estado estado = EstadoDe(__instance);
				bool flag = ___haulGoalFetched && __instance.haulGoal - ___haulCurrent <= 0;
				bool flag2 = (estado.Esperando = !___isShop && (int)___currentState == 2 && flag);
				estado.Activo = (int)___currentState == 2;
				estado.MetaConocida = ___haulGoalFetched;
				estado.FaltaBotin = __instance.haulGoal - ___haulCurrent;
				if ((int)___currentState == 7 && !estado.YaCompletado)
				{
					estado.YaCompletado = true;
					if ((Object)(object)estado.Estacion != (Object)null)
					{
						((Component)estado.Estacion).gameObject.SetActive(false);
					}
					RetirarPato(estado);
					Plugin.Debug("Extraccion completada: boton/pato retirado.");
				}
				if ((Object)(object)estado.Pato != (Object)null && SemiFunc.IsMasterClientOrSingleplayer())
				{
					Transform transform = ((Component)estado.Pato).transform;
					Vector3 val = transform.position - estado.PatoPosicion;
					if (((Vector3)(ref val)).sqrMagnitude > 0.0004f)
					{
						transform.position = estado.PatoPosicion;
						transform.rotation = estado.PatoRotacion;
					}
					Rigidbody component = ((Component)estado.Pato).GetComponent<Rigidbody>();
					if ((Object)(object)component != (Object)null && !component.isKinematic)
					{
						component.velocity = Vector3.zero;
						component.angularVelocity = Vector3.zero;
						component.isKinematic = true;
					}
				}
				if (flag2 && (Object)(object)estado.Pato != (Object)null && SemiFunc.IsMasterClientOrSingleplayer())
				{
					List<PhysGrabber> playerGrabbing = estado.Pato.playerGrabbing;
					if (playerGrabbing != null && playerGrabbing.Count > 0)
					{
						Plugin.Debug("Pato agarrado: lanzando la extraccion.");
						Ejecutar(__instance);
					}
				}
				if (!flag2)
				{
					if (estado.LuzIntensidad >= 0f && (Object)(object)__instance.buttonLight != (Object)null)
					{
						__instance.buttonLight.intensity = estado.LuzIntensidad;
						__instance.buttonLight.color = estado.LuzColor;
						estado.LuzIntensidad = -1f;
					}
					estado.AvisoMostrado = false;
					return;
				}
				if ((Object)(object)__instance.buttonGrabObject != (Object)null && !((Behaviour)__instance.buttonGrabObject).enabled)
				{
					((Behaviour)__instance.buttonGrabObject).enabled = true;
					Plugin.Debug("Boton del extractor reactivado (estaba desactivado).");
				}
				if ((Object)(object)__instance.buttonLight != (Object)null && !((Behaviour)__instance.buttonLight).enabled)
				{
					((Behaviour)__instance.buttonLight).enabled = true;
				}
				if (!estado.AvisoMostrado)
				{
					Plugin.Debug($"Meta cubierta ({___haulCurrent}/{__instance.haulGoal}). Esperando pulsacion. " + "buttonGrabObject=" + (((Object)(object)__instance.buttonGrabObject == (Object)null) ? "NULO" : "ok") + " buttonLight=" + (((Object)(object)__instance.buttonLight == (Object)null) ? "NULO" : "ok") + " pedestalRescatado=" + (((Object)(object)estado.Estacion == (Object)null) ? "NO" : "SI"));
					EncenderBoton(__instance, estado);
				}
				if (!AvisoEnPantalla.Value || estado.AvisoMostrado)
				{
					return;
				}
				estado.AvisoMostrado = true;
				try
				{
					_tubeScreenTextChange.Invoke(__instance, new object[2]
					{
						TextoPantalla.Value,
						Color.yellow
					});
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("No se pudo cambiar el texto del extractor: " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(ExtractionPoint), "OnShopClick")]
		private static class BotonTiendaLanzaExtraccion
		{
			private static bool Prefix(ExtractionPoint __instance, PhotonView ___photonView, bool ___isShop)
			{
				if (!Enabled.Value)
				{
					return true;
				}
				if (___isShop)
				{
					return true;
				}
				if (EstadoDe(__instance).Esperando)
				{
					Solicitar(__instance, ___photonView);
					return false;
				}
				AvisarPorQueNoSePuede(EstadoDe(__instance));
				return false;
			}
		}

		[HarmonyPatch(typeof(ExtractionPoint), "OnClick")]
		private static class BotonLanzaExtraccion
		{
			private static bool Prefix(ExtractionPoint __instance, PhotonView ___photonView)
			{
				if (!Enabled.Value)
				{
					return true;
				}
				if (__instance.isLocked)
				{
					return true;
				}
				if (!EstadoDe(__instance).Esperando)
				{
					return true;
				}
				Solicitar(__instance, ___photonView);
				return false;
			}
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> AvisoEnPantalla;

		internal static ConfigEntry<string> TextoPantalla;

		internal static ConfigEntry<bool> BotonGrande;

		internal static ConfigEntry<float> DesplazamientoX;

		internal static ConfigEntry<float> DesplazamientoY;

		internal static ConfigEntry<float> DesplazamientoZ;

		internal static ConfigEntry<bool> SoloElBoton;

		internal static ConfigEntry<float> EscalaBoton;

		internal static ConfigEntry<string> PiezasVisibles;

		internal static ConfigEntry<float> GiroBoton;

		internal static ConfigEntry<bool> UsarPato;

		internal static ConfigEntry<float> EscalaPato;

		internal static ConfigEntry<string> NombrePato;

		internal static ConfigEntry<float> GiroPatoX;

		internal static ConfigEntry<float> GiroPatoY;

		internal static ConfigEntry<float> GiroPatoZ;

		private static readonly ConditionalWeakTable<ExtractionPoint, Estado> _estados = new ConditionalWeakTable<ExtractionPoint, Estado>();

		private const byte EventoPulsarBoton = 174;

		private static bool _redConectada;

		private static readonly MethodInfo _stateSet = AccessTools.Method(typeof(ExtractionPoint), "StateSet", (Type[])null, (Type[])null);

		private static readonly MethodInfo _tubeScreenTextChange = AccessTools.Method(typeof(ExtractionPoint), "TubeScreenTextChange", (Type[])null, (Type[])null);

		private static readonly FieldInfo _buttonOriginalMaterial = AccessTools.Field(typeof(ExtractionPoint), "buttonOriginalMaterial");

		private const string MarcaPato = "RepoAmigos:BotonPato";

		private static Item _itemPato;

		private static bool _patoBuscado;

		private static readonly ConditionalWeakTable<PhysGrabObject, Vinculo> _patos = new ConditionalWeakTable<PhysGrabObject, Vinculo>();

		private static readonly FieldInfo _photonViewExtractor = AccessTools.Field(typeof(ExtractionPoint), "photonView");

		private static float _ultimoAvisoPato = -10f;

		private static readonly HashSet<ItemEquippable> _equipablesDelPato = new HashSet<ItemEquippable>();

		internal static void BindConfig(ConfigFile config)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Expected O, but got Unknown
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Expected O, but got Unknown
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Expected O, but got Unknown
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Expected O, but got Unknown
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Expected O, but got Unknown
			Enabled = config.Bind<bool>("ExtraccionManual", "Activado", true, "Si esta activado, al cubrir la meta el extractor NO se dispara solo: espera a que alguien pulse el boton. Ponlo en false para volver al comportamiento original.");
			AvisoEnPantalla = config.Bind<bool>("ExtraccionManual", "AvisoEnPantalla", true, "Cambia el texto del tubo del extractor cuando ya esta listo, para que se vea que falta pulsarlo.");
			TextoPantalla = config.Bind<string>("ExtraccionManual", "TextoAviso", "PULSA EL BOTON", "Texto que aparece en el extractor cuando la meta ya esta cubierta.");
			BotonGrande = config.Bind<bool>("ExtraccionManual", "BotonGrandeDeTienda", true, "Conserva el pedestal con el boton grande que los extractores solo tienen en la tienda, y lo hace aparecer cuando la meta esta cubierta. En vanilla el juego lo destruye al arrancar cualquier nivel que no sea la tienda.");
			DesplazamientoX = config.Bind<float>("ExtraccionManual", "BotonLateral", 2.5f, new ConfigDescription("Mueve el boton grande a un lado u otro de la rampa, en metros.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-20f, 20f), Array.Empty<object>()));
			DesplazamientoY = config.Bind<float>("ExtraccionManual", "BotonAltura", 1.1f, new ConfigDescription("Altura del boton sobre el suelo de la rampa, en metros.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-20f, 20f), Array.Empty<object>()));
			UsarPato = config.Bind<bool>("ExtraccionManual", "UsarPato", true, "En vez del boton de la tienda, pone un pato de goma fijo al que agarrar para lanzar la extraccion. Mucho mas visible y no hay que pelearse con la geometria del mostrador.");
			NombrePato = config.Bind<string>("ExtraccionManual", "PatoNombreDelItem", "Rubber Duck", "Nombre exacto del item que se usa como boton. Si no lo encuentra, coge el primero que lleve 'duck' en el nombre. El log lista todos los disponibles.");
			GiroPatoX = config.Bind<float>("ExtraccionManual", "PatoGiroX", 0f, new ConfigDescription("Inclina el pato hacia delante o atras, en grados.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-360f, 360f), Array.Empty<object>()));
			GiroPatoY = config.Bind<float>("ExtraccionManual", "PatoGiroY", 0f, new ConfigDescription("Gira el pato sobre si mismo para que mire a donde quieras, en grados.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-360f, 360f), Array.Empty<object>()));
			GiroPatoZ = config.Bind<float>("ExtraccionManual", "PatoGiroZ", 0f, new ConfigDescription("Ladea el pato, en grados.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-360f, 360f), Array.Empty<object>()));
			EscalaPato = config.Bind<float>("ExtraccionManual", "PatoEscala", 2f, new ConfigDescription("Tamano del pato. 1 es su tamano normal.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), Array.Empty<object>()));
			GiroBoton = config.Bind<float>("ExtraccionManual", "BotonGiro", 0f, new ConfigDescription("Gira el boton sobre si mismo, en grados. Prueba 180 si te da la espalda.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-360f, 360f), Array.Empty<object>()));
			DesplazamientoZ = config.Bind<float>("ExtraccionManual", "BotonHaciaFuera", 1f, new ConfigDescription("Aleja el boton grande del extractor (positivo) o lo acerca (negativo), en metros.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-20f, 20f), Array.Empty<object>()));
			SoloElBoton = config.Bind<bool>("ExtraccionManual", "SoloElBoton", true, "El pedestal de la tienda incluye el mostrador entero. Con esto activado solo se muestran las piezas indicadas en PiezasDelBoton y se oculta el resto.");
			PiezasVisibles = config.Bind<string>("ExtraccionManual", "PiezasDelBoton", "Extraction Point Side Button, Shop Button", "Piezas del pedestal que se dejan visibles, separadas por comas. Disponibles: 'Shop Button' (la placa roja, el boton en si), 'Extraction Point Side Button' (la consola sobre la que va montada la placa), 'Meshtownusa' y 'Cube (1)' (el mostrador de la tienda, un armatoste).");
			EscalaBoton = config.Bind<float>("ExtraccionManual", "BotonEscala", 1f, new ConfigDescription("Tamano del boton. 0.5 lo deja a la mitad, 2 al doble.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
		}

		private static Estado EstadoDe(ExtractionPoint punto)
		{
			return _estados.GetOrCreateValue(punto);
		}

		private static void AsegurarRed()
		{
			if (!_redConectada && PhotonNetwork.NetworkingClient != null)
			{
				PhotonNetwork.NetworkingClient.EventReceived += AlRecibirEvento;
				_redConectada = true;
				Plugin.Debug("Enganchado al canal de eventos de Photon.");
			}
		}

		internal static void DesconectarRed()
		{
			if (_redConectada)
			{
				if (PhotonNetwork.NetworkingClient != null)
				{
					PhotonNetwork.NetworkingClient.EventReceived -= AlRecibirEvento;
				}
				_redConectada = false;
			}
		}

		private static void AlRecibirEvento(EventData datos)
		{
			if (datos.Code != 174 || !SemiFunc.IsMasterClient())
			{
				return;
			}
			try
			{
				int num = (int)datos.CustomData;
				PhotonView val = PhotonView.Find(num);
				if (!((Object)(object)val == (Object)null))
				{
					ExtractionPoint component = ((Component)val).GetComponent<ExtractionPoint>();
					if (!((Object)(object)component == (Object)null) && EstadoDe(component).Esperando)
					{
						Plugin.Debug($"Peticion de extraccion recibida para el extractor {num}.");
						Ejecutar(component);
					}
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Error procesando el evento de extraccion: {arg}");
			}
		}

		private static void Solicitar(ExtractionPoint punto, PhotonView view)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			punto.ButtonPress();
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				Ejecutar(punto);
				return;
			}
			if ((Object)(object)view == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Extractor sin PhotonView: no se puede avisar al master.");
				return;
			}
			PhotonNetwork.RaiseEvent((byte)174, (object)view.ViewID, new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)2
			}, SendOptions.SendReliable);
			Plugin.Debug($"No soy master: peticion enviada para el extractor {view.ViewID}.");
		}

		private static void Ejecutar(ExtractionPoint punto)
		{
			EstadoDe(punto).Armado = true;
			_stateSet.Invoke(punto, new object[1] { (object)(State)3 });
		}

		private static void EncenderBoton(ExtractionPoint punto, Estado estado)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				object? obj = _buttonOriginalMaterial?.GetValue(punto);
				Material val = (Material)((obj is Material) ? obj : null);
				if ((Object)(object)val != (Object)null && (Object)(object)punto.button != (Object)null)
				{
					((Renderer)punto.button).material = val;
				}
				if ((Object)(object)punto.buttonLight != (Object)null && estado.LuzIntensidad < 0f)
				{
					estado.LuzIntensidad = punto.buttonLight.intensity;
					estado.LuzColor = punto.buttonLight.color;
					punto.buttonLight.intensity = Mathf.Max(estado.LuzIntensidad * 3f, 5f);
					punto.buttonLight.color = Color.green;
				}
				if ((Object)(object)punto.buttonGrabObject != (Object)null)
				{
					Vector3 position = ((Component)punto.buttonGrabObject).transform.position;
					Vector3 position2 = ((Component)punto).transform.position;
					Plugin.Debug("Boton en " + ((Vector3)(ref position)).ToString("F1") + ", extractor en " + ((Vector3)(ref position2)).ToString("F1") + ", " + $"separacion {Vector3.Distance(position, position2):F1} m.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("No se pudo encender el boton: " + ex.Message));
			}
		}

		private static void DesplegarEstacion(Transform estacion, ExtractionPoint punto)
		{
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_0339: Expected O, but got Unknown
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Unknown result type (might be due to invalid IL or missing references)
			//IL_039c: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a3: Expected O, but got Unknown
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_046c: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0532: Unknown result type (might be due to invalid IL or missing references)
			//IL_0537: Unknown result type (might be due to invalid IL or missing references)
			//IL_0577: Unknown result type (might be due to invalid IL or missing references)
			//IL_057c: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05db: Expected O, but got Unknown
			try
			{
				Transform[] componentsInChildren = ((Component)estacion).GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					if (!((Component)val).gameObject.activeSelf)
					{
						((Component)val).gameObject.SetActive(true);
					}
				}
				int num = 0;
				int num2 = 0;
				Renderer[] componentsInChildren2 = ((Component)estacion).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val2 in componentsInChildren2)
				{
					num2++;
					if (!val2.enabled)
					{
						val2.enabled = true;
						num++;
					}
				}
				if (SoloElBoton.Value)
				{
					List<string> list = new List<string>();
					string[] array = (PiezasVisibles.Value ?? "").Split(',');
					foreach (string text in array)
					{
						if (!string.IsNullOrEmpty(text.Trim()))
						{
							list.Add(text.Trim());
						}
					}
					List<string> list2 = new List<string>();
					foreach (Transform item in estacion)
					{
						Transform val3 = item;
						bool flag = false;
						foreach (string item2 in list)
						{
							if (string.Equals(item2, ((Object)val3).name, StringComparison.OrdinalIgnoreCase))
							{
								flag = true;
								break;
							}
						}
						((Component)val3).gameObject.SetActive(flag);
						if (!flag)
						{
							list2.Add(((Object)val3).name);
						}
					}
					componentsInChildren2 = ((Component)estacion).GetComponents<Renderer>();
					for (int i = 0; i < componentsInChildren2.Length; i++)
					{
						componentsInChildren2[i].enabled = false;
					}
					if (list2.Count > 0)
					{
						Plugin.Debug("Mobiliario oculto: " + string.Join(", ", list2.ToArray()));
					}
				}
				estacion.localScale = Vector3.one * EscalaBoton.Value;
				Vector3 val4 = (((Object)(object)punto.ramp != (Object)null) ? punto.ramp.position : (((Object)(object)punto.platform != (Object)null) ? punto.platform.position : ((Component)punto).transform.position));
				Vector3 val5 = val4 - ((Component)punto).transform.position;
				val5.y = 0f;
				val5 = ((((Vector3)(ref val5)).sqrMagnitude < 0.01f) ? ((Component)punto).transform.forward : ((Vector3)(ref val5)).normalized);
				Vector3 val6 = Vector3.Cross(Vector3.up, val5);
				estacion.rotation = Quaternion.LookRotation(val5, Vector3.up) * Quaternion.Euler(0f, GiroBoton.Value, 0f);
				Vector3 val7 = val4 + val6 * DesplazamientoX.Value + Vector3.up * DesplazamientoY.Value + val5 * DesplazamientoZ.Value;
				if (UsarPato.Value)
				{
					((Component)estacion).gameObject.SetActive(false);
					CrearPato(EstadoDe(punto), val7);
					return;
				}
				Transform val8 = null;
				foreach (Transform item3 in estacion)
				{
					Transform val9 = item3;
					if (((Component)val9).gameObject.activeSelf && string.Equals(((Object)val9).name, "Shop Button", StringComparison.OrdinalIgnoreCase))
					{
						val8 = val9;
						break;
					}
				}
				if ((Object)(object)val8 == (Object)null)
				{
					foreach (Transform item4 in estacion)
					{
						Transform val10 = item4;
						if (((Component)val10).gameObject.activeSelf)
						{
							val8 = val10;
							break;
						}
					}
				}
				Vector3 val11 = (((Object)(object)val8 != (Object)null) ? (val8.position - estacion.position) : Vector3.zero);
				estacion.position = val7 - val11;
				Plugin.Debug("Pieza de referencia: " + (((Object)(object)val8 == (Object)null) ? "ninguna" : ((Object)val8).name) + ", desfase=" + ((Vector3)(ref val11)).ToString("F2"));
				string[] obj = new string[8] { "Referencia: rampa=", null, null, null, null, null, null, null };
				Vector3 val12;
				object obj2;
				if (!((Object)(object)punto.ramp == (Object)null))
				{
					val12 = punto.ramp.position;
					obj2 = ((Vector3)(ref val12)).ToString("F1");
				}
				else
				{
					obj2 = "NULA";
				}
				obj[1] = (string)obj2;
				obj[2] = " plataforma=";
				object obj3;
				if (!((Object)(object)punto.platform == (Object)null))
				{
					val12 = punto.platform.position;
					obj3 = ((Vector3)(ref val12)).ToString("F1");
				}
				else
				{
					obj3 = "NULA";
				}
				obj[3] = (string)obj3;
				obj[4] = " haciaFuera=";
				obj[5] = ((Vector3)(ref val5)).ToString("F2");
				obj[6] = " lateral=";
				obj[7] = ((Vector3)(ref val6)).ToString("F2");
				Plugin.Debug(string.Concat(obj));
				string[] obj4 = new string[12]
				{
					"Pedestal '",
					((Object)estacion).name,
					"': colocado en ",
					((Vector3)(ref val7)).ToString("F1"),
					" (extractor en ",
					null,
					null,
					null,
					null,
					null,
					null,
					null
				};
				val12 = ((Component)punto).transform.position;
				obj4[5] = ((Vector3)(ref val12)).ToString("F1");
				obj4[6] = ") ";
				obj4[7] = $"activoEnJerarquia={((Component)estacion).gameObject.activeInHierarchy} ";
				obj4[8] = "escala=";
				val12 = estacion.lossyScale;
				obj4[9] = ((Vector3)(ref val12)).ToString("F2");
				obj4[10] = " ";
				obj4[11] = $"renderers={num2} (reactivados {num}) hijos={estacion.childCount}";
				Plugin.Debug(string.Concat(obj4));
				foreach (Transform item5 in estacion)
				{
					Transform val13 = item5;
					Plugin.Debug($"   hijo '{((Object)val13).name}' activo={((Component)val13).gameObject.activeSelf}");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Fallo al desplegar el pedestal: " + ex.Message));
			}
		}

		internal static void DesnudarPato(GameObject pato)
		{
			//IL_0407: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)pato == (Object)null)
			{
				return;
			}
			string[] array = new string[7] { "ItemRubberDuck", "HurtCollider", "ItemEquippable", "ItemBattery", "ItemToggle", "ParticleScriptExplosion", "ItemDeactivatedUntilLevel" };
			MonoBehaviour[] componentsInChildren = pato.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string name = ((object)val).GetType().Name;
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (name == text)
					{
						Object.Destroy((Object)(object)val);
						break;
					}
				}
			}
			MapCustom[] componentsInChildren2 = pato.GetComponentsInChildren<MapCustom>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren2[i]);
			}
			ItemBattery[] componentsInChildren3 = pato.GetComponentsInChildren<ItemBattery>(true);
			foreach (ItemBattery val2 in componentsInChildren3)
			{
				if ((Object)(object)val2.batteryTransform != (Object)null)
				{
					((Component)val2.batteryTransform).gameObject.SetActive(false);
				}
			}
			componentsInChildren = pato.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val3 in componentsInChildren)
			{
				if (!((Object)(object)val3 == (Object)null))
				{
					string name2 = ((object)val3).GetType().Name;
					if (name2 == "BatteryUI" || name2 == "ItemEquipCube")
					{
						((Component)val3).gameObject.SetActive(false);
					}
				}
			}
			componentsInChildren = pato.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val4 in componentsInChildren)
			{
				if (!((Object)(object)val4 == (Object)null))
				{
					string name3 = ((object)val4).GetType().Name;
					if (name3 == "ItemEquipCube" || name3 == "BatteryUI")
					{
						Object.Destroy((Object)(object)((Component)val4).gameObject);
					}
				}
			}
			Animator[] componentsInChildren4 = pato.GetComponentsInChildren<Animator>(true);
			for (int i = 0; i < componentsInChildren4.Length; i++)
			{
				((Behaviour)componentsInChildren4[i]).enabled = false;
			}
			AudioSource[] componentsInChildren5 = pato.GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource obj in componentsInChildren5)
			{
				obj.Stop();
				((Behaviour)obj).enabled = false;
			}
			ParticleSystem[] componentsInChildren6 = pato.GetComponentsInChildren<ParticleSystem>(true);
			for (int i = 0; i < componentsInChildren6.Length; i++)
			{
				componentsInChildren6[i].Stop();
			}
			TrailRenderer[] componentsInChildren7 = pato.GetComponentsInChildren<TrailRenderer>(true);
			for (int i = 0; i < componentsInChildren7.Length; i++)
			{
				((Renderer)componentsInChildren7[i]).enabled = false;
			}
			ItemEquippable[] componentsInChildren8 = pato.GetComponentsInChildren<ItemEquippable>(true);
			foreach (ItemEquippable val5 in componentsInChildren8)
			{
				((Behaviour)val5).enabled = false;
				_equipablesDelPato.Add(val5);
			}
			Plugin.Debug($"Pato: {_equipablesDelPato.Count} ItemEquippable silenciados.");
			Rigidbody[] componentsInChildren9 = pato.GetComponentsInChildren<Rigidbody>(true);
			foreach (Rigidbody obj2 in componentsInChildren9)
			{
				obj2.isKinematic = true;
				obj2.useGravity = false;
			}
			RegistrarPato(pato);
			if (Plugin.VerboseLogging == null || !Plugin.VerboseLogging.Value)
			{
				return;
			}
			List<string> list = new List<string>();
			Renderer[] componentsInChildren10 = pato.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val6 in componentsInChildren10)
			{
				if (val6.enabled && ((Component)val6).gameObject.activeInHierarchy)
				{
					list.Add(((Object)((Component)val6).gameObject).name + "(" + ((object)val6).GetType().Name + ")");
				}
			}
			List<string> list2 = new List<string>();
			componentsInChildren = pato.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val7 in componentsInChildren)
			{
				if ((Object)(object)val7 != (Object)null)
				{
					list2.Add(((object)val7).GetType().Name + "@" + ((Object)((Component)val7).gameObject).name);
				}
			}
			Plugin.Debug("Pato desnudado. Piezas visibles: " + string.Join(", ", list.ToArray()));
			Plugin.Debug("Componentes que quedan: " + string.Join(", ", list2.ToArray()));
			List<string> list3 = new List<string>();
			ItemEquipCube[] array3 = Object.FindObjectsOfType<ItemEquipCube>();
			foreach (ItemEquipCube val8 in array3)
			{
				float num = Vector3.Distance(((Component)val8).transform.position, pato.transform.position);
				if (!(num > 4f))
				{
					Transform root = ((Component)val8).transform.root;
					bool flag = ((Component)val8).transform.IsChildOf(pato.transform);
					list3.Add($"'{((Object)((Component)val8).gameObject).name}' raiz='{((Object)root).name}' a {num:F1}m " + (flag ? "(del pato)" : "(AJENO)"));
				}
			}
			Plugin.Debug((list3.Count == 0) ? "No hay ningun ItemEquipCube cerca del pato." : ("Cubos de equipar cerca: " + string.Join(" | ", list3.ToArray())));
		}

		private static void RegistrarPato(GameObject pato)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			PhysGrabObject component = pato.GetComponent<PhysGrabObject>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			ExtractionPoint punto = null;
			float num = float.MaxValue;
			ExtractionPoint[] array = Object.FindObjectsOfType<ExtractionPoint>();
			foreach (ExtractionPoint val in array)
			{
				float num2 = Vector3.Distance(((Component)val).transform.position, pato.transform.position);
				if (num2 < num)
				{
					num = num2;
					punto = val;
				}
			}
			if (_patos.TryGetValue(component, out var value))
			{
				value.Punto = punto;
			}
			else
			{
				_patos.Add(component, new Vinculo
				{
					Punto = punto
				});
			}
			Plugin.Debug($"Pato registrado, extractor mas cercano a {num:F1} m.");
		}

		private static void AvisarPorQueNoSePuede(Estado estado)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			string text = ((!estado.Activo) ? "Este extractor todavia no esta activo" : (estado.MetaConocida ? $"Faltan {estado.FaltaBotin:N0} de botin" : "Espera, el extractor aun no ha pedido su botin"));
			if (!(Time.time - _ultimoAvisoPato < 1.5f))
			{
				_ultimoAvisoPato = Time.time;
				Plugin.Debug("Pato pulsado y no se puede: " + text + ".");
				SemiFunc.UIFocusText(text, new Color(1f, 0.6f, 0.2f), Color.white, 3f);
			}
		}

		private static Item BuscarItemPato()
		{
			if (_patoBuscado)
			{
				return _itemPato;
			}
			_patoBuscado = true;
			string text = (NombrePato.Value ?? "").Trim();
			try
			{
				List<Item> list = new List<Item>();
				Item[] array = Resources.LoadAll<Item>("");
				foreach (Item val in array)
				{
					if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(val.itemName))
					{
						if (string.Equals(val.itemName, text, StringComparison.OrdinalIgnoreCase))
						{
							_itemPato = val;
							break;
						}
						if (val.itemName.IndexOf("duck", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							list.Add(val);
						}
					}
				}
				if ((Object)(object)_itemPato == (Object)null && list.Count > 0)
				{
					_itemPato = list[0];
				}
				if (list.Count > 0)
				{
					List<string> list2 = new List<string>();
					foreach (Item item in list)
					{
						list2.Add("'" + item.itemName + "'");
					}
					Plugin.Debug("Items tipo pato disponibles: " + string.Join(", ", list2.ToArray()));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Fallo buscando el item del pato: " + ex.Message));
			}
			if ((Object)(object)_itemPato == (Object)null)
			{
				Plugin.Log.LogWarning((object)("No encontre ningun item llamado '" + text + "' ni con 'duck' en el nombre."));
			}
			else
			{
				Plugin.Debug("Item del pato: '" + _itemPato.itemName + "' (buscaba '" + text + "').");
			}
			return _itemPato;
		}

		private static void CrearPato(Estado estado, Vector3 posicion)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)estado.Pato != (Object)null)
			{
				return;
			}
			Item val = BuscarItemPato();
			if ((Object)(object)val == (Object)null || val.prefab == null)
			{
				return;
			}
			Quaternion val2 = val.spawnRotationOffset * Quaternion.Euler(GiroPatoX.Value, GiroPatoY.Value, GiroPatoZ.Value);
			GameObject val3;
			if (GameManager.instance.gameMode == 0)
			{
				val3 = Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)val.prefab).Prefab, posicion, val2);
				DesnudarPato(val3);
			}
			else
			{
				val3 = PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)val.prefab).ResourcePath, posicion, val2, (byte)0, new object[1] { "RepoAmigos:BotonPato" });
			}
			if ((Object)(object)val3 == (Object)null)
			{
				Plugin.Log.LogWarning((object)"No se pudo crear el pato.");
				return;
			}
			Transform transform = val3.transform;
			transform.localScale *= EscalaPato.Value;
			estado.Pato = val3.GetComponent<PhysGrabObject>();
			estado.PatoPosicion = posicion;
			estado.PatoRotacion = val2;
			if (Plugin.VerboseLogging != null && Plugin.VerboseLogging.Value)
			{
				List<string> list = new List<string>();
				Component[] components = val3.GetComponents<Component>();
				foreach (Component val4 in components)
				{
					if ((Object)(object)val4 != (Object)null)
					{
						list.Add(((object)val4).GetType().Name);
					}
				}
				Plugin.Debug("Prefab creado: '" + ((Object)val3).name + "' (ruta '" + ((PrefabRef<GameObject>)(object)val.prefab).ResourcePath + "'). Componentes: " + string.Join(", ", list.ToArray()));
			}
			Plugin.Debug("Pato colocado en " + ((Vector3)(ref posicion)).ToString("F1") + " (PhysGrabObject=" + (((Object)(object)estado.Pato == (Object)null) ? "NULO" : "ok") + ").");
		}

		private static void RetirarPato(Estado estado)
		{
			if (!((Object)(object)estado.Pato == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer())
			{
				GameObject gameObject = ((Component)estado.Pato).gameObject;
				estado.Pato = null;
				if (SemiFunc.IsMultiplayer())
				{
					PhotonNetwork.Destroy(gameObject);
				}
				else
				{
					Object.Destroy((Object)(object)gameObject);
				}
				Plugin.Debug("Pato retirado.");
			}
		}

		internal static void DestruirSalvoQueLoConservemos(Object objeto)
		{
			if (Enabled == null || !Enabled.Value || BotonGrande == null || !BotonGrande.Value)
			{
				Object.Destroy(objeto);
			}
		}
	}
	internal static class GeneradorDePruebas
	{
		[HarmonyPatch(typeof(RunManager), "Update")]
		private static class EngancheDeTeclado
		{
			private static bool _primeraVez = true;

			private static void Postfix()
			{
				if (_primeraVez)
				{
					_primeraVez = false;
					Plugin.Log.LogInfo((object)"Pruebas: RunManager.Update esta corriendo. Enganche OK.");
				}
				Tick("RunManager");
			}
		}

		[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
		private static class SondaCambioDeNivel
		{
			private static void Postfix()
			{
				Plugin.Log.LogInfo((object)"Pruebas: sonda ChangeLevel ejecutada. Los parches SI corren.");
			}
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyCode> Tecla;

		internal static ConfigEntry<int> ValorMin;

		internal static ConfigEntry<int> ValorMax;

		internal static ConfigEntry<int> Cantidad;

		private static bool _diagnosticoEscrito;

		private static int _teclasRegistradas;

		private static int _ultimoFrame = -1;

		private static readonly string[] _listasPorTamano = new string[3] { "smallValuables", "tinyValuables", "mediumValuables" };

		internal static void BindConfig(ConfigFile config)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Pruebas", "Activado", true, "Permite generar objetos de valor con una tecla para probar el mod. Ponlo en false cuando juegues en serio.");
			Tecla = config.Bind<KeyCode>("Pruebas", "Tecla", (KeyCode)284, "Tecla que genera los objetos.");
			ValorMin = config.Bind<int>("Pruebas", "ValorMinimo", 2000, new ConfigDescription("Precio minimo de cada objeto generado.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000000), Array.Empty<object>()));
			ValorMax = config.Bind<int>("Pruebas", "ValorMaximo", 9000, new ConfigDescription("Precio maximo de cada objeto generado.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000000), Array.Empty<object>()));
			Cantidad = config.Bind<int>("Pruebas", "CantidadPorPulsacion", 1, new ConfigDescription("Cuantos objetos salen con cada pulsacion.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
		}

		private static bool TeclaPulsada()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Keyboard current = Keyboard.current;
				if (current != null && Enum.TryParse<Key>(((object)Tecla.Value/*cast due to .constrained prefix*/).ToString(), ignoreCase: true, out Key result))
				{
					KeyControl val = current[result];
					if (val != null && ((ButtonControl)val).wasPressedThisFrame)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			try
			{
				if (Input.GetKeyDown(Tecla.Value))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private static void EspiarTeclado()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.VerboseLogging == null || !Plugin.VerboseLogging.Value || _teclasRegistradas >= 25)
			{
				return;
			}
			try
			{
				Keyboard current = Keyboard.current;
				if (current == null || !((ButtonControl)current.anyKey).wasPressedThisFrame)
				{
					return;
				}
				Enumerator<KeyControl> enumerator = current.allKeys.GetEnumerator();
				try
				{
					while (enumerator.MoveNext())
					{
						KeyControl current2 = enumerator.Current;
						if (((ButtonControl)current2).wasPressedThisFrame)
						{
							_teclasRegistradas++;
							Plugin.Log.LogInfo((object)$"Pruebas: tecla pulsada = {current2.keyCode}  (la configurada es {Tecla.Value})");
							if (_teclasRegistradas >= 25)
							{
								Plugin.Log.LogInfo((object)"Pruebas: dejo de anotar teclas para no llenar el log.");
								break;
							}
						}
					}
				}
				finally
				{
					((IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
				}
			}
			catch
			{
			}
		}

		private static bool LegacyOperativo()
		{
			try
			{
				Input.GetKeyDown((KeyCode)294);
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal static void Tick(string origen)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (Enabled == null || !Enabled.Value || Time.frameCount == _ultimoFrame)
			{
				return;
			}
			_ultimoFrame = Time.frameCount;
			if (!_diagnosticoEscrito)
			{
				_diagnosticoEscrito = true;
				bool flag = false;
				try
				{
					flag = Keyboard.current != null;
				}
				catch
				{
				}
				Plugin.Log.LogInfo((object)($"Pruebas: activo via {origen}. Tecla configurada = {Tecla.Value}. " + "InputSystem nuevo = " + (flag ? "SI" : "no") + ", Input clasico = " + (LegacyOperativo() ? "SI" : "no") + "."));
			}
			EspiarTeclado();
			if (!TeclaPulsada())
			{
				return;
			}
			Plugin.Log.LogInfo((object)$"Pruebas: {Tecla.Value} detectada.");
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				Plugin.Log.LogWarning((object)"F3: solo funciona si eres el anfitrion de la partida.");
				return;
			}
			if ((Object)(object)ValuableDirector.instance == (Object)null)
			{
				Plugin.Log.LogWarning((object)"F3: todavia no estas en un nivel.");
				return;
			}
			int num = 0;
			for (int i = 0; i < Cantidad.Value; i++)
			{
				if (Generar(i))
				{
					num++;
				}
			}
			if (num > 0)
			{
				Plugin.Log.LogInfo((object)$"F3: generados {num} objeto(s) de prueba.");
			}
		}

		private static List<PrefabRef> ElegirLista()
		{
			string[] listasPorTamano = _listasPorTamano;
			foreach (string text in listasPorTamano)
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(ValuableDirector), text);
				if (!(fieldInfo == null) && fieldInfo.GetValue(ValuableDirector.instance) is List<PrefabRef> { Count: >0 } list)
				{
					return list;
				}
			}
			return null;
		}

		private static bool Generar(int indice)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			PlayerAvatar val = SemiFunc.PlayerAvatarLocal();
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"F3: no encuentro al jugador local.");
				return false;
			}
			List<PrefabRef> list = ElegirLista();
			if (list == null)
			{
				Plugin.Log.LogWarning((object)"F3: las listas de objetos del nivel estan vacias.");
				return false;
			}
			PrefabRef val2 = list[Random.Range(0, list.Count)];
			Transform transform = ((Component)val).transform;
			float num = (float)indice * 0.9f;
			Vector3 val3 = new Vector3(Mathf.Sin(num), 0f, Mathf.Cos(num)) * 0.35f;
			Vector3 val4 = transform.position + transform.forward * 1.4f + Vector3.up * 1.1f + val3;
			GameObject val5 = ((GameManager.instance.gameMode != 0) ? PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)val2).ResourcePath, val4, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate<GameObject>(((PrefabRef<GameObject>)(object)val2).Prefab, val4, Quaternion.identity));
			if ((Object)(object)val5 == (Object)null)
			{
				Plugin.Log.LogWarning((object)("F3: no se pudo crear '" + ((PrefabRef<GameObject>)(object)val2).ResourcePath + "'."));
				return false;
			}
			ValuableObject component = val5.GetComponent<ValuableObject>();
			if ((Object)(object)component == (Object)null)
			{
				Plugin.Log.LogWarning((object)"F3: el prefab creado no tiene ValuableObject.");
				return false;
			}
			int num2 = Random.Range(ValorMin.Value, ValorMax.Value + 1);
			AccessTools.Field(typeof(ValuableObject), "dollarValueOverride").SetValue(component, num2);
			component.DollarValueSetLogic();
			if (SemiFunc.IsMultiplayer())
			{
				PhotonView component2 = val5.GetComponent<PhotonView>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.RPC("DollarValueSetRPC", (RpcTarget)1, new object[1] { (float)num2 });
				}
			}
			Plugin.Debug($"F3: '{((PrefabRef<GameObject>)(object)val2).PrefabName}' generado por ${num2}.");
			return true;
		}
	}
	internal static class ReviveEnExtractor
	{
		private sealed class Estado
		{
			public float Temporizador = -1f;

			public bool YaRevivido;
		}

		[HarmonyPatch(typeof(PlayerDeathHead), "Update")]
		private static class RevivirPorEstarDentro
		{
			private static void Postfix(PlayerDeathHead __instance, bool ___inExtractionPoint, bool ___triggered)
			{
				if (!Enabled.Value || !SemiFunc.IsMasterClientOrSingleplayer())
				{
					return;
				}
				Estado orCreateValue = _estados.GetOrCreateValue(__instance);
				if (!___inExtractionPoint || !___triggered)
				{
					orCreateValue.Temporizador = Segundos.Value;
					orCreateValue.YaRevivido = false;
				}
				else if (!orCreateValue.YaRevivido)
				{
					if (orCreateValue.Temporizador < 0f)
					{
						orCreateValue.Temporizador = Segundos.Value;
					}
					orCreateValue.Temporizador -= Time.deltaTime;
					if (!(orCreateValue.Temporizador > 0f) && !((Object)(object)__instance.playerAvatar == (Object)null))
					{
						orCreateValue.YaRevivido = true;
						Plugin.Debug("Reviviendo a " + ((Object)__instance.playerAvatar).name + " desde el extractor.");
						__instance.Revive();
					}
				}
			}
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> Segundos;

		private static readonly ConditionalWeakTable<PlayerDeathHead, Estado> _estados = new ConditionalWeakTable<PlayerDeathHead, Estado>();

		internal static void BindConfig(ConfigFile config)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			Enabled = config.Bind<bool>("RevivirEnExtractor", "Activado", true, "Si esta activado, dejar la cabeza de un companero muerto dentro de un extractor lo revive tras unos segundos, sin necesidad de completar la extraccion.");
			Segundos = config.Bind<float>("RevivirEnExtractor", "SegundosDeEspera", 2f, new ConfigDescription("Tiempo que la cabeza debe permanecer dentro del extractor antes de revivir. El camion usa 2 segundos en vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
		}
	}
	internal enum Rol : byte
	{
		Ninguno,
		Medico,
		Ingeniero,
		Saboteador,
		Rastreador
	}
	internal static class Roles
	{
		[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
		private static class MarcarRepartoAlCambiarNivel
		{
			private static void Postfix()
			{
				ComprobacionDeVersion.AlCambiarNivel();
				if (Enabled != null && Enabled.Value)
				{
					if (_repartoHecho && !RepartirCadaNivel.Value)
					{
						_esperaAnuncio = 4f;
						return;
					}
					_pendienteRepartir = true;
					_esperaReparto = 3f;
					Plugin.Debug("Roles: cambio de nivel, reparto pendiente.");
				}
			}
		}

		[HarmonyPatch(typeof(RunManager), "Update")]
		private static class TicPorFrame
		{
			private static void Postfix()
			{
				AsegurarRed();
				ComprobacionDeVersion.Tick();
				TicDelRecordatorio();
				if (Enabled == null || !Enabled.Value)
				{
					return;
				}
				if (_pendienteRepartir)
				{
					IntentarRepartir();
				}
				RevisarHuerfanos();
				RedifundirDeVezEnCuando();
				if (_esperaAnuncio > 0f)
				{
					_esperaAnuncio -= Time.deltaTime;
					if (_esperaAnuncio <= 0f)
					{
						_esperaAnuncio = -1f;
						Anunciar();
					}
				}
				RolMedico.Tick();
				RolIngeniero.Tick();
				RolSaboteador.Tick();
				RolRastreador.Tick();
			}
		}

		internal enum EscribiendoResultado
		{
			No,
			Si,
			NoSePuedeSaber
		}

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> MinJugadores;

		internal static ConfigEntry<bool> RepartirCadaNivel;

		internal static ConfigEntry<bool> AnunciarRol;

		internal static ConfigEntry<bool> RecordarRol;

		internal static ConfigEntry<KeyCode> TeclaRecordar;

		internal static readonly Color ColorMedico = new Color(0.4f, 1f, 0.5f);

		internal static readonly Color ColorIngeniero = new Color(0.4f, 0.8f, 1f);

		internal static readonly Color ColorSaboteador = new Color(1f, 0.3f, 0.3f);

		internal static readonly Color ColorRastreador = new Color(1f, 0.85f, 0.3f);

		internal static readonly Color ColorSinRol = new Color(0.75f, 0.75f, 0.75f);

		private static readonly Dictionary<string, Rol> _asignados = new Dictionary<string, Rol>();

		private static Rol _local = Rol.Ninguno;

		private static bool _repartoHecho;

		private static bool _pendienteRepartir;

		private static float _esperaReparto;

		private static float _esperaAnuncio = -1f;

		private static readonly FieldInfo _campoTemporizadorUI = AccessTools.Field(typeof(MissionUI), "messageTimer");

		private static bool _recordatorioEnPantalla;

		private static readonly FieldInfo _campoChatActivo = AccessTools.Field(typeof(ChatManager), "chatActive");

		internal const byte EventoReparto = 175;

		internal const byte EventoSabotaje = 176;

		internal const byte EventoApagon = 177;

		internal const byte EventoVersion = 178;

		internal const byte EventoRecarga = 179;

		private static bool _redConectada;

		private static float _proximaRevisionHuerfanos = 5f;

		private static float _proximaRedifusion = 20f;

		internal static Rol Local
		{
			get
			{
				if (Enabled == null || !Enabled.Value)
				{
					return Rol.Ninguno;
				}
				return _local;
			}
		}

		internal static event Action AlRepartir;

		internal static void BindConfig(ConfigFile config)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Roles", "Activado", true, "Reparte roles secretos (medico, ingeniero, saboteador, rastreador) al empezar cada nivel. Cada rol se activa ademas por su cuenta en su propia seccion.");
			MinJugadores = config.Bind<int>("Roles", "MinimoJugadores", 3, new ConfigDescription("Por debajo de esta cantidad de jugadores no se reparte ningun rol. Con dos personas un saboteador no tiene gracia.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			RepartirCadaNivel = config.Bind<bool>("Roles", "RepartirCadaNivel", true, "true: se vuelven a sortear los roles en cada nivel, asi todos van rotando. false: se sortean una vez y duran toda la partida.");
			AnunciarRol = config.Bind<bool>("Roles", "AnunciarRol", true, "Muestra en pantalla que rol te ha tocado al empezar el nivel. Solo lo ves tu: el reparto de los demas no se anuncia a nadie.");
			RecordarRol = config.Bind<bool>("Roles", "RecordarRol", true, "Permite volver a ver en cualquier momento que rol tienes y como se usa, pulsando la tecla de abajo. El aviso del principio del nivel se pierde enseguida y luego no habia forma de consultarlo.");
			TeclaRecordar = config.Bind<KeyCode>("Roles", "TeclaRecordar", (KeyCode)9, "Tecla que recuerda tu rol. Tab es tambien la del mapa del juego, asi que las dos cosas salen a la vez; no se pisan, pero se puede cambiar aqui.");
		}

		internal static bool SoyEl(Rol rol)
		{
			if (rol != Rol.Ninguno)
			{
				return Local == rol;
			}
			return false;
		}

		internal static bool RolEnJuego(Rol rol)
		{
			if (Enabled == null || !Enabled.Value)
			{
				return false;
			}
			List<PlayerAvatar> list = SemiFunc.PlayerGetAll();
			if (list == null || list.Count == 0)
			{
				return false;
			}
			foreach (KeyValuePair<string, Rol> asignado in _asignados)
			{
				if (asignado.Value != rol)
				{
					continue;
				}
				foreach (PlayerAvatar item in list)
				{
					if (!((Object)(object)item == (Object)null) && SemiFunc.PlayerGetSteamID(item) == asignado.Key)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static void IntentarRepartir()
		{
			_esperaReparto -= Time.deltaTime;
			if (!(_esperaReparto > 0f) && !((Object)(object)RunManager.instance == (Object)null) && SemiFunc.RunIsLevel() && !((Object)(object)PlayerAvatar.instance == (Object)null) && (!SemiFunc.IsMultiplayer() || PhotonNetwork.InRoom) && SemiFunc.IsMasterClientOrSingleplayer())
			{
				Repartir();
			}
		}

		private static void Repartir()
		{
			_pendienteRepartir = false;
			List<PlayerAvatar> list = SemiFunc.PlayerGetAll();
			if (list == null || list.Count == 0)
			{
				_pendienteRepartir = true;
				_esperaReparto = 1f;
				return;
			}
			_asignados.Clear();
			if (list.Count < MinJugadores.Value)
			{
				Plugin.Debug($"Roles: solo {list.Count} jugadores, minimo {MinJugadores.Value}. Sin roles.");
				Difundir(repartoNuevo: true);
				Aplicar(repartoNuevo: true);
				return;
			}
			List<Rol> list2 = new List<Rol>();
			if (RolIngeniero.Enabled.Value)
			{
				list2.Add(Rol.Ingeniero);
			}
			if (RolMedico.Enabled.Value)
			{
				list2.Add(Rol.Medico);
			}
			if (RolSaboteador.Enabled.Value)
			{
				list2.Add(Rol.Saboteador);
			}
			if (RolRastreador.Enabled.Value)
			{
				list2.Add(Rol.Rastreador);
			}
			List<PlayerAvatar> list3 = new List<PlayerAvatar>(list);
			for (int num = list3.Count - 1; num > 0; num--)
			{
				int index = Random.Range(0, num + 1);
				PlayerAvatar value = list3[num];
				list3[num] = list3[index];
				list3[index] = value;
			}
			int num2 = 0;
			for (int i = 0; i < list3.Count; i++)
			{
				if (num2 >= list2.Count)
				{
					break;
				}
				string text = SemiFunc.PlayerGetSteamID(list3[i]);
				if (!string.IsNullOrEmpty(text))
				{
					_asignados[text] = list2[num2];
					num2++;
				}
			}
			_repartoHecho = true;
			Plugin.Debug($"Roles: repartidos {num2} de {list2.Count} entre {list.Count} jugadores.");
			Difundir(repartoNuevo: true);
			Aplicar(repartoNuevo: true);
		}

		private static void Aplicar(bool repartoNuevo)
		{
			Rol local = _local;
			_local = Rol.Ninguno;
			PlayerAvatar instance = PlayerAvatar.instance;
			if ((Object)(object)instance != (Object)null)
			{
				string text = SemiFunc.PlayerGetSteamID(instance);
				if (!string.IsNullOrEmpty(text) && _asignados.TryGetValue(text, out var value))
				{
					_local = value;
				}
			}
			bool flag = _local != local;
			if (repartoNuevo || flag)
			{
				Plugin.Log.LogInfo((object)$"Roles: me ha tocado {_local}.");
			}
			if ((repartoNuevo || flag) && Roles.AlRepartir != null)
			{
				Roles.AlRepartir();
			}
			if (repartoNuevo || flag)
			{
				_esperaAnuncio = (AnunciarRol.Value ? 2f : (-1f));
			}
		}

		private static void Anunciar()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			switch (_local)
			{
			case Rol.Medico:
				SemiFunc.UIBigMessage("ERES EL MEDICO", "+", 40f, new Color(0.4f, 1f, 0.5f), Color.white);
				RolMedico.AvisarUso();
				break;
			case Rol.Ingeniero:
				SemiFunc.UIBigMessage("ERES EL INGENIERO", "*", 40f, new Color(0.4f, 0.8f, 1f), Color.white);
				RolIngeniero.AvisarUso();
				break;
			case Rol.Saboteador:
				SemiFunc.UIBigMessage("ERES EL SABOTEADOR", "!", 40f, new Color(1f, 0.3f, 0.3f), Color.white);
				RolSaboteador.AvisarUso();
				break;
			case Rol.Rastreador:
				SemiFunc.UIBigMessage("ERES EL RASTREADOR", "?", 40f, new Color(1f, 0.85f, 0.3f), Color.white);
				RolRastreador.AvisarUso();
				break;
			}
		}

		private static string TextoDelRecordatorio(out Color color)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			if (Enabled != null && Enabled.Value)
			{
				switch (_local)
				{
				case Rol.Medico:
					color = ColorMedico;
					return "ERES EL MEDICO  -  " + RolMedico.TextoDeUso();
				case Rol.Ingeniero:
					color = ColorIngeniero;
					return "ERES EL INGENIERO  -  " + RolIngeniero.TextoDeUso();
				case Rol.Saboteador:
					color = ColorSaboteador;
					return "ERES EL SABOTEADOR  -  " + RolSaboteador.TextoDeUso();
				case Rol.Rastreador:
					color = ColorRastreador;
					return "ERES EL RASTREADOR  -  " + RolRastreador.TextoDeUso();
				}
			}
			color = ColorSinRol;
			if (Enabled == null || !Enabled.Value)
			{
				return "SIN ROL  -  los roles estan desactivados en la configuracion";
			}
			if (!_repartoHecho)
			{
				return "SIN ROL  -  todavia no se han repartido los roles de este nivel";
			}
			int num = SemiFunc.PlayerGetAll()?.Count ?? 0;
			if (num >= MinJugadores.Value)
			{
				return "SIN ROL  -  este nivel no te ha tocado ningun rol";
			}
			return $"SIN ROL  -  hacen falta {MinJugadores.Value} jugadores y sois {num}";
		}

		private static bool MantenerAvisoVivo(float segundos)
		{
			if (_campoTemporizadorUI == null)
			{
				return false;
			}
			MissionUI instance = MissionUI.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			_campoTemporizadorUI.SetValue(instance, segundos);
			return true;
		}

		private static void TicDelRecordatorio()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if (RecordarRol == null || !RecordarRol.Value)
			{
				return;
			}
			if (!EnPartida())
			{
				_recordatorioEnPantalla = false;
			}
			else if (TeclaPulsada(TeclaRecordar.Value))
			{
				SemiFunc.UIFocusText(TextoDelRecordatorio(out var color), color, Color.white, 0.4f);
				_recordatorioEnPantalla = true;
			}
			else if (_recordatorioEnPantalla)
			{
				if (TeclaMantenida(TeclaRecordar.Value))
				{
					MantenerAvisoVivo(0.4f);
				}
				else
				{
					_recordatorioEnPantalla = false;
				}
			}
		}

		internal static EscribiendoResultado Escribiendo()
		{
			if (_campoChatActivo == null)
			{
				return EscribiendoResultado.NoSePuedeSaber;
			}
			ChatManager instance = ChatManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return EscribiendoResultado.No;
			}
			object value = _campoChatActivo.GetValue(instance);
			if (!(value is bool))
			{
				return EscribiendoResultado.NoSePuedeSaber;
			}
			if (!(bool)value)
			{
				return EscribiendoResultado.No;
			}
			return EscribiendoResultado.Si;
		}

		internal unsafe static bool TeclaMantenida(KeyCode tecla)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (Escribiendo() == EscribiendoResultado.Si)
			{
				return false;
			}
			try
			{
				Keyboard current = Keyboard.current;
				if (current != null && Enum.TryParse<Key>(((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString(), ignoreCase: true, out Key result))
				{
					KeyControl val = current[result];
					if (val != null && ((ButtonControl)val).isPressed)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			try
			{
				if (Input.GetKey(tecla))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal unsafe static bool TeclaPulsada(KeyCode tecla)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (Escribiendo() == EscribiendoResultado.Si)
			{
				return false;
			}
			try
			{
				Keyboard current = Keyboard.current;
				if (current != null && Enum.TryParse<Key>(((object)(*(KeyCode*)(&tecla))/*cast due to .constrained prefix*/).ToString(), ignoreCase: true, out Key result))
				{
					KeyControl val = current[result];
					if (val != null && ((ButtonControl)val).wasPressedThisFrame)
					{
						return true;
					}
				}
			}
			catch
			{
			}
			try
			{
				if (Input.GetKeyDown(tecla))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static bool EnPartida()
		{
			if ((Object)(object)RunManager.instance != (Object)null && (Object)(object)PlayerAvatar.instance != (Object)null)
			{
				return SemiFunc.RunIsLevel();
			}
			return false;
		}

		private static void AsegurarRed()
		{
			if (!_redConectada && PhotonNetwork.NetworkingClient != null)
			{
				PhotonNetwork.NetworkingClient.EventReceived -= AlRecibirEvento;
				PhotonNetwork.NetworkingClient.EventReceived += AlRecibirEvento;
				_redConectada = true;
				Plugin.Debug("Roles: enganchado al canal de eventos de Photon.");
			}
		}

		internal static void DesconectarRed()
		{
			if (_redConectada)
			{
				if (PhotonNetwork.NetworkingClient != null)
				{
					PhotonNetwork.NetworkingClient.EventReceived -= AlRecibirEvento;
				}
				_redConectada = false;
			}
		}

		private static void AlRecibirEvento(EventData datos)
		{
			try
			{
				switch (datos.Code)
				{
				case 175:
					RecibirReparto(datos.CustomData);
					break;
				case 176:
					if (SemiFunc.IsMasterClient())
					{
						RolSaboteador.EjecutarBoostEnemigos(datos.CustomData);
					}
					break;
				case 177:
					RolSaboteador.EjecutarApagon(datos.CustomData);
					break;
				case 178:
					ComprobacionDeVersion.Recibir(datos.CustomData);
					break;
				case 179:
					if (SemiFunc.IsMasterClient())
					{
						RolIngeniero.EjecutarRecargaRemota(datos.CustomData);
					}
					break;
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)($"Roles: fallo procesando el evento {datos.Code}. " + $"Se ignora para no tumbar la conexion.\n{arg}"));
			}
		}

		private static void RevisarHuerfanos()
		{
			if (!_repartoHecho || !SemiFunc.IsMasterClientOrSingleplayer() || !EnPartida())
			{
				return;
			}
			_proximaRevisionHuerfanos -= Time.deltaTime;
			if (_proximaRevisionHuerfanos > 0f)
			{
				return;
			}
			_proximaRevisionHuerfanos = 5f;
			List<PlayerAvatar> list = SemiFunc.PlayerGetAll();
			if (list == null || list.Count == 0)
			{
				return;
			}
			List<string> list2 = new List<string>();
			foreach (PlayerAvatar item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					string text = SemiFunc.PlayerGetSteamID(item);
					if (!string.IsNullOrEmpty(text))
					{
						list2.Add(text);
					}
				}
			}
			if (list2.Count == 0)
			{
				return;
			}
			List<string> list3 = new List<string>();
			List<Rol> list4 = new List<Rol>();
			foreach (KeyValuePair<string, Rol> asignado in _asignados)
			{
				if (!list2.Contains(asignado.Key))
				{
					list3.Add(asignado.Key);
					list4.Add(asignado.Value);
				}
			}
			if (list3.Count == 0)
			{
				return;
			}
			foreach (string item2 in list3)
			{
				_asignados.Remove(item2);
			}
			List<string> list5 = new List<string>();
			foreach (string item3 in list2)
			{
				if (!_asignados.ContainsKey(item3))
				{
					list5.Add(item3);
				}
			}
			list4.Sort((Rol a, Rol b) => Prioridad(a).CompareTo(Prioridad(b)));
			int num = 0;
			for (int num2 = 0; num2 < list4.Count && num2 < list5.Count; num2++)
			{
				_asignados[list5[num2]] = list4[num2];
				num++;
			}
			Plugin.Log.LogInfo((object)($"Roles: {list3.Count} rol(es) sin dueno tras una desconexion. " + $"Reasignados {num}, descartados {list4.Count - num}."));
			Difundir(repartoNuevo: false);
			Aplicar(repartoNuevo: false);
		}

		private static int Prioridad(Rol rol)
		{
			return rol switch
			{
				Rol.Ingeniero => 0, 
				Rol.Medico => 1, 
				Rol.Saboteador => 2, 
				_ => 3, 
			};
		}

		private static void RedifundirDeVezEnCuando()
		{
			if (_repartoHecho && SemiFunc.IsMasterClient())
			{
				_proximaRedifusion -= Time.deltaTime;
				if (!(_proximaRedifusion > 0f))
				{
					_proximaRedifusion = 20f;
					Difundir(repartoNuevo: false);
				}
			}
		}

		private static void Difundir(bool repartoNuevo)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			if (!SemiFunc.IsMultiplayer() || PhotonNetwork.NetworkingClient == null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(repartoNuevo ? 'N' : 'R').Append('|');
			bool flag = true;
			foreach (KeyValuePair<string, Rol> asignado in _asignados)
			{
				if (!flag)
				{
					stringBuilder.Append(';');
				}
				flag = false;
				stringBuilder.Append(asignado.Key).Append(':').Append((byte)asignado.Value);
			}
			PhotonNetwork.RaiseEvent((byte)175, (object)stringBuilder.ToString(), new RaiseEventOptions
			{
				Receivers = (ReceiverGroup)0
			}, SendOptions.SendReliable);
		}

		private static void RecibirReparto(object datos)
		{
			if (!(datos is string text))
			{
				return;
			}
			int num = text.IndexOf('|');
			if (num < 0)
			{
				return;
			}
			bool repartoNuevo = text.Substring(0, num) == "N";
			string text2 = text.Substring(num + 1);
			Dictionary<string, Rol> dictionary = new Dictionary<string, Rol>();
			if (text2.Length > 0)
			{
				string[] array = text2.Split(';');
				foreach (string text3 in array)
				{
					int num2 = text3.LastIndexOf(':');
					if (num2 > 0 && num2 != text3.Length - 1 && byte.TryParse(text3.Substring(num2 + 1), out var result))
					{
						dictionary[text3.Substring(0, num2)] = (Rol)result;
					}
				}
			}
			if (EsElMismoReparto(dictionary))
			{
				return;
			}
			_asignados.Clear();
			foreach (KeyValuePair<string, Rol> item in dictionary)
			{
				_asignados[item.Key] = item.Value;
			}
			_repartoHecho = true;
			Aplicar(repartoNuevo);
		}

		private static bool EsElMismoReparto(Dictionary<string, Rol> otro)
		{
			if (!_repartoHecho)
			{
				return false;
			}
			if (_asignados.Count != otro.Count)
			{
				return false;
			}
			foreach (KeyValuePair<string, Rol> item in otro)
			{
				if (!_asignados.TryGetValue(item.Key, out var value))
				{
					return false;
				}
				if (value != item.Value)
				{
					return false;
				}
			}
			return true;
		}
	}
	internal static class RolIngeniero
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyCode> Tecla;

		internal static ConfigEntry<int> Cargas;

		internal static ConfigEntry<float> MinutosRecarga;

		private static int _cargas;

		private static float _temporizador;

		private static readonly FieldInfo _campoAgarrado = AccessTools.Field(typeof(PhysGrabber), "grabbedPhysGrabObject");

		internal static void BindConfig(ConfigFile config)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			Enabled = config.Bind<bool>("Ingeniero", "Activado", true, "Mete el rol de ingeniero en el sorteo. El ingeniero recarga la bateria del objeto que lleve en las manos.");
			Tecla = config.Bind<KeyCode>("Ingeniero", "Tecla", (KeyCode)106, "Tecla que recarga a tope el objeto que tengas agarrado.");
			config.Remove(new ConfigDefinition("Ingeniero", "AvisarAlRechazar"));
			Cargas = config.Bind<int>("Ingeniero", "Cargas", 5, new ConfigDescription("Reparaciones disponibles a la vez.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()));
			MinutosRecarga = config.Bind<float>("Ingeniero", "MinutosRecarga", 5f, new ConfigDescription("Cada cuantos minutos se recupera UNA reparacion.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 30f), Array.Empty<object>()));
			Roles.AlRepartir += delegate
			{
				if (Roles.SoyEl(Rol.Ingeniero))
				{
					Reiniciar();
				}
			};
		}

		internal static void Reiniciar()
		{
			_cargas = Cargas.Value;
			_temporizador = MinutosRecarga.Value * 60f;
		}

		internal static string TextoDeUso()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			return $"[{Tecla.Value}] recargar lo que lleves en las manos  -  {_cargas}/{Cargas.Value} reparaciones";
		}

		internal static void AvisarUso()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			SemiFunc.UIFocusText(TextoDeUso(), Roles.ColorIngeniero, Color.white, 5f);
		}

		private static PhysGrabObject LoQueLlevoEnLasManos()
		{
			PhysGrabber instance = PhysGrabber.instance;
			if ((Object)(object)instance == (Object)null || !instance.grabbed)
			{
				return null;
			}
			if (_campoAgarrado == null)
			{
				return null;
			}
			object? value = _campoAgarrado.GetValue(instance);
			return (PhysGrabObject)((value is PhysGrabObject) ? value : null);
		}

		internal static void Tick()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (Enabled != null && Enabled.Value && Roles.SoyEl(Rol.Ingeniero) && Roles.EnPartida())
			{
				RecargarCargas();
				if (Roles.TeclaPulsada(Tecla.Value))
				{
					Reparar();
				}
			}
		}

		private static void RecargarCargas()
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (_cargas < Cargas.Value)
			{
				_temporizador -= Time.deltaTime;
				if (!(_temporizador > 0f))
				{
					_cargas++;
					_temporizador = MinutosRecarga.Value * 60f;
					SemiFunc.UIFocusText($"Reparacion recuperada  ({_cargas}/{Cargas.Value})", new Color(0.4f, 0.8f, 1f), Color.white, 3f);
					Plugin.Debug($"Ingeniero: reparacion recuperada, ahora {_cargas}/{Cargas.Value}. " + $"Siguiente en {MinutosRecarga.Value * 60f:0} s.");
				}
			}
		}

		private static void Reparar()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Expected O, but got Unknown
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			Color val = default(Color);
			((Color)(ref val))..ctor(0.4f, 0.8f, 1f);
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(1f, 0.6f, 0.2f);
			if (_cargas <= 0)
			{
				int num = Mathf.CeilToInt(_temporizador);
				SemiFunc.UIFocusText($"Sin reparaciones  -  {num / 60}:{num % 60:00} para la siguiente", val2, Color.white, 3f);
				return;
			}
			PhysGrabObject val3 = LoQueLlevoEnLasManos();
			if ((Object)(object)val3 == (Object)null)
			{
				SemiFunc.UIFocusText("Agarra algo para repararlo", val2, Color.white, 2f);
				return;
			}
			ItemBattery componentInChildren = ((Component)val3).GetComponentInChildren<ItemBattery>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{