Decompiled source of Repo Contracts v0.5.16

BepInEx\plugins\RepoContracts\RepoContracts.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using RepoContracts.Adapters;
using RepoContracts.Contracts;
using RepoContracts.Networking;
using RepoContracts.Patches;
using RepoContracts.Rewards;
using RepoContracts.UI;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("RepoContracts")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Optional contract objectives and tiered rewards for R.E.P.O.")]
[assembly: AssemblyFileVersion("0.5.16.0")]
[assembly: AssemblyInformationalVersion("0.5.16")]
[assembly: AssemblyProduct("RepoContracts")]
[assembly: AssemblyTitle("RepoContracts")]
[assembly: AssemblyVersion("0.5.16.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RepoContracts
{
	[BepInPlugin("com.amgru.repocontracts", "RepoContracts", "0.5.16")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.amgru.repocontracts";

		public const string PluginName = "RepoContracts";

		public const string PluginVersion = "0.5.16";

		internal static ConfigEntry<bool> EnableRepoContracts;

		internal static ConfigEntry<int> OfferedContractsPerLevel;

		internal static ConfigEntry<int> SelectedContractsPerLevel;

		internal static ConfigEntry<bool> OneActiveContractAtATime;

		internal static ConfigEntry<bool> AdvanceQueueOnFailure;

		internal static ConfigEntry<bool> AdvanceQueueOnCompletion;

		internal static ConfigEntry<bool> EnableMilestoneContracts;

		internal static ConfigEntry<int> MilestoneContractEveryXLevels;

		internal static ConfigEntry<bool> AllowNormalContractsDuringMilestone;

		internal static ConfigEntry<float> RewardMultiplier;

		internal static ConfigEntry<FailureStrictness> FailureStrictness;

		internal static ConfigEntry<DisciplineMode> DisciplineMode;

		internal static ConfigEntry<bool> EnableDisciplineDebt;

		internal static ConfigEntry<bool> EnableDebugLogging;

		internal static ConfigEntry<bool> AutoLockFirstOffersForDebug;

		internal static ConfigEntry<KeyCode> ContractHudKey;

		internal static ConfigEntry<KeyCode> ContractBoardLockKey;

		internal static ConfigEntry<float> BoardSideOffset;

		internal static ConfigEntry<float> BoardForwardOffset;

		internal static ConfigEntry<float> BoardUpOffset;

		internal static ConfigEntry<float> BoardYawOffset;

		internal static ConfigEntry<float> BoardScale;

		internal static ConfigEntry<float> BoardNudgeStep;

		internal static ConfigEntry<float> BoardRotateStep;

		internal static ConfigEntry<bool> EnableExperimentalContracts;

		internal static ConfigEntry<bool> EnableAssetHeavyContracts;

		internal static ConfigEntry<bool> EnableCompatibilityMode;

		private Harmony? harmony;

		internal static Plugin Instance { get; private set; }

		internal static ContractManager Manager { get; private set; }

		internal static ManualLogSource Log => ((BaseUnityPlugin)Instance).Logger;

		private void Awake()
		{
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cb: Expected O, but got Unknown
			Instance = this;
			EnableRepoContracts = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableRepoContracts", true, "Enables or disables Repo Contracts.");
			OfferedContractsPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Contracts", "OfferedContractsPerLevel", 5, "Contracts shown on the truck board.");
			SelectedContractsPerLevel = ((BaseUnityPlugin)this).Config.Bind<int>("Contracts", "SelectedContractsPerLevel", 3, "Contracts selected into the priority queue.");
			OneActiveContractAtATime = ((BaseUnityPlugin)this).Config.Bind<bool>("Contracts", "OneActiveContractAtATime", true, "Only one selected contract is active at a time.");
			AdvanceQueueOnFailure = ((BaseUnityPlugin)this).Config.Bind<bool>("Contracts", "AdvanceQueueOnFailure", true, "Automatically activate the next contract when the active one fails.");
			AdvanceQueueOnCompletion = ((BaseUnityPlugin)this).Config.Bind<bool>("Contracts", "AdvanceQueueOnCompletion", true, "Automatically activate the next contract when the active one completes.");
			EnableMilestoneContracts = ((BaseUnityPlugin)this).Config.Bind<bool>("Milestones", "EnableMilestoneContracts", true, "Enables special all-encompassing milestone contracts.");
			MilestoneContractEveryXLevels = ((BaseUnityPlugin)this).Config.Bind<int>("Milestones", "MilestoneContractEveryXLevels", 3, "Offer a milestone contract every X completed levels.");
			AllowNormalContractsDuringMilestone = ((BaseUnityPlugin)this).Config.Bind<bool>("Milestones", "AllowNormalContractsDuringMilestone", false, "Allows the normal queue alongside milestone contracts.");
			RewardMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Rewards", "RewardMultiplier", 1f, "Scales additive contract rewards.");
			FailureStrictness = ((BaseUnityPlugin)this).Config.Bind<FailureStrictness>("Contracts", "FailureStrictness", RepoContracts.Contracts.FailureStrictness.Normal, "How punishing failure variants should be.");
			DisciplineMode = ((BaseUnityPlugin)this).Config.Bind<DisciplineMode>("Discipline", "DisciplineMode", RepoContracts.Contracts.DisciplineMode.Standard, "Future penalty severity for failed contracts when discipline debt is enabled.");
			EnableDisciplineDebt = ((BaseUnityPlugin)this).Config.Bind<bool>("Discipline", "EnableDisciplineDebt", false, "Future/off-by-default setting. When enabled, failed contracts add debt that withholds future contract rewards.");
			EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugLogging", true, "Logs adapter activity and TODO hook gaps.");
			AutoLockFirstOffersForDebug = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "AutoLockFirstOffersForDebug", false, "Debug helper that auto-selects and locks the first offered contracts after generation.");
			ContractHudKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "ContractHudKey", (KeyCode)99, "Keyboard key that toggles the active contract HUD overlay.");
			ContractBoardLockKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "ContractBoardLockKey", (KeyCode)108, "Host keyboard key that locks/tallies board votes while pointing at the board.");
			BoardSideOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Board", "SideOffset", -6.149996f, "Meters to the truck side from the anchor for the contract TV.");
			BoardForwardOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Board", "ForwardOffset", -0.04999985f, "Meters forward/back from the anchor for the contract TV.");
			BoardUpOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Board", "UpOffset", 0.3499997f, "Meters upward from the anchor for the contract TV.");
			BoardYawOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Board", "YawOffset", 270f, "Yaw offset from the truck anchor for the contract TV.");
			BoardScale = ((BaseUnityPlugin)this).Config.Bind<float>("Board", "Scale", 2.099999f, "Overall contract TV scale.");
			BoardNudgeStep = ((BaseUnityPlugin)this).Config.Bind<float>("Board", "NudgeStep", 0.1f, "Meters moved per calibration key press.");
			BoardRotateStep = ((BaseUnityPlugin)this).Config.Bind<float>("Board", "RotateStep", 5f, "Degrees rotated per calibration key press.");
			EnableExperimentalContracts = ((BaseUnityPlugin)this).Config.Bind<bool>("Contracts", "EnableExperimentalContracts", false, "Allows contracts that rely on unverified or risky hooks.");
			EnableAssetHeavyContracts = ((BaseUnityPlugin)this).Config.Bind<bool>("Contracts", "EnableAssetHeavyContracts", false, "Allows contracts needing new world models, VFX, SFX, or heavier UI assets.");
			EnableCompatibilityMode = ((BaseUnityPlugin)this).Config.Bind<bool>("Compatibility", "EnableCompatibilityMode", true, "Skips contracts needing risky object replacement, spawn replacement, control manipulation, or enemy roster manipulation.");
			ContractBoardUi boardUi = ((Component)this).gameObject.AddComponent<ContractBoardUi>();
			ContractHud hud = ((Component)this).gameObject.AddComponent<ContractHud>();
			Manager = new ContractManager(new ContractCatalog(), new RewardAdapter(), new ContractSyncAdapter(), boardUi, hud);
			harmony = new Harmony("com.amgru.repocontracts");
			harmony.PatchAll();
			OptionalContractPatches.Apply(harmony);
			HookAudit.LogKnownHookStatus();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"RepoContracts v0.5.16 loaded.");
		}

		private void Update()
		{
			Manager?.PollSyncedState();
			Manager?.UpdateActiveWorldContracts();
		}

		private void OnDestroy()
		{
			Harmony? obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		internal static void SaveSettings()
		{
			((BaseUnityPlugin)Instance).Config.Save();
		}
	}
}
namespace RepoContracts.Patches
{
	[HarmonyPatch(typeof(LevelGenerator), "GenerateDone")]
	internal static class LevelGeneratorGenerateDonePatch
	{
		private static void Postfix()
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			Plugin.Manager.GenerateOffersAtTruck();
			if (Plugin.Manager.Queue.OfferedContracts.Count != 0)
			{
				Plugin.Manager.EventBus.OnLevelStarted();
				if (Plugin.AutoLockFirstOffersForDebug.Value)
				{
					Plugin.Manager.HostLockQueue();
				}
			}
		}
	}
	[HarmonyPatch(typeof(HurtCollider), "EnemyHurt")]
	internal static class ContractEnemyHurtPatch
	{
		private static void Postfix(HurtCollider __instance, Enemy _enemy, bool __result)
		{
			if (__result && SemiFunc.IsMasterClientOrSingleplayer() && (Object)(object)_enemy != (Object)null)
			{
				Plugin.Manager.EventBus.OnEnemyDamaged(((Component)_enemy).gameObject, 1, "hurt-collider");
			}
		}
	}
	[HarmonyPatch(typeof(HurtCollider), "PlayerHurt")]
	internal static class ContractPlayerHurtPatch
	{
		private static void Postfix(PlayerAvatar _player)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && (Object)(object)_player != (Object)null)
			{
				Plugin.Manager.EventBus.OnPlayerDamaged(((Component)_player).gameObject);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyHealth), "DeathImpulseRPC")]
	internal static class ContractEnemyKilledPatch
	{
		private static void Postfix(EnemyHealth __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null))
			{
				Enemy component = ((Component)__instance).GetComponent<Enemy>();
				GameObject enemy = (((Object)(object)component != (Object)null) ? ((Component)component).gameObject : ((Component)__instance).gameObject);
				Plugin.Manager.HandleEnemyKilled(enemy, "verified-enemy-health");
			}
		}
	}
	[HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")]
	internal static class ContractPlayerDeathPatch
	{
		private static void Postfix(PlayerAvatar __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null))
			{
				Plugin.Manager.HandlePlayerDeath(((Component)__instance).gameObject);
			}
		}
	}
	[HarmonyPatch(typeof(ItemGun), "Shoot")]
	internal static class ContractProjectileWeaponUsedPatch
	{
		private static void Postfix(ItemGun __instance, bool __result)
		{
			if (__result && SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null))
			{
				Plugin.Manager.EventBus.OnProjectileWeaponUsed(((Component)__instance).gameObject);
			}
		}
	}
	[HarmonyPatch(typeof(RoundDirector), "HaulCheck")]
	internal static class ContractHaulCheckPatch
	{
		private static void Postfix(RoundDirector __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null))
			{
				List<GameObject> candidates = __instance.dollarHaulList.Where((GameObject item) => (Object)(object)item != (Object)null).ToList();
				Plugin.Manager.CaptureExtractionCandidates(candidates);
			}
		}
	}
	[HarmonyPatch(typeof(RoundDirector), "ExtractionCompleted")]
	internal static class ContractExtractionCompletedPatch
	{
		private static void Postfix(RoundDirector __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				Plugin.Manager.HandleExtractionCompleted(null);
			}
		}
	}
	[HarmonyPatch(typeof(ExtractionPoint), "DestroyTheFirstPhysObjectsInHaulList")]
	internal static class ContractExtractionPointItemExtractPatch
	{
		private static bool Prefix(ExtractionPoint __instance)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer() || (Object)(object)RoundDirector.instance == (Object)null || RoundDirector.instance.dollarHaulList.Count == 0)
			{
				return true;
			}
			if ((Object)(object)__instance != (Object)null && Plugin.Manager.ShouldBlockPressurePlateExtraction(((Component)__instance).gameObject))
			{
				return false;
			}
			GameObject val = RoundDirector.instance.dollarHaulList[0];
			if ((Object)(object)val == (Object)null || (Object)(object)val.GetComponent<ValuableObject>() == (Object)null)
			{
				return true;
			}
			Plugin.Manager.HandleValuableExtracting(val, ((Object)(object)__instance != (Object)null) ? ((Component)__instance).gameObject : null);
			return true;
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")]
	internal static class ContractValuableBreakPatch
	{
		private static bool Prefix(PhysGrabObjectImpactDetector __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				return !Plugin.Manager.IsProtectedPressurePlateWeight(((Component)__instance).gameObject);
			}
			return true;
		}

		private static void Postfix(PhysGrabObjectImpactDetector __instance, float valueLost, bool _loseValue)
		{
			if (_loseValue && SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null))
			{
				ValuableObject component = ((Component)__instance).GetComponent<ValuableObject>();
				if (!((Object)(object)component == (Object)null))
				{
					Plugin.Manager.HandleValuableDamaged(((Component)component).gameObject, Mathf.RoundToInt(valueLost));
				}
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "DestroyObjectRPC")]
	internal static class ContractValuableDestroyedPatch
	{
		private static bool Prefix(PhysGrabObjectImpactDetector __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				return !Plugin.Manager.IsProtectedPressurePlateWeight(((Component)__instance).gameObject);
			}
			return true;
		}

		private static void Postfix(PhysGrabObjectImpactDetector __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null))
			{
				ValuableObject component = ((Component)__instance).GetComponent<ValuableObject>();
				if (!((Object)(object)component == (Object)null))
				{
					Plugin.Manager.HandleValuableDestroyed(((Component)component).gameObject);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabStarted")]
	internal static class ContractItemGrabStartedPatch
	{
		private static void Postfix(PhysGrabObject __instance, PhysGrabber player)
		{
			if (!((Object)(object)__instance == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer())
			{
				ValuableObject component = ((Component)__instance).GetComponent<ValuableObject>();
				if (!((Object)(object)component == (Object)null))
				{
					Plugin.Manager.HandleItemGrabbed(((Component)component).gameObject, ((Object)(object)player != (Object)null) ? ((Component)player).gameObject : null);
				}
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObject), "GrabPlayerAddRPC")]
	internal static class ContractItemGrabPlayerAddPatch
	{
		private static void Postfix(PhysGrabObject __instance, int photonViewID)
		{
			if (!((Object)(object)__instance == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer())
			{
				ValuableObject component = ((Component)__instance).GetComponent<ValuableObject>();
				if (!((Object)(object)component == (Object)null))
				{
					PhotonView obj = PhotonView.Find(photonViewID);
					GameObject player = ((obj != null) ? ((Component)obj).gameObject : null);
					Plugin.Manager.HandleItemGrabbed(((Component)component).gameObject, player);
				}
			}
		}
	}
	internal static class OptionalContractPatches
	{
		internal static void Apply(Harmony harmony)
		{
			PatchIfFound(harmony, typeof(TruckHealer), "Heal", "player healing", null, AccessTools.Method(typeof(OptionalContractPatches), "TruckHealerHealPostfix", (Type[])null, (Type[])null));
			PatchIfFound(harmony, typeof(RunManager), "ChangeLevel", "level transition fallback", AccessTools.Method(typeof(OptionalContractPatches), "RunManagerChangeLevelPrefix", (Type[])null, (Type[])null));
		}

		private static void PatchIfFound(Harmony harmony, Type type, string methodName, string label, MethodInfo? prefix = null, MethodInfo? postfix = null)
		{
			//IL_0024: 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)
			MethodInfo methodInfo = HookAudit.TryMethod(type, methodName, label);
			if (!(methodInfo == null))
			{
				object obj = harmony;
				obj = methodInfo;
				obj = (object)((!(prefix != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(prefix));
				obj = (object)((!(postfix != null)) ? ((HarmonyMethod)null) : new HarmonyMethod(postfix));
				((Harmony)obj).Patch((MethodBase)obj, (HarmonyMethod)obj, (HarmonyMethod)obj, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
		}

		private static void TruckHealerHealPostfix()
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				Plugin.Manager.EventBus.OnPlayerHealed(null, 10);
			}
		}

		private static void RunManagerChangeLevelPrefix()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				Plugin.Manager.CompleteLevel(Vector3.zero);
			}
		}
	}
}
namespace RepoContracts.UI
{
	internal static class ContractAssets
	{
		private static readonly Dictionary<string, Texture2D?> Textures = new Dictionary<string, Texture2D>();

		private static readonly Dictionary<string, Material?> Materials = new Dictionary<string, Material>();

		internal static Texture2D? Texture(string relativePath)
		{
			//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)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			if (Textures.TryGetValue(relativePath, out Texture2D value))
			{
				return value;
			}
			string path = FindAssetPath(relativePath);
			if (!File.Exists(path))
			{
				Plugin.Log.LogWarning((object)("[RepoContracts] Missing packaged asset: " + relativePath));
				Textures[relativePath] = null;
				return null;
			}
			try
			{
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = Path.GetFileNameWithoutExtension(relativePath),
					wrapMode = (TextureWrapMode)1,
					filterMode = (FilterMode)1
				};
				if (!LoadImage(val, File.ReadAllBytes(path)))
				{
					Plugin.Log.LogWarning((object)("[RepoContracts] Unity could not decode packaged asset: " + relativePath));
					Textures[relativePath] = null;
					return null;
				}
				Textures[relativePath] = val;
				return val;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[RepoContracts] Failed to load packaged asset " + relativePath + ": " + ex.Message));
				Textures[relativePath] = null;
				return null;
			}
		}

		internal static Material? Material(string relativePath, Color tint, bool transparent = true)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			string key = $"{relativePath}|{tint}|{transparent}";
			if (Materials.TryGetValue(key, out Material value))
			{
				return value;
			}
			Texture2D val = Texture(relativePath);
			if ((Object)(object)val == (Object)null)
			{
				Materials[key] = null;
				return null;
			}
			Material val2 = new Material(Shader.Find("Unlit/Transparent") ?? Shader.Find("GUI/Text Shader") ?? Shader.Find("Standard"))
			{
				name = "RepoContracts " + Path.GetFileNameWithoutExtension(relativePath),
				mainTexture = (Texture)(object)val,
				color = tint
			};
			if (transparent)
			{
				ConfigureTransparentMaterial(val2);
			}
			Materials[key] = val2;
			return val2;
		}

		internal static void ApplyTexture(GameObject target, string relativePath, Color tint, bool transparent = true)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			Renderer component = target.GetComponent<Renderer>();
			Material val = Material(relativePath, tint, transparent);
			if ((Object)(object)component != (Object)null && (Object)(object)val != (Object)null)
			{
				component.material = val;
			}
		}

		internal static void ApplyTextMaterial(TextMesh text, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			text.color = color;
			Renderer component = ((Component)text).GetComponent<Renderer>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			Material val = (((Object)(object)text.font != (Object)null) ? text.font.material : null);
			Shader val2 = Shader.Find("GUI/Text Shader");
			if (!((Object)(object)val == (Object)null) || !((Object)(object)val2 == (Object)null))
			{
				Material val3 = (((Object)(object)val != (Object)null) ? new Material(val) : new Material(val2));
				if ((Object)(object)val2 != (Object)null)
				{
					val3.shader = val2;
				}
				if ((Object)(object)val != (Object)null && (Object)(object)val.mainTexture != (Object)null)
				{
					val3.mainTexture = val.mainTexture;
				}
				val3.color = color;
				val3.renderQueue = 3000;
				component.material = val3;
			}
		}

		private static string FindAssetPath(string relativePath)
		{
			string text = relativePath.Replace('/', Path.DirectorySeparatorChar);
			string text2 = Path.Combine(Paths.PluginPath, "RepoContracts", "assets", text);
			if (File.Exists(text2))
			{
				return text2;
			}
			string text3 = Path.Combine(Path.GetDirectoryName(typeof(Plugin).Assembly.Location) ?? Paths.PluginPath, "assets", text);
			if (File.Exists(text3))
			{
				return text3;
			}
			return Path.Combine(Paths.GameRootPath, "assets", "repo_contracts", text);
		}

		private static bool LoadImage(Texture2D texture, byte[] bytes)
		{
			MethodInfo methodInfo = (Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule") ?? Type.GetType("UnityEngine.ImageConversion, UnityEngine.CoreModule") ?? Type.GetType("UnityEngine.ImageConversion, UnityEngine"))?.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
			{
				typeof(Texture2D),
				typeof(byte[]),
				typeof(bool)
			}, null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)"[RepoContracts] UnityEngine.ImageConversion.LoadImage was unavailable; packaged PNG assets cannot be decoded.");
				return false;
			}
			object obj = methodInfo.Invoke(null, new object[3] { texture, bytes, false });
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static void ConfigureTransparentMaterial(Material material)
		{
			material.SetFloat("_Mode", 3f);
			material.SetInt("_SrcBlend", 5);
			material.SetInt("_DstBlend", 10);
			material.SetInt("_ZWrite", 0);
			material.DisableKeyword("_ALPHATEST_ON");
			material.EnableKeyword("_ALPHABLEND_ON");
			material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
			material.renderQueue = 3000;
		}
	}
	internal sealed class ContractBoardUi : MonoBehaviour
	{
		private readonly List<ContractBoardCard> cards = new List<ContractBoardCard>();

		private ContractQueue? queue;

		private GameObject? boardRoot;

		private TextMesh? headerText;

		private TextMesh? selectedText;

		private TextMesh? promptText;

		private int highlightedIndex = -1;

		private bool placementMode;

		private float readableSideSign = -1f;

		private float controllerMoveCooldownUntil;

		internal void Show(ContractQueue contractQueue)
		{
			queue = contractQueue;
			EnsureBoard();
			RebuildBoardText();
			LogBoard("show");
		}

		internal void Refresh(ContractQueue contractQueue)
		{
			queue = contractQueue;
			EnsureBoard();
			RebuildBoardText();
			LogBoard("refresh");
		}

		internal void Hide()
		{
			if ((Object)(object)boardRoot != (Object)null)
			{
				Object.Destroy((Object)(object)boardRoot);
				boardRoot = null;
			}
			cards.Clear();
			headerText = null;
			selectedText = null;
			promptText = null;
			highlightedIndex = -1;
		}

		internal Vector3? GetRewardSpawnPosition()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)boardRoot == (Object)null)
			{
				return null;
			}
			return boardRoot.transform.position - boardRoot.transform.forward * (1.15f * Plugin.BoardScale.Value) + Vector3.up * (0.2f * Plugin.BoardScale.Value);
		}

		private void Update()
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)boardRoot != (Object)null)
			{
				UpdatePlacementMode();
				if (placementMode)
				{
					return;
				}
			}
			if (queue == null || (Object)(object)boardRoot == (Object)null || queue.OfferedContracts.Count == 0)
			{
				return;
			}
			UpdateHighlight();
			if (queue.IsLocked)
			{
				return;
			}
			UpdateControllerHighlight();
			Plugin.Manager.TryAutoLockQueueFromVotes();
			if (highlightedIndex >= 0 && highlightedIndex < queue.OfferedContracts.Count)
			{
				if (BoardClickPressed())
				{
					VoteNextRank();
				}
				else if (Input.GetKeyDown((KeyCode)49) || ControllerRankPressed(1))
				{
					Vote(1);
				}
				else if (Input.GetKeyDown((KeyCode)50) || ControllerRankPressed(2))
				{
					Vote(2);
				}
				else if (Input.GetKeyDown((KeyCode)51) || ControllerRankPressed(3))
				{
					Vote(3);
				}
				if (Input.GetKeyDown(Plugin.ContractBoardLockKey.Value))
				{
					Plugin.Manager.HostLockQueueFromVotes();
				}
			}
		}

		private void Vote(int rank)
		{
			if (queue != null)
			{
				string localPlayerId = GetLocalPlayerId();
				string definitionId = queue.OfferedContracts[highlightedIndex].DefinitionId;
				if (Plugin.Manager.CastVote(localPlayerId, rank, definitionId))
				{
					RebuildBoardText();
					Plugin.Manager.TryAutoLockQueueFromVotes();
				}
			}
		}

		private void VoteNextRank()
		{
			if (queue == null || highlightedIndex < 0 || highlightedIndex >= queue.OfferedContracts.Count)
			{
				return;
			}
			string localPlayerId = GetLocalPlayerId();
			Plugin.Manager.PlayerVotes.TryGetValue(localPlayerId, out string[] value);
			string definitionId = queue.OfferedContracts[highlightedIndex].DefinitionId;
			if (value != null)
			{
				int num = Array.IndexOf(value, definitionId);
				if (num >= 0)
				{
					Vote(num + 1);
					return;
				}
			}
			int num2 = ((value != null) ? Array.FindIndex(value, string.IsNullOrWhiteSpace) : 0);
			int rank = ((num2 < 0) ? 1 : (num2 + 1));
			Vote(rank);
		}

		private void EnsureBoard()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_0471: Unknown result type (might be due to invalid IL or missing references)
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_050d: Unknown result type (might be due to invalid IL or missing references)
			//IL_052c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0550: Unknown result type (might be due to invalid IL or missing references)
			//IL_0575: Unknown result type (might be due to invalid IL or missing references)
			//IL_0589: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0620: Unknown result type (might be due to invalid IL or missing references)
			//IL_0634: Unknown result type (might be due to invalid IL or missing references)
			//IL_0650: Unknown result type (might be due to invalid IL or missing references)
			//IL_066e: Unknown result type (might be due to invalid IL or missing references)
			//IL_068f: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_075e: Unknown result type (might be due to invalid IL or missing references)
			//IL_077d: Unknown result type (might be due to invalid IL or missing references)
			//IL_07c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_085b: Unknown result type (might be due to invalid IL or missing references)
			//IL_087a: Unknown result type (might be due to invalid IL or missing references)
			//IL_089e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)boardRoot != (Object)null))
			{
				GameObject val = FindTruckAnchor();
				Vector3 position = (((Object)(object)val != (Object)null) ? (val.transform.position + val.transform.right * Plugin.BoardSideOffset.Value + val.transform.forward * Plugin.BoardForwardOffset.Value + Vector3.up * Plugin.BoardUpOffset.Value) : (Vector3.up * Plugin.BoardUpOffset.Value));
				Quaternion rotation = (((Object)(object)val != (Object)null) ? Quaternion.Euler(0f, val.transform.eulerAngles.y + Plugin.BoardYawOffset.Value, 0f) : Quaternion.identity);
				boardRoot = new GameObject("Repo Contracts Board");
				boardRoot.transform.position = position;
				boardRoot.transform.rotation = rotation;
				boardRoot.transform.localScale = Vector3.one * Plugin.BoardScale.Value;
				if ((Object)(object)((val != null) ? val.transform.parent : null) != (Object)null)
				{
					boardRoot.transform.SetParent(val.transform.parent, true);
				}
				readableSideSign = ResolveReadableSideSign(boardRoot.transform);
				GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj).name = "Repo Contracts Board Back";
				obj.transform.SetParent(boardRoot.transform, false);
				obj.transform.localPosition = Vector3.zero;
				obj.transform.localScale = new Vector3(1.58f, 0.92f, 0.045f);
				DisablePhysicalCollider(obj);
				SetColor(obj, new Color(0.008f, 0.012f, 0.014f, 1f));
				GameObject obj2 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj2).name = "Repo Contracts TV Bezel";
				obj2.transform.SetParent(boardRoot.transform, false);
				obj2.transform.localPosition = new Vector3(0f, 0f, SurfaceZ(0.03f));
				obj2.transform.localScale = new Vector3(1.68f, 1.02f, 0.035f);
				DisablePhysicalCollider(obj2);
				SetColor(obj2, new Color(0.02f, 0.022f, 0.024f, 1f));
				GameObject obj3 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj3).name = "Repo Contracts TV Screen";
				obj3.transform.SetParent(boardRoot.transform, false);
				obj3.transform.localPosition = new Vector3(0f, 0f, SurfaceZ(0.035f));
				obj3.transform.localScale = new Vector3(1.5f, 0.82f, 0.018f);
				DisablePhysicalCollider(obj3);
				SetColor(obj3, new Color(0.006f, 0.035f, 0.04f, 1f));
				GameObject obj4 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj4).name = "Repo Contracts Card Panel";
				obj4.transform.SetParent(boardRoot.transform, false);
				obj4.transform.localPosition = new Vector3(-0.2f, -0.035f, SurfaceZ(0.07f));
				obj4.transform.localScale = new Vector3(1.14f, 0.68f, 0.012f);
				DisablePhysicalCollider(obj4);
				SetColor(obj4, new Color(0.004f, 0.018f, 0.02f, 1f));
				GameObject obj5 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj5).name = "Repo Contracts Accent Rail";
				obj5.transform.SetParent(boardRoot.transform, false);
				obj5.transform.localPosition = new Vector3(-0.2f, 0.305f, SurfaceZ(0.118f));
				obj5.transform.localScale = new Vector3(1.16f, 0.012f, 0.01f);
				DisablePhysicalCollider(obj5);
				SetColor(obj5, new Color(0.18f, 0.62f, 0.52f, 1f));
				GameObject obj6 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj6).name = "Repo Contracts TV Glow";
				obj6.transform.SetParent(boardRoot.transform, false);
				obj6.transform.localPosition = new Vector3(0f, 0f, SurfaceZ(0.052f));
				obj6.transform.localScale = new Vector3(1.38f, 0.68f, 0.01f);
				DisablePhysicalCollider(obj6);
				SetColor(obj6, new Color(0.006f, 0.035f, 0.035f, 0.55f));
				float num = SurfaceZ(0.13f);
				GameObject obj7 = GameObject.CreatePrimitive((PrimitiveType)3);
				((Object)obj7).name = "Repo Contracts Votes Panel";
				obj7.transform.SetParent(boardRoot.transform, false);
				obj7.transform.localPosition = new Vector3(0.54f, -0.044f, SurfaceZ(0.122f));
				obj7.transform.localScale = new Vector3(0.34f, 0.646f, 0.012f);
				DisablePhysicalCollider(obj7);
				SetColor(obj7, new Color(0.66f, 0.7f, 0.54f, 1f));
				CreatePanelBorder("Votes Border Top", new Vector3(0.54f, 0.285f, SurfaceZ(0.136f)), new Vector3(0.36f, 0.008f, 0.01f));
				CreatePanelBorder("Votes Border Bottom", new Vector3(0.54f, -0.373f, SurfaceZ(0.136f)), new Vector3(0.36f, 0.008f, 0.01f));
				CreatePanelBorder("Votes Border Left", new Vector3(0.36f, -0.044f, SurfaceZ(0.136f)), new Vector3(0.008f, 0.646f, 0.01f));
				CreatePanelBorder("Votes Border Right", new Vector3(0.72f, -0.044f, SurfaceZ(0.136f)), new Vector3(0.008f, 0.646f, 0.01f));
				headerText = CreateText("Header", new Vector3(-0.68f, 0.365f, num), 0.009f, new Color(0.72f, 0.94f, 0.86f, 1f));
				selectedText = CreateText("Selected", new Vector3(0.39f, 0.255f, num), 0.0058f, new Color(0.025f, 0.035f, 0.025f, 1f));
				promptText = CreateText("Prompt", new Vector3(-0.68f, -0.36f, num), 0.0048f, new Color(0.72f, 0.94f, 0.72f, 1f));
				for (int i = 0; i < 5; i++)
				{
					GameObject obj8 = GameObject.CreatePrimitive((PrimitiveType)3);
					((Object)obj8).name = $"Repo Contracts Card {i + 1}";
					obj8.transform.SetParent(boardRoot.transform, false);
					obj8.transform.localPosition = new Vector3(-0.18f, 0.22f - (float)i * 0.132f, SurfaceZ(0.108f));
					obj8.transform.localScale = new Vector3(1.14f, 0.118f, 0.035f);
					MakeTriggerOnly(obj8);
					ContractBoardCard contractBoardCard = obj8.AddComponent<ContractBoardCard>();
					contractBoardCard.Index = i;
					contractBoardCard.Text = CreateText($"CardText{i}", new Vector3(-0.69f, 0.253f - (float)i * 0.132f, num), 0.0049f, new Color(0.045f, 0.055f, 0.04f, 1f));
					cards.Add(contractBoardCard);
					GameObject obj9 = GameObject.CreatePrimitive((PrimitiveType)3);
					((Object)obj9).name = $"Repo Contracts Row Separator {i + 1}";
					obj9.transform.SetParent(boardRoot.transform, false);
					obj9.transform.localPosition = new Vector3(-0.18f, 0.154f - (float)i * 0.132f, SurfaceZ(0.136f));
					obj9.transform.localScale = new Vector3(1.12f, 0.006f, 0.01f);
					DisablePhysicalCollider(obj9);
					SetColor(obj9, new Color(0.055f, 0.06f, 0.05f, 1f));
				}
			}
		}

		private void CreatePanelBorder(string name, Vector3 localPosition, Vector3 localScale)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)obj).name = "Repo Contracts " + name;
			obj.transform.SetParent(boardRoot.transform, false);
			obj.transform.localPosition = localPosition;
			obj.transform.localScale = localScale;
			DisablePhysicalCollider(obj);
			SetColor(obj, new Color(0.055f, 0.065f, 0.052f, 1f));
		}

		private void UpdatePlacementMode()
		{
			if (Input.GetKeyDown((KeyCode)290))
			{
				placementMode = !placementMode;
				Plugin.Log.LogInfo((object)(placementMode ? "[RepoContracts] Placement ON: Numpad 4/6 side, 8/2 forward, 7/9 up, 1/3 yaw, +/- scale, F9 save/exit." : "[RepoContracts] Placement OFF."));
				RebuildBoardText();
				SavePlacement();
			}
			if (placementMode)
			{
				bool flag = false;
				float num = Mathf.Max(0.01f, Plugin.BoardNudgeStep.Value);
				float num2 = Mathf.Max(0.5f, Plugin.BoardRotateStep.Value);
				if (Input.GetKeyDown((KeyCode)260))
				{
					ConfigEntry<float> boardSideOffset = Plugin.BoardSideOffset;
					boardSideOffset.Value -= num;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)262))
				{
					ConfigEntry<float> boardSideOffset2 = Plugin.BoardSideOffset;
					boardSideOffset2.Value += num;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)258))
				{
					ConfigEntry<float> boardForwardOffset = Plugin.BoardForwardOffset;
					boardForwardOffset.Value -= num;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)264))
				{
					ConfigEntry<float> boardForwardOffset2 = Plugin.BoardForwardOffset;
					boardForwardOffset2.Value += num;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)263))
				{
					ConfigEntry<float> boardUpOffset = Plugin.BoardUpOffset;
					boardUpOffset.Value += num;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)265))
				{
					ConfigEntry<float> boardUpOffset2 = Plugin.BoardUpOffset;
					boardUpOffset2.Value -= num;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)257))
				{
					ConfigEntry<float> boardYawOffset = Plugin.BoardYawOffset;
					boardYawOffset.Value -= num2;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)259))
				{
					ConfigEntry<float> boardYawOffset2 = Plugin.BoardYawOffset;
					boardYawOffset2.Value += num2;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)270) || Input.GetKeyDown((KeyCode)61))
				{
					ConfigEntry<float> boardScale = Plugin.BoardScale;
					boardScale.Value += 0.04f;
					flag = true;
				}
				if (Input.GetKeyDown((KeyCode)269) || Input.GetKeyDown((KeyCode)45))
				{
					Plugin.BoardScale.Value = Mathf.Max(0.25f, Plugin.BoardScale.Value - 0.04f);
					flag = true;
				}
				if (flag)
				{
					RecreateBoard();
					SavePlacement();
				}
			}
		}

		private void ApplyPlacement()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)boardRoot == (Object)null))
			{
				GameObject val = FindTruckAnchor();
				Vector3 position = (((Object)(object)val != (Object)null) ? (val.transform.position + val.transform.right * Plugin.BoardSideOffset.Value + val.transform.forward * Plugin.BoardForwardOffset.Value + Vector3.up * Plugin.BoardUpOffset.Value) : (Vector3.up * Plugin.BoardUpOffset.Value));
				Quaternion rotation = (((Object)(object)val != (Object)null) ? Quaternion.Euler(0f, val.transform.eulerAngles.y + Plugin.BoardYawOffset.Value, 0f) : Quaternion.Euler(0f, Plugin.BoardYawOffset.Value, 0f));
				boardRoot.transform.position = position;
				boardRoot.transform.rotation = rotation;
				boardRoot.transform.localScale = Vector3.one * Plugin.BoardScale.Value;
			}
		}

		private void RecreateBoard()
		{
			if ((Object)(object)boardRoot != (Object)null)
			{
				Object.Destroy((Object)(object)boardRoot);
				boardRoot = null;
			}
			cards.Clear();
			headerText = null;
			selectedText = null;
			promptText = null;
			highlightedIndex = -1;
			EnsureBoard();
			RebuildBoardText();
		}

		private static void SavePlacement()
		{
			Plugin.SaveSettings();
			Plugin.Log.LogInfo((object)("[RepoContracts] Board placement saved: " + $"SideOffset={Plugin.BoardSideOffset.Value:F2}, " + $"ForwardOffset={Plugin.BoardForwardOffset.Value:F2}, " + $"UpOffset={Plugin.BoardUpOffset.Value:F2}, " + $"YawOffset={Plugin.BoardYawOffset.Value:F1}, " + $"Scale={Plugin.BoardScale.Value:F2}"));
		}

		private void UpdateHighlight()
		{
			int num = -1;
			Transform val = (((Object)(object)PlayerController.instance != (Object)null && (Object)(object)PlayerController.instance.cameraGameObject != (Object)null) ? PlayerController.instance.cameraGameObject.transform : (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : null));
			if ((Object)(object)val != (Object)null && TryGetBoardAimIndex(val, out var index))
			{
				num = index;
			}
			if (num != highlightedIndex)
			{
				highlightedIndex = num;
				RebuildBoardText();
			}
		}

		private bool TryGetBoardAimIndex(Transform aim, out int index)
		{
			//IL_0028: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			index = -1;
			if ((Object)(object)boardRoot == (Object)null || queue == null)
			{
				return false;
			}
			Plane val = default(Plane);
			((Plane)(ref val))..ctor(boardRoot.transform.forward, boardRoot.transform.position);
			Ray val2 = default(Ray);
			((Ray)(ref val2))..ctor(aim.position, aim.forward);
			float num = default(float);
			if (!((Plane)(ref val)).Raycast(val2, ref num) || num < 0f || num > 7f)
			{
				return false;
			}
			Vector3 val3 = boardRoot.transform.InverseTransformPoint(((Ray)(ref val2)).GetPoint(num));
			if (val3.x < -0.78f || val3.x > 0.33f)
			{
				return false;
			}
			int num2 = Mathf.Min(queue.OfferedContracts.Count, cards.Count);
			for (int i = 0; i < num2; i++)
			{
				float num3 = 0.22f - (float)i * 0.132f;
				if (Mathf.Abs(val3.y - num3) <= 0.072f)
				{
					index = i;
					return true;
				}
			}
			return false;
		}

		private void UpdateControllerHighlight()
		{
			if (queue != null && queue.OfferedContracts.Count != 0 && !(Time.time < controllerMoveCooldownUntil))
			{
				int num = 0;
				float axisRaw = Input.GetAxisRaw("Vertical");
				if (axisRaw > 0.55f || Input.GetKeyDown((KeyCode)343))
				{
					num = -1;
				}
				else if (axisRaw < -0.55f || Input.GetKeyDown((KeyCode)344))
				{
					num = 1;
				}
				if (num != 0)
				{
					int count = queue.OfferedContracts.Count;
					highlightedIndex = ((highlightedIndex >= 0) ? ((highlightedIndex + num + count) % count) : 0);
					controllerMoveCooldownUntil = Time.time + 0.22f;
					RebuildBoardText();
				}
			}
		}

		private static bool ControllerRankPressed(int rank)
		{
			return rank switch
			{
				1 => Input.GetKeyDown((KeyCode)330) || SafeInputDown((InputKey)26), 
				2 => Input.GetKeyDown((KeyCode)331) || SafeInputDown((InputKey)27), 
				3 => Input.GetKeyDown((KeyCode)332) || SafeInputDown((InputKey)28), 
				_ => false, 
			};
		}

		private static bool BoardClickPressed()
		{
			if (!Input.GetMouseButtonDown(0) && !Input.GetKeyDown((KeyCode)101) && !Input.GetKeyDown((KeyCode)13) && !Input.GetKeyDown((KeyCode)330))
			{
				return SafeInputDown((InputKey)2);
			}
			return true;
		}

		private static bool SafeInputDown(InputKey key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return SemiFunc.InputDown(key);
			}
			catch
			{
				return false;
			}
		}

		private void RebuildBoardText()
		{
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if (queue == null || (Object)(object)boardRoot == (Object)null)
			{
				return;
			}
			if ((Object)(object)headerText != (Object)null)
			{
				headerText.text = (queue.IsLocked ? "QUEUE LOCKED" : "CONTRACT BOARD");
			}
			for (int i = 0; i < cards.Count; i++)
			{
				ContractBoardCard contractBoardCard = cards[i];
				if ((Object)(object)contractBoardCard == (Object)null || (Object)(object)((Component)contractBoardCard).gameObject == (Object)null)
				{
					continue;
				}
				if (i >= queue.OfferedContracts.Count)
				{
					((Component)contractBoardCard).gameObject.SetActive(false);
					continue;
				}
				((Component)contractBoardCard).gameObject.SetActive(true);
				ContractInstance contractInstance = queue.OfferedContracts[i];
				if (contractInstance == null || contractInstance.Definition == null)
				{
					if ((Object)(object)contractBoardCard.Text != (Object)null)
					{
						contractBoardCard.Text.text = $"{i + 1}. Contract unavailable";
					}
					SetColor(((Component)contractBoardCard).gameObject, new Color(0.12f, 0.12f, 0.12f, 1f));
					continue;
				}
				ContractDefinition definition = contractInstance.Definition;
				ApplyCardVisual(((Component)contractBoardCard).gameObject, definition.Rarity, i == highlightedIndex);
				if ((Object)(object)contractBoardCard.Text != (Object)null)
				{
					contractBoardCard.Text.text = BuildCardText(i, contractInstance);
				}
			}
			if ((Object)(object)selectedText != (Object)null)
			{
				selectedText.text = BuildVoteSummary();
			}
			if ((Object)(object)promptText != (Object)null)
			{
				promptText.text = (placementMode ? "PLACE: numpad move/yaw, +/- scale, F9 save" : (queue.IsLocked ? "Press C for active contracts." : "Aim + click/interact vote. 1/2/3 rank."));
			}
		}

		private string BuildVoteSummary()
		{
			if (queue == null)
			{
				return string.Empty;
			}
			string localPlayerId = GetLocalPlayerId();
			Plugin.Manager.PlayerVotes.TryGetValue(localPlayerId, out string[] value);
			string text = "Priority\n";
			for (int i = 0; i < Plugin.SelectedContractsPerLevel.Value; i++)
			{
				string definitionId = ((value != null && i < value.Length) ? value[i] : string.Empty);
				ContractInstance contractInstance = ((!string.IsNullOrWhiteSpace(definitionId)) ? queue.OfferedContracts.FirstOrDefault((ContractInstance c) => c.DefinitionId == definitionId) : null);
				text += string.Format("{0}. {1}\n", i + 1, (contractInstance?.Definition != null) ? VoteName(contractInstance.Definition.DisplayName) : "-");
			}
			int num = ((!SemiFunc.IsMultiplayer() || PhotonNetwork.PlayerList == null) ? 1 : PhotonNetwork.PlayerList.Length);
			return text + $"{Plugin.Manager.PlayerVotes.Count}/{num} ready";
		}

		private TextMesh CreateText(string name, Vector3 localPosition, float size, Color color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Repo Contracts " + name);
			val.transform.SetParent(boardRoot.transform, false);
			val.transform.localPosition = localPosition;
			val.transform.localRotation = TextRotation();
			TextMesh obj = val.AddComponent<TextMesh>();
			obj.anchor = (TextAnchor)0;
			obj.alignment = (TextAlignment)0;
			obj.characterSize = size;
			obj.fontSize = 64;
			obj.text = string.Empty;
			ContractAssets.ApplyTextMaterial(obj, color);
			return obj;
		}

		private static string BuildCardText(int index, ContractInstance contract)
		{
			return $"{index + 1} {contract.Definition.DisplayName}  {DifficultyLabel(contract.Definition.Rarity)}  {RewardLabel(contract.RewardRoll)}\n" + BoardRuleLabel(contract);
		}

		private static string VoteName(string displayName)
		{
			return displayName switch
			{
				"High Value Quota" => "High Value", 
				"No Truck Healing" => "No Healing", 
				"No Damage Taken" => "No Damage", 
				"Melee Kills Only" => "Melee", 
				"Pressure Plates" => "Plates", 
				"Fragile Delivery" => "Fragile", 
				"Cursed Delivery" => "Cursed", 
				_ => Shorten(displayName, 10), 
			};
		}

		private static string Shorten(string value, int maxLength)
		{
			if (string.IsNullOrWhiteSpace(value))
			{
				return string.Empty;
			}
			if (value.Length <= maxLength)
			{
				return value;
			}
			return value.Substring(0, Mathf.Max(1, maxLength - 1)) + ".";
		}

		private static string DifficultyLabel(ContractRarity rarity)
		{
			return rarity switch
			{
				ContractRarity.Common => "EASY", 
				ContractRarity.Uncommon => "MED", 
				ContractRarity.Rare => "HARD", 
				ContractRarity.Epic => "EPIC", 
				ContractRarity.Legendary => "LEG", 
				_ => rarity.ToString(), 
			};
		}

		private static string ProgressLabel(ContractInstance contract)
		{
			if (contract.Definition.ProgressHook != ContractHook.ExtractionValueChanged)
			{
				return $"Goal {contract.RequiredProgress}";
			}
			return $"${contract.RequiredProgress:N0}";
		}

		private static string BoardRuleLabel(ContractInstance contract)
		{
			switch (contract.Definition.Id)
			{
			case "clean_sweep":
			case "debt_collector":
				return "Extract " + ProgressLabel(contract);
			case "monster_hunter":
				return $"Kill {contract.RequiredProgress}";
			case "melee_clause":
				return $"Melee kills {contract.RequiredProgress}";
			case "glass_cargo":
				return "Extract fragile item";
			case "cursed_loot":
				return "Extract cursed item; no player damage";
			case "no_deaths":
			case "no_one_left_bent":
				return "Extract; no deaths";
			case "no_damage":
				return "Extract; no damage";
			case "limited_healing":
				return "Extract; no healing";
			case "no_loud_tools":
				return "Extract; no guns";
			case "pressure_plate_extraction":
				return "Secure plates";
			default:
				return Shorten(contract.Definition.ShortDescription, 22);
			}
		}

		private static string RewardLabel(RewardRoll reward)
		{
			switch (reward.Kind)
			{
			case RewardKind.CashBonus:
			case RewardKind.MoneyBag:
				return $"+${reward.Amount:N0}";
			case RewardKind.ShopItem:
				return "Shop item";
			case RewardKind.Weapon:
				return "Weapon";
			case RewardKind.UpgradeDiscount:
				return "Upgrade";
			case RewardKind.SurpriseBox:
				return "Present";
			case RewardKind.CosmeticToken:
				return "Cosmetic";
			default:
				return Shorten(reward.PreviewText, 18);
			}
		}

		private static Color GetCardColor(ContractRarity rarity, bool highlighted)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			Color val = (Color)(rarity switch
			{
				ContractRarity.Common => new Color(0.46f, 0.58f, 0.42f, 1f), 
				ContractRarity.Uncommon => new Color(0.58f, 0.54f, 0.36f, 1f), 
				ContractRarity.Rare => new Color(0.62f, 0.43f, 0.34f, 1f), 
				ContractRarity.Epic => new Color(0.5f, 0.38f, 0.58f, 1f), 
				ContractRarity.Legendary => new Color(0.64f, 0.49f, 0.3f, 1f), 
				_ => new Color(0.42f, 0.48f, 0.46f, 1f), 
			});
			if (!highlighted)
			{
				return val;
			}
			return Color.Lerp(val, Color.white, 0.18f);
		}

		private static void ApplyCardVisual(GameObject card, ContractRarity rarity, bool highlighted)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			SetColor(card, GetCardColor(rarity, highlighted));
		}

		private static GameObject? FindTruckAnchor()
		{
			if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null)
			{
				return ((Component)TruckSafetySpawnPoint.instance).gameObject;
			}
			if ((Object)(object)TruckHealer.instance != (Object)null)
			{
				return ((Component)TruckHealer.instance).gameObject;
			}
			if ((Object)(object)TruckScreenText.instance != (Object)null)
			{
				return ((Component)TruckScreenText.instance).gameObject;
			}
			return GameObject.Find("cctv") ?? GameObject.Find("cctv (1)");
		}

		private float SurfaceZ(float depth)
		{
			return Mathf.Sign(readableSideSign) * Mathf.Abs(depth);
		}

		private Quaternion TextRotation()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (!(readableSideSign < 0f))
			{
				return Quaternion.Euler(0f, 180f, 0f);
			}
			return Quaternion.identity;
		}

		private static float ResolveReadableSideSign(Transform boardTransform)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			Transform val = (((Object)(object)PlayerController.instance != (Object)null && (Object)(object)PlayerController.instance.cameraGameObject != (Object)null) ? PlayerController.instance.cameraGameObject.transform : (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : (((Object)(object)PlayerAvatar.instance != (Object)null) ? ((Component)PlayerAvatar.instance).transform : null)));
			if ((Object)(object)val == (Object)null)
			{
				return -1f;
			}
			if (!(boardTransform.InverseTransformPoint(val.position).z >= 0f))
			{
				return -1f;
			}
			return 1f;
		}

		private static string GetLocalPlayerId()
		{
			if (!SemiFunc.IsMultiplayer() || PhotonNetwork.LocalPlayer == null)
			{
				return "singleplayer";
			}
			return $"player-{PhotonNetwork.LocalPlayer.ActorNumber}";
		}

		private static void SetColor(GameObject target, Color color)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Renderer component = target.GetComponent<Renderer>();
			if ((Object)(object)component != (Object)null)
			{
				component.material.color = color;
			}
		}

		private static void DisablePhysicalCollider(GameObject target)
		{
			Collider component = target.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
		}

		private static void MakeTriggerOnly(GameObject target)
		{
			Collider component = target.GetComponent<Collider>();
			if ((Object)(object)component != (Object)null)
			{
				component.isTrigger = true;
			}
		}

		private void LogBoard(string action)
		{
			if (Plugin.EnableDebugLogging.Value && queue != null)
			{
				Plugin.Log.LogInfo((object)$"[RepoContracts] Board UI {action}: {queue.OfferedContracts.Count} offers, {queue.SelectedContracts.Count} selected.");
			}
		}
	}
	internal sealed class ContractBoardCard : MonoBehaviour
	{
		internal int Index { get; set; }

		internal TextMesh? Text { get; set; }
	}
	internal sealed class ContractHud : MonoBehaviour
	{
		private ContractQueue? queue;

		private string notification = string.Empty;

		private float notificationUntil;

		private bool visible;

		private bool overlayOpen;

		private GameObject? trackedTarget;

		private string trackedTargetLabel = string.Empty;

		internal void ShowActive(ContractQueue contractQueue)
		{
			queue = contractQueue;
			visible = true;
			LogHud();
		}

		internal void Hide()
		{
			visible = false;
			overlayOpen = false;
			notification = string.Empty;
			trackedTarget = null;
			trackedTargetLabel = string.Empty;
		}

		internal void SetTrackedTarget(GameObject? target, string label)
		{
			trackedTarget = target;
			trackedTargetLabel = label;
		}

		internal void NotifyComplete(ContractInstance contract)
		{
			ShowNotification("Contract Complete: " + contract.Definition.DisplayName);
		}

		internal void NotifyFailed(ContractInstance contract)
		{
			ShowNotification("Contract Failed: " + contract.Definition.DisplayName);
		}

		internal void NotifyNext(ContractInstance contract)
		{
			ShowNotification("Next Contract Active: " + contract.Definition.DisplayName);
		}

		internal void NotifyDiscipline(string message)
		{
			ShowNotification(message);
		}

		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(Plugin.ContractHudKey.Value))
			{
				overlayOpen = !overlayOpen;
			}
		}

		private void OnGUI()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			if (!visible || !overlayOpen || queue == null)
			{
				DrawTrackedTarget();
				DrawNotification();
				return;
			}
			GUIStyle val = new GUIStyle(GUI.skin.label)
			{
				fontSize = 16,
				fontStyle = (FontStyle)1,
				wordWrap = false
			};
			GUIStyle val2 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 13,
				wordWrap = true
			};
			GUIStyle val3 = new GUIStyle(GUI.skin.label)
			{
				fontSize = 12,
				wordWrap = false
			};
			Rect rect = default(Rect);
			((Rect)(ref rect))..ctor((float)Screen.width - 620f, 30f, 590f, 430f);
			DrawTexturedBox(rect, "ui/hud/ui_active_contract_hud_panel.png", new Color(0.65f, 1f, 0.95f, 0.78f));
			GUILayout.BeginArea(new Rect(((Rect)(ref rect)).x + 20f, ((Rect)(ref rect)).y + 14f, ((Rect)(ref rect)).width - 40f, ((Rect)(ref rect)).height - 28f));
			GUILayout.Label("Repo Contracts", val, Array.Empty<GUILayoutOption>());
			if (Plugin.EnableDisciplineDebt.Value && queue.DisciplineDebt > 0)
			{
				GUILayout.Label(string.Format("Discipline Debt: {0} future reward{1} withheld", queue.DisciplineDebt, (queue.DisciplineDebt == 1) ? string.Empty : "s"), val2, Array.Empty<GUILayoutOption>());
			}
			ContractInstance activeContract = queue.ActiveContract;
			if (activeContract == null)
			{
				GUILayout.Label(queue.IsLocked ? "No active contract." : "Vote at the truck contract board.", val2, Array.Empty<GUILayoutOption>());
			}
			else
			{
				GUILayout.Label($"Active {queue.ActiveIndex + 1}/{queue.SelectedContracts.Count}: {activeContract.Definition.DisplayName}", val, Array.Empty<GUILayoutOption>());
				GUILayout.Label(Shorten(activeContract.Definition.ObjectiveText, 92), val2, Array.Empty<GUILayoutOption>());
				GUILayout.Label($"Progress: {activeContract.CurrentProgress}/{activeContract.RequiredProgress}", val2, Array.Empty<GUILayoutOption>());
				GUILayout.Label("Failure: " + Shorten(activeContract.Definition.FailureText, 82), val2, Array.Empty<GUILayoutOption>());
				GUILayout.Label("Reward: " + activeContract.RewardRoll.PreviewText, val2, Array.Empty<GUILayoutOption>());
				string id = activeContract.Definition.Id;
				if ((id == "glass_cargo" || id == "cursed_loot") ? true : false)
				{
					string value;
					string text = ((activeContract.SelectedTargets.TryGetValue("targetName", out value) && !string.IsNullOrWhiteSpace(value)) ? value : "Random marked valuable");
					GUILayout.Label("Target: " + text, val2, Array.Empty<GUILayoutOption>());
				}
			}
			GUILayout.Space(8f);
			for (int i = 0; i < queue.SelectedContracts.Count; i++)
			{
				ContractInstance contractInstance = queue.SelectedContracts[i];
				GUILayout.Label($"{i + 1}. {Shorten(contractInstance.Definition.DisplayName, 24)} - {contractInstance.Status}", val3, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndArea();
			DrawTrackedTarget();
			DrawNotification();
		}

		private void DrawNotification()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(notification) && !(Time.time >= notificationUntil))
			{
				Rect rect = default(Rect);
				((Rect)(ref rect))..ctor(((float)Screen.width - 520f) * 0.5f, 80f, 520f, 70f);
				string asset = (notification.Contains("Failed") ? "ui/banners/ui_banner_contract_failed.png" : (notification.Contains("Complete") ? "ui/banners/ui_banner_contract_complete.png" : (notification.Contains("Reward") ? "ui/banners/ui_banner_reward_earned.png" : "ui/warnings/ui_warning_timer_critical.png")));
				DrawTexturedBox(rect, asset, Color.white);
				GUILayout.BeginArea(new Rect(((Rect)(ref rect)).x + 28f, ((Rect)(ref rect)).y + 18f, ((Rect)(ref rect)).width - 56f, ((Rect)(ref rect)).height - 28f));
				GUILayout.Label(notification, Array.Empty<GUILayoutOption>());
				GUILayout.EndArea();
			}
		}

		private void DrawTrackedTarget()
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			ContractInstance contractInstance = queue?.ActiveContract;
			bool flag = !visible || (Object)(object)trackedTarget == (Object)null;
			if (!flag)
			{
				string text = contractInstance?.Definition.Id;
				bool flag2 = ((text == "glass_cargo" || text == "cursed_loot") ? true : false);
				flag = !flag2;
			}
			if (flag)
			{
				return;
			}
			Camera main = Camera.main;
			if (!((Object)(object)main == (Object)null))
			{
				Vector3 val = trackedTarget.transform.position + Vector3.up * 0.6f;
				Vector3 val2 = main.WorldToScreenPoint(val);
				bool num = val2.z < 0f;
				if (num)
				{
					val2.x = (float)Screen.width - val2.x;
					val2.y = (float)Screen.height - val2.y;
				}
				float num2 = 48f;
				bool flag3 = num || val2.x < num2 || val2.x > (float)Screen.width - num2 || val2.y < num2 || val2.y > (float)Screen.height - num2;
				float num3 = Mathf.Clamp(val2.x, num2, (float)Screen.width - num2);
				float num4 = Mathf.Clamp((float)Screen.height - val2.y, num2, (float)Screen.height - num2);
				float num5 = Vector3.Distance(((Component)main).transform.position, trackedTarget.transform.position);
				string arg = (string.IsNullOrWhiteSpace(trackedTargetLabel) ? contractInstance.Definition.DisplayName.ToUpperInvariant() : trackedTargetLabel);
				string arg2 = ((contractInstance.Definition.Id == "cursed_loot") ? "CURSED TARGET" : "MARKED TARGET");
				Color color = GUI.color;
				GUI.color = new Color(0.2f, 1f, 0.9f, 0.95f);
				GUI.Box(new Rect(((float)Screen.width - 320f) * 0.5f, 132f, 320f, 54f), $"{arg2}\n{arg} - {Mathf.RoundToInt(num5)}m");
				GUI.Box(new Rect(num3 - 105f, num4 - 26f, 210f, 52f), string.Format("{0}\n{1} - {2}m", flag3 ? "TARGET" : "MARKED", arg, Mathf.RoundToInt(num5)));
				GUI.color = color;
			}
		}

		private void ShowNotification(string message)
		{
			notification = message;
			notificationUntil = Time.time + 4f;
			if (Plugin.EnableDebugLogging.Value)
			{
				Plugin.Log.LogInfo((object)("[RepoContracts] HUD notification: " + message));
			}
		}

		private static void DrawTexturedBox(Rect rect, string asset, Color tint)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = ContractAssets.Texture(asset);
			Color color = GUI.color;
			if ((Object)(object)val != (Object)null)
			{
				GUI.color = tint;
				GUI.DrawTexture(rect, (Texture)(object)val, (ScaleMode)0, true);
			}
			else
			{
				GUI.Box(rect, GUIContent.none);
			}
			GUI.color = color;
		}

		private static string Shorten(string value, int maxLength)
		{
			if (string.IsNullOrWhiteSpace(value) || value.Length <= maxLength)
			{
				return value ?? string.Empty;
			}
			return value.Substring(0, Mathf.Max(1, maxLength - 1)) + ".";
		}

		private void LogHud()
		{
			if (Plugin.EnableDebugLogging.Value && visible && queue != null)
			{
				ContractInstance activeContract = queue.ActiveContract;
				if (activeContract == null)
				{
					Plugin.Log.LogInfo((object)"[RepoContracts] HUD ready: no active contract.");
				}
				else
				{
					Plugin.Log.LogInfo((object)($"[RepoContracts] HUD active {queue.ActiveIndex + 1}/{queue.SelectedContracts.Count}: " + $"{activeContract.Definition.DisplayName} {activeContract.CurrentProgress}/{activeContract.RequiredProgress}. " + "Reward: " + activeContract.RewardRoll.PreviewText));
				}
			}
		}
	}
}
namespace RepoContracts.Rewards
{
	internal sealed class RewardAdapter
	{
		private static readonly FieldInfo? DollarOverrideField = typeof(ValuableObject).GetField("dollarValueOverride", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		internal void Grant(RewardRoll reward, Vector3 position, RewardDeliveryContext context)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			if (!context.CashRewardsUsable && IsCashReward(reward))
			{
				GrantNonCashSubstitute(reward, position, context);
				return;
			}
			switch (reward.Kind)
			{
			case RewardKind.CashBonus:
			case RewardKind.MoneyBag:
				SpawnMoneyBag(position, Mathf.RoundToInt((float)reward.Amount * Plugin.RewardMultiplier.Value));
				break;
			case RewardKind.ShopItem:
				if (!TrySpawnShopItemReward(reward, position, IsGeneralShopReward, "shop item"))
				{
					GrantFallback(reward, position, context);
				}
				break;
			case RewardKind.Weapon:
				if (!TrySpawnShopItemReward(reward, position, IsWeaponReward, "weapon"))
				{
					GrantFallback(reward, position, context);
				}
				break;
			case RewardKind.UpgradeDiscount:
				if (!TrySpawnShopItemReward(reward, position, IsUpgradeReward, "upgrade"))
				{
					GrantFallback(reward, position, context);
				}
				break;
			case RewardKind.SurpriseBox:
				if (!TrySpawnSurpriseReward(reward, position))
				{
					GrantFallback(reward, position, context);
				}
				break;
			case RewardKind.CosmeticToken:
				if (!TryGrantCosmeticToken(reward))
				{
					GrantFallback(reward, position, context);
				}
				break;
			case RewardKind.SpawnableValuable:
			case RewardKind.ContractRerollToken:
				Plugin.Log.LogWarning((object)$"[RepoContracts] Reward adapter TODO: {reward.Kind} is modeled but not safely implemented yet.");
				GrantFallback(reward, position, context);
				break;
			default:
				Plugin.Log.LogWarning((object)$"[RepoContracts] Unknown reward kind {reward.Kind}.");
				break;
			}
		}

