Decompiled source of STARCODE Events v0.5.0

plugins/Starcode.Events.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace Starcode.Events;

public sealed class Card
{
	public string Id;

	public string Ja;

	public string En;

	public string DescriptionJa;

	public string DescriptionEn;

	public string Icon;

	public string Effect;

	public string Attribute;

	public bool Permanent;

	public int Danger;

	public int DeckCount;

	public int Amount;

	public double WeightPerCard;

	[JsonIgnore]
	public double Weight => (double)DeckCount * WeightPerCard;
}
public sealed class EventSettings
{
	public double DebuffPercent = 95.0;

	public double BuffPercent = 5.0;

	public Card[] Events;

	public static EventSettings Read(string json, string defaultsJson)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Invalid comparison between Unknown and I4
		JToken val = JToken.Parse(json);
		if ((int)val.Type != 2)
		{
			return val.ToObject<EventSettings>();
		}
		EventSettings eventSettings = JsonConvert.DeserializeObject<EventSettings>(defaultsJson);
		List<Card> list = eventSettings.Events.ToList();
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		Card[] array = val.ToObject<Card[]>();
		foreach (Card previous in array)
		{
			if (previous == null || string.IsNullOrWhiteSpace(previous.Id) || !hashSet.Add(previous.Id))
			{
				throw new FormatException("Invalid or duplicate event ID in legacy configuration.");
			}
			Card card = list.Find((Card c) => c.Id == previous.Id);
			if (card != null)
			{
				card.DeckCount = previous.DeckCount;
				card.WeightPerCard = previous.WeightPerCard;
				continue;
			}
			previous.Attribute = ((previous.Effect == "apex") ? "debuff" : "buff");
			previous.Amount = ((previous.Effect == "maxHp") ? 2 : ((previous.Effect == "heal") ? 25 : 0));
			if (previous.Effect == "maxHp")
			{
				previous.DescriptionJa = "最大HPが1回につき2増える。";
				previous.DescriptionEn = "Gain 2 maximum HP per stack.";
			}
			list.Add(previous);
		}
		eventSettings.Events = list.ToArray();
		return eventSettings;
	}
}
public sealed class EventOdds
{
	public readonly Card Card;

	public readonly double FirstSlot;

	public readonly double InOffer;

	public EventOdds(Card card, double firstSlot, double inOffer)
	{
		Card = card;
		FirstSlot = firstSlot;
		InOffer = inOffer;
	}
}
public sealed class Catalog
{
	private readonly Card[] cards;

	private readonly Random random;

	private readonly double debuffChance;

	public Catalog(EventSettings settings, Random random)
	{
		if (settings == null || !(settings.DebuffPercent >= 0.0) || !(settings.DebuffPercent <= 100.0) || !(settings.BuffPercent >= 0.0) || !(settings.BuffPercent <= 100.0) || Math.Abs(settings.DebuffPercent + settings.BuffPercent - 100.0) > 1E-09)
		{
			throw new FormatException("DebuffPercent and BuffPercent must be between 0 and 100 and sum to 100.");
		}
		Card[] events = settings.Events;
		if (events == null || events.Any((Card c) => c == null || string.IsNullOrWhiteSpace(c.Id) || string.IsNullOrWhiteSpace(c.Ja) || c.DeckCount < 0 || c.WeightPerCard < 0.0 || double.IsNaN(c.Weight) || double.IsInfinity(c.Weight) || c.Danger < 1 || c.Danger > 5 || !SupportedEffect(c)))
		{
			throw new FormatException("Invalid event definition.");
		}
		if (events.Select((Card c) => c.Id).Distinct<string>(StringComparer.Ordinal).Count() != events.Length)
		{
			throw new FormatException("Duplicate event ID.");
		}
		cards = events.Where((Card c) => c.Weight > 0.0).ToArray();
		if (cards.Length < 3 || double.IsInfinity(cards.Sum((Card c) => c.Weight)))
		{
			throw new FormatException("At least three positive weights and a finite total are required.");
		}
		this.random = random;
		debuffChance = settings.DebuffPercent / 100.0;
	}

	private static bool SupportedEffect(Card card)
	{
		bool flag = card.Effect == "maxHp" || card.Effect == "maxStamina";
		bool flag2 = card.Effect == "moveSpeed";
		bool flag3 = card.Effect == "healScale";
		bool flag4 = card.Effect == "enemyHp" || card.Effect == "enemySpeed" || card.Effect == "enemyVision";
		if (card.Attribute == "debuff")
		{
			if ((!(card.Effect == "apex") || card.Permanent || card.Amount != 0) && ((!flag && !flag2) || card.Amount < (flag2 ? (-90) : (-100)) || card.Amount >= 0) && (!flag4 || card.Amount <= 0) && (!flag3 || card.Amount < -100 || card.Amount >= 0) && (!(card.Effect == "enemyAttack") || card.Permanent || card.Amount <= 0 || card.Amount > 100) && (!(card.Effect == "mapJam") || card.Permanent || card.Amount != 1) && (!(card.Effect == "hunterAim") || card.Permanent || card.Amount <= 0 || card.Amount > 75) && (!(card.Effect == "quota") || card.Permanent || card.Amount <= 0 || card.Amount > 100) && (!(card.Effect == "enemyAudio") || card.Permanent || card.Amount < -100 || card.Amount >= 0) && (!(card.Effect == "treasureMass") || card.Permanent || card.Amount <= 0 || card.Amount > 100) && (!(card.Effect == "treasureValue") || card.Permanent || card.Amount < -90 || card.Amount >= 0) && (!(card.Effect == "durability") || card.Permanent || card.Amount < -90 || card.Amount >= 0))
			{
				if (card.Effect == "extraSlots" && !card.Permanent && card.Amount > 0)
				{
					return card.Amount <= 3;
				}
				return false;
			}
			return true;
		}
		if (card.Attribute == "buff")
		{
			if ((!(card.Effect == "heal") || card.Permanent || card.Amount <= 0) && ((!flag && !flag2) || card.Amount <= 0 || (card.Permanent && card.Amount > 2)) && (!flag4 || card.Amount < -90 || card.Amount >= 0 || (card.Permanent && card.Amount < -2)))
			{
				if (flag3 && card.Amount > 0)
				{
					if (card.Permanent)
					{
						return card.Amount <= 2;
					}
					return true;
				}
				return false;
			}
			return true;
		}
		return false;
	}

	public Card[] Draw()
	{
		List<Card> list = cards.ToList();
		Card[] array = new Card[3];
		for (int i = 0; i < array.Length; i++)
		{
			bool debuff = random.NextDouble() < debuffChance;
			Card[] array2 = list.Where((Card c) => c.Attribute == "debuff" == debuff).ToArray();
			if (array2.Length == 0)
			{
				array2 = list.ToArray();
			}
			double num = random.NextDouble() * array2.Sum((Card c) => c.Weight);
			int num2 = 0;
			while (num2 < array2.Length - 1 && num >= array2[num2].Weight)
			{
				num -= array2[num2++].Weight;
			}
			array[i] = array2[num2];
			list.Remove(array[i]);
		}
		return array;
	}

	private double ConditionalChance(Card target, Card first, Card second)
	{
		double num = 0.0;
		double num2 = 0.0;
		Card[] array = cards;
		foreach (Card card in array)
		{
			if (card != first && card != second)
			{
				if (card.Attribute == target.Attribute)
				{
					num += card.Weight;
				}
				else
				{
					num2 += card.Weight;
				}
			}
		}
		double num3 = ((target.Attribute == "debuff") ? debuffChance : (1.0 - debuffChance));
		if (num2 == 0.0)
		{
			num3 = 1.0;
		}
		return num3 * (target.Weight / num);
	}

	public EventOdds[] GetOdds()
	{
		return cards.Select(delegate(Card card)
		{
			double num = ConditionalChance(card, null, null);
			double num2 = num;
			foreach (Card first in cards.Where((Card c) => c != card))
			{
				double num3 = ConditionalChance(first, null, null);
				num2 += num3 * ConditionalChance(card, first, null);
				Card[] source = cards;
				Func<Card, bool> predicate = (Card c) => c != card && c != first;
				foreach (Card item in source.Where(predicate))
				{
					num2 += num3 * ConditionalChance(item, first, null) * ConditionalChance(card, first, item);
				}
			}
			return new EventOdds(card, num, num2);
		}).ToArray();
	}

	public string[] Probabilities()
	{
		return (from odds in GetOdds()
			select $"{odds.Card.Id} ({odds.Card.Attribute}): first {odds.FirstSlot:P3}, any card {odds.InOffer:P3}").ToArray();
	}
}
public sealed class RunState
{
	private readonly Dictionary<string, int> stacks = new Dictionary<string, int>();

	public Card Active { get; private set; }

	public Card Selected { get; private set; }

	public bool Committed { get; private set; }

	public void BeginStage()
	{
		Active = null;
		Selected = null;
		Committed = false;
	}

	public void Reset()
	{
		BeginStage();
		stacks.Clear();
	}

	public int Count(string id)
	{
		if (!stacks.TryGetValue(id, out var value))
		{
			return 0;
		}
		return value;
	}

	public Dictionary<string, int> Snapshot()
	{
		return new Dictionary<string, int>(stacks);
	}

	public void Restore(IEnumerable<KeyValuePair<string, int>> saved, IEnumerable<Card> definitions)
	{
		Reset();
		HashSet<string> hashSet = new HashSet<string>(definitions.Select((Card card) => card.Id), StringComparer.Ordinal);
		foreach (KeyValuePair<string, int> item in saved)
		{
			if (item.Value > 0 && hashSet.Contains(item.Key))
			{
				stacks[item.Key] = item.Value;
			}
		}
	}

	public bool Commit(Card card)
	{
		if (Committed)
		{
			return false;
		}
		stacks[card.Id] = checked(Count(card.Id) + 1);
		if (!card.Permanent)
		{
			Active = card;
		}
		Selected = card;
		Committed = true;
		return true;
	}

	internal bool ClearActive()
	{
		if (Active == null)
		{
			return false;
		}
		Active = null;
		Selected = null;
		return true;
	}

	internal bool AddPermanent(Card card)
	{
		if (!card.Permanent || Count(card.Id) == int.MaxValue)
		{
			return false;
		}
		stacks[card.Id] = Count(card.Id) + 1;
		return true;
	}

	internal void ClearPermanent(IEnumerable<Card> definitions)
	{
		foreach (Card item in definitions.Where((Card c) => c.Permanent))
		{
			stacks.Remove(item.Id);
		}
	}

	public int MaxHpModifier(IEnumerable<Card> definitions)
	{
		return Modifier(definitions, "maxHp");
	}

	internal int Modifier(IEnumerable<Card> definitions, string effect)
	{
		long num = 0L;
		foreach (Card definition in definitions)
		{
			if (definition.Permanent && definition.Effect == effect)
			{
				num += (long)definition.Amount * (long)Count(definition.Id);
			}
		}
		if (Active != null && Active.Effect == effect)
		{
			num += Active.Amount;
		}
		return (int)Math.Max(-2147483648L, Math.Min(2147483647L, num));
	}
}
public sealed class StageVote
{
	private readonly HashSet<int> eligible;

	private readonly Dictionary<int, int> votes = new Dictionary<int, int>();

	public int EligibleCount => eligible.Count;

	public StageVote(IEnumerable<int> eligibleActors)
	{
		eligible = new HashSet<int>(eligibleActors);
	}

	public bool Cast(int actor, int card)
	{
		if (!eligible.Contains(actor) || card < 0 || card >= 3)
		{
			return false;
		}
		votes[actor] = card;
		return true;
	}

	public int[] Totals(IEnumerable<int> connected)
	{
		HashSet<int> hashSet = new HashSet<int>(connected);
		int[] array = new int[3];
		foreach (KeyValuePair<int, int> vote in votes)
		{
			if (hashSet.Contains(vote.Key))
			{
				array[vote.Value]++;
			}
		}
		return array;
	}

	public int[] Leaders(int host, IEnumerable<int> connected)
	{
		int[] totals = Totals(connected);
		int[] array = (from i in Enumerable.Range(0, 3)
			where totals[i] == totals.Max()
			select i).ToArray();
		if (!votes.TryGetValue(host, out var value) || !array.Contains(value))
		{
			return array;
		}
		return new int[1] { value };
	}
}
public sealed class StageSnapshot
{
	public int Serial;

	public int Revision;

	public int Stage;

	public string Round;

	public float SecondsRemaining;

	public Card[] Cards;

	public Card[] PermanentCards;

