Decompiled source of TeammateRevival v4.2.0
plugins/KosmosisDire-TeammateRevival/TeammateRevive.dll
Decompiled 2 months 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+fe025af625e539c28b5dab1bc7365a9365ef000c")] [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.1.3")] 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.1.3"; 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