using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using RepoContracts.Adapters; using RepoContracts.Contracts; using RepoContracts.Networking; using RepoContracts.Patches; using RepoContracts.Rewards; using RepoContracts.UI; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RepoContracts")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Optional contract objectives and tiered rewards for R.E.P.O.")] [assembly: AssemblyFileVersion("0.5.16.0")] [assembly: AssemblyInformationalVersion("0.5.16")] [assembly: AssemblyProduct("RepoContracts")] [assembly: AssemblyTitle("RepoContracts")] [assembly: AssemblyVersion("0.5.16.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace RepoContracts { [BepInPlugin("com.amgru.repocontracts", "RepoContracts", "0.5.16")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "com.amgru.repocontracts"; public const string PluginName = "RepoContracts"; public const string PluginVersion = "0.5.16"; internal static ConfigEntry 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.16 loaded."); } private void Update() { Manager?.PollSyncedState(); Manager?.UpdateActiveWorldContracts(); } private void OnDestroy() { Harmony? obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } internal static void SaveSettings() { ((BaseUnityPlugin)Instance).Config.Save(); } } } namespace RepoContracts.Patches { [HarmonyPatch(typeof(LevelGenerator), "GenerateDone")] internal static class LevelGeneratorGenerateDonePatch { private static void Postfix() { if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } Plugin.Manager.GenerateOffersAtTruck(); if (Plugin.Manager.Queue.OfferedContracts.Count != 0) { Plugin.Manager.EventBus.OnLevelStarted(); if (Plugin.AutoLockFirstOffersForDebug.Value) { Plugin.Manager.HostLockQueue(); } } } } [HarmonyPatch(typeof(HurtCollider), "EnemyHurt")] internal static class ContractEnemyHurtPatch { private static void Postfix(HurtCollider __instance, Enemy _enemy, bool __result) { if (__result && SemiFunc.IsMasterClientOrSingleplayer() && (Object)(object)_enemy != (Object)null) { Plugin.Manager.EventBus.OnEnemyDamaged(((Component)_enemy).gameObject, 1, "hurt-collider"); } } } [HarmonyPatch(typeof(HurtCollider), "PlayerHurt")] internal static class ContractPlayerHurtPatch { private static void Postfix(PlayerAvatar _player) { if (SemiFunc.IsMasterClientOrSingleplayer() && (Object)(object)_player != (Object)null) { Plugin.Manager.EventBus.OnPlayerDamaged(((Component)_player).gameObject); } } } [HarmonyPatch(typeof(EnemyHealth), "DeathImpulseRPC")] internal static class ContractEnemyKilledPatch { private static void Postfix(EnemyHealth __instance) { if (SemiFunc.IsMasterClientOrSingleplayer() && !((Object)(object)__instance == (Object)null)) { Enemy component = ((Component)__instance).GetComponent(); 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; } } internal static void ApplyTextMaterial(TextMesh text, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) text.color = color; Renderer component = ((Component)text).GetComponent(); if ((Object)(object)component == (Object)null) { return; } Material val = (((Object)(object)text.font != (Object)null) ? text.font.material : null); Shader val2 = Shader.Find("GUI/Text Shader"); if (!((Object)(object)val == (Object)null) || !((Object)(object)val2 == (Object)null)) { Material val3 = (((Object)(object)val != (Object)null) ? new Material(val) : new Material(val2)); if ((Object)(object)val2 != (Object)null) { val3.shader = val2; } if ((Object)(object)val != (Object)null && (Object)(object)val.mainTexture != (Object)null) { val3.mainTexture = val.mainTexture; } val3.color = color; val3.renderQueue = 3000; component.material = val3; } } private static string FindAssetPath(string relativePath) { string text = relativePath.Replace('/', Path.DirectorySeparatorChar); string text2 = Path.Combine(Paths.PluginPath, "RepoContracts", "assets", text); if (File.Exists(text2)) { return text2; } string text3 = Path.Combine(Path.GetDirectoryName(typeof(Plugin).Assembly.Location) ?? Paths.PluginPath, "assets", text); if (File.Exists(text3)) { return text3; } return Path.Combine(Paths.GameRootPath, "assets", "repo_contracts", text); } private static bool LoadImage(Texture2D texture, byte[] bytes) { MethodInfo methodInfo = (Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule") ?? Type.GetType("UnityEngine.ImageConversion, UnityEngine.CoreModule") ?? Type.GetType("UnityEngine.ImageConversion, UnityEngine"))?.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3] { typeof(Texture2D), typeof(byte[]), typeof(bool) }, null); if (methodInfo == null) { Plugin.Log.LogWarning((object)"[RepoContracts] UnityEngine.ImageConversion.LoadImage was unavailable; packaged PNG assets cannot be decoded."); return false; } object obj = methodInfo.Invoke(null, new object[3] { texture, bytes, false }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static void ConfigureTransparentMaterial(Material material) { material.SetFloat("_Mode", 3f); material.SetInt("_SrcBlend", 5); material.SetInt("_DstBlend", 10); material.SetInt("_ZWrite", 0); material.DisableKeyword("_ALPHATEST_ON"); material.EnableKeyword("_ALPHABLEND_ON"); material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); material.renderQueue = 3000; } } internal sealed class ContractBoardUi : MonoBehaviour { private readonly List 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 readableSideSign = -1f; private float controllerMoveCooldownUntil; internal void Show(ContractQueue contractQueue) { queue = contractQueue; EnsureBoard(); RebuildBoardText(); LogBoard("show"); } internal void Refresh(ContractQueue contractQueue) { queue = contractQueue; EnsureBoard(); RebuildBoardText(); LogBoard("refresh"); } internal void Hide() { if ((Object)(object)boardRoot != (Object)null) { Object.Destroy((Object)(object)boardRoot); boardRoot = null; } cards.Clear(); headerText = null; selectedText = null; promptText = null; highlightedIndex = -1; } internal Vector3? GetRewardSpawnPosition() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)boardRoot == (Object)null) { return null; } return boardRoot.transform.position - boardRoot.transform.forward * (1.15f * Plugin.BoardScale.Value) + Vector3.up * (0.2f * Plugin.BoardScale.Value); } private void Update() { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)boardRoot != (Object)null) { UpdatePlacementMode(); if (placementMode) { return; } } if (queue == null || (Object)(object)boardRoot == (Object)null || queue.OfferedContracts.Count == 0) { return; } UpdateHighlight(); if (queue.IsLocked) { return; } UpdateControllerHighlight(); Plugin.Manager.TryAutoLockQueueFromVotes(); if (highlightedIndex >= 0 && highlightedIndex < queue.OfferedContracts.Count) { if (BoardClickPressed()) { VoteNextRank(); } else if (Input.GetKeyDown((KeyCode)49) || ControllerRankPressed(1)) { Vote(1); } else if (Input.GetKeyDown((KeyCode)50) || ControllerRankPressed(2)) { Vote(2); } else if (Input.GetKeyDown((KeyCode)51) || ControllerRankPressed(3)) { Vote(3); } if (Input.GetKeyDown(Plugin.ContractBoardLockKey.Value)) { Plugin.Manager.HostLockQueueFromVotes(); } } } private void Vote(int rank) { if (queue != null) { string localPlayerId = GetLocalPlayerId(); string definitionId = queue.OfferedContracts[highlightedIndex].DefinitionId; if (Plugin.Manager.CastVote(localPlayerId, rank, definitionId)) { RebuildBoardText(); Plugin.Manager.TryAutoLockQueueFromVotes(); } } } private void VoteNextRank() { if (queue == null || highlightedIndex < 0 || highlightedIndex >= queue.OfferedContracts.Count) { return; } string localPlayerId = GetLocalPlayerId(); Plugin.Manager.PlayerVotes.TryGetValue(localPlayerId, out string[] value); string definitionId = queue.OfferedContracts[highlightedIndex].DefinitionId; if (value != null) { int num = Array.IndexOf(value, definitionId); if (num >= 0) { Vote(num + 1); return; } } int num2 = ((value != null) ? Array.FindIndex(value, string.IsNullOrWhiteSpace) : 0); int rank = ((num2 < 0) ? 1 : (num2 + 1)); Vote(rank); } private void EnsureBoard() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_0424: Unknown result type (might be due to invalid IL or missing references) //IL_0471: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_04b4: Unknown result type (might be due to invalid IL or missing references) //IL_050d: Unknown result type (might be due to invalid IL or missing references) //IL_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0550: Unknown result type (might be due to invalid IL or missing references) //IL_0575: Unknown result type (might be due to invalid IL or missing references) //IL_0589: Unknown result type (might be due to invalid IL or missing references) //IL_05ae: Unknown result type (might be due to invalid IL or missing references) //IL_05c2: Unknown result type (might be due to invalid IL or missing references) //IL_05e7: Unknown result type (might be due to invalid IL or missing references) //IL_05fb: Unknown result type (might be due to invalid IL or missing references) //IL_0620: Unknown result type (might be due to invalid IL or missing references) //IL_0634: Unknown result type (might be due to invalid IL or missing references) //IL_0650: Unknown result type (might be due to invalid IL or missing references) //IL_066e: Unknown result type (might be due to invalid IL or missing references) //IL_068f: Unknown result type (might be due to invalid IL or missing references) //IL_06ad: Unknown result type (might be due to invalid IL or missing references) //IL_06ce: Unknown result type (might be due to invalid IL or missing references) //IL_06ec: Unknown result type (might be due to invalid IL or missing references) //IL_075e: Unknown result type (might be due to invalid IL or missing references) //IL_077d: Unknown result type (might be due to invalid IL or missing references) //IL_07c6: Unknown result type (might be due to invalid IL or missing references) //IL_07e4: Unknown result type (might be due to invalid IL or missing references) //IL_085b: Unknown result type (might be due to invalid IL or missing references) //IL_087a: Unknown result type (might be due to invalid IL or missing references) //IL_089e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)boardRoot != (Object)null)) { GameObject val = FindTruckAnchor(); Vector3 position = (((Object)(object)val != (Object)null) ? (val.transform.position + val.transform.right * Plugin.BoardSideOffset.Value + val.transform.forward * Plugin.BoardForwardOffset.Value + Vector3.up * Plugin.BoardUpOffset.Value) : (Vector3.up * Plugin.BoardUpOffset.Value)); Quaternion rotation = (((Object)(object)val != (Object)null) ? Quaternion.Euler(0f, val.transform.eulerAngles.y + Plugin.BoardYawOffset.Value, 0f) : Quaternion.identity); boardRoot = new GameObject("Repo Contracts Board"); boardRoot.transform.position = position; boardRoot.transform.rotation = rotation; boardRoot.transform.localScale = Vector3.one * Plugin.BoardScale.Value; if ((Object)(object)((val != null) ? val.transform.parent : null) != (Object)null) { boardRoot.transform.SetParent(val.transform.parent, true); } readableSideSign = ResolveReadableSideSign(boardRoot.transform); GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "Repo Contracts Board Back"; obj.transform.SetParent(boardRoot.transform, false); obj.transform.localPosition = Vector3.zero; obj.transform.localScale = new Vector3(1.58f, 0.92f, 0.045f); DisablePhysicalCollider(obj); SetColor(obj, new Color(0.008f, 0.012f, 0.014f, 1f)); GameObject obj2 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj2).name = "Repo Contracts TV Bezel"; obj2.transform.SetParent(boardRoot.transform, false); obj2.transform.localPosition = new Vector3(0f, 0f, SurfaceZ(0.03f)); obj2.transform.localScale = new Vector3(1.68f, 1.02f, 0.035f); DisablePhysicalCollider(obj2); SetColor(obj2, new Color(0.02f, 0.022f, 0.024f, 1f)); GameObject obj3 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj3).name = "Repo Contracts TV Screen"; obj3.transform.SetParent(boardRoot.transform, false); obj3.transform.localPosition = new Vector3(0f, 0f, SurfaceZ(0.035f)); obj3.transform.localScale = new Vector3(1.5f, 0.82f, 0.018f); DisablePhysicalCollider(obj3); SetColor(obj3, new Color(0.006f, 0.035f, 0.04f, 1f)); GameObject obj4 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj4).name = "Repo Contracts Card Panel"; obj4.transform.SetParent(boardRoot.transform, false); obj4.transform.localPosition = new Vector3(-0.2f, -0.035f, SurfaceZ(0.07f)); obj4.transform.localScale = new Vector3(1.14f, 0.68f, 0.012f); DisablePhysicalCollider(obj4); SetColor(obj4, new Color(0.004f, 0.018f, 0.02f, 1f)); GameObject obj5 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj5).name = "Repo Contracts Accent Rail"; obj5.transform.SetParent(boardRoot.transform, false); obj5.transform.localPosition = new Vector3(-0.2f, 0.305f, SurfaceZ(0.118f)); obj5.transform.localScale = new Vector3(1.16f, 0.012f, 0.01f); DisablePhysicalCollider(obj5); SetColor(obj5, new Color(0.18f, 0.62f, 0.52f, 1f)); GameObject obj6 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj6).name = "Repo Contracts TV Glow"; obj6.transform.SetParent(boardRoot.transform, false); obj6.transform.localPosition = new Vector3(0f, 0f, SurfaceZ(0.052f)); obj6.transform.localScale = new Vector3(1.38f, 0.68f, 0.01f); DisablePhysicalCollider(obj6); SetColor(obj6, new Color(0.006f, 0.035f, 0.035f, 0.55f)); float num = SurfaceZ(0.13f); GameObject obj7 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj7).name = "Repo Contracts Votes Panel"; obj7.transform.SetParent(boardRoot.transform, false); obj7.transform.localPosition = new Vector3(0.54f, -0.044f, SurfaceZ(0.122f)); obj7.transform.localScale = new Vector3(0.34f, 0.646f, 0.012f); DisablePhysicalCollider(obj7); SetColor(obj7, new Color(0.66f, 0.7f, 0.54f, 1f)); CreatePanelBorder("Votes Border Top", new Vector3(0.54f, 0.285f, SurfaceZ(0.136f)), new Vector3(0.36f, 0.008f, 0.01f)); CreatePanelBorder("Votes Border Bottom", new Vector3(0.54f, -0.373f, SurfaceZ(0.136f)), new Vector3(0.36f, 0.008f, 0.01f)); CreatePanelBorder("Votes Border Left", new Vector3(0.36f, -0.044f, SurfaceZ(0.136f)), new Vector3(0.008f, 0.646f, 0.01f)); CreatePanelBorder("Votes Border Right", new Vector3(0.72f, -0.044f, SurfaceZ(0.136f)), new Vector3(0.008f, 0.646f, 0.01f)); headerText = CreateText("Header", new Vector3(-0.68f, 0.365f, num), 0.009f, new Color(0.72f, 0.94f, 0.86f, 1f)); selectedText = CreateText("Selected", new Vector3(0.39f, 0.255f, num), 0.0058f, new Color(0.025f, 0.035f, 0.025f, 1f)); promptText = CreateText("Prompt", new Vector3(-0.68f, -0.36f, num), 0.0048f, new Color(0.72f, 0.94f, 0.72f, 1f)); for (int i = 0; i < 5; i++) { GameObject obj8 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj8).name = $"Repo Contracts Card {i + 1}"; obj8.transform.SetParent(boardRoot.transform, false); obj8.transform.localPosition = new Vector3(-0.18f, 0.22f - (float)i * 0.132f, SurfaceZ(0.108f)); obj8.transform.localScale = new Vector3(1.14f, 0.118f, 0.035f); MakeTriggerOnly(obj8); ContractBoardCard contractBoardCard = obj8.AddComponent(); contractBoardCard.Index = i; contractBoardCard.Text = CreateText($"CardText{i}", new Vector3(-0.69f, 0.253f - (float)i * 0.132f, num), 0.0049f, new Color(0.045f, 0.055f, 0.04f, 1f)); cards.Add(contractBoardCard); GameObject obj9 = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj9).name = $"Repo Contracts Row Separator {i + 1}"; obj9.transform.SetParent(boardRoot.transform, false); obj9.transform.localPosition = new Vector3(-0.18f, 0.154f - (float)i * 0.132f, SurfaceZ(0.136f)); obj9.transform.localScale = new Vector3(1.12f, 0.006f, 0.01f); DisablePhysicalCollider(obj9); SetColor(obj9, new Color(0.055f, 0.06f, 0.05f, 1f)); } } } private void CreatePanelBorder(string name, Vector3 localPosition, Vector3 localScale) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "Repo Contracts " + name; obj.transform.SetParent(boardRoot.transform, false); obj.transform.localPosition = localPosition; obj.transform.localScale = localScale; DisablePhysicalCollider(obj); SetColor(obj, new Color(0.055f, 0.065f, 0.052f, 1f)); } private void UpdatePlacementMode() { if (Input.GetKeyDown((KeyCode)290)) { placementMode = !placementMode; Plugin.Log.LogInfo((object)(placementMode ? "[RepoContracts] Placement ON: Numpad 4/6 side, 8/2 forward, 7/9 up, 1/3 yaw, +/- scale, F9 save/exit." : "[RepoContracts] Placement OFF.")); RebuildBoardText(); SavePlacement(); } if (placementMode) { bool flag = false; float num = Mathf.Max(0.01f, Plugin.BoardNudgeStep.Value); float num2 = Mathf.Max(0.5f, Plugin.BoardRotateStep.Value); if (Input.GetKeyDown((KeyCode)260)) { ConfigEntry 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) { RecreateBoard(); SavePlacement(); } } } private void ApplyPlacement() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)boardRoot == (Object)null)) { GameObject val = FindTruckAnchor(); Vector3 position = (((Object)(object)val != (Object)null) ? (val.transform.position + val.transform.right * Plugin.BoardSideOffset.Value + val.transform.forward * Plugin.BoardForwardOffset.Value + Vector3.up * Plugin.BoardUpOffset.Value) : (Vector3.up * Plugin.BoardUpOffset.Value)); Quaternion rotation = (((Object)(object)val != (Object)null) ? Quaternion.Euler(0f, val.transform.eulerAngles.y + Plugin.BoardYawOffset.Value, 0f) : Quaternion.Euler(0f, Plugin.BoardYawOffset.Value, 0f)); boardRoot.transform.position = position; boardRoot.transform.rotation = rotation; boardRoot.transform.localScale = Vector3.one * Plugin.BoardScale.Value; } } private void RecreateBoard() { if ((Object)(object)boardRoot != (Object)null) { Object.Destroy((Object)(object)boardRoot); boardRoot = null; } cards.Clear(); headerText = null; selectedText = null; promptText = null; highlightedIndex = -1; EnsureBoard(); RebuildBoardText(); } private static void SavePlacement() { Plugin.SaveSettings(); Plugin.Log.LogInfo((object)("[RepoContracts] Board placement saved: " + $"SideOffset={Plugin.BoardSideOffset.Value:F2}, " + $"ForwardOffset={Plugin.BoardForwardOffset.Value:F2}, " + $"UpOffset={Plugin.BoardUpOffset.Value:F2}, " + $"YawOffset={Plugin.BoardYawOffset.Value:F1}, " + $"Scale={Plugin.BoardScale.Value:F2}")); } private void UpdateHighlight() { int num = -1; Transform val = (((Object)(object)PlayerController.instance != (Object)null && (Object)(object)PlayerController.instance.cameraGameObject != (Object)null) ? PlayerController.instance.cameraGameObject.transform : (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : null)); if ((Object)(object)val != (Object)null && TryGetBoardAimIndex(val, out var index)) { num = index; } if (num != highlightedIndex) { highlightedIndex = num; RebuildBoardText(); } } private bool TryGetBoardAimIndex(Transform aim, out int index) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) index = -1; if ((Object)(object)boardRoot == (Object)null || queue == null) { return false; } Plane val = default(Plane); ((Plane)(ref val))..ctor(boardRoot.transform.forward, boardRoot.transform.position); Ray val2 = default(Ray); ((Ray)(ref val2))..ctor(aim.position, aim.forward); float num = default(float); if (!((Plane)(ref val)).Raycast(val2, ref num) || num < 0f || num > 7f) { return false; } Vector3 val3 = boardRoot.transform.InverseTransformPoint(((Ray)(ref val2)).GetPoint(num)); if (val3.x < -0.78f || val3.x > 0.33f) { return false; } int num2 = Mathf.Min(queue.OfferedContracts.Count, cards.Count); for (int i = 0; i < num2; i++) { float num3 = 0.22f - (float)i * 0.132f; if (Mathf.Abs(val3.y - num3) <= 0.072f) { index = i; return true; } } return false; } private void UpdateControllerHighlight() { if (queue != null && queue.OfferedContracts.Count != 0 && !(Time.time < controllerMoveCooldownUntil)) { int num = 0; float axisRaw = Input.GetAxisRaw("Vertical"); if (axisRaw > 0.55f || Input.GetKeyDown((KeyCode)343)) { num = -1; } else if (axisRaw < -0.55f || Input.GetKeyDown((KeyCode)344)) { num = 1; } if (num != 0) { int count = queue.OfferedContracts.Count; highlightedIndex = ((highlightedIndex >= 0) ? ((highlightedIndex + num + count) % count) : 0); controllerMoveCooldownUntil = Time.time + 0.22f; RebuildBoardText(); } } } private static bool ControllerRankPressed(int rank) { return rank switch { 1 => Input.GetKeyDown((KeyCode)330) || SafeInputDown((InputKey)26), 2 => Input.GetKeyDown((KeyCode)331) || SafeInputDown((InputKey)27), 3 => Input.GetKeyDown((KeyCode)332) || SafeInputDown((InputKey)28), _ => false, }; } private static bool BoardClickPressed() { if (!Input.GetMouseButtonDown(0) && !Input.GetKeyDown((KeyCode)101) && !Input.GetKeyDown((KeyCode)13) && !Input.GetKeyDown((KeyCode)330)) { return SafeInputDown((InputKey)2); } return true; } private static bool SafeInputDown(InputKey key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return SemiFunc.InputDown(key); } catch { return false; } } private void RebuildBoardText() { //IL_010c: Unknown result type (might be due to invalid IL or missing references) if (queue == null || (Object)(object)boardRoot == (Object)null) { return; } if ((Object)(object)headerText != (Object)null) { headerText.text = (queue.IsLocked ? "QUEUE LOCKED" : "CONTRACT BOARD"); } for (int i = 0; i < cards.Count; i++) { ContractBoardCard contractBoardCard = cards[i]; if ((Object)(object)contractBoardCard == (Object)null || (Object)(object)((Component)contractBoardCard).gameObject == (Object)null) { continue; } if (i >= queue.OfferedContracts.Count) { ((Component)contractBoardCard).gameObject.SetActive(false); continue; } ((Component)contractBoardCard).gameObject.SetActive(true); ContractInstance contractInstance = queue.OfferedContracts[i]; if (contractInstance == null || contractInstance.Definition == null) { if ((Object)(object)contractBoardCard.Text != (Object)null) { contractBoardCard.Text.text = $"{i + 1}. Contract unavailable"; } SetColor(((Component)contractBoardCard).gameObject, new Color(0.12f, 0.12f, 0.12f, 1f)); continue; } ContractDefinition definition = contractInstance.Definition; ApplyCardVisual(((Component)contractBoardCard).gameObject, definition.Rarity, i == highlightedIndex); if ((Object)(object)contractBoardCard.Text != (Object)null) { contractBoardCard.Text.text = BuildCardText(i, contractInstance); } } if ((Object)(object)selectedText != (Object)null) { selectedText.text = BuildVoteSummary(); } if ((Object)(object)promptText != (Object)null) { promptText.text = (placementMode ? "PLACE: numpad move/yaw, +/- scale, F9 save" : (queue.IsLocked ? "Press C for active contracts." : "Aim + click/interact vote. 1/2/3 rank.")); } } private string BuildVoteSummary() { if (queue == null) { return string.Empty; } string localPlayerId = GetLocalPlayerId(); Plugin.Manager.PlayerVotes.TryGetValue(localPlayerId, out string[] value); string text = "Priority\n"; for (int i = 0; i < Plugin.SelectedContractsPerLevel.Value; i++) { string definitionId = ((value != null && i < value.Length) ? value[i] : string.Empty); ContractInstance contractInstance = ((!string.IsNullOrWhiteSpace(definitionId)) ? queue.OfferedContracts.FirstOrDefault((ContractInstance c) => c.DefinitionId == definitionId) : null); text += string.Format("{0}. {1}\n", i + 1, (contractInstance?.Definition != null) ? VoteName(contractInstance.Definition.DisplayName) : "-"); } int num = ((!SemiFunc.IsMultiplayer() || PhotonNetwork.PlayerList == null) ? 1 : PhotonNetwork.PlayerList.Length); return text + $"{Plugin.Manager.PlayerVotes.Count}/{num} ready"; } private TextMesh CreateText(string name, Vector3 localPosition, float size, Color color) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Repo Contracts " + name); val.transform.SetParent(boardRoot.transform, false); val.transform.localPosition = localPosition; val.transform.localRotation = TextRotation(); TextMesh obj = val.AddComponent(); obj.anchor = (TextAnchor)0; obj.alignment = (TextAlignment)0; obj.characterSize = size; obj.fontSize = 64; obj.text = string.Empty; ContractAssets.ApplyTextMaterial(obj, color); return obj; } private static string BuildCardText(int index, ContractInstance contract) { return $"{index + 1} {contract.Definition.DisplayName} {DifficultyLabel(contract.Definition.Rarity)} {RewardLabel(contract.RewardRoll)}\n" + BoardRuleLabel(contract); } private static string VoteName(string displayName) { return displayName switch { "High Value Quota" => "High Value", "No Truck Healing" => "No Healing", "No Damage Taken" => "No Damage", "Melee Kills Only" => "Melee", "Pressure Plates" => "Plates", "Fragile Delivery" => "Fragile", "Cursed Delivery" => "Cursed", _ => Shorten(displayName, 10), }; } private static string Shorten(string value, int maxLength) { if (string.IsNullOrWhiteSpace(value)) { return string.Empty; } if (value.Length <= maxLength) { return value; } return value.Substring(0, Mathf.Max(1, maxLength - 1)) + "."; } private static string DifficultyLabel(ContractRarity rarity) { return rarity switch { ContractRarity.Common => "EASY", ContractRarity.Uncommon => "MED", ContractRarity.Rare => "HARD", ContractRarity.Epic => "EPIC", ContractRarity.Legendary => "LEG", _ => rarity.ToString(), }; } private static string ProgressLabel(ContractInstance contract) { if (contract.Definition.ProgressHook != ContractHook.ExtractionValueChanged) { return $"Goal {contract.RequiredProgress}"; } return $"${contract.RequiredProgress:N0}"; } private static string BoardRuleLabel(ContractInstance contract) { switch (contract.Definition.Id) { case "clean_sweep": case "debt_collector": return "Extract " + ProgressLabel(contract); case "monster_hunter": return $"Kill {contract.RequiredProgress}"; case "melee_clause": return $"Melee kills {contract.RequiredProgress}"; case "glass_cargo": return "Extract fragile item"; case "cursed_loot": return "Extract cursed item; no player damage"; case "no_deaths": case "no_one_left_bent": return "Extract; no deaths"; case "no_damage": return "Extract; no damage"; case "limited_healing": return "Extract; no healing"; case "no_loud_tools": return "Extract; no guns"; case "pressure_plate_extraction": return "Secure plates"; default: return Shorten(contract.Definition.ShortDescription, 22); } } private static string RewardLabel(RewardRoll reward) { switch (reward.Kind) { case RewardKind.CashBonus: case RewardKind.MoneyBag: return $"+${reward.Amount:N0}"; case RewardKind.ShopItem: return "Shop item"; case RewardKind.Weapon: return "Weapon"; case RewardKind.UpgradeDiscount: return "Upgrade"; case RewardKind.SurpriseBox: return "Present"; case RewardKind.CosmeticToken: return "Cosmetic"; default: return Shorten(reward.PreviewText, 18); } } private static Color GetCardColor(ContractRarity rarity, bool highlighted) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) Color val = (Color)(rarity switch { ContractRarity.Common => new Color(0.46f, 0.58f, 0.42f, 1f), ContractRarity.Uncommon => new Color(0.58f, 0.54f, 0.36f, 1f), ContractRarity.Rare => new Color(0.62f, 0.43f, 0.34f, 1f), ContractRarity.Epic => new Color(0.5f, 0.38f, 0.58f, 1f), ContractRarity.Legendary => new Color(0.64f, 0.49f, 0.3f, 1f), _ => new Color(0.42f, 0.48f, 0.46f, 1f), }); if (!highlighted) { return val; } return Color.Lerp(val, Color.white, 0.18f); } private static void ApplyCardVisual(GameObject card, ContractRarity rarity, bool highlighted) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) SetColor(card, GetCardColor(rarity, highlighted)); } private static GameObject? FindTruckAnchor() { if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null) { return ((Component)TruckSafetySpawnPoint.instance).gameObject; } if ((Object)(object)TruckHealer.instance != (Object)null) { return ((Component)TruckHealer.instance).gameObject; } if ((Object)(object)TruckScreenText.instance != (Object)null) { return ((Component)TruckScreenText.instance).gameObject; } return GameObject.Find("cctv") ?? GameObject.Find("cctv (1)"); } private float SurfaceZ(float depth) { return Mathf.Sign(readableSideSign) * Mathf.Abs(depth); } private Quaternion TextRotation() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (!(readableSideSign < 0f)) { return Quaternion.Euler(0f, 180f, 0f); } return Quaternion.identity; } private static float ResolveReadableSideSign(Transform boardTransform) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) Transform val = (((Object)(object)PlayerController.instance != (Object)null && (Object)(object)PlayerController.instance.cameraGameObject != (Object)null) ? PlayerController.instance.cameraGameObject.transform : (((Object)(object)Camera.main != (Object)null) ? ((Component)Camera.main).transform : (((Object)(object)PlayerAvatar.instance != (Object)null) ? ((Component)PlayerAvatar.instance).transform : null))); if ((Object)(object)val == (Object)null) { return -1f; } if (!(boardTransform.InverseTransformPoint(val.position).z >= 0f)) { return -1f; } return 1f; } private static string GetLocalPlayerId() { if (!SemiFunc.IsMultiplayer() || PhotonNetwork.LocalPlayer == null) { return "singleplayer"; } return $"player-{PhotonNetwork.LocalPlayer.ActorNumber}"; } private static void SetColor(GameObject target, Color color) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) Renderer component = target.GetComponent(); 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: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if (!visible || !overlayOpen || queue == null) { DrawTrackedTarget(); DrawNotification(); return; } GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1, wordWrap = false }; GUIStyle val2 = new GUIStyle(GUI.skin.label) { fontSize = 13, wordWrap = true }; GUIStyle val3 = new GUIStyle(GUI.skin.label) { fontSize = 12, wordWrap = false }; Rect rect = default(Rect); ((Rect)(ref rect))..ctor((float)Screen.width - 620f, 30f, 590f, 430f); DrawTexturedBox(rect, "ui/hud/ui_active_contract_hud_panel.png", new Color(0.65f, 1f, 0.95f, 0.78f)); GUILayout.BeginArea(new Rect(((Rect)(ref rect)).x + 20f, ((Rect)(ref rect)).y + 14f, ((Rect)(ref rect)).width - 40f, ((Rect)(ref rect)).height - 28f)); GUILayout.Label("Repo Contracts", val, Array.Empty()); if (Plugin.EnableDisciplineDebt.Value && queue.DisciplineDebt > 0) { GUILayout.Label(string.Format("Discipline Debt: {0} future reward{1} withheld", queue.DisciplineDebt, (queue.DisciplineDebt == 1) ? string.Empty : "s"), val2, Array.Empty()); } ContractInstance activeContract = queue.ActiveContract; if (activeContract == null) { GUILayout.Label(queue.IsLocked ? "No active contract." : "Vote at the truck contract board.", val2, Array.Empty()); } else { GUILayout.Label($"Active {queue.ActiveIndex + 1}/{queue.SelectedContracts.Count}: {activeContract.Definition.DisplayName}", val, Array.Empty()); GUILayout.Label(Shorten(activeContract.Definition.ObjectiveText, 92), val2, Array.Empty()); GUILayout.Label($"Progress: {activeContract.CurrentProgress}/{activeContract.RequiredProgress}", val2, Array.Empty()); GUILayout.Label("Failure: " + Shorten(activeContract.Definition.FailureText, 82), val2, Array.Empty()); GUILayout.Label("Reward: " + activeContract.RewardRoll.PreviewText, val2, Array.Empty()); string id = activeContract.Definition.Id; if ((id == "glass_cargo" || id == "cursed_loot") ? true : false) { string value; string text = ((activeContract.SelectedTargets.TryGetValue("targetName", out value) && !string.IsNullOrWhiteSpace(value)) ? value : "Random marked valuable"); GUILayout.Label("Target: " + text, val2, Array.Empty()); } } GUILayout.Space(8f); for (int i = 0; i < queue.SelectedContracts.Count; i++) { ContractInstance contractInstance = queue.SelectedContracts[i]; GUILayout.Label($"{i + 1}. {Shorten(contractInstance.Definition.DisplayName, 24)} - {contractInstance.Status}", val3, Array.Empty()); } 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) ContractInstance contractInstance = queue?.ActiveContract; bool flag = !visible || (Object)(object)trackedTarget == (Object)null; if (!flag) { string text = contractInstance?.Definition.Id; bool flag2 = ((text == "glass_cargo" || text == "cursed_loot") ? true : false); flag = !flag2; } if (flag) { return; } Camera main = Camera.main; if (!((Object)(object)main == (Object)null)) { Vector3 val = trackedTarget.transform.position + Vector3.up * 0.6f; Vector3 val2 = main.WorldToScreenPoint(val); bool num = val2.z < 0f; if (num) { val2.x = (float)Screen.width - val2.x; val2.y = (float)Screen.height - val2.y; } float num2 = 48f; bool flag3 = num || val2.x < num2 || val2.x > (float)Screen.width - num2 || val2.y < num2 || val2.y > (float)Screen.height - num2; float num3 = Mathf.Clamp(val2.x, num2, (float)Screen.width - num2); float num4 = Mathf.Clamp((float)Screen.height - val2.y, num2, (float)Screen.height - num2); float num5 = Vector3.Distance(((Component)main).transform.position, trackedTarget.transform.position); string arg = (string.IsNullOrWhiteSpace(trackedTargetLabel) ? contractInstance.Definition.DisplayName.ToUpperInvariant() : trackedTargetLabel); string arg2 = ((contractInstance.Definition.Id == "cursed_loot") ? "CURSED TARGET" : "MARKED TARGET"); Color color = GUI.color; GUI.color = new Color(0.2f, 1f, 0.9f, 0.95f); GUI.Box(new Rect(((float)Screen.width - 320f) * 0.5f, 132f, 320f, 54f), $"{arg2}\n{arg} - {Mathf.RoundToInt(num5)}m"); GUI.Box(new Rect(num3 - 105f, num4 - 26f, 210f, 52f), string.Format("{0}\n{1} - {2}m", flag3 ? "TARGET" : "MARKED", arg, Mathf.RoundToInt(num5))); GUI.color = color; } } private void ShowNotification(string message) { notification = message; notificationUntil = Time.time + 4f; if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogInfo((object)("[RepoContracts] HUD notification: " + message)); } } private static void DrawTexturedBox(Rect rect, string asset, Color tint) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) Texture2D val = ContractAssets.Texture(asset); Color color = GUI.color; if ((Object)(object)val != (Object)null) { GUI.color = tint; GUI.DrawTexture(rect, (Texture)(object)val, (ScaleMode)0, true); } else { GUI.Box(rect, GUIContent.none); } GUI.color = color; } private static string Shorten(string value, int maxLength) { if (string.IsNullOrWhiteSpace(value) || value.Length <= maxLength) { return value ?? string.Empty; } return value.Substring(0, Mathf.Max(1, maxLength - 1)) + "."; } private void LogHud() { if (Plugin.EnableDebugLogging.Value && visible && queue != null) { ContractInstance activeContract = queue.ActiveContract; if (activeContract == null) { Plugin.Log.LogInfo((object)"[RepoContracts] HUD ready: no active contract."); } else { Plugin.Log.LogInfo((object)($"[RepoContracts] HUD active {queue.ActiveIndex + 1}/{queue.SelectedContracts.Count}: " + $"{activeContract.Definition.DisplayName} {activeContract.CurrentProgress}/{activeContract.RequiredProgress}. " + "Reward: " + activeContract.RewardRoll.PreviewText)); } } } } } namespace RepoContracts.Rewards { internal sealed class RewardAdapter { private static readonly FieldInfo? DollarOverrideField = typeof(ValuableObject).GetField("dollarValueOverride", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); internal void Grant(RewardRoll reward, Vector3 position, RewardDeliveryContext context) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) if (!SemiFunc.IsMasterClientOrSingleplayer()) { return; } if (!context.CashRewardsUsable && IsCashReward(reward)) { GrantNonCashSubstitute(reward, position, context); return; } switch (reward.Kind) { case RewardKind.CashBonus: case RewardKind.MoneyBag: SpawnMoneyBag(position, Mathf.RoundToInt((float)reward.Amount * Plugin.RewardMultiplier.Value)); break; case RewardKind.ShopItem: if (!TrySpawnShopItemReward(reward, position, IsGeneralShopReward, "shop item")) { GrantFallback(reward, position, context); } break; case RewardKind.Weapon: if (!TrySpawnShopItemReward(reward, position, IsWeaponReward, "weapon")) { GrantFallback(reward, position, context); } break; case RewardKind.UpgradeDiscount: if (!TrySpawnShopItemReward(reward, position, IsUpgradeReward, "upgrade")) { GrantFallback(reward, position, context); } break; case RewardKind.SurpriseBox: if (!TrySpawnSurpriseReward(reward, position)) { GrantFallback(reward, position, context); } break; case RewardKind.CosmeticToken: if (!TryGrantCosmeticToken(reward)) { GrantFallback(reward, position, context); } break; case RewardKind.SpawnableValuable: case RewardKind.ContractRerollToken: Plugin.Log.LogWarning((object)$"[RepoContracts] Reward adapter TODO: {reward.Kind} is modeled but not safely implemented yet."); GrantFallback(reward, position, context); break; default: Plugin.Log.LogWarning((object)$"[RepoContracts] Unknown reward kind {reward.Kind}."); break; } } private static bool IsCashReward(RewardRoll reward) { RewardKind kind = reward.Kind; if ((uint)kind <= 1u) { return true; } return false; } private static void GrantNonCashSubstitute(RewardRoll reward, Vector3 position, RewardDeliveryContext context) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)("[RepoContracts] Cash reward converted because cash is unsafe now (" + context.Reason + ").")); if (!TrySpawnShopItemReward(reward, position, IsGeneralShopReward, "shop item substitute") && !TryGrantCosmeticToken(reward)) { Plugin.Log.LogWarning((object)"[RepoContracts] Non-cash substitute failed; spawning money bag as last-resort fallback."); SpawnFallbackMoneyBag(reward, position); } } private static void GrantFallback(RewardRoll reward, Vector3 position, RewardDeliveryContext context) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (context.CashRewardsUsable) { SpawnFallbackMoneyBag(reward, position); return; } if (TryGrantCosmeticToken(reward)) { Plugin.Log.LogInfo((object)("[RepoContracts] Granted cosmetic token fallback because cash is unsafe now (" + context.Reason + ").")); return; } Plugin.Log.LogWarning((object)"[RepoContracts] Cash-unsafe fallback had no cosmetic path; spawning money bag as last resort."); SpawnFallbackMoneyBag(reward, position); } private static void SpawnMoneyBag(Vector3 position, int amount) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)AssetManager.instance == (Object)null || (Object)(object)AssetManager.instance.surplusValuableSmall == (Object)null) { Plugin.Log.LogWarning((object)"[RepoContracts] Money bag reward unavailable: AssetManager.surplusValuableSmall was not found."); return; } GameObject surplusValuableSmall = AssetManager.instance.surplusValuableSmall; int num = Mathf.Max(1000, Mathf.RoundToInt(Mathf.Round((float)amount / 100f) * 100f)); ValuableObject component = (SemiFunc.IsMultiplayer() ? PhotonNetwork.InstantiateRoomObject("Valuables/" + ((Object)surplusValuableSmall).name, position, Quaternion.identity, (byte)0, (object[])null) : Object.Instantiate(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_0072: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null) { Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward unavailable: StatsManager item dictionary was not ready.")); return false; } List eligibleItems = GetEligibleItems(predicate); if (eligibleItems.Count == 0) { Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward unavailable: no eligible shop items were found.")); return false; } return TrySpawnItem(eligibleItems[Random.Range(0, eligibleItems.Count)], position, label); } private static bool TrySpawnSurpriseReward(RewardRoll reward, Vector3 position) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)StatsManager.instance == (Object)null || StatsManager.instance.itemDictionary == null) { Plugin.Log.LogWarning((object)"[RepoContracts] surprise reward unavailable: StatsManager item dictionary was not ready."); return false; } List eligibleItems = GetEligibleItems(IsPresentBoxReward); if (eligibleItems.Count > 0) { return TrySpawnItem(eligibleItems[Random.Range(0, eligibleItems.Count)], position, "surprise present"); } List eligibleItems2 = GetEligibleItems(IsGeneralShopReward); if (eligibleItems2.Count == 0) { Plugin.Log.LogWarning((object)"[RepoContracts] surprise reward unavailable: no eligible bundle items were found."); return false; } int num; switch (reward.Rarity) { case ContractRarity.Epic: case ContractRarity.Legendary: num = 3; break; case ContractRarity.Rare: num = 2; break; default: num = 2; break; } int num2 = num; bool flag = false; Vector3 val = default(Vector3); for (int i = 0; i < num2; i++) { Item selected = eligibleItems2[Random.Range(0, eligibleItems2.Count)]; ((Vector3)(ref val))..ctor(((float)i - (float)(num2 - 1) * 0.5f) * 0.55f, 0f, Random.Range(-0.25f, 0.25f)); flag |= TrySpawnItem(selected, position + val, "surprise bundle item"); } return flag; } private static List GetEligibleItems(Func predicate) { Func predicate2 = predicate; return StatsManager.instance.itemDictionary.Values.Where((Item item) => (Object)(object)item != (Object)null && !item.disabled && item.physicalItem && item.prefab != null && item.prefab.IsValid() && predicate2(item)).ToList(); } private static bool TrySpawnItem(Item selected, Vector3 position, string label) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) Quaternion val = Quaternion.identity * selected.spawnRotationOffset; try { if ((Object)(object)(SemiFunc.IsMultiplayer() ? PhotonNetwork.InstantiateRoomObject(selected.prefab.ResourcePath, position, val, (byte)0, (object[])null) : Object.Instantiate(selected.prefab.Prefab, position, val)) == (Object)null) { Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward failed to spawn: " + selected.itemName + ".")); return false; } Plugin.Log.LogInfo((object)("[RepoContracts] Spawned contract " + label + " reward: " + selected.itemName + ".")); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[RepoContracts] " + label + " reward failed to spawn " + selected.itemName + ": " + ex.Message)); return false; } } private static bool TryGrantCosmeticToken(RewardRoll reward) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)MetaManager.instance == (Object)null) { Plugin.Log.LogWarning((object)"[RepoContracts] Cosmetic token reward unavailable: MetaManager was not ready."); return false; } try { MetaManager.instance.CosmeticTokenAdd(ToGameRarity(reward.Rarity)); Plugin.Log.LogInfo((object)$"[RepoContracts] Granted contract cosmetic token reward: {reward.Rarity}."); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[RepoContracts] Cosmetic token reward failed: " + ex.Message)); return false; } } private static bool IsGeneralShopReward(Item item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected I4, but got Unknown itemType itemType = item.itemType; switch ((int)itemType) { case 0: case 1: case 2: case 5: case 6: case 7: case 9: case 10: case 11: case 12: case 13: case 14: case 15: return true; default: return false; } } private static bool IsWeaponReward(Item item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected I4, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 itemType itemType = item.itemType; switch (itemType - 6) { default: if ((int)itemType != 15) { break; } goto case 0; case 0: case 1: case 3: case 5: return true; case 2: case 4: break; } return false; } private static bool IsUpgradeReward(Item item) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 itemType itemType = item.itemType; if (itemType - 3 <= 1) { return true; } return false; } private static bool IsPresentBoxReward(Item item) { string[] obj = new string[5] { item.itemName, " ", ((Object)item).name, " ", null }; PrefabRef prefab = item.prefab; obj[4] = ((prefab != null) ? prefab.ResourcePath : null); string text = string.Concat(obj).ToLowerInvariant(); if (!text.Contains("present") && !text.Contains("gift") && !text.Contains("surprise") && !text.Contains("package") && !text.Contains("crate")) { return text.Contains("box"); } return true; } private static Rarity ToGameRarity(ContractRarity rarity) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) return (Rarity)(rarity switch { ContractRarity.Common => 0, ContractRarity.Uncommon => 1, ContractRarity.Rare => 2, ContractRarity.Epic => 3, ContractRarity.Legendary => 3, _ => 0, }); } } internal readonly struct RewardDeliveryContext { internal bool CashRewardsUsable { get; } internal string Reason { get; } private RewardDeliveryContext(bool cashRewardsUsable, string reason) { CashRewardsUsable = cashRewardsUsable; Reason = reason; } internal static RewardDeliveryContext Normal() { return new RewardDeliveryContext(cashRewardsUsable: true, "normal"); } internal static RewardDeliveryContext CashUnsafe(string reason) { return new RewardDeliveryContext(cashRewardsUsable: false, reason); } } } namespace RepoContracts.Networking { internal sealed class ContractSyncAdapter { internal readonly struct ContractSnapshot { internal static readonly ContractSnapshot Empty = new ContractSnapshot(Array.Empty(), 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), contract.RewardRoll.Amount.ToString(), Escape(contract.RewardRoll.PreviewText), ((int)contract.RewardRoll.Kind).ToString(), Escape(contract.RewardRoll.RewardId)); } 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)); } } } if (array.Length > 6 && int.TryParse(array[6], out var result4) && result4 > 0) { string previewText = ((array.Length > 7) ? Unescape(array[7]) : contractInstance.RewardRoll.PreviewText); RewardKind kind = contractInstance.RewardRoll.Kind; if (array.Length > 8 && int.TryParse(array[8], out var result5) && Enum.IsDefined(typeof(RewardKind), result5)) { kind = (RewardKind)result5; } string rewardId = ((array.Length > 9) ? Unescape(array[9]) : contractInstance.RewardRoll.RewardId); contractInstance.SetRewardRoll(new RewardRoll(kind, contractInstance.RewardRoll.Rarity, result4, rewardId, previewText)); } return contractInstance; } private static IEnumerable 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", "Value Quota", ContractCategory.Loot, ContractRarity.Common, "Extract valuables worth the target amount.", "Extract valuables worth the target amount while this contract is active.", "Extract the target value.", "No failure condition.", "+$2,500 money bag", 1000, ContractHook.ExtractionValueChanged, null, RewardRoll.MoneyBag(ContractRarity.Common, 2500, "+$2,500"), "loot", "value"), Prototype("monster_hunter", "Enemy Hunt", ContractCategory.Combat, ContractRarity.Uncommon, "Kill enemies while active.", "Hunt enemies during the active contract window.", "Kill the required number of 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 Kills Only", ContractCategory.Combat, ContractRarity.Rare, "Kill enemies using melee only.", "Projectile weapon kills fail this contract while it is active.", "Kill enemies without projectile weapons.", "Projectile weapon used while active.", "+$6,500 money bag", 2, ContractHook.EnemyKilled, ContractHook.ProjectileWeaponUsed, RewardRoll.MoneyBag(ContractRarity.Rare, 6500, "+$6,500"), "combat", "melee"), Prototype("glass_cargo", "Fragile Delivery", ContractCategory.Loot, ContractRarity.Rare, "Extract the marked fragile valuable.", "A random breakable valuable is marked and must be extracted before it breaks.", "Extract the marked fragile item.", "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"), RetiredPrototype("no_one_left_bent", "No Deaths", 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"), Prototype("cursed_loot", "Cursed Delivery", ContractCategory.Loot, ContractRarity.Epic, "Extract a cursed valuable without taking damage.", "A random valuable is cursed. Extract it before it is destroyed or abandoned, and avoid player damage while the curse is active.", "Extract the marked cursed item.", "Marked item destroyed, abandoned, or any player took damage.", "+$10,000 money bag", 1, ContractHook.ItemExtracted, ContractHook.PlayerDamaged, RewardRoll.MoneyBag(ContractRarity.Epic, 10000, "+$10,000"), new AssetRequirements(existingAssetsOnly: false, needsUiAsset: false, needsWorldObject: false, needsVfx: true, needsSfx: true, needsIcon: true), "loot", "cursed", "marker"), 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", "High Value Quota", ContractCategory.Loot, ContractRarity.Common, "Extract a larger value quota.", "Extract enough valuables before the contract advances.", "Extract the higher target value.", "No failure condition.", "+$3,000", 1500, ContractHook.ExtractionValueChanged, null, RewardRoll.MoneyBag(ContractRarity.Common, 3000, "+$3,000"), "loot", "value"), Scaffold("crew_limit_clause", "Grab Limit", 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", "Alert Fight", 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", "Marked Targets", 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", "Assigned 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", "High Ground Kills", 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 Plates", 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 Guns", ContractCategory.Stealth, ContractRarity.Uncommon, "Complete an extraction without projectile weapons.", "Complete one extraction while this contract is active without using projectile weapons.", "Complete the next extraction without guns.", "Projectile 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 Taken", 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", "No Truck 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, int runLevel = 1) { 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 = list.Sum((ContractDefinition definition) => GetWeight(definition, runLevel)); int num2 = Random.Range(0, num); int num3 = 0; for (int i = 0; i < list.Count; i++) { num3 += GetWeight(list[i], runLevel); 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, int runLevel) { int num = definition.Rarity switch { ContractRarity.Common => 45, ContractRarity.Uncommon => 30, ContractRarity.Rare => 16, ContractRarity.Epic => 7, ContractRarity.Legendary => 2, _ => 1, }; int num2 = Mathf.Clamp((Math.Max(1, runLevel) - 1) / 5, 0, 8); return Math.Max(2, definition.Rarity switch { ContractRarity.Common => num - num2 * 4, ContractRarity.Uncommon => num + num2, ContractRarity.Rare => num + num2 * 3, ContractRarity.Epic => num + num2 * 2, ContractRarity.Legendary => num + num2, _ => num, }); } 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 RetiredPrototype(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) { 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, AssetRequirements.ExistingOnly, isMilestoneContract: false, isQueueEligible: false, "Contracts." + id, requiredProgress, progressHook, failureHook, reward, isPrototypeImplemented: true); } 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 static class ContractDifficultyScaler { private static readonly BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static readonly string[] RunDepthFieldNames = new string[5] { "runLevel", "runLevelCurrent", "levelCurrentIndex", "levelIndex", "level" }; private static readonly string[] UpgradeStatKeywords = new string[10] { "strength", "speed", "sprint", "stamina", "health", "energy", "range", "grab", "throw", "jump" }; internal static int GetEffectiveRunLevel(int completedGameplayLevels) { return Math.Max(completedGameplayLevels + 1, DetectRunLevel()); } internal static void ApplyTo(ContractInstance contract, int runLevel) { runLevel = Math.Max(1, runLevel); float playerUpgradeScale = GetPlayerUpgradeScale(); contract.SetRequiredProgress(GetRequiredProgress(contract.Definition, runLevel, playerUpgradeScale)); contract.SetRewardRoll(GetRewardRoll(contract.RewardRoll, runLevel, playerUpgradeScale)); } internal static float GetRewardScale(int runLevel) { return 1f + (float)Math.Max(0, runLevel - 1) * 0.1f; } private static int GetRequiredProgress(ContractDefinition definition, int runLevel, float playerUpgradeScale) { int num = Math.Max(1, definition.DefaultRequiredProgress); int num2 = Math.Max(0, runLevel - 1); float num3 = Math.Max(0f, playerUpgradeScale - 1f); return definition.ProgressHook switch { ContractHook.ExtractionValueChanged => RoundToNearest((float)num * (1f + (float)num2 * 0.12f + num3 * 0.7f), 250), ContractHook.EnemyKilled => Math.Max(num, num + Math.Min(6, num2 / 7 + Mathf.FloorToInt(num3 * 3f))), _ => num, }; } private static RewardRoll GetRewardRoll(RewardRoll reward, int runLevel, float playerUpgradeScale) { if (reward.Amount <= 1) { return reward; } int num = RoundToNearest((float)reward.Amount * GetRewardScale(runLevel) * playerUpgradeScale, 500); RewardKind kind = reward.Kind; bool flag = (uint)kind <= 1u; string previewText = (flag ? $"+${num:N0} money bag" : ScaleFallbackPreview(reward.PreviewText, num)); return reward.WithAmount(num, previewText); } private static string ScaleFallbackPreview(string previewText, int scaledAmount) { int num = previewText.LastIndexOf("or +$", StringComparison.Ordinal); if (num < 0) { return previewText; } return previewText.Substring(0, num) + $"or +${scaledAmount:N0}"; } private static float GetPlayerUpgradeScale() { try { PlayerAvatar[] array = Object.FindObjectsOfType(); if (array == null || array.Length == 0) { return 1f; } float num = 0f; int num2 = 0; PlayerAvatar[] array2 = array; foreach (PlayerAvatar val in array2) { if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy) { float num3 = EstimatePlayerUpgradeScore(val); num += Mathf.Clamp(num3, 0f, 12f); num2++; } } if (num2 == 0) { return 1f; } float num4 = num / (float)num2; return 1f + Mathf.Clamp(num4 * 0.035f, 0f, 0.35f); } catch (Exception ex) { if (Plugin.EnableDebugLogging.Value) { Plugin.Log.LogWarning((object)("[RepoContracts] Player upgrade scaling unavailable: " + ex.Message)); } return 1f; } } private static float EstimatePlayerUpgradeScore(PlayerAvatar player) { HashSet visited = new HashSet(); float num = 0f; num += SumUpgradeValues(player, visited); Component[] components = ((Component)player).GetComponents(); foreach (Component val in components) { if ((Object)(object)val != (Object)null) { num += SumUpgradeValues(val, visited); } } return num; } private static float SumUpgradeValues(object target, HashSet visited) { if (!visited.Add(target)) { return 0f; } float num = 0f; Type type = target.GetType(); FieldInfo[] fields = type.GetFields(InstanceFlags); foreach (FieldInfo fieldInfo in fields) { if (IsUpgradeStatName(fieldInfo.Name)) { num += ReadUpgradeNumber(fieldInfo.GetValue(target)); } } foreach (PropertyInfo item in from p in type.GetProperties(InstanceFlags) where p.GetIndexParameters().Length == 0 select p) { if (IsUpgradeStatName(item.Name)) { try { num += ReadUpgradeNumber(item.GetValue(target)); } catch { } } } return num; } private static bool IsUpgradeStatName(string name) { string text = name.ToLowerInvariant(); if (UpgradeStatKeywords.Any(text.Contains)) { if (!text.Contains("upgrade") && !text.Contains("level")) { return text.Contains("amount"); } return true; } return false; } private static float ReadUpgradeNumber(object? raw) { float num5; if (!(raw is int num)) { if (!(raw is long num2)) { if (!(raw is float num3)) { if (!(raw is double num4)) { goto IL_0071; } num5 = (float)num4; } else { num5 = num3; } } else { if (num2 > 1000) { goto IL_0071; } num5 = num2; } } else { num5 = num; } goto IL_0078; IL_0078: float num6 = num5; if (!(num6 > 0f) || !(num6 <= 100f)) { return 0f; } return num6; IL_0071: num5 = 0f; goto IL_0078; } private static int DetectRunLevel() { RunManager instance = RunManager.instance; if ((Object)(object)instance == (Object)null) { return 1; } string[] runDepthFieldNames = RunDepthFieldNames; foreach (string fieldName in runDepthFieldNames) { if (TryReadPositiveIntField(instance, fieldName, out var value)) { if (value > 0) { return value; } return 1; } } try { IList list = GetFieldValue(instance, "levels") as IList; object fieldValue = GetFieldValue(instance, "levelCurrent"); if (list != null && fieldValue != null) { int num = list.IndexOf(fieldValue); if (num >= 0) { return num + 1; } } } catch { } return 1; } private static bool TryReadPositiveIntField(object target, string fieldName, out int value) { value = 0; object fieldValue = GetFieldValue(target, fieldName); if (!(fieldValue is int num)) { if (!(fieldValue is long num2)) { if (fieldValue is float num3 && num3 > 0f) { value = Mathf.RoundToInt(num3); return true; } } else if (num2 > 0 && num2 <= int.MaxValue) { value = (int)num2; return true; } } else if (num > 0) { value = num; return true; } return false; } private static object? GetFieldValue(object target, string fieldName) { return target.GetType().GetField(fieldName, InstanceFlags)?.GetValue(target); } private static int RoundToNearest(float value, int nearest) { nearest = Math.Max(1, nearest); return Math.Max(nearest, Mathf.RoundToInt(Mathf.Round(value / (float)nearest) * (float)nearest)); } } 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, SurpriseBox, 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; private set; } private bool IsMarkedItemContract { get { string id = Definition.Id; if (id == "glass_cargo" || id == "cursed_loot") { return true; } return false; } } 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 SetRewardRoll(RewardRoll rewardRoll) { RewardRoll = rewardRoll; } 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 (IsMarkedItemContract && IsMarkedItemTargetEvent(contractEvent) && !MatchesSelectedTarget(contractEvent)) { return false; } if (IsMarkedItemContract && 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 IsMarkedItemTargetEvent(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 string cursedTargetObjectId = string.Empty; private Vector3 cursedLastPosition; private float cursedUntouchedDeadline; private bool cursedWarningIssued; 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; } string voteSessionId = Queue.VoteSessionId; Queue.ApplySnapshot(result.Offers, result.Selected, result.ActiveIndex, result.IsLocked, result.DisciplineDebt, result.VoteSessionId); if (!result.IsLocked && voteSessionId != Queue.VoteSessionId) { 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(); int runLevel = ContractDifficultyScaler.GetEffectiveRunLevel(completedGameplayLevels); IReadOnlyList readOnlyList2; if (!ShouldOfferMilestone(runLevel)) { IReadOnlyList readOnlyList = Array.Empty(); readOnlyList2 = readOnlyList; } else { IReadOnlyList readOnlyList = catalog.All.Where((ContractDefinition c) => c.IsMilestoneContract && c.IsPrototypeImplemented && c.IsQueueEligible).Take(1).ToList(); readOnlyList2 = readOnlyList; } IReadOnlyList readOnlyList3 = readOnlyList2; IReadOnlyList source = ((readOnlyList3.Count > 0) ? readOnlyList3 : catalog.GenerateOffers(Plugin.OfferedContractsPerLevel.Value, runLevel)); Queue.SetOffers(source.Select((ContractDefinition d) => CreateScaledContract(d, random.Next(), runLevel))); PublishLocalPhotonVote(Array.Empty()); boardUi.Show(Queue); GrantPendingDeferredRewards(); syncAdapter.SyncOffers(Queue); DebugLogQueue($"Offers generated for run level {runLevel}"); } 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; } bool flag = false; for (int i = 0; i < value.Length; i++) { if (value[i] == definitionId2) { if (i == rank - 1) { flag = true; } value[i] = string.Empty; } } if (!flag) { 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 (CompleteMarkedItemFromExtractionCandidates()) { 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; string text = activeContract?.Definition.Id; bool flag = ((text == "glass_cargo" || text == "cursed_loot") ? true : false); if (flag && IsActiveMarkedValuableTarget(item)) { activeContract.Complete(); ResolveActiveContract(completed: true); Plugin.Log.LogInfo((object)("[RepoContracts] Completed " + activeContract.Definition.DisplayName + " 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) && !IsActiveMarkedValuableTarget(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 (IsActiveMarkedValuableTarget(item) && IsInExtractionPoint(item)) { extractedItemIds.Add(objectId); EventBus.OnItemExtracted(item, Math.Max(1, GetValuableValue(item))); Plugin.Log.LogInfo((object)"[RepoContracts] Treated marked item destruction inside extraction as successful extraction."); } else { EventBus.OnItemDestroyed(item); } } } internal void HandleItemGrabbed(GameObject item, GameObject? player) { if (!IsHost() || (Object)(object)item == (Object)null) { return; } EventBus.OnItemGrabbed(item, player); if (IsActiveMarkedValuableTarget(item)) { if (Queue.ActiveContract?.Definition.Id == "cursed_loot") { ResetCursedTimer(item); } else { 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 == "cursed_loot" && IsHost()) { UpdateCursedDelivery(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(); ClearCursedTimer(); 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(); ClearCursedTimer(); 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 CompleteMarkedItemFromExtractionCandidates() { ContractInstance activeContract = Queue.ActiveContract; string text = activeContract?.Definition.Id; bool flag = ((text == "glass_cargo" || text == "cursed_loot") ? true : false); if (!flag || 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 " + activeContract.Definition.DisplayName + " from successful extraction haul.")); return true; } } return false; } private void PrepareActiveContract() { ClearGlassCargoMarker(); ClearPressurePlates(); ContractInstance activeContract = Queue.ActiveContract; if (activeContract != null && activeContract.Status == ContractStatus.Active) { string id = activeContract.Definition.Id; if ((id == "glass_cargo" || id == "cursed_loot") ? true : false) { SelectMarkedValuableTarget(activeContract); } else if (activeContract.Definition.Id == "pressure_plate_extraction") { PreparePressurePlateContract(activeContract, isAuthoritative: true); } } } private void SelectMarkedValuableTarget(ContractInstance active) { if (active.SelectedTargets.ContainsKey("targetObjectId")) { GameObject val = FindObjectByInstanceId(active.SelectedTargets["targetObjectId"]); if ((Object)(object)val != (Object)null && val.activeInHierarchy) { CreateMarkedValuableMarker(val, active.Definition.DisplayName.ToUpperInvariant()); return; } active.SelectedTargets.Remove("targetObjectId"); active.SelectedTargets.Remove("targetPhotonViewId"); active.SelectedTargets.Remove("targetName"); Plugin.Log.LogInfo((object)("[RepoContracts] " + active.Definition.DisplayName + " 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] " + active.Definition.DisplayName + " could not find an eligible valuable to mark.")); return; } ValuableObject val2 = list[random.Next(list.Count)]; AssignMarkedValuableTarget(active, ((Component)val2).gameObject); } private void AssignMarkedValuableTarget(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(); } CreateMarkedValuableMarker(target, active.Definition.DisplayName.ToUpperInvariant()); if (active.Definition.Id == "cursed_loot") { ResetCursedTimer(target); active.SelectedTargets["curseSeconds"] = GetCursedUntouchedSeconds().ToString(); } hud.ShowActive(Queue); syncAdapter.SyncProgress(Queue); Plugin.Log.LogInfo((object)("[RepoContracts] " + active.Definition.DisplayName + " 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); return; } string text = activeContract?.Definition.Id; if ((!(text == "glass_cargo") && !(text == "cursed_loot")) || 1 == 0) { return; } GameObject val = FindSyncedGlassCargoTarget(activeContract); if ((Object)(object)val != (Object)null) { CreateMarkedValuableMarker(val, activeContract.Definition.DisplayName.ToUpperInvariant()); if (activeContract.Definition.Id == "cursed_loot") { ResetCursedTimer(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 CreateMarkedValuableMarker(GameObject target, string labelText) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: 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_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_022a: 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_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025a: 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_00ac: 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) { Shader val2 = Shader.Find("Unlit/Transparent") ?? Shader.Find("GUI/Text Shader"); Material val3 = (Material)((!((Object)(object)val2 != (Object)null)) ? ((object)component.material) : ((object)new Material(val2))); Color color = default(Color); ((Color)(ref color))..ctor(0.12f, 0.7f, 0.6f, 0.18f); val3.color = color; ConfigureTransparentMaterial(val3); component.material = val3; } GameObject val4 = GameObject.CreatePrimitive((PrimitiveType)5); ((Object)val4).name = "ProtectedItemSprite"; val4.transform.SetParent(glassCargoMarker.transform, false); val4.transform.localPosition = Vector3.zero; Collider component2 = val4.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } ContractAssets.ApplyTexture(val4, "markers/items/marker_item_protected.png", Color.white); TextMesh obj = glassCargoMarker.AddComponent(); obj.text = labelText; obj.anchor = (TextAnchor)4; obj.alignment = (TextAlignment)1; obj.fontSize = 64; obj.characterSize = 0.04f; ContractAssets.ApplyTextMaterial(obj, new Color(0.7f, 1f, 0.95f, 0.78f)); Bounds val5 = CalculateBounds(target); glassCargoMarker.transform.SetParent(target.transform, true); glassCargoMarker.transform.position = ((Bounds)(ref val5)).center; glassCargoMarker.AddComponent(); Transform transform = val.transform; Vector3 one = Vector3.one; Vector3 extents = ((Bounds)(ref val5)).extents; transform.localScale = one * Mathf.Max(0.28f, Mathf.Min(0.75f, ((Vector3)(ref extents)).magnitude * 0.32f)); Transform transform2 = val4.transform; Vector3 one2 = Vector3.one; extents = ((Bounds)(ref val5)).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 val5)).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 void UpdateCursedDelivery(ContractInstance active) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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) if (active.Status != ContractStatus.Active || !active.SelectedTargets.TryGetValue("targetObjectId", out string value) || string.IsNullOrWhiteSpace(value)) { return; } GameObject val = FindObjectByInstanceId(value); if ((Object)(object)val == (Object)null || !val.activeInHierarchy) { active.Fail("Cursed item was lost."); ResolveActiveContract(completed: false); return; } Vector3 position = val.transform.position; if (cursedTargetObjectId != value || Vector3.Distance(position, cursedLastPosition) > 0.16f) { ResetCursedTimer(val); return; } float num = cursedUntouchedDeadline - Time.time; if (!cursedWarningIssued && num <= 5f) { cursedWarningIssued = true; hud.NotifyDiscipline("Curse warning: move the marked item."); } if (!(num > 0f)) { active.Fail("Cursed item was abandoned."); ResolveActiveContract(completed: false); Plugin.Log.LogInfo((object)"[RepoContracts] Cursed Delivery failed: marked item was abandoned."); } } private void ResetCursedTimer(GameObject target) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) cursedTargetObjectId = ((Object)target).GetInstanceID().ToString(); cursedLastPosition = target.transform.position; cursedUntouchedDeadline = Time.time + (float)GetCursedUntouchedSeconds(); cursedWarningIssued = false; } private void ClearCursedTimer() { //IL_000c: 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) cursedTargetObjectId = string.Empty; cursedLastPosition = Vector3.zero; cursedUntouchedDeadline = 0f; cursedWarningIssued = false; } private static int GetCursedUntouchedSeconds() { return Plugin.FailureStrictness.Value switch { FailureStrictness.Casual => 24, FailureStrictness.Hardcore => 10, _ => 16, }; } 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 effectiveRunLevel = ContractDifficultyScaler.GetEffectiveRunLevel(completedGameplayLevels); int num2 = ((effectiveRunLevel >= 20) ? 2 : ((effectiveRunLevel >= 10) ? 1 : 0)); int num3 = num2; return Math.Max(1, Math.Min(4, num + num3)); } 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 ContractInstance CreateScaledContract(ContractDefinition definition, int seed, int runLevel) { ContractInstance contractInstance = new ContractInstance(definition, seed); contractInstance.SetRewardRoll(ContractRewardTable.Roll(definition, seed, runLevel)); ContractDifficultyScaler.ApplyTo(contractInstance, runLevel); return contractInstance; } private bool ShouldOfferMilestone(int runLevel) { if (Plugin.EnableMilestoneContracts.Value && Plugin.MilestoneContractEveryXLevels.Value > 0 && runLevel > 1) { return (runLevel - 1) % 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 IsActiveMarkedValuableTarget(GameObject item) { ContractInstance activeContract = Queue.ActiveContract; string text = activeContract?.Definition.Id; bool flag = ((text == "glass_cargo" || text == "cursed_loot") ? true : false); if (flag && 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 static class ContractRewardTable { internal static RewardRoll Roll(ContractDefinition definition, int seed, int runLevel) { RewardRoll rewardRoll = definition.RewardRoll; if (rewardRoll.Amount <= 1 || definition.Category == ContractCategory.Milestone) { return rewardRoll; } int num = new Random(HashCode.Combine(seed, definition.Id, runLevel, "repo-contract-reward")).Next(100); return definition.RewardRoll.Rarity switch { ContractRarity.Common => (num < 83) ? ((num >= 58) ? Shop(rewardRoll) : Money(rewardRoll)) : ((num >= 95) ? Surprise(rewardRoll) : Upgrade(rewardRoll)), ContractRarity.Uncommon => (num < 86) ? ((num < 45) ? Money(rewardRoll) : ((num >= 70) ? Upgrade(rewardRoll) : Shop(rewardRoll))) : ((num >= 96) ? Surprise(rewardRoll) : Weapon(rewardRoll)), ContractRarity.Rare => (num < 75) ? ((num < 35) ? Money(rewardRoll) : ((num >= 56) ? Weapon(rewardRoll) : Shop(rewardRoll))) : ((num >= 91) ? Surprise(rewardRoll) : Upgrade(rewardRoll)), ContractRarity.Epic => (num < 69) ? ((num < 24) ? Money(rewardRoll) : ((num >= 47) ? Upgrade(rewardRoll) : Weapon(rewardRoll))) : ((num >= 84) ? Surprise(rewardRoll) : Shop(rewardRoll)), ContractRarity.Legendary => (num < 61) ? ((num < 15) ? Money(rewardRoll) : ((num >= 38) ? Upgrade(rewardRoll) : Weapon(rewardRoll))) : ((num >= 78) ? Surprise(rewardRoll) : Shop(rewardRoll)), _ => rewardRoll, }; } private static RewardRoll Money(RewardRoll fallback) { return RewardRoll.MoneyBag(fallback.Rarity, fallback.Amount, $"+${fallback.Amount:N0} money bag"); } private static RewardRoll Shop(RewardRoll fallback) { return RewardRoll.ShopItem(fallback.Rarity, fallback.Amount, $"Random shop item or +${fallback.Amount:N0}"); } private static RewardRoll Weapon(RewardRoll fallback) { return RewardRoll.Weapon(fallback.Rarity, fallback.Amount, $"Random weapon or +${fallback.Amount:N0}"); } private static RewardRoll Upgrade(RewardRoll fallback) { return RewardRoll.Upgrade(fallback.Rarity, fallback.Amount, $"Random upgrade or +${fallback.Amount:N0}"); } private static RewardRoll Surprise(RewardRoll fallback) { return RewardRoll.SurpriseBox(fallback.Rarity, fallback.Amount, $"Surprise present or +${fallback.Amount:N0}"); } } internal sealed class PressurePlateContractRuntime : MonoBehaviour { private sealed class PlateState { internal GameObject GameObject { get; } internal TextMesh Label { get; } internal Light Light { get; } internal GameObject Beacon { get; } internal bool Secured { get; set; } internal PlateState(GameObject gameObject, TextMesh label, Light light, GameObject beacon) { GameObject = gameObject; Label = label; Light = light; Beacon = beacon; } } private readonly List plates = new List(); private const float PlateSize = 2.25f; private const float PlateHeight = 0.12f; 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) 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.08f; val.transform.localScale = new Vector3(2.25f, 0.12f, 2.25f); Collider component = val.GetComponent(); if ((Object)(object)component != (Object)null) { component.isTrigger = true; } SetColor(val, new Color(0.42f, 0.32f, 0.09f, 1f)); GameObject val2 = GameObject.CreatePrimitive((PrimitiveType)2); ((Object)val2).name = $"Pressure Plate Beacon {i + 1}"; val2.transform.SetParent(val.transform, false); val2.transform.localPosition = new Vector3(0f, 5.4f, 0f); val2.transform.localScale = new Vector3(0.055f, 4.2f, 0.055f); Collider component2 = val2.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } SetColor(val2, new Color(0.9f, 0.68f, 0.16f, 0.92f)); GameObject val3 = new GameObject($"RepoContracts Pressure Plate Label {i + 1}"); val3.transform.SetParent(val.transform, false); val3.transform.localPosition = new Vector3(0f, 9.2f, 0f); val3.transform.localRotation = Quaternion.identity; TextMesh val4 = val3.AddComponent(); val4.anchor = (TextAnchor)4; val4.alignment = (TextAlignment)1; val4.fontSize = 24; val4.characterSize = 0.03f; val4.text = $"PLATE {i + 1}"; ContractAssets.ApplyTextMaterial(val4, new Color(1f, 0.9f, 0.48f, 0.94f)); Light val5 = val.AddComponent(); val5.type = (LightType)2; val5.range = 3.2f; val5.intensity = 0.28f; val5.color = new Color(0.95f, 0.68f, 0.18f); plates.Add(new PlateState(val, val4, val5, val2)); } } 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0173: 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.75f; GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "Controlled Extraction Panel"; obj.transform.SetParent(extractionMarker.transform, false); obj.transform.localScale = new Vector3(3.4f, 0.08f, 0.72f); obj.transform.localPosition = Vector3.zero; Collider component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } SetColor(obj, new Color(0.03f, 0.18f, 0.14f, 1f)); TextMesh obj2 = extractionMarker.AddComponent(); obj2.text = "PRESSURE PLATES HERE"; obj2.anchor = (TextAnchor)4; obj2.alignment = (TextAlignment)1; obj2.fontSize = 36; obj2.characterSize = 0.03f; ContractAssets.ApplyTextMaterial(obj2, new Color(0.72f, 1f, 0.9f, 0.96f)); Light obj3 = extractionMarker.AddComponent(); obj3.type = (LightType)2; obj3.range = 4.5f; obj3.intensity = 0.35f; obj3.color = new Color(0.15f, 0.75f, 0.55f); } } 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_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: 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_0189: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0277: 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(2.25f * num2, 0.12f, 2.25f * num2); Renderer component = plateState.GameObject.GetComponent(); if ((Object)(object)component != (Object)null) { component.material.color = (plateState.Secured ? new Color(0.08f, 0.34f, 0.18f, 1f) : new Color(0.46f, 0.33f, 0.08f, 1f)); } plateState.Label.text = (plateState.Secured ? $"PLATE {i + 1} OK" : $"PLATE {i + 1} {num:0.#}/{requiredItemWeight:0.#}"); SetTextColor(plateState.Label, plateState.Secured ? new Color(0.58f, 1f, 0.68f, 0.86f) : new Color(1f, 0.86f, 0.36f, 0.95f)); SetColor(plateState.Beacon, plateState.Secured ? new Color(0.18f, 0.95f, 0.32f, 0.92f) : new Color(0.9f, 0.68f, 0.16f, 0.92f)); plateState.Light.color = (plateState.Secured ? new Color(0.15f, 0.85f, 0.35f) : new Color(0.85f, 0.55f, 0.12f)); plateState.Light.intensity = (plateState.Secured ? 0.36f : 0.25f); if ((Object)(object)Camera.main != (Object)null) { ((Component)plateState.Label).transform.rotation = Quaternion.LookRotation(((Component)plateState.Label).transform.position - ((Component)Camera.main).transform.position, Vector3.up); } } 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(1.14f, 0.62f, 1.14f), 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.35f) { Vector2 val2 = new Vector2(val.x, val.z); return ((Vector2)(ref val2)).magnitude <= 1.25f; } 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); } 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 SetTextColor(TextMesh text, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) text.color = color; Renderer component = ((Component)text).GetComponent(); if ((Object)(object)component != (Object)null) { component.material.color = color; } } } 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 RewardRoll WithAmount(int amount, string previewText) { return new RewardRoll(Kind, Rarity, amount, RewardId, previewText); } internal RewardRoll WithPreview(string previewText) { return new RewardRoll(Kind, Rarity, Amount, RewardId, 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 SurpriseBox(ContractRarity rarity, int fallbackCashAmount, string previewText) { return new RewardRoll(RewardKind.SurpriseBox, rarity, fallbackCashAmount, "surprise_box", 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); } } }