		private static bool IsCashReward(RewardRoll reward)
		{
			RewardKind kind = reward.Kind;
			if ((uint)kind <= 1u)
			{
				return true;
			}
			return false;
		}

		private static void GrantNonCashSubstitute(RewardRoll reward, Vector3 position, RewardDeliveryContext context)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Log.LogInfo((object)("[RepoContracts] Cash reward converted because cash is unsafe now (" + context.Reason + ")."));
			if (!TrySpawnShopItemReward(reward, position, IsGeneralShopReward, "shop item substitute") && !TryGrantCosmeticToken(reward))
			{
				Plugin.Log.LogWarning((object)"[RepoContracts] Non-cash substitute failed; spawning money bag as last-resort fallback.");
				SpawnFallbackMoneyBag(reward, position);
			}
		}

		private static void GrantFallback(RewardRoll reward, Vector3 position, RewardDeliveryContext context)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (context.CashRewardsUsable)
			{
				SpawnFallbackMoneyBag(reward, position);
				return;
			}
			if (TryGrantCosmeticToken(reward))
			{
				Plugin.Log.LogInfo((object)("[RepoContracts] Granted cosmetic token fallback because cash is unsafe now (" + context.Reason + ")."));
				return;
			}
			Plugin.Log.LogWarning((object)"[RepoContracts] Cash-unsafe fallback had no cosmetic path; spawning money bag as last resort.");
			SpawnFallbackMoneyBag(reward, position);
		}

		private static void SpawnMoneyBag(Vector3 position, int amount)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)AssetManager.instance == (Object)null || (Object)(object)AssetManager.instance.surplusValuableSmall == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[RepoContracts] Money bag reward unavailable: AssetManager.surplusValuableSmall was not found.");
				return;
			}
			GameObject surplusValuableSmall = AssetManager.instance.surplusValuableSmall;
			int num = Mathf.Max(1000, Mathf.RoundToInt(Mathf.Round((float)amount / 100f) * 100f));
			ValuableObject component = (SemiFunc.IsMultiplayer() ? PhotonNetwork.InstantiateRoomObject("Valuables/" + ((Object)surplusValuableSmall).name, position, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate<GameObject>(surplusValuableSmall, position, Quaternion.identity)).GetComponent<ValuableObject>();
			if ((Object)(object)component != (Object)null)
			{
				DollarOverrideField?.SetValue(component, num);
			}
			Plugin.Log.LogInfo((object)$"[RepoContracts] Spawned contract money bag reward ${num}.");
		}

		private static void SpawnFallbackMoneyBag(RewardRoll reward, Vector3 position)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			int num = ((reward.Amount > 0) ? reward.Amount : 2500);
			SpawnMoneyBag(position, Mathf.RoundToInt((float)num * Plugin.RewardMultiplier.Value));
		}

		private static bool TrySpawnShopItemReward(RewardRoll reward, Vector3 position, Func<Item, bool> predicate, string label)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null)
			{
				Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward unavailable: StatsManager item dictionary was not ready."));
				return false;
			}
			List<Item> eligibleItems = GetEligibleItems(predicate);
			if (eligibleItems.Count == 0)
			{
				Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward unavailable: no eligible shop items were found."));
				return false;
			}
			return TrySpawnItem(eligibleItems[Random.Range(0, eligibleItems.Count)], position, label);
		}

		private static bool TrySpawnSurpriseReward(RewardRoll reward, Vector3 position)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null)
			{
				Plugin.Log.LogWarning((object)"[RepoContracts] surprise reward unavailable: StatsManager item dictionary was not ready.");
				return false;
			}
			List<Item> eligibleItems = GetEligibleItems(IsPresentBoxReward);
			if (eligibleItems.Count > 0)
			{
				return TrySpawnItem(eligibleItems[Random.Range(0, eligibleItems.Count)], position, "surprise present");
			}
			List<Item> eligibleItems2 = GetEligibleItems(IsGeneralShopReward);
			if (eligibleItems2.Count == 0)
			{
				Plugin.Log.LogWarning((object)"[RepoContracts] surprise reward unavailable: no eligible bundle items were found.");
				return false;
			}
			int num;
			switch (reward.Rarity)
			{
			case ContractRarity.Epic:
			case ContractRarity.Legendary:
				num = 3;
				break;
			case ContractRarity.Rare:
				num = 2;
				break;
			default:
				num = 2;
				break;
			}
			int num2 = num;
			bool flag = false;
			Vector3 val = default(Vector3);
			for (int i = 0; i < num2; i++)
			{
				Item selected = eligibleItems2[Random.Range(0, eligibleItems2.Count)];
				((Vector3)(ref val))..ctor(((float)i - (float)(num2 - 1) * 0.5f) * 0.55f, 0f, Random.Range(-0.25f, 0.25f));
				flag |= TrySpawnItem(selected, position + val, "surprise bundle item");
			}
			return flag;
		}

		private static List<Item> GetEligibleItems(Func<Item, bool> predicate)
		{
			Func<Item, bool> predicate2 = predicate;
			return StatsManager.instance.itemDictionary.Values.Where((Item item) => (Object)(object)item != (Object)null && !item.disabled && item.physicalItem && item.prefab != null && item.prefab.IsValid() && predicate2(item)).ToList();
		}

		private static bool TrySpawnItem(Item selected, Vector3 position, string label)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Quaternion val = Quaternion.identity * selected.spawnRotationOffset;
			try
			{
				if ((Object)(object)(SemiFunc.IsMultiplayer() ? PhotonNetwork.InstantiateRoomObject(selected.prefab.ResourcePath, position, val, (byte)0, (object[])null) : Object.Instantiate<GameObject>(selected.prefab.Prefab, position, val)) == (Object)null)
				{
					Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward failed to spawn: " + selected.itemName + "."));
					return false;
				}
				Plugin.Log.LogInfo((object)("[RepoContracts] Spawned contract " + label + " reward: " + selected.itemName + "."));
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward failed to spawn " + selected.itemName + ": " + ex.Message));
				return false;
			}
		}

		private static bool TryGrantCosmeticToken(RewardRoll reward)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)MetaManager.instance == (Object)null)
			{
				Plugin.Log.LogWarning((object)"[RepoContracts] Cosmetic token reward unavailable: MetaManager was not ready.");
				return false;
			}
			try
			{
				MetaManager.instance.CosmeticTokenAdd(ToGameRarity(reward.Rarity));
				Plugin.Log.LogInfo((object)$"[RepoContracts] Granted contract cosmetic token reward: {reward.Rarity}.");
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[RepoContracts] Cosmetic token reward failed: " + ex.Message));
				return false;
			}
		}

		private static bool IsGeneralShopReward(Item item)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected I4, but got Unknown
			itemType itemType = item.itemType;
			switch ((int)itemType)
			{
			case 0:
			case 1:
			case 2:
			case 5:
			case 6:
			case 7:
			case 9:
			case 10:
			case 11:
			case 12:
			case 13:
			case 14:
			case 15:
				return true;
			default:
				return false;
			}
		}

		private static bool IsWeaponReward(Item item)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected I4, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Invalid comparison between Unknown and I4
			itemType itemType = item.itemType;
			switch (itemType - 6)
			{
			default:
				if ((int)itemType != 15)
				{
					break;
				}
				goto case 0;
			case 0:
			case 1:
			case 3:
			case 5:
				return true;
			case 2:
			case 4:
				break;
			}
			return false;
		}

		private static bool IsUpgradeReward(Item item)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			itemType itemType = item.itemType;
			if (itemType - 3 <= 1)
			{
				return true;
			}
			return false;
		}

		private static bool IsPresentBoxReward(Item item)
		{
			string[] obj = new string[5]
			{
				item.itemName,
				" ",
				((Object)item).name,
				" ",
				null
			};
			PrefabRef prefab = item.prefab;
			obj[4] = ((prefab != null) ? prefab.ResourcePath : null);
			string text = string.Concat(obj).ToLowerInvariant();
			if (!text.Contains("present") && !text.Contains("gift") && !text.Contains("surprise") && !text.Contains("package") && !text.Contains("crate"))
			{
				return text.Contains("box");
			}
			return true;
		}

		private static Rarity ToGameRarity(ContractRarity rarity)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			return (Rarity)(rarity switch
			{
				ContractRarity.Common => 0, 
				ContractRarity.Uncommon => 1, 
				ContractRarity.Rare => 2, 
				ContractRarity.Epic => 3, 
				ContractRarity.Legendary => 3, 
				_ => 0, 
			});
		}
	}
	internal readonly struct RewardDeliveryContext
	{
		internal bool CashRewardsUsable { get; }

		internal string Reason { get; }

		private RewardDeliveryContext(bool cashRewardsUsable, string reason)
		{
			CashRewardsUsable = cashRewardsUsable;
			Reason = reason;
		}

		internal static RewardDeliveryContext Normal()
		{
			return new RewardDeliveryContext(cashRewardsUsable: true, "normal");
		}

		internal static RewardDeliveryContext CashUnsafe(string reason)
		{
			return new RewardDeliveryContext(cashRewardsUsable: false, reason);
		}
	}
}
namespace RepoContracts.Networking
{
	internal sealed class ContractSyncAdapter
	{
		internal readonly struct ContractSnapshot
		{
			internal static readonly ContractSnapshot Empty = new ContractSnapshot(Array.Empty<ContractInstance>(), Array.Empty<ContractInstance>(), -1, isLocked: false, 0, string.Empty);

			internal IReadOnlyList<ContractInstance> Offers { get; }

			internal IReadOnlyList<ContractInstance> Selected { get; }

			internal int ActiveIndex { get; }

			internal bool IsLocked { get; }

			internal int DisciplineDebt { get; }

			internal string VoteSessionId { get; }

			internal ContractSnapshot(IReadOnlyList<ContractInstance> offers, IReadOnlyList<ContractInstance> selected, int activeIndex, bool isLocked, int disciplineDebt, string voteSessionId)
			{
				Offers = offers;
				Selected = selected;
				ActiveIndex = activeIndex;
				IsLocked = isLocked;
				DisciplineDebt = disciplineDebt;
				VoteSessionId = voteSessionId;
			}
		}