	public int[] Totals;

	public int[] Tied;

	public int[] HealActors;

	public int Eligible;

	public int MaxHpModifier;

	public int MaxStaminaModifier;

	public int MoveSpeedModifier;

	public int HealScaleModifier;

	public int EnemyAttackModifier;

	public int TreasureMassModifier;

	public int EnemyAudioModifier;

	public Dictionary<string, int> Stacks;

	public Card Selected;

	public string Enemy;

	public static bool FromHost(int sender, int currentHost, int observedHost)
	{
		if (sender > 0 && sender == currentHost)
		{
			return currentHost == observedHost;
		}
		return false;
	}

	public bool IsNewerThan(StageSnapshot previous)
	{
		if (Serial < 1 || Revision < 1 || string.IsNullOrEmpty(Round))
		{
			return false;
		}
		if (previous != null && Serial <= previous.Serial)
		{
			if (Serial == previous.Serial && Round == previous.Round)
			{
				return Revision > previous.Revision;
			}
			return false;
		}
		return true;
	}
}
[BepInPlugin("starcode.repo.events", "STARCODE R.E.P.O. Events", "0.5.0")]
public sealed class Plugin : BaseUnityPlugin
{
	private const string NetworkTag = "starcode.events.v8";

	private const byte SnapshotEvent = 171;

	private const byte VoteEvent = 172;

	private const string Id = "starcode.repo.events";

	private const string SaveKey = "starcodeEventSelectionsV1";

	private Room networkRoom;

	private int networkMaster;

	private int networkSerial;

	private bool authorityChanged;

	private float nextPublish;

	private float voteEndsAt;

	private StageVote ballot;

	private StageSnapshot networkSnapshot;

	private int localVote = -1;

	private string lastHealRound;

	private string traceOfferRound;

	private string traceResultRound;

	private string traceEffectRound;

	private string traceStaminaRound;

	private string traceMovementRound;

	private static readonly FieldInfo SaveDictionaries = AccessTools.Field(typeof(StatsManager), "dictionaryOfDictionaries");

	private static readonly FieldInfo MaxHp = AccessTools.Field(typeof(PlayerHealth), "maxHealth");

	private static readonly FieldInfo CurrentHp = AccessTools.Field(typeof(PlayerHealth), "health");

	private static readonly FieldInfo HealthSet = AccessTools.Field(typeof(PlayerHealth), "healthSet");

	private static readonly FieldInfo AvatarHealth = AccessTools.Field(typeof(PlayerAvatar), "playerHealth");

	private static readonly FieldInfo ControlledAvatar = AccessTools.Field(typeof(PlayerController), "playerAvatarScript");

	private static readonly FieldInfo SpawnObjects = AccessTools.Field(typeof(EnemySetup), "spawnObjects");

	private static readonly FieldInfo RankThree = AccessTools.Field(typeof(EnemyDirector), "enemiesDifficulty3");

	private static readonly FieldInfo DebugEnemies = AccessTools.Field(typeof(EnemyDirector), "debugEnemy");

	private static readonly FieldInfo LevelNumber = AccessTools.Field(typeof(LevelUI), "textNumber");

	private static readonly FieldInfo LevelMoon = AccessTools.Field(typeof(LevelUI), "textMoon");

	private static readonly FieldInfo MapActive = AccessTools.Field(typeof(MapToolController), "Active");

	private static readonly FieldInfo CurrentMenuPage = AccessTools.Field(typeof(MenuManager), "currentMenuPage");

	private static Plugin instance;

	private readonly Random random = new Random();

	private readonly RunState run = new RunState();

	private Catalog catalog;

	private Card[] definitions;

	private Card[] offer;

	private EnemySetup replacement;

	private readonly Dictionary<int, EnemySetup> apexGroups = new Dictionary<int, EnemySetup>();

	private bool selectingEnemies;

	private bool healPending;

	private int originalSlots;

	private int replacedSlots;

	private float deadline;

	private PlayerHealth trackedHealth;

	private int appliedBonus;

	private PlayerController staminaController;

	private float staminaApplied;

	private ConfigEntry<bool> english;

	private ConfigEntry<bool> debug;

	private Harmony harmony;

	private Font japaneseFont;

	private readonly Vector3[] hudCorners = (Vector3[])(object)new Vector3[4];

	private GUIStyle statusStyle;

	private EventSettings eventSettings;

	private Dictionary<string, EventOdds> menuOdds;

	private bool eventMenuOpen;

	private Vector2 eventMenuScroll;

	private Dictionary<string, int> pendingLoad;

	private bool consoleOpen;

	private string command = "";

	private string commandStatus = "";

	private float movementMultiplier = 1f;

	private static readonly FieldInfo EnemyCurrentHealth = AccessTools.Field(typeof(EnemyHealth), "healthCurrent");

	private readonly Dictionary<EnemyHealth, int> enemyBaseHealth = new Dictionary<EnemyHealth, int>();

	private int reconciledEnemyModifier;

	private static readonly FieldInfo EnemyAgent = AccessTools.Field(typeof(EnemyNavMeshAgent), "Agent");

	private readonly Dictionary<NavMeshAgent, float> enemyBaseSpeeds = new Dictionary<NavMeshAgent, float>();

	private int enemySpeedModifier;

	private float enemyVisionMultiplier = 1f;

	private bool buildingEnemySetup;

	private static readonly FieldInfo AvailableHaul = AccessTools.Field(typeof(RoundDirector), "haulGoalMax");

	private static readonly FieldInfo DamageSourceEnemy = AccessTools.Field(typeof(HurtCollider), "enemyHost");

	private static readonly FieldInfo DamageSourcePlayer = AccessTools.Field(typeof(HurtCollider), "playerCausingHurt");

	private static readonly FieldInfo Appraised = AccessTools.Field(typeof(ValuableObject), "dollarValueSet");

	private static readonly FieldInfo OriginalValue = AccessTools.Field(typeof(ValuableObject), "dollarValueOriginal");

	private static readonly FieldInfo CurrentValue = AccessTools.Field(typeof(ValuableObject), "dollarValueCurrent");

	private static readonly FieldInfo ValuableView = AccessTools.Field(typeof(ValuableObject), "photonView");

	private readonly Dictionary<ValuableObject, float> valuableOriginalAppraisals = new Dictionary<ValuableObject, float>();

	private readonly Dictionary<Rigidbody, float> valuableBaseMasses = new Dictionary<Rigidbody, float>();

	private int valuableMassModifier;

	private static readonly FieldInfo SoundLowPass = AccessTools.Field(typeof(Sound), "LowPassLogic");

	private static readonly FieldInfo LowPassVolume = AccessTools.Field(typeof(AudioLowPassLogic), "Volume");

	private readonly Dictionary<Type, FieldInfo[]> enemySoundFields = new Dictionary<Type, FieldInfo[]>();

	private readonly Dictionary<Sound, EnemyParent> enemySounds = new Dictionary<Sound, EnemyParent>();

	private readonly Dictionary<Sound, float> enemyLoopVolumes = new Dictionary<Sound, float>();

	private float enemyAudioMultiplier = 1f;

	private static readonly FieldInfo NativeMapActive = AccessTools.Field(typeof(Map), "Active");

	private static readonly FieldInfo NativeMapParent = AccessTools.Field(typeof(Map), "ActiveParent");

	private Map hiddenMap;

	private bool NetworkActive
	{
		get
		{
			if (Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMultiplayer())
			{
				return PhotonNetwork.InRoom;
			}
			return false;
		}
	}

	private bool SnapshotForStage
	{
		get
		{
			if (networkSnapshot != null && Object.op_Implicit((Object)(object)RunManager.instance) && SemiFunc.RunIsLevel())
			{
				return networkSnapshot.Stage == SemiFunc.RunGetLevelsCompleted();
			}
			return false;
		}
	}

