Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TeammateRevival v4.2.1
plugins/KosmosisDire-TeammateRevival/TeammateRevive.dll
Decompiled a year ago
The result has been truncated due to the large size, download it to view full contents!
#define DEBUG using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using InLobbyConfig; using InLobbyConfig.Fields; using Microsoft.CodeAnalysis; using Mono.Cecil.Cil; using MonoMod.Cil; using MonoMod.Utils; using On.RoR2; using On.RoR2.UI; using On.RoR2.UI.LogBook; using R2API; using R2API.Networking; using R2API.Networking.Interfaces; using RiskOfOptions; using RiskOfOptions.OptionConfigs; using RiskOfOptions.Options; using RoR2; using RoR2.ExpansionManagement; using RoR2.UI; using RoR2.UI.LogBook; using SimpleJSON; using TMPro; using TeammateRevive.Artifact; using TeammateRevive.Common; using TeammateRevive.Configuration; using TeammateRevive.Content; using TeammateRevive.DeathTotem; using TeammateRevive.Debugging; using TeammateRevive.Integrations; using TeammateRevive.Localization; using TeammateRevive.Logging; using TeammateRevive.Players; using TeammateRevive.ProgressBar; using TeammateRevive.Resources; using TeammateRevive.Revive; using TeammateRevive.Revive.Rules; 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.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("TeammateRevive")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+efed43a66155c54449bd8feeac64240a9df01807")] [assembly: AssemblyProduct("TeammateRevive")] [assembly: AssemblyTitle("TeammateRevive")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace TeammateRevive { public class ContentManager { private readonly ReviveRules rules; private readonly RunTracker run; private readonly DeathCurseArtifact deathCurseArtifact; private List<ContentBase> addedContent = new List<ContentBase>(); public static bool ContentInited; public ContentManager(ReviveRules rules, RunTracker run, DeathCurseArtifact deathCurseArtifact) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown this.rules = rules; this.run = run; this.deathCurseArtifact = deathCurseArtifact; Language.SetStringByToken += new hook_SetStringByToken(LanguageOnSetStringByToken); ItemCatalog.Init += new hook_Init(ItemsOnInit); BuffCatalog.Init += new hook_Init(BuffsOnInit); } private void LanguageOnSetStringByToken(orig_SetStringByToken orig, Language self, string token, string localizedstring) { if (Object.op_Implicit((Object)(object)Items.ShieldOnly) && token == Items.ShieldOnly.descriptionToken) { localizedstring += Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_SHIELD_ONLY_POSTFIX); } orig.Invoke(self, token, localizedstring); if (Object.op_Implicit((Object)(object)Items.ShieldOnly) && token == Items.ShieldOnly.descriptionToken) { Log.Info("Transcendence description (" + token + ") patched!"); } } private void BuffsOnInit(orig_Init orig) { orig.Invoke(); if (!ContentInited) { Log.Error("BuffsOnInit called before content was inited!"); } foreach (ContentBase item in addedContent) { try { item.OnBuffsAvailable(); } catch (Exception arg) { Log.Error($"Error on OnBuffsAvailable for {item}: {arg}"); } } } private void ItemsOnInit(orig_Init orig) { orig.Invoke(); if (!ContentInited) { Log.Error("ItemsOnInit called before content was inited!"); } foreach (ContentBase item in addedContent) { try { item.OnItemsAvailable(); } catch (Exception arg) { Log.Error($"Error on OnBuffsAvailable for {item}: {arg}"); } } } public void Init() { LoadAddedContent(); deathCurseArtifact.Init(); } public void LoadAddedContent() { addedContent = new List<ContentBase> { new DeathCurse(rules, run), new CharonsObol(), new DeadMansHandItem(), new ReviveLink(), new ReviveRegen(rules), new RevivalToken() }; foreach (ContentBase item in addedContent) { item.Init(); item.GetType().GetField("instance")?.SetValue(null, item); } ContentInited = true; } } public static class HideDeathCurseContent { [CompilerGenerated] private static class <>O { public static hook_BuildPickupEntries <0>__OnBuildPickupEntries; public static hook_UpdateChoiceDisplay <1>__OnRuleChoiceUpdate; } private static readonly string[] ItemsToHide = new string[2] { CharonsObol.NameToken, DeadMansHandItem.NameToken }; public static void Init(PluginConfig config) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown Log.Info($"Init: {config.HideDeathCurseItemsInLogBook}"); if (config.HideDeathCurseItemsInLogBook) { object obj = <>O.<0>__OnBuildPickupEntries; if (obj == null) { hook_BuildPickupEntries val = OnBuildPickupEntries; <>O.<0>__OnBuildPickupEntries = val; obj = (object)val; } LogBookController.BuildPickupEntries += (hook_BuildPickupEntries)obj; } if (config.HideDeathCurseArtifact) { object obj2 = <>O.<1>__OnRuleChoiceUpdate; if (obj2 == null) { hook_UpdateChoiceDisplay val2 = OnRuleChoiceUpdate; <>O.<1>__OnRuleChoiceUpdate = val2; obj2 = (object)val2; } RuleChoiceController.UpdateChoiceDisplay += (hook_UpdateChoiceDisplay)obj2; } } private static void OnRuleChoiceUpdate(orig_UpdateChoiceDisplay orig, RuleChoiceController self, RuleChoiceDef def) { orig.Invoke(self, def); if (def.globalName.StartsWith("Artifacts.ARTIFACT_DEATH_CURSE.Off")) { ((Component)self).gameObject.SetActive(false); } } private static Entry[] OnBuildPickupEntries(orig_BuildPickupEntries orig, Dictionary<ExpansionDef, bool> expansionAvailability) { Entry[] array = orig.Invoke(expansionAvailability); Entry[] array2 = array.Where((Entry r) => !ItemsToHide.Contains(r.nameToken)).ToArray(); Log.Info($"Hiding Death Curse items ({array2.Length}/{array.Length})"); return array2; } } public class ItemDropManager { private readonly RunTracker run; private readonly ReviveRules rules; public ItemDropManager(RunTracker run, ReviveRules rules) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown this.run = run; this.rules = rules; Run.BuildDropTable += new hook_BuildDropTable(OnBuildDropTable); AllItemsEnumerator.MoveNext += new hook_MoveNext(OnNextItemIterator); } private bool OnNextItemIterator(orig_MoveNext orig, ref AllItemsEnumerator self) { //IL_001e: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) while (run.IsDeathCurseEnabled && (self.position == (ItemIndex)(CharonsObol.Index - 1) || self.position == (ItemIndex)(DeadMansHandItem.Index - 1))) { ref ItemIndex position = ref self.position; position = (ItemIndex)((int)position + 1); } return orig.Invoke(ref self); } private void OnBuildDropTable(orig_BuildDropTable orig, Run self) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(self); if (!ContentManager.ContentInited) { Log.Error("Content wasn't inited on OnBuildDropTable!"); } else if (!run.IsDeathCurseEnabled && (!NetworkHelper.IsServer || !rules.Values.ForceDeathCurseRule)) { RemoveItem(CharonsObol.Index, self.availableTier2DropList, CharonsObol.Name); RemoveItem(DeadMansHandItem.Index, self.availableLunarItemDropList, DeadMansHandItem.Name); RemoveItem(DeadMansHandItem.Index, self.availableLunarCombinedDropList, DeadMansHandItem.Name); } } private void RemoveItem(ItemIndex itemIndex, List<PickupIndex> dropList, string name) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) int num = dropList.FindIndex((PickupIndex pi) => ((PickupIndex)(ref pi)).pickupDef.itemIndex == itemIndex); if (num >= 0) { Log.Info("Removing '" + name + "' from drop list"); dropList.RemoveAt(num); } else { Log.Info("Item '" + name + "' isn't found in drop list!"); } } } [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("KosmosisDire.TeammateRevival", "TeammateRevival", "4.2.1")] public class MainTeammateRevival : BaseUnityPlugin { public const string PluginGUID = "KosmosisDire.TeammateRevival"; public const string PluginAuthor = "KosmosisDire"; public const string PluginName = "TeammateRevival"; public const string PluginVersion = "4.2.1"; public static MainTeammateRevival instance; private DeathCurseArtifact deathCurseArtifact; public PluginConfig pluginConfig; private PlayersTracker players; private RunTracker run; private RevivalTracker revivalTracker; private InLobbyConfigIntegration inLobbyConfigIntegration; private RiskOfOptionsIntegration riskOfOptionsIntegration; private ConsoleCommands consoleCommands; private ReviveRules rules; private ReviveLinkBuffIconManager linkBuffIconManager; private ReviveLongRangeActivationManager reviveLongRangeActivationManager; private DeathTotemTracker deathTotemTracker; private ProgressBarController progressBarController; private ReviveProgressBarTracker progressBarTracker; private ItemDropManager itemDropManager; private ContentManager contentManager; public Func<IEnumerator, Coroutine> DoCoroutine => ((MonoBehaviour)this).StartCoroutine; public void Awake() { instance = this; pluginConfig = PluginConfig.Load(((BaseUnityPlugin)this).Config); LanguageManager.RegisterLanguages(); NetworkingAPI.RegisterMessageType<SyncDeathTotemMessage>(); NetworkingAPI.RegisterMessageType<SetRulesMessage>(); deathCurseArtifact = new DeathCurseArtifact(); run = new RunTracker(deathCurseArtifact); players = new PlayersTracker(run, pluginConfig); rules = new ReviveRules(run, pluginConfig); deathTotemTracker = new DeathTotemTracker(players, run, rules); progressBarController = new ProgressBarController(); progressBarTracker = new ReviveProgressBarTracker(progressBarController, players, deathTotemTracker, rules); revivalTracker = new RevivalTracker(players, run, rules, deathTotemTracker, progressBarTracker); consoleCommands = new ConsoleCommands(rules, pluginConfig); linkBuffIconManager = new ReviveLinkBuffIconManager(); inLobbyConfigIntegration = new InLobbyConfigIntegration(pluginConfig); riskOfOptionsIntegration = new RiskOfOptionsIntegration(pluginConfig); reviveLongRangeActivationManager = new ReviveLongRangeActivationManager(run, deathTotemTracker); itemDropManager = new ItemDropManager(run, rules); contentManager = new ContentManager(rules, run, deathCurseArtifact); Log.Init(pluginConfig, ((BaseUnityPlugin)this).Logger); ReviveHelper.Init(); CustomResources.LoadCustomResources(); HideDeathCurseContent.Init(pluginConfig); contentManager.Init(); rules.ApplyConfigValues(); DebugHelper.Init(pluginConfig); run.RunStarted += OnRunStarted; run.RunEnded += OnRunEnded; SetupHooks(); Log.Debug("Setup Teammate Revival"); } private void SetupHooks() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown Run.BeginGameOver += new hook_BeginGameOver(Hook_BeginGameOver); Run.AdvanceStage += new hook_AdvanceStage(Hook_AdvanceStage); NetworkUser.OnStartLocalPlayer += new hook_OnStartLocalPlayer(Hook_OnStartLocalPlayer); } private void Hook_OnStartLocalPlayer(orig_OnStartLocalPlayer orig, NetworkUser self) { orig.Invoke(self); if (NetworkHelper.IsClient()) { ClientScene.RegisterPrefab(((Component)CustomResources.DeathTotem).gameObject); Object.FindObjectOfType<NetworkManager>().spawnPrefabs.Add(((Component)CustomResources.DeathTotem).gameObject); Log.Info("Client registered prefabs"); } else { NetworkManager.singleton.connectionConfig.DisconnectTimeout = 5u; NetworkManager.singleton.connectionConfig.MaxSentMessageQueueSize = 1024; } } private void Hook_BeginGameOver(orig_BeginGameOver orig, Run self, GameEndingDef gameEndingDef) { orig.Invoke(self, gameEndingDef); if (!NetworkHelper.IsClient()) { Log.Info("Game Over - reseting data"); players.Reset(); run.IsStarted = false; } } private void Hook_AdvanceStage(orig_AdvanceStage orig, Run self, SceneDef nextScene) { orig.Invoke(self, nextScene); if (!NetworkHelper.IsClient()) { Log.Info("Advanced a stage - now resetting"); players.Reset(); } } public void Update() { DebugHelper.Update(); players.Update(); revivalTracker.Update(); } private void OnRunStarted(RunTracker obj) { deathCurseArtifact.EnsureEnabled(rules); } private void OnRunEnded(RunTracker obj) { players.Reset(); } } public class RunTracker { private readonly DeathCurseArtifact deathCurseArtifact; public static RunTracker instance; private bool isStarted; public bool IsStarted { get { return isStarted; } set { if (IsStarted != value) { isStarted = value; if (value) { Log.Info("Run started"); this.RunStarted?.Invoke(this); } else { Log.Info("Run ended"); this.RunEnded?.Invoke(this); } } } } public bool IsDeathCurseEnabled { get { ReviveRules reviveRules = ReviveRules.instance; int result; if (reviveRules == null || !reviveRules.Values.ForceEnableDeathCurseForSinglePlayer) { if (!deathCurseArtifact.ArtifactEnabled) { ReviveRules reviveRules2 = ReviveRules.instance; if (reviveRules2 == null || !reviveRules2.Values.ForceDeathCurseRule) { result = 0; goto IL_005a; } } Run obj = Run.instance; result = ((obj == null || obj.participatingPlayerCount != 1) ? 1 : 0); } else { result = 1; } goto IL_005a; IL_005a: return (byte)result != 0; } } public event Action<RunTracker> RunStarted; public event Action<RunTracker> RunEnded; public RunTracker(DeathCurseArtifact deathCurseArtifact) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown this.deathCurseArtifact = deathCurseArtifact; Run.onRunStartGlobal += GlobalOnRunStarted; Run.onRunDestroyGlobal += GlobalOnRunDestroy; Run.BeginStage += new hook_BeginStage(Hook_BeginStage); instance = this; } private void Hook_BeginStage(orig_BeginStage orig, Run self) { orig.Invoke(self); IsStarted = true; } private void GlobalOnRunStarted(Run obj) { IsStarted = true; } private void GlobalOnRunDestroy(Run obj) { IsStarted = false; } } } namespace TeammateRevive.Revive { public class RevivalTracker { public static RevivalTracker instance; public static readonly string[] IgnoredStages = new string[1] { "bazaar" }; private readonly PlayersTracker players; private readonly RunTracker run; private readonly ReviveRules rules; private readonly DeathTotemTracker deathTotemTracker; private readonly ReviveProgressBarTracker reviveProgressBarTracker; public RevivalTracker(PlayersTracker players, RunTracker run, ReviveRules rules, DeathTotemTracker deathTotemTracker, ReviveProgressBarTracker reviveProgressBarTracker) { instance = this; this.players = players; this.run = run; this.rules = rules; this.deathTotemTracker = deathTotemTracker; this.reviveProgressBarTracker = reviveProgressBarTracker; this.players.OnPlayerDead += OnPlayerDead; this.players.OnPlayerAlive += OnPlayerAlive; Stage.onStageStartGlobal += OnStageStart; } private void OnPlayerDead(Player player) { player.ClearReviveLinks(); } private void OnPlayerAlive(Player player) { foreach (Player item in players.All) { item.RemoveReviveLink(player); } } private void OnStageStart(Stage self) { //IL_00d2: Unknown result type (might be due to invalid IL or missing references) string cachedName = self.sceneDef.cachedName; Log.Debug("Stage start: " + self.sceneDef.cachedName); deathTotemTracker.Clear(); foreach (Player item in players.All) { item.ClearReviveLinks(); } if (NetworkHelper.IsClient() || IgnoredStages.Contains(cachedName) || !run.IsDeathCurseEnabled) { return; } foreach (NetworkUser readOnlyInstances in NetworkUser.readOnlyInstancesList) { RemoveReduceHpItem(readOnlyInstances); Inventory inventory = readOnlyInstances.master.inventory; if (inventory != null) { inventory.RemoveItem(RevivalToken.Index, 1); } } } public void Update() { //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) if (!run.IsStarted) { return; } if (NetworkHelper.IsClient()) { reviveProgressBarTracker.Update(); } else { if (!players.Setup) { return; } UpdatePlayersGroundPosition(); for (int i = 0; i < players.Dead.Count; i++) { Player player = players.Dead[i]; DeathTotemBehavior deathTotem = player.deathTotem; if ((Object)(object)deathTotem == (Object)null) { Log.Warn($"Death Totem is missing {i}"); continue; } if (player.CheckAlive() && !rules.Values.DebugKeepTotem) { Log.Info("Removing totem revived by other means"); players.PlayerAlive(player); continue; } float num = 0f; int num2 = 0; int insidePlayersHash = deathTotem.GetInsidePlayersHash(); float num3 = rules.CalculateDeathTotemRadius(player); for (int j = 0; j < players.Alive.Count; j++) { Player player2 = players.Alive[j]; if (player2.CheckDead()) { continue; } CharacterBody body = player2.GetBody(); bool flag = body.inventory.GetItemCount(DeadMansHandItem.Index) > 0 || (double)Vector3.Distance(body.transform.position, ((Component)deathTotem).transform.position) < (double)num3 * 0.5; bool flag2 = !rules.Values.RequireHitboxesActive || !body.disablingHurtBoxes; if (flag && flag2) { num2++; if (!deathTotem.insidePlayerIDs.Contains(((NetworkBehaviour)body).netId)) { deathTotem.insidePlayerIDs.Add(((NetworkBehaviour)body).netId); } float reviveSpeed = rules.GetReviveSpeed(player2, deathTotem.insidePlayerIDs.Count); num += reviveSpeed; player.reviveProgress += reviveSpeed * Time.deltaTime; player.reviveProgress = Mathf.Clamp01(player.reviveProgress); player2.IncreaseReviveLinkDuration(player, Time.deltaTime + Time.deltaTime / rules.Values.ReduceReviveProgressFactor * rules.Values.ReviveLinkBuffTimeFactor); DamageReviver(body, player); } else if (deathTotem.insidePlayerIDs.Contains(((NetworkBehaviour)body).netId)) { deathTotem.insidePlayerIDs.Remove(((NetworkBehaviour)body).netId); } } if (player.reviveProgress >= 1f) { Revive(player); } else { deathTotemTracker.UpdateTotem(player, insidePlayersHash, num2, num); } } if (run.IsDeathCurseEnabled) { UpdateReviveLinkBuffs(); } reviveProgressBarTracker.Update(); } } private void Revive(Player dead) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) Player[] array = players.Alive.Where((Player p) => p.IsLinkedTo(dead)).ToArray(); ScheduleCutReviveeHp(dead); players.Respawn(dead); if (run.IsDeathCurseEnabled) { ApplyDeathCurses(dead, array); } foreach (Player item in players.All) { item.RemoveReviveLink(dead); } if (rules.Values.PostReviveRegenDurationSec != 0f) { Player[] array2 = array; foreach (Player player in array2) { player.GetBody().AddTimedBuff(ReviveRegen.Index, rules.Values.PostReviveRegenDurationSec); } } } private void ApplyDeathCurses(Player dead, Player[] linkedPlayers) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) float chance2 = Mathf.Clamp(100f - rules.Values.DeathCurseChance, 0f, 100f); float chance3 = Mathf.Clamp(100f - rules.Values.ReviverDeathCurseChance, 0f, 100f); RollCurse(dead, chance2, dead.ItemCount(RevivalToken.Index)); Player[] array = SortByLinkDuration(linkedPlayers, dead).ToArray(); for (int i = 0; i < array.Length; i++) { Player player2 = array[i]; if (RollCurse(player2, chance3, i)) { break; } } if (rules.Values.EnableRevivalToken) { dead.GiveItem(RevivalToken.Index); } static bool RollCurse(Player player, float chance, float extraLuck) { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown float num = extraLuck + player.master.master.luck; bool flag = Util.CheckRoll(chance, num, (player.master.master.luck > 0f) ? player.master.master : null); Log.Info($"Rolled curse for {player.networkUser.userName}: {chance:F1}% (luck: {player.master.master.luck:F0}+{extraLuck:F0}). Success: {flag}"); SimpleChatMessage val; if (!flag) { player.GiveItem(DeathCurse.ItemIndex); val = new SimpleChatMessage(); val.baseToken = LanguageConsts.TEAMMATE_REVIVAL_UI_CURSED; val.paramTokens = new string[2] { player.networkUser.userName, $"{num:F0}" }; Chat.SendBroadcastChat((ChatMessageBase)(object)val); return true; } val = new SimpleChatMessage(); val.baseToken = LanguageConsts.TEAMMATE_REVIVAL_UI_AVOIDED_CURSE; val.paramTokens = new string[2] { player.networkUser.userName, $"{num:F0}" }; Chat.SendBroadcastChat((ChatMessageBase)(object)val); return false; } } private IEnumerable<Player> SortByLinkDuration(Player[] linkedPlayers, Player dead) { float totalTime = linkedPlayers.Sum((Player p) => p.GetReviveLinkDuration(dead)); float p2 = 0f; return from player in linkedPlayers select new { player = player, fraction = (p2 += player.GetReviveLinkDuration(dead) / totalTime) } into t orderby t.fraction select t.player; } private void DamageReviver(CharacterBody playerBody, Player dead) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (playerBody.inventory.GetItemCount(Items.ShieldOnly) > 0) { playerBody.healthComponent.Networkshield = CalcDamageResult(playerBody.maxShield, playerBody.healthComponent.shield, 0.1f, dead, playerBody.inventory.GetItemCount(DeadMansHandItem.Index)); } else { playerBody.healthComponent.Networkhealth = CalcDamageResult(playerBody.maxHealth, playerBody.healthComponent.health, 0.05f, dead, playerBody.inventory.GetItemCount(DeadMansHandItem.Index)); } if (playerBody.outOfDangerStopwatch > 3f) { playerBody.outOfDangerStopwatch = 3f; } } private float CalcDamageResult(float max, float current, float dmgThreshold, Player dead, int reviverReviveEverywhereCount) { float damageSpeed = rules.GetDamageSpeed(max, dead, reviverReviveEverywhereCount); float num = damageSpeed * Time.deltaTime; float num2 = max * dmgThreshold; if (current < num2) { return current; } return Mathf.Clamp(current - num, num2, max); } private void UpdatePlayersGroundPosition() { foreach (Player item in players.Alive) { if (!((Object)(object)item.GetBody() == (Object)null)) { item.UpdateGroundPosition(); } } } private void RemoveReduceHpItem(NetworkUser networkUser) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) if (!NetworkHelper.IsClient()) { Log.DebugMethod(null, "RemoveReduceHpItem", 336); string userName = networkUser.userName; CharacterMaster master = networkUser.master; Inventory val = ((master != null) ? master.inventory : null); if ((Object)(object)val == (Object)null) { Log.Warn("Player has no inventory! " + userName); return; } int itemCount = val.GetItemCount(DeathCurse.ItemIndex); val.RemoveItem(DeathCurse.ItemIndex, val.GetItemCount(CharonsObol.Index) + 1); Log.Info($"Removed reduce HP item for ({userName}). Was {itemCount}. Now: {val.GetItemCount(DeathCurse.ItemIndex)}"); } } private void UpdateReviveLinkBuffs() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) foreach (Player item in players.Alive) { CharacterBody body = item.GetBody(); if (!((Object)(object)body == (Object)null)) { body.SetBuffCount(ReviveLink.Index, item.GetPlayersReviveLinks()); } } } private void ScheduleCutReviveeHp(Player player) { CharacterBody.onBodyStartGlobal += Callback; void Callback(CharacterBody body) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //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) NetworkInstanceId? bodyId = player.BodyId; NetworkInstanceId netId = ((NetworkBehaviour)body).netId; if (bodyId.HasValue && !(bodyId.GetValueOrDefault() != netId)) { CutReviveeHp(body); CharacterBody.onBodyStartGlobal -= Callback; } } } private void CutReviveeHp(CharacterBody body) { float networkhealth = body.healthComponent.Networkhealth; float num = (body.maxHealth + body.maxShield) * 0.4f; body.healthComponent.Networkhealth = Mathf.Clamp(num, 1f, body.maxHealth); body.healthComponent.Networkshield = Mathf.Clamp(body.maxHealth - num, 0f, body.maxShield); Log.DebugMethod($"Prev hp: {networkhealth}; Now: {body.healthComponent.health}", "CutReviveeHp", 380); } } public class ReviveInteraction : MonoBehaviour, IInteractable, IDisplayNameProvider { public string GetContextString(Interactor activator) { return Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_USE_OBOL); } public Interactability GetInteractability(Interactor activator) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) NetworkUser val = Util.LookUpBodyNetworkUser(((Component)activator).gameObject); if (Object.op_Implicit((Object)(object)val) && val.master.inventory.GetItemCount(CharonsObol.Index) > 0) { return (Interactability)2; } return (Interactability)1; } public void OnInteractionBegin(Interactor interactor) { //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_0034: 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_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) Log.DebugMethod("Interaction! " + ((Object)interactor).name, "OnInteractionBegin", 31); NetworkInstanceId netId = ((Component)this).gameObject.GetComponent<NetworkBehaviour>().netId; NetworkInstanceId val = netId; Log.DebugMethod("Totem Id " + ((object)(NetworkInstanceId)(ref val)).ToString(), "OnInteractionBegin", 33); HandleInteraction(((NetworkBehaviour)interactor).netId, netId); } public static void HandleInteraction(NetworkInstanceId playerNetId, NetworkInstanceId totemId) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) Log.DebugMethod("Server respawn!", "HandleInteraction", 39); Player player = PlayersTracker.instance.FindByBodyId(playerNetId); Log.DebugMethod("Player " + player, "HandleInteraction", 41); DeathTotemBehavior totemComponent = Util.FindNetworkObject(totemId).GetComponent<DeathTotemBehavior>(); NetworkInstanceId val = totemId; Log.DebugMethod("Totem component " + ((object)(NetworkInstanceId)(ref val)).ToString(), "HandleInteraction", 44); Player player2 = PlayersTracker.instance.All.FirstOrDefault((Player dp) => (Object)(object)dp.deathTotem == (Object)(object)totemComponent); Log.DebugMethod("Dead " + player2, "HandleInteraction", 46); if (player2 == null || player == null) { Log.Error($"Cannot find player(s): {player} -> {player2}"); } else if (player.GetBody().inventory.GetItemCount(CharonsObol.Index) > 0) { PlayersTracker.instance.Respawn(player2); player.master.master.inventory.RemoveItem(CharonsObol.Index, 1); } } public bool ShouldIgnoreSpherecastForInteractibility(Interactor activator) { return false; } public bool ShouldShowOnScanner() { return false; } public string GetDisplayName() { return Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_USE_OBOL); } public void OnEnable() { InstanceTracker.Add<ReviveInteraction>(this); } public void OnDisable() { InstanceTracker.Remove<ReviveInteraction>(this); } public bool ShouldProximityHighlight() { return false; } } public class ReviveLinkBuffIconManager { private const float MIN_OPACITY = 0f; private const float OPACITY_CHANGE_TIME = 1f; private bool decreasing = false; private float elapsedTime = 0f; public ReviveLinkBuffIconManager() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown BuffIcon.UpdateIcon += new hook_UpdateIcon(Hook_Update); } private void Hook_Update(orig_UpdateIcon orig, BuffIcon buffIcon) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) orig.Invoke(buffIcon); if (!Object.op_Implicit((Object)(object)buffIcon.buffDef)) { return; } Image iconImage = buffIcon.iconImage; Color color = ((Graphic)iconImage).color; if (buffIcon.buffDef.buffIndex != ReviveLink.Index) { if (color.a != 1f) { Color color2 = color; color2.a = 1f; ((Graphic)iconImage).color = color2; } return; } Color val = default(Color); Color val2 = default(Color); if (decreasing) { ((Color)(ref val))..ctor(color.r, color.g, color.b, 1f); ((Color)(ref val2))..ctor(color.r, color.g, color.b, 0f); } else { ((Color)(ref val))..ctor(color.r, color.g, color.b, 0f); ((Color)(ref val2))..ctor(color.r, color.g, color.b, 1f); } ((Graphic)iconImage).color = Color.Lerp(val, val2, EaseInCubic(elapsedTime / 1f)); elapsedTime += Time.deltaTime; while (elapsedTime > 1f) { elapsedTime -= 1f; decreasing = !decreasing; } } private static float EaseInCubic(float val) { if (val > 1f) { return 1f; } return val * val * val; } } } namespace TeammateRevive.Revive.Rules { public class ReviveRules { public static ReviveRules instance; private readonly RunTracker run; private readonly PluginConfig pluginConfig; public ReviveRuleValues Values { get; private set; } public float ReduceReviveProgressSpeed { get; private set; } public float ReviveLinkBuffTime { get; private set; } public event Action<ReviveRuleValues, ReviveRuleValues> ValuesChanged; public ReviveRules(RunTracker run, PluginConfig pluginConfig) { instance = this; this.run = run; this.pluginConfig = pluginConfig; run.RunStarted += OnRunStarted; pluginConfig.RuleValuesBindCollection.OnChanged += ApplyConfigValues; } private void OnRunStarted(RunTracker sender) { if (NetworkHelper.IsServer) { SendValues(); } } public void ApplyConfigValues() { ApplyValues(pluginConfig.RuleValues.Clone()); } public void ApplyValues(ReviveRuleValues newValues) { ReviveRuleValues values = Values; Values = newValues; ReduceReviveProgressSpeed = 0f - 1f / newValues.ReviveTimeSeconds * newValues.ReduceReviveProgressFactor; ReviveLinkBuffTime = newValues.ReviveTimeSeconds / newValues.ReduceReviveProgressFactor * newValues.ReviveLinkBuffTimeFactor; this.ValuesChanged?.Invoke(values, newValues); } public void SendValues() { NetMessageExtensions.Send((INetMessage)(object)new SetRulesMessage(Values), (NetworkDestination)1); } public float CalculateDeathTotemRadius(Player dead) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) int itemCount = dead.master.master.inventory.GetItemCount(CharonsObol.Index); int count = dead.deathTotem.insidePlayerIDs.Count; return CalculateDeathTotemRadius(itemCount, count); } public float CalculateDeathTotemRadius(int itemCount, int playersCount) { float num = Values.BaseTotemRange; if (run.IsDeathCurseEnabled) { float num2 = Values.BaseTotemRange * (float)itemCount * Values.ItemIncreaseRangeFactor; num += num2; } float num3 = Values.BaseTotemRange * Values.IncreaseRangeWithPlayersFactor * (float)playersCount; return num + num3; } public float GetReviveSpeed(Player reviver, int playersInRange) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) int itemCount = reviver.master.master.inventory.GetItemCount(CharonsObol.Index); int itemCount2 = reviver.master.master.inventory.GetItemCount(DeadMansHandItem.Index); return GetReviveSpeed(itemCount, itemCount2, playersInRange); } public float GetReviveSpeed(int obolsCount, int reviveEverywhereCount, int playersInRange) { float num = 1f / Values.ReviveTimeSeconds / (float)playersInRange; if (reviveEverywhereCount > 0) { num /= 2f; } if (run.IsDeathCurseEnabled) { float num2 = Values.ReviveTimeSeconds / (Values.ReviveTimeSeconds / Mathf.Pow(Values.ObolReviveFactor, (float)(obolsCount + reviveEverywhereCount))); num *= num2; } return num; } public float GetReviveIncrease(int obolsCount) { return Values.ReviveTimeSeconds / Mathf.Pow(Values.ObolReviveFactor, (float)obolsCount); } public float GetReviveTime(int obolsCount, int reviveEverywhereCount) { return Values.ReviveTimeSeconds / GetReviveSpeed(obolsCount, reviveEverywhereCount, 1); } public float GetReviveTimeIncrease(int obolsCount, int reviveEverywhereCount) { return GetReviveSpeed(obolsCount, 0, 1) / GetReviveSpeed(obolsCount, reviveEverywhereCount, 1); } public float GetDamageSpeed(float playerMaxHealth, Player dead, int reviverEverywhereObolCount) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) int count = dead.deathTotem.insidePlayerIDs.Count; int itemCount = dead.master.master.inventory.GetItemCount(CharonsObol.Index); return GetDamageSpeed(count, playerMaxHealth, itemCount, reviverEverywhereObolCount); } public float GetDamageSpeed(int playersInRange, float playerMaxHealth, int deadPlayerObolsCount, int reviverEverywhereObolCount) { float num = playerMaxHealth * 0.85f / Values.ReviveTimeSeconds / (float)playersInRange; if (run.IsDeathCurseEnabled) { num *= GetReviveReduceDamageFactor(deadPlayerObolsCount, reviverEverywhereObolCount); } return num; } public float GetReviveReduceDamageFactor(int deadPlayerObolsCount, int reviverEverywhereObolCount) { return 1f / Mathf.Pow(Values.ObolDamageReduceFactor, (float)deadPlayerObolsCount); } public float GetCurseReduceHpFactor(int reducesCount) { return Mathf.Pow(Values.ReduceHpFactor, (float)reducesCount) + Values.BaseReduceHpFactor; } } public class ReviveRuleValues { public float BaseTotemRange { get; set; } = 30f; public float IncreaseRangeWithPlayersFactor { get; set; } = 0.4f; public float ItemIncreaseRangeFactor { get; set; } = 0.5f; public float ReviveTimeSeconds { get; set; } = 6f; public float ReduceReviveProgressFactor { get; set; } = 0.05f; public float ReviveLinkBuffTimeFactor { get; set; } = 1f; public float ObolReviveFactor { get; set; } = 1.125f; public float ObolDamageReduceFactor { get; set; } = 1.1f; public float PostReviveRegenDurationSec { get; set; } = 4f; public float PostReviveRegenFraction { get; set; } = 0.4f; public float BaseReduceHpFactor { get; set; } = 0.25f; public float ReduceHpFactor { get; set; } = 1.2f; public bool ForceDeathCurseRule { get; set; } = false; public bool DebugKeepTotem { get; set; } = false; public bool ForceEnableDeathCurseForSinglePlayer { get; set; } = false; public bool EnableRevivalToken { get; set; } = true; public bool CutReviveeHp { get; set; } = true; public float DeathCurseChance { get; set; } = 66f; public float ReviverDeathCurseChance { get; set; } = 66f; public bool RequireHitboxesActive { get; set; } = false; public ReviveRuleValues Clone() { return (ReviveRuleValues)MemberwiseClone(); } } public class SetRulesMessage : INetMessage, ISerializableObject { private readonly ReviveRuleValues ruleValues; public SetRulesMessage() { ruleValues = new ReviveRuleValues(); } public SetRulesMessage(ReviveRuleValues ruleValues) { this.ruleValues = ruleValues; } public void Serialize(NetworkWriter writer) { writer.Write(ruleValues.BaseTotemRange); writer.Write(ruleValues.IncreaseRangeWithPlayersFactor); writer.Write(ruleValues.ItemIncreaseRangeFactor); writer.Write(ruleValues.ReviveTimeSeconds); writer.Write(ruleValues.ObolReviveFactor); writer.Write(ruleValues.ReduceHpFactor); writer.Write(ruleValues.BaseReduceHpFactor); writer.Write(ruleValues.ReduceReviveProgressFactor); writer.Write(ruleValues.ReviveLinkBuffTimeFactor); writer.Write(ruleValues.ObolDamageReduceFactor); writer.Write(ruleValues.ForceDeathCurseRule); writer.Write(ruleValues.DebugKeepTotem); writer.Write(ruleValues.EnableRevivalToken); writer.Write(ruleValues.CutReviveeHp); writer.Write(ruleValues.PostReviveRegenDurationSec); Log.Info("Sending new rule values"); } public void Deserialize(NetworkReader reader) { ruleValues.BaseTotemRange = reader.ReadSingle(); ruleValues.IncreaseRangeWithPlayersFactor = reader.ReadSingle(); ruleValues.ItemIncreaseRangeFactor = reader.ReadSingle(); ruleValues.ReviveTimeSeconds = reader.ReadSingle(); ruleValues.ObolReviveFactor = reader.ReadSingle(); ruleValues.ReduceHpFactor = reader.ReadSingle(); ruleValues.BaseReduceHpFactor = reader.ReadSingle(); ruleValues.ReduceReviveProgressFactor = reader.ReadSingle(); ruleValues.ReviveLinkBuffTimeFactor = reader.ReadSingle(); ruleValues.ObolDamageReduceFactor = reader.ReadSingle(); ruleValues.ForceDeathCurseRule = reader.ReadBoolean(); ruleValues.DebugKeepTotem = reader.ReadBoolean(); ruleValues.EnableRevivalToken = reader.ReadBoolean(); ruleValues.CutReviveeHp = reader.ReadBoolean(); ruleValues.PostReviveRegenDurationSec = reader.ReadSingle(); } public void OnReceived() { Log.Info("Received new rule values"); if (NetworkHelper.IsClient()) { ReviveRules.instance.ApplyValues(ruleValues); Log.Info("Applied new rule values"); } } } } namespace TeammateRevive.Resources { public static class CustomResources { public static GameObject CharonsObolItemPrefab; public static GameObject HandItemPrefab; public static Sprite CharonsObolItemIcon; public static Sprite DeathCurseBuffIcon; public static Sprite ReviveLinkBuffIcon; public static Sprite LunarHandIcon; public static Sprite DeathCurseArtifactEnabledIcon; public static Sprite DeathCurseArtifactDisabledIcon; public static readonly List<Material> Materials = new List<Material>(); public static GameObject CurseOrbPrefab; public static GameObject progressBarPrefab; public static DeathTotemBehavior DeathTotem { get; private set; } private static void InitializeDeathTotem(GameObject totemPrefab) { Log.DebugMethod(null, "InitializeDeathTotem", 17); totemPrefab.AddComponent<DeathTotemBehavior>(); DeathTotem = PrefabAPI.InstantiateClone(totemPrefab, "Death Totem").GetComponent<DeathTotemBehavior>(); totemPrefab.GetComponent<DeathTotemBehavior>().Setup(); } public static void LoadCustomResources() { Log.DebugMethod(null, "LoadCustomResources", 25); Log.Debug("Loading custom resources..."); using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("TeammateRevive.Resources.customresources"); AssetBundle val = AssetBundle.LoadFromStream(stream); ReplaceStubbedShaders(val); CharonsObolItemIcon = val.LoadAsset<Sprite>("Assets/CustomAssets/icons/obol.png"); DeathCurseBuffIcon = val.LoadAsset<Sprite>("Assets/CustomAssets/icons/curse.png"); ReviveLinkBuffIcon = val.LoadAsset<Sprite>("Assets/CustomAssets/icons/timed_curse.png"); LunarHandIcon = val.LoadAsset<Sprite>("Assets/CustomAssets/icons/lunar_hand.png"); DeathCurseArtifactEnabledIcon = val.LoadAsset<Sprite>("Assets/CustomAssets/icons/artifactCurseEnabled.png"); DeathCurseArtifactDisabledIcon = val.LoadAsset<Sprite>("Assets/CustomAssets/icons/artifactCurseDisabled.png"); CurseOrbPrefab = val.LoadAsset<GameObject>("Assets/CustomAssets/curseOrb.prefab"); progressBarPrefab = val.LoadAsset<GameObject>("Assets/CustomAssets/progressBar.prefab"); CharonsObolItemPrefab = val.LoadAsset<GameObject>("Assets/CustomAssets/obol.prefab"); HandItemPrefab = val.LoadAsset<GameObject>("Assets/CustomAssets/handItem.prefab"); InitializeDeathTotem(val.LoadAsset<GameObject>("Assets/CustomAssets/deathTotem.prefab")); val.Unload(false); } private static void ReplaceStubbedShaders(AssetBundle bundle) { Log.Debug("Replacing the stubbed shaders of the " + ((Object)bundle).name + " asset bundle"); Material[] array = bundle.LoadAllAssets<Material>(); Log.Debug($"Found {array.Length} materials in the asset bundle"); Material[] array2 = array; foreach (Material val in array2) { Log.Debug("Loading the material " + ((Object)val).name); if (((Object)val.shader).name.StartsWith("StubbedShader")) { Log.Debug("Loading the stubbed shared for shader " + ((Object)val.shader).name); string text = "shaders" + ((Object)val.shader).name.Substring(13); Shader val2 = LegacyResourcesAPI.Load<Shader>(text); if (val2 == null) { Log.Warn("Could not find the shader for material " + ((Object)val).name + " at the path " + text); } else { Log.Debug("Loaded the stubbed shared " + ((Object)val2).name + " from the path " + text); } val.shader = val2; Materials.Add(val); } else { Log.Debug("The shader was not loaded because it is not a stubbed shader"); } } } } } namespace TeammateRevive.ProgressBar { public class CharArrayBuilder { private class Part { public int Start; public int Len; public Part(int start, int len) { Start = start; Len = len; } } private Part[] parts = Array.Empty<Part>(); private const char PADDING = '\u200b'; private const int FRACTION_LEN = 1; private const int WHOLE_NUMBER_LEN = 3; private const int DIGITS_COUNT = 4; private const int DIGITS_MULT = 1000; public char[] Buffer { get; private set; } = Array.Empty<char>(); public int Length { get; set; } public CharArrayBuilder(params string[] strs) { InitParts(strs); } public void InitParts(params string[] strs) { Length = 0; foreach (string text in strs) { Length += text.Length; } if (Buffer.Length < Length) { Buffer = new char[Length + 7]; } if (parts.Length < strs.Length) { parts = new Part[strs.Length]; } int num = 0; for (int j = 0; j < strs.Length; j++) { string text2 = strs[j]; text2.CopyTo(0, Buffer, num, text2.Length); parts[j] = new Part(num, text2.Length); num += text2.Length; } } public void UpdatePart(int idx, string value) { Part part = parts[idx]; UpdatePartLen(part, value.Length, idx); value.CopyTo(0, Buffer, part.Start, value.Length); } private void UpdatePartLen(Part part, int len, int partIdx) { int num = part.Len - len; if (num != 0) { int num2 = Length - num; if (num < 0 && Buffer.Length < num2) { char[] array = new char[num2]; Array.Copy(Buffer, array, Length); Buffer = array; } Array.Copy(Buffer, part.Start + part.Len, Buffer, part.Start + part.Len - num, Length - part.Start - part.Len); part.Len = len; Length -= num; for (int i = partIdx + 1; i < parts.Length; i++) { Part part2 = parts[i]; part2.Start -= num; } } } public void SetPaddedPercentagePart(int partIdx, float value) { InternalSetPaddedFloatPart(partIdx, (int)(value * 1000f)); } private void InternalSetPaddedFloatPart(int partIdx, int value) { if (value > 1000) { value = 1000; } Part part = parts[partIdx]; UpdatePartLen(part, 5, partIdx); int start = part.Start; for (int i = 0; i < 2; i++) { Buffer[start + i] = '\u200b'; } int num = 10; int num2 = 1; int num3 = 0; bool flag = false; for (int j = 0; j < 4; j++) { int num4 = value % num; if (num4 == value) { flag = true; } num4 /= num2; num *= 10; num2 *= 10; if (j == 1) { num3 = 1; Buffer[start + 4 - j] = '.'; } Buffer[start + 4 - j - num3] = (char)(48 + num4); if (flag && num3 == 1) { break; } } } public override string ToString() { return new string(Buffer, 0, Length); } public string TraceParts() { return string.Join("", parts.Select((Part p, int i) => $"[{i}>" + new string(Buffer, p.Start, p.Len))); } } public class ProgressBarController { public static ProgressBarController Instance; private TextMeshProUGUI textComponent; private string currentName = DefaultName; private readonly CharArrayBuilder charArrayBuilder; public bool showing = false; private Slider progressBar; public float progress; private HUD hudRef; private RectTransform healthbarTransform; private RectTransform barRootTransform; private RectTransform progressBarTransform; private static string DefaultName => Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_PLAYER); private static string Reviving => Language.GetString(LanguageConsts.TEAMMATE_REVIVAL_UI_PROGRESS_BAR_REVIVING); public ProgressBarController() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown Log.Debug("Init ResurrectController"); HUD.Awake += new hook_Awake(HUDOnAwake); Instance = this; charArrayBuilder = new CharArrayBuilder("Reviving ", DefaultName, " - ", "000.0", "%"); } public void UpdateText(string name, float progress = 0f) { name = (string.IsNullOrEmpty(name) ? DefaultName : name); if (currentName != name) { charArrayBuilder.UpdatePart(1, name); currentName = name; } charArrayBuilder.SetPaddedPercentagePart(3, progress); ((TMP_Text)textComponent).SetCharArray(charArrayBuilder.Buffer, 0, charArrayBuilder.Length); Show(); } public void SetFraction(float fraction) { if (!((Object)(object)progressBar == (Object)null)) { progressBar.value = fraction; progress = fraction; Show(); } } public void SetColor(Color color) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)progressBar == (Object)null)) { ((Graphic)((Component)progressBar.fillRect).GetComponent<Image>()).color = color; Show(); } } public (Vector3 bottomLeftOffset, Vector2 size) GetBarPositionAndSize() { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) if (((Object)((Component)progressBar).transform.parent).name != "BottomCenterCluster") { Transform val = hudRef.mainUIPanel.transform.Find("SpringCanvas/BottomCenterCluster"); if ((Object)(object)val != (Object)null) { ((Component)progressBar).transform.SetParent(val); } else { ((Component)progressBar).transform.SetParent(hudRef.mainUIPanel.transform); } } Vector2 parentSize = GetParentSize(progressBarTransform); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor((float)Screen.width / 3.5f, (float)Screen.height / 27f); Vector3 item = default(Vector3); ((Vector3)(ref item))..ctor(parentSize.x / 2f - val2.x / 2f, val2.y * 6f, 0f); Transform val3 = hudRef.mainUIPanel.transform.Find("SpringCanvas/BottomLeftCluster/BarRoots/HealthbarRoot"); Transform val4 = hudRef.mainUIPanel.transform.Find("SpringCanvas/BottomLeftCluster/BarRoots"); if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null) { return (item, val2); } if ((Object)(object)healthbarTransform == (Object)null || (Object)(object)barRootTransform == (Object)null) { healthbarTransform = ((Component)val3).GetComponent<RectTransform>(); barRootTransform = ((Component)val4).GetComponent<RectTransform>(); } float num = parentSize.x * 0.8f; Rect rect = healthbarTransform.rect; ((Vector2)(ref val2))..ctor(num, ((Rect)(ref rect)).height); rect = barRootTransform.rect; float num2 = ((Rect)(ref rect)).width / Mathf.Sin(MathF.PI / 2f); Quaternion rotation = val4.parent.rotation; float num3 = num2 * Mathf.Sin((0f - ((Quaternion)(ref rotation)).eulerAngles.y) * (MathF.PI / 180f)); ((Vector3)(ref item))..ctor(parentSize.x / 2f - val2.x / 2f, healthbarTransform.GetBottomLeftOffset().y, num3); return (item, val2); } public void UpdatePositionAndSize() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) var (val, val2) = GetBarPositionAndSize(); progressBarTransform.SetSizeInPixels(val2.x, val2.y); progressBarTransform.SetBottomLeftOffset(val.x, val.y); ((Transform)progressBarTransform).localScale = Vector3.one; ((Transform)progressBarTransform).localPosition = ((Transform)progressBarTransform).localPosition.SetZ(val.z); } public void Hide() { currentName = DefaultName; ((Component)progressBar).GetComponent<CanvasGroup>().alpha = 0f; showing = false; } public void Show() { if (!showing) { UpdatePositionAndSize(); showing = true; ((Component)progressBar).GetComponent<CanvasGroup>().alpha = 1f; charArrayBuilder.UpdatePart(0, Reviving); } } public void AttachProgressBar(HUD hud) { Log.Debug("AttachProgressBar"); hudRef = hud; progressBar = PrefabAPI.InstantiateClone(CustomResources.progressBarPrefab, "Revival Progress Bar").GetComponent<Slider>(); progressBarTransform = ((Component)progressBar).GetComponent<RectTransform>(); textComponent = ((Component)progressBar).GetComponentInChildren<TextMeshProUGUI>(); ((TMP_Text)textComponent).font = HGTextMeshProUGUI.defaultLanguageFont; Hide(); } public void Destroy() { Slider obj = progressBar; if (Object.op_Implicit((Object)(object)((obj != null) ? ((Component)obj).gameObject : null))) { Object.Destroy((Object)(object)((Component)progressBar).gameObject); } } private void HUDOnAwake(orig_Awake orig, HUD self) { Log.Debug("HUDOnAwake"); orig.Invoke(self); AttachProgressBar(self); } private Vector2 GetParentSize(RectTransform transform) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) Transform parent = ((Transform)transform).parent; RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null); Vector2 result; if (!((Object)(object)val == (Object)null)) { Rect rect = val.rect; result = ((Rect)(ref rect)).size; } else { result = Vector2.zero; } return result; } } public class ReviveProgressBarTracker { public static readonly Color NegativeProgressColor = new Color(0.84f, 0.2f, 0.28f, 1f); public static readonly Color ZeroProgressColor = new Color(0.39f, 0.12f, 0.73f, 1f); public static readonly Color FullProgressColor = new Color(0.09f, 0.49f, 0.3f, 1f); private readonly ProgressBarController progressBar; private readonly PlayersTracker players; private readonly DeathTotemTracker deathTotemTracker; private readonly ReviveRules rules; public DeathTotemBehavior trackingTotem; private float queuedToHideAt; private SpectatorLabel spectatorLabel; public Color PositiveProgressColor => Color.Lerp(ZeroProgressColor, FullProgressColor, progressBar.progress); private bool IsQueuedToHide => queuedToHideAt > 0f; public ReviveProgressBarTracker(ProgressBarController progressBar, PlayersTracker players, DeathTotemTracker totemTracker, ReviveRules rules) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown this.progressBar = progressBar; this.players = players; deathTotemTracker = totemTracker; this.rules = rules; DeathTotemBehavior.GlobalOnDestroy = (Action<DeathTotemBehavior>)Delegate.Combine(DeathTotemBehavior.GlobalOnDestroy, new Action<DeathTotemBehavior>(OnDeathTotemDestroy)); SpectatorLabel.Awake += new hook_Awake(SpectatorLabelAwake); } private void SpectatorLabelAwake(orig_Awake orig, SpectatorLabel self) { orig.Invoke(self); spectatorLabel = self; } private void OnDeathTotemDestroy(DeathTotemBehavior totem) { if ((Object)(object)trackingTotem == (Object)(object)totem) { Log.DebugMethod("removing tracking - totem destroyed", "OnDeathTotemDestroy", 57); RemoveTracking(); } } public void Update() { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) DeathTotemBehavior deathTotemInRange = GetDeathTotemInRange(); if ((Object)(object)deathTotemInRange == (Object)(object)trackingTotem && (Object)(object)deathTotemInRange == (Object)null) { if (progressBar.showing) { Log.DebugMethod("hide - no totem, no tracking", "Update", 71); progressBar.Hide(); } return; } if ((Object)(object)deathTotemInRange != (Object)null && (Object)(object)trackingTotem != (Object)(object)deathTotemInRange) { Log.DebugMethod("new totem", "Update", 80); trackingTotem = deathTotemInRange; DequeFromHiding(); progressBar.UpdateText(deathTotemInRange.PlayerName); } if ((Object)(object)trackingTotem != (Object)null) { progressBar.SetFraction(trackingTotem.progress); progressBar.SetColor((trackingTotem.fractionPerSecond >= 0f) ? PositiveProgressColor : NegativeProgressColor); progressBar.UpdateText(trackingTotem.PlayerName, trackingTotem.progress); } if ((Object)(object)deathTotemInRange == (Object)null && (Object)(object)trackingTotem != (Object)null && !IsQueuedToHide) { Log.DebugMethod("queue to hide", "Update", 97); QueueToHide(); } if ((Object)(object)trackingTotem != (Object)null && trackingTotem.progress == 0f) { Log.DebugMethod("removing due to progress is 0", "Update", 104); RemoveTracking(); } if (IsQueuedToHide && Time.time > queuedToHideAt) { Log.DebugMethod($"removing tracking after delay ({Time.time} > {queuedToHideAt})", "Update", 111); RemoveTracking(); } } private void QueueToHide() { queuedToHideAt = Time.time + rules.ReviveLinkBuffTime; Log.DebugMethod($"Queued to hide at {queuedToHideAt} (current time: {Time.time})", "QueueToHide", 120); } private void DequeFromHiding() { queuedToHideAt = 0f; } private void RemoveTracking() { DequeFromHiding(); progressBar.Hide(); trackingTotem = null; } public DeathTotemBehavior GetDeathTotemInRange() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!deathTotemTracker.HasAnyTotems) { return null; } NetworkInstanceId? val = players.CurrentUserBodyId ?? GetSpectatingBody(); if (!val.HasValue) { return null; } return deathTotemTracker.GetDeathTotemInRange(val.Value); } private NetworkInstanceId? GetSpectatingBody() { //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (IsInSpectatorMode()) { GameObject cachedTarget = spectatorLabel.cachedTarget; if (cachedTarget.IsDestroyed()) { return null; } CharacterBody component = cachedTarget.GetComponent<CharacterBody>(); if ((Object)(object)component != (Object)null && !component.isPlayerControlled) { return null; } return ((NetworkBehaviour)component).netId; } return null; } private bool IsInSpectatorMode() { return (Object)(object)spectatorLabel != (Object)null && !((Component)spectatorLabel).gameObject.IsDestroyed() && !spectatorLabel.labelRoot.IsDestroyed() && spectatorLabel.labelRoot.activeSelf; } } } namespace TeammateRevive.Players { public class Player { public readonly NetworkUser networkUser; public readonly PlayerCharacterMasterController master; public DeathTotemBehavior deathTotem = null; public Vector3 groundPosition = Vector3.zero; public float reviveProgress = 0f; public bool isDead = false; public readonly Dictionary<Player, float> reviveLinks = new Dictionary<Player, float>(); public NetworkInstanceId? BodyId { get { //IL_001f: Unknown result type (might be due to invalid IL or missing references) CharacterBody body = master.master.GetBody(); return (body != null) ? new NetworkInstanceId?(((NetworkBehaviour)body).netId) : null; } } public Player(PlayerCharacterMasterController _player) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)_player.networkUser)) { networkUser = _player.networkUser; } master = _player; reviveProgress = 0f; } public CharacterBody GetBody() { return master.master.GetBody(); } public bool CheckAlive() { return Object.op_Implicit((Object)(object)GetBody()) && !master.master.IsDeadAndOutOfLivesServer() && GetBody().healthComponent.alive; } public bool CheckDead() { return !Object.op_Implicit((Object)(object)GetBody()) || master.master.IsDeadAndOutOfLivesServer() || !GetBody().healthComponent.alive; } public void UpdateGroundPosition() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) groundPosition = GetGroundPosition(this); } public static Vector3 GetGroundPosition(Player player) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //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) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) CharacterBody body = player.GetBody(); if ((Object)(object)body == (Object)null) { return Vector3.zero; } RaycastHit val = default(RaycastHit); if (Physics.Raycast(body.transform.position, Vector3.down, ref val, 1000f, LayerMask.GetMask(new string[1] { "World" })) && Vector3.Dot(((RaycastHit)(ref val)).normal, Vector3.up) > 0.5f && Vector3.Distance(body.transform.position, player.groundPosition) > Vector3.Distance(body.transform.position, ((RaycastHit)(ref val)).point)) { return ((RaycastHit)(ref val)).point; } return player.groundPosition; } public void IncreaseReviveLinkDuration(Player dead, float increase) { if (!reviveLinks.TryGetValue(dead, out var value)) { value = Time.time + 1f; } reviveLinks[dead] = value + increase; } public int RemoveReviveLink(Player dead) { reviveLinks.Remove(dead); return reviveLinks.Count; } public float GetReviveLinkDuration(Player dead) { if (reviveLinks.TryGetValue(dead, out var value)) { return value; } return 0f; } public void ClearReviveLinks() { reviveLinks.Clear(); } public int GetPlayersReviveLinks() { float time = Time.time; KeyValuePair<Player, float>[] array = reviveLinks.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair<Player, float> keyValuePair = array[i]; Player key = keyValuePair.Key; float value = keyValuePair.Value; if (time > value) { RemoveReviveLink(key); Log.Debug($"Removed revive link for revive of {key.networkUser.userName} from {networkUser.userName}. Left: {reviveLinks.Count}"); } } return reviveLinks.Count; } public bool IsLinkedTo(Player player) { return reviveLinks.ContainsKey(player); } } public static class PlayerExtensions { public static void GiveItem(this Player player, ItemIndex index, int count = 1) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) player.master.master.inventory.GiveItem(index, count); } public static void RemoveItem(this Player player, ItemIndex index, int count = 1) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) player.master.master.inventory.RemoveItem(index, count); } public static int ItemCount(this Player player, ItemIndex index) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) return player.master.master.inventory.GetItemCount(index); } } public class PlayersTracker { public static PlayersTracker instance; private readonly RunTracker run; private readonly PluginConfig config; private int numPlayersSetup = 0; private float playerSetupTimer = 3f; public List<Player> Alive = new List<Player>(); public List<Player> Dead = new List<Player>(); public List<Player> All = new List<Player>(); public PlayerCharacterMasterController CurrentUserPlayerCharacterMasterController { get; set; } public NetworkInstanceId? CurrentUserBodyId { get { //IL_002e: Unknown result type (might be due to invalid IL or missing references) PlayerCharacterMasterController currentUserPlayerCharacterMasterController = CurrentUserPlayerCharacterMasterController; NetworkInstanceId? result; if (currentUserPlayerCharacterMasterController == null) { result = null; } else { CharacterBody body = currentUserPlayerCharacterMasterController.master.GetBody(); result = ((body != null) ? new NetworkInstanceId?(((NetworkBehaviour)body).netId) : null); } return result; } } public int TotalCount { get; set; } public bool Setup { get; set; } public event Action<Player> OnPlayerDead; public event Action<Player> OnPlayerAlive; public event Action OnSetupFinished; public event Action<Player> OnPlayerRespawned; public PlayersTracker(RunTracker run, PluginConfig config) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown instance = this; this.run = run; this.config = config; Run.OnUserRemoved += new hook_OnUserRemoved(hook_OnUserRemoved); GlobalEventManager.OnPlayerCharacterDeath += new hook_OnPlayerCharacterDeath(hook_OnPlayerCharacterDeath); PlayerCharacterMasterController.OnBodyStart += new hook_OnBodyStart(hook_OnBodyStart); } public void Respawn(Player player) { if (NetworkHelper.IsClient() || !Dead.Contains(player)) { return; } if (player.master.isConnected) { if (!player.CheckAlive()) { ReviveHelper.RespawnExtraLife(player.master.master); } else { Log.Warn("Respawn was called for alive player!"); } PlayerAlive(player); } this.OnPlayerRespawned?.Invoke(player); Log.Info("Player Respawned"); } public void Reset() { Setup = false; Alive.Clear(); Dead.Clear(); All.Clear(); TotalCount = 0; numPlayersSetup = 0; playerSetupTimer = 0f; } public Player FindByCharacterMasterControllerId(NetworkInstanceId id) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) foreach (Player item in All) { if (Object.op_Implicit((Object)(object)item.master) && ((NetworkBehaviour)item.master).netId == id) { return item; } } return null; } public Player FindByBodyId(NetworkInstanceId id) { //IL_0027: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) foreach (Player item in Alive) { item.GetBody(); NetworkInstanceId? bodyId = item.BodyId; if (bodyId.HasValue && bodyId.GetValueOrDefault() == id) { return item; } } return null; } private void hook_OnUserRemoved(orig_OnUserRemoved orig, Run self, NetworkUser user) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (NetworkHelper.IsClient()) { orig.Invoke(self, user); return; } if (!run.IsStarted) { Log.Info(user.userName + " left while run wasn't in session."); } Player item = FindByCharacterMasterControllerId(((NetworkBehaviour)user.masterController).netId); if (All.Contains(item)) { All.Remove(item); if (Dead.Contains(item)) { Dead.Remove(item); } if (Alive.Contains(item)) { Alive.Remove(item); } Log.Info(user.userName + " Left!"); } else { Log.Error(user.userName + " Left - but they were not registered as a player!"); orig.Invoke(self, user); } } private void hook_OnPlayerCharacterDeath(orig_OnPlayerCharacterDeath orig, GlobalEventManager self, DamageReport damageReport, NetworkUser victimNetworkUser) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (NetworkHelper.IsServer) { Player player = FindByBodyId(((NetworkBehaviour)victimNetworkUser.GetCurrentBody()).netId); if (Alive.Contains(player)) { PlayerDead(player); Log.Info(victimNetworkUser.userName + " Died!"); } else { Log.Error("Player Died but they were not alive to begin with!"); } } orig.Invoke(self, damageReport, victimNetworkUser); } private void hook_OnBodyStart(orig_OnBodyStart orig, PlayerCharacterMasterController self) { orig.Invoke(self); if (((NetworkBehaviour)self.networkUser).isLocalPlayer) { CurrentUserPlayerCharacterMasterController = self; Log.Debug($"Set local player body to {CurrentUserBodyId} ({CurrentUserBodyId})"); } if (NetworkHelper.IsClient()) { return; } if (All.Any((Player p) => (Object)(object)p.master == (Object)(object)self)) { Log.Info("BodyStart: " + self.networkUser.userName + " player already exists."); return; } Player player = new Player(self); if (config.GodMode) { player.GetBody().baseDamage = 120f; player.networkUser.GetCurrentBody().baseMoveSpeed = 30f; player.GetBody().baseAttackSpeed = 200f; } Alive.Add(player); All.Add(player); player.isDead = false; numPlayersSetup++; Log.Info(self.networkUser.userName + " Setup"); TotalCount = NetworkManager.singleton.numPlayers; playerSetupTimer = 0f; if (numPlayersSetup == TotalCount) { Setup = true; Log.Info("All " + TotalCount + " Players Setup Successfully"); this.OnSetupFinished?.Invoke(); } } private void PlayerDead(Player p) { RemoveDuplicates(); LogPlayers("Player dead: " + p.networkUser.userName); if (Alive.Contains(p)) { Alive.Remove(p); } if (!Dead.Contains(p)) { Dead.Add(p); } p.isDead = true; p.reviveProgress = 0f; if (!NetworkHelper.IsClient()) { this.OnPlayerDead?.Invoke(p); } } public void PlayerAlive(Player p) { RemoveDuplicates(); LogPlayers("Player alive: " + p.networkUser.userName); if (!Alive.Contains(p)) { Alive.Add(p); } if (Dead.Contains(p)) { Dead.Remove(p); } p.isDead = false; p.reviveProgress = 0f; NetworkServer.Destroy(((Component)p.deathTotem).gameObject); this.OnPlayerAlive?.Invoke(p); } [Conditional("DEBUG")] private void RemoveDuplicates() { Alive = Alive.Distinct().ToList(); Dead = Dead.Distinct().ToList(); } private void LogPlayers(string prefix) { string text = string.Join(", ", Alive.Select((Player p) => p.networkUser.userName)); string text2 = string.Join(", ", Dead.Select((Player p) => p.networkUser.userName)); Log.Info(prefix + "; Alive: " + text + " | Dead: " + text2); } public void Update() { if (NetworkHelper.IsServer && !Setup && numPlayersSetup > 0) { playerSetupTimer += Time.deltaTime; if (playerSetupTimer >= 3f) { Setup = true; Log.Error("The " + TotalCount + " total players were not all setup, falling back. Consider filing an issue on Github."); } } } } public static class ReviveHelper { private static readonly Lazy<Action<CharacterMaster>> lazyRespawnDelegate = new Lazy<Action<CharacterMaster>>(CreateRespawnDelegate); public static Action<CharacterMaster> RespawnExtraLife => lazyRespawnDelegate.Value; public static void Init() { _ = lazyRespawnDelegate.Value; } private static Action<CharacterMaster> CreateRespawnDelegate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown MethodInfo method = typeof(CharacterMaster).GetMethod("RespawnExtraLife"); try { DynamicMethodDefinition val = new DynamicMethodDefinition((MethodBase)method); ILCursor val2 = new ILCursor(new ILContext(val.Definition)); if (val2.TryGotoNext(new Func<Instruction, bool>[1] { (Instruction x) => ILPatternMatchingExt.MatchCall(x, typeof(CharacterMaster), "get_deathFootPosition") })) { int index = val2.Index; val2.Index = 0; StringBuilder stringBuilder = new StringBuilder("CharacterMaster.RespawnExtraLife removed instructions:").AppendLine(); foreach (Instruction item in ((IEnumerable<Instruction>)val.Definition.Body.Instructions).Take(index - 1)) { stringBuilder.AppendLine(((object)item).ToString()); } Log.Debug(stringBuilder.ToString()); val2.RemoveRange(index - 1); Action<CharacterMaster> result = Extensions.CreateDelegate<Action<CharacterMaster>>((MethodBase)val.Generate()); Log.Info("Created patched CharacterMaster.RespawnExtraLife version successfully!"); return result; } Log.Warn("Cannot create patched version of RespawnExtraLife!"); } catch (Exception arg) { Log.Warn($"Error when trying to create patched RespawnExtraLife: {arg}"); } return FallbackRespawnFunction; } private static void FallbackRespawnFunction(CharacterMaster master) { master.RespawnExtraLife(); master.inventory.RemoveItem(Items.ExtraLifeConsumed, 1); } } } namespace TeammateRevive.Logging { public class ChatLogTarget : ILogTarget { public void Write(LogLevel level, object msg) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected I4, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if ((int)level == 32 || !RunTracker.instance.IsStarted) { return; } if (1 == 0) { } Color val; switch (level - 1) { default: if ((int)level != 16) { goto case 2; } val = Color.blue; break; case 3: val = Color.yellow; break; case 1: val = Color.red; break; case 0: val = Color.red; break; case 2: val = Color.blue; break; } if (1 == 0) { } Color val2 = val; } } public class ConsoleLoggerTarget : ILogTarget { private readonly ManualLogSource log; public ConsoleLoggerTarget(ManualLogSource log) { this.log = log; } public void Write(LogLevel level, object msg) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) log.Log(level, msg); } } public class DebugFileTarget : ILogTarget { private static readonly string DefaultPath = Environment.GetEnvironmentVariable("USERPROFILE") + "\\Desktop\\log.txt"; private readonly string filePath; public bool IsEnabled { get; set; } = true; public DebugFileTarget(string path, ManualLogSource consoleLogger) { if (string.IsNullOrEmpty(path)) { path = DefaultPath; } try { filePath = NormalizeAndVerifyPath(path); } catch { consoleLogger.LogError((object)("Couldn't write to specified file logging path! Will write to \"" + DefaultPath + "\" instead --------------------------------------------")); filePath = DefaultPath; } try { Write((LogLevel)32, "Log Setup"); } catch { consoleLogger.LogWarning((object)"Log file location unavailable!"); IsEnabled = false; } } private string NormalizeAndVerifyPath(string path) { if (File.Exists(path)) { return path; } if (Directory.Exists(path) || path.EndsWith("\\")) { return NormalizeAndVerifyPath(Path.Combine(path, "log.txt")); } File.WriteAllText(path, ""); return path; } public void Write(LogLevel level, object msg) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!IsEnabled) { return; } using StreamWriter streamWriter = new StreamWriter(filePath, append: true); streamWriter.WriteLine(string.Format("[{0}] [{1}] {2} \n", ((Enum)level).ToString("G").ToUpper(), DateTime.Now, msg)); } } public interface ILogTarget { void Write(LogLevel level, object msg); } public class Log { public static Log Instance; public List<ILogTarget> Targets = new List<ILogTarget>(); public static void Init(PluginConfig config, ManualLogSource log) { Log log2 = new Log(); log2.Targets = new ILogTarget[4] { CreateIf(config.ConsoleLogging, log, () => new ConsoleLoggerTarget(log)), CreateIf(config.FileLogging, log, () => new DebugFileTarget(config.FileLoggingPath, log)), CreateIf(config.ChatLogging, log, () => new ChatLogTarget()), new ServerLogTarget(config.ServerLogging) }.Where((ILogTarget t) => t != null).ToList(); Instance = log2; } private static ILogTarget CreateIf(bool condition, ManualLogSource consoleLog, Func<ILogTarget> create) { if (!condition) { return null; } try { return create(); } catch (Exception ex) { consoleLog.LogWarning((object)("Error on logging target creation: " + ex)); return null; } } protected Log() { } public static void Info(object msg) { Instance?.Write((LogLevel)16, msg); } public static void Warn(object msg) { Instance?.Write((LogLevel)4, msg); } public static void WarnMethod(object msg, [CallerMemberName] string callingMember = null, [CallerLineNumber] int lineNo = 0) { Instance?.Write((LogLevel)4, $"[{NameOfCallingClass()}.{callingMember}:{lineNo}] {msg}"); } public static void Error(object msg) { Instance?.Write((LogLevel)2, msg); } [Conditional("DEBUG")] public static void Debug(object msg) { Instance?.Write((LogLevel)32, msg); } public static void ChatDebug(object msg) { Instance.Targets.FirstOrDefault((ILogTarget t) => t.GetType() == typeof(ChatLogTarget))?.Write((LogLevel)32, msg); } [Conditional("DEBUG")] public static void DebugMethod(object message = null, [CallerMemberName] string callingMember = null, [CallerLineNumber] int lineNo = 0) { Instance?.Write((LogLevel)32, string.Format("[{0}.{1}:{2}] {3}", NameOfCallingClass(), callingMember, lineNo, message ?? "called")); } public static string NameOfCallingClass() { int num = 2; Type type; string fullName; do { MethodBase method = new StackFrame(num, needFileInfo: false).GetMethod(); type = method?.DeclaringType; if (type == null) { return method?.Name ?? "__"; } num++; fullName = type.FullName; } while (type.Module.Name.Equals("mscorlib.dll", StringComparison.OrdinalIgnoreCase)); return fullName; } public void Write(LogLevel level, object msg) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) foreach (ILogTarget target in Targets) { try { target.Write(level, msg); } catch { } } } } public class ServerLogTarget : ILogTarget, ILogListener, IDisposable { private readonly object locker = new object(); private readonly ServerLoggingConfig config; private Task shipTask = Task.CompletedTask; private readonly ConcurrentQueue<JSONObject> logsQueue = new ConcurrentQueue<JSONObject>(); private readonly HttpClient client = new HttpClient(); public ServerLogTarget(ServerLoggingConfig config) { this.config = config; Logger.Listeners.Add((ILogListener)(object)this); } public void Write(LogLevel level, object message) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) InternalWrite(level, message, fromHook: false); } private void InternalWrite(LogLevel level, object message, bool fromHook) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown if (config.IsEnabled && (!config.LogAll || fromHook)) { logsQueue.Enqueue(new JSONObject { ["timestamp"] = JSONNode.op_Implicit((float)DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()), ["level"] = JSONNode.op_Implicit(MapLevel(level)), ["msg"] = JSONNode.op_Implicit((NetworkHelper.IsClient() ? "C " : "S ") + message), ["user"] = JSONNode.op_Implicit((NetworkHelper.IsClient() ? "C " : "S ") + config.UserName), ["room"] = JSONNode.op_Implicit(config.RoomName) }); ShipLogs(); } } private string MapLevel(LogLevel level) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 if (1 == 0) { } string result = ((level - 1 <= 1) ? "ERROR" : (((int)level == 4) ? "WARN" : (((int)level != 32) ? "INFO" : "DEBUG"))); if (1 == 0) { } return result; } private void ShipLogs() { if (logsQueue.IsEmpty) { return; } lock (locker) { shipTask = shipTask.ContinueWith((Func<Task, Task>)async delegate { JSONArray array = GroupEntries(); if (config.IsEnabled) { await SendEntries(array); } }); } } private async Task SendEntries(JSONArray entry) { StringContent content = new StringContent(((object)entry).ToString(), Encoding.UTF8, "application/json"); try { await client.PostAsync(config.Url.TrimEnd('/') + "/logs/add", content); } catch (Exception) { } } private JSONArray GroupEntries() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown JSONArray val = new JSONArray(); int num = 50; JSONObject result; while (logsQueue.TryDequeue(out result)) { ((JSONNode)val).Add((JSONNode)(object)result); if (((JSONNode)val).Count == num) { break; } } return val; } public void Dispose() { } void ILogListener.LogEvent(object sender, LogEventArgs eventArgs) { //IL_000f: 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) if (config.LogAll || ((Enum)eventArgs.Level).HasFlag((Enum)(object)(LogLevel)2)) { InternalWrite(eventArgs.Level, eventArgs.Data.ToString(), fromHook: true); } } } } namespace TeammateRevive.Localization { public class LanguageConsts { public static string ARTIFACT_DEATH_CURSE_NAME = "ARTIFACT_DEATH_CURSE_NAME"; public static string ARTIFACT_DEATH_CURSE_DESCRIPTION = "ARTIFACT_DEATH_CURSE_DESCRIPTION"; public static string ARTIFACT_DEATH_CURSE_DISABLED = "ARTIFACT_DEATH_CURSE_DISABLED"; public static string ARTIFACT_DEATH_CURSE_ENFORCED_BY_SERVER = "ARTIFACT_DEATH_CURSE_ENFORCED_BY_SERVER"; public static string ITEM_CHARONS_OBOL_NAME = "ITEM_CHARONS_OBOL_NAME"; public static string ITEM_CHARONS_OBOL_DESCRIPTION = "ITEM_CHARONS_OBOL_DESCRIPTION"; public static string ITEM_CHARONS_OBOL_PICKUP = "ITEM_CHARONS_OBOL_PICKUP"; public static string ITEM_REVIVE_EVERYWHERE_NAME = "ITEM_REVIVE_EVERYWHERE_NAME"; public static string ITEM_REVIVE_EVERYWHERE_DESCRIPTION = "ITEM_REVIVE_EVERYWHERE_DESCRIPTION"; public static string ITEM_REVIVE_EVERYWHERE_LORE = "ITEM_REVIVE_EVERYWHERE_LORE"; public static string ITEM_REVIVE_EVERYWHERE_PICKUP = "ITEM_REVIVE_EVERYWHERE_PICKUP"; public static string ITEM_DEATH_CURSE_NAME = "ITEM_DEATH_CURSE_NAME"; public static string ITEM_DEATH_CURSE_DESCRIPTION = "ITEM_DEATH_CURSE_DESCRIPTION"; public static string ITEM_DEATH_CURSE_LORE = "ITEM_DEATH_CURSE_LORE"; public static string ITEM_DEATH_CURSE_PICKUP = "ITEM_DEATH_CURSE_PICKUP"; public static string ITEM_REVIVAL_TOKEN_NAME = "ITEM_REVIVAL_TOKEN_NAME"; public static string ITEM_REVIVAL_TOKEN_DESCRIPTION = "ITEM_REVIVAL_TOKEN_DESCRIPTION"; public static string BUFF_DEATH_CURSE_DESCRIPTION = "BUFF_DEATH_CURSE_DESCRIPTION"; public static string BUFF_REVIVE_LINK_NAME = "BUFF_REVIVE_LINK_NAME"; public static string BUFF_REVIVE_LINK_DESCRIPTION = "BUFF_REVIVE_LINK_DESCRIPTION"; public static string BUFF_REVIVE_REGEN_NAME = "BUFF_REVIVE_REGEN_NAME"; public static string BUFF_REVIVE_REGEN_DESCRIPTION = "BUFF_REVIVE_REGEN_DESCRIPTION"; public static string TEAMMATE_REVIVAL_UI_PLAYER = "TEAMMATE_REVIVAL_UI_PLAYER"; public static string TEAMMATE_REVIVAL_UI_USE_OBOL = "TEAMMATE_REVIVAL_UI_USE_OBOL"; public static string TEAMMATE_REVIVAL_UI_NO_OBOL = "TEAMMATE_REVIVAL_UI_NO_OBOL"; public static string TEAMMATE_REVIVAL_SHIELD_ONLY_POSTFIX = "TEAMMATE_REVIVAL_SHIELD_ONLY_POSTFIX"; public static string TEAMMATE_REVIVAL_UI_AVOIDED_CURSE = "TEAMMATE_REVIVAL_UI_AVOIDED_CURSE"; public static string TEAMMATE_REVIVAL_UI_CURSED = "TEAMMATE_REVIVAL_UI_CURSED"; public static string TEAMMATE_REVIVAL_UI_PROGRESS_BAR_REVIVING = "TEAMMATE_REVIVAL_UI_PROGRESS_BAR_REVIVING"; public static string ITEM_STAT_CHARON_OBOL_REVIVE_SPEED_INCREASE = "ITEM_STAT_CHARON_OBOL_REVIVE_SPEED_INCREASE"; public static string ITEM_STAT_CHARON_OBOL_REVIVE_TIME_ALONE = "ITEM_STAT_CHARON_OBOL_REVIVE_TIME_ALONE"; public static string ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_RANGE = "ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_RANGE"; public static string ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_DAMAGE = "ITEM_STAT_CHARON_OBOL_REVIVE_CIRCLE_DAMAGE"; public static string ITEM_STAT_REVIVE_EVERYWHERE_REVIVE_CIRCLE_DAMAGE = "ITEM_STAT_REVIVE_EVERYWHERE_REVIVE_CIRCLE_DAMAGE"; } public class LanguageManager { public static void RegisterLanguages() { Language.collectLanguageRootFolders += LanguageOnCollectLanguageRootFolders; } private static void LanguageOnCollectLanguageRootFolders(List<string> folders) { folders.Add(Path.Combine(Path.GetDirectoryName(((BaseUnityPlugin)MainTeammateRevival.instance).Info.Location), "Localization", "Languages")); } } } namespace TeammateRevive.Integrations { public class InLobbyConfigIntegration { private readonly PluginConfig pluginConfig; public InLobbyConfigIntegration(PluginConfig pluginConfig) { this.pluginConfig = pluginConfig; RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, (Action)delegate { if (Chainloader.PluginInfos.ContainsKey("com.KingEnderBrine.InLobbyConfig")) { Register(); } }); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private void Register() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown try { ModConfigEntry val = new ModConfigEntry { DisplayName = "TeammateRevival" }; AddSection(val, pluginConfig.RuleValuesBindCollection); AddSection(val, pluginConfig.DebugBindCollection); ModConfigCatalog.Add(val); Log.Info("InLobbyConfig integration: OK!"); } catch (Exception ex) { Log.Error("InLobbyConfig integration failed: " + ex); } } private static void AddSection(ModConfigEntry configEntry, BindCollection bindCollection) { configEntry.SectionFields.Add(bindCollection.Section, GetFieldsFromBindings(bindCollection)); } private static IConfigField[] GetFieldsFromBindings(BindCollection bindCollection) { Func<ConfigEntryBase, IConfigField> createField = (Func<ConfigEntryBase, IConfigField>)typeof(ConfigFieldUtilities).GetMethod("ProcessConfigRow", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).CreateDelegate(typeof(Func<ConfigEntryBase, IConfigField>)); return bindCollection.Bindings.Select(delegate(ConfigEntryBase f) { AcceptableValueBase acceptableValues = f.Description.AcceptableValues; AcceptableValueList<string> list = acceptableValues as AcceptableValueList<string>; return (IConfigField)((list != null) ? ((object)new SelectListField<string>(f.Definition.Key, f.Description.Description, (Func<ICollection<string>>)(() => new string[1] { (string)f.BoxedValue }), (Action<string, int>)delegate(string s, int i) { f.BoxedValue = s; }, (Action<int>)delegate { }, (Func<IDictionary<string, string>>)(() => list.AcceptableValues.ToDictionary((string k) => k)))) : ((object)createField(f))); }).ToArray(); } } public class RiskOfOptionsIntegration { public const string Guid = "com.rune580.riskofoptions"; private readonly PluginConfig config; public RiskOfOptionsIntegration(PluginConfig config) { this.config = config; RoR2Application.onLoad = (Action)Delegate.Combine(RoR2Application.onLoad, (Action)delegate { if (Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions")) { try { RegisterOptions(); } catch (Exception arg) { Log.Error($"Error during RiskOfOptions integration: {arg}"); } } }); } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] private void RegisterOptions() { int num = RegisterBindCollection(config.RuleValuesBindCollection); num += RegisterBindCollection(config.MiscBindCollection); num += RegisterBindCollection(config.DebugBindCollection); Log.Info($"RiskOfOptions integration: OK! (Options: {num})"); } private int RegisterBindCollection(BindCollection bindCollection) { BaseOption[] array = (from o in bindCollection.Bindi