		private const string StateKey = "RepoContracts.State";

		private const string RevisionKey = "RepoContracts.Revision";

		private int revision;

		internal void SyncOffers(ContractQueue queue)
		{
			Publish("offers", queue);
		}

		internal void SyncSelection(ContractQueue queue)
		{
			Publish("selection", queue);
		}

		internal void SyncLockedQueue(ContractQueue queue)
		{
			Publish("locked queue", queue);
		}

		internal void SyncVotes(ContractQueue queue, IReadOnlyDictionary<string, string[]> votes)
		{
			Publish("votes", queue);
			if (Plugin.EnableDebugLogging.Value)
			{
				Plugin.Log.LogInfo((object)$"[RepoContracts] Synced votes players={votes.Count}, offers={queue.OfferedContracts.Count}.");
			}
		}

		internal void SyncProgress(ContractQueue queue)
		{
			Publish("progress", queue);
		}

		internal void SyncCleared()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			if (CanPublish())
			{
				revision++;
				Hashtable val = new Hashtable
				{
					[(object)"RepoContracts.State"] = string.Empty,
					[(object)"RepoContracts.Revision"] = revision
				};
				PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				if (Plugin.EnableDebugLogging.Value)
				{
					Plugin.Log.LogInfo((object)"[RepoContracts] Synced cleared contract state.");
				}
			}
		}

