using System; 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.3.0")] [assembly: AssemblyInformationalVersion("0.5.3")] [assembly: AssemblyProduct("RepoContracts")] [assembly: AssemblyTitle("RepoContracts")] [assembly: AssemblyVersion("0.5.3.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.3")] [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.3"; internal static ConfigEntry EnableRepoContracts; internal static ConfigEntry OfferedContractsPerLevel; internal static ConfigEntry SelectedContractsPerLevel; internal static ConfigEntry OneActiveContractAtATime; internal static ConfigEntry AdvanceQueueOnFailure; internal static ConfigEntry AdvanceQueueOnCompletion; internal static ConfigEntry EnableMilestoneContracts; internal static ConfigEntry MilestoneContractEveryXLevels; internal static ConfigEntry AllowNormalContractsDuringMilestone; internal static ConfigEntry RewardMultiplier; internal static ConfigEntry FailureStrictness; internal static ConfigEntry DisciplineMode; internal static ConfigEntry EnableDisciplineDebt; internal static ConfigEntry EnableDebugLogging; internal static ConfigEntry AutoLockFirstOffersForDebug; internal static ConfigEntry ContractHudKey; internal static ConfigEntry ContractBoardLockKey; internal static ConfigEntry BoardSideOffset; internal static ConfigEntry BoardForwardOffset; internal static ConfigEntry BoardUpOffset; internal static ConfigEntry BoardYawOffset; internal static ConfigEntry BoardScale; internal static ConfigEntry BoardNudgeStep; internal static ConfigEntry BoardRotateStep; internal static ConfigEntry EnableExperimentalContracts; internal static ConfigEntry EnableAssetHeavyContracts; internal static ConfigEntry 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("General", "EnableRepoContracts", true, "Enables or disables Repo Contracts."); OfferedContractsPerLevel = ((BaseUnityPlugin)this).Config.Bind("Contracts", "OfferedContractsPerLevel", 5, "Contracts shown on the truck board."); SelectedContractsPerLevel = ((BaseUnityPlugin)this).Config.Bind("Contracts", "SelectedContractsPerLevel", 3, "Contracts selected into the priority queue."); OneActiveContractAtATime = ((BaseUnityPlugin)this).Config.Bind("Contracts", "OneActiveContractAtATime", true, "Only one selected contract is active at a time."); AdvanceQueueOnFailure = ((BaseUnityPlugin)this).Config.Bind("Contracts", "AdvanceQueueOnFailure", true, "Automatically activate the next contract when the active one fails."); AdvanceQueueOnCompletion = ((BaseUnityPlugin)this).Config.Bind("Contracts", "AdvanceQueueOnCompletion", true, "Automatically activate the next contract when the active one completes."); EnableMilestoneContracts = ((BaseUnityPlugin)this).Config.Bind("Milestones", "EnableMilestoneContracts", true, "Enables special all-encompassing milestone contracts."); MilestoneContractEveryXLevels = ((BaseUnityPlugin)this).Config.Bind("Milestones", "MilestoneContractEveryXLevels", 3, "Offer a milestone contract every X completed levels."); AllowNormalContractsDuringMilestone = ((BaseUnityPlugin)this).Config.Bind("Milestones", "AllowNormalContractsDuringMilestone", false, "Allows the normal queue alongside milestone contracts."); RewardMultiplier = ((BaseUnityPlugin)this).Config.Bind("Rewards", "RewardMultiplier", 1f, "Scales additive contract rewards."); FailureStrictness = ((BaseUnityPlugin)this).Config.Bind("Contracts", "FailureStrictness", RepoContracts.Contracts.FailureStrictness.Normal, "How punishing failure variants should be."); DisciplineMode = ((BaseUnityPlugin)this).Config.Bind("Discipline", "DisciplineMode", RepoContracts.Contracts.DisciplineMode.Standard, "Future penalty severity for failed contracts when discipline debt is enabled."); EnableDisciplineDebt = ((BaseUnityPlugin)this).Config.Bind("Discipline", "EnableDisciplineDebt", false, "Future/off-by-default setting. When enabled, failed contracts add debt that withholds future contract rewards."); EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind("Debug", "EnableDebugLogging", true, "Logs adapter activity and TODO hook gaps."); AutoLockFirstOffersForDebug = ((BaseUnityPlugin)this).Config.Bind("Debug", "AutoLockFirstOffersForDebug", false, "Debug helper that auto-selects and locks the first offered contracts after generation."); ContractHudKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "ContractHudKey", (KeyCode)99, "Keyboard key that toggles the active contract HUD overlay."); ContractBoardLockKey = ((BaseUnityPlugin)this).Config.Bind("Controls", "ContractBoardLockKey", (KeyCode)108, "Host keyboard key that locks/tallies board votes while pointing at the board."); BoardSideOffset = ((BaseUnityPlugin)this).Config.Bind("Board", "SideOffset", -6.149996f, "Meters to the truck side from the anchor for the contract TV."); BoardForwardOffset = ((BaseUnityPlugin)this).Config.Bind("Board", "ForwardOffset", -0.04999985f, "Meters forward/back from the anchor for the contract TV."); BoardUpOffset = ((BaseUnityPlugin)this).Config.Bind("Board", "UpOffset", 0.3499997f, "Meters upward from the anchor for the contract TV."); BoardYawOffset = ((BaseUnityPlugin)this).Config.Bind("Board", "YawOffset", 270f, "Yaw offset from the truck anchor for the contract TV."); BoardScale = ((BaseUnityPlugin)this).Config.Bind("Board", "Scale", 2.099999f, "Overall contract TV scale."); BoardNudgeStep = ((BaseUnityPlugin)this).Config.Bind("Board", "NudgeStep", 0.1f, "Meters moved per calibration key press."); BoardRotateStep = ((BaseUnityPlugin)this).Config.Bind("Board", "RotateStep", 5f, "Degrees rotated per calibration key press."); EnableExperimentalContracts = ((BaseUnityPlugin)this).Config.Bind("Contracts", "EnableExperimentalContracts", false, "Allows contracts that rely on unverified or risky hooks."); EnableAssetHeavyContracts = ((BaseUnityPlugin)this).Config.Bind("Contracts", "EnableAssetHeavyContracts", false, "Allows contracts needing new world models, VFX, SFX, or heavier UI assets."); EnableCompatibilityMode = ((BaseUnityPlugin)this).Config.Bind("Compatibility", "EnableCompatibilityMode", true, "Skips contracts needing risky object replacement, spawn replacement, control manipulation, or enemy roster manipulation."); ContractBoardUi boardUi = ((Component)this).gameObject.AddComponent(); ContractHud hud = ((Component)this).gameObject.AddComponent(); 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.3 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(); 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 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() == (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(); 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(); 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(); 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(); 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 Textures = new Dictionary(); private static readonly Dictionary Materials = new Dictionary(); 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(); Material val = Material(relativePath, tint, transparent); if ((Object)(object)component != (Object)null && (Object)(object)val != (Object)null) { component.material = val; } } 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 cards = new List(); private ContractQueue? queue; private GameObject? boardRoot; private TextMesh? headerText; private TextMesh? selectedText; private TextMesh? promptText; private int highlightedIndex = -1; private bool placementMode; 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_00dc: 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 || queue.IsLocked) { return; } UpdateHighlight(); UpdateControllerHighlight(); Plugin.Manager.TryAutoLockQueueFromVotes(); if (highlightedIndex >= 0 && highlightedIndex < queue.OfferedContracts.Count) { 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 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_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0258: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_043b: Unknown result type (might be due to invalid IL or missing references) //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_049c: Unknown result type (might be due to invalid IL or missing references) //IL_04c1: Unknown result type (might be due to invalid IL or missing references) //IL_04df: Unknown result type (might be due to invalid IL or missing references) //IL_0548: Unknown result type (might be due to invalid IL or missing references) //IL_0567: Unknown result type (might be due to invalid IL or missing references) //IL_05b1: Unknown result type (might be due to invalid IL or missing references) //IL_05cf: 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); } 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.54f, 0.88f, 0.045f); DisablePhysicalCollider(obj); SetColor(obj, new Color(0.012f, 0.018f, 0.02f, 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, 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, -0.035f); obj3.transform.localScale = new Vector3(1.48f, 0.8f, 0.018f); DisablePhysicalCollider(obj3); SetColor(obj3, new Color(0.02f, 0.08f, 0.085f, 1f)); ContractAssets.ApplyTexture(obj3, "ui/board/ui_contract_board_header.png", new Color(0.65f, 1f, 0.95f, 0.58f)); 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, -0.07f); obj4.transform.localScale = new Vector3(1.08f, 0.66f, 0.012f); DisablePhysicalCollider(obj4); SetColor(obj4, new Color(0.01f, 0.035f, 0.04f, 1f)); GameObject obj5 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj5).name = "Repo Contracts TV Glow"; obj5.transform.SetParent(boardRoot.transform, false); obj5.transform.localPosition = new Vector3(0f, 0f, -0.052f); obj5.transform.localScale = new Vector3(1.36f, 0.66f, 0.01f); DisablePhysicalCollider(obj5); SetColor(obj5, new Color(0.03f, 0.16f, 0.16f, 0.9f)); headerText = CreateText("Header", new Vector3(-0.68f, 0.35f, -0.09f), 0.0185f, new Color(0.72f, 1f, 0.94f, 1f)); selectedText = CreateText("Selected", new Vector3(0.38f, 0.24f, -0.09f), 0.0105f, new Color(0.88f, 1f, 0.72f, 1f)); promptText = CreateText("Prompt", new Vector3(-0.68f, -0.37f, -0.09f), 0.0105f, new Color(1f, 0.95f, 0.55f, 1f)); for (int i = 0; i < 5; i++) { GameObject obj6 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj6).name = $"Repo Contracts Card {i + 1}"; obj6.transform.SetParent(boardRoot.transform, false); obj6.transform.localPosition = new Vector3(-0.21f, 0.19f - (float)i * 0.118f, -0.105f); obj6.transform.localScale = new Vector3(1.02f, 0.105f, 0.035f); MakeTriggerOnly(obj6); ContractBoardCard contractBoardCard = obj6.AddComponent(); contractBoardCard.Index = i; contractBoardCard.Text = CreateText($"CardText{i}", new Vector3(-0.66f, 0.224f - (float)i * 0.118f, -0.13f), 0.0072f, new Color(0.94f, 1f, 0.96f, 1f)); cards.Add(contractBoardCard); } } } 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 boardSideOffset = Plugin.BoardSideOffset; boardSideOffset.Value -= num; flag = true; } if (Input.GetKeyDown((KeyCode)262)) { ConfigEntry boardSideOffset2 = Plugin.BoardSideOffset; boardSideOffset2.Value += num; flag = true; } if (Input.GetKeyDown((KeyCode)258)) { ConfigEntry boardForwardOffset = Plugin.BoardForwardOffset; boardForwardOffset.Value -= num; flag = true; } if (Input.GetKeyDown((KeyCode)264)) { ConfigEntry boardForwardOffset2 = Plugin.BoardForwardOffset; boardForwardOffset2.Value += num; flag = true; } if (Input.GetKeyDown((KeyCode)263)) { ConfigEntry boardUpOffset = Plugin.BoardUpOffset; boardUpOffset.Value += num; flag = true; } if (Input.GetKeyDown((KeyCode)265)) { ConfigEntry boardUpOffset2 = Plugin.BoardUpOffset; boardUpOffset2.Value -= num; flag = true; } if (Input.GetKeyDown((KeyCode)257)) { ConfigEntry boardYawOffset = Plugin.BoardYawOffset; boardYawOffset.Value -= num2; flag = true; } if (Input.GetKeyDown((KeyCode)259)) { ConfigEntry boardYawOffset2 = Plugin.BoardYawOffset; boardYawOffset2.Value += num2; flag = true; } if (Input.GetKeyDown((KeyCode)270) || Input.GetKeyDown((KeyCode)61)) { ConfigEntry 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) { ApplyPlacement(); RebuildBoardText(); 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 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.19f - (float)i * 0.118f; if (Mathf.Abs(val3.y - num3) <= 0.067f) { 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 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() { if (queue == null) { return; } if ((Object)(object)headerText != (Object)null) { headerText.text = (queue.IsLocked ? "CONTRACT QUEUE LOCKED" : "CONTRACT BOARD"); } for (int i = 0; i < cards.Count; i++) { ContractBoardCard contractBoardCard = cards[i]; if (i >= queue.OfferedContracts.Count) { ((Component)contractBoardCard).gameObject.SetActive(false); continue; } ((Component)contractBoardCard).gameObject.SetActive(true); ContractInstance contractInstance = queue.OfferedContracts[i]; ApplyCardVisual(((Component)contractBoardCard).gameObject, contractInstance.Definition.Rarity, i == highlightedIndex); if ((Object)(object)contractBoardCard.Text != (Object)null) { contractBoardCard.Text.text = $"{i + 1}. {Shorten(contractInstance.Definition.DisplayName, 18)} [{DifficultyLabel(contractInstance.Definition.Rarity)}]\n" + Shorten(contractInstance.Definition.ObjectiveText, 32); } } if ((Object)(object)selectedText != (Object)null) { selectedText.text = BuildVoteSummary(); } if ((Object)(object)promptText != (Object)null) { promptText.text = (placementMode ? "PLACE: 4/6 side 8/2 fwd 7/9 up 1/3 yaw +/- scale F9 save" : (queue.IsLocked ? "QUEUE LOCKED - Press C to view contracts." : "Aim/D-pad select. 1/2/3 or buttons vote. Auto-locks.")); } } private string BuildVoteSummary() { if (queue == null) { return string.Empty; } string localPlayerId = GetLocalPlayerId(); Plugin.Manager.PlayerVotes.TryGetValue(localPlayerId, out string[] value); string text = "Votes\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 != null) ? Shorten(contractInstance.Definition.DisplayName, 11) : "-"); } int num = ((!SemiFunc.IsMultiplayer() || PhotonNetwork.PlayerList == null) ? 1 : PhotonNetwork.PlayerList.Length); return text + $"{Plugin.Manager.PlayerVotes.Count}/{num}"; } 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_0039: 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) GameObject val = new GameObject("Repo Contracts " + name); val.transform.SetParent(boardRoot.transform, false); val.transform.localPosition = localPosition; val.transform.localRotation = Quaternion.identity; TextMesh obj = val.AddComponent(); obj.anchor = (TextAnchor)0; obj.alignment = (TextAlignment)0; obj.characterSize = size; obj.fontSize = 46; obj.color = color; return obj; } private static string Shorten(string value, int maxLength) { 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 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.05f, 0.42f, 0.16f, 1f), ContractRarity.Uncommon => new Color(0.55f, 0.46f, 0.08f, 1f), ContractRarity.Rare => new Color(0.58f, 0.11f, 0.08f, 1f), ContractRarity.Epic => new Color(0.33f, 0.13f, 0.48f, 1f), ContractRarity.Legendary => new Color(0.65f, 0.32f, 0.05f, 1f), _ => new Color(0.035f, 0.13f, 0.14f, 1f), }); if (!highlighted) { return val; } return Color.Lerp(val, Color.white, 0.28f); } private static void ApplyCardVisual(GameObject card, ContractRarity rarity, bool highlighted) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_00a2: Unknown result type (might be due to invalid IL or missing references) string relativePath = rarity switch { ContractRarity.Common => "ui/cards/ui_contract_card_frame_easy.png", ContractRarity.Uncommon => "ui/cards/ui_contract_card_frame_med.png", ContractRarity.Rare => "ui/cards/ui_contract_card_frame_hard.png", ContractRarity.Epic => "ui/cards/ui_contract_card_frame_hard.png", ContractRarity.Legendary => "ui/cards/ui_contract_card_frame_hard.png", _ => "ui/cards/ui_contract_card_frame_med.png", }; Color tint = (Color)(highlighted ? Color.white : new Color(0.78f, 0.88f, 0.82f, 0.92f)); ContractAssets.ApplyTexture(card, relativePath, tint); Renderer component = card.GetComponent(); object obj; if (component == null) { obj = null; } else { Material material = component.material; obj = ((material != null) ? material.mainTexture : null); } if ((Object)obj == (Object)null) { 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 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(); if ((Object)(object)component != (Object)null) { component.material.color = color; } } private static void DisablePhysicalCollider(GameObject target) { Collider component = target.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } private static void MakeTriggerOnly(GameObject target) { Collider component = target.GetComponent(); 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: Expected O, but got Unknown //IL_005e: 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_00b6: 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) { wordWrap = true }; Rect rect = default(Rect); ((Rect)(ref rect))..ctor((float)Screen.width - 550f, 36f, 520f, 340f); 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 + 18f, ((Rect)(ref rect)).y + 14f, ((Rect)(ref rect)).width - 36f, ((Rect)(ref rect)).height - 28f)); GUILayout.Label("Repo Contracts", val, Array.Empty()); 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"), val, Array.Empty()); } ContractInstance activeContract = queue.ActiveContract; if (activeContract == null) { GUILayout.Label(queue.IsLocked ? "No active contract." : "Vote at the truck contract board.", val, Array.Empty()); } else { GUILayout.Label($"Active {queue.ActiveIndex + 1}/{queue.SelectedContracts.Count}: {activeContract.Definition.DisplayName}", val, Array.Empty()); GUILayout.Label(activeContract.Definition.ObjectiveText, val, Array.Empty()); GUILayout.Label($"Progress: {activeContract.CurrentProgress}/{activeContract.RequiredProgress}", val, Array.Empty()); GUILayout.Label("Failure: " + activeContract.Definition.FailureText, val, Array.Empty()); GUILayout.Label("Reward: " + activeContract.Definition.RewardPreviewText, val, Array.Empty()); if (activeContract.Definition.Id == "glass_cargo") { string value; string text = ((activeContract.SelectedTargets.TryGetValue("targetName", out value) && !string.IsNullOrWhiteSpace(value)) ? value : "Random marked valuable"); GUILayout.Label("Target: " + text, val, Array.Empty()); } } GUILayout.Space(8f); for (int i = 0; i < queue.SelectedContracts.Count; i++) { ContractInstance contractInstance = queue.SelectedContracts[i]; GUILayout.Label($"{i + 1}. {contractInstance.Definition.DisplayName} - {contractInstance.Status}", val, Array.Empty()); } 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()); GUILayout.EndArea(); } } private void DrawTrackedTarget() { //IL_0061: 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) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //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_009b: 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_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0143: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: 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) if (!visible || (Object)(object)trackedTarget == (Object)null || queue?.ActiveContract?.Definition.Id != "glass_cargo") { 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 flag = 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 text = (string.IsNullOrWhiteSpace(trackedTargetLabel) ? "GLASS CARGO" : trackedTargetLabel); 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), $"GLASS CARGO TARGET\n{text} - {Mathf.RoundToInt(num5)}m"); GUI.Box(new Rect(num3 - 105f, num4 - 26f, 210f, 52f), string.Format("{0}\n{1} - {2}m", flag ? "TARGET" : "MARKED", text, 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 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.Definition.RewardPreviewText)); } } } } } 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_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00df: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_0108: 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.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(surplusValuableSmall, position, Quaternion.identity)).GetComponent(); 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 predicate, string label) { //IL_009f: 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_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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00c3: Unknown result type (might be due to invalid IL or missing references) Func predicate2 = predicate; 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 list = 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(); if (list.Count == 0) { Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward unavailable: no eligible shop items were found.")); return false; } Item val = list[Random.Range(0, list.Count)]; Quaternion val2 = Quaternion.identity * val.spawnRotationOffset; try { if ((Object)(object)(SemiFunc.IsMultiplayer() ? PhotonNetwork.InstantiateRoomObject(val.prefab.ResourcePath, position, val2, (byte)0, (object[])null) : Object.Instantiate(val.prefab.Prefab, position, val2)) == (Object)null) { Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward failed to spawn: " + val.itemName + ".")); return false; } Plugin.Log.LogInfo((object)("[RepoContracts] Spawned contract " + label + " reward: " + val.itemName + ".")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward failed to spawn " + val.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 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(), Array.Empty(), -1, isLocked: false, 0, string.Empty); internal IReadOnlyList Offers { get; } internal IReadOnlyList Selected { get; } internal int ActiveIndex { get; } internal bool IsLocked { get; } internal int DisciplineDebt { get; } internal string VoteSessionId { get; } internal ContractSnapshot(IReadOnlyList offers, IReadOnlyList 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 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)((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)((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 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 list = new List(); 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 list2 = new List(); 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, string>((KeyValuePair 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)); } private static ContractInstance? DeserializeContract(string data, IReadOnlyList 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 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)); } } } return contractInstance; } private static IEnumerable SplitList(string value, char separator) { if (!string.IsNullOrWhiteSpace(value)) { return from v in value.Split(separator) where !string.IsNullOrWhiteSpace(v) select v; } return Enumerable.Empty(); } private static string Escape(string value) { return Uri.EscapeDataString(value ?? string.Empty); } private static string Unescape(string value) { return Uri.UnescapeDataString(value ?? string.Empty); } } } namespace RepoContracts.Contracts { internal readonly struct AssetRequirements { internal bool ExistingAssetsOnly { get; } internal bool NeedsUiAsset { get; } internal bool NeedsWorldObject { get; } internal bool NeedsVfx { get; } internal bool NeedsSfx { get; } internal bool NeedsIcon { get; } internal bool NeedsModel { get; } internal bool NeedsAnimation { get; } internal static AssetRequirements ExistingOnly => default(AssetRequirements); internal AssetRequirements(bool existingAssetsOnly = true, bool needsUiAsset = false, bool needsWorldObject = false, bool needsVfx = false, bool needsSfx = false, bool needsIcon = false, bool needsModel = false, bool needsAnimation = false) { ExistingAssetsOnly = existingAssetsOnly; NeedsUiAsset = needsUiAsset; NeedsWorldObject = needsWorldObject; NeedsVfx = needsVfx; NeedsSfx = needsSfx; NeedsIcon = needsIcon; NeedsModel = needsModel; NeedsAnimation = needsAnimation; } } internal sealed class ContractCatalog { private readonly List definitions; internal IReadOnlyList All => definitions; internal ContractCatalog() { definitions = new List { Prototype("clean_sweep", "Clean Sweep", ContractCategory.Loot, ContractRarity.Common, "Extract a target value while active.", "Extract valuables while this contract is active.", "Extract $1,000 in valuables.", "No failure condition.", "+$2,500 money bag", 1000, ContractHook.ExtractionValueChanged, null, RewardRoll.MoneyBag(ContractRarity.Common, 2500, "+$2,500"), "loot", "value"), Prototype("monster_hunter", "Monster Hunter", ContractCategory.Combat, ContractRarity.Uncommon, "Kill enemies while active.", "Hunt enemies during the active contract window.", "Kill 3 enemies.", "No failure condition.", "+$4,000 money bag", 3, ContractHook.EnemyKilled, null, RewardRoll.MoneyBag(ContractRarity.Uncommon, 4000, "+$4,000"), "combat", "enemy-kill"), Prototype("melee_clause", "Melee Clause", ContractCategory.Combat, ContractRarity.Rare, "Kill enemies using melee only.", "Projectile weapon kills fail this contract while it is active.", "Kill 2 enemies with melee or close-range physics.", "Projectile kill while active.", "+$6,500 money bag", 2, ContractHook.EnemyKilled, ContractHook.ProjectileWeaponUsed, RewardRoll.MoneyBag(ContractRarity.Rare, 6500, "+$6,500"), "combat", "melee"), Prototype("glass_cargo", "Glass Cargo", ContractCategory.Loot, ContractRarity.Rare, "Extract a random protected valuable without breaking it.", "A random breakable valuable is marked and must be extracted before it breaks.", "Find the marked valuable and extract it before it breaks.", "Marked item broke or was destroyed.", "+$7,500 money bag", 1, ContractHook.ItemExtracted, ContractHook.ItemDestroyed, RewardRoll.MoneyBag(ContractRarity.Rare, 7500, "+$7,500"), new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), "loot", "fragile", "marker"), Prototype("no_one_left_bent", "No One Left Bent", ContractCategory.Survival, ContractRarity.Uncommon, "Complete the next extraction with no deaths.", "Complete one successful extraction while this contract is active. Any player death fails it, even if they are revived later.", "Complete the next extraction with no player deaths.", "Any player dies before extraction completes.", "+$4,500 money bag", 1, ContractHook.ExtractionPointCompleted, ContractHook.PlayerDeath, RewardRoll.MoneyBag(ContractRarity.Uncommon, 4500, "+$4,500"), "survival", "death"), Scaffold("rotation_lock", "Rotation Lock", ContractCategory.Loot, ContractRarity.Rare, "Move a marked item without rotation assistance.", ContractHook.ItemExtracted, ContractHook.ItemGrabbed, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), true, "loot", "experimental", "rotation-control"), Scaffold("heavy_haul", "Heavy Haul", ContractCategory.Loot, ContractRarity.Uncommon, "Marked valuables become absurdly heavy.", ContractHook.ItemExtracted, ContractHook.ItemGrabbed, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: false, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), true, "loot", "weight-control"), Scaffold("cursed_loot", "Cursed Loot", ContractCategory.Loot, ContractRarity.Epic, "A cursed item must reach extraction.", ContractHook.ItemExtracted, ContractHook.ItemReleased, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: false, needsWorldObject: false, needsVfx: true, needsSfx: true, needsIcon: true), true, "loot", "cursed"), Scaffold("awkward_monopoly", "Awkward Monopoly", ContractCategory.Loot, ContractRarity.Epic, "Bias the level toward awkward bulky valuables.", ContractHook.ExtractionValueChanged, null, AssetRequirements.ExistingOnly, false, "loot", "spawn-replacement", "experimental"), Prototype("debt_collector", "Debt Collector", ContractCategory.Loot, ContractRarity.Common, "Extract a target value while active.", "Extract enough valuables before the contract advances.", "Extract $1,500 in valuables.", "No failure condition.", "+$3,000", 1500, ContractHook.ExtractionValueChanged, null, RewardRoll.MoneyBag(ContractRarity.Common, 3000, "+$3,000"), "loot", "value"), Scaffold("crew_limit_clause", "Crew Limit Clause", ContractCategory.Loot, ContractRarity.Rare, "Move a marked item with an exact grabber count.", ContractHook.ItemExtracted, ContractHook.ItemGrabbed, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), true, "loot", "grab-count"), Scaffold("bellringer", "Bellringer", ContractCategory.Combat, ContractRarity.Rare, "Alert enemies and survive the response.", ContractHook.EnemyKilled, ContractHook.EnemyAlerted, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: true), true, "combat", "enemy-alert"), Scaffold("hit_list", "Hit List", ContractCategory.Combat, ContractRarity.Epic, "Kill selected enemies back-to-back.", ContractHook.EnemyKilled, ContractHook.EnemySpawned, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), true, "combat", "target-marker"), Scaffold("chosen_killer", "Chosen Killer", ContractCategory.Combat, ContractRarity.Rare, "Only one selected player may score kills.", ContractHook.EnemyKilled, ContractHook.PlayerDeath, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "combat", "kill-owner"), Scaffold("physics_department", "Physics Department", ContractCategory.Combat, ContractRarity.Epic, "Enemies must die from physics or environmental causes.", ContractHook.EnemyKilled, ContractHook.WeaponUsed, AssetRequirements.ExistingOnly, false, "combat", "damage-source", "experimental"), Scaffold("height_advantage", "Height Advantage", ContractCategory.Combat, ContractRarity.Uncommon, "Kill a target from a required vertical difference.", ContractHook.EnemyKilled, null, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true), true, "combat", "position-check"), Scaffold("gallery_code", "Gallery Code", ContractCategory.Puzzle, ContractRarity.Epic, "Find clue symbols and open a code safe.", ContractHook.ExtractionPointCompleted, null, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: true, needsVfx: false, needsSfx: false, needsIcon: true, needsModel: true, needsAnimation: true), false, "puzzle", "asset-heavy"), Scaffold("simon_extraction", "Simon Extraction", ContractCategory.Puzzle, ContractRarity.Rare, "Repeat the extraction point button sequence.", ContractHook.ExtractionPointCompleted, null, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: true, needsVfx: false, needsSfx: true, needsIcon: false, needsModel: true, needsAnimation: true), false, "puzzle", "asset-heavy"), Prototype("pressure_plate_extraction", "Pressure Plate Extraction", ContractCategory.Puzzle, ContractRarity.Rare, "Secure plates controlling one extraction.", "One random extraction point is locked behind pressure plates. Each plate can be held by a player or weighed down with medium-weight loot, so solo players can still complete it.", "Secure all plates, then use the controlled extraction.", "The controlled extraction was used before every plate was secured.", "+$8,000 money bag", 2, ContractHook.ExtractionPointCompleted, null, RewardRoll.MoneyBag(ContractRarity.Rare, 8000, "+$8,000"), new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: true, needsVfx: true, needsSfx: false, needsIcon: false, needsModel: true), "puzzle", "pressure-plate"), Scaffold("quiet_quota", "Quiet Quota", ContractCategory.Stealth, ContractRarity.Rare, "Extract value while alerts stay below a cap.", ContractHook.ExtractionValueChanged, ContractHook.EnemyAlerted, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "stealth", "enemy-alert"), Scaffold("silent_courier", "Silent Courier", ContractCategory.Stealth, ContractRarity.Rare, "Extract a marked item without detection.", ContractHook.ItemExtracted, ContractHook.EnemyAlerted, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), true, "stealth", "marker"), Prototype("no_loud_tools", "No Loud Tools", ContractCategory.Stealth, ContractRarity.Uncommon, "Complete an extraction without loud weapons.", "Complete one extraction while this contract is active without using projectile or loud weapon tools.", "Complete the next extraction without projectile weapons.", "Projectile or loud weapon used.", "+$4,000 money bag", 1, ContractHook.ExtractionPointCompleted, ContractHook.ProjectileWeaponUsed, RewardRoll.MoneyBag(ContractRarity.Uncommon, 4000, "+$4,000"), "stealth", "weapon-use"), Scaffold("avoid_the_hunter", "Avoid the Hunter", ContractCategory.Stealth, ContractRarity.Rare, "Avoid detection from a selected enemy type.", ContractHook.LevelStarted, ContractHook.EnemyAlerted, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "stealth", "enemy-type"), Scaffold("shadow_recovery", "Shadow Recovery", ContractCategory.Stealth, ContractRarity.Rare, "Recover a valuable near patrol without alerting it.", ContractHook.ItemExtracted, ContractHook.EnemyAlerted, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), true, "stealth", "marker"), Scaffold("low_profile", "Low Profile", ContractCategory.Stealth, ContractRarity.Uncommon, "Complete without sprinting or making loud noise.", ContractHook.LevelStarted, ContractHook.NoiseMade, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "stealth", "noise"), Scaffold("ghost_clause", "Ghost Clause", ContractCategory.Stealth, ContractRarity.Epic, "Complete without any player being targeted.", ContractHook.LevelStarted, ContractHook.EnemyAlerted, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "stealth", "targeting"), Scaffold("quarter_health", "Quarter Health", ContractCategory.Survival, ContractRarity.Rare, "Cap max health for the active contract.", ContractHook.LevelStarted, ContractHook.PlayerDeath, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "survival", "health-mutator"), Scaffold("protect_the_asset", "Protect the Asset", ContractCategory.Survival, ContractRarity.Rare, "Protect a selected player.", ContractHook.LevelStarted, ContractHook.PlayerDeath, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "survival", "player-marker"), Prototype("no_deaths", "No Deaths", ContractCategory.Survival, ContractRarity.Uncommon, "Complete an extraction with no deaths.", "Complete one successful extraction while this contract is active. Revives do not erase death tracking.", "Complete the next extraction with no deaths.", "Any player dies before extraction completes.", "+$4,500 money bag", 1, ContractHook.ExtractionPointCompleted, ContractHook.PlayerDeath, RewardRoll.MoneyBag(ContractRarity.Uncommon, 4500, "+$4,500"), "survival", "death"), Prototype("no_damage", "No Damage", ContractCategory.Survival, ContractRarity.Rare, "Complete an extraction without player damage.", "Complete one successful extraction while this contract is active without any player taking damage.", "Complete the next extraction without player damage.", "Any player takes damage.", "+$7,000 money bag", 1, ContractHook.ExtractionPointCompleted, ContractHook.PlayerDamaged, RewardRoll.MoneyBag(ContractRarity.Rare, 7000, "+$7,000"), "survival", "damage"), Prototype("limited_healing", "Limited Healing", ContractCategory.Survival, ContractRarity.Uncommon, "Complete an extraction without truck healing.", "Complete one extraction while this contract is active without using truck healing.", "Complete the next extraction without healing.", "A player heals while active.", "+$4,000 money bag", 1, ContractHook.ExtractionPointCompleted, ContractHook.PlayerHealed, RewardRoll.MoneyBag(ContractRarity.Uncommon, 4000, "+$4,000"), "survival", "healing"), Scaffold("last_legs", "Last Legs", ContractCategory.Survival, ContractRarity.Rare, "Start weakened; completion restores a small amount.", ContractHook.LevelStarted, ContractHook.PlayerDeath, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), true, "survival", "health-mutator"), Scaffold("holdout_clause", "Holdout Clause", ContractCategory.Survival, ContractRarity.Epic, "Survive pressure near a chosen area.", ContractHook.LevelStarted, ContractHook.PlayerDeath, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: true, needsVfx: true), true, "survival", "area-marker"), Scaffold("hunter_lockdown", "Hunter Lockdown", ContractCategory.Milestone, ContractRarity.Legendary, "Milestone placeholder: stealth pressure replaces the normal queue.", ContractHook.LevelStarted, ContractHook.ExtractionValueChanged, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), false, "milestone", "enemy-roster", "experimental"), Scaffold("all_fragile_level", "All Fragile Level", ContractCategory.Milestone, ContractRarity.Legendary, "Milestone placeholder: all loot is fragile.", ContractHook.LevelStarted, ContractHook.ItemDamaged, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: true, needsSfx: false, needsIcon: true), false, "milestone", "item-mutator"), Scaffold("heavy_world", "Heavy World", ContractCategory.Milestone, ContractRarity.Legendary, "Milestone placeholder: the world is heavy.", ContractHook.LevelStarted, null, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), false, "milestone", "weight-control"), Scaffold("enemy_swarm", "Enemy Swarm", ContractCategory.Milestone, ContractRarity.Legendary, "Milestone placeholder: more enemy pressure.", ContractHook.LevelStarted, ContractHook.EnemyKilled, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: false, needsVfx: false, needsSfx: false, needsIcon: true), false, "milestone", "enemy-spawn"), Scaffold("puzzle_extraction_override", "Puzzle Extraction Override", ContractCategory.Milestone, ContractRarity.Legendary, "Milestone placeholder: extraction requires a puzzle.", ContractHook.ExtractionPointCompleted, null, new AssetRequirements(existingAssetsOnly: false, needsUiAsset: true, needsWorldObject: true, needsVfx: false, needsSfx: false, needsIcon: false, needsModel: true, needsAnimation: true), false, "milestone", "asset-heavy") }; } internal IReadOnlyList GenerateOffers(int count) { List list = definitions.Where(IsEligibleForOffers).ToList(); List list2 = new List(); count = Mathf.Clamp(count, 1, Math.Max(1, list.Count)); while (list2.Count < count && list.Count > 0) { int num = ((IEnumerable)list).Sum((Func)GetWeight); int num2 = Random.Range(0, num); int num3 = 0; for (int i = 0; i < list.Count; i++) { num3 += GetWeight(list[i]); if (num2 < num3) { list2.Add(list[i]); list.RemoveAt(i); break; } } } return list2; } private static bool IsEligibleForOffers(ContractDefinition definition) { if (!definition.IsQueueEligible || definition.IsMilestoneContract) { return false; } if (!Plugin.EnableExperimentalContracts.Value && definition.Tags.Contains("experimental")) { return false; } if (!Plugin.EnableAssetHeavyContracts.Value && definition.Tags.Contains("asset-heavy")) { return false; } if (Plugin.EnableCompatibilityMode.Value && definition.Tags.Any(IsRiskyCompatibilityTag)) { return false; } if (!definition.IsPrototypeImplemented) { return Plugin.EnableExperimentalContracts.Value; } return true; } private static bool IsRiskyCompatibilityTag(string tag) { switch (tag) { case "spawn-replacement": case "enemy-roster": case "health-mutator": case "item-mutator": case "weight-control": case "rotation-control": return true; default: return false; } } private static int GetWeight(ContractDefinition definition) { return definition.Rarity switch { ContractRarity.Common => 45, ContractRarity.Uncommon => 30, ContractRarity.Rare => 16, ContractRarity.Epic => 7, ContractRarity.Legendary => 2, _ => 1, }; } private static ContractDefinition Prototype(string id, string name, ContractCategory category, ContractRarity rarity, string shortDescription, string longDescription, string objective, string failure, string rewardPreview, int requiredProgress, ContractHook progressHook, ContractHook? failureHook, RewardRoll reward, params string[] tags) { return Prototype(id, name, category, rarity, shortDescription, longDescription, objective, failure, rewardPreview, requiredProgress, progressHook, failureHook, reward, AssetRequirements.ExistingOnly, tags); } private static ContractDefinition Prototype(string id, string name, ContractCategory category, ContractRarity rarity, string shortDescription, string longDescription, string objective, string failure, string rewardPreview, int requiredProgress, ContractHook progressHook, ContractHook? failureHook, RewardRoll reward, AssetRequirements assets, params string[] tags) { List list = new List { progressHook }; if (failureHook.HasValue) { list.Add(failureHook.Value); } return new ContractDefinition(id, name, category, rarity, shortDescription, longDescription, objective, failure, rewardPreview, tags, Array.Empty(), list, assets, isMilestoneContract: false, isQueueEligible: true, "Contracts." + id, requiredProgress, progressHook, failureHook, reward, isPrototypeImplemented: true); } private static ContractDefinition Scaffold(string id, string name, ContractCategory category, ContractRarity rarity, string description, ContractHook progressHook, ContractHook? failureHook, AssetRequirements assets, bool queueEligible, params string[] tags) { List list = new List { progressHook }; if (failureHook.HasValue) { list.Add(failureHook.Value); } bool isMilestoneContract = category == ContractCategory.Milestone; return new ContractDefinition(id, name, category, rarity, description, description, "Scaffolded objective. Implementation pending verified hooks.", failureHook.HasValue ? "Scaffolded failure condition." : "No failure condition.", "Future reward roll.", tags, Array.Empty(), list, assets, isMilestoneContract, queueEligible, "Contracts." + id, 1, progressHook, failureHook, RewardRoll.Stub(RewardKind.CashBonus, rarity, id, "Future reward"), isPrototypeImplemented: false); } } internal sealed class ContractDefinition { internal string Id { get; } internal string DisplayName { get; } internal ContractCategory Category { get; } internal ContractRarity Rarity { get; } internal string ShortDescription { get; } internal string LongDescription { get; } internal string ObjectiveText { get; } internal string FailureText { get; } internal string RewardPreviewText { get; } internal IReadOnlyList Tags { get; } internal IReadOnlyList IncompatibleTags { get; } internal IReadOnlyList RequiredHooks { get; } internal AssetRequirements AssetRequirements { get; } internal bool RequiresNewAssets => !AssetRequirements.ExistingAssetsOnly; internal bool IsMilestoneContract { get; } internal bool IsQueueEligible { get; } internal string ConfigSettingsReference { get; } internal int DefaultRequiredProgress { get; } internal ContractHook ProgressHook { get; } internal ContractHook? FailureHook { get; } internal RewardRoll RewardRoll { get; } internal bool IsPrototypeImplemented { get; } internal ContractDefinition(string id, string displayName, ContractCategory category, ContractRarity rarity, string shortDescription, string longDescription, string objectiveText, string failureText, string rewardPreviewText, IReadOnlyList tags, IReadOnlyList incompatibleTags, IReadOnlyList requiredHooks, AssetRequirements assetRequirements, bool isMilestoneContract, bool isQueueEligible, string configSettingsReference, int defaultRequiredProgress, ContractHook progressHook, ContractHook? failureHook, RewardRoll rewardRoll, bool isPrototypeImplemented) { Id = id; DisplayName = displayName; Category = category; Rarity = rarity; ShortDescription = shortDescription; LongDescription = longDescription; ObjectiveText = objectiveText; FailureText = failureText; RewardPreviewText = rewardPreviewText; Tags = tags; IncompatibleTags = incompatibleTags; RequiredHooks = requiredHooks; AssetRequirements = assetRequirements; IsMilestoneContract = isMilestoneContract; IsQueueEligible = isQueueEligible; ConfigSettingsReference = configSettingsReference; DefaultRequiredProgress = Math.Max(1, defaultRequiredProgress); ProgressHook = progressHook; FailureHook = failureHook; RewardRoll = rewardRoll; IsPrototypeImplemented = isPrototypeImplemented; } } internal enum ContractCategory { Loot, Combat, Puzzle, Stealth, Survival, Milestone } internal enum ContractRarity { Common, Uncommon, Rare, Epic, Legendary } internal enum ContractStatus { Offered, Queued, Active, Completed, Failed, Skipped } internal enum ContractHook { LevelStarted, ShopStarted, ContractBoardOpened, ContractQueueLocked, ItemSpawned, ItemDamaged, ItemDestroyed, ItemGrabbed, ItemReleased, ItemExtracted, ExtractionValueChanged, EnemySpawned, EnemyDamaged, EnemyKilled, PlayerDamaged, PlayerDeath, PlayerHealed, WeaponUsed, ProjectileWeaponUsed, MeleeHit, NoiseMade, EnemyAlerted, ExtractionPointActivated, ExtractionPointCompleted } internal enum RewardKind { CashBonus, MoneyBag, SpawnableValuable, ShopItem, Weapon, UpgradeDiscount, ContractRerollToken, CosmeticToken } internal enum FailureStrictness { Casual, Normal, Hardcore } internal enum DisciplineMode { Off, Casual, Standard, Strict } internal readonly struct ContractEvent { internal ContractHook Hook { get; } internal int Amount { get; } internal int Value { get; } internal string SubjectId { get; } internal string SourceKind { get; } internal GameObject? Subject { get; } internal GameObject? Source { get; } internal ContractEvent(ContractHook hook, int amount = 1, int value = 0, string subjectId = "", string sourceKind = "", GameObject? subject = null, GameObject? source = null) { Hook = hook; Amount = amount; Value = value; SubjectId = subjectId; SourceKind = sourceKind; Subject = subject; Source = source; } } internal sealed class ContractEventBus { private readonly ContractManager manager; internal ContractEventBus(ContractManager manager) { this.manager = manager; } internal void OnLevelStarted() { Publish(new ContractEvent(ContractHook.LevelStarted)); } internal void OnShopStarted() { Publish(new ContractEvent(ContractHook.ShopStarted)); } internal void OnContractBoardOpened() { Publish(new ContractEvent(ContractHook.ContractBoardOpened)); } internal void OnContractQueueLocked() { Publish(new ContractEvent(ContractHook.ContractQueueLocked)); } internal void OnItemSpawned(GameObject item) { Publish(new ContractEvent(ContractHook.ItemSpawned, 1, 0, ((Object)item).name, "", item)); } internal void OnItemDamaged(GameObject item, int amount = 1) { Publish(new ContractEvent(ContractHook.ItemDamaged, amount, 0, ((Object)item).name, "", item)); } internal void OnItemDestroyed(GameObject item) { Publish(new ContractEvent(ContractHook.ItemDestroyed, 1, 0, ((Object)item).name, "", item)); } internal void OnItemGrabbed(GameObject item, GameObject? player = null) { Publish(new ContractEvent(ContractHook.ItemGrabbed, 1, 0, ((Object)item).name, "", item, player)); } internal void OnItemReleased(GameObject item, GameObject? player = null) { Publish(new ContractEvent(ContractHook.ItemReleased, 1, 0, ((Object)item).name, "", item, player)); } internal void OnItemExtracted(GameObject item, int value = 1) { Publish(new ContractEvent(ContractHook.ItemExtracted, 1, value, ((Object)item).name, "", item)); } internal void OnExtractionValueChanged(int deltaValue) { Publish(new ContractEvent(ContractHook.ExtractionValueChanged, 1, deltaValue)); } internal void OnEnemySpawned(GameObject enemy) { Publish(new ContractEvent(ContractHook.EnemySpawned, 1, 0, ((Object)enemy).name, "", enemy)); } internal void OnEnemyDamaged(GameObject enemy, int damage, string sourceKind = "") { Publish(new ContractEvent(ContractHook.EnemyDamaged, 1, damage, ((Object)enemy).name, sourceKind, enemy)); } internal void OnEnemyKilled(GameObject enemy, string sourceKind = "") { Publish(new ContractEvent(ContractHook.EnemyKilled, 1, 0, ((Object)enemy).name, sourceKind, enemy)); } internal void OnPlayerDamaged(GameObject player, int damage = 1) { Publish(new ContractEvent(ContractHook.PlayerDamaged, 1, damage, ((Object)player).name, "", player)); } internal void OnPlayerDeath(GameObject player) { Publish(new ContractEvent(ContractHook.PlayerDeath, 1, 0, ((Object)player).name, "", player)); } internal void OnPlayerHealed(GameObject? player, int amount) { Publish(new ContractEvent(ContractHook.PlayerHealed, 1, amount, ((Object)(object)player != (Object)null) ? ((Object)player).name : string.Empty, "", player)); } internal void OnWeaponUsed(GameObject weapon, string sourceKind = "") { Publish(new ContractEvent(ContractHook.WeaponUsed, 1, 0, ((Object)weapon).name, sourceKind, weapon)); } internal void OnProjectileWeaponUsed(GameObject? weapon = null) { Publish(new ContractEvent(ContractHook.ProjectileWeaponUsed, 1, 0, ((Object)(object)weapon != (Object)null) ? ((Object)weapon).name : string.Empty, "", weapon)); } internal void OnMeleeHit(GameObject? weapon = null) { Publish(new ContractEvent(ContractHook.MeleeHit, 1, 0, ((Object)(object)weapon != (Object)null) ? ((Object)weapon).name : string.Empty, "", weapon)); } internal void OnNoiseMade(GameObject? source = null) { Publish(new ContractEvent(ContractHook.NoiseMade, 1, 0, ((Object)(object)source != (Object)null) ? ((Object)source).name : string.Empty, "", null, source)); } internal void OnEnemyAlerted(GameObject enemy) { Publish(new ContractEvent(ContractHook.EnemyAlerted, 1, 0, ((Object)enemy).name, "", enemy)); } internal void OnExtractionPointActivated(GameObject point) { Publish(new ContractEvent(ContractHook.ExtractionPointActivated, 1, 0, ((Object)point).name, "", point)); } internal void OnExtractionPointCompleted(GameObject? point = null) { Publish(new ContractEvent(ContractHook.ExtractionPointCompleted, 1, 0, ((Object)(object)point != (Object)null) ? ((Object)point).name : string.Empty, "", point)); } private void Publish(ContractEvent contractEvent) { manager.HandleEvent(contractEvent); } } internal sealed class ContractInstance { internal ContractDefinition Definition { get; } internal string DefinitionId { get; } internal int Seed { get; } internal ContractStatus Status { get; private set; } internal int CurrentProgress { get; private set; } internal int RequiredProgress { get; private set; } internal string FailureReason { get; private set; } = string.Empty; internal Dictionary SelectedTargets { get; } = new Dictionary(); internal RewardRoll RewardRoll { get; } internal ContractInstance(ContractDefinition definition, int seed) { Definition = definition; DefinitionId = definition.Id; Seed = seed; RequiredProgress = definition.DefaultRequiredProgress; RewardRoll = definition.RewardRoll; Status = ContractStatus.Offered; } internal void SetStatus(ContractStatus status) { Status = status; } internal void SetRequiredProgress(int requiredProgress) { RequiredProgress = Math.Max(1, requiredProgress); } internal void SetProgress(int currentProgress, int requiredProgress) { RequiredProgress = Math.Max(1, requiredProgress); CurrentProgress = Math.Max(0, Math.Min(RequiredProgress, currentProgress)); } internal void SetFailureReason(string failureReason) { FailureReason = failureReason; } internal bool ApplyEvent(ContractEvent contractEvent) { if (Status != ContractStatus.Active) { return false; } if (ApplyCustomContractEvent(contractEvent)) { return true; } if (Definition.Id == "glass_cargo" && IsGlassCargoTargetEvent(contractEvent) && !MatchesSelectedTarget(contractEvent)) { return false; } if (Definition.Id == "glass_cargo" && contractEvent.Hook == ContractHook.ItemDestroyed) { Fail(string.IsNullOrWhiteSpace(Definition.FailureText) ? "Marked item destroyed." : Definition.FailureText); return true; } if (Definition.FailureHook == contractEvent.Hook) { Fail(string.IsNullOrWhiteSpace(Definition.FailureText) ? "Failure condition triggered." : Definition.FailureText); return true; } if (Definition.ProgressHook != contractEvent.Hook) { return false; } int num = ((contractEvent.Value > 0) ? contractEvent.Value : Math.Max(1, contractEvent.Amount)); CurrentProgress = Math.Min(RequiredProgress, CurrentProgress + num); if (CurrentProgress >= RequiredProgress) { Status = ContractStatus.Completed; } return true; } private bool ApplyCustomContractEvent(ContractEvent contractEvent) { switch (Definition.Id) { case "limited_healing": if (contractEvent.Hook == ContractHook.PlayerHealed) { Fail(string.IsNullOrWhiteSpace(Definition.FailureText) ? "Healing used while active." : Definition.FailureText); return true; } break; case "no_loud_tools": if (contractEvent.Hook == ContractHook.ProjectileWeaponUsed || contractEvent.Hook == ContractHook.WeaponUsed) { Fail(string.IsNullOrWhiteSpace(Definition.FailureText) ? "Loud tool used while active." : Definition.FailureText); return true; } break; case "no_damage": if (contractEvent.Hook == ContractHook.PlayerDamaged) { Fail(string.IsNullOrWhiteSpace(Definition.FailureText) ? "A player took damage." : Definition.FailureText); return true; } break; case "no_deaths": if (contractEvent.Hook == ContractHook.PlayerDeath) { Fail(string.IsNullOrWhiteSpace(Definition.FailureText) ? "A player died." : Definition.FailureText); return true; } break; } return false; } private static bool IsGlassCargoTargetEvent(ContractEvent contractEvent) { ContractHook hook = contractEvent.Hook; if (hook == ContractHook.ItemDestroyed || hook == ContractHook.ItemExtracted) { return true; } return false; } private bool MatchesSelectedTarget(ContractEvent contractEvent) { if (!SelectedTargets.TryGetValue("targetObjectId", out string value) || string.IsNullOrWhiteSpace(value)) { return false; } if ((Object)(object)contractEvent.Subject != (Object)null) { return ((Object)contractEvent.Subject).GetInstanceID().ToString() == value; } return false; } internal void Complete() { CurrentProgress = RequiredProgress; Status = ContractStatus.Completed; } internal void Fail(string reason) { FailureReason = reason; Status = ContractStatus.Failed; } } internal sealed class ContractManager { private readonly struct ExtractionCandidate { internal string Id { get; } internal GameObject Item { get; } internal int Value { get; } internal ExtractionCandidate(string id, GameObject item, int value) { Id = id; Item = item; Value = value; } } private sealed class GlassCargoMarkerPulse : MonoBehaviour { private Transform? beacon; private Vector3 baseBeaconScale = Vector3.one; private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) beacon = ((Component)this).transform.Find("Beacon"); if ((Object)(object)beacon != (Object)null) { baseBeaconScale = beacon.localScale; } } private void LateUpdate() { //IL_0019: 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_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_0037: 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_0079: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Camera.main != (Object)null) { ((Component)this).transform.rotation = Quaternion.LookRotation(((Component)this).transform.position - ((Component)Camera.main).transform.position, Vector3.up); } if ((Object)(object)beacon != (Object)null) { float num = 1f + Mathf.Sin(Time.time * 5f) * 0.22f; beacon.localScale = baseBeaconScale * num; } } } private readonly ContractCatalog catalog; private readonly RewardAdapter rewardAdapter; private readonly ContractSyncAdapter syncAdapter; private readonly ContractBoardUi boardUi; private readonly ContractHud hud; private readonly Random random = new Random(); private readonly Dictionary playerVotes = new Dictionary(); private readonly Dictionary pendingExtractionCandidates = new Dictionary(); private readonly List pendingDeferredRewards = new List(); private readonly HashSet extractedItemIds = new HashSet(); private readonly HashSet killedEnemyIds = new HashSet(); private readonly HashSet deadPlayerIds = new HashSet(); private readonly HashSet destroyedItemIds = new HashSet(); private const string VotePropertyPrefix = "RepoContractsVote"; private const string VoteSessionProperty = "RepoContractsVoteSession"; private static readonly FieldInfo? ValuableCurrentValueField = typeof(ValuableObject).GetField("dollarValueCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? RoomVolumeInExtractionField = typeof(RoomVolumeCheck).GetField("inExtractionPoint", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? ExtractionPointActiveField = typeof(RoundDirector).GetField("extractionPointActive", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? ExtractionPointsField = typeof(RoundDirector).GetField("extractionPoints", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? ExtractionPointsCompletedField = typeof(RoundDirector).GetField("extractionPointsCompleted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly FieldInfo? AllExtractionPointsCompletedField = typeof(RoundDirector).GetField("allExtractionPointsCompleted", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private int completedGameplayLevels; private GameObject? glassCargoMarker; private PressurePlateContractRuntime? pressurePlateRuntime; private GameObject? lastPressurePlateExtractionPoint; private bool ignoredBlockedPressurePlateExtractionCompletion; private int lastSyncedRevision; internal ContractEventBus EventBus { get; } internal ContractQueue Queue { get; } = new ContractQueue(); internal IReadOnlyList Catalog => catalog.All; internal IReadOnlyDictionary PlayerVotes => playerVotes; internal ContractManager(ContractCatalog catalog, RewardAdapter rewardAdapter, ContractSyncAdapter syncAdapter, ContractBoardUi boardUi, ContractHud hud) { this.catalog = catalog; this.rewardAdapter = rewardAdapter; this.syncAdapter = syncAdapter; this.boardUi = boardUi; this.hud = hud; EventBus = new ContractEventBus(this); } internal void PollSyncedState() { if (!SemiFunc.IsMultiplayer() || IsHost() || !syncAdapter.TryReadSnapshot(ref lastSyncedRevision, out string snapshot)) { return; } if (string.IsNullOrWhiteSpace(snapshot)) { Queue.Clear(); playerVotes.Clear(); ClearGlassCargoMarker(); boardUi.Hide(); hud.Hide(); return; } if (!syncAdapter.TryDeserialize(snapshot, Catalog, out var result)) { Plugin.Log.LogWarning((object)"[RepoContracts] Failed to parse synced contract snapshot."); return; } Queue.ApplySnapshot(result.Offers, result.Selected, result.ActiveIndex, result.IsLocked, result.DisciplineDebt, result.VoteSessionId); if (!result.IsLocked) { playerVotes.Clear(); PublishLocalPhotonVote(Array.Empty()); } if (Queue.OfferedContracts.Count > 0) { boardUi.Refresh(Queue); } hud.ShowActive(Queue); PrepareSyncedVisuals(); } internal void GenerateOffersAtTruck() { ResetGameplayEventCache(); if (!Plugin.EnableRepoContracts.Value || !IsGameplayLevel()) { return; } if (Queue.HasUnresolvedContracts) { PrepareActiveContract(); boardUi.Refresh(Queue); hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); DebugLogQueue("Continuing unfinished queue"); return; } Queue.Clear(preserveDisciplineDebt: true); playerVotes.Clear(); IReadOnlyList readOnlyList; if (!ShouldOfferMilestone()) { readOnlyList = catalog.GenerateOffers(Plugin.OfferedContractsPerLevel.Value); } else { IReadOnlyList readOnlyList2 = catalog.All.Where((ContractDefinition c) => c.IsMilestoneContract).Take(1).ToList(); readOnlyList = readOnlyList2; } IReadOnlyList source = readOnlyList; Queue.SetOffers(source.Select((ContractDefinition d) => new ContractInstance(d, random.Next()))); PublishLocalPhotonVote(Array.Empty()); boardUi.Show(Queue); GrantPendingDeferredRewards(); syncAdapter.SyncOffers(Queue); DebugLogQueue("Offers generated"); } internal bool CastVote(string playerId, int rank, string definitionId) { string definitionId2 = definitionId; if (Queue.IsLocked || rank < 1 || rank > Plugin.SelectedContractsPerLevel.Value) { return false; } if (!Queue.OfferedContracts.Any((ContractInstance c) => c.DefinitionId == definitionId2)) { return false; } if (!playerVotes.TryGetValue(playerId, out string[] value)) { value = new string[Plugin.SelectedContractsPerLevel.Value]; playerVotes[playerId] = value; } for (int i = 0; i < value.Length; i++) { if (value[i] == definitionId2) { value[i] = string.Empty; } } value[rank - 1] = definitionId2; PublishLocalPhotonVote(value); syncAdapter.SyncVotes(Queue, playerVotes); boardUi.Refresh(Queue); return true; } internal bool HostLockQueueFromVotes() { if (!IsHost()) { Plugin.Log.LogWarning((object)"[RepoContracts] Client attempted to lock contract votes. Host controls queue locking in v1."); return false; } if (Queue.IsLocked) { return false; } PullPhotonVotes(); Dictionary dictionary = new Dictionary(); foreach (ContractInstance offeredContract in Queue.OfferedContracts) { dictionary[offeredContract.DefinitionId] = 0; } foreach (string[] value in playerVotes.Values) { for (int i = 0; i < value.Length; i++) { string text = value[i]; if (!string.IsNullOrWhiteSpace(text) && dictionary.ContainsKey(text)) { dictionary[text] += value.Length - i; } } } foreach (string item in (from kv in dictionary where kv.Value > 0 orderby kv.Value descending, Queue.OfferedContracts.ToList().FindIndex((ContractInstance c) => c.DefinitionId == kv.Key) select kv.Key).Take(Plugin.SelectedContractsPerLevel.Value)) { Queue.SelectOffer(item); } return HostLockQueue(); } internal void TryAutoLockQueueFromVotes() { if (IsHost() && !Queue.IsLocked && Queue.OfferedContracts.Count != 0) { PullPhotonVotes(); if (AllPlayersHaveCompleteVotes()) { Plugin.Log.LogInfo((object)"[RepoContracts] All players voted. Auto-locking contract queue."); HostLockQueueFromVotes(); } } } internal bool HostSelectContract(string definitionId) { if (!IsHost()) { Plugin.Log.LogWarning((object)"[RepoContracts] Client attempted to select a contract. Host controls queue locking in v1."); return false; } bool num = Queue.SelectOffer(definitionId); if (num) { boardUi.Refresh(Queue); syncAdapter.SyncSelection(Queue); } return num; } internal bool HostRemoveSelection(int selectedIndex) { if (!IsHost()) { return false; } bool num = Queue.RemoveSelection(selectedIndex); if (num) { boardUi.Refresh(Queue); syncAdapter.SyncSelection(Queue); } return num; } internal bool HostMoveSelection(int fromIndex, int toIndex) { if (!IsHost()) { return false; } bool num = Queue.MoveSelection(fromIndex, toIndex); if (num) { boardUi.Refresh(Queue); syncAdapter.SyncSelection(Queue); } return num; } internal bool HostLockQueue() { if (!IsHost()) { return false; } EnsureDefaultSelectionIfEmpty(); if (!Queue.LockAndActivateFirst()) { return false; } boardUi.Refresh(Queue); hud.ShowActive(Queue); syncAdapter.SyncLockedQueue(Queue); EventBus.OnContractQueueLocked(); PrepareActiveContract(); DebugLogQueue("Queue locked"); return true; } internal void CaptureExtractionCandidates(IEnumerable candidates) { if (!IsHost()) { return; } pendingExtractionCandidates.Clear(); foreach (GameObject candidate in candidates) { if (!((Object)(object)candidate == (Object)null)) { int valuableValue = GetValuableValue(candidate); if (valuableValue > 0) { string objectId = GetObjectId(candidate); pendingExtractionCandidates[objectId] = new ExtractionCandidate(objectId, candidate, valuableValue); } } } } internal void HandleExtractionCompleted(GameObject? extractionPoint) { if (!IsHost()) { return; } if (CompleteGlassCargoFromExtractionCandidates()) { pendingExtractionCandidates.Clear(); syncAdapter.SyncProgress(Queue); return; } ContractInstance activeContract = Queue.ActiveContract; if (activeContract?.Definition.Id == "pressure_plate_extraction") { if (extractionPoint == null) { extractionPoint = lastPressurePlateExtractionPoint; } UpdatePressurePlateProgress(activeContract); if (ignoredBlockedPressurePlateExtractionCompletion && ((Object)(object)pressurePlateRuntime == (Object)null || !pressurePlateRuntime.AllPlatesSecured)) { ignoredBlockedPressurePlateExtractionCompletion = false; pendingExtractionCandidates.Clear(); hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); Plugin.Log.LogInfo((object)"[RepoContracts] Ignored blocked pressure plate extraction completion; plates are still unsecured."); return; } ignoredBlockedPressurePlateExtractionCompletion = false; if (!IsActivePressurePlateExtractionPoint(activeContract, extractionPoint)) { activeContract.Fail("A different extraction point was completed instead of the controlled one."); ResolveActiveContract(completed: false); pendingExtractionCandidates.Clear(); Plugin.Log.LogInfo((object)"[RepoContracts] Pressure Plate Extraction failed: wrong extraction point completed."); } else if ((Object)(object)pressurePlateRuntime == (Object)null || !pressurePlateRuntime.AllPlatesSecured) { activeContract.Fail("Extraction completed before all pressure plates were secured."); ResolveActiveContract(completed: false); pendingExtractionCandidates.Clear(); Plugin.Log.LogInfo((object)"[RepoContracts] Pressure Plate Extraction failed: extraction completed without secured plates."); } else { activeContract.Complete(); ResolveActiveContract(completed: true); pendingExtractionCandidates.Clear(); Plugin.Log.LogInfo((object)"[RepoContracts] Pressure Plate Extraction completed."); } return; } int num = 0; foreach (ExtractionCandidate item in pendingExtractionCandidates.Values.ToList()) { if (!((Object)(object)item.Item == (Object)null) && !extractedItemIds.Contains(item.Id)) { extractedItemIds.Add(item.Id); num += item.Value; EventBus.OnItemExtracted(item.Item, item.Value); } } if (num > 0) { EventBus.OnExtractionValueChanged(num); } EventBus.OnExtractionPointCompleted(extractionPoint); pendingExtractionCandidates.Clear(); } internal void HandleValuableExtracting(GameObject item, GameObject? extractionPoint = null) { if (!IsHost() || (Object)(object)item == (Object)null) { return; } lastPressurePlateExtractionPoint = extractionPoint; int num = Math.Max(1, GetValuableValue(item)); string objectId = GetObjectId(item); if (extractedItemIds.Add(objectId)) { ContractInstance activeContract = Queue.ActiveContract; if (activeContract?.Definition.Id == "glass_cargo" && IsActiveGlassCargoTarget(item)) { activeContract.Complete(); ResolveActiveContract(completed: true); Plugin.Log.LogInfo((object)"[RepoContracts] Completed Glass Cargo from extraction point item processing."); } else { EventBus.OnItemExtracted(item, num); EventBus.OnExtractionValueChanged(num); } } } internal void HandleEnemyKilled(GameObject enemy, string sourceKind = "") { if (IsHost() && !((Object)(object)enemy == (Object)null)) { string objectId = GetObjectId(enemy); if (killedEnemyIds.Add(objectId)) { EventBus.OnEnemyKilled(enemy, sourceKind); } } } internal void HandlePlayerDeath(GameObject player) { if (IsHost() && !((Object)(object)player == (Object)null)) { string objectId = GetObjectId(player); if (deadPlayerIds.Add(objectId)) { EventBus.OnPlayerDeath(player); } } } internal void HandleValuableDamaged(GameObject item, int valueLost) { if (IsHost() && !((Object)(object)item == (Object)null) && !IsActiveGlassCargoTarget(item)) { EventBus.OnItemDamaged(item, Math.Max(1, valueLost)); } } internal bool ShouldBlockPressurePlateExtraction(GameObject? extractionPoint) { ContractInstance activeContract = Queue.ActiveContract; if (activeContract?.Definition.Id != "pressure_plate_extraction" || (Object)(object)extractionPoint == (Object)null) { return false; } lastPressurePlateExtractionPoint = extractionPoint; if (!IsActivePressurePlateExtractionPoint(activeContract, extractionPoint)) { return false; } UpdatePressurePlateProgress(activeContract); int num; if (!((Object)(object)pressurePlateRuntime == (Object)null)) { num = ((!pressurePlateRuntime.AllPlatesSecured) ? 1 : 0); if (num == 0) { goto IL_0091; } } else { num = 1; } ignoredBlockedPressurePlateExtractionCompletion = true; if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogInfo((object)"[RepoContracts] Pressure Plate Extraction blocked item turn-in because controlled plates are not secured."); } goto IL_0091; IL_0091: return (byte)num != 0; } internal bool IsProtectedPressurePlateWeight(GameObject item) { if (Queue.ActiveContract?.Definition.Id == "pressure_plate_extraction" && (Object)(object)pressurePlateRuntime != (Object)null) { return pressurePlateRuntime.IsItemOnPlate(item); } return false; } internal void HandleValuableDestroyed(GameObject item) { if (!IsHost() || (Object)(object)item == (Object)null) { return; } string objectId = GetObjectId(item); if (destroyedItemIds.Add(objectId)) { if (IsActiveGlassCargoTarget(item) && IsInExtractionPoint(item)) { extractedItemIds.Add(objectId); EventBus.OnItemExtracted(item, Math.Max(1, GetValuableValue(item))); Plugin.Log.LogInfo((object)"[RepoContracts] Treated Glass Cargo destruction inside extraction as successful extraction."); } else { EventBus.OnItemDestroyed(item); } } } internal void HandleItemGrabbed(GameObject item, GameObject? player) { if (IsHost() && !((Object)(object)item == (Object)null)) { EventBus.OnItemGrabbed(item, player); if (IsActiveGlassCargoTarget(item)) { ClearGlassCargoWorldMarker(clearHud: false); } } } internal void HandleEvent(ContractEvent contractEvent) { ContractInstance activeContract = Queue.ActiveContract; if (activeContract == null || activeContract.Status != ContractStatus.Active) { return; } ContractStatus status = activeContract.Status; if (!activeContract.ApplyEvent(contractEvent)) { return; } hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); if (status != activeContract.Status) { if (activeContract.Status == ContractStatus.Completed) { ResolveActiveContract(completed: true); } else if (activeContract.Status == ContractStatus.Failed) { ResolveActiveContract(completed: false); } } } internal void CompleteLevel(Vector3 rewardPosition) { if (IsGameplayLevel()) { completedGameplayLevels++; RetireResolvedQueueAtLevelBoundary(); } } internal void UpdateActiveWorldContracts() { ContractInstance activeContract = Queue.ActiveContract; if (!(activeContract?.Definition.Id != "pressure_plate_extraction")) { if ((Object)(object)pressurePlateRuntime == (Object)null) { PreparePressurePlateContract(activeContract, IsHost()); } if (IsHost()) { UpdatePressurePlateProgress(activeContract); } } } internal void ClearForShopOrRunReset() { Queue.Clear(); playerVotes.Clear(); ResetGameplayEventCache(); ClearGlassCargoMarker(); ClearPressurePlates(); boardUi.Hide(); hud.Hide(); syncAdapter.SyncCleared(); } private void ResolveActiveContract(bool completed) { //IL_0138: Unknown result type (might be due to invalid IL or missing references) ContractInstance activeContract = Queue.ActiveContract; if (activeContract == null) { return; } if (completed) { hud.NotifyComplete(activeContract); if (Plugin.EnableDisciplineDebt.Value && Queue.ConsumeDisciplineDebt()) { hud.NotifyDiscipline("Discipline paid: reward withheld for " + activeContract.Definition.DisplayName + "."); Plugin.Log.LogInfo((object)$"[RepoContracts] Discipline withheld reward for {activeContract.Definition.DisplayName}. Remaining debt={Queue.DisciplineDebt}."); } else { RewardDeliveryContext rewardDeliveryContext = GetRewardDeliveryContext(); if (!rewardDeliveryContext.CashRewardsUsable && IsCashReward(activeContract.RewardRoll)) { pendingDeferredRewards.Add(activeContract.RewardRoll); hud.NotifyDiscipline("Reward deferred: " + activeContract.Definition.DisplayName); Plugin.Log.LogInfo((object)("[RepoContracts] Deferred cash reward for " + activeContract.Definition.DisplayName + " because " + rewardDeliveryContext.Reason + ".")); } else { rewardAdapter.Grant(activeContract.RewardRoll, FindRewardPosition(), rewardDeliveryContext); } } } else { hud.NotifyFailed(activeContract); ApplyDiscipline(activeContract); } ClearGlassCargoMarker(); ClearPressurePlates(); bool advance = (completed ? Plugin.AdvanceQueueOnCompletion.Value : Plugin.AdvanceQueueOnFailure.Value); ContractInstance contractInstance = Queue.ResolveActive(advance); if (contractInstance != null && contractInstance != activeContract && contractInstance.Status == ContractStatus.Active) { PrepareActiveContract(); hud.NotifyNext(contractInstance); } hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); } private void GrantPendingDeferredRewards() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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) if (pendingDeferredRewards.Count == 0) { return; } Vector3 position = FindRewardPosition(); foreach (RewardRoll item in pendingDeferredRewards.ToList()) { rewardAdapter.Grant(item, position, RewardDeliveryContext.Normal()); pendingDeferredRewards.Remove(item); } hud.NotifyDiscipline("Deferred contract rewards delivered at the board."); } private static bool IsCashReward(RewardRoll reward) { RewardKind kind = reward.Kind; if ((uint)kind <= 1u) { return true; } return false; } private void ApplyDiscipline(ContractInstance failedContract) { if (Plugin.EnableDisciplineDebt.Value) { int disciplineDebt = GetDisciplineDebt(failedContract); if (disciplineDebt > 0) { Queue.AddDisciplineDebt(disciplineDebt); hud.NotifyDiscipline(string.Format("Discipline: {0} future reward{1} withheld.", disciplineDebt, (disciplineDebt == 1) ? string.Empty : "s")); Plugin.Log.LogInfo((object)$"[RepoContracts] Discipline debt added for {failedContract.Definition.DisplayName}: +{disciplineDebt}, total={Queue.DisciplineDebt}."); } } } private void RetireResolvedQueueAtLevelBoundary() { if (Queue.HasUnresolvedContracts) { PrepareActiveContract(); hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); DebugLogQueue("Preserved unfinished queue at level end"); return; } if (!Queue.HasResolvedLockedQueue) { syncAdapter.SyncProgress(Queue); return; } Queue.Clear(preserveDisciplineDebt: true); playerVotes.Clear(); ResetGameplayEventCache(); ClearGlassCargoMarker(); boardUi.Hide(); hud.Hide(); syncAdapter.SyncCleared(); DebugLogQueue("Retired resolved queue at level end"); } private static int GetDisciplineDebt(ContractInstance failedContract) { return Plugin.DisciplineMode.Value switch { DisciplineMode.Off => 0, DisciplineMode.Casual => 1, DisciplineMode.Standard => (failedContract.Definition.Rarity < ContractRarity.Rare) ? 1 : 2, DisciplineMode.Strict => failedContract.Definition.Rarity switch { ContractRarity.Common => 1, ContractRarity.Uncommon => 2, ContractRarity.Rare => 3, ContractRarity.Epic => 4, ContractRarity.Legendary => 5, _ => 2, }, _ => 1, }; } private bool CompleteGlassCargoFromExtractionCandidates() { ContractInstance activeContract = Queue.ActiveContract; if (activeContract?.Definition.Id != "glass_cargo" || activeContract.Status != ContractStatus.Active) { return false; } if (!activeContract.SelectedTargets.TryGetValue("targetObjectId", out string value) || string.IsNullOrWhiteSpace(value)) { return false; } foreach (ExtractionCandidate value2 in pendingExtractionCandidates.Values) { if (!((Object)(object)value2.Item == (Object)null) && !(((Object)value2.Item).GetInstanceID().ToString() != value)) { extractedItemIds.Add(value2.Id); activeContract.Complete(); ResolveActiveContract(completed: true); Plugin.Log.LogInfo((object)"[RepoContracts] Completed Glass Cargo from successful extraction haul."); return true; } } return false; } private void PrepareActiveContract() { ClearGlassCargoMarker(); ClearPressurePlates(); ContractInstance activeContract = Queue.ActiveContract; if (activeContract != null && activeContract.Status == ContractStatus.Active) { if (activeContract.Definition.Id == "glass_cargo") { SelectGlassCargoTarget(activeContract); } else if (activeContract.Definition.Id == "pressure_plate_extraction") { PreparePressurePlateContract(activeContract, isAuthoritative: true); } } } private void SelectGlassCargoTarget(ContractInstance active) { if (active.SelectedTargets.ContainsKey("targetObjectId")) { GameObject val = FindObjectByInstanceId(active.SelectedTargets["targetObjectId"]); if ((Object)(object)val != (Object)null && val.activeInHierarchy) { CreateGlassCargoMarker(val); return; } active.SelectedTargets.Remove("targetObjectId"); active.SelectedTargets.Remove("targetPhotonViewId"); active.SelectedTargets.Remove("targetName"); Plugin.Log.LogInfo((object)"[RepoContracts] Glass Cargo target was no longer in this level. Selecting a new target."); } List list = (from v in Object.FindObjectsOfType() where (Object)(object)v != (Object)null && ((Component)v).gameObject.activeInHierarchy && IsEligibleGlassCargoTarget(((Component)v).gameObject) select v).ToList(); if (list.Count == 0) { Plugin.Log.LogWarning((object)"[RepoContracts] Glass Cargo could not find an eligible breakable valuable to mark."); return; } ValuableObject val2 = list[random.Next(list.Count)]; AssignGlassCargoTarget(active, ((Component)val2).gameObject); } private void AssignGlassCargoTarget(ContractInstance active, GameObject target) { active.SelectedTargets["targetObjectId"] = ((Object)target).GetInstanceID().ToString(); active.SelectedTargets["targetName"] = CleanObjectName(((Object)target).name); PhotonView component = target.GetComponent(); if ((Object)(object)component != (Object)null && component.ViewID != 0) { active.SelectedTargets["targetPhotonViewId"] = component.ViewID.ToString(); } CreateGlassCargoMarker(target); hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); Plugin.Log.LogInfo((object)("[RepoContracts] Glass Cargo target marked: " + ((Object)target).name + ".")); } private void PrepareSyncedVisuals() { ClearGlassCargoMarker(); ClearPressurePlates(); ContractInstance activeContract = Queue.ActiveContract; if (activeContract?.Definition.Id == "pressure_plate_extraction") { PreparePressurePlateContract(activeContract, isAuthoritative: false); } else if (!(activeContract?.Definition.Id != "glass_cargo")) { GameObject val = FindSyncedGlassCargoTarget(activeContract); if ((Object)(object)val != (Object)null) { CreateGlassCargoMarker(val); } } } private void PreparePressurePlateContract(ContractInstance active, bool isAuthoritative) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0155: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_019b: 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_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)pressurePlateRuntime != (Object)null) { return; } if (isAuthoritative && !active.SelectedTargets.ContainsKey("plate0")) { Vector3 val = FindPressurePlateAnchor(); Vector3 forward = Vector3.forward; Vector3 right = Vector3.right; ExtractionPoint val2 = ChoosePressurePlateExtractionPoint(active.Seed); if ((Object)(object)val2 != (Object)null) { forward = ((Component)val2).transform.forward; right = ((Component)val2).transform.right; val = ((Component)val2).transform.position; active.SelectedTargets["targetExtractionPoint"] = GetObjectId(((Component)val2).gameObject); active.SelectedTargets["targetExtractionPosition"] = SerializeVector(val); active.SelectedTargets["targetExtractionName"] = CleanObjectName(((Object)((Component)val2).gameObject).name); } int num = CalculatePressurePlateCount(); Vector3[] array = (Vector3[])(object)new Vector3[4] { right * 2.35f + forward * 0.8f, -right * 2.35f + forward * 0.8f, forward * 2.6f, -forward * 2.6f }; active.SelectedTargets["plateCount"] = num.ToString(); active.SelectedTargets["requiredItemWeight"] = 8f.ToString("0.##"); for (int i = 0; i < num; i++) { Vector3 vector = ProjectToFloor(val + array[i]); active.SelectedTargets[$"plate{i}"] = SerializeVector(vector); } active.SetProgress(0, num); syncAdapter.SyncProgress(Queue); } List list = ReadPressurePlatePositions(active); if (list.Count != 0) { string value; float result; float num2 = ((active.SelectedTargets.TryGetValue("requiredItemWeight", out value) && float.TryParse(value, out result)) ? Math.Max(1f, result) : 8f); Vector3? targetExtractionPosition = null; if (active.SelectedTargets.TryGetValue("targetExtractionPosition", out string value2) && TryParseVector(value2, out var vector2)) { targetExtractionPosition = vector2; } pressurePlateRuntime = PressurePlateContractRuntime.Create(list, num2, isAuthoritative, targetExtractionPosition); hud.ShowActive(Queue); Plugin.Log.LogInfo((object)$"[RepoContracts] Pressure Plate Extraction prepared: plates={list.Count}, itemWeightRequired={num2:0.#}."); } } private void UpdatePressurePlateProgress(ContractInstance active) { if (!((Object)(object)pressurePlateRuntime == (Object)null)) { int securedCount = pressurePlateRuntime.SecuredCount; int num = Math.Max(1, pressurePlateRuntime.PlateCount); if (active.CurrentProgress != securedCount || active.RequiredProgress != num) { active.SetProgress(securedCount, num); hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); } } } private void ClearPressurePlates() { if ((Object)(object)pressurePlateRuntime != (Object)null) { Object.Destroy((Object)(object)((Component)pressurePlateRuntime).gameObject); pressurePlateRuntime = null; } } private void CreateGlassCargoMarker(GameObject target) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0196: 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_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) ClearGlassCargoMarker(); glassCargoMarker = new GameObject("RepoContracts Glass Cargo Marker"); GameObject val = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)val).name = "Beacon"; val.transform.SetParent(glassCargoMarker.transform, false); Object.Destroy((Object)(object)val.GetComponent()); Renderer component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Material val2 = new Material(Shader.Find("GUI/Text Shader") ?? Shader.Find("Standard")); Color color = default(Color); ((Color)(ref color))..ctor(0.2f, 1f, 0.9f, 0.24f); val2.color = color; ConfigureTransparentMaterial(val2); val2.EnableKeyword("_EMISSION"); val2.SetColor("_EmissionColor", new Color(0.2f, 1f, 0.9f, 1f) * 1.2f); component.material = val2; } GameObject val3 = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)val3).name = "ProtectedItemSprite"; val3.transform.SetParent(glassCargoMarker.transform, false); val3.transform.localPosition = Vector3.zero; Collider component2 = val3.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } ContractAssets.ApplyTexture(val3, "markers/items/marker_item_protected.png", Color.white); TextMesh obj = glassCargoMarker.AddComponent(); obj.text = "GLASS CARGO"; obj.anchor = (TextAnchor)4; obj.alignment = (TextAlignment)1; obj.fontSize = 64; obj.characterSize = 0.04f; obj.color = new Color(0.7f, 1f, 0.95f, 0.78f); Bounds val4 = CalculateBounds(target); glassCargoMarker.transform.SetParent(target.transform, true); glassCargoMarker.transform.position = ((Bounds)(ref val4)).center; glassCargoMarker.AddComponent(); Transform transform = val.transform; Vector3 one = Vector3.one; Vector3 extents = ((Bounds)(ref val4)).extents; transform.localScale = one * Mathf.Max(0.28f, Mathf.Min(0.75f, ((Vector3)(ref extents)).magnitude * 0.32f)); Transform transform2 = val3.transform; Vector3 one2 = Vector3.one; extents = ((Bounds)(ref val4)).extents; transform2.localScale = one2 * Mathf.Max(0.9f, Mathf.Min(2.3f, ((Vector3)(ref extents)).magnitude * 0.95f)); ((Component)obj).transform.localPosition = Vector3.up * Mathf.Max(0.45f, ((Bounds)(ref val4)).extents.y + 0.35f); hud.SetTrackedTarget(target, CleanObjectName(((Object)target).name)); } private void ClearGlassCargoMarker() { ClearGlassCargoWorldMarker(clearHud: true); } private void ClearGlassCargoWorldMarker(bool clearHud) { if ((Object)(object)glassCargoMarker != (Object)null) { Object.Destroy((Object)(object)glassCargoMarker); glassCargoMarker = null; } if (clearHud) { hud.SetTrackedTarget(null, string.Empty); } } private static Bounds CalculateBounds(GameObject target) { //IL_0024: 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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) Renderer[] componentsInChildren = target.GetComponentsInChildren(); if (componentsInChildren.Length == 0) { return new Bounds(target.transform.position, Vector3.one); } Bounds bounds = componentsInChildren[0].bounds; for (int i = 1; i < componentsInChildren.Length; i++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds); } return bounds; } private int CalculatePressurePlateCount() { int num = 1; if (SemiFunc.IsMultiplayer() && PhotonNetwork.PlayerList != null) { num = Math.Max(1, PhotonNetwork.PlayerList.Length); } int num2 = completedGameplayLevels; int num3 = ((num2 >= 6) ? 2 : ((num2 >= 3) ? 1 : 0)); int num4 = num3; return Math.Max(1, Math.Min(4, num + num4)); } private static ExtractionPoint? ChoosePressurePlateExtractionPoint(int seed) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) ExtractionPoint[] array = (from point in Object.FindObjectsOfType() where (Object)(object)point != (Object)null orderby ((Component)point).transform.position.x, ((Component)point).transform.position.y, ((Component)point).transform.position.z select point).ToArray(); if (array.Length == 0) { return FindNearestExtractionPoint(FindPressurePlateAnchor()); } Random random = new Random(seed); return array[random.Next(array.Length)]; } private static bool IsActivePressurePlateExtractionPoint(ContractInstance active, GameObject? extractionPoint) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)extractionPoint == (Object)null) { return false; } if (active.SelectedTargets.TryGetValue("targetExtractionPoint", out string value) && GetObjectId(extractionPoint) == value) { return true; } if (active.SelectedTargets.TryGetValue("targetExtractionPosition", out string value2) && TryParseVector(value2, out var vector)) { return Vector3.Distance(extractionPoint.transform.position, vector) <= 2f; } return false; } private static Vector3 FindPressurePlateAnchor() { //IL_0017: 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_0034: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)PlayerAvatar.instance != (Object)null) { return ((Component)PlayerAvatar.instance).transform.position; } if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null) { return ((Component)TruckSafetySpawnPoint.instance).transform.position; } return Vector3.zero; } private static ExtractionPoint? FindNearestExtractionPoint(Vector3 position) { //IL_000d: 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_0058: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)RoundDirector.instance != (Object)null) { ExtractionPoint val = SemiFunc.ExtractionPointGetNearest(position); if ((Object)(object)val != (Object)null) { return val; } } } catch { } ExtractionPoint[] array = Object.FindObjectsOfType(); ExtractionPoint result = null; float num = float.MaxValue; ExtractionPoint[] array2 = array; foreach (ExtractionPoint val2 in array2) { if (!((Object)(object)val2 == (Object)null)) { float num2 = Vector3.SqrMagnitude(((Component)val2).transform.position - position); if (num2 < num) { result = val2; num = num2; } } } return result; } private static Vector3 ProjectToFloor(Vector3 position) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) RaycastHit val = default(RaycastHit); if (Physics.Raycast(position + Vector3.up * 5f, Vector3.down, ref val, 20f, -1, (QueryTriggerInteraction)1)) { return ((RaycastHit)(ref val)).point + Vector3.up * 0.04f; } return position; } private static string SerializeVector(Vector3 vector) { return string.Join(",", vector.x.ToString("0.###", CultureInfo.InvariantCulture), vector.y.ToString("0.###", CultureInfo.InvariantCulture), vector.z.ToString("0.###", CultureInfo.InvariantCulture)); } private static List ReadPressurePlatePositions(ContractInstance active) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) List list = new List(); int result = 0; if (active.SelectedTargets.TryGetValue("plateCount", out string value)) { int.TryParse(value, out result); } result = Math.Max(result, active.SelectedTargets.Keys.Count((string k) => k.StartsWith("plate", StringComparison.Ordinal) && k != "plateCount")); for (int i = 0; i < result; i++) { if (active.SelectedTargets.TryGetValue($"plate{i}", out string value2) && TryParseVector(value2, out var vector)) { list.Add(vector); } } return list; } private static bool TryParseVector(string value, out Vector3 vector) { //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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) vector = Vector3.zero; string[] array = value.Split(','); if (array.Length != 3) { return false; } if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { return false; } vector = new Vector3(result, result2, result3); return true; } private void EnsureDefaultSelectionIfEmpty() { if (Queue.SelectedContracts.Count <= 0) { int num = Math.Min(Plugin.SelectedContractsPerLevel.Value, Queue.OfferedContracts.Count); for (int i = 0; i < num; i++) { Queue.SelectOffer(Queue.OfferedContracts[i].DefinitionId); } } } private void PublishLocalPhotonVote(string[] ranks) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (SemiFunc.IsMultiplayer() && PhotonNetwork.LocalPlayer != null) { Hashtable val = new Hashtable(); val[(object)"RepoContractsVoteSession"] = Queue.VoteSessionId; for (int i = 0; i < Plugin.SelectedContractsPerLevel.Value; i++) { val[(object)string.Format("{0}{1}", "RepoContractsVote", i + 1)] = ((i < ranks.Length) ? (ranks[i] ?? string.Empty) : string.Empty); } PhotonNetwork.LocalPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } } private void PullPhotonVotes() { if (!SemiFunc.IsMultiplayer() || PhotonNetwork.PlayerList == null) { return; } playerVotes.Clear(); Player[] playerList = PhotonNetwork.PlayerList; foreach (Player val in playerList) { if (val.CustomProperties == null || !((Dictionary)(object)val.CustomProperties).TryGetValue((object)"RepoContractsVoteSession", out object value) || !(value is string text) || text != Queue.VoteSessionId) { continue; } string[] array = new string[Plugin.SelectedContractsPerLevel.Value]; for (int j = 0; j < array.Length; j++) { object key = string.Format("{0}{1}", "RepoContractsVote", j + 1); if (((Dictionary)(object)val.CustomProperties).TryGetValue(key, out object value2) && value2 is string text2) { array[j] = text2; } } if (array.Any((string r) => !string.IsNullOrWhiteSpace(r))) { playerVotes[$"player-{val.ActorNumber}"] = array; } } } private bool AllPlayersHaveCompleteVotes() { int num = 1; if (SemiFunc.IsMultiplayer() && PhotonNetwork.PlayerList != null) { num = Math.Max(1, PhotonNetwork.PlayerList.Length); } if (playerVotes.Count < num) { return false; } foreach (string[] value in playerVotes.Values) { if (value.Length < Plugin.SelectedContractsPerLevel.Value) { return false; } for (int i = 0; i < Plugin.SelectedContractsPerLevel.Value; i++) { if (string.IsNullOrWhiteSpace(value[i])) { return false; } } } return true; } private bool ShouldOfferMilestone() { if (Plugin.EnableMilestoneContracts.Value && Plugin.MilestoneContractEveryXLevels.Value > 0 && completedGameplayLevels > 0) { return completedGameplayLevels % Plugin.MilestoneContractEveryXLevels.Value == 0; } return false; } private static bool IsHost() { if (SemiFunc.IsMultiplayer()) { return SemiFunc.IsMasterClientOrSingleplayer(); } return true; } internal static bool IsGameplayLevel() { RunManager instance = RunManager.instance; if ((Object)(object)instance != (Object)null && instance.levels.Contains(instance.levelCurrent) && (Object)(object)instance.levelCurrent != (Object)(object)instance.levelTutorial) { List levelShop = instance.levelShop; if (levelShop == null || !levelShop.Contains(instance.levelCurrent)) { return (Object)(object)instance.levelCurrent != (Object)(object)instance.levelLobbyMenu; } } return false; } private Vector3 FindRewardPosition() { //IL_0017: 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_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_006a: Unknown result type (might be due to invalid IL or missing references) //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) Vector3? rewardSpawnPosition = boardUi.GetRewardSpawnPosition(); if (rewardSpawnPosition.HasValue) { return rewardSpawnPosition.Value; } if ((Object)(object)TruckHealer.instance != (Object)null) { return ((Component)TruckHealer.instance).transform.position + Vector3.up * 0.75f; } if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null) { return ((Component)TruckSafetySpawnPoint.instance).transform.position + Vector3.up * 0.75f; } return Vector3.zero; } private static RewardDeliveryContext GetRewardDeliveryContext() { RoundDirector instance = RoundDirector.instance; if ((Object)(object)instance == (Object)null) { return RewardDeliveryContext.Normal(); } bool boolField = GetBoolField(ExtractionPointActiveField, instance); int intField = GetIntField(ExtractionPointsField, instance); int intField2 = GetIntField(ExtractionPointsCompletedField, instance); bool flag = boolField && intField > 0 && intField2 >= intField - 1; if (GetBoolField(AllExtractionPointsCompletedField, instance) || (intField > 0 && intField2 >= intField)) { return RewardDeliveryContext.CashUnsafe("all extraction points completed"); } if (flag) { return RewardDeliveryContext.CashUnsafe("final extraction active"); } return RewardDeliveryContext.Normal(); } private static bool GetBoolField(FieldInfo? field, object target) { object obj = field?.GetValue(target); 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 int GetIntField(FieldInfo? field, object target) { object obj = field?.GetValue(target); if (obj is int) { return (int)obj; } return 0; } private void ResetGameplayEventCache() { pendingExtractionCandidates.Clear(); extractedItemIds.Clear(); killedEnemyIds.Clear(); deadPlayerIds.Clear(); destroyedItemIds.Clear(); } private static int GetValuableValue(GameObject item) { ValuableObject component = item.GetComponent(); if ((Object)(object)component == (Object)null || ValuableCurrentValueField == null) { return 0; } if (!(ValuableCurrentValueField.GetValue(component) is float num)) { return 0; } return Mathf.RoundToInt(num); } private static bool IsEligibleGlassCargoTarget(GameObject item) { if (GetValuableValue(item) <= 0) { return false; } PhysGrabObjectImpactDetector component = item.GetComponent(); if ((Object)(object)component != (Object)null && !component.destroyDisable) { return component.durability > 0f; } return false; } private bool IsActiveGlassCargoTarget(GameObject item) { ContractInstance activeContract = Queue.ActiveContract; if (activeContract?.Definition.Id == "glass_cargo" && activeContract.SelectedTargets.TryGetValue("targetObjectId", out string value)) { return ((Object)item).GetInstanceID().ToString() == value; } return false; } private static bool IsInExtractionPoint(GameObject item) { RoomVolumeCheck component = item.GetComponent(); if ((Object)(object)component == (Object)null || RoomVolumeInExtractionField == null) { return false; } component.CheckSet(); object value = RoomVolumeInExtractionField.GetValue(component); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; 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; } private static GameObject? FindObjectByInstanceId(string instanceId) { ValuableObject[] array = Object.FindObjectsOfType(); foreach (ValuableObject val in array) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).GetInstanceID().ToString() == instanceId) { return ((Component)val).gameObject; } } return null; } private static GameObject? FindSyncedGlassCargoTarget(ContractInstance active) { if (active.SelectedTargets.TryGetValue("targetPhotonViewId", out string value) && int.TryParse(value, out var result)) { PhotonView val = PhotonView.Find(result); if ((Object)(object)val != (Object)null) { return ((Component)val).gameObject; } } if (active.SelectedTargets.TryGetValue("targetName", out string value2) && !string.IsNullOrWhiteSpace(value2)) { ValuableObject[] array = Object.FindObjectsOfType(); foreach (ValuableObject val2 in array) { if ((Object)(object)val2 != (Object)null && CleanObjectName(((Object)((Component)val2).gameObject).name) == value2) { return ((Component)val2).gameObject; } } } return null; } private static string CleanObjectName(string objectName) { return objectName.Replace("(Clone)", string.Empty).Trim(); } private static string GetObjectId(GameObject gameObject) { PhotonView component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null && component.ViewID != 0) { return $"photon-{component.ViewID}"; } return $"instance-{((Object)gameObject).GetInstanceID()}"; } private void DebugLogQueue(string label) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogInfo((object)string.Format("[RepoContracts] {0}: offered={1}, selected={2}, active={3}", label, Queue.OfferedContracts.Count, Queue.SelectedContracts.Count, Queue.ActiveContract?.Definition.DisplayName ?? "none")); } } } internal sealed class ContractQueue { private readonly List offeredContracts = new List(); private readonly List selectedContracts = new List(); private readonly List completedContracts = new List(); private readonly List failedContracts = new List(); internal IReadOnlyList OfferedContracts => offeredContracts; internal IReadOnlyList SelectedContracts => selectedContracts; internal IReadOnlyList CompletedContracts => completedContracts; internal IReadOnlyList FailedContracts => failedContracts; internal int ActiveIndex { get; private set; } = -1; internal bool IsLocked { get; private set; } internal int DisciplineDebt { get; private set; } internal string VoteSessionId { get; private set; } = string.Empty; internal ContractInstance? ActiveContract { get { if (ActiveIndex < 0 || ActiveIndex >= selectedContracts.Count) { return null; } return selectedContracts[ActiveIndex]; } } internal bool HasUnresolvedContracts { get { if (IsLocked) { return selectedContracts.Any(delegate(ContractInstance c) { ContractStatus status = c.Status; return (uint)(status - 1) <= 1u; }); } return false; } } internal bool HasResolvedLockedQueue { get { if (IsLocked && selectedContracts.Count > 0) { return !HasUnresolvedContracts; } return false; } } internal void SetOffers(IEnumerable offers) { Clear(preserveDisciplineDebt: true); VoteSessionId = Guid.NewGuid().ToString("N"); offeredContracts.AddRange(offers.Take(Plugin.OfferedContractsPerLevel.Value)); } internal bool SelectOffer(string definitionId) { string definitionId2 = definitionId; if (IsLocked || selectedContracts.Count >= Plugin.SelectedContractsPerLevel.Value) { return false; } ContractInstance contractInstance = offeredContracts.FirstOrDefault((ContractInstance c) => c.DefinitionId == definitionId2); if (contractInstance == null || selectedContracts.Any((ContractInstance c) => c.DefinitionId == definitionId2)) { return false; } contractInstance.SetStatus(ContractStatus.Queued); selectedContracts.Add(contractInstance); return true; } internal bool RemoveSelection(int selectedIndex) { if (IsLocked || selectedIndex < 0 || selectedIndex >= selectedContracts.Count) { return false; } selectedContracts[selectedIndex].SetStatus(ContractStatus.Offered); selectedContracts.RemoveAt(selectedIndex); return true; } internal bool MoveSelection(int fromIndex, int toIndex) { if (IsLocked || fromIndex < 0 || fromIndex >= selectedContracts.Count || toIndex < 0 || toIndex >= selectedContracts.Count) { return false; } ContractInstance item = selectedContracts[fromIndex]; selectedContracts.RemoveAt(fromIndex); selectedContracts.Insert(toIndex, item); return true; } internal bool LockAndActivateFirst() { if (IsLocked || selectedContracts.Count == 0) { return false; } IsLocked = true; ActiveIndex = 0; selectedContracts[0].SetStatus(ContractStatus.Active); return true; } internal ContractInstance? ResolveActive(bool advance) { ContractInstance activeContract = ActiveContract; if (activeContract == null) { return null; } if (activeContract.Status == ContractStatus.Completed) { completedContracts.Add(activeContract); } else { if (activeContract.Status != ContractStatus.Failed) { return activeContract; } failedContracts.Add(activeContract); } if (!advance) { return activeContract; } ActiveIndex++; ContractInstance? activeContract2 = ActiveContract; if (activeContract2 != null) { activeContract2.SetStatus(ContractStatus.Active); return activeContract2; } return activeContract2; } internal void Clear(bool preserveDisciplineDebt = false) { int disciplineDebt = DisciplineDebt; offeredContracts.Clear(); selectedContracts.Clear(); completedContracts.Clear(); failedContracts.Clear(); ActiveIndex = -1; IsLocked = false; DisciplineDebt = (preserveDisciplineDebt ? disciplineDebt : 0); VoteSessionId = string.Empty; } internal void ApplySnapshot(IEnumerable offers, IEnumerable selected, int activeIndex, bool isLocked, int disciplineDebt = 0, string voteSessionId = "") { offeredContracts.Clear(); selectedContracts.Clear(); completedContracts.Clear(); failedContracts.Clear(); offeredContracts.AddRange(offers.Take(Plugin.OfferedContractsPerLevel.Value)); selectedContracts.AddRange(selected.Take(Plugin.SelectedContractsPerLevel.Value)); ActiveIndex = activeIndex; IsLocked = isLocked; DisciplineDebt = Math.Max(0, disciplineDebt); VoteSessionId = voteSessionId ?? string.Empty; foreach (ContractInstance selectedContract in selectedContracts) { if (selectedContract.Status == ContractStatus.Completed) { completedContracts.Add(selectedContract); } else if (selectedContract.Status == ContractStatus.Failed) { failedContracts.Add(selectedContract); } } } internal void AddDisciplineDebt(int amount) { DisciplineDebt = Math.Max(0, DisciplineDebt + amount); } internal bool ConsumeDisciplineDebt() { if (DisciplineDebt <= 0) { return false; } DisciplineDebt--; return true; } } internal sealed class PressurePlateContractRuntime : MonoBehaviour { private sealed class PlateState { internal GameObject GameObject { get; } internal TextMesh Label { get; } internal Light Light { get; } internal bool Secured { get; set; } internal PlateState(GameObject gameObject, TextMesh label, Light light) { GameObject = gameObject; Label = label; Light = light; } } private readonly List plates = new List(); private float requiredItemWeight = 8f; private bool authoritative; private GameObject? extractionMarker; internal int PlateCount => plates.Count; internal int SecuredCount => plates.Count((PlateState p) => p.Secured); internal bool AllPlatesSecured { get { if (plates.Count > 0) { return plates.All((PlateState p) => p.Secured); } return false; } } internal static PressurePlateContractRuntime Create(IReadOnlyList positions, float requiredItemWeight, bool authoritative, Vector3? targetExtractionPosition) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) PressurePlateContractRuntime pressurePlateContractRuntime = new GameObject("RepoContracts Pressure Plate Runtime").AddComponent(); pressurePlateContractRuntime.requiredItemWeight = Mathf.Max(1f, requiredItemWeight); pressurePlateContractRuntime.authoritative = authoritative; pressurePlateContractRuntime.Build(positions); pressurePlateContractRuntime.BuildExtractionMarker(targetExtractionPosition); return pressurePlateContractRuntime; } internal bool IsItemOnPlate(GameObject item) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)item == (Object)null) { return false; } foreach (PlateState plate in plates) { if (IsSubjectOnPlate(item, plate.GameObject.transform.position)) { return true; } } return false; } private void Build(IReadOnlyList positions) { //IL_0040: 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_004f: 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_0073: 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_00cb: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < positions.Count; i++) { GameObject val = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)val).name = $"RepoContracts Pressure Plate {i + 1}"; val.transform.SetParent(((Component)this).transform, true); val.transform.position = positions[i] + Vector3.up * 0.03f; val.transform.localScale = new Vector3(1.55f, 0.08f, 1.55f); Collider component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.isTrigger = true; } ContractAssets.ApplyTexture(val, "textures/world/texture_pressure_plate_unlit.png", new Color(1f, 0.9f, 0.55f, 0.9f)); GameObject val2 = new GameObject($"RepoContracts Pressure Plate Label {i + 1}"); val2.transform.SetParent(val.transform, false); val2.transform.localPosition = new Vector3(-0.48f, 0.62f, -0.48f); TextMesh val3 = val2.AddComponent(); val3.anchor = (TextAnchor)4; val3.alignment = (TextAlignment)1; val3.fontSize = 40; val3.characterSize = 0.035f; val3.color = new Color(0.65f, 1f, 0.92f, 0.9f); val3.text = "PLATE"; Light val4 = val.AddComponent(); val4.type = (LightType)2; val4.range = 2.2f; val4.intensity = 0.35f; val4.color = new Color(0.1f, 0.95f, 0.85f); plates.Add(new PlateState(val, val3, val4)); } } private void BuildExtractionMarker(Vector3? targetExtractionPosition) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_003e: 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_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_0099: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0138: 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) if (targetExtractionPosition.HasValue) { extractionMarker = new GameObject("RepoContracts Controlled Extraction Marker"); extractionMarker.transform.SetParent(((Component)this).transform, true); extractionMarker.transform.position = targetExtractionPosition.Value + Vector3.up * 1.1f; GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "Controlled Extraction Panel"; obj.transform.SetParent(extractionMarker.transform, false); obj.transform.localScale = new Vector3(2.2f, 0.08f, 0.55f); obj.transform.localPosition = Vector3.zero; Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } ContractAssets.ApplyTexture(obj, "textures/world/texture_pressure_plate_lit.png", new Color(0.35f, 1f, 0.88f, 0.68f)); TextMesh obj2 = extractionMarker.AddComponent(); obj2.text = "PLATES CONTROL THIS EXTRACTION"; obj2.anchor = (TextAnchor)4; obj2.alignment = (TextAlignment)1; obj2.fontSize = 44; obj2.characterSize = 0.035f; obj2.color = new Color(0.72f, 1f, 0.92f, 0.95f); Light obj3 = extractionMarker.AddComponent(); obj3.type = (LightType)2; obj3.range = 3.5f; obj3.intensity = 0.7f; obj3.color = new Color(0.15f, 1f, 0.8f); } } private void Update() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0220: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < plates.Count; i++) { PlateState plateState = plates[i]; float num = CalculateItemWeightOnPlate(plateState.GameObject.transform.position); bool flag = IsPlayerOnPlate(plateState.GameObject.transform.position); plateState.Secured = flag || num >= requiredItemWeight; float num2 = 1f + Mathf.Sin(Time.time * 4f + (float)i) * 0.04f; plateState.GameObject.transform.localScale = new Vector3(1.55f * num2, 0.08f, 1.55f * num2); Renderer component = plateState.GameObject.GetComponent(); if ((Object)(object)component != (Object)null) { component.material.color = (plateState.Secured ? new Color(0.55f, 1f, 0.78f, 0.95f) : new Color(1f, 0.82f, 0.25f, 0.84f)); } plateState.Label.text = (plateState.Secured ? "SECURED" : $"{num:0.#}/{requiredItemWeight:0.#}"); plateState.Label.color = (plateState.Secured ? new Color(0.62f, 1f, 0.72f, 0.95f) : new Color(1f, 0.9f, 0.45f, 0.95f)); plateState.Light.color = (plateState.Secured ? new Color(0.2f, 1f, 0.45f) : new Color(1f, 0.75f, 0.15f)); plateState.Light.intensity = (plateState.Secured ? 0.9f : 0.35f); } if ((Object)(object)extractionMarker != (Object)null && (Object)(object)Camera.main != (Object)null) { extractionMarker.transform.rotation = Quaternion.LookRotation(extractionMarker.transform.position - ((Component)Camera.main).transform.position, Vector3.up); } } private static bool IsPlayerOnPlate(Vector3 platePosition) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_004e: 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) PlayerAvatar[] array = Object.FindObjectsOfType(); foreach (PlayerAvatar val in array) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy) { continue; } Vector3 val2 = ((Component)val).transform.position - platePosition; if (Mathf.Abs(val2.y) <= 2.2f) { Vector2 val3 = new Vector2(val2.x, val2.z); if (((Vector2)(ref val3)).magnitude <= 0.95f) { return true; } } } return false; } private static float CalculateItemWeightOnPlate(Vector3 platePosition) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: 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_0024: 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) Collider[] array = Physics.OverlapBox(platePosition + Vector3.up * 0.45f, new Vector3(0.78f, 0.55f, 0.78f), Quaternion.identity, -1, (QueryTriggerInteraction)2); HashSet hashSet = new HashSet(); float num = 0f; Collider[] array2 = array; foreach (Collider val in array2) { if (!((Object)(object)val == (Object)null)) { PhysGrabObject componentInParent = ((Component)val).GetComponentInParent(); ValuableObject componentInParent2 = ((Component)val).GetComponentInParent(); Rigidbody val2 = val.attachedRigidbody ?? ((Component)val).GetComponentInParent(); GameObject val3 = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).gameObject : (((Object)(object)componentInParent2 != (Object)null) ? ((Component)componentInParent2).gameObject : (((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : null))); if (!((Object)(object)val3 == (Object)null) && !((Object)(object)val3.GetComponent() != (Object)null) && hashSet.Add(val3)) { num += EstimateItemWeight(val3, val2); } } } return num; } private static bool IsSubjectOnPlate(GameObject subject, Vector3 platePosition) { //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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) Vector3 val = CalculateSubjectCenter(subject) - platePosition; if (Mathf.Abs(val.y) <= 1.25f) { Vector2 val2 = new Vector2(val.x, val.z); return ((Vector2)(ref val2)).magnitude <= 1.08f; } return false; } private static Vector3 CalculateSubjectCenter(GameObject subject) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_003f: Unknown result type (might be due to invalid IL or missing references) Renderer[] componentsInChildren = subject.GetComponentsInChildren(); if (componentsInChildren.Length == 0) { return subject.transform.position; } Bounds bounds = componentsInChildren[0].bounds; for (int i = 1; i < componentsInChildren.Length; i++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds); } return ((Bounds)(ref bounds)).center; } private static float EstimateItemWeight(GameObject subject, Rigidbody? rigidbody) { //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_0063: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rigidbody != (Object)null && rigidbody.mass > 0f) { return Mathf.Clamp(rigidbody.mass, 0.5f, 30f); } Renderer[] componentsInChildren = subject.GetComponentsInChildren(); if (componentsInChildren.Length == 0) { if (!((Object)(object)subject.GetComponent() != (Object)null)) { return 1f; } return 4f; } Bounds bounds = componentsInChildren[0].bounds; for (int i = 1; i < componentsInChildren.Length; i++) { ((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds); } float num = ((Bounds)(ref bounds)).size.x * ((Bounds)(ref bounds)).size.y * ((Bounds)(ref bounds)).size.z; float num2 = (((Object)(object)subject.GetComponent() != (Object)null) ? 2f : 0f); return Mathf.Clamp(num * 2.8f + num2, 0.5f, 30f); } } internal readonly struct RewardRoll { internal RewardKind Kind { get; } internal ContractRarity Rarity { get; } internal int Amount { get; } internal string RewardId { get; } internal string PreviewText { get; } internal RewardRoll(RewardKind kind, ContractRarity rarity, int amount, string rewardId, string previewText) { Kind = kind; Rarity = rarity; Amount = amount; RewardId = rewardId; PreviewText = previewText; } internal static RewardRoll MoneyBag(ContractRarity rarity, int amount, string previewText) { return new RewardRoll(RewardKind.MoneyBag, rarity, amount, string.Empty, previewText); } internal static RewardRoll ShopItem(ContractRarity rarity, int fallbackCashAmount, string previewText) { return new RewardRoll(RewardKind.ShopItem, rarity, fallbackCashAmount, "shop_item", previewText); } internal static RewardRoll Weapon(ContractRarity rarity, int fallbackCashAmount, string previewText) { return new RewardRoll(RewardKind.Weapon, rarity, fallbackCashAmount, "weapon", previewText); } internal static RewardRoll Upgrade(ContractRarity rarity, int fallbackCashAmount, string previewText) { return new RewardRoll(RewardKind.UpgradeDiscount, rarity, fallbackCashAmount, "upgrade", previewText); } internal static RewardRoll CosmeticToken(ContractRarity rarity, int fallbackCashAmount, string previewText) { return new RewardRoll(RewardKind.CosmeticToken, rarity, fallbackCashAmount, "cosmetic_token", previewText); } internal static RewardRoll Stub(RewardKind kind, ContractRarity rarity, string rewardId, string previewText) { return new RewardRoll(kind, rarity, 1, rewardId, previewText); } } } namespace RepoContracts.Adapters { internal static class HookAudit { internal static void LogKnownHookStatus() { if (Plugin.EnableDebugLogging.Value) { Check(typeof(LevelGenerator), "GenerateDone", "level start after generation"); Check(typeof(HurtCollider), "EnemyHurt", "enemy damaged via hurt collider"); Check(typeof(HurtCollider), "PlayerHurt", "player damaged via hurt collider"); Check(typeof(EnemyHealth), "DeathImpulseRPC", "enemy killed after death impulse"); Check(typeof(PlayerAvatar), "PlayerDeathRPC", "player death"); Check(typeof(RoundDirector), "HaulCheck", "successful extraction candidate capture"); Check(typeof(RoundDirector), "ExtractionCompleted", "successful extraction completion"); Check(typeof(PhysGrabObjectImpactDetector), "BreakRPC", "valuable damage"); Check(typeof(PhysGrabObjectImpactDetector), "DestroyObjectRPC", "valuable destroyed"); Check(typeof(TruckHealer), "Heal", "truck healing"); } } internal static MethodInfo? TryMethod(Type type, string methodName, string label) { MethodInfo methodInfo = FindMethod(type, methodName); if (methodInfo == null && Plugin.EnableDebugLogging.Value) { Plugin.Log.LogWarning((object)("[RepoContracts] Hook TODO: " + label + " target " + type.Name + "." + methodName + " was not found. Adapter will stay inactive.")); } return methodInfo; } private static void Check(Type type, string methodName, string label) { MethodInfo methodInfo = FindMethod(type, methodName); Plugin.Log.LogInfo((object)((methodInfo != null) ? ("[RepoContracts] Verified hook candidate: " + type.Name + "." + methodName + " (" + label + ").") : ("[RepoContracts] Missing hook candidate: " + type.Name + "." + methodName + " (" + label + ")."))); } private static MethodInfo? FindMethod(Type type, string methodName) { return type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } } }