Decompiled source of TasksRevived v1.2.4
Tasks.dll
Decompiled 3 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using Microsoft.CodeAnalysis; using On.EntityStates; using On.RoR2; using On.RoR2.UI; using R2API; using R2API.Networking; using R2API.Networking.Interfaces; using R2API.Utils; using RoR2; using RoR2.Artifacts; using RoR2.Stats; using RoR2.UI; using RoR2BepInExPack.GameAssetPathsBetter; using TMPro; using UnityEngine; using UnityEngine.AddressableAssets; using UnityEngine.Networking; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Tasks")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5b08c45b6bd7226a94bce6c28bd1872d0c40934d")] [assembly: AssemblyProduct("Tasks")] [assembly: AssemblyTitle("Tasks")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 Tasks { internal class AirKills : Task { private int[] kills; private int killsNeeded = 3; private List<HitGroundDelegate> groundDelegateList = new List<HitGroundDelegate>(); protected new string description { get; } = "Get 3 kills whilst airborne"; public override TaskType type { get; } = TaskType.AirKills; protected override string name { get; } = "Air Kills"; public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + "."; } protected override void SetHooks(int numPlayers) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown Debug.Log((object)$"Set Hooks in AirKills. {numPlayers} players"); base.SetHooks(numPlayers); kills = new int[numPlayers]; GlobalEventManager.onCharacterDeathGlobal += OnKill; for (int i = 0; i < totalNumberPlayers; i++) { CharacterMaster playerCharacterMaster = TasksPlugin.GetPlayerCharacterMaster(i); if (playerCharacterMaster == null || playerCharacterMaster.GetBody() == null || playerCharacterMaster.GetBody().characterMotor == null) { break; } int tempInt = i; HitGroundDelegate item = (HitGroundDelegate)delegate { PlayerHitGround(tempInt); }; groundDelegateList.Add(item); playerCharacterMaster.GetBody().characterMotor.onHitGround += groundDelegateList[tempInt]; } } protected override void Unhook() { GlobalEventManager.onCharacterDeathGlobal -= OnKill; for (int i = 0; i < totalNumberPlayers; i++) { CharacterMaster playerCharacterMaster = TasksPlugin.GetPlayerCharacterMaster(i); if (playerCharacterMaster == null || playerCharacterMaster.GetBody() == null || playerCharacterMaster.GetBody().characterMotor == null) { break; } int index = i; playerCharacterMaster.GetBody().characterMotor.onHitGround -= groundDelegateList[index]; } ResetAllKills(); base.Unhook(); } private void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { progress[i] = (float)kills[i] / (float)killsNeeded; } base.UpdateProgress(progress); } protected override bool IsComplete(int playerNum) { return kills[playerNum] >= killsNeeded; } public void OnKill(DamageReport damageReport) { if (damageReport == null || damageReport.attackerMaster == null || damageReport.attackerMaster.playerCharacterMasterController == null) { return; } int playerNumber = TasksPlugin.GetPlayerNumber(damageReport.attackerMaster); if (!damageReport.attackerMaster.GetBody().characterMotor.isGrounded) { kills[playerNumber]++; UpdateProgress(); if (IsComplete(playerNumber)) { CompleteTask(playerNumber); ResetAllKills(); } } } private void PlayerHitGround(int playerNum) { UpdateProgress(); kills[playerNum] = 0; } private void ResetAllKills() { if (kills != null) { for (int i = 0; i < kills.Length; i++) { kills[i] = 0; } ResetProgress(); } } } internal class BabyDrone : Task { private CharacterMaster[] drones; private CharacterMaster createdDrone; private bool[] playerFailed; private int numPlayersFailed; private bool alreadyAwarded; public override TaskType type { get; } = TaskType.BabyDrone; protected override string name { get; } = "Baby Drone"; public override bool CanActivate(int numPlayers) { return true; } public override string GetDescription() { return "Keep your drone alive"; } public override string GetWinMessage(int winningPlayer) { if (winningPlayer < 0) { return "You failed " + GetStylizedTaskName(name) + " by letting your drone die."; } return GetStylizedName(winningPlayer) + " kept their drone alive the longest. Completed " + GetStylizedTaskName(name) + "."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in BabyDrone. {numPlayers} players"); base.SetHooks(numPlayers); drones = (CharacterMaster[])(object)new CharacterMaster[numPlayers]; playerFailed = new bool[numPlayers]; numPlayersFailed = 0; alreadyAwarded = false; for (int i = 0; i < numPlayers; i++) { playerFailed[i] = false; SpawnDrone(i); } GlobalEventManager.onCharacterDeathGlobal += OnKill; } protected override void StageEnd() { base.StageEnd(); Evaluate(); ResetProgress(); } protected override void Unhook() { GlobalEventManager.onCharacterDeathGlobal -= OnKill; base.Unhook(); } private void Evaluate() { if (alreadyAwarded) { return; } for (int i = 0; i < playerFailed.Length; i++) { if (!playerFailed[i]) { UpdateProgressMultiWinner(); CompleteTask(i); } } } private void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { if (totalNumberPlayers > 1) { progress[i] = ((!playerFailed[i]) ? (numPlayersFailed / (totalNumberPlayers - 1)) : 0); } else { progress[i] = 0f; } } base.UpdateProgress(progress); } private void UpdateProgressMultiWinner() { for (int i = 0; i < progress.Length; i++) { progress[i] = ((!playerFailed[i]) ? 1 : 0); } base.UpdateProgress(progress); } public void OnKill(DamageReport damageReport) { if (damageReport == null || damageReport.victimMaster == null) { return; } for (int i = 0; i < drones.Length; i++) { if (playerFailed[i] || !((Object)(object)damageReport.victimMaster == (Object)(object)drones[i])) { continue; } string text = $"BABY_DRONE_{i}"; ChatMessage.Send(GetDroneDeathMessage(Language.currentLanguage.GetLocalizedStringByToken(text))); playerFailed[i] = true; numPlayersFailed++; UpdateProgress(); if (totalNumberPlayers > 1) { if (numPlayersFailed < totalNumberPlayers - 1) { continue; } for (int j = 0; j < playerFailed.Length; j++) { if (!playerFailed[j]) { alreadyAwarded = true; CompleteTask(j); return; } } } else { CompleteTask(-1); } } } private void SpawnDrone(int playerNum) { //IL_001c: 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_0023: 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_002f: 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_004d: 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_005e: Expected O, but got Unknown //IL_005e: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown CharacterBody body = TasksPlugin.GetPlayerCharacterMaster(playerNum).GetBody(); GameObject val = DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(Resources.Load<SpawnCard>("SpawnCards/InteractableSpawnCard/iscbrokendrone1"), new DirectorPlacementRule { placementMode = (PlacementMode)0, position = body.transform.position + new Vector3(1f, 0f, 0f) }, new Xoroshiro128Plus(0uL))); PurchaseInteraction component = val.GetComponent<PurchaseInteraction>(); component.cost = 0; Interactor component2 = ((Component)body).GetComponent<Interactor>(); if (Object.op_Implicit((Object)(object)component2)) { MasterSummon.Perform += new hook_Perform(GrabDrone); component.OnInteractionBegin(component2); MasterSummon.Perform -= new hook_Perform(GrabDrone); string text = $"BABY_DRONE_{playerNum}"; Language.currentLanguage.SetStringByToken(text, GetDroneName(playerNum)); CharacterBody body2 = createdDrone.GetBody(); body2.baseNameToken = text; body2.baseRegen = 0f; drones[playerNum] = createdDrone; ((MonoBehaviour)TasksPlugin.instance).StartCoroutine(DamageDrone(body2)); } } private CharacterMaster GrabDrone(orig_Perform orig_Perform, MasterSummon self) { return createdDrone = orig_Perform.Invoke(self); } private string GetDroneName(int playerNum) { string displayName = ((Component)TasksPlugin.GetPlayerCharacterMaster(playerNum)).GetComponent<PlayerCharacterMasterController>().GetDisplayName(); WeightedSelection<string> val = new WeightedSelection<string>(14); val.AddChoice(displayName + " Junior", 1f); val.AddChoice(displayName + " Jr.", 1f); val.AddChoice("Baby " + displayName, 1f); val.AddChoice("Little " + displayName, 1f); val.AddChoice("Li'l " + displayName, 1f); val.AddChoice(displayName + " the Second", 1f); val.AddChoice(displayName + " the Third", 1f); val.AddChoice(displayName + " II", 1f); val.AddChoice(displayName + " III", 1f); val.AddChoice(displayName + " the Younger", 1f); val.AddChoice("Mini " + displayName, 1f); val.AddChoice(displayName + " Mk II", 1f); val.AddChoice(displayName + " 2.0", 1f); val.AddChoice(displayName + " (Clone)", 1f); return val.Evaluate(Random.value); } private string GetDroneDeathMessage(string droneName) { WeightedSelection<string> val = new WeightedSelection<string>(8); val.AddChoice(droneName + " died. And it was all your fault.", 1f); val.AddChoice(droneName + " died. And you just let it happen.", 1f); val.AddChoice(droneName + " died because you didn't love him enough.", 1f); val.AddChoice(droneName + " died. Play on an easier difficulty so this doesn't happen again.", 1f); val.AddChoice("You killed " + droneName + ". You monster.", 1f); val.AddChoice("You let " + droneName + " die. You monster.", 1f); val.AddChoice("You let " + droneName + " die. What did he ever do to you?", 1f); val.AddChoice(droneName + " said goodbye. When he died.", 1f); val.AddChoice(droneName + " died. You can repair him, but it won't be the same " + droneName + ".", 1f); val.AddChoice(droneName + " has went to a junkyard in the country.", 1f); val.AddChoice(droneName + " has went to a junkyard in the country.", 1f); val.AddChoice("MISSION FAILURE! " + droneName + " has been lost.", 1f); val.AddChoice("Well, " + droneName + " has been lost. Hope you're happy", 1f); val.AddChoice("Well, " + droneName + " is gone. Hope you're happy", 1f); val.AddChoice(droneName + " has gone and there shall never be another like him.", 1f); val.AddChoice("RIP " + droneName + ", the goodest boy.", 1f); val.AddChoice("RIP " + droneName + ".", 1f); val.AddChoice("At least " + droneName + " didn't suffer.", 1f); val.AddChoice(droneName + " deserved better.", 1f); return "<style=cDeath><sprite name=\"Skull\" tint=1> " + val.Evaluate(Random.value) + " <sprite name=\"Skull\" tint=1></style>"; } private IEnumerator DamageDrone(CharacterBody droneBody) { yield return (object)new WaitForSeconds(1f); DamageInfo damageInfo = new DamageInfo { damage = droneBody.healthComponent.combinedHealth, position = droneBody.corePosition, force = Vector3.zero, damageColorIndex = (DamageColorIndex)0, crit = false, attacker = null, inflictor = null, damageType = DamageTypeCombo.op_Implicit((DamageType)3), procCoefficient = 0f, procChainMask = default(ProcChainMask) }; droneBody.healthComponent.TakeDamage(damageInfo); } } internal class BiggestHit : Task { private float[] biggestHit; protected new string description { get; } = "Biggest hit wins"; public override TaskType type { get; } = TaskType.BiggestHit; protected override string name { get; } = "Biggest Hit"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " with the big hit of " + GetStylizedTaskWinStat(biggestHit[winningPlayer].ToString()) + " damage."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in BiggestHit. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onServerDamageDealt += OnDamage; if (biggestHit == null || biggestHit.Length != numPlayers) { biggestHit = new float[numPlayers]; } Reset(); } protected override void StageEnd() { base.StageEnd(); Evaluate(); Reset(); } protected override void Unhook() { GlobalEventManager.onServerDamageDealt -= OnDamage; base.Unhook(); } protected void UpdateProgress() { float num = 0f; for (int i = 0; i < biggestHit.Length; i++) { if (biggestHit[i] > num) { num = biggestHit[i]; } } if (num > 0f) { for (int j = 0; j < progress.Length; j++) { progress[j] = biggestHit[j] / num; } } else { for (int k = 0; k < progress.Length; k++) { progress[k] = 0f; } } base.UpdateProgress(progress); } public void OnDamage(DamageReport report) { if (report != null && report.attackerMaster != null && report.attackerMaster.playerCharacterMasterController != null) { int playerNumber = TasksPlugin.GetPlayerNumber(report.attackerMaster); float damageDealt = report.damageDealt; if (damageDealt > biggestHit[playerNumber]) { biggestHit[playerNumber] = damageDealt; UpdateProgress(); } } } private void Evaluate() { float num = 0f; int playerNum = 0; for (int i = 0; i < biggestHit.Length; i++) { if (biggestHit[i] > num) { num = biggestHit[i]; playerNum = i; } } CompleteTask(playerNum); } private void Reset() { if (biggestHit != null) { for (int i = 0; i < biggestHit.Length; i++) { biggestHit[i] = 0f; } ResetProgress(); } } } internal class ConfigManager { public static ConfigManager instance; private ConfigFile config; public static ConfigEntry<int> TasksPerPlayer { get; set; } public static ConfigEntry<int> AdditionalTasks { get; set; } public static ConfigEntry<float> AirKillsWeight { get; set; } public static ConfigEntry<float> DamageMultipleWeight { get; set; } public static ConfigEntry<float> DamageInTimeWeight { get; set; } public static ConfigEntry<float> StayInAirWeight { get; set; } public static ConfigEntry<float> BiggestHitWeight { get; set; } public static ConfigEntry<float> MostDistanceWeight { get; set; } public static ConfigEntry<float> PreonEventWeight { get; set; } public static ConfigEntry<float> FarthestAwayWeight { get; set; } public static ConfigEntry<float> FailShrineWeight { get; set; } public static ConfigEntry<float> OpenChestsWeight { get; set; } public static ConfigEntry<float> StartTeleWeight { get; set; } public static ConfigEntry<float> UsePrintersWeight { get; set; } public static ConfigEntry<float> OrderedSkillsWeight { get; set; } public static ConfigEntry<float> BadSkillWeight { get; set; } public static ConfigEntry<float> BabyDroneWeight { get; set; } public static ConfigEntry<float> DieWeight { get; set; } public static ConfigEntry<float> FindLockboxWeight { get; set; } public static ConfigEntry<float> HealingItemWeight { get; set; } public static ConfigEntry<float> NoJumpWeight { get; set; } public static ConfigEntry<float> VeryBestWeight { get; set; } public static ConfigEntry<float> FewestElitesWeight { get; set; } public static ConfigEntry<float> GetLuckyWeight { get; set; } public static ConfigEntry<float> GetLowWeight { get; set; } public static ConfigEntry<float> KillStreakWeight { get; set; } public static ConfigEntry<float> QuickDrawWeight { get; set; } public static ConfigEntry<float> FarKillWeight { get; set; } public static ConfigEntry<float> FewEliteWeight { get; set; } public ConfigManager() { if (instance == null) { instance = this; } } public void Awake() { SetupWeights(); } public void SetConfigFile(ConfigFile c) { config = c; } private void SetupWeights() { TasksPerPlayer = config.Bind<int>("TaskOptions", "TasksPerPlayer", 1, "Number of tasks per player. Number of tasks: (TasksPerPlayer * number of player) + AdditionalTasks"); AdditionalTasks = config.Bind<int>("TaskOptions", "AdditionalTasks", 2, "Extra tasks. Number of tasks: (TasksPerPlayer * number of player) + AdditionalTasks"); AirKillsWeight = config.Bind<float>("TaskWeights", "AirKills", 1.5f, "Relative weight of this task. Bigger number means more likely to roll task. 0 for no chance."); DamageMultipleWeight = config.Bind<float>("TaskWeights", "DamageMultiple", 2f, "Relative weight of this task."); DamageInTimeWeight = config.Bind<float>("TaskWeights", "DamageInTime", 2f, "Relative weight of this task. Do x damage within y seconds"); StayInAirWeight = config.Bind<float>("TaskWeights", "StayInAir", 1.5f, "Relative weight of this task."); BiggestHitWeight = config.Bind<float>("TaskWeights", "BiggestHit", 2f, "Relative weight of this task."); MostDistanceWeight = config.Bind<float>("TaskWeights", "MostDistance", 1f, "Relative weight of this task."); PreonEventWeight = config.Bind<float>("TaskWeights", "PreonEvent", 1f, "Relative weight of this task."); FarthestAwayWeight = config.Bind<float>("TaskWeights", "FarthestAway", 1.5f, "Relative weight of this task."); FailShrineWeight = config.Bind<float>("TaskWeights", "FailShrine", 1f, "Relative weight of this task."); OpenChestsWeight = config.Bind<float>("TaskWeights", "OpenChests", 2f, "Relative weight of this task."); StartTeleWeight = config.Bind<float>("TaskWeights", "StartTele", 2f, "Relative weight of this task."); UsePrintersWeight = config.Bind<float>("TaskWeights", "UsePrinters", 1f, "Relative weight of this task."); OrderedSkillsWeight = config.Bind<float>("TaskWeights", "OrderedSkills", 0f, "Relative weight of this task. Use abilities in left-to-right order. This task sucks."); BadSkillWeight = config.Bind<float>("TaskWeights", "BadSkill", 0.5f, "Relative weight of this task. Don't use utility skill"); BabyDroneWeight = config.Bind<float>("TaskWeights", "BabyDrone", 0.75f, "Relative weight of this task. Keep your drone alive"); DieWeight = config.Bind<float>("TaskWeights", "Die", 0.25f, "Relative weight of this task."); FindLockboxWeight = config.Bind<float>("TaskWeights", "FindLockbox", 2f, "Relative weight of this task."); HealingItemWeight = config.Bind<float>("TaskWeights", "HealingItem", 1.5f, "Relative weight of this task. Find a healing item"); NoJumpWeight = config.Bind<float>("TaskWeights", "NoJump", 1f, "Relative weight of this task."); VeryBestWeight = config.Bind<float>("TaskWeights", "VeryBest", 1.5f, "Relative weight of this task. Be the very best. Like no one ever was."); FewestElitesWeight = config.Bind<float>("TaskWeights", "FewestElites", 1.5f, "Relative weight of this task."); GetLuckyWeight = config.Bind<float>("TaskWeights", "GetLucky", 0.5f, "Relative weight of this task."); GetLowWeight = config.Bind<float>("TaskWeights", "GetLow", 1.5f, "Relative weight of this task."); KillStreakWeight = config.Bind<float>("TaskWeights", "KillStreaks", 1.5f, "Relative weight of this task."); QuickDrawWeight = config.Bind<float>("TaskWeights", "QuickDraw", 1.5f, "Relative weight of this task. Kill something that spawned 3s ago"); FarKillWeight = config.Bind<float>("TaskWeights", "FarKill", 1f, "Relative weight of this task. Kill something far away from where you last left the ground"); FewEliteWeight = config.Bind<float>("TaskWeights", "FewElites", 1f, "Relative weight of this task. Kill the boss before you kill 10 elites"); } public float GetTaskWeight(TaskType type) { return type switch { TaskType.AirKills => AirKillsWeight.Value, TaskType.DamageMultiple => DamageMultipleWeight.Value, TaskType.DamageInTime => DamageInTimeWeight.Value, TaskType.StayInAir => StayInAirWeight.Value, TaskType.BiggestHit => BiggestHitWeight.Value, TaskType.MostDistance => MostDistanceWeight.Value, TaskType.PreonEvent => PreonEventWeight.Value, TaskType.FarthestAway => FarthestAwayWeight.Value, TaskType.FailShrine => FailShrineWeight.Value, TaskType.OpenChests => OpenChestsWeight.Value, TaskType.StartTele => StartTeleWeight.Value, TaskType.UsePrinters => UsePrintersWeight.Value, TaskType.OrderedSkills => OrderedSkillsWeight.Value, TaskType.BadSkill => BadSkillWeight.Value, TaskType.BabyDrone => BabyDroneWeight.Value, TaskType.Die => DieWeight.Value, TaskType.FindLockbox => FindLockboxWeight.Value, TaskType.HealingItem => HealingItemWeight.Value, TaskType.NoJump => NoJumpWeight.Value, TaskType.VeryBest => VeryBestWeight.Value, TaskType.FewestElites => FewestElitesWeight.Value, TaskType.GetLucky => GetLuckyWeight.Value, TaskType.GetLow => GetLowWeight.Value, TaskType.KillStreak => KillStreakWeight.Value, TaskType.QuickDraw => QuickDrawWeight.Value, TaskType.FarKill => FarKillWeight.Value, TaskType.FewElites => FewEliteWeight.Value, _ => 1f, }; } public int GetNumberOfTasks(int numPlayers) { return numPlayers * TasksPerPlayer.Value + AdditionalTasks.Value; } } internal class DamageMultipleTargets : Task { private int numToHit = 4; private HashSet<GameObject>[] targets; protected new string description { get; } = "Have 4 enemies damaged by you alive at once"; public override TaskType type { get; } = TaskType.DamageMultiple; protected override string name { get; } = "Damage Multiple"; public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by having " + GetStylizedTaskWinStat(numToHit.ToString()) + " enemies damaged, but alive, at once."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in DamageMultiple. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onServerDamageDealt += OnDamage; GlobalEventManager.onCharacterDeathGlobal += OnKill; targets = new HashSet<GameObject>[numPlayers]; for (int i = 0; i < targets.Length; i++) { targets[i] = new HashSet<GameObject>(); } } protected override void Unhook() { GlobalEventManager.onServerDamageDealt -= OnDamage; GlobalEventManager.onCharacterDeathGlobal -= OnKill; ResetKills(); base.Unhook(); } private void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { progress[i] = (float)targets[i].Count / (float)numToHit; } base.UpdateProgress(progress); } public void OnDamage(DamageReport report) { if (report == null || report.attackerMaster == null || report.attackerMaster.playerCharacterMasterController == null) { return; } int playerNumber = TasksPlugin.GetPlayerNumber(report.attackerMaster); if (report.victim.alive && !targets[playerNumber].Contains(((Component)report.victim).gameObject)) { targets[playerNumber].Add(((Component)report.victim).gameObject); UpdateProgress(); if (IsComplete(playerNumber)) { CompleteTask(playerNumber); ResetKills(); } } } protected override bool IsComplete(int playerNum) { return targets[playerNum].Count >= numToHit; } private void OnKill(DamageReport report) { if (report != null && report.attackerMaster != null && report.attackerMaster.playerCharacterMasterController != null) { int playerNumber = TasksPlugin.GetPlayerNumber(report.attackerMaster); targets[playerNumber].Remove(((Component)report.victim).gameObject); UpdateProgress(); } } private void ResetKills() { if (targets != null) { for (int i = 0; i < targets.Length; i++) { targets[i].Clear(); } ResetProgress(); } } } internal class DealDamageInTime : Task { private float damageToDeal = 500f; private float timeLimit = 5f; private float[] currentDamage; private bool active = false; protected new string description { get; } = "Deal 500 damage in 5 seconds"; public override TaskType type { get; } = TaskType.DamageInTime; protected override string name { get; } = "Damage In Time"; public override string GetDescription() { return $"Deal {damageToDeal:N0} damage in 5 seconds"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by dealing " + GetStylizedTaskWinStat(damageToDeal.ToString()) + " damage within 5s."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in DamageInTime. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onServerDamageDealt += OnDamage; damageToDeal = Run.instance.difficultyCoefficient * damageToDeal; if (currentDamage == null || currentDamage.Length != numPlayers) { currentDamage = new float[numPlayers]; } active = true; Reset(); } protected override void Unhook() { GlobalEventManager.onServerDamageDealt -= OnDamage; Reset(); base.Unhook(); } private void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { progress[i] = currentDamage[i] / damageToDeal; } base.UpdateProgress(progress); } public void OnDamage(DamageReport report) { if (report != null && report.attackerMaster != null && report.attackerMaster.playerCharacterMasterController != null) { int playerNumber = TasksPlugin.GetPlayerNumber(report.attackerMaster); currentDamage[playerNumber] += report.damageDealt; UpdateProgress(); ((MonoBehaviour)TasksPlugin.instance).StartCoroutine(ReduceDamage(report.damageDealt, playerNumber)); if (IsComplete(playerNumber)) { active = false; CompleteTask(playerNumber); Reset(); } } } protected override bool IsComplete(int playerNum) { return currentDamage[playerNum] >= damageToDeal; } private IEnumerator ReduceDamage(float damage, int playerNum) { yield return (object)new WaitForSeconds(timeLimit); currentDamage[playerNum] = Mathf.Max(0f, currentDamage[playerNum] - damage); if (active) { UpdateProgress(); } } private void Reset() { if (currentDamage != null) { for (int i = 0; i < currentDamage.Length; i++) { currentDamage[i] = 0f; } ResetProgress(); } } } internal class Die : Task { public override TaskType type { get; } = TaskType.Die; protected override string name { get; } = "Die"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return "Die"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by dying first. Congratulations."; } protected override void SetHooks(int numPlayers) { base.SetHooks(numPlayers); GlobalEventManager.onCharacterDeathGlobal += OnKill; } protected override void Unhook() { GlobalEventManager.onCharacterDeathGlobal -= OnKill; base.Unhook(); } public void OnKill(DamageReport damageReport) { if (damageReport != null && damageReport.victimMaster != null) { int playerNumber = TasksPlugin.GetPlayerNumber(damageReport.victimMaster); if (playerNumber > -1) { CompleteTask(playerNumber); } } } } internal class DontUseSkill : Task { private SkillSlot badSkill; private bool[] playerFailed; private int numPlayersFailed; protected new string description { get; } = "Last to use their utility skill wins"; public override TaskType type { get; } = TaskType.BadSkill; protected override string name { get; } = "Bad Skill"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by not using their " + GetStylizedTaskWinStat(((object)(SkillSlot)(ref badSkill)).ToString()) + " skill."; } protected override void SetHooks(int numPlayers) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) Debug.Log((object)$"Set Hooks in DontUseSkill. {numPlayers} players"); base.SetHooks(numPlayers); badSkill = (SkillSlot)2; playerFailed = new bool[numPlayers]; Reset(); TasksPlugin.OnAbilityUsed += AbilityUsed; } protected override void StageEnd() { base.StageEnd(); Evaluate(); Reset(); } protected override void Unhook() { TasksPlugin.OnAbilityUsed -= AbilityUsed; base.Unhook(); } private void Evaluate() { if (totalNumberPlayers - numPlayersFailed <= 1) { return; } for (int i = 0; i < playerFailed.Length; i++) { if (!playerFailed[i]) { UpdateProgressMultiWinner(); CompleteTask(i); } } } private void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { progress[i] = ((!playerFailed[i]) ? (numPlayersFailed / (totalNumberPlayers - 1)) : 0); } base.UpdateProgress(progress); } private void UpdateProgressMultiWinner() { for (int i = 0; i < progress.Length; i++) { progress[i] = ((!playerFailed[i]) ? 1 : 0); } base.UpdateProgress(progress); } private void AbilityUsed(int playerNum, SkillSlot slot) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) if (slot != badSkill || playerFailed[playerNum]) { return; } Debug.Log((object)$"Player {playerNum} failed BadSkill"); playerFailed[playerNum] = true; numPlayersFailed++; UpdateProgress(); if (numPlayersFailed < totalNumberPlayers - 1) { return; } for (int i = 0; i < playerFailed.Length; i++) { if (!playerFailed[i]) { CompleteTask(i); break; } } } private void Reset() { if (playerFailed != null) { for (int i = 0; i < playerFailed.Length; i++) { playerFailed[i] = false; } ResetProgress(); } } } internal class FailShrine : Task { protected new string description { get; } = "First to Fail"; public override TaskType type { get; } = TaskType.FailShrine; protected override string name { get; } = "Fail a Chance Shrine"; public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by " + GetStylizedTaskWinStat("failing") + " a chance shrine."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in FailShrine. {numPlayers} players"); base.SetHooks(numPlayers); ShrineChanceBehavior.onShrineChancePurchaseGlobal += OnShrineChancePurchase; } protected override void Unhook() { ShrineChanceBehavior.onShrineChancePurchaseGlobal -= OnShrineChancePurchase; base.Unhook(); } private void OnShrineChancePurchase(bool failed, Interactor interactor) { if (!failed) { return; } for (int i = 0; i < totalNumberPlayers; i++) { if ((Object)(object)((Component)TasksPlugin.GetPlayerCharacterMaster(i).GetBody()).GetComponent<Interactor>() == (Object)(object)interactor) { CompleteTask(i); break; } } } } internal class FarKill : Task { private int distance = 70; private Vector3[] playerGroundPositions; private bool active = false; public override TaskType type { get; } = TaskType.FarKill; protected override string name { get; } = "Far Kill"; public override string GetDescription() { return $"Kill an enemy {distance}m from where you left the ground"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by killing something from a ways away."; } protected override void SetHooks(int numPlayers) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) Debug.Log((object)$"Set hooks in FarKill. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onCharacterDeathGlobal += OnKill; playerGroundPositions = (Vector3[])(object)new Vector3[numPlayers]; for (int i = 0; i < totalNumberPlayers; i++) { CharacterMaster playerCharacterMaster = TasksPlugin.GetPlayerCharacterMaster(i); playerGroundPositions[i] = playerCharacterMaster.GetBody().corePosition; } active = true; ((MonoBehaviour)TasksPlugin.instance).StartCoroutine(CalculatePositions()); } protected override void Unhook() { if (active) { active = false; GlobalEventManager.onCharacterDeathGlobal -= OnKill; base.Unhook(); } } private void OnKill(DamageReport report) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (report != null && report.attackerMaster != null && report.attackerMaster.playerCharacterMasterController != null) { int playerNumber = TasksPlugin.GetPlayerNumber(report.attackerMaster); float num = Vector3.Distance(report.victimBody.corePosition, playerGroundPositions[playerNumber]); if (num > (float)distance) { CompleteTask(playerNumber); return; } progress[playerNumber] = num / (float)distance; UpdateProgress(progress); } } private IEnumerator CalculatePositions() { while (active) { for (int i = 0; i < totalNumberPlayers; i++) { CharacterBody body = TasksPlugin.GetPlayerCharacterMaster(i).GetBody(); if (body.characterMotor.isGrounded) { playerGroundPositions[i] = body.corePosition; } } yield return null; } } } internal class FarthestAway : Task { private Vector3[] startPositions; private bool active = false; private IEnumerator timerRoutine; private float winnerDist = 0f; protected new string description { get; } = "Farthest away in 20s wins"; public override TaskType type { get; } = TaskType.FarthestAway; protected override string name { get; } = "Farthest From Spawn"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by being " + GetStylizedTaskWinStat(winnerDist.ToString("F2")) + "m away in 20 seconds."; } protected override void SetHooks(int numPlayers) { //IL_00a4: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) Debug.Log((object)$"Set Hooks in FarthestAway. {numPlayers} players"); base.SetHooks(numPlayers); if (startPositions == null || startPositions.Length != numPlayers) { startPositions = (Vector3[])(object)new Vector3[numPlayers]; } for (int i = 0; i < startPositions.Length; i++) { if (TasksPlugin.GetPlayerCharacterMaster(i).GetBody() == null) { Debug.Log((object)$"Task.FarthestAway player body is null. Player: {i}"); startPositions[i] = Vector3.zero; } else { startPositions[i] = TasksPlugin.GetPlayerCharacterMaster(i).GetBody().transform.position; } } timerRoutine = EndTask(); ((MonoBehaviour)TasksPlugin.instance).StartCoroutine(timerRoutine); active = true; } protected override void Unhook() { if (active) { active = false; if (timerRoutine != null) { ((MonoBehaviour)TasksPlugin.instance).StopCoroutine(timerRoutine); } base.Unhook(); } } private void UpdateProgress(int time) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) float[] array = new float[startPositions.Length]; float num = 0f; if (time > 0) { for (int i = 0; i < startPositions.Length; i++) { array[i] = Vector3.Distance(startPositions[i], TasksPlugin.GetPlayerCharacterMaster(i).GetBody().transform.position); if (array[i] > num) { num = array[i]; } } } if (num > 0f) { for (int j = 0; j < progress.Length; j++) { progress[j] = (float)time * (array[j] / num) / 20f; } } else { for (int k = 0; k < progress.Length; k++) { progress[k] = 0f; } } base.UpdateProgress(progress); } private IEnumerator EndTask() { for (int i = 0; i < 20; i++) { yield return (object)new WaitForSeconds(1f); UpdateProgress(i + 1); } Evaluate(); } private void Evaluate() { //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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) float num = 0f; int playerNum = 0; for (int i = 0; i < startPositions.Length; i++) { CharacterMaster playerCharacterMaster = TasksPlugin.GetPlayerCharacterMaster(i); if ((Object)(object)playerCharacterMaster == (Object)null) { continue; } CharacterBody body = playerCharacterMaster.GetBody(); if (!((Object)(object)body == (Object)null)) { Vector3 position = body.transform.position; float num2 = Vector3.Distance(startPositions[i], position); if (num2 > num) { num = num2; playerNum = i; } } } winnerDist = num; CompleteTask(playerNum); ResetProgress(); } } internal class FewElites : Task { private int[] kills; private int maxKills = 5; private bool taskFinished = false; public override TaskType type { get; } = TaskType.FewElites; protected override string name { get; } = "Few Elites"; public override bool CanActivate(int numPlayers) { return true; } public override string GetDescription() { return "Kill the boss before you kill 5 elites."; } public override string GetWinMessage(int winningPlayer) { if (winningPlayer < 0) { return "You failed " + GetStylizedTaskName(name) + " by killing too many elites."; } return $"{GetStylizedName(winningPlayer)} completed {GetStylizedTaskName(name)} by killing the boss while only killing {GetStylizedTaskWinStat(kills[winningPlayer].ToString())}/{maxKills} elites."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set hooks in FewElites. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onCharacterDeathGlobal += OnKill; BossGroup.onBossGroupDefeatedServer += BossDied; kills = new int[numPlayers]; taskFinished = false; Reset(); UpdateProgress(); } private void BossDied(BossGroup group) { Evaluate(); taskFinished = true; } protected override void StageEnd() { base.StageEnd(); Reset(); } protected override void Unhook() { GlobalEventManager.onCharacterDeathGlobal -= OnKill; BossGroup.onBossGroupDefeatedServer -= BossDied; base.Unhook(); } public void OnKill(DamageReport damageReport) { if (taskFinished || damageReport == null || damageReport.attackerMaster == null || damageReport.attackerMaster.playerCharacterMasterController == null) { return; } int playerNumber = TasksPlugin.GetPlayerNumber(damageReport.attackerMaster); if (damageReport.victimIsElite) { kills[playerNumber]++; if (totalNumberPlayers == 1 && kills[playerNumber] > maxKills) { CompleteTask(-1); } } UpdateProgress(); } private void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { if (taskFinished) { progress[i] = 0f; continue; } float num = (float)(maxKills - kills[i]) / (float)maxKills; num = Mathf.Max(num, 0f); progress[i] = num; } base.UpdateProgress(progress); } private void Evaluate() { if (kills == null) { return; } string text = ""; string text2 = ""; for (int i = 0; i < kills.Length; i++) { if (kills[i] <= maxKills) { CompleteTask(i); text += $"{i}, "; text2 += $"{kills[i]}, "; } } Debug.Log((object)$"Players {text} won with {text2} /{maxKills} kills"); } private void Reset() { if (kills != null) { taskFinished = false; for (int i = 0; i < kills.Length; i++) { kills[i] = 0; } ResetProgress(); } } } internal class FewestElites : Task { private int[] kills; public override TaskType type { get; } = TaskType.FewestElites; protected override string name { get; } = "Fewest Elites"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return "Kill the fewest elites."; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by killing the fewest (" + GetStylizedTaskWinStat(kills[winningPlayer].ToString()) + ") elites."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set hooks in FewestElites. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onCharacterDeathGlobal += OnKill; kills = new int[numPlayers]; Reset(); } protected override void StageEnd() { base.StageEnd(); Evaluate(); Reset(); } protected override void Unhook() { GlobalEventManager.onCharacterDeathGlobal -= OnKill; base.Unhook(); } public void OnKill(DamageReport damageReport) { if (damageReport != null && damageReport.attackerMaster != null && damageReport.attackerMaster.playerCharacterMasterController != null) { int playerNumber = TasksPlugin.GetPlayerNumber(damageReport.attackerMaster); if (damageReport.victimIsElite) { kills[playerNumber]++; } UpdateProgress(); } } private void UpdateProgress() { int num = 0; for (int i = 0; i < kills.Length; i++) { int num2 = kills[i]; if (num2 > num) { num = num2; } } if (num > 0) { for (int j = 0; j < progress.Length; j++) { progress[j] = (float)kills[j] / (float)num; } } else { for (int k = 0; k < progress.Length; k++) { progress[k] = 0f; } } base.UpdateProgress(progress); } private void Evaluate() { if (kills == null) { return; } int num = 0; int num2 = kills[0]; for (int i = 1; i < kills.Length; i++) { int num3 = kills[i]; if (num3 < num2) { num = i; num2 = num3; } } Debug.Log((object)$"Player {num} won with {num2} elite kills"); CompleteTask(num); } private void Reset() { if (kills != null) { for (int i = 0; i < kills.Length; i++) { kills[i] = 0; } ResetProgress(); } } } internal class FindLockbox : Task { public override TaskType type { get; } = TaskType.FindLockbox; protected override string name { get; } = "Find Lockbox"; public override bool CanActivate(int numPlayers) { int num = 0; foreach (CharacterMaster readOnlyInstances in CharacterMaster.readOnlyInstancesList) { num += readOnlyInstances.inventory.GetItemCount(Items.TreasureCache); } return num > 0; } public override string GetDescription() { return "Find the Lockbox"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by finding the lockbox first."; } protected override void SetHooks(int numPlayers) { base.SetHooks(numPlayers); GlobalEventManager.OnInteractionsGlobal += ChestsOpened; TasksPlugin.StartLockboxTask(); } protected override void Unhook() { GlobalEventManager.OnInteractionsGlobal -= ChestsOpened; base.Unhook(); } private void ChestsOpened(Interactor interactor, IInteractable interactable, GameObject go) { int playerNum = 0; for (int i = 0; i < totalNumberPlayers; i++) { if ((Object)(object)((Component)TasksPlugin.GetPlayerCharacterMaster(i).GetBody()).GetComponent<Interactor>() == (Object)(object)interactor) { playerNum = i; } } if (Object.op_Implicit((Object)(object)((go != null) ? go.GetComponent<ChestBehavior>() : null)) && ((Object)go).name.Contains("Lockbox")) { CompleteTask(playerNum); } } } internal class GetLow : Task { private float[] lowPercents; public override TaskType type { get; } = TaskType.GetLow; protected override string name { get; } = "Get Low"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return "Get to the lowest life. No dying."; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by getting down to " + GetStylizedTaskWinStat(lowPercents[winningPlayer].ToString("F2")) + "% hp."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in GetLow. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onServerDamageDealt += OnDamage; lowPercents = new float[numPlayers]; Reset(); } protected override void StageEnd() { base.StageEnd(); Evaluate(); Reset(); } protected override void Unhook() { GlobalEventManager.onServerDamageDealt -= OnDamage; base.Unhook(); } private void OnDamage(DamageReport report) { if (report == null || report.victimMaster == null || report.victim == null || report.victimMaster.playerCharacterMasterController == null) { return; } int playerNumber = TasksPlugin.GetPlayerNumber(report.victimMaster); float num = 100f * (report.victim.health + report.victim.shield) / report.victim.fullCombinedHealth; if (num > 0f) { if (num < lowPercents[playerNumber]) { lowPercents[playerNumber] = num; UpdateProgress(); } } else { lowPercents[playerNumber] = 101f; UpdateProgress(); } } protected void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { progress[i] = (100f - lowPercents[i]) / 100f; } base.UpdateProgress(progress); } private void Evaluate() { float num = 100f; int playerNum = 0; for (int i = 0; i < lowPercents.Length; i++) { if (lowPercents[i] < num) { num = lowPercents[i]; playerNum = i; } } CompleteTask(playerNum); } private void Reset() { if (lowPercents != null) { for (int i = 0; i < lowPercents.Length; i++) { lowPercents[i] = 100f; } ResetProgress(); } } } internal class GetLucky : Task { private bool active = false; public override TaskType type { get; } = TaskType.GetLucky; protected override string name { get; } = "Get Lucky"; public override bool CanActivate(int numPlayers) { return true; } public override string GetDescription() { return "Be the luckiest."; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by getting " + GetStylizedTaskWinStat("lucky") + "."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set hooks in GetLucky. {numPlayers} players"); base.SetHooks(numPlayers); active = true; } protected override void StageEnd() { base.StageEnd(); Evaluate(); } protected override void Unhook() { base.Unhook(); } private void Evaluate() { int playerNum = Random.Range(0, totalNumberPlayers); if (totalNumberPlayers == 1) { playerNum = Random.Range(0, 4); } if (Object.op_Implicit((Object)(object)TasksPlugin.instance)) { ((MonoBehaviour)TasksPlugin.instance).StartCoroutine(RollEvent(playerNum)); } } private string GetRollMessage() { WeightedSelection<string> val = new WeightedSelection<string>(8); val.AddChoice("Rolling...", 1f); val.AddChoice("Who's going to be the lucky one?", 1f); val.AddChoice("Feeling lucky?", 1f); val.AddChoice("Choosing the winner. Stand by.", 1f); val.AddChoice("Scratching the lottery ticket.", 1f); val.AddChoice("Rolling for the winner.", 1f); val.AddChoice("Flipping a coin. Call it in the air.", 1f); val.AddChoice("Rigging the draw. Please wait.", 1f); return "<style=cEvent>" + val.Evaluate(Random.value) + "</style>"; } private string GetWinFlavourMessage(string playerName) { WeightedSelection<string> val = new WeightedSelection<string>(8); playerName = GetStylizedTaskName(playerName); val.AddChoice(playerName + " won!", 1f); val.AddChoice(playerName + " was the lucky one!", 1f); val.AddChoice("Congrats, " + playerName + ".", 1f); val.AddChoice("The winner is " + playerName + ".", 1f); val.AddChoice("Your winner was " + playerName + ".", 1f); val.AddChoice("Congrats, " + playerName + ".", 1f); val.AddChoice("It's your lucky day, " + playerName + ".", 1f); val.AddChoice("Enjoy your prize, " + playerName + ".", 1f); val.AddChoice(playerName + " won, but it was rigged from the start.", 1f); val.AddChoice("Everyone loses except for " + playerName + ".", 1f); return val.Evaluate(Random.value); } private IEnumerator RollEvent(int playerNum) { ChatMessage.Send(GetRollMessage()); yield return (object)new WaitForSeconds(1.5f); string name = "Nobody"; if (totalNumberPlayers > 1 || playerNum == 0) { name = Util.GetBestMasterName(TasksPlugin.GetPlayerCharacterMaster(playerNum)); CompleteTask(playerNum); } ChatMessage.Send(GetWinFlavourMessage(name)); } } internal class HealingItem : Task { public override TaskType type { get; } = TaskType.HealingItem; protected override string name { get; } = "Find a Healing Item"; public override string GetDescription() { return "Find a healing item"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by finding a " + GetStylizedTaskWinStat("healing") + " item first."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in HealingItem. {numPlayers} players"); base.SetHooks(numPlayers); Inventory.onServerItemGiven += OnItemGained; } protected override void Unhook() { Inventory.onServerItemGiven -= OnItemGained; base.Unhook(); } private void OnItemGained(Inventory inventory, ItemIndex item, int count) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!ItemCatalog.GetItemDef(item).ContainsTag((ItemTag)2)) { return; } for (int i = 0; i < totalNumberPlayers; i++) { if ((Object)(object)TasksPlugin.GetPlayerCharacterMaster(i).inventory == (Object)(object)inventory) { CompleteTask(i); break; } } } } internal class KillStreak : Task { private int[] bestStreaks; private int[] currentStreaks; public override TaskType type { get; } = TaskType.KillStreak; protected override string name { get; } = "Kill Streak"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return "Most kills without getting hit"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " with a streak of " + GetStylizedTaskWinStat(bestStreaks[winningPlayer].ToString()) + "."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in KillStreak. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onServerDamageDealt += OnDamage; GlobalEventManager.onCharacterDeathGlobal += OnKill; bestStreaks = new int[numPlayers]; currentStreaks = new int[numPlayers]; Reset(); } protected override void StageEnd() { base.StageEnd(); Evaluate(); Reset(); } protected override void Unhook() { GlobalEventManager.onServerDamageDealt -= OnDamage; GlobalEventManager.onCharacterDeathGlobal -= OnKill; base.Unhook(); } private void OnDamage(DamageReport report) { if (report == null || report.victimMaster == null || report.victimMaster.playerCharacterMasterController == null) { return; } if (report.damageInfo.attacker == null && !report.isFallDamage) { Debug.Log((object)"KillStreak. Self damage ignore."); return; } int playerNumber = TasksPlugin.GetPlayerNumber(report.victimMaster); if (playerNumber > -1) { currentStreaks[playerNumber] = 0; } } private void OnKill(DamageReport report) { if (report != null && report.attackerMaster != null && report.attackerMaster.playerCharacterMasterController != null) { int playerNumber = TasksPlugin.GetPlayerNumber(report.attackerMaster); if (playerNumber > -1) { currentStreaks[playerNumber]++; UpdateStreaks(playerNumber); } } } private void UpdateStreaks(int playerNum) { if (currentStreaks[playerNum] > bestStreaks[playerNum]) { Debug.Log((object)$"Player {playerNum} improved their streak. {bestStreaks[playerNum]} -> {currentStreaks[playerNum]}"); bestStreaks[playerNum] = currentStreaks[playerNum]; UpdateProgress(); } } protected void UpdateProgress() { int num = 0; for (int i = 0; i < bestStreaks.Length; i++) { if (bestStreaks[i] > num) { num = bestStreaks[i]; } } if (num > 0) { for (int j = 0; j < progress.Length; j++) { progress[j] = (float)bestStreaks[j] / (float)num; } } else { for (int k = 0; k < progress.Length; k++) { progress[k] = 0f; } } base.UpdateProgress(progress); } private void Evaluate() { int num = 0; int num2 = 0; for (int i = 0; i < bestStreaks.Length; i++) { if (bestStreaks[i] > num2) { num2 = bestStreaks[i]; num = i; } } Debug.Log((object)$"Player {num} won with a streak of {num2}"); CompleteTask(num); } private void Reset() { if (bestStreaks != null && currentStreaks != null) { for (int i = 0; i < bestStreaks.Length; i++) { bestStreaks[i] = 0; currentStreaks[i] = 0; } ResetProgress(); } } } internal class MostDistance : Task { private double[] startDistances; private double winnerDist; protected new string description { get; } = "Most distance wins"; public override TaskType type { get; } = TaskType.MostDistance; protected override string name { get; } = "Most Distance"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by covering the most (" + GetStylizedTaskWinStat(winnerDist.ToString("F2")) + "m) distance."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in MostDistance. {numPlayers} players"); base.SetHooks(numPlayers); if (startDistances == null || startDistances.Length != numPlayers) { startDistances = new double[numPlayers]; } for (int i = 0; i < startDistances.Length; i++) { StatSheet currentStats = TasksPlugin.GetPlayerCharacterMaster(i).playerStatsComponent.currentStats; startDistances[i] = currentStats.GetStatValueDouble(StatDef.totalDistanceTraveled); } } protected override void StageEnd() { base.StageEnd(); Evaluate(); } protected override void Unhook() { base.Unhook(); } private void Evaluate() { double num = 0.0; int num2 = 0; for (int i = 0; i < startDistances.Length; i++) { StatSheet currentStats = TasksPlugin.GetPlayerCharacterMaster(i).playerStatsComponent.currentStats; double statValueDouble = currentStats.GetStatValueDouble(StatDef.totalDistanceTraveled); double num3 = statValueDouble - startDistances[i]; if (num3 > num) { num = num3; num2 = i; } Chat.AddMessage($"MostDist({i}): {startDistances[i]} -> {statValueDouble} = {num3}. Winner: {num2} with {num}"); } winnerDist = num; CompleteTask(num2); } } internal class NoJump : Task { private bool[] playerFailed; private int numPlayersFailed; public override TaskType type { get; } = TaskType.NoJump; protected override string name { get; } = "No Jump"; public override string GetDescription() { return "Don't jump"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by being the last to jump."; } public override bool CanActivate(int numPlayers) { return numPlayers > 1; } protected override void SetHooks(int numPlayers) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown Debug.Log((object)$"Set Hooks in NoJump. {numPlayers} players"); base.SetHooks(numPlayers); playerFailed = new bool[numPlayers]; Reset(); GenericCharacterMain.ApplyJumpVelocity += new hook_ApplyJumpVelocity(OnJump); } protected override void StageEnd() { base.StageEnd(); Evaluate(); Reset(); } protected override void Unhook() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown GenericCharacterMain.ApplyJumpVelocity -= new hook_ApplyJumpVelocity(OnJump); base.Unhook(); } private void Evaluate() { if (totalNumberPlayers - numPlayersFailed <= 1) { return; } for (int i = 0; i < playerFailed.Length; i++) { if (!playerFailed[i]) { UpdateProgressMultiWinner(); CompleteTask(i); } } } private void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { progress[i] = ((!playerFailed[i]) ? (numPlayersFailed / (totalNumberPlayers - 1)) : 0); } base.UpdateProgress(progress); } private void UpdateProgressMultiWinner() { for (int i = 0; i < progress.Length; i++) { progress[i] = ((!playerFailed[i]) ? 1 : 0); } base.UpdateProgress(progress); } private void OnJump(orig_ApplyJumpVelocity orig, CharacterMotor characterMotor, CharacterBody characterBody, float horizontalBonus, float verticalBonus, bool vault = false) { orig.Invoke(characterMotor, characterBody, horizontalBonus, verticalBonus, vault); int playerNumber = TasksPlugin.GetPlayerNumber(characterBody.master); if (playerNumber < 0 || playerFailed[playerNumber]) { return; } Debug.Log((object)$"Player {playerNumber} failed NoJump"); playerFailed[playerNumber] = true; numPlayersFailed++; UpdateProgress(); if (numPlayersFailed < totalNumberPlayers - 1) { return; } for (int i = 0; i < playerFailed.Length; i++) { if (!playerFailed[i]) { CompleteTask(i); break; } } } private void Reset() { if (playerFailed != null) { for (int i = 0; i < playerFailed.Length; i++) { playerFailed[i] = false; } ResetProgress(); } } } internal class OpenChests : Task { private int[] chestsOpened; private int numToOpen = 5; protected new string description { get; } = "First to open 5 chests wins (multishops count too)"; public override TaskType type { get; } = TaskType.OpenChests; protected override string name { get; } = "Open 5 Chests"; public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by opening " + GetStylizedTaskWinStat(numToOpen.ToString()) + " chests first."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in OpenChests. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.OnInteractionsGlobal += ChestsOpened; chestsOpened = new int[numPlayers]; Reset(); } protected override void Unhook() { GlobalEventManager.OnInteractionsGlobal -= ChestsOpened; Reset(); base.Unhook(); } protected void UpdateProgress() { for (int i = 0; i < progress.Length; i++) { progress[i] = (float)chestsOpened[i] / (float)numToOpen; } base.UpdateProgress(progress); } private void ChestsOpened(Interactor interactor, IInteractable interactable, GameObject go) { int num = 0; for (int i = 0; i < chestsOpened.Length; i++) { if ((Object)(object)((Component)TasksPlugin.GetPlayerCharacterMaster(i).GetBody()).GetComponent<Interactor>() == (Object)(object)interactor) { num = i; } } if (Object.op_Implicit((Object)(object)((go != null) ? go.GetComponent<ChestBehavior>() : null))) { chestsOpened[num]++; } else if (Object.op_Implicit((Object)(object)((go != null) ? go.GetComponent<ShopTerminalBehavior>() : null)) && ((Object)go).name.Contains("MultiShop")) { chestsOpened[num]++; } UpdateProgress(); if (IsComplete(num)) { CompleteTask(num); } } protected override bool IsComplete(int playerNum) { return chestsOpened[playerNum] >= numToOpen; } private void Reset() { if (chestsOpened != null) { for (int i = 0; i < chestsOpened.Length; i++) { chestsOpened[i] = 0; } ResetProgress(); } } } internal class OrderedSkills : Task { private SkillSlot[] order; private int[] whereInOrder; protected new string description { get; } = "Use skills in order"; public override TaskType type { get; } = TaskType.OrderedSkills; protected override string name { get; } = "Ordered Skills"; public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by using their abilities in order."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in OrderedSkills. {numPlayers} players"); base.SetHooks(numPlayers); SkillSlot[] array = new SkillSlot[4]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); order = (SkillSlot[])(object)array; whereInOrder = new int[numPlayers]; Reset(); TasksPlugin.OnAbilityUsed += AbilityUsed; } protected override void Unhook() { TasksPlugin.OnAbilityUsed -= AbilityUsed; Reset(); base.Unhook(); } private void AbilityUsed(int playerNum, SkillSlot slot) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between I4 and Unknown int num = whereInOrder[playerNum]; if ((int)order[num] == (int)slot) { whereInOrder[playerNum]++; if (whereInOrder[playerNum] >= order.Length) { CompleteTask(playerNum); } } else { whereInOrder[playerNum] = 0; } } private void Reset() { if (whereInOrder != null) { for (int i = 0; i < whereInOrder.Length; i++) { whereInOrder[i] = 0; } } } } internal class PreonEvent : Task { private int[] kills; private int preonIndex = -1; protected new string description { get; } = "Most Preon kills wins"; public override TaskType type { get; } = TaskType.PreonEvent; protected override string name { get; } = "Preon Event"; public override bool CanActivate(int numPlayers) { return numPlayers > 1; } public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by getting " + GetStylizedTaskWinStat(kills[winningPlayer].ToString()) + " kills with the Preon Accumulator."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in PreonEvent. {numPlayers} players"); base.SetHooks(numPlayers); preonIndex = ProjectileCatalog.FindProjectileIndex("BeamSphere"); GlobalEventManager.onCharacterDeathGlobal += OnKill; if (kills == null || kills.Length != numPlayers) { kills = new int[numPlayers]; } Reset(); TasksPlugin.StartPreonEvent(); } protected override void StageEnd() { base.StageEnd(); Evaluate(); TasksPlugin.EndPreonEvent(); Reset(); } protected override void Unhook() { GlobalEventManager.onCharacterDeathGlobal -= OnKill; base.Unhook(); } protected void UpdateProgress() { int num = 0; for (int i = 0; i < kills.Length; i++) { if (kills[i] > num) { num = kills[i]; } } if (num > 0) { for (int j = 0; j < progress.Length; j++) { progress[j] = (float)kills[j] / (float)num; } } else { for (int k = 0; k < progress.Length; k++) { progress[k] = 0f; } } base.UpdateProgress(progress); } public void OnKill(DamageReport damageReport) { if (damageReport != null && damageReport.attackerMaster != null && damageReport.attackerMaster.playerCharacterMasterController != null) { int playerNumber = TasksPlugin.GetPlayerNumber(damageReport.attackerMaster); if (ProjectileCatalog.GetProjectileIndex(damageReport.damageInfo.inflictor) == preonIndex) { kills[playerNumber]++; UpdateProgress(); } } } private void Evaluate() { int num = 0; int num2 = 0; for (int i = 0; i < kills.Length; i++) { if (kills[i] > num) { num = kills[i]; num2 = i; } } Chat.AddMessage($"Player {num2} won Preon event with {num} kills"); CompleteTask(num2); } private void Reset() { if (kills != null) { for (int i = 0; i < kills.Length; i++) { kills[i] = 0; } ResetProgress(); } } } internal class QuickDraw : Task { public override TaskType type { get; } = TaskType.QuickDraw; protected override string name { get; } = "Quick Draw"; public override string GetDescription() { return "Kill an enemy within 3s of spawning."; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by killing an enemy within " + GetStylizedTaskWinStat("3") + "s of spawning."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in KillStreak. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.onCharacterDeathGlobal += OnKill; } protected override void Unhook() { GlobalEventManager.onCharacterDeathGlobal -= OnKill; base.Unhook(); } private void OnKill(DamageReport report) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (report != null && report.attackerMaster != null && report.attackerMaster.playerCharacterMasterController != null) { float num = FixedTimeStamp.now.t - report.victimBody.localStartTime.t; if (num < 3f) { int playerNumber = TasksPlugin.GetPlayerNumber(report.attackerMaster); CompleteTask(playerNumber); } } } } internal class RewardBuilder { public static Reward CreateRandomReward() { //IL_0068: 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_0078: Unknown result type (might be due to invalid IL or missing references) WeightedSelection<RewardType> val = new WeightedSelection<RewardType>(8); val.AddChoice(RewardType.Item, 2f); val.AddChoice(RewardType.TempItem, 1f); val.AddChoice(RewardType.Command, 0.5f); val.AddChoice(RewardType.Drone, 0.5f); RewardType rewardType = val.Evaluate(Random.value); List<PickupIndex> list = Run.instance.smallChestDropTierSelector.Evaluate(Random.value); PickupIndex item = Run.instance.treasureRng.NextElementUniform<PickupIndex>(list); string randomDronePath = GetRandomDronePath(); Reward result = new Reward(rewardType, item, (rewardType != RewardType.TempItem) ? 1 : 5, _temporary: false, 100, 100, randomDronePath); return result; } public static string GetRandomDronePath() { WeightedSelection<string> val = new WeightedSelection<string>(8); val.AddChoice("iscBrokenDrone1", 4f); val.AddChoice("iscBrokenDrone2", 4f); val.AddChoice("iscBrokenTurret1", 3f); val.AddChoice("iscBrokenFlameDrone", 1f); val.AddChoice("iscBrokenEmergencyDrone", 1f); val.AddChoice("iscBrokenMissileDrone", 0.5f); return val.Evaluate(Random.value); } public static string GetDroneTexturePath(string spawnCardName) { return spawnCardName switch { "iscBrokenDrone1" => "Textures/BodyIcons/texDrone1Icon", "iscBrokenDrone2" => "Textures/BodyIcons/texDrone2Icon", "iscBrokenTurret1" => "Textures/BodyIcons/texTurret1Icon", "iscBrokenFlameDrone" => "Textures/BodyIcons/FlameDroneBody", "iscBrokenEmergencyDrone" => "Textures/BodyIcons/EmergencyDroneBody", "iscBrokenMissileDrone" => "Textures/BodyIcons/texMissileDroneIcon", _ => "Textures/BodyIcons/texDrone1Icon", }; } public static void GivePlayerDrone(int playerNum, string droneSpawnCardName) { //IL_0022: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown //IL_0064: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown CharacterBody body = TasksPlugin.GetPlayerCharacterMaster(playerNum).GetBody(); GameObject val = DirectorCore.instance.TrySpawnObject(new DirectorSpawnRequest(Resources.Load<SpawnCard>("SpawnCards/InteractableSpawnCard/" + droneSpawnCardName), new DirectorPlacementRule { placementMode = (PlacementMode)3, position = body.transform.position + new Vector3(1f, 0f, 0f) }, new Xoroshiro128Plus(0uL))); PurchaseInteraction component = val.GetComponent<PurchaseInteraction>(); component.cost = 0; Interactor component2 = ((Component)body).GetComponent<Interactor>(); if (Object.op_Implicit((Object)(object)component2)) { component.OnInteractionBegin(component2); } } } public struct Reward { public RewardType type; public PickupIndex item; public int numItems; public bool temporary; public int gold; public int xp; public string dronePath; public Reward(RewardType _type, PickupIndex _item, int _numItems, bool _temporary, int _gold, int _xp, string _path) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) type = _type; item = _item; numItems = _numItems; temporary = _temporary; gold = _gold; xp = _xp; dronePath = _path; } public override string ToString() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) return $"{type:g}, {item:g}"; } } public struct TempItem { public PickupIndex item; public int count; public TempItem(PickupIndex _item, int _count) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) item = _item; count = _count; } } public enum RewardType { Item, TempItem, Command, Gold, Xp, Drone } public class TaskInfo : MessageBase { public int taskType; public string description; public bool completed; public int index; public int total; public Reward reward; public override void Serialize(NetworkWriter writer) { writer.Write(taskType); writer.Write(description); writer.Write(completed); writer.Write(index); writer.Write(total); writer.Write((int)reward.type); writer.Write(reward.item.value); writer.Write(reward.numItems); writer.Write(reward.temporary); writer.Write(reward.gold); writer.Write(reward.xp); writer.Write(reward.dronePath); } public override void Deserialize(NetworkReader reader) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) taskType = reader.ReadInt32(); description = reader.ReadString(); completed = reader.ReadBoolean(); index = reader.ReadInt32(); total = reader.ReadInt32(); int type = reader.ReadInt32(); int num = reader.ReadInt32(); int numItems = reader.ReadInt32(); bool temporary = reader.ReadBoolean(); int gold = reader.ReadInt32(); int xp = reader.ReadInt32(); string path = reader.ReadString(); PickupIndex item = default(PickupIndex); ((PickupIndex)(ref item))..ctor(num); reward = new Reward((RewardType)type, item, numItems, temporary, gold, xp, path); } public TaskInfo(int _type, string _description, bool _completed, int _index, int _total, Reward _reward) { taskType = _type; description = _description; completed = _completed; index = _index; total = _total; reward = _reward; } public TaskInfo(NetworkReader reader) { ((MessageBase)this).Deserialize(reader); } public override string ToString() { return $"TaskInfo: {taskType}, {description}, {completed}, {index}/{total}"; } } public class ProgressInfo : MessageBase { public int taskIndex; private float myProgress; private float rivalProgress; public ProgressInfo(int _taskIndex, float _myProgress, float _rivalProgress) { taskIndex = _taskIndex; myProgress = _myProgress; rivalProgress = _rivalProgress; } public ProgressInfo(int _taskIndex, int _myProgress, int _rivalProgress) { taskIndex = _taskIndex; myProgress = _myProgress; rivalProgress = _rivalProgress; } public float GetMyProgress() { return myProgress; } public float GetRivalProgress() { return rivalProgress; } public override void Serialize(NetworkWriter writer) { writer.Write(taskIndex); writer.Write(myProgress); writer.Write(rivalProgress); } public override void Deserialize(NetworkReader reader) { taskIndex = reader.ReadInt32(); myProgress = reader.ReadSingle(); rivalProgress = reader.ReadSingle(); } } public class TaskCompletionInfo : MessageBase { public int taskType; public string winnerName; public TaskCompletionInfo(int _taskType, string _winnerName) { taskType = _taskType; winnerName = _winnerName; } public override void Serialize(NetworkWriter writer) { writer.Write(taskType); writer.Write(winnerName); } public override void Deserialize(NetworkReader reader) { taskType = reader.ReadInt32(); winnerName = reader.ReadString(); } } internal class StartTeleporter : Task { private float soloTimer; private float soloTimeAmountSec = 90f; private bool hasFailed = false; private bool succeeded = false; protected new string description { get; } = "Better be First"; public override TaskType type { get; } = TaskType.StartTele; protected override string name { get; } = "Activate the Teleporter"; public override bool CanActivate(int numPlayers) { return true; } public override string GetDescription() { return description; } public override string GetWinMessage(int winningPlayer) { if (winningPlayer < 0) { return "You failed " + GetStylizedTaskName(name) + " by not getting to the " + GetStylizedTaskWinStat("teleporter") + " in " + GetStylizedTaskWinStat(soloTimeAmountSec.ToString()) + " seconds."; } return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by triggering the " + GetStylizedTaskWinStat("teleporter") + " first."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in StartTele. {numPlayers} players"); base.SetHooks(numPlayers); GlobalEventManager.OnInteractionsGlobal += OnInteraction; if (numPlayers == 1) { StartSolo(); } } protected override void Unhook() { GlobalEventManager.OnInteractionsGlobal -= OnInteraction; base.Unhook(); } private void OnInteraction(Interactor interactor, IInteractable interactable, GameObject go) { int playerNum = 0; for (int i = 0; i < totalNumberPlayers; i++) { if ((Object)(object)((Component)TasksPlugin.GetPlayerCharacterMaster(i).GetBody()).GetComponent<Interactor>() == (Object)(object)interactor) { playerNum = i; } } if (Object.op_Implicit((Object)(object)((go != null) ? go.GetComponent<TeleporterInteraction>() : null))) { Evaluate(playerNum); } } private void StartSolo() { if (Object.op_Implicit((Object)(object)TasksPlugin.instance)) { ((MonoBehaviour)TasksPlugin.instance).StartCoroutine(Countdown()); } } private IEnumerator Countdown() { hasFailed = false; succeeded = false; soloTimer = soloTimeAmountSec; for (int i = 0; (float)i < soloTimeAmountSec; i++) { yield return (object)new WaitForSeconds(1f); soloTimer -= 1f; UpdateProgressSolo(); } hasFailed = true; if (!succeeded) { CompleteTask(-1); } } private void Evaluate(int playerNum) { if (totalNumberPlayers > 1) { CompleteTask(playerNum); } else if (!hasFailed) { succeeded = true; CompleteTask(playerNum); } } private void UpdateProgressSolo() { if (succeeded) { progress[0] = 0f; } else { progress[0] = Mathf.Max(0f, soloTimer / soloTimeAmountSec); } base.UpdateProgress(progress); } } internal class StayInAir : Task { private CharacterMotor[] motors; private CharacterBody[] bodies; private float[] timeInAir; private float timeToStayInAir; protected new string description { get; } = "Stay airborne for 10 seconds"; public override TaskType type { get; } = TaskType.StayInAir; protected override string name { get; } = "Stay Airborne"; public override string GetDescription() { return $"Stay in the air for {Math.Round(timeToStayInAir, 1)} seconds"; } public override string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + GetStylizedTaskName(name) + " by staying in the air for " + GetStylizedTaskWinStat(timeToStayInAir.ToString("0.00")) + "s."; } protected override void SetHooks(int numPlayers) { Debug.Log((object)$"Set Hooks in Stay Airborne. {numPlayers} players"); base.SetHooks(numPlayers); timeToStayInAir = 2f + Run.instance.difficultyCoefficient; if (timeInAir == null || timeInAir.Length != numPlayers) { timeInAir = new float[numPlayers]; for (int i = 0; i < timeInAir.Length; i++) { timeInAir[i] = 0f; } } if (motors == null || motors.Length != numPlayers) { motors = (CharacterMotor[])(object)new CharacterMotor[numPlayers]; } if (bodies == null || motors.Length != numPlayers) { bodies = (CharacterBody[])(object)new CharacterBody[numPlayers]; } SetupBodies(); RoR2Application.onFixedUpdate += AirborneFixedUpdate; } protected override void Unhook() { RoR2Application.onFixedUpdate -= AirborneFixedUpdate; Reset(); base.Unhook(); } protected void UpdateProgress() { if (timeInAir != null && progress != null) { for (int i = 0; i < progress.Length; i++) { progress[i] = timeInAir[i] / timeToStayInAir; } base.UpdateProgress(progress); } } private void AirborneFixedUpdate() { UpdateProgress(); for (int i = 0; i < timeInAir.Length; i++) { timeInAir[i] = ((Object.op_Implicit((Object)(object)motors[i]) && !motors[i].isGrounded && !Object.op_Implicit((Object)(object)bodies[i].currentVehicle)) ? (timeInAir[i] + Time.fixedDeltaTime) : 0f); if (IsComplete(i)) { CompleteTask(i); Reset(); } } } protected override bool IsComplete(int playerNum) { return timeInAir[playerNum] >= timeToStayInAir; } private void Reset() { UpdateProgress(); if (timeInAir != null) { for (int i = 0; i < timeInAir.Length; i++) { timeInAir[i] = 0f; } ResetProgress(); } } private void SetupBodies() { for (int i = 0; i < motors.Length; i++) { CharacterMaster playerCharacterMaster = TasksPlugin.GetPlayerCharacterMaster(i); bodies[i] = playerCharacterMaster.GetBody(); motors[i] = playerCharacterMaster.GetBody().characterMotor; } } } public enum TaskType { Base, AirKills, DamageMultiple, DamageInTime, StayInAir, BiggestHit, MostDistance, PreonEvent, FarthestAway, FailShrine, OpenChests, StartTele, UsePrinters, OrderedSkills, BadSkill, BabyDrone, Die, FindLockbox, HealingItem, NoJump, VeryBest, FewestElites, GetLucky, GetLow, KillStreak, QuickDraw, FarKill, FewElites } internal class Task { private static string myName = "SOLRUN_"; private static string myMod = "TASKS_"; protected static string thisClass = "BASE_TASK_"; protected int totalNumberPlayers; protected float[] progress; private bool taskActive = false; protected string description { get; } = "Base description"; public virtual TaskType type { get; } = TaskType.Base; protected virtual string name { get; } = "Base Task"; public static event Action<TaskType, int, string> OnCompletion; public static event Action<TaskType, float[]> OnUpdateProgress; public Task() { OnInstall(); } protected virtual void SetupName() { } public virtual string GetDescription() { return description; } public virtual void OnInstall() { Run.onRunDestroyGlobal += RunOver; TasksPlugin.OnActivate += Activate; TasksPlugin.OnDeactivate += Deactivate; TasksPlugin.OnCancelAll += CancelAllTasks; } public virtual void OnUninstall() { Run.onRunDestroyGlobal -= RunOver; TasksPlugin.OnActivate -= Activate; TasksPlugin.OnDeactivate -= Deactivate; TasksPlugin.OnCancelAll -= CancelAllTasks; } protected virtual void UpdateProgress(float[] progress) { if (taskActive) { Task.OnUpdateProgress?.Invoke(type, progress); } } protected void ResetProgress() { TasksPlugin instance = TasksPlugin.instance; if (instance != null) { ((MonoBehaviour)instance).StartCoroutine(ResetProgressInTime()); } } private IEnumerator ResetProgressInTime() { yield return (object)new WaitForSeconds(4f); if (progress != null) { for (int i = 0; i < progress.Length; i++) { progress[i] = 0f; } if (taskActive) { Task.OnUpdateProgress?.Invoke(type, progress); } else { Debug.Log((object)$"{type:g} was inactive. Don't update progress"); } } } public virtual string GetWinMessage(int winningPlayer) { return GetStylizedName(winningPlayer) + " completed " + name; } protected string GetStylizedName(int playerNum) { return "<color=#ffffff>" + TasksPlugin.GetPlayerName(playerNum) + "</color>"; } protected string GetStylizedTaskName(string taskName) { return "<style=cIsUtility>" + taskName + "</style>"; } protected string GetStylizedTaskWinStat(string stat) { return "<style=cIsUtility>" + stat + "</style>"; } protected virtual void CompleteTask(int playerNum) { Unhook(); Task.OnCompletion?.Invoke(type, playerNum, GetWinMessage(playerNum)); } protected virtual bool IsComplete(int playerNum) { return false; } public virtual bool CanActivate(int numPlayers) { return true; } private void Activate(int id, int numPlayers) { if (id == (int)type) { totalNumberPlayers = numPlayers; progress = new float[numPlayers]; SetHooks(numPlayers); taskActive = true; } } public void Deactivate(int id) { if (id == (int)type || id < 0) { taskActive = false; StageEnd(); Unhook(); } } public void CancelAllTasks() { taskActive = false; Unhook(); } protected virtual void StageEnd() { } protected virtual void SetHooks(int numPlayers) { } protected virtual void Unhook() { } private void RunOver(Run run) { Unhook(); OnUninstall(); } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("com.Solrun.Tasks", "Tasks", "1.2.4")] public sealed class TasksPlugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static hook_OnCollisionEnter <0>__TurnOffCommand; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static hook_OnSkillActivated <>9__48_0; public static Func<string> <>9__65_1; internal void <SetGameHooks>b__48_0(orig_OnSkillActivated orig, CharacterBody self, GenericSkill param) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (NetworkServer.active && self.isPlayerControlled) { int playerNumber = GetPlayerNumber(self.master); SkillSlot arg = self.skillLocator.FindSkillSlot(param); TasksPlugin.OnAbilityUsed?.Invoke(playerNumber, arg); } orig.Invoke(self, param); } internal string <CreateNotification>b__65_1() { return "Task Complete!"; } } public const string MODNAME = "Tasks"; public const string AUTHOR = "Solrun"; public const string GUID = "com.Solrun.Tasks"; public const string VERSION = "1.2.4"; public static TasksPlugin instance; private static List<CharacterMaster> playerCharacterMasters; private int totalNumPlayers = 0; private int totalNumTasks; public Reward[] rewards; private List<TempItem>[] TempItemLists; private EquipmentIndex[] preonEventEqCache; private int[] stageStartTasks; private int[] teleStartTasks; private Task[] taskCopies; private bool telePlaced = false; public TaskInfo[] currentTasks; public GameObject[] tasksUIObjects; public RectTransform[] tasksUIRects; public RectTransform[] rivalTasksUIRects; private HUD hud; private ObjectivePanelController panel; private GameObject itemIconPrefabCopy; private bool rectWasAlreadyNull = false; private bool rivalRectWasAlreadyNull = false; public static event Action<int, int> OnActivate; public static event Action<int> OnDeactivate; public static event Action OnCancelAll; public static event Action<int> OnPopup; public static event Action<int, SkillSlot> OnAbilityUsed; public void InvokeOnPopup(int taskType) { TasksPlugin.OnPopup?.Invoke(taskType); } private void Awake() { Chat.AddMessage("Loaded Task plugin"); if (instance == null) { instance = this; } ConfigManager configManager = new ConfigManager(); ConfigManager.instance.SetConfigFile(((BaseUnityPlugin)this).Config); ConfigManager.instance.Awake(); Task.OnCompletion += TaskCompletion; Task.OnUpdateProgress += UpdateTaskProgress; Run.onRunStartGlobal += GameSetup; SetupNetworking(); SetGameHooks(); } private void Start() { } public void Update() { if (0 == 0) { } } private void GameSetup(Run run) { totalNumTasks = Enum.GetNames(typeof(TaskType)).Length; rewards = new Reward[totalNumTasks]; Debug.Log((object)$"Tasks mod: Number of players: {run.participatingPlayerCount} Living Players: {run.livingPlayerCount}"); totalNumPlayers = run.participatingPlayerCount; playerCharacterMasters = new List<CharacterMaster>(totalNumPlayers); rectWasAlreadyNull = false; rivalRectWasAlreadyNull = false; PopulatePlayerCharaterMasterList(); if (NetworkServer.active) { TaskSetup(); PopulateTempItemLists(); TasksPlugin.OnCancelAll?.Invoke(); } } private void TaskSetup() { Debug.Log((object)"Creating Task Objects"); taskCopies = new Task[27]; AirKills airKills = new AirKills(); DamageMultipleTargets damageMultipleTargets = new DamageMultipleTargets(); DealDamageInTime dealDamageInTime = new DealDamageInTime(); StayInAir stayInAir = new StayInAir(); BiggestHit biggestHit = new BiggestHit(); MostDistance mostDistance = new MostDistance(); PreonEvent preonEvent = new PreonEvent(); FarthestAway farthestAway = new FarthestAway(); FailShrine failShrine = new FailShrine(); OpenChests openChests = new OpenChests(); StartTeleporter startTeleporter = new StartTeleporter(); UsePrinters usePrinters = new UsePrinters(); OrderedSkills orderedSkills = new OrderedSkills(); DontUseSkill dontUseSkill = new DontUseSkill(); BabyDrone babyDrone = new BabyDrone(); Die die = new Die(); FindLockbox findLockbox = new FindLockbox(); HealingItem healingItem = new HealingItem(); NoJump noJump = new NoJump(); VeryBest veryBest = new VeryBest(); FewestElites fewestElites = new FewestElites(); GetLucky getLucky = new GetLucky(); GetLow getLow = new GetLow(); KillStreak killStreak = new KillStreak(); QuickDraw quickDraw = new QuickDraw(); FarKill farKill = new FarKill(); FewElites fewElites = new FewElites(); taskCopies[(int)(airKills.type - 1)] = airKills; taskCopies[(int)(damageMultipleTargets.type - 1)] = damageMultipleTargets; taskCopies[(int)(dealDamageInTime.type - 1)] = dealDamageInTime; taskCopies[(int)(stayInAir.type - 1)] = stayInAir; taskCopies[(int)(biggestHit.type - 1)] = biggestHit; taskCopies[(int)(mostDistance.type - 1)] = mostDistance; taskCopies[(int)(preonEvent.type - 1)] = preonEvent; taskCopies[(int)(farthestAway.type - 1)] = farthestAway; taskCopies[(int)(failShrine.type - 1)] = failShrine; taskCopies[(int)(openChests.type - 1)] = openChests; taskCopies[(int)(startTeleporter.type - 1)] = startTeleporter; taskCopies[(int)(usePrinters.type - 1)] = usePrinters; taskCopies[(int)(orderedSkills.type - 1)] = orderedSkills; taskCopies[(int)(dontUseSkill.type - 1)] = dontUseSkill; taskCopies[(int)(babyDrone.type - 1)] = babyDrone; taskCopies[(int)(die.type - 1)] = die; taskCopies[(int)(findLockbox.type - 1)] = findLockbox; taskCopies[(int)(healingItem.type - 1)] = healingItem; taskCopies[(int)(noJump.type - 1)] = noJump; taskCopies[(int)(veryBest.type - 1)] = veryBest; taskCopies[(int)(fewestElites.type - 1)] = fewestElites; taskCopies[(int)(getLucky.type - 1)] = getLucky; taskCopies[(int)(getLow.type - 1)] = getLow; taskCopies[(int)(killStreak.type - 1)] = killStreak; taskCopies[(int)(quickDraw.type - 1)] = quickDraw; taskCopies[(int)(farKill.type - 1)] = farKill; taskCopies[(int)(fewElites.type - 1)] = fewElites; } private void PopulatePlayerCharaterMasterList() { for (int i = 0; i < CharacterMaster.readOnlyInstancesList.Count; i++) { if ((Object)(object)CharacterMaster.readOnlyInstancesList[i].playerCharacterMasterController != (Object)null) { playerCharacterMasters.Add(CharacterMaster.readOnlyInstancesList[i]); } } } private void PopulateTempItemLists() { TempItemLists = new List<TempItem>[CharacterMaster.readOnlyInstancesList.Count]; for (int i = 0; i < TempItemLists.Length; i++) { TempItemLists[i] = new List<TempItem>(); } } private void SetupNetworking() { NetworkingAPI.RegisterMessageType<SyncTaskCompletion>(); NetworkingAPI.RegisterMessageType<SyncTaskEnded>(); NetworkingAPI.RegisterMessageType<SyncUpdateTask>(); NetworkingAPI.RegisterMessageType<SyncUpdateProgress>(); } private void SetGameHooks() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_0015: 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_0020: Expected O, but got Unknown object obj = <>c.<>9__48_0; if (obj == null) { hook_OnSkillActivated val = delegate(orig_OnSkillActivated orig, CharacterBody self, GenericSkill param) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (NetworkServer.active && self.isPlayerControlled) { int playerNumber = GetPlayerNumber(self.master); SkillSlot arg = self.skillLocator.FindSkillSlot(param); TasksPlugin.OnAbilityUsed?.Invoke(playerNumber, arg); } orig.Invoke(self, param); }; <>c.<>9__48_0 = val; obj = (object)val; } CharacterBody.OnSkillActivated += (hook_OnSkillActivated)obj; GlobalEventManager.OnInteractionsGlobal += delegate(Interactor interactor, IInteractable interactable, GameObject go) { string text = ((object)interactable).GetType().ToString(); if (Object.op_Implicit((Object)(object)((go != null) ? go.GetComponent<TeleporterInteraction>() : null))) { Debug.Log((object)("Interacted with TP. InterType: " + text + " name: " + ((Object)go).name)); } else if (Object.op_Implicit((Object)(object)((go != null) ? go.GetComponent<SceneExitController>() : null))) { Debug.Log((object)("Interacted with a SceneExitController " + ((Object)go).name)); StageEnd(); } }; TeleporterInteraction.onTeleporterFinishGlobal += delegate { Debug.Log((object)"TP finished and player chose to leave"); if (NetworkServer.active) { StageEnd(); } }; Run.OnServerTeleporterPlaced += (hook_OnServerTeleporterPlaced)delegate(orig_OnServerTeleporterPlaced orig, Run self, SceneDirector director, GameObject teleporter) { orig.Invoke(self, director, teleporter); telePlaced = true; }; HudObjectiveTargetSetter.Update += (hook_Update)delegate(orig_Update orig, HudObjectiveTargetSetter self) { orig.Invoke(self); if ((Object)(object)panel == (Object)null) { Debug.Log((object)"Panel is null in HudObjSetter.Update"); } if (self.objectivePanelController == null) { Debug.Log((object)"Task mod: objectivePanelController was null"); } if (Object.op_Implicit((Object)(object)self.objectivePanelController)) { panel = self.objectivePanelController; } }; HUD.Awake += (hook_Awake)delegate(orig_Awake orig, HUD self) { orig.Invoke(self); hud = self; int numberOfTasks = ConfigManager.instance.GetNumberOfTasks(totalNumPlayers); numberOfTasks = Math.Min(numberOfTasks, 7); tasksUIObjects = (GameObject[])(object)new GameObject[numberOfTasks]; tasksUIRects = (RectTransform[])(object)new RectTransform[numberOfTasks]; rivalTasksUIRects = (RectTransform[])(object)new RectTransform[numberOfTasks]; if (telePlaced && NetworkServer.active) { GenerateTasks(numberOfTasks); } }; GameOverController.Awake += (hook_Awake)delegate(orig_Awake orig, GameOverController self) { Debug.Log((object)"GameOverController.Awake"); CancelAllTasks(); orig.Invoke(self); }; } public void UpdateTasksUI(int taskIndex, string text = "") { //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be