		internal bool TryReadSnapshot(ref int lastRevision, out string snapshot)
		{
			snapshot = string.Empty;
			if (SemiFunc.IsMultiplayer())
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null) != null)
				{
					if (!((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"RepoContracts.Revision", out object value) || !(value is int num))
					{
						return false;
					}
					if (num == lastRevision)
					{
						return false;
					}
					lastRevision = num;
					if (((Dictionary<object, object>)(object)((RoomInfo)PhotonNetwork.CurrentRoom).CustomProperties).TryGetValue((object)"RepoContracts.State", out object value2) && value2 is string text)
					{
						snapshot = text;
					}
					return true;
				}
			}
			return false;
		}

		internal string Serialize(ContractQueue queue)
		{
			string text = string.Join(",", queue.OfferedContracts.Select((ContractInstance c) => Escape(c.DefinitionId)));
			string text2 = string.Join("^", queue.SelectedContracts.Select(SerializeContract));
			return string.Join("|", "v1", queue.IsLocked ? "1" : "0", queue.ActiveIndex.ToString(), text, text2, queue.DisciplineDebt.ToString(), Escape(queue.VoteSessionId));
		}

		internal bool TryDeserialize(string snapshot, IReadOnlyList<ContractDefinition> catalog, out ContractSnapshot result)
		{
			result = ContractSnapshot.Empty;
			if (string.IsNullOrWhiteSpace(snapshot))
			{
				return true;
			}
			string[] array = snapshot.Split('|');
			if (array.Length < 5 || array[0] != "v1")
			{
				return false;
			}
			bool isLocked = array[1] == "1";
			int.TryParse(array[2], out var result2);
			List<ContractInstance> list = new List<ContractInstance>();
			foreach (string id in SplitList(array[3], ','))
			{
				ContractDefinition contractDefinition = catalog.FirstOrDefault((ContractDefinition c) => c.Id == Unescape(id));
				if (contractDefinition != null)
				{
					list.Add(new ContractInstance(contractDefinition, 0));
				}
			}
			List<ContractInstance> list2 = new List<ContractInstance>();
			foreach (string item in SplitList(array[4], '^'))
			{
				ContractInstance contractInstance = DeserializeContract(item, catalog);
				if (contractInstance != null)
				{
					list2.Add(contractInstance);
				}
			}
			int result3 = 0;
			if (array.Length > 5)
			{
				int.TryParse(array[5], out result3);
			}
			string voteSessionId = ((array.Length > 6) ? Unescape(array[6]) : string.Empty);
			result = new ContractSnapshot(list, list2, result2, isLocked, result3, voteSessionId);
			return true;
		}

		private void Publish(string label, ContractQueue queue)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			if (!CanPublish())
			{
				LogLocal(label, queue);
				return;
			}
			revision++;
			Hashtable val = new Hashtable
			{
				[(object)"RepoContracts.State"] = Serialize(queue),
				[(object)"RepoContracts.Revision"] = revision
			};
			PhotonNetwork.CurrentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			LogLocal(label, queue);
		}

		private static bool CanPublish()
		{
			if (SemiFunc.IsMultiplayer() && SemiFunc.IsMasterClientOrSingleplayer())
			{
				return PhotonNetwork.CurrentRoom != null;
			}
			return false;
		}

		private static void LogLocal(string stateName, ContractQueue queue)
		{
			if (Plugin.EnableDebugLogging.Value)
			{
				string text = (CanPublish() ? "Synced" : "Local");
				Plugin.Log.LogInfo((object)string.Format("[RepoContracts] {0} {1}: offered={2}, selected={3}, active={4}.", text, stateName, queue.OfferedContracts.Count, queue.SelectedContracts.Count, queue.ActiveContract?.Definition.DisplayName ?? "none"));
			}
		}

		private static string SerializeContract(ContractInstance contract)
		{
			string value = string.Join(";", contract.SelectedTargets.Select<KeyValuePair<string, string>, string>((KeyValuePair<string, string> kv) => Escape(kv.Key) + "=" + Escape(kv.Value)));
			return string.Join("~", Escape(contract.DefinitionId), ((int)contract.Status).ToString(), contract.CurrentProgress.ToString(), contract.RequiredProgress.ToString(), Escape(contract.FailureReason), Escape(value), contract.RewardRoll.Amount.ToString(), Escape(contract.RewardRoll.PreviewText), ((int)contract.RewardRoll.Kind).ToString(), Escape(contract.RewardRoll.RewardId));
		}

		private static ContractInstance? DeserializeContract(string data, IReadOnlyList<ContractDefinition> catalog)
		{
			string[] array = data.Split('~');
			if (array.Length < 4)
			{
				return null;
			}
			string definitionId = Unescape(array[0]);
			ContractDefinition contractDefinition = catalog.FirstOrDefault((ContractDefinition c) => c.Id == definitionId);
			if (contractDefinition == null)
			{
				return null;
			}
			ContractInstance contractInstance = new ContractInstance(contractDefinition, 0);
			if (int.TryParse(array[1], out var result))
			{
				contractInstance.SetStatus((ContractStatus)result);
			}
			int.TryParse(array[2], out var result2);
			int.TryParse(array[3], out var result3);
			contractInstance.SetProgress(result2, result3);
			if (array.Length > 4)
			{
				contractInstance.SetFailureReason(Unescape(array[4]));
			}
			if (array.Length > 5)
			{
				foreach (string item in SplitList(Unescape(array[5]), ';'))
				{
					int num = item.IndexOf('=');
					if (num > 0)
					{
						Dictionary<string, string> selectedTargets = contractInstance.SelectedTargets;
						string key = Unescape(item.Substring(0, num));
						string text = item;
						int num2 = num + 1;
						selectedTargets[key] = Unescape(text.Substring(num2, text.Length - num2));
					}
				}
			}
			if (array.Length > 6 && int.TryParse(array[6], out var result4) && result4 > 0)
			{
				string previewText = ((array.Length > 7) ? Unescape(array[7]) : contractInstance.RewardRoll.PreviewText);
				RewardKind kind = contractInstance.RewardRoll.Kind;
				if (array.Length > 8 && int.TryParse(array[8], out var result5) && Enum.IsDefined(typeof(RewardKind), result5))
				{
					kind = (RewardKind)result5;
				}
				string rewardId = ((array.Length > 9) ? Unescape(array[9]) : contractInstance.RewardRoll.RewardId);
				contractInstance.SetRewardRoll(new RewardRoll(kind, contractInstance.RewardRoll.Rarity, result4, rewardId, previewText));
			}
			return contractInstance;
		}

		private static IEnumerable<string> SplitList(string value, char separator)
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				return from v in value.Split(separator)
					where !string.IsNullOrWhiteSpace(v)
					select v;
			}
			return