	private void UpdateNetwork()
	{
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Expected O, but got Unknown
		Room val = (NetworkActive ? PhotonNetwork.CurrentRoom : null);
		int num = ((val != null && PhotonNetwork.MasterClient != null) ? PhotonNetwork.MasterClient.ActorNumber : 0);
		if (val != networkRoom || num != networkMaster)
		{
			bool flag = networkRoom != null;
			authorityChanged = val != null && val == networkRoom;
			networkRoom = val;
			networkMaster = num;
			networkSerial = 0;
			nextPublish = 0f;
			voteEndsAt = 0f;
			networkSnapshot = null;
			ballot = null;
			localVote = -1;
			lastHealRound = null;
			traceOfferRound = (traceResultRound = (traceEffectRound = (traceStaminaRound = (traceMovementRound = null))));
			if (flag || val != null)
			{
				run.Reset();
				offer = null;
				healPending = false;
				RestorePlayerEffects();
			}
			if (val != null)
			{
				Player localPlayer = PhotonNetwork.LocalPlayer;
				Hashtable val2 = new Hashtable();
				((Dictionary<object, object>)(object)val2).Add((object)"starcode.events.v8", (object)true);
				localPlayer.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null);
			}
			if (authorityChanged)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Event voting suspended after host migration. Start a new room to resume.");
			}
		}
		if (val == null || authorityChanged || !Object.op_Implicit((Object)(object)RunManager.instance))
		{
			return;
		}
		if (PhotonNetwork.IsMasterClient && networkSnapshot != null && Time.realtimeSinceStartup >= nextPublish)
		{
			PublishSnapshot();
		}
		if (!PhotonNetwork.IsMasterClient)
		{
			offer = ((SnapshotForStage && networkSnapshot.Selected == null && Time.realtimeSinceStartup < voteEndsAt) ? networkSnapshot.Cards : null);
			if (offer != null)
			{
				SemiFunc.CursorUnlock(0.15f);
			}
		}
	}

	private IEnumerator MultiplayerSelection()
	{
		double until = Time.realtimeSinceStartup + 3f;
		while (NetworkActive && PhotonNetwork.IsMasterClient && !authorityChanged && !AllPlayersCompatible() && (double)Time.realtimeSinceStartup < until)
		{
			yield return null;
		}
		if (!NetworkActive || !PhotonNetwork.IsMasterClient || authorityChanged || !AllPlayersCompatible())
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Skipped multiplayer events: every active player must have the same protocol.");
			yield break;
		}
		int host = PhotonNetwork.LocalPlayer.ActorNumber;
		ballot = new StageVote(ConnectedActors());
		localVote = -1;
		offer = catalog.Draw();
		voteEndsAt = Time.realtimeSinceStartup + 25f;
		networkSnapshot = new StageSnapshot
		{
			Serial = ++networkSerial,
			Stage = SemiFunc.RunGetLevelsCompleted(),
			Round = Guid.NewGuid().ToString("N"),
			Cards = offer,
			Eligible = ballot.EligibleCount
		};
		PublishSnapshot();
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Multiplayer offer: " + string.Join(", ", offer.Select((Card card) => card.Id).ToArray())));
		while (HasAuthority(host) && Time.realtimeSinceStartup < voteEndsAt)
		{
			SemiFunc.CursorUnlock(0.15f);
			yield return null;
		}
		if (!HasAuthority(host))
		{
			yield break;
		}
		if (!AllPlayersCompatible())
		{
			offer = null;
			ballot = null;
			voteEndsAt = 0f;
			PublishSnapshot();
			yield break;
		}
		int[] leaders = ballot.Leaders(host, ConnectedActors());
		if (leaders.Length > 1)
		{
			networkSnapshot.Tied = leaders;
			voteEndsAt = Time.realtimeSinceStartup + 8f;
			PublishSnapshot();
			while (HasAuthority(host) && offer != null && Time.realtimeSinceStartup < voteEndsAt)
			{
				SemiFunc.CursorUnlock(0.15f);
				yield return null;
			}
		}
		if (HasAuthority(host))
		{
			if (offer != null)
			{
				CommitSelection(leaders[0]);
			}
			networkSnapshot.Selected = run.Selected;
			networkSnapshot.HealActors = ConnectedActors();
			ballot = null;
			PublishSnapshot();
		}
	}

	private bool HasAuthority(int host)
	{
		if (NetworkActive && PhotonNetwork.IsMasterClient && !authorityChanged && PhotonNetwork.LocalPlayer.ActorNumber == host)
		{
			return networkSnapshot != null;
		}
		return false;
	}

	private bool AllPlayersCompatible()
	{
		object value;
		return PhotonNetwork.PlayerList.Where((Player player) => !player.IsInactive).All((Player player) => player.IsLocal || (((Dictionary<object, object>)(object)player.CustomProperties).TryGetValue((object)"starcode.events.v8", out value) && object.Equals(value, true)));
	}

	private static int[] ConnectedActors()
	{
		return (from player in PhotonNetwork.PlayerList
			where !player.IsInactive
			select player.ActorNumber).ToArray();
	}

	private void PublishSnapshot()
	{
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Expected O, but got Unknown
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
		if (NetworkActive && PhotonNetwork.IsMasterClient && networkSnapshot != null)
		{
			networkSnapshot.Revision++;
			networkSnapshot.Stacks = run.Snapshot();
			networkSnapshot.PermanentCards = definitions.Where((Card card) => card.Permanent && run.Count(card.Id) > 0).ToArray();
			networkSnapshot.MaxHpModifier = run.MaxHpModifier(definitions);
			networkSnapshot.MaxStaminaModifier = run.Modifier(definitions, "maxStamina");
			networkSnapshot.MoveSpeedModifier = run.Modifier(definitions, "moveSpeed");
			networkSnapshot.HealScaleModifier = run.Modifier(definitions, "healScale");
			networkSnapshot.EnemyAttackModifier = run.Modifier(definitions, "enemyAttack");
			networkSnapshot.TreasureMassModifier = run.Modifier(definitions, "treasureMass");
			networkSnapshot.EnemyAudioModifier = run.Modifier(definitions, "enemyAudio");
			if (ballot != null)
			{
				networkSnapshot.Totals = ballot.Totals(ConnectedActors());
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			networkSnapshot.SecondsRemaining = Mathf.Max(0f, voteEndsAt - realtimeSinceStartup);
			nextPublish = realtimeSinceStartup + 2f;
			object[] obj = new object[2]
			{
				"starcode.events.v8",
				JsonConvert.SerializeObject((object)networkSnapshot)
			};
			RaiseEventOptions val = new RaiseEventOptions();
			val.Receivers = (ReceiverGroup)0;
			PhotonNetwork.RaiseEvent((byte)171, (object)obj, val, SendOptions.SendReliable);
			TraceResult();
		}
	}

	private void Vote(int index)
	{
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Expected O, but got Unknown
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		if (!SnapshotForStage || networkSnapshot.Selected != null || Time.realtimeSinceStartup >= voteEndsAt)
		{
			return;
		}
		if (networkSnapshot.Tied != null)
		{
			if (PhotonNetwork.IsMasterClient && Extensions.Contains(networkSnapshot.Tied, index))
			{
				CommitSelection(index);
			}
			return;
		}
		int actorNumber = PhotonNetwork.LocalPlayer.ActorNumber;
		if (PhotonNetwork.IsMasterClient)
		{
			if (ballot == null || !ballot.Cast(actorNumber, index))
			{
				return;
			}
			PublishSnapshot();
		}
		else
		{
			object[] obj = new object[3] { "starcode.events.v8", networkSnapshot.Round, index };
			RaiseEventOptions val = new RaiseEventOptions();
			val.Receivers = (ReceiverGroup)2;
			if (!PhotonNetwork.RaiseEvent((byte)172, (object)obj, val, SendOptions.SendReliable))
			{
				return;
			}
		}
		localVote = index;
	}

	private void OnPhotonEvent(EventData message)
	{
		if (!NetworkActive || authorityChanged || (message.Code != 171 && message.Code != 172) || !(message.CustomData is object[] array) || array.Length < 2 || !object.Equals(array[0], "starcode.events.v8"))
		{
			return;
		}
		if (message.Code == 172)
		{
			if (PhotonNetwork.IsMasterClient && ballot != null && networkSnapshot != null && networkSnapshot.Tied == null && !(Time.realtimeSinceStartup >= voteEndsAt) && array.Length == 3 && object.Equals(array[1], networkSnapshot.Round) && array[2] is int && Extensions.Contains(ConnectedActors(), message.Sender) && ballot.Cast(message.Sender, (int)array[2]))
			{
				PublishSnapshot();
			}
			return;
		}
		Player masterClient = PhotonNetwork.MasterClient;
		string text = array[1] as string;
		if (PhotonNetwork.IsMasterClient || masterClient == null || !StageSnapshot.FromHost(message.Sender, masterClient.ActorNumber, networkMaster) || text == null || text.Length > 65536)
		{
			return;
		}
		StageSnapshot stageSnapshot;
		try
		{
			stageSnapshot = JsonConvert.DeserializeObject<StageSnapshot>(text);
		}
		catch (JsonException)
		{
			return;
		}
		if (stageSnapshot != null && stageSnapshot.Cards != null && stageSnapshot.Cards.Length == 3 && !stageSnapshot.Cards.Any((Card card) => card == null) && stageSnapshot.Stacks != null && (stageSnapshot.Totals == null || stageSnapshot.Totals.Length == 3) && stageSnapshot.PermanentCards != null && !stageSnapshot.PermanentCards.Any((Card card) => card == null) && stageSnapshot.IsNewerThan(networkSnapshot))
		{
			if (networkSnapshot == null || networkSnapshot.Round != stageSnapshot.Round)
			{
				localVote = -1;
			}
			if (stageSnapshot.Selected != null && (networkSnapshot == null || networkSnapshot.Selected == null || networkSnapshot.Round != stageSnapshot.Round))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Host selection received: " + stageSnapshot.Selected.Id));
			}
			voteEndsAt = Time.realtimeSinceStartup + Mathf.Clamp(stageSnapshot.SecondsRemaining, 0f, 25f);
			networkSnapshot = stageSnapshot;
			TraceResult();
		}
	}

	private void TraceDisplayedOffer()
	{
		if (debug.Value && !(networkSnapshot.Round == traceOfferRound))
		{
			traceOfferRound = networkSnapshot.Round;
			WriteSyncTrace("offer", new
			{
				cards = offer.Select((Card card) => card.Id).ToArray()
			});
		}
	}

	private void TraceResult()
	{
		if (debug.Value && networkSnapshot.Selected != null && !(networkSnapshot.Round == traceResultRound))
		{
			traceResultRound = networkSnapshot.Round;
			WriteSyncTrace("result", new
			{
				cards = networkSnapshot.Cards.Select((Card card) => card.Id).ToArray(),
				selected = networkSnapshot.Selected.Id,
				effect = networkSnapshot.Selected.Effect,
				permanent = networkSnapshot.Selected.Permanent,
				modifier = networkSnapshot.MaxHpModifier,
				staminaModifier = networkSnapshot.MaxStaminaModifier,
				speedModifier = networkSnapshot.MoveSpeedModifier,
				healScaleModifier = networkSnapshot.HealScaleModifier,
				enemyAttackModifier = networkSnapshot.EnemyAttackModifier,
				treasureMassModifier = networkSnapshot.TreasureMassModifier,
				enemyAudioModifier = networkSnapshot.EnemyAudioModifier,
				votes = networkSnapshot.Totals,
				stacks = networkSnapshot.Stacks
			});
		}
	}

	private void TraceAppliedHealth(PlayerHealth hp)
	{
		if (debug.Value && NetworkActive && SnapshotForStage && networkSnapshot.Selected != null && !(networkSnapshot.Round == traceEffectRound))
		{
			traceEffectRound = networkSnapshot.Round;
			int num = (int)MaxHp.GetValue(hp);
			WriteSyncTrace("health", new
			{
				baseline = (long)num - (long)appliedBonus,
				maximum = num,
				applied = appliedBonus
			});
		}
	}

	private void TraceAppliedStamina(PlayerController controller)
	{
		if (debug.Value && Object.op_Implicit((Object)(object)controller) && NetworkActive && SnapshotForStage && networkSnapshot.Selected != null && !(networkSnapshot.Selected.Effect != "maxStamina") && !(networkSnapshot.Round == traceStaminaRound))
		{
			traceStaminaRound = networkSnapshot.Round;
			float energyStart = controller.EnergyStart;
			WriteSyncTrace("stamina", new
			{
				baseline = energyStart - staminaApplied,
				maximum = energyStart,
				applied = staminaApplied
			});
		}
	}

	private void TraceAppliedMovement(float native, float scaled)
	{
		if (debug.Value && !(native <= 0f) && NetworkActive && SnapshotForStage && networkSnapshot.Selected != null && !(networkSnapshot.Selected.Effect != "moveSpeed") && !(networkSnapshot.Round == traceMovementRound) && movementMultiplier != 1f)
		{
			traceMovementRound = networkSnapshot.Round;
			WriteSyncTrace("movement", new
			{
				baseline = native,
				target = scaled,
				multiplier = movementMultiplier
			});
		}
	}

	private void WriteSyncTrace(string kind, object data)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)("SYNC_CHECK " + JsonConvert.SerializeObject((object)new
		{
			kind = kind,
			round = networkSnapshot.Round,
			stage = networkSnapshot.Stage,
			actor = PhotonNetwork.LocalPlayer.ActorNumber,
			role = (PhotonNetwork.IsMasterClient ? "host" : "client"),
			data = data
		})));
	}

	private int EffectiveModifier(string effect)
	{
		if (!NetworkActive || PhotonNetwork.IsMasterClient)
		{
			return run.Modifier(definitions, effect);
		}
		if (networkSnapshot == null)
		{
			return 0;
		}
		if (SnapshotForStage)
		{
			switch (effect)
			{
			case "maxHp":
				return networkSnapshot.MaxHpModifier;
			case "maxStamina":
				return networkSnapshot.MaxStaminaModifier;
			case "moveSpeed":
				return networkSnapshot.MoveSpeedModifier;
			case "healScale":
				return networkSnapshot.HealScaleModifier;
			case "enemyAttack":
				return networkSnapshot.EnemyAttackModifier;
			case "treasureMass":
				return networkSnapshot.TreasureMassModifier;
			case "enemyAudio":
				return networkSnapshot.EnemyAudioModifier;
			}
		}
		long val = networkSnapshot.PermanentCards.Where((Card card) => card.Effect == effect).Sum((Card card) => (long)card.Amount * (long)DisplayCount(card.Id));
		return (int)Math.Max(-2147483648L, Math.Min(2147483647L, val));
	}

	private int DisplayCount(string id)
	{
		if (!NetworkActive || PhotonNetwork.IsMasterClient)
		{
			return run.Count(id);
		}
		if (networkSnapshot == null || !networkSnapshot.Stacks.TryGetValue(id, out var value))
		{
			return 0;
		}
		return value;
	}

	private void HealFromHost(PlayerHealth hp)
	{
		if (SnapshotForStage && networkSnapshot.Selected != null && !(networkSnapshot.Selected.Effect != "heal") && !(lastHealRound == networkSnapshot.Round) && networkSnapshot.HealActors != null && Extensions.Contains(networkSnapshot.HealActors, PhotonNetwork.LocalPlayer.ActorNumber))
		{
			lastHealRound = networkSnapshot.Round;
			hp.Heal(networkSnapshot.Selected.Amount, true);
		}
	}

	private void Awake()
	{
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0185: Expected O, but got Unknown
		//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Expected O, but got Unknown
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Expected O, but got Unknown
		//IL_0234: Unknown result type (might be due to invalid IL or missing references)
		//IL_0241: Expected O, but got Unknown
		//IL_026e: Unknown result type (might be due to invalid IL or missing references)
		//IL_027c: Expected O, but got Unknown
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b7: Expected O, but got Unknown
		//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cf: Expected O, but got Unknown
		//IL_0406: Unknown result type (might be due to invalid IL or missing references)
		//IL_0412: Expected O, but got Unknown
		//IL_0457: Unknown result type (might be due to invalid IL or missing references)
		//IL_046e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0479: Expected O, but got Unknown
		//IL_0479: Expected O, but got Unknown
		//IL_04a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c8: Expected O, but got Unknown
		//IL_04c8: Expected O, but got Unknown
		//IL_04f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0503: Expected O, but got Unknown
		//IL_0530: Unknown result type (might be due to invalid IL or missing references)
		//IL_053e: Expected O, but got Unknown
		try
		{
			english = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "English", false, "Use English card text.");
			debug = ((BaseUnityPlugin)this).Config.Bind<bool>("Developer", "Enabled", false, "Log multiplayer verification records and enable F8 developer commands.");
			string text = Path.Combine(Paths.ConfigPath, "starcode.repo.events.json");
			string text2 = Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), "events.json");
			if (!File.Exists(text))
			{
				File.Copy(text2, text);
			}
			string text3 = File.ReadAllText(text);
			EventSettings eventSettings = (this.eventSettings = EventSettings.Read(text3, File.ReadAllText(text2)));
			definitions = eventSettings.Events;
			catalog = new Catalog(eventSettings, random);
			if (text3.TrimStart(new char[0]).StartsWith("[", StringComparison.Ordinal))
			{
				string text4 = text + ".migration";
				File.WriteAllText(text4, JsonConvert.SerializeObject((object)eventSettings, (Formatting)1));
				File.Replace(text4, text, text + ".pre-attributes");
			}
			if (MaxHp == null || CurrentHp == null || HealthSet == null || AvatarHealth == null || ControlledAvatar == null || SpawnObjects == null || RankThree == null || DebugEnemies == null)
			{
				throw new MissingFieldException("This game build does not expose the expected event fields.");
			}
			instance = this;
			harmony = new Harmony("starcode.repo.events");
			harmony.Patch((MethodBase)AccessTools.Method(typeof(LevelGenerator), "EnemySetup", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "BeforeEnemySetup", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Harmony obj = harmony;
			MethodInfo methodInfo = AccessTools.Method(typeof(EnemyDirector), "GetEnemy", (Type[])null, (Type[])null);
			HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "AfterGetEnemy", (Type[])null);
			val.priority = 0;
			obj.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerHealth), "UpdateHealthRPC", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AfterHealthReplaced", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerHealth), "Heal", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeHeal", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(PlayerController), "FixedUpdate", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ScaleNativeMovement", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
			HarmonyMethod val2 = new HarmonyMethod(typeof(Plugin), "BeforeEnemyHealthInitialized", (Type[])null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(EnemyHealth), "Awake", (Type[])null, (Type[])null), val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(EnemyHealth), "OnSpawn", (Type[])null, (Type[])null), val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			if (EnemyCurrentHealth == null)
			{
				throw new MissingFieldException("Enemy health field unavailable.");
			}
			InstallEnemyAiPatches(harmony);
			InstallAdvancedStagePatches(harmony);
			InstallExtractionPatches(harmony);
			InstallValuableMassPatches(harmony);
			if (definitions.Any((Card card) => card.Effect == "enemyAudio"))
			{
				InstallEnemyAudioPatches(harmony);
			}
			InstallEnemyDamagePatches(harmony);
			if (definitions.Any((Card card) => card.Effect == "treasureValue"))
			{
				InstallValuableValuePatches(harmony);
			}
			harmony.Patch((MethodBase)AccessTools.Method(typeof(EnemyDirector), "AmountSetup", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ModifyEnemyAmounts", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
			if (SaveDictionaries == null)
			{
				throw new MissingFieldException("Event save dictionary unavailable.");
			}
			harmony.Patch((MethodBase)AccessTools.Method(typeof(StatsManager), "SaveGame", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeSave", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AfterSave", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(StatsManager), "LoadGame", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeLoad", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "AfterLoad", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(SemiFunc), "MenuActionSingleplayerGame", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeStartRun", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(SemiFunc), "MenuActionHostGame", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeStartRun", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			SceneManager.activeSceneChanged += SceneChanged;
			PhotonNetwork.NetworkingClient.EventReceived += OnPhotonEvent;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Event system ready.");
		}
		catch (Exception ex)
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			instance = null;
			((BaseUnityPlugin)this).Logger.LogError((object)ex);
			((Behaviour)this).enabled = false;
		}
	}

	private void OnDestroy()
	{
		SceneManager.activeSceneChanged -= SceneChanged;
		PhotonNetwork.NetworkingClient.EventReceived -= OnPhotonEvent;
		RestorePlayerEffects();
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		if (Object.op_Implicit((Object)(object)japaneseFont))
		{
			Object.Destroy((Object)(object)japaneseFont);
		}
		if ((Object)(object)instance == (Object)(object)this)
		{
			instance = null;
		}
	}

	private void SceneChanged(Scene before, Scene after)
	{
		consoleOpen = false;
		valuableOriginalAppraisals.Clear();
		RestorePlayerEffects();
		run.BeginStage();
		offer = null;
		replacement = null;
		healPending = false;
		if (!Object.op_Implicit((Object)(object)RunManager.instance) || SemiFunc.MenuLevel() || SemiFunc.RunIsLobby() || SemiFunc.RunIsArena())
		{
			run.Reset();
		}
		if (NetworkActive && PhotonNetwork.IsMasterClient && networkSnapshot != null)
		{
			networkSnapshot.Stage = -1;
			networkSnapshot.Selected = null;
			voteEndsAt = 0f;
			PublishSnapshot();
		}
	}

	private static void BeforeEnemySetup(ref IEnumerator __result)
	{
		if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)GameManager.instance) || !Object.op_Implicit((Object)(object)RunManager.instance) || !SemiFunc.RunIsLevel())
		{
			return;
		}
		if (SemiFunc.IsMultiplayer())
		{
			if (!instance.NetworkActive || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			instance.UpdateNetwork();
		}
		instance.RestorePendingLoad();
		__result = instance.WithSelection(__result);
	}

	private static SortedDictionary<string, Dictionary<string, int>> SaveData(StatsManager stats)
	{
		return (SortedDictionary<string, Dictionary<string, int>>)SaveDictionaries.GetValue(stats);
	}

	private static void BeforeSave(StatsManager __instance, string __0, out bool __state)
	{
		__state = Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMasterClientOrSingleplayer() && !string.IsNullOrEmpty(__0);
		if (__state)
		{
			SaveData(__instance)["starcodeEventSelectionsV1"] = instance.pendingLoad ?? instance.run.Snapshot();
		}
	}

	private static void AfterSave(StatsManager __instance, bool __state)
	{
		if (__state)
		{
			SaveData(__instance).Remove("starcodeEventSelectionsV1");
		}
	}

	private static void BeforeLoad(StatsManager __instance, out bool __state)
	{
		__state = Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMasterClientOrSingleplayer();
		if (__state)
		{
			instance.pendingLoad = null;
			SaveData(__instance)["starcodeEventSelectionsV1"] = new Dictionary<string, int>();
		}
	}

	private static void AfterLoad(StatsManager __instance, Exception __exception, bool __state)
	{
		if (__state)
		{
			SortedDictionary<string, Dictionary<string, int>> sortedDictionary = SaveData(__instance);
			if (__exception == null && Object.op_Implicit((Object)(object)instance) && sortedDictionary.TryGetValue("starcodeEventSelectionsV1", out var value))
			{
				instance.pendingLoad = new Dictionary<string, int>(value);
			}
			sortedDictionary.Remove("starcodeEventSelectionsV1");
		}
	}

	private static void BeforeStartRun(string __0)
	{
		if (Object.op_Implicit((Object)(object)instance) && string.IsNullOrEmpty(__0))
		{
			instance.pendingLoad = null;
			instance.run.Reset();
		}
	}

	private void RestorePendingLoad()
	{
		if (pendingLoad != null)
		{
			run.Restore(pendingLoad, definitions);
			pendingLoad = null;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Restored event selections: " + JsonConvert.SerializeObject((object)run.Snapshot())));
		}
	}

	private IEnumerator WithSelection(IEnumerator original)
	{
		try
		{
			if (!run.Committed && SemiFunc.IsMultiplayer())
			{
				{
					IEnumerator voting = MultiplayerSelection();
					try
					{
						while (voting.MoveNext())
						{
							yield return voting.Current;
						}
					}
					finally
					{
						IDisposable disposable2 = voting as IDisposable;
						if (disposable2 != null)
						{
							disposable2.Dispose();
						}
					}
				}
			}
			else if (!run.Committed)
			{
				offer = catalog.Draw();
				deadline = Time.realtimeSinceStartup + 25f;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Offer: " + string.Join(", ", offer.Select((Card c) => c.Id).ToArray())));
				while (offer != null && Time.realtimeSinceStartup < deadline)
				{
					SemiFunc.CursorUnlock(0.15f);
					yield return null;
				}
				if (offer != null)
				{
					Choose(0);
				}
			}
			ReconcileExistingAppraisals();
			Plugin plugin = this;
			Plugin plugin2 = this;
			int num = 0;
			plugin2.replacedSlots = 0;
			plugin.originalSlots = num;
			replacement = FindReplacement();
			if (NetworkActive && PhotonNetwork.IsMasterClient && networkSnapshot != null)
			{
				networkSnapshot.Enemy = (Object.op_Implicit((Object)(object)replacement) ? ((Object)replacement).name : null);
				PublishSnapshot();
			}
			while (true)
			{
				buildingEnemySetup = true;
				selectingEnemies = Object.op_Implicit((Object)(object)replacement);
				bool moved;
				try
				{
					moved = original.MoveNext();
				}
				finally
				{
					Plugin plugin3 = this;
					Plugin plugin4 = this;
					bool flag = false;
					plugin4.selectingEnemies = false;
					plugin3.buildingEnemySetup = flag;
				}
				if (!moved)
				{
					break;
				}
				yield return original.Current;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Enemy slots: {replacedSlots} replaced of {originalSlots} planned enemies");
		}
		finally
		{
			offer = null;
			replacement = null;
			foreach (EnemySetup value in apexGroups.Values)
			{
				Object.Destroy((Object)(object)value);
			}
			apexGroups.Clear();
			if (original is IDisposable disposable)
			{
				disposable.Dispose();
			}
		}
	}

	private EnemySetup FindReplacement()
	{
		if (run.Active == null || run.Active.Effect != "apex")
		{
			return null;
		}
		if (HasExternalSpawnConfig())
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Apex skipped: another spawn configuration plugin is active.");
			return null;
		}
		EnemyDirector val = Object.FindFirstObjectByType<EnemyDirector>();
		List<EnemySetup> list = (Object.op_Implicit((Object)(object)val) ? ((List<EnemySetup>)RankThree.GetValue(val)) : null);
		EnemySetup[] array = (EnemySetup[])((list == null) ? ((Array)new EnemySetup[0]) : ((Array)list.Where(IsReplacementCandidate).Distinct().ToArray()));
		if (array.Length == 0)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"Apex skipped: no single-enemy tier-three setup is available.");
			return null;
		}
		EnemySetup val2 = array[random.Next(array.Length)];
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Apex enemy: " + ((Object)val2).name));
		return val2;
	}

	private static List<PrefabRef> GetSpawnObjects(EnemySetup setup)
	{
		if (!Object.op_Implicit((Object)(object)setup))
		{
			return null;
		}
		return (List<PrefabRef>)SpawnObjects.GetValue(setup);
	}

	private static bool IsReplacementCandidate(EnemySetup setup)
	{
		List<PrefabRef> spawnObjects = GetSpawnObjects(setup);
		if (spawnObjects != null && spawnObjects.Count == 1)
		{
			return OrdinaryEnemyCount(spawnObjects) == 1;
		}
		return false;
	}

	private static int OrdinaryEnemyCount(List<PrefabRef> objects)
	{
		if (objects == null)
		{
			return 0;
		}
		GameObject val = null;
		int num = 0;
		foreach (PrefabRef @object in objects)
		{
			GameObject val2 = ((PrefabRef<GameObject>)(object)@object)?.Prefab;
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return 0;
			}
			if (!Object.op_Implicit((Object)(object)val2.GetComponent<EnemyGnomeDirector>()) && !Object.op_Implicit((Object)(object)val2.GetComponent<EnemyBangDirector>()))
			{
				if (!Object.op_Implicit((Object)(object)val2.GetComponent<EnemyParent>()) || (Object.op_Implicit((Object)(object)val) && (Object)(object)val != (Object)(object)val2))
				{
					return 0;
				}
				val = val2;
				num++;
			}
		}
		return num;
	}

	private static void AfterGetEnemy(EnemyDirector __instance, ref EnemySetup __result)
	{
		if (!Object.op_Implicit((Object)(object)instance) || !instance.selectingEnemies)
		{
			return;
		}
		EnemySetup[] array = (EnemySetup[])DebugEnemies.GetValue(__instance);
		if (array != null && array.Length > 0)
		{
			return;
		}
		List<PrefabRef> spawnObjects = GetSpawnObjects(__result);
		if (spawnObjects == null || spawnObjects.Count == 0)
		{
			return;
		}
		int num = OrdinaryEnemyCount(spawnObjects);
		if (num == 0)
		{
			((BaseUnityPlugin)instance).Logger.LogWarning((object)("Apex preserved an unsupported enemy setup: " + ((Object)__result).name));
			return;
		}
		instance.originalSlots += num;
		EnemySetup value = instance.replacement;
		if (num > 1 && !instance.apexGroups.TryGetValue(num, out value))
		{
			value = Object.Instantiate<EnemySetup>(instance.replacement);
			PrefabRef element = GetSpawnObjects(instance.replacement)[0];
			SpawnObjects.SetValue(value, Enumerable.Repeat<PrefabRef>(element, num).ToList());
			instance.apexGroups.Add(num, value);
		}
		__result = value;
		instance.replacedSlots += num;
	}

	private void Choose(int index)
	{
		if (NetworkActive)
		{
			Vote(index);
		}
		else
		{
			CommitSelection(index);
		}
	}

	private void CommitSelection(int index)
	{
		if (offer != null && index >= 0 && index < offer.Length)
		{
			CommitCard(offer[index]);
		}
	}

	private void CommitCard(Card card)
	{
		offer = null;
		if (run.Commit(card))
		{
			healPending = card.Effect == "heal";
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Selected: " + card.Id + " (" + card.Attribute + ") | run count: " + run.Count(card.Id)));
		}
	}

	private void Update()
	{
		UpdateNetwork();
		if (debug.Value && Input.GetKeyDown((KeyCode)289))
		{
			consoleOpen = !consoleOpen;
		}
		if (debug.Value && consoleOpen && Object.op_Implicit((Object)(object)GameManager.instance) && Object.op_Implicit((Object)(object)RunManager.instance) && SemiFunc.RunIsLevel())
		{
			SemiFunc.CursorUnlock(0.15f);
		}
		if (offer != null && !consoleOpen)
		{
			if (Input.GetKeyDown((KeyCode)49))
			{
				Choose(0);
			}
			else if (Input.GetKeyDown((KeyCode)50))
			{
				Choose(1);
			}
			else if (Input.GetKeyDown((KeyCode)51))
			{
				Choose(2);
			}
		}
		if (!Object.op_Implicit((Object)(object)GameManager.instance) || !Object.op_Implicit((Object)(object)RunManager.instance) || (SemiFunc.IsMultiplayer() && (!NetworkActive || authorityChanged)))
		{
			return;
		}
		if (SemiFunc.MenuLevel() || SemiFunc.RunIsLobby() || SemiFunc.RunIsArena())
		{
			RestorePlayerEffects();
			run.Reset();
		}
		else
		{
			if (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsShop())
			{
				return;
			}
			if (!NetworkActive || PhotonNetwork.IsMasterClient)
			{
				RestorePendingLoad();
			}
			if (!Object.op_Implicit((Object)(object)LevelGenerator.Instance) || !SemiFunc.LevelGenDone())
			{
				return;
			}
			PlayerController controller = PlayerController.instance;
			ApplyStamina(controller, EffectiveModifier("maxStamina"));
			TraceAppliedStamina(controller);
			movementMultiplier = MovementMultiplier(EffectiveModifier("moveSpeed"));
			UpdateEnemyHealth();
			UpdateEnemyAiEffects();
			UpdateValuableMasses();
			UpdateEnemyAudio();
			UpdateMapVisibility();
			PlayerAvatar controlledAvatar = GetControlledAvatar();
			PlayerHealth val = (PlayerHealth)(Object.op_Implicit((Object)(object)controlledAvatar) ? /*isinst with value type is only supported in some contexts*/: null);
			if (Object.op_Implicit((Object)(object)val) && (bool)HealthSet.GetValue(val) && (int)MaxHp.GetValue(val) > 0)
			{
				if ((Object)(object)trackedHealth != (Object)(object)val)
				{
					RestoreHealth();
					trackedHealth = val;
				}
				ApplyMaxHealth(val);
				TraceAppliedHealth(val);
				if (NetworkActive && !PhotonNetwork.IsMasterClient)
				{
					HealFromHost(val);
				}
				if (healPending && run.Active != null)
				{
					healPending = false;
					val.Heal(run.Active.Amount, true);
				}
			}
		}
	}

	private static void AfterHealthReplaced(PlayerHealth __instance, PhotonMessageInfo __4, PhotonView ___photonView)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)GameManager.instance) && !((Object)(object)instance.trackedHealth != (Object)(object)__instance) && (!SemiFunc.IsMultiplayer() || SemiFunc.MasterAndOwnerOnlyRPC(__4, ___photonView)))
		{
			instance.appliedBonus = 0;
			instance.ApplyMaxHealth(__instance);
		}
	}

	private static void BeforeHeal(PlayerHealth __instance, ref int __0)
	{
		if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)GameManager.instance) || (Object)(object)instance.trackedHealth != (Object)(object)__instance)
		{
			return;
		}
		instance.ApplyMaxHealth(__instance);
		int num = instance.EffectiveModifier("healScale");
		if (num != 0 && __0 > 0)
		{
			long num2 = int.MaxValue - Math.Max(0, (int)CurrentHp.GetValue(__instance));
			int num3 = (int)Math.Min(num2, Math.Max(0.0, Math.Round((double)__0 * (1.0 + (double)num / 100.0), MidpointRounding.AwayFromZero)));
			if (instance.debug.Value)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Player heal amount: {__0} -> {num3}");
			}
			__0 = num3;
		}
	}

	private void ApplyMaxHealth(PlayerHealth hp)
	{
		int num = (int)MaxHp.GetValue(hp);
		long num2 = (long)num - (long)appliedBonus;
		int num3 = (int)Math.Max(1L, Math.Min(2147483647L, num2 + EffectiveModifier("maxHp")));
		if (num3 != num)
		{
			MaxHp.SetValue(hp, num3);
			appliedBonus = (int)(num3 - num2);
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Max HP: {num} -> {num3} (base {num2}, event {appliedBonus:+0;-0;0})");
		}
		if ((int)CurrentHp.GetValue(hp) > num3)
		{
			CurrentHp.SetValue(hp, num3);
			PlayerAvatar controlledAvatar = GetControlledAvatar();
			if (Object.op_Implicit((Object)(object)controlledAvatar) && Object.op_Implicit((Object)(object)StatsManager.instance))
			{
				StatsManager.instance.SetPlayerHealth(SemiFunc.PlayerGetSteamID(controlledAvatar), num3, false);
			}
		}
	}

	private static PlayerAvatar GetControlledAvatar()
	{
		PlayerController val = PlayerController.instance;
		if (!Object.op_Implicit((Object)(object)val))
		{
			return null;
		}
		object? value = ControlledAvatar.GetValue(val);
		return (PlayerAvatar)((value is PlayerAvatar) ? value : null);
	}

	private void ApplyStamina(PlayerController controller, int modifier)
	{
		if ((Object)(object)staminaController != (Object)(object)controller)
		{
			RestoreStamina();
			staminaController = controller;
		}
		if (Object.op_Implicit((Object)(object)controller) && (modifier != 0 || staminaApplied != 0f))
		{
			float num = controller.EnergyStart - staminaApplied;
			float num2 = Mathf.Max(1f, num + (float)modifier);
			if (num2 != controller.EnergyStart)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Max stamina: {controller.EnergyStart} -> {num2} (base {num})");
				controller.EnergyStart = num2;
				staminaApplied = num2 - num;
			}
			controller.EnergyCurrent = Mathf.Min(controller.EnergyCurrent, num2);
		}
	}

	private void RestoreStamina()
	{
		if (Object.op_Implicit((Object)(object)staminaController) && staminaApplied != 0f)
		{
			PlayerController obj = staminaController;
			obj.EnergyStart -= staminaApplied;
			staminaController.EnergyCurrent = Mathf.Min(staminaController.EnergyCurrent, staminaController.EnergyStart);
		}
		staminaController = null;
		staminaApplied = 0f;
	}

	private void RestorePlayerEffects()
	{
		RestoreHealth();
		RestoreStamina();
		movementMultiplier = 1f;
		RestoreEnemyHealth();
		RestoreEnemyAiEffects();
		RestoreValuableMasses();
		RestoreEnemyAudio();
		RestoreMapVisibility();
	}

	private void RestoreHealth()
	{
		if (Object.op_Implicit((Object)(object)trackedHealth))
		{
			MaxHp.SetValue(trackedHealth, (int)MaxHp.GetValue(trackedHealth) - appliedBonus);
		}
		trackedHealth = null;
		appliedBonus = 0;
	}

	private void OnGUI()
	{
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Expected O, but got Unknown
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Expected O, but got Unknown
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Expected O, but got Unknown
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Invalid comparison between Unknown and I4
		//IL_0220: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_0327: Unknown result type (might be due to invalid IL or missing references)
		//IL_02df: Unknown result type (might be due to invalid IL or missing references)
		//IL_039f: Unknown result type (might be due to invalid IL or missing references)
		//IL_044e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0579: Unknown result type (might be due to invalid IL or missing references)
		DrawDeveloperConsole();
		if (IsMainMenu())
		{
			DrawEventMenu();
			return;
		}
		eventMenuOpen = false;
		if (offer == null)
		{
			DrawEventStatus();
			return;
		}
		bool value = english.Value;
		if (!value)
		{
			EnsureJapaneseFont();
		}
		GUI.depth = -100;
		float num = Mathf.Min(Screen.width - 24, 960);
		Rect val = default(Rect);
		((Rect)(ref val))..ctor(((float)Screen.width - num) / 2f, (float)Screen.height * 0.27f, num, 245f);
		GUIStyle val2 = new GUIStyle(GUI.skin.label);
		val2.fontSize = 17;
		val2.alignment = (TextAnchor)4;
		GUIStyle val3 = val2;
		GUIStyle val4 = new GUIStyle(GUI.skin.label);
		val4.wordWrap = true;
		GUIStyle val5 = val4;
		GUIStyle val6 = new GUIStyle(GUI.skin.button);
		if (!value && Object.op_Implicit((Object)(object)japaneseFont))
		{
			Font val7 = (val6.font = japaneseFont);
			Font font = (val5.font = val7);
			val3.font = font;
		}
		GUI.Box(val, "");
		bool flag = NetworkActive && SnapshotForStage;
		if (flag && (int)Event.current.type == 7)
		{
			TraceDisplayedOffer();
		}
		int[] array = (flag ? networkSnapshot.Totals : null);
		double num2 = (flag ? (voteEndsAt - Time.realtimeSinceStartup) : (deadline - Time.realtimeSinceStartup));
		string text = (value ? "Choose an event" : "イベントを選択");
		if (flag)
		{
			text = ((networkSnapshot.Tied == null) ? ((value ? "Event vote" : "イベント投票") + "   " + (array?.Sum() ?? 0) + "/" + networkSnapshot.Eligible) : (value ? "Host decides the tie" : "同票・ホストが決定"));
		}
		GUI.Label(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + 8f, num, 30f), text + "   " + Mathf.Max(0, Mathf.CeilToInt((float)num2)) + " s", val3);
		float num3 = (num - 32f) / 3f;
		int num4 = 0;
		while (offer != null && num4 < offer.Length)
		{
			Card card = offer[num4];
			float num5 = ((Rect)(ref val)).x + 8f + (float)num4 * (num3 + 8f);
			float num6 = ((Rect)(ref val)).y + 46f;
			Color color = GUI.color;
			if (flag && localVote == num4)
			{
				GUI.color = new Color(0.55f, 0.9f, 0.7f, 1f);
			}
			GUI.Box(new Rect(num5, num6, num3, 184f), "");
			GUI.color = color;
			GUI.Label(new Rect(num5 + 8f, num6 + 7f, num3 - 16f, 38f), card.Icon + " " + (value ? card.En : card.Ja), val5);
			string text2 = ((!card.Permanent) ? (value ? "Single stage" : "単発型") : (value ? "Permanent" : "永久型"));
			GUI.Label(new Rect(num5 + 8f, num6 + 46f, num3 - 16f, 28f), AttributeLabel(card, value) + "   " + text2 + "   " + (value ? "Danger " : "危険度 ") + card.Danger + (card.Permanent ? ("   ×" + DisplayCount(card.Id)) : ""), val5);
			GUI.Label(new Rect(num5 + 8f, num6 + 77f, num3 - 16f, 61f), value ? card.DescriptionEn : card.DescriptionJa, val5);
			string text3 = (value ? "Select" : "選択") + " [" + (num4 + 1) + "]";
			if (flag)
			{
				text3 = (value ? "Vote" : "投票") + " [" + (num4 + 1) + "]   " + ((array != null) ? array[num4] : 0) + (value ? " votes" : "票");
			}
			bool enabled = GUI.enabled;
			if (flag && networkSnapshot.Tied != null)
			{
				GUI.enabled = enabled && PhotonNetwork.IsMasterClient && networkSnapshot.Tied.Contains(num4);
			}
			if (GUI.Button(new Rect(num5 + 8f, num6 + 146f, num3 - 16f, 28f), text3, val6))
			{
				Choose(num4);
			}
			GUI.enabled = enabled;
			num4++;
		}
	}

	private static bool IsMainMenu()
	{
		MenuManager val = MenuManager.instance;
		MenuPageMain val2 = MenuPageMain.instance;
		if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)val2) || !((Behaviour)val2).isActiveAndEnabled || CurrentMenuPage == null)
		{
			return false;
		}
		object? value = CurrentMenuPage.GetValue(val);
		MenuPage val3 = (MenuPage)((value is MenuPage) ? value : null);
		if (Object.op_Implicit((Object)(object)val3))
		{
			return (Object)(object)((Component)val3).GetComponentInChildren<MenuPageMain>(true) == (Object)(object)val2;
		}
		return false;
	}

	private void DrawEventMenu()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Expected O, but got Unknown
		//IL_018a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Invalid comparison between Unknown and I4
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0238: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
		bool value = english.Value;
		if (!value)
		{
			EnsureJapaneseFont();
		}
		GUIStyle val = new GUIStyle(GUI.skin.button);
		val.fontSize = 18;
		GUIStyle val2 = val;
		GUIStyle val3 = new GUIStyle(GUI.skin.label);
		val3.fontSize = 16;
		val3.wordWrap = true;
		val3.richText = false;
		GUIStyle val4 = val3;
		GUIStyle val5 = new GUIStyle(val4);
		val5.fontSize = 21;
		val5.fontStyle = (FontStyle)1;
		GUIStyle val6 = val5;
		if (!value && Object.op_Implicit((Object)(object)japaneseFont))
		{
			Font val7 = (val6.font = japaneseFont);
			Font font = (val4.font = val7);
			val2.font = font;
		}
		GUI.depth = -100;
		float num = Mathf.Min(590f, (float)Screen.width * 0.48f);
		float num2 = Screen.width - 24;
		if (!eventMenuOpen)
		{
			if (!GUI.Button(new Rect(num2 - 160f, 32f, 160f, 40f), value ? "Events" : "イベント", val2))
			{
				return;
			}
			if (menuOdds == null)
			{
				menuOdds = catalog.GetOdds().ToDictionary<EventOdds, string>((EventOdds row) => row.Card.Id, StringComparer.Ordinal);
			}
			eventMenuOpen = true;
			eventMenuScroll = Vector2.zero;
			return;
		}
		Rect val10 = default(Rect);
		((Rect)(ref val10))..ctor(num2 - num, 32f, num, (float)(Screen.height - 64));
		if ((int)Event.current.type == 7)
		{
			Color color = GUI.color;
			GUI.color = new Color(0.065f, 0.08f, 0.09f, 0.97f);
			GUI.DrawTexture(val10, (Texture)(object)Texture2D.whiteTexture);
			GUI.color = color;
		}
		if (GUI.Button(new Rect(num2 - 110f, 46f, 92f, 32f), value ? "Close" : "閉じる", val2))
		{
			eventMenuOpen = false;
		}
		GUILayout.BeginArea(new Rect(((Rect)(ref val10)).x + 18f, ((Rect)(ref val10)).y + 14f, ((Rect)(ref val10)).width - 36f, ((Rect)(ref val10)).height - 28f));
		GUILayout.Label(value ? "Events" : "イベント一覧", val6, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label(value ? $"Per draw: debuff {eventSettings.DebuffPercent:0.##}% / buff {eventSettings.BuffPercent:0.##}%" : $"各枠の属性設定\u3000デバフ {eventSettings.DebuffPercent:0.##}% / バフ {eventSettings.BuffPercent:0.##}%", val4, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Label(value ? "Odds include fallback when a category runs out." : "候補不足時の繰り替えも確率に反映しています。", val4, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.Space(10f);
		eventMenuScroll = GUILayout.BeginScrollView(eventMenuScroll, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		Card[] array = definitions;
		foreach (Card card in array)
		{
			EventOdds value2;
			bool flag = menuOdds.TryGetValue(card.Id, out value2);
			GUILayout.Label((value ? card.En : card.Ja) + "  [" + AttributeLabel(card, value) + "・" + (card.Permanent ? (value ? "Permanent" : "永続") : (value ? "Single stage" : "単発")) + "]", val6, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label(value ? card.DescriptionEn : card.DescriptionJa, val4, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label(flag ? string.Format(value ? "First slot {0:P2}   Appears among three {1:P2}" : "第1枠 {0:P2}\u3000\u30003枚中に登場 {1:P2}", value2.FirstSlot, value2.InOffer) : (value ? "Disabled · 0%" : "抽選対象外・0%"), val4, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Space(12f);
		}
		GUILayout.EndScrollView();
		GUILayout.EndArea();
	}

	private void EnsureJapaneseFont()
	{
		if (!Object.op_Implicit((Object)(object)japaneseFont))
		{
			japaneseFont = Font.CreateDynamicFontFromOSFont(new string[3] { "Yu Gothic UI", "Meiryo", "MS Gothic" }, 16);
		}
	}

	private void DrawEventStatus()
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Invalid comparison between Unknown and I4
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Expected O, but got Unknown
		//IL_0569: Unknown result type (might be due to invalid IL or missing references)
		//IL_0570: Expected O, but got Unknown
		//IL_063d: Unknown result type (might be due to invalid IL or missing references)
		//IL_066c: Unknown result type (might be due to invalid IL or missing references)
		if ((int)Event.current.type != 7 || !Object.op_Implicit((Object)(object)GameManager.instance) || !Object.op_Implicit((Object)(object)RunManager.instance) || (SemiFunc.IsMultiplayer() && (!NetworkActive || authorityChanged)) || (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsShop()) || !Object.op_Implicit((Object)(object)MapToolController.instance) || !Object.op_Implicit((Object)(object)LevelUI.instance) || MapActive == null || !(bool)MapActive.GetValue(MapToolController.instance))
		{
			return;
		}
		TextMeshProUGUI val = (TextMeshProUGUI)((LevelNumber == null) ? null : /*isinst with value type is only supported in some contexts*/);
		if (Object.op_Implicit((Object)(object)val) && ((Behaviour)val).isActiveAndEnabled && Object.op_Implicit((Object)(object)((Graphic)val).canvas))
		{
			TextMeshProUGUI val2 = (TextMeshProUGUI)((LevelMoon == null) ? null : /*isinst with value type is only supported in some contexts*/);
			Canvas rootCanvas = ((Graphic)val).canvas.rootCanvas;
			Camera camera = (((int)rootCanvas.renderMode == 0) ? null : rootCanvas.worldCamera);
			Vector2 val3 = BottomCenter(((TMP_Text)val).rectTransform, camera);
			if (Object.op_Implicit((Object)(object)val2) && ((Behaviour)val2).isActiveAndEnabled)
			{
				val3.y = Mathf.Min(val3.y, BottomCenter(((TMP_Text)val2).rectTransform, camera).y);
			}
			if (val3.x < 0.72f || val3.x > 0.98f || val3.y < 0.25f || val3.y > 0.58f)
			{
				((Vector2)(ref val3))..ctor(0.85f, 0.46f);
			}
			bool value = english.Value;
			if (!value)
			{
				EnsureJapaneseFont();
			}
			if (statusStyle == null)
			{
				GUIStyle val4 = new GUIStyle(GUI.skin.label);
				val4.wordWrap = true;
				val4.richText = false;
				val4.fontSize = 16;
				statusStyle = val4;
			}
			statusStyle.font = (value ? GUI.skin.font : japaneseFont);
			List<string> list = new List<string>();
			list.Add(value ? "Current event" : "現在のイベント");
			bool flag = NetworkActive && !PhotonNetwork.IsMasterClient;
			Card card = ((!flag) ? run.Selected : (SnapshotForStage ? networkSnapshot.Selected : null));
			list.Add((card != null) ? Describe(card, value) : (value ? "None" : "なし"));
			if (SnapshotForStage && !string.IsNullOrEmpty(networkSnapshot.Enemy))
			{
				list.Add(networkSnapshot.Enemy);
			}
			list.Add("");
			list.Add(value ? "Permanent effects" : "永続効果");
			Card[] array = ((!flag) ? definitions.Where((Card c) => c.Permanent && run.Count(c.Id) > 0).ToArray() : ((networkSnapshot == null) ? new Card[0] : networkSnapshot.PermanentCards));
			if (array.Length == 0)
			{
				list.Add(value ? "None" : "なし");
			}
			Card[] array2 = array;
			foreach (Card card2 in array2)
			{
				int num2 = DisplayCount(card2.Id);
				string text = card2.Effect switch
				{
					"moveSpeed" => value ? "Movement speed" : "移動速度", 
					"enemyHp" => value ? "Enemy max HP" : "敵の最大HP", 
					"enemySpeed" => value ? "Enemy movement speed" : "敵の移動速度", 
					"enemyVision" => value ? "Enemy detection range" : "敵の索敵距離", 
					"healScale" => value ? "Healing received" : "回復量", 
					"maxStamina" => value ? "Max stamina" : "最大スタミナ", 
					_ => value ? "Max HP" : "最大HP", 
				};
				bool flag2 = card2.Effect != "maxHp" && card2.Effect != "maxStamina";
				list.Add((value ? card2.En : card2.Ja) + " ×" + num2 + "   " + text + (value ? " total " : " 合計 ") + ((long)card2.Amount * (long)num2).ToString("+0;-0;0") + (flag2 ? "%" : ""));
			}
			GUIContent val5 = new GUIContent(string.Join("\n", list.ToArray()));
			float num3 = Mathf.Min(390, Screen.width - 24);
			float num4 = Mathf.Clamp(val3.x * (float)Screen.width - num3 / 2f, 12f, (float)Screen.width - num3 - 12f);
			float num5 = Mathf.Clamp((1f - val3.y) * (float)Screen.height + 10f, 12f, (float)(Screen.height - 12));
			float num6 = statusStyle.CalcHeight(val5, num3 - 24f) + 18f;
			if (num5 + num6 > (float)(Screen.height - 12))
			{
				num5 = Mathf.Max(12f, (float)Screen.height - num6 - 12f);
			}
			GUI.depth = -100;
			GUI.Box(new Rect(num4, num5, num3, num6), "");
			GUI.Label(new Rect(num4 + 12f, num5 + 8f, num3 - 24f, num6 - 16f), val5, statusStyle);
		}
	}

	private Vector2 BottomCenter(RectTransform rect, Camera camera)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: 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_004d: Unknown result type (might be due to invalid IL or missing references)
		rect.GetWorldCorners(hudCorners);
		Vector3 val = (hudCorners[0] + hudCorners[3]) / 2f;
		if (Object.op_Implicit((Object)(object)camera))
		{
			return Vector2.op_Implicit(camera.WorldToViewportPoint(val));
		}
		Vector2 val2 = RectTransformUtility.WorldToScreenPoint((Camera)null, val);
		return new Vector2(val2.x / (float)Screen.width, val2.y / (float)Screen.height);
	}

	private static string Describe(Card card, bool en)
	{
		return (en ? card.En : card.Ja) + " [" + AttributeLabel(card, en) + "]\n" + (en ? card.DescriptionEn : card.DescriptionJa);
	}

	private static string AttributeLabel(Card card, bool en)
	{
		if (!(card.Attribute == "debuff"))
		{
			if (!en)
			{
				return "バフ";
			}
			return "Buff";
		}
		if (!en)
		{
			return "デバフ";
		}
		return "Debuff";
	}

	private void DrawDeveloperConsole()
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Invalid comparison between Unknown and I4
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Invalid comparison between Unknown and I4
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Invalid comparison between Unknown and I4
		if (!debug.Value || !consoleOpen)
		{
			return;
		}
		GUI.depth = -101;
		GUILayout.BeginArea(new Rect(20f, 20f, (float)Mathf.Min(610, Screen.width - 40), 135f), GUI.skin.box);
		GUILayout.Label("STARCODE / Developer (F8)", (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUI.SetNextControlName("StarcodeCommand");
		command = GUILayout.TextField(command, 128, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		if (GUILayout.Button("Run command", (GUILayoutOption[])(object)new GUILayoutOption[0]) || ((int)Event.current.type == 4 && (int)Event.current.keyCode == 13 && GUI.GetNameOfFocusedControl() == "StarcodeCommand"))
		{
			if ((int)Event.current.type == 4)
			{
				Event.current.Use();
			}
			commandStatus = ExecuteDeveloperCommand(command);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Developer: " + commandStatus));
			command = "";
		}
		GUILayout.Label((commandStatus.Length == 0) ? "Type help for available commands." : commandStatus, (GUILayoutOption[])(object)new GUILayoutOption[0]);
		GUILayout.EndArea();
	}

	private string ExecuteDeveloperCommand(string input)
	{
		string[] args = input.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
		if (args.Length == 0 || args[0] == "help")
		{
			return "draw | apply ID | clear | stacks | add ID | reset | ranks | spawns | apex | odds";
		}
		if (args.Length != ((!(args[0] == "apply") && !(args[0] == "add")) ? 1 : 2))
		{
			return "Invalid arguments. Type help.";
		}
		switch (args[0])
		{
		case "odds":
		{
			string[] array = catalog.Probabilities();
			foreach (string text in array)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)text);
			}
			return "Event odds written to BepInEx log.";
		}
		case "stacks":
		{
			Dictionary<string, int> counts = ((NetworkActive && !PhotonNetwork.IsMasterClient && networkSnapshot != null) ? networkSnapshot.Stacks : run.Snapshot());
			((BaseUnityPlugin)this).Logger.LogInfo((object)JsonConvert.SerializeObject((object)definitions.Where((Card c) => c.Permanent).ToDictionary((Card c) => c.Id, (Card c) => counts.ContainsKey(c.Id) ? counts[c.Id] : 0)));
			return "Permanent stacks written to BepInEx log.";
		}
		case "ranks":
			return LogEnemyRanks();
		case "spawns":
			return LogEnemySpawns();
		default:
			if (!Object.op_Implicit((Object)(object)GameManager.instance) || !Object.op_Implicit((Object)(object)RunManager.instance) || !SemiFunc.RunIsLevel() || SemiFunc.IsMultiplayer())
			{
				return "State-changing commands require a single-player stage.";
			}
			switch (args[0])
			{
			case "draw":
				if (run.Committed || offer == null)
				{
					return "Reroll requires an open selection.";
				}
				offer = catalog.Draw();
				deadline = Time.realtimeSinceStartup + 25f;
				return "Offer: " + string.Join(", ", offer.Select((Card c) => c.Id).ToArray());
			case "clear":
				if (!run.ClearActive())
				{
					return "No single-stage effect is active.";
				}
				healPending = false;
				return "Single-stage effect cleared. Completed heals, appraisals, quotas and spawned enemies are retained.";
			case "reset":
				run.ClearPermanent(definitions);
				return "Permanent stacks reset.";
			case "add":
			{
				Card card2 = definitions.FirstOrDefault((Card c) => c.Permanent && c.Id == args[1]);
				if (card2 == null)
				{
					return "Unknown permanent event.";
				}
				if (!run.AddPermanent(card2))
				{
					return "Stack limit reached.";
				}
				return "Added " + card2.Id;
			}
			case "apex":
			case "apply":
			{
				string id = ((args[0] == "apex") ? "apex" : args[1]);
				Card card = definitions.FirstOrDefault((Card c) => c.Id == id);
				if (card == null)
				{
					return "Unknown event ID.";
				}
				if (run.Committed || offer == null)
				{
					return "Forced selection requires an open selection.";
				}
				CommitCard(card);
				return "Selected " + id;
			}
			default:
				return "Unknown command. Type help.";
			}
		}
	}

	private string LogEnemyRanks()
	{
		EnemyDirector val = Object.FindFirstObjectByType<EnemyDirector>();
		if (!Object.op_Implicit((Object)(object)val))
		{
			return "Enemy director unavailable.";
		}
		for (int i = 1; i <= 3; i++)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyDirector), "enemiesDifficulty" + i);
			List<EnemySetup> list = ((fieldInfo == null) ? null : (fieldInfo.GetValue(val) as List<EnemySetup>));
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Tier " + i + ": " + ((list == null) ? "unavailable" : string.Join(", ", (from e in list
				where Object.op_Implicit((Object)(object)e)
				select ((Object)e).name).ToArray()))));
		}
		return "Enemy tiers written to BepInEx log.";
	}

	private string LogEnemySpawns()
	{
		EnemyDirector val = Object.FindFirstObjectByType<EnemyDirector>();
		FieldInfo fieldInfo = AccessTools.Field(typeof(EnemyDirector), "enemiesSpawned");
		List<EnemyParent> list = ((Object.op_Implicit((Object)(object)val) && fieldInfo != null) ? (fieldInfo.GetValue(val) as List<EnemyParent>) : null);
		if (list == null)
		{
			return "Spawn roster unavailable.";
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Enemy roster: " + string.Join(", ", (from e in list
			where Object.op_Implicit((Object)(object)e)
			group e by ((Object)((Component)e).gameObject).name into @group
			select @group.Key + " x" + @group.Count()).ToArray())));
		return $"Roster written to BepInEx log. Apex replacements: {replacedSlots}/{originalSlots}.";
	}

	private static float MovementMultiplier(int percent)
	{
		return Math.Max(0.1f, 1f + (float)percent / 100f);
	}

	private static float ScaleMovement(float native)
	{
		if (!Object.op_Implicit((Object)(object)instance) || instance.movementMultiplier == 1f)
		{
			return native;
		}
		float num = native * instance.movementMultiplier;
		instance.TraceAppliedMovement(native, num);
		return num;
	}

	private static IEnumerable<CodeInstruction> ScaleNativeMovement(IEnumerable<CodeInstruction> instructions)
	{
		return ScaleFloatFieldReads(instructions, typeof(PlayerController), "ScaleMovement", "MoveSpeed", "SprintSpeed", "CrouchSpeed");
	}

	private static IEnumerable<CodeInstruction> ScaleFloatFieldReads(IEnumerable<CodeInstruction> instructions, Type owner, string scaler, params string[] fieldNames)
	{
		HashSet<FieldInfo> fields = new HashSet<FieldInfo>();
		foreach (string text in fieldNames)
		{
			FieldInfo fieldInfo = AccessTools.Field(owner, text);
			if (fieldInfo == null)
			{
				throw new MissingFieldException(owner.FullName, text);
			}
			fields.Add(fieldInfo);
		}
		MethodInfo scale = AccessTools.Method(typeof(Plugin), scaler, (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			yield return instruction;
			if (instruction.opcode == OpCodes.Ldfld && fields.Contains(instruction.operand as FieldInfo))
			{
				yield return new CodeInstruction(OpCodes.Call, (object)scale);
			}
		}
	}

	private static void BeforeEnemyHealthInitialized(EnemyHealth __instance)
	{
		if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)GameManager.instance) && Object.op_Implicit((Object)(object)RunManager.instance) && SemiFunc.RunIsLevel() && SemiFunc.IsMasterClientOrSingleplayer())
		{
			instance.ApplyEnemyHealth(__instance, instance.run.Modifier(instance.definitions, "enemyHp"));
		}
	}

	private void UpdateEnemyHealth()
	{
		if (!SemiFunc.IsMasterClientOrSingleplayer())
		{
			return;
		}
		int num = (SemiFunc.RunIsLevel() ? run.Modifier(definitions, "enemyHp") : 0);
		if (num == reconciledEnemyModifier)
		{
			return;
		}
		reconciledEnemyModifier = num;
		EnemyHealth[] array = Object.FindObjectsByType<EnemyHealth>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		foreach (EnemyHealth enemy in array)
		{
			if (ApplyEnemyHealth(enemy, num))
			{
				ClampEnemyHealth(enemy);
			}
		}
	}

	private bool ApplyEnemyHealth(EnemyHealth enemy, int modifier)
	{
		if (!enemyBaseHealth.TryGetValue(enemy, out var value))
		{
			if (modifier == 0 || enemy.health <= 0)
			{
				return false;
			}
			value = enemy.health;
			enemyBaseHealth.Add(enemy, value);
		}
		enemy.health = (int)Math.Max(1.0, Math.Min(2147483647.0, Math.Round((double)value * (1.0 + (double)modifier / 100.0), MidpointRounding.AwayFromZero)));
		if (modifier == 0)
		{
			enemyBaseHealth.Remove(enemy);
		}
		return true;
	}

	private static void ClampEnemyHealth(EnemyHealth enemy)
	{
		if ((int)EnemyCurrentHealth.GetValue(enemy) > enemy.health)
		{
			EnemyCurrentHealth.SetValue(enemy, enemy.health);
		}
	}

	private void RestoreEnemyHealth()
	{
		foreach (KeyValuePair<EnemyHealth, int> item in enemyBaseHealth)
		{
			if (Object.op_Implicit((Object)(object)item.Key))
			{
				item.Key.health = item.Value;
				ClampEnemyHealth(item.Key);
			}
		}
		enemyBaseHealth.Clear();
		reconciledEnemyModifier = 0;
	}

	private static void InstallEnemyAiPatches(Harmony harmony)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(EnemyVision), "Vision", (Type[])null, (Type[])null));
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ScaleNativeEnemyVision", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "InterceptEnemyAgentSpeed", (Type[])null);
		string[] array = new string[3] { "Update", "UpdateAgent", "OverrideAgent" };
		foreach (string text in array)
		{
			harmony.Patch((MethodBase)AccessTools.Method(typeof(EnemyNavMeshAgent), text, (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
		}
	}

	private void UpdateEnemyAiEffects()
	{
		if (!SemiFunc.IsMasterClientOrSingleplayer())
		{
			return;
		}
		bool flag = SemiFunc.RunIsLevel();
		enemyVisionMultiplier = (flag ? MovementMultiplier(run.Modifier(definitions, "enemyVision")) : 1f);
		int num = (flag ? run.Modifier(definitions, "enemySpeed") : 0);
		if (num == enemySpeedModifier)
		{
			return;
		}
		if (num == 0)
		{
			RestoreEnemySpeeds();
			return;
		}
		enemySpeedModifier = num;
		EnemyNavMeshAgent[] array = Object.FindObjectsByType<EnemyNavMeshAgent>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		foreach (EnemyNavMeshAgent obj in array)
		{
			object? value = EnemyAgent.GetValue(obj);
			NavMeshAgent val = (NavMeshAgent)((value is NavMeshAgent) ? value : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				if (!enemyBaseSpeeds.TryGetValue(val, out var value2))
				{
					value2 = val.speed;
				}
				SetEnemyAgentSpeed(val, value2);
			}
		}
	}

	private void RestoreEnemyAiEffects()
	{
		enemyVisionMultiplier = 1f;
		RestoreEnemySpeeds();
	}

	private void RestoreEnemySpeeds()
	{
		foreach (KeyValuePair<NavMeshAgent, float> enemyBaseSpeed in enemyBaseSpeeds)
		{
			if (Object.op_Implicit((Object)(object)enemyBaseSpeed.Key))
			{
				enemyBaseSpeed.Key.speed = enemyBaseSpeed.Value;
			}
		}
		enemyBaseSpeeds.Clear();
		enemySpeedModifier = 0;
	}

	private static void SetEnemyAgentSpeed(NavMeshAgent agent, float native)
	{
		if (!Object.op_Implicit((Object)(object)instance) || instance.enemySpeedModifier == 0)
		{
			agent.speed = native;
			return;
		}
		instance.enemyBaseSpeeds[agent] = native;
		agent.speed = native * MovementMultiplier(instance.enemySpeedModifier);
	}

	private static float ScaleEnemyVision(float native)
	{
		if (!Object.op_Implicit((Object)(object)instance))
		{
			return native;
		}
		return native * instance.enemyVisionMultiplier;
	}

	private static IEnumerable<CodeInstruction> ScaleNativeEnemyVision(IEnumerable<CodeInstruction> instructions)
	{
		return ScaleFloatFieldReads(instructions, typeof(EnemyVision), "ScaleEnemyVision", "VisionDistance", "VisionDistanceClose", "VisionDistanceCloseCrouch");
	}

	private static IEnumerable<CodeInstruction> InterceptEnemyAgentSpeed(IEnumerable<CodeInstruction> instructions)
	{
		MethodInfo original = AccessTools.PropertySetter(typeof(NavMeshAgent), "speed");
		MethodInfo replacement = AccessTools.Method(typeof(Plugin), "SetEnemyAgentSpeed", (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if (instruction.opcode == OpCodes.Callvirt && object.Equals(instruction.operand, original))
			{
				instruction.opcode = OpCodes.Call;
				instruction.operand = replacement;
			}
			yield return instruction;
		}
	}

	private static bool HasExternalSpawnConfig()
	{
		return Chainloader.PluginInfos.Values.Any((PluginInfo p) => p.Metadata.GUID.IndexOf("spawnconfig", StringComparison.OrdinalIgnoreCase) >= 0 || p.Metadata.Name.IndexOf("spawnconfig", StringComparison.OrdinalIgnoreCase) >= 0);
	}

	private static int AdjustTierOneCount(int original)
	{
		if (!Object.op_Implicit((Object)(object)instance) || !instance.buildingEnemySetup || instance.run.Active == null || instance.run.Active.Effect != "extraSlots")
		{
			return original;
		}
		if (HasExternalSpawnConfig())
		{
			((BaseUnityPlugin)instance).Logger.LogWarning((object)"Reinforcements skipped: another spawn configuration plugin is active.");
			return original;
		}
		int num = original + instance.run.Active.Amount;
		((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Tier-one enemy slots: {original} -> {num}");
		return num;
	}

	private static IEnumerable<CodeInstruction> ModifyEnemyAmounts(IEnumerable<CodeInstruction> instructions)
	{
		FieldInfo amount = AccessTools.Field(typeof(EnemyDirector), "amountCurve1Value");
		MethodInfo adjust = AccessTools.Method(typeof(Plugin), "AdjustTierOneCount", (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if (instruction.opcode == OpCodes.Stfld && object.Equals(instruction.operand, amount))
			{
				CodeInstruction call = new CodeInstruction(OpCodes.Call, (object)adjust);
				call.labels.AddRange(instruction.labels);
				instruction.labels.Clear();
				yield return call;
			}
			yield return instruction;
		}
	}

	private static void InstallExtractionPatches(Harmony harmony)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.EnumeratorMoveNext((MethodBase)AccessTools.Method(typeof(RoundDirector), "StartRound", (Type[])null, (Type[])null));
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ModifyRoundQuota", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "ReplaceDurabilityReads", (Type[])null);
		string[] array = new string[3] { "BreakLight", "BreakMedium", "BreakHeavy" };
		foreach (string text in array)
		{
			harmony.Patch((MethodBase)AccessTools.Method(typeof(PhysGrabObjectImpactDetector), text, (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
		}
	}

	private static int AdjustRoundQuota(int original)
	{
		int num = original;
		if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)GameManager.instance) && Object.op_Implicit((Object)(object)RunManager.instance) && SemiFunc.RunIsLevel() && SemiFunc.IsMasterClientOrSingleplayer() && instance.run.Active != null && instance.run.Active.Effect == "quota" && original > 0)
		{
			int amount = instance.run.Active.Amount;
			int num2 = ((Object.op_Implicit((Object)(object)RoundDirector.instance) && AvailableHaul != null) ? Math.Max(original, (int)AvailableHaul.GetValue(RoundDirector.instance)) : original);
			num = (int)Math.Min(num2, Math.Round((double)original * (1.0 + (double)amount / 100.0), MidpointRounding.AwayFromZero));
			((BaseUnityPlugin)instance).Logger.LogInfo((object)$"Extraction goal: {original} -> {num} (available {num2})");
		}
		return num;
	}

	private static float EffectiveDurability(PhysGrabObjectImpactDetector detector)
	{
		float durability = detector.durability;
		if (!Object.op_Implicit((Object)(object)instance) || !detector.isValuable || !Object.op_Implicit((Object)(object)GameManager.instance) || !Object.op_Implicit((Object)(object)RunManager.instance) || !SemiFunc.RunIsLevel() || !SemiFunc.IsMasterClientOrSingleplayer() || instance.run.Active == null || instance.run.Active.Effect != "durability")
		{
			return durability;
		}
		return Mathf.Clamp(durability * MovementMultiplier(instance.run.Active.Amount), 0f, 100f);
	}

	private static IEnumerable<CodeInstruction> ModifyRoundQuota(IEnumerable<CodeInstruction> instructions)
	{
		MethodInfo original = AccessTools.Method(typeof(RoundDirector), "StartRoundLogic", (Type[])null, (Type[])null);
		MethodInfo replacement = AccessTools.Method(typeof(Plugin), "AdjustRoundQuota", (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && object.Equals(instruction.operand, original))
			{
				CodeInstruction call = new CodeInstruction(OpCodes.Call, (object)replacement);
				call.labels.AddRange(instruction.labels);
				instruction.labels.Clear();
				yield return call;
			}
			yield return instruction;
		}
	}

	private static IEnumerable<CodeInstruction> ReplaceDurabilityReads(IEnumerable<CodeInstruction> instructions)
	{
		FieldInfo original = AccessTools.Field(typeof(PhysGrabObjectImpactDetector), "durability");
		MethodInfo replacement = AccessTools.Method(typeof(Plugin), "EffectiveDurability", (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if (instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, original))
			{
				instruction.opcode = OpCodes.Call;
				instruction.operand = replacement;
			}
			yield return instruction;
		}
	}

	private static void InstallEnemyDamagePatches(Harmony harmony)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Expected O, but got Unknown
		harmony.Patch((MethodBase)AccessTools.Method(typeof(HurtCollider), "PlayerHurt", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ReplaceEnemyDamageReads", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
		HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "ReplaceDirectEnemyHurtCalls", (Type[])null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(EnemySnatcher), "BackKickHitApply", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(EnemySnatcher), "FrontPunchHitApply", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(EnemySpinny), "Update", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static int EnemyDamageModifier()
	{
		if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)GameManager.instance) || !Object.op_Implicit((Object)(object)RunManager.instance) || !SemiFunc.RunIsLevel() || (SemiFunc.IsMultiplayer() && (!instance.NetworkActive || instance.authorityChanged)))
		{
			return 0;
		}
		return instance.EffectiveModifier("enemyAttack");
	}

	private static int ScaleEnemyDamage(int native, int modifier)
	{
		if (native <= 0 || modifier == 0)
		{
			return native;
		}
		return (int)Math.Min(2147483647.0, Math.Round((double)native * (1.0 + (double)modifier / 100.0), MidpointRounding.AwayFromZero));
	}

	private static int EffectiveColliderDamage(HurtCollider collider)
	{
		int num = EnemyDamageModifier();
		if (num == 0)
		{
			return collider.playerDamage;
		}
		PlayerAvatar val = (PlayerAvatar)((DamageSourcePlayer == null) ? null : /*isinst with value type is only supported in some contexts*/);
		if (Object.op_Implicit((Object)(object)val))
		{
			return collider.playerDamage;
		}
		Enemy val2 = (Enemy)((DamageSourceEnemy == null) ? null : /*isinst with value type is only supported in some contexts*/);
		if (!Object.op_Implicit((Object)(object)val2) && !Object.op_Implicit((Object)(object)((Component)collider).GetComponentInParent<EnemyParent>()))
		{
			return collider.playerDamage;
		}
		return ScaleEnemyDamage(collider.playerDamage, num);
	}

	private static void HurtFromEnemy(PlayerHealth health, int damage, bool allowDeath, int enemyIndex, bool hurtFreeze)
	{
		health.Hurt(ScaleEnemyDamage(damage, EnemyDamageModifier()), allowDeath, enemyIndex, hurtFreeze);
	}

	private static IEnumerable<CodeInstruction> ReplaceEnemyDamageReads(IEnumerable<CodeInstruction> instructions)
	{
		FieldInfo original = AccessTools.Field(typeof(HurtCollider), "playerDamage");
		MethodInfo replacement = AccessTools.Method(typeof(Plugin), "EffectiveColliderDamage", (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if (instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, original))
			{
				instruction.opcode = OpCodes.Call;
				instruction.operand = replacement;
			}
			yield return instruction;
		}
	}

	private static IEnumerable<CodeInstruction> ReplaceDirectEnemyHurtCalls(IEnumerable<CodeInstruction> instructions)
	{
		MethodInfo original = AccessTools.Method(typeof(PlayerHealth), "Hurt", (Type[])null, (Type[])null);
		MethodInfo replacement = AccessTools.Method(typeof(Plugin), "HurtFromEnemy", (Type[])null, (Type[])null);
		foreach (CodeInstruction instruction in instructions)
		{
			if ((instruction.opcode == OpCodes.Callvirt || instruction.opcode == OpCodes.Call) && object.Equals(instruction.operand, original))
			{
				instruction.opcode = OpCodes.Call;
				instruction.operand = replacement;
			}
			yield return instruction;
		}
	}

	private static void InstallValuableValuePatches(Harmony harmony)
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Expected O, but got Unknown
		//IL_0095: Expected O, but got Unknown
		if (Appraised == null || OriginalValue == null || CurrentValue == null || ValuableView == null || AvailableHaul == null)
		{
			throw new MissingFieldException("Valuable appraisal fields unavailable.");
		}
		harmony.Patch((MethodBase)AccessTools.Method(typeof(ValuableObject), "DollarValueSetLogic", (Type[])null, (Type[])null), new HarmonyMethod(typeof(Plugin), "BeforeNativeAppraisal", (Type[])null), new HarmonyMethod(typeof(Plugin), "AfterNativeAppraisal", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private bool ValueEffectActive()
	{
		if (Object.op_Implicit((Object)(object)GameManager.instance) && Object.op_Implicit((Object)(object)RunManager.instance) && SemiFunc.RunIsLevel() && SemiFunc.IsMasterClientOrSingleplayer() && run.Active != null)
		{
			return run.Active.Effect == "treasureValue";
		}
		return false;
	}

	private void ReconcileExistingAppraisals()
	{
		if (!ValueEffectActive() || !Object.op_Implicit((Object)(object)RoundDirector.instance))
		{
			return;
		}
		RoundDirector obj = RoundDirector.instance;
		long num = (int)AvailableHaul.GetValue(obj);
		ValuableObject[] array = Object.FindObjectsByType<ValuableObject>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		foreach (ValuableObject val in array)
		{
			if (!(bool)Appraised.GetValue(val))
			{
				continue;
			}
			float num2 = (float)OriginalValue.GetValue(val);
			float num3 = (float)CurrentValue.GetValue(val);
			PhotonView val2 = null;
			if (SemiFunc.IsMultiplayer())
			{
				object? value = ValuableView.GetValue(val);
				val2 = (PhotonView)((value is PhotonView) ? value : null);
				if (!Object.op_Implicit((Object)(object)val2) || val2.ViewID == 0 || num3 != num2)
				{
					continue;
				}
			}
			if (ApplyAppraisal(val, num2, num3, out var adjusted))
			{
				num += (long)(int)(float)CurrentValue.GetValue(val) - (long)(int)num3;
				if (Object.op_Implicit((Object)(object)val2))
				{
					val2.RPC("DollarValueSetRPC", (RpcTarget)1, new object[1] { adjusted });
				}
			}
		}
		AvailableHaul.SetValue(obj, (int)Math.Max(0L, Math.Min(2147483647L, num)));
	}

	private static void BeforeNativeAppraisal(ValuableObject __instance, out bool __state)
	{
		__state = Object.op_Implicit((Object)(object)instance) && instance.ValueEffectActive() && !(bool)Appraised.GetValue(__instance);
	}

	private static void AfterNativeAppraisal(ValuableObject __instance, bool __state)
	{
		if (__state && Object.op_Implicit((Object)(object)instance) && instance.ValueEffectActive() && (bool)Appraised.GetValue(__instance))
		{
			instance.ApplyAppraisal(__instance, (float)OriginalValue.GetValue(__instance), (float)CurrentValue.GetValue(__instance), out var _);
		}
	}

	private bool ApplyAppraisal(ValuableObject item, float original, float current, out float adjusted)
	{
		adjusted = original;
		if (original <= 0f || valuableOriginalAppraisals.ContainsKey(item))
		{
			return false;
		}
		float num = MovementMultiplier(run.Active.Amount);
		adjusted = Mathf.Max(1f, Mathf.Round(original * num));
		valuableOriginalAppraisals.Add(item, original);
		OriginalValue.SetValue(item, adjusted);
		CurrentValue.SetValue(item, Mathf.Max(0f, Mathf.Round(current * num)));
		if (debug.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Valuable appraisal: {original} -> {adjusted}");
		}
		return true;
	}

	private static void InstallValuableMassPatches(Harmony harmony)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Expected O, but got Unknown
		HarmonyMethod val = new HarmonyMethod(typeof(Plugin), "InterceptValuableMass", (Type[])null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(PhysGrabObject), "OverrideTimersTick", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(PhysGrabObject), "ResetMass", (Type[])null, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private void UpdateValuableMasses()
	{
		int num = EffectiveModifier("treasureMass");
		if (num == valuableMassModifier)
		{
			return;
		}
		if (num == 0)
		{
			RestoreValuableMasses();
			return;
		}
		valuableMassM