Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of VikingRevival v1.1.3
BepInEx/plugins/VikingRevival/VikingRevival.Core.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("VikingRevival.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0+ea662e905434b42c1797bdd20292302b9389ba81")] [assembly: AssemblyProduct("VikingRevival.Core")] [assembly: AssemblyTitle("VikingRevival.Core")] [assembly: AssemblyVersion("1.1.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 VikingRevival.Core { public static class AuthorityRange { public static bool Allows(bool sourceDead, bool targetDead, double distance, double range, bool deadTarget = false, bool deadSource = false) { if ((deadSource || !sourceDead) && (deadTarget || !targetDead) && !double.IsNaN(distance) && !double.IsInfinity(distance) && distance >= 0.0) { return distance <= range; } return false; } } public enum LocalAction { None, Die, RestoreRevive, ClearPending, AcknowledgeDeath } public static class LocalLifecycle { public static LocalAction Reconcile(PlayerState? state, string pending, string completed) { if (state != null && !string.IsNullOrEmpty(completed) && completed == state.Event) { if (state.Life != Life.Alive || !state.DeathAcknowledged) { return LocalAction.AcknowledgeDeath; } return LocalAction.ClearPending; } if (state != null && state.Life == Life.Dying) { return LocalAction.Die; } if (string.IsNullOrEmpty(pending)) { return LocalAction.None; } if (state == null || pending != state.Event) { return LocalAction.Die; } if (state.Life == Life.Downed) { return LocalAction.None; } if (state.DeathAcknowledged) { return LocalAction.ClearPending; } if (!(state.Health > 0.0)) { return LocalAction.Die; } return LocalAction.RestoreRevive; } } public enum DeathDecision { Wait, Revive, Respawn } public static class NativeDeathLifecycle { public static DeathDecision Decide(PlayerState? state, string evt, bool fresh, bool requestedRespawn, double now, double confirmationUntil) { if (requestedRespawn) { return DeathDecision.Respawn; } if (!fresh || state == null || state.Event != evt) { if (!(now < confirmationUntil)) { return DeathDecision.Respawn; } return DeathDecision.Wait; } if (state.Life == Life.Downed && RevivalEngine.Remaining(state, now) > 0.0) { return DeathDecision.Wait; } if (state.Life == Life.Alive && !state.DeathAcknowledged && state.Health > 0.0) { return DeathDecision.Revive; } return DeathDecision.Respawn; } } public sealed class PlayerState { public long Id; public Life Life; public string Event = ""; public List<string> SeenEvents = new List<string>(); public int Knockdowns; public double Deadline; public double RecoveryUntil; public double ProtectedUntil; public double LastPenalty = double.MinValue; public string Attempt = ""; public double Health; public bool DeathAcknowledged; public bool TimerPaused; public double PausedRemaining; public double PauseUntil; } public sealed class Attempt { public string Id = ""; public string Event = ""; public long Rescuer; public long Target; public Tier Tier; public TransactionPhase Phase; public double Started; public double LastHold; public double PrepareUntil; } public sealed class RevivalEngine { public readonly Rules Rules; public readonly Dictionary<long, PlayerState> Players = new Dictionary<long, PlayerState>(); public readonly Dictionary<string, Attempt> Attempts = new Dictionary<string, Attempt>(); public RevivalEngine(Rules rules) { rules.Validate(); Rules = rules; } public PlayerState Get(long id) { if (!Players.TryGetValue(id, out PlayerState value)) { Dictionary<long, PlayerState> players = Players; PlayerState obj = new PlayerState { Id = id }; value = obj; players.Add(id, obj); } return value; } public bool KnockDown(long id, string request, Eligibility eligibility, double now) { if (string.IsNullOrEmpty(request) || request.Length > 64) { return false; } PlayerState playerState = Get(id); if (playerState.Life != Life.Alive || playerState.ProtectedUntil > now || playerState.SeenEvents.Contains(request) || playerState.SeenEvents.Count >= 10000 || !Eligibility.Allows(Rules, eligibility)) { return false; } if (playerState.RecoveryUntil <= now) { playerState.Knockdowns = 0; } playerState.Knockdowns++; playerState.SeenEvents.Add(request); Interrupt(id); playerState.Health = 0.0; playerState.TimerPaused = false; playerState.PausedRemaining = (playerState.PauseUntil = 0.0); playerState.Life = Life.Downed; playerState.Event = request; playerState.Attempt = ""; playerState.DeathAcknowledged = false; playerState.Deadline = now + ((playerState.Knockdowns == 1) ? Rules.BleedoutSeconds : ((playerState.Knockdowns == 2) ? Rules.SecondKnockdownSeconds : Rules.ThirdKnockdownSeconds)); playerState.RecoveryUntil = now + Rules.RecoveryWindowSeconds; playerState.LastPenalty = double.MinValue; return true; } public bool Hit(long id, string evt, double now, bool qualifying) { PlayerState playerState = Get(id); if (playerState.Life != Life.Downed || playerState.Event != evt || !qualifying || now - playerState.LastPenalty < Rules.EnemyHitPenaltyCooldown) { return false; } playerState.LastPenalty = now; if (playerState.TimerPaused) { playerState.PausedRemaining = Math.Max(0.0, playerState.PausedRemaining - Rules.EnemyHitBleedoutPenalty); } else { playerState.Deadline = Math.Max(now, playerState.Deadline - Rules.EnemyHitBleedoutPenalty); } if (Remaining(playerState, now) <= 0.0) { Die(playerState); } return true; } public bool Begin(long rescuer, long target, string evt, string id, Tier tier, double now, bool inRange, bool inventoryAvailable) { if (!Enum.IsDefined(typeof(Tier), tier) || string.IsNullOrEmpty(id) || id.Length > 64 || Attempts.ContainsKey(id) || rescuer == target) { return false; } PlayerState playerState = Get(target); if (playerState.Life != Life.Downed || playerState.Event != evt || playerState.Deadline <= now || playerState.Attempt != "" || Get(rescuer).Life != Life.Alive || !inRange || (tier != Tier.None && !inventoryAvailable)) { return false; } if (Attempts.Values.Any((Attempt a) => a.Rescuer == rescuer && Active(a))) { return false; } Attempts.Add(id, new Attempt { Id = id, Event = evt, Target = target, Rescuer = rescuer, Tier = tier, Started = now, LastHold = now }); playerState.Attempt = id; playerState.PausedRemaining = playerState.Deadline - now; playerState.PauseUntil = now + 1.25; playerState.TimerPaused = true; return true; } public bool Hold(long rescuer, string id, double now, bool valid) { if (!Attempts.TryGetValue(id, out Attempt value) || value.Rescuer != rescuer || !Active(value)) { return false; } if (!valid || now - value.LastHold > 1.25) { Abort(value, now); return false; } value.LastHold = now; Get(value.Target).PauseUntil = now + 1.25; return true; } public void Interrupt(long id, double now = double.NaN) { Attempt[] array = Attempts.Values.Where((Attempt attempt) => Active(attempt) && (attempt.Rescuer == id || attempt.Target == id)).ToArray(); foreach (Attempt a in array) { Abort(a, now); } } public bool Cancel(long rescuer, string id, double now = double.NaN) { if (!Attempts.TryGetValue(id, out Attempt value) || value.Rescuer != rescuer || !Active(value)) { return false; } Abort(value, now); return true; } public void Tick(double now, Func<Attempt, bool> valid) { Attempt[] array = Attempts.Values.Where(Active).ToArray(); foreach (Attempt attempt in array) { if (now - attempt.LastHold > 1.25 || !valid(attempt) || Get(attempt.Target).Life != Life.Downed || Get(attempt.Target).Event != attempt.Event) { Abort(attempt, now); } else if (attempt.Phase == TransactionPhase.Preparing && now >= attempt.PrepareUntil) { Abort(attempt, now); } else if (attempt.Phase == TransactionPhase.Channeling && now - attempt.Started >= Rules.Seconds(attempt.Tier)) { attempt.Phase = TransactionPhase.Preparing; attempt.PrepareUntil = now + 3.0; } } PlayerState[] array2 = Players.Values.ToArray(); foreach (PlayerState playerState in array2) { if (playerState.Life == Life.Downed && Remaining(playerState, now) <= 0.0) { Die(playerState); } } } public bool Commit(long rescuer, string id, double now, bool receipt, bool valid) { if (!Attempts.TryGetValue(id, out Attempt value) || value.Rescuer != rescuer) { return false; } if (value.Phase == TransactionPhase.Committed) { return true; } PlayerState playerState = Get(value.Target); if (value.Phase != TransactionPhase.Preparing || !receipt || !valid || now >= value.PrepareUntil || now - value.LastHold > 1.25 || Remaining(playerState, now) <= 0.0 || playerState.Life != Life.Downed || playerState.Event != value.Event) { if (Active(value)) { Abort(value, now); } return false; } value.Phase = TransactionPhase.Committed; playerState.Life = Life.Alive; playerState.Attempt = ""; playerState.TimerPaused = false; playerState.Health = Rules.Health(value.Tier); playerState.ProtectedUntil = now + Rules.PostReviveProtectionSeconds; playerState.RecoveryUntil = now + Rules.RecoveryWindowSeconds; return true; } public void Disconnect(long id, double now = double.NaN) { Interrupt(id, now); } public void ForceDeath(long id) { PlayerState playerState = Get(id); if (playerState.Life == Life.Downed) { Die(playerState); } } public bool GiveUp(long id, string evt) { PlayerState playerState = Get(id); if (playerState.Life != Life.Downed || playerState.Event != evt || string.IsNullOrEmpty(evt)) { return false; } Die(playerState); return true; } public void AcknowledgeDeath(long id, string evt) { PlayerState playerState = Get(id); if ((playerState.Life == Life.Dying || playerState.Life == Life.Downed) && !(playerState.Event != evt) && !string.IsNullOrEmpty(evt)) { Interrupt(id); playerState.DeathAcknowledged = true; playerState.Life = Life.Alive; playerState.Knockdowns = 0; playerState.RecoveryUntil = 0.0; playerState.ProtectedUntil = 0.0; playerState.Health = 0.0; playerState.Deadline = 0.0; playerState.TimerPaused = false; playerState.PausedRemaining = (playerState.PauseUntil = 0.0); } } public static double RestoredHealth(double requested, double max) { return Math.Min(Math.Max(1.0, max), Math.Max(1.0, requested)); } public static bool Active(Attempt a) { if (a.Phase != TransactionPhase.Channeling) { return a.Phase == TransactionPhase.Preparing; } return true; } public static double Remaining(PlayerState p, double now) { return Math.Max(0.0, p.TimerPaused ? (p.PausedRemaining - Math.Max(0.0, now - p.PauseUntil)) : (p.Deadline - now)); } private void Abort(Attempt a, double now = double.NaN) { a.Phase = TransactionPhase.Aborted; PlayerState playerState = Get(a.Target); if (!(playerState.Attempt != a.Id)) { if (playerState.TimerPaused) { playerState.Deadline = (double.IsNaN(now) ? a.LastHold : Math.Min(now, playerState.PauseUntil)) + playerState.PausedRemaining; } playerState.TimerPaused = false; playerState.Attempt = ""; } } private void Die(PlayerState p) { Interrupt(p.Id); p.Life = Life.Dying; p.Attempt = ""; p.TimerPaused = false; } } public enum Life { Alive, Downed, Dying } public enum Tier { Minor, Medium, Major, None } public enum TransactionPhase { Channeling, Preparing, Committed, Aborted } public enum Cause { Enemy, Environment, Pvp, Drowning, Scripted } public sealed class Rules { public bool EnableRevival = true; public bool AllowSoloRevival; public bool AllowPvPRevival; public double BleedoutSeconds = 60.0; public double SecondKnockdownSeconds = 60.0; public double ThirdKnockdownSeconds = 60.0; public double RecoveryWindowSeconds = 300.0; public double EnemyHitBleedoutPenalty = 5.0; public double EnemyHitPenaltyCooldown = 1.0; public double ReviveRange = 2.5; public double TeammateEligibilityRange = 150.0; public double CrawlForwardSpeedMultiplier = 0.2; public double CrawlBackwardSpeedMultiplier = 0.12; public double CrawlLateralSpeedMultiplier = 0.15; public double MinorReviveSeconds = 8.0; public double MediumReviveSeconds = 5.0; public double MajorReviveSeconds = 3.0; public double MinorReviveHealth = 25.0; public double MediumReviveHealth = 50.0; public double MajorReviveHealth = 100.0; public double PostReviveProtectionSeconds = 2.0; public void Validate() { Check(BleedoutSeconds, 1.0, 3600.0); Check(SecondKnockdownSeconds, 1.0, 3600.0); Check(ThirdKnockdownSeconds, 1.0, 3600.0); Check(RecoveryWindowSeconds, 1.0, 86400.0); Check(EnemyHitBleedoutPenalty, 0.0, 300.0); Check(EnemyHitPenaltyCooldown, 0.1, 60.0); Check(ReviveRange, 0.5, 10.0); Check(CrawlForwardSpeedMultiplier, 0.01, 0.3); Check(CrawlBackwardSpeedMultiplier, 0.01, 0.3); Check(CrawlLateralSpeedMultiplier, 0.01, 0.3); Check(MinorReviveSeconds, 0.5, 120.0); Check(MediumReviveSeconds, 0.5, 120.0); Check(MajorReviveSeconds, 0.5, 120.0); Check(MinorReviveHealth, 1.0, 1000.0); Check(MediumReviveHealth, 1.0, 1000.0); Check(MajorReviveHealth, 1.0, 1000.0); Check(PostReviveProtectionSeconds, 0.0, 30.0); } private static void Check(double x, double lo, double hi) { if (double.IsNaN(x) || double.IsInfinity(x) || x < lo || x > hi) { throw new ArgumentOutOfRangeException("x", $"Value {x} outside {lo}..{hi}"); } } public double Seconds(Tier tier) { return tier switch { Tier.Medium => MediumReviveSeconds, Tier.Minor => MinorReviveSeconds, Tier.None => MinorReviveSeconds * 2.0, _ => MajorReviveSeconds, }; } public double Health(Tier tier) { return tier switch { Tier.Medium => MediumReviveHealth, Tier.Minor => MinorReviveHealth, Tier.None => MinorReviveHealth, _ => MajorReviveHealth, }; } public static string Prefab(Tier tier) { return tier switch { Tier.Medium => "MeadHealthMedium", Tier.Minor => "MeadHealthMinor", Tier.None => "", _ => "MeadHealthMajor", }; } } public sealed class Eligibility { public bool Connected; public bool Dead; public bool Downed; public bool DeepWater; public bool HasNearbyTeammate; public Cause Cause; public static bool Allows(Rules r, Eligibility e) { if (r.EnableRevival && e.Connected && !e.Dead && !e.Downed && !e.DeepWater && e.Cause != Cause.Drowning && e.Cause != Cause.Scripted) { if (e.Cause == Cause.Pvp) { return r.AllowPvPRevival; } return true; } return false; } } }
BepInEx/plugins/VikingRevival/VikingRevival.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using VikingRevival.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("VikingRevival")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.2.0")] [assembly: AssemblyInformationalVersion("1.1.2+ea662e905434b42c1797bdd20292302b9389ba81")] [assembly: AssemblyProduct("VikingRevival")] [assembly: AssemblyTitle("VikingRevival")] [assembly: AssemblyVersion("1.1.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 VikingRevival { [BepInPlugin("com.vikingrevival", "Viking Revival", "1.1.2")] [BepInDependency("com.jotunn.jotunn", "2.30.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { private sealed class NativeInterface : IDisposable { private readonly Plugin owner; private GameObject root; private GameObject welcome; private GameObject shade; private GameObject menu; private GameObject downed; private GameObject rescue; private GameObject death; private Text timer; private Text instructions; private Text rescueText; private Text progress; private Text feedbackLabel; private Button giveUp; private Button respawn; private Button startup; private string scene; private readonly List<Text> markers = new List<Text>(); private bool visited; private bool open; private bool blocked; private static readonly Vector2 TL = new Vector2(0f, 1f); private static readonly Vector2 C = new Vector2(0.5f, 0.5f); private static readonly Color Ink = new Color(0.94f, 0.88f, 0.73f); internal bool DeathPanelVisible { get { if (Object.op_Implicit((Object)(object)root) && root.activeInHierarchy) { if (!Object.op_Implicit((Object)(object)downed) || !downed.activeInHierarchy) { if (Object.op_Implicit((Object)(object)death)) { return death.activeInHierarchy; } return false; } return true; } return false; } } internal NativeInterface(Plugin plugin) { owner = plugin; } internal void Tick() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0411: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Invalid comparison between Unknown and I4 //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0461: Unknown result type (might be due to invalid IL or missing references) //IL_050e: Unknown result type (might be due to invalid IL or missing references) //IL_0593: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_06c8: Unknown result type (might be due to invalid IL or missing references) //IL_06d4: Unknown result type (might be due to invalid IL or missing references) //IL_06ff: Unknown result type (might be due to invalid IL or missing references) //IL_0704: Unknown result type (might be due to invalid IL or missing references) //IL_070e: Unknown result type (might be due to invalid IL or missing references) //IL_0713: Unknown result type (might be due to invalid IL or missing references) //IL_0718: Unknown result type (might be due to invalid IL or missing references) //IL_071d: Unknown result type (might be due to invalid IL or missing references) //IL_071f: Unknown result type (might be due to invalid IL or missing references) //IL_0730: Unknown result type (might be due to invalid IL or missing references) //IL_0741: Unknown result type (might be due to invalid IL or missing references) //IL_0753: Unknown result type (might be due to invalid IL or missing references) //IL_0764: Unknown result type (might be due to invalid IL or missing references) //IL_079f: Unknown result type (might be due to invalid IL or missing references) //IL_07a4: Unknown result type (might be due to invalid IL or missing references) //IL_07a9: Unknown result type (might be due to invalid IL or missing references) //IL_07ad: Unknown result type (might be due to invalid IL or missing references) //IL_07b2: Unknown result type (might be due to invalid IL or missing references) //IL_07b6: Unknown result type (might be due to invalid IL or missing references) //IL_07ca: Unknown result type (might be due to invalid IL or missing references) //IL_07d2: Unknown result type (might be due to invalid IL or missing references) //IL_07e6: Unknown result type (might be due to invalid IL or missing references) //IL_07f9: Unknown result type (might be due to invalid IL or missing references) //IL_07fe: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)GUIManager.Instance.AveriaSerif)) { ReleaseInput(); return; } Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (Object.op_Implicit((Object)(object)root) && scene != name) { Dispose(); root = null; } if (!Object.op_Implicit((Object)(object)root)) { Create(); scene = name; } bool flag = Object.op_Implicit((Object)(object)FejdStartup.instance) && !Object.op_Implicit((Object)(object)Player.m_localPlayer); if (flag && !visited) { visited = true; open = owner.showWelcome.Value; } if (!flag) { open = false; visited = false; } welcome.SetActive(flag && open); shade.SetActive(flag && open); menu.SetActive(flag && !open); if (flag && open && Input.GetKeyDown((KeyCode)27)) { Close(); } Player player = Player.m_localPlayer; PlayerState val = (Object.op_Implicit((Object)(object)player) ? owner.State(Id(player)) : null); bool waitingForRevival = owner.WaitingForRevival; bool canRespawnNow = owner.CanRespawnNow; downed.SetActive(waitingForRevival); death.SetActive(canRespawnNow && !waitingForRevival); bool flag2 = !flag && (canRespawnNow || (waitingForRevival && (Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307)))); if (flag2 != blocked) { GUIManager.BlockInput(flag2); blocked = flag2; } if (canRespawnNow && !Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && Input.GetKeyDown((KeyCode)13)) { owner.RespawnNow(); } Scale(welcome, 760f, 620f, 1f); Scale(downed, 560f, 260f, owner.hudScale.Value); Scale(death, 520f, 210f, owner.hudScale.Value); Scale(rescue, 520f, 210f, owner.hudScale.Value); if (waitingForRevival) { timer.text = ((val?.Event != owner.nativeDeathEvent) ? "Confirming revival availability…" : ("Revival window " + Math.Ceiling(Math.Max(0.0, RevivalEngine.Remaining(val, owner.Now))) + " s" + ((val.TimerPaused && owner.Now <= val.PauseUntil) ? " (paused)" : ""))); instructions.text = "Your ragdoll and tombstone are on the ground.\nA friend can loot with E and hold " + ((object)owner.reviveKey.Value/*cast due to .constrained prefix*/).ToString() + " to revive.\nChoose Respawn now, or wait here for help."; ((Selectable)giveUp).interactable = owner.CanRespawnNow; progress.text = Attempt(val); } Player val2 = ((Object.op_Implicit((Object)(object)player) && !waitingForRevival && !((Character)player).IsDead()) ? owner.Nearby(player) : null); rescue.SetActive(Object.op_Implicit((Object)(object)val2)); if (Object.op_Implicit((Object)(object)val2)) { PlayerState val3 = owner.State(Id(val2)); bool flag3 = (int)owner.selected == 3 || owner.FindMead(player, owner.selected) != null || player.m_customData.ContainsKey("VikingRevival.escrow"); string newValue = ((object)owner.reviveKey.Value/*cast due to .constrained prefix*/).ToString(); rescueText.text = val2.GetPlayerName() + " · " + Math.Ceiling(Math.Max(0.0, RevivalEngine.Remaining(val3, owner.Now))) + " s\n" + owner.Text(((object)Unsafe.As<Tier, Tier>(ref owner.selected)/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()) + " · " + owner.Rules.Seconds(owner.selected) + " s\n" + (flag3 ? owner.Text("hold").Replace("Use", newValue) : owner.Text("required")) + "\n" + owner.Text("cycle") + ": " + ((object)owner.cycle.Value/*cast due to .constrained prefix*/).ToString() + "\n" + Attempt(val3); } foreach (Text marker in markers) { if (Object.op_Implicit((Object)(object)marker)) { ((Component)marker).gameObject.SetActive(false); } } if (Object.op_Implicit((Object)(object)player) && owner.indicators.Value && Object.op_Implicit((Object)(object)Camera.main)) { int num = 0; Vector2 anchoredPosition = default(Vector2); foreach (Player item in from x in Player.GetAllPlayers().Where(delegate(Player x) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 if (Object.op_Implicit((Object)(object)x) && (Object)(object)x != (Object)(object)player) { PlayerState obj2 = owner.State(Id(x)); if (obj2 == null) { return false; } return (int)obj2.Life == 1; } return false; }) orderby Vector3.Distance(((Component)player).transform.position, ((Component)x).transform.position) select x) { if (num < markers.Count && !(Vector3.Distance(((Component)player).transform.position, ((Component)item).transform.position) > owner.indicatorRange.Value)) { Vector3 val4 = Camera.main.WorldToScreenPoint(((Component)item).transform.position + Vector3.up * 1.2f); if (!(val4.z <= 0f) && !(val4.x < 0f) && !(val4.x > (float)Screen.width) && !(val4.y < 0f) && !(val4.y > (float)Screen.height)) { Text obj = markers[num++]; ((Component)obj).gameObject.SetActive(true); Rect rect = ((RectTransform)root.transform).rect; Vector2 size = ((Rect)(ref rect)).size; ((Vector2)(ref anchoredPosition))..ctor((val4.x / (float)Screen.width - 0.5f) * size.x, (val4.y / (float)Screen.height - 0.5f) * size.y); ((RectTransform)((Component)obj).transform).anchoredPosition = anchoredPosition; obj.text = item.GetPlayerName() + " · " + Math.Ceiling(Math.Max(0.0, RevivalEngine.Remaining(owner.State(Id(item)), owner.Now))) + " s"; } } } } feedbackLabel.text = ((Utc < owner.feedbackUntil) ? owner.feedback : ((val != null && !waitingForRevival && !canRespawnNow && val.RecoveryUntil > owner.Now) ? (owner.Text("recovery") + " " + Math.Ceiling(val.RecoveryUntil - owner.Now) + " s (" + val.Knockdowns + ")") : "")); } private string Attempt(PlayerState state) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) Attempt val = ((IEnumerable<Attempt>)owner.View?.Attempts).FirstOrDefault((Func<Attempt, bool>)((Attempt x) => x.Id == state?.Attempt && RevivalEngine.Active(x))); if (val != null) { return owner.Text("reviving") + " " + Math.Round(Mathf.Clamp01((float)((owner.Now - val.Started) / owner.Rules.Seconds(val.Tier))) * 100f) + "%"; } return ""; } private void Create() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Expected O, but got Unknown //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Expected O, but got Unknown //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Expected O, but got Unknown //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Expected O, but got Unknown //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Expected O, but got Unknown //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Expected O, but got Unknown //IL_041d: Unknown result type (might be due to invalid IL or missing references) //IL_0512: Unknown result type (might be due to invalid IL or missing references) //IL_051c: Expected O, but got Unknown //IL_0548: Unknown result type (might be due to invalid IL or missing references) //IL_0552: Expected O, but got Unknown //IL_0564: Unknown result type (might be due to invalid IL or missing references) //IL_05e3: Unknown result type (might be due to invalid IL or missing references) //IL_0679: Unknown result type (might be due to invalid IL or missing references) //IL_0683: Expected O, but got Unknown //IL_06bd: Unknown result type (might be due to invalid IL or missing references) //IL_06c2: Unknown result type (might be due to invalid IL or missing references) //IL_06c3: Unknown result type (might be due to invalid IL or missing references) //IL_06c4: Unknown result type (might be due to invalid IL or missing references) //IL_06c9: Unknown result type (might be due to invalid IL or missing references) //IL_06ca: Unknown result type (might be due to invalid IL or missing references) //IL_06d1: Unknown result type (might be due to invalid IL or missing references) //IL_06d8: Unknown result type (might be due to invalid IL or missing references) //IL_0732: Unknown result type (might be due to invalid IL or missing references) //IL_0737: Unknown result type (might be due to invalid IL or missing references) //IL_0738: Unknown result type (might be due to invalid IL or missing references) //IL_074a: Unknown result type (might be due to invalid IL or missing references) //IL_0751: Unknown result type (might be due to invalid IL or missing references) //IL_0758: Unknown result type (might be due to invalid IL or missing references) markers.Clear(); root = new GameObject("VikingRevival Native UI", new Type[4] { typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler), typeof(GraphicRaycaster) }); Canvas component = root.GetComponent<Canvas>(); component.renderMode = (RenderMode)0; component.sortingOrder = 31000; CanvasScaler component2 = root.GetComponent<CanvasScaler>(); component2.uiScaleMode = (ScaleMode)1; component2.referenceResolution = new Vector2(1920f, 1080f); component2.matchWidthOrHeight = 0.5f; shade = new GameObject("Welcome backdrop", new Type[2] { typeof(RectTransform), typeof(Image) }); shade.transform.SetParent(root.transform, false); RectTransform val = (RectTransform)shade.transform; val.anchorMin = Vector2.zero; val.anchorMax = Vector2.one; Vector2 val2 = (val.offsetMin = (val.offsetMax = Vector2.zero)); ((Graphic)shade.GetComponent<Image>()).color = new Color(0f, 0f, 0f, 0.7f); welcome = Panel("Welcome", C, 760f, 620f); Label(welcome, "Viking Revival", 32f, -24f, 696f, 48f, 32); Label(welcome, "How revival works", 32f, -78f, 696f, 32f, 24); Text val4 = Label(welcome, "A fatal eligible hit causes normal Valheim death: your ragdoll falls and your inventory goes into your tombstone. Death penalties apply once.\n\nFriends loot the tombstone normally with E. Hold R near the fallen player to revive. T cycles available meads and No potion.\n\nMinor / Medium / Major: 8 / 5 / 3 seconds by default. No potion: 16 seconds, restoring minor-mead health. Leaving range, releasing R or rescuer damage interrupts revival.\n\nThe window is 60 seconds by default, regardless of teammate distance. Holding R pauses it; interruption resumes it. Choose Respawn now at any time, or wait for help. Reviving leaves items in the tombstone unless someone already looted them.\n\nThe server and every player must run the same mod version. PvP revival is disabled by default.", 32f, -128f, 696f, 365f, 20); GameObject val5 = new GameObject("Instructions scroll area", new Type[4] { typeof(RectTransform), typeof(Image), typeof(RectMask2D), typeof(ScrollRect) }); val5.transform.SetParent(welcome.transform, false); RectTransform val6 = (RectTransform)val5.transform; val2 = (val6.anchorMin = (val6.anchorMax = TL)); val6.pivot = TL; val6.anchoredPosition = new Vector2(32f, -128f); val6.sizeDelta = new Vector2(696f, 365f); ((Graphic)val5.GetComponent<Image>()).color = Color.clear; RectTransform val8 = (RectTransform)((Component)val4).transform; ((Transform)val8).SetParent(val5.transform, false); val2 = (val8.anchorMin = (val8.anchorMax = TL)); val8.pivot = TL; val8.anchoredPosition = Vector2.zero; val8.sizeDelta = new Vector2(680f, Mathf.Max(365f, val4.preferredHeight + 16f)); ScrollRect component3 = val5.GetComponent<ScrollRect>(); component3.viewport = val6; component3.content = val8; component3.horizontal = false; component3.movementType = (MovementType)2; component3.scrollSensitivity = 60f; startup = Button(welcome, "Show on startup: " + (owner.showWelcome.Value ? "Yes" : "No"), 32f, -514f, 300f, 38f, (UnityAction)delegate { owner.showWelcome.Value = !owner.showWelcome.Value; ((BaseUnityPlugin)owner).Config.Save(); ((Component)startup).GetComponentInChildren<Text>().text = "Show on startup: " + (owner.showWelcome.Value ? "Yes" : "No"); }); Button(welcome, "Continue", 544f, -565f, 184f, 42f, new UnityAction(Close)); menu = ((Component)Button(root, "Viking Revival", 24f, -24f, 200f, 42f, (UnityAction)delegate { open = true; })).gameObject; downed = Panel("Downed", new Vector2(0.5f, 0.25f), 560f, 260f); Label(downed, "You have fallen", 26f, -18f, 508f, 36f, 28); timer = Label(downed, "", 26f, -60f, 508f, 34f, 23); instructions = Label(downed, "", 26f, -100f, 508f, 83f, 18); progress = Label(downed, "", 26f, -181f, 508f, 24f, 18); giveUp = Button(downed, "Respawn now", 40f, -212f, 220f, 38f, new UnityAction(owner.GiveUp)); Button(downed, "Wait for rescue", 300f, -212f, 220f, 38f, (UnityAction)delegate { owner.Notify("Waiting for a nearby friend to hold R. The timer pauses during an active revive and resumes if interrupted."); }); rescue = Panel("Revive", new Vector2(0.5f, 0.48f), 520f, 210f); Label(rescue, "Revive your companion", 24f, -15f, 472f, 34f, 25); rescueText = Label(rescue, "", 24f, -58f, 472f, 143f, 19); death = Panel("Respawn", new Vector2(0.5f, 0.25f), 520f, 210f); Label(death, "You died", 24f, -18f, 472f, 40f, 30); Label(death, "Normal death penalties apply.\nRespawn now, or wait for Valheim's normal respawn.", 24f, -70f, 472f, 65f, 18); respawn = Button(death, "Respawn now", 160f, -153f, 200f, 40f, new UnityAction(owner.RespawnNow)); for (int num = 0; num < 5; num++) { Text val10 = Label(root, "", 0f, 0f, 270f, 40f, 18); RectTransform val11 = (RectTransform)((Component)val10).transform; val2 = (val11.anchorMin = (val11.anchorMax = C)); val11.pivot = C; markers.Add(val10); } feedbackLabel = Label(root, "", 0f, 0f, 700f, 50f, 20); RectTransform val13 = (RectTransform)((Component)feedbackLabel).transform; ((Vector2)(ref val2))..ctor(0.5f, 0.7f); val13.anchorMax = val2; val13.anchorMin = val2; val13.pivot = C; } private GameObject Panel(string name, Vector2 anchor, float width, float height) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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) GameObject obj = GUIManager.Instance.CreateWoodpanel(root.transform, anchor, anchor, Vector2.zero, width, height, false); ((Object)obj).name = name; ((RectTransform)obj.transform).pivot = C; return obj; } private Text Label(GameObject parent, string value, float x, float y, float width, float height, int size) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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) GameObject obj = GUIManager.Instance.CreateText(value, parent.transform, TL, TL, new Vector2(x, y), GUIManager.Instance.AveriaSerif, size, Ink, false, Color.black, width, height, false); ((RectTransform)obj.transform).pivot = TL; Text component = obj.GetComponent<Text>(); component.alignment = (TextAnchor)0; component.supportRichText = false; ((Graphic)component).raycastTarget = false; return component; } private Button Button(GameObject parent, string value, float x, float y, float width, float height, UnityAction action) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) GameObject obj = GUIManager.Instance.CreateButton(value, parent.transform, TL, TL, new Vector2(x, y), width, height); ((RectTransform)obj.transform).pivot = TL; Button component = obj.GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener(action); return component; } private void Scale(GameObject panel, float width, float height, float requested) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) Rect rect = ((RectTransform)root.transform).rect; Vector2 size = ((Rect)(ref rect)).size; if (!(size.x <= 32f) && !(size.y <= 32f)) { panel.transform.localScale = Vector3.one * Mathf.Clamp(Mathf.Min(requested, Mathf.Min((size.x - 32f) / width, (size.y - 32f) / height)), 0.25f, 2f); } } private void Close() { open = false; } internal void ReleaseInput() { if (blocked) { GUIManager.BlockInput(false); blocked = false; } } public void Dispose() { ReleaseInput(); markers.Clear(); if (Object.op_Implicit((Object)(object)root)) { Object.Destroy((Object)(object)root); } } } private Player nativeDeathPlayer; private string nativeDeathEvent = ""; private bool nativeWaiting; private bool nativeCompleted; private bool nativeRespawnRequested; private bool nativeTailReached; private double nativeConfirmationUntil; private readonly HashSet<Player> observedDead = new HashSet<Player>(); public const string Guid = "com.vikingrevival"; internal static Plugin Instance; internal RevivalEngine Engine; internal Snapshot View; internal Rules Rules = new Rules(); private Harmony harmony; private ZRoutedRpc registered; private readonly HashSet<ZRpc> boundConnections = new HashSet<ZRpc>(); private readonly Dictionary<long, long> members = new Dictionary<long, long>(); private readonly Dictionary<long, double> lastMessage = new Dictionary<long, double>(); private readonly Dictionary<long, int> messageCount = new Dictionary<long, int>(); private string session = System.Guid.NewGuid().ToString("N"); private string journalPath; private string pending = ""; private long world; private long sequence; private long lastSequence = -1L; private double nextHello; private double nextTick; private double nextBroadcast; private double receivedAt; private double clockAtReceive; private bool fault; private bool restoring; private int nativeDeathDepth; private double nextDeathAcknowledgement; private ConfigEntry<KeyboardShortcut> reviveKey; private string heldAttempt = ""; private string heldEvent = ""; private string feedback = ""; private long heldTarget; private Tier selected; private double feedbackUntil; private double nextHold; private double heldStartedAt; private ConfigEntry<float> hudScale; private ConfigEntry<float> audioVolume; private ConfigEntry<float> indicatorRange; private ConfigEntry<bool> indicators; private ConfigEntry<bool> audio; private ConfigEntry<KeyboardShortcut> cycle; internal const string PendingKey = "VikingRevival.pending"; internal const string AppliedKey = "VikingRevival.applied"; internal const string EscrowKey = "VikingRevival.escrow"; internal const string DeathKey = "VikingRevival.death"; internal const string CompletedDeathKey = "VikingRevival.deathCompleted"; private readonly Dictionary<string, string> words = new Dictionary<string, string> { ["downed"] = "You are downed", ["bleedout"] = "Bleed-out", ["reviving"] = "Reviving", ["dying"] = "You are dying", ["hold"] = "Hold Use to revive", ["required"] = "Healing mead required", ["recovery"] = "Recovery", ["cycle"] = "Cycle mead", ["none"] = "No potion", ["minor"] = "Minor Healing Mead", ["medium"] = "Medium Healing Mead", ["major"] = "Major Healing Mead" }; private bool wordsLoaded; private ConfigEntry<bool> showWelcome; private NativeInterface nativeUi; private double nextUiAttempt; internal bool WaitingForRevival { get { if (Owner(Player.m_localPlayer) && (Object)(object)Player.m_localPlayer == (Object)(object)nativeDeathPlayer && ((Character)Player.m_localPlayer).IsDead() && nativeCompleted && nativeWaiting) { return !nativeRespawnRequested; } return false; } } internal bool CanRespawnNow { get { if (Owner(Player.m_localPlayer) && (Object)(object)Player.m_localPlayer == (Object)(object)nativeDeathPlayer && ((Character)Player.m_localPlayer).IsDead() && nativeCompleted) { return !nativeRespawnRequested; } return false; } } internal static double Utc => (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds; internal double Now { get { if (View != null) { return clockAtReceive + (Time.realtimeSinceStartupAsDouble - receivedAt); } return Utc; } } internal bool Bypass => nativeDeathDepth > 0; private long ServerPeer { get { if (!ZNet.instance.IsServer()) { return ZNet.instance.GetServerPeer()?.m_uid ?? 0; } return ZNet.GetUID(); } } private void BeginNativeDeath(Player p) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Expected I4, but got Unknown //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Invalid comparison between Unknown and I4 //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Invalid comparison between Unknown and I4 //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Invalid comparison between Unknown and I4 if (Owner(p)) { nativeDeathPlayer = p; nativeCompleted = (nativeRespawnRequested = (nativeWaiting = (nativeTailReached = false))); nativeDeathEvent = ""; Cause val = Classify(AccessTools.FieldRefAccess<Character, HitData>("m_lastHit").Invoke((Character)(object)p)); bool flag = View != null && Time.realtimeSinceStartupAsDouble - receivedAt < 3.0; if (!(!fault && flag) || !Rules.EnableRevival || ((Character)p).IsSwimming() || ((Character)p).InCutscene() || ((Character)p).IsTeleporting() || (int)val == 3 || (int)val == 4 || ((int)val == 2 && !Rules.AllowPvPRevival)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Native death: normal respawn; revival unavailable (authority or death type)."); return; } nativeDeathEvent = System.Guid.NewGuid().ToString("N"); nativeWaiting = true; nativeConfirmationUntil = Now + 3.0; p.m_customData["VikingRevival.death"] = world + ":" + nativeDeathEvent; Send(new Wire { Op = "down", Id = nativeDeathEvent, Cause = (int)val, Water = false }); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Native death: requesting revival window " + nativeDeathEvent)); } } internal bool DeferNativeRespawn(bool afterDeath) { if (afterDeath && nativeDeathDepth > 0 && (Object)(object)nativeDeathPlayer == (Object)(object)Player.m_localPlayer) { nativeTailReached = true; } if (afterDeath && nativeWaiting && !nativeRespawnRequested) { return (Object)(object)nativeDeathPlayer == (Object)(object)Player.m_localPlayer; } return false; } internal void Died(Player p) { if (!Owner(p)) { return; } if (!((Character)p).IsDead() || !nativeTailReached) { nativeWaiting = false; ((BaseUnityPlugin)this).Logger.LogWarning((object)"Another death patch skipped the expected native death sequence. Revival disabled for this event."); if (((Character)p).IsDead()) { Game.instance.RequestRespawn(10f, true); } return; } nativeCompleted = true; p.m_customData.TryGetValue("VikingRevival.death", out var value); if (!string.IsNullOrEmpty(value)) { p.m_customData["VikingRevival.deathCompleted"] = value; } p.m_customData.Remove("VikingRevival.pending"); p.m_customData.Remove("VikingRevival.applied"); pending = ""; try { Game.instance.SavePlayerProfile(false, false); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Post-death profile save failed: " + ex.Message)); } if (!nativeWaiting) { AcknowledgeCompletedDeath(p); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Native death completed. Waiting for revival: " + nativeWaiting)); } internal void NativeDeathFailed(Player p, Exception error) { if (Owner(p) && !nativeRespawnRequested) { nativeWaiting = false; nativeRespawnRequested = true; if (!string.IsNullOrEmpty(nativeDeathEvent)) { p.m_customData["VikingRevival.deathCompleted"] = world + ":" + nativeDeathEvent; Send(new Wire { Op = "giveup", Event = nativeDeathEvent }); AcknowledgeCompletedDeath(p); } ((BaseUnityPlugin)this).Logger.LogWarning((object)("Native death threw before completing. Falling back to normal respawn; preserve the log and tombstone. " + error)); Game.instance.RequestRespawn(0f, true); } } private void TickNativeDeath(Player p) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Invalid comparison between Unknown and I4 if ((Object)(object)p != (Object)(object)nativeDeathPlayer || !nativeCompleted || !nativeWaiting || nativeRespawnRequested) { return; } bool flag = !fault && View != null && Time.realtimeSinceStartupAsDouble - receivedAt < 3.0; DeathDecision val = NativeDeathLifecycle.Decide(State(Id(p)), nativeDeathEvent, flag, nativeRespawnRequested, Now, nativeConfirmationUntil); if ((int)val != 0) { if ((int)val == 1) { RestoreNativePlayer(p, State(Id(p))); } else { RespawnNow(); } } } internal void RespawnNow() { if (CanRespawnNow) { nativeRespawnRequested = true; nativeWaiting = false; if (!string.IsNullOrEmpty(nativeDeathEvent)) { Send(new Wire { Op = "giveup", Event = nativeDeathEvent }); } AcknowledgeCompletedDeath(Player.m_localPlayer); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Native respawn requested; revival window closed."); Game.instance.RequestRespawn(0f, true); } } internal void GiveUp() { RespawnNow(); } private void RestoreNativePlayer(Player p, PlayerState state) { nativeWaiting = false; ((MonoBehaviour)Game.instance).CancelInvoke("_RequestRespawn"); p.OnRespawn(); AccessTools.FieldRefAccess<Character, bool>("m_isDead").Invoke((Character)(object)p) = false; ((Character)p).SetHealth((float)RevivalEngine.RestoredHealth(state.Health, (double)((Character)p).GetMaxHealth())); GameObject visual = ((Character)p).GetVisual(); if (Object.op_Implicit((Object)(object)visual)) { visual.SetActive(true); } Ragdoll ragdoll = p.GetRagdoll(); if (Object.op_Implicit((Object)(object)ragdoll)) { ZNetView component = ((Component)ragdoll).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner()) { component.Destroy(); } } AccessTools.FieldRefAccess<Player, Ragdoll>("m_ragdoll").Invoke(p) = null; p.m_customData["VikingRevival.applied"] = world + ":" + state.Event; p.m_customData.Remove("VikingRevival.death"); p.m_customData.Remove("VikingRevival.deathCompleted"); p.m_customData.Remove("VikingRevival.pending"); nativeCompleted = false; nativeDeathPlayer = null; Game.instance.SavePlayerProfile(false, false); Notify("Revived. Your items remain in the tombstone unless already looted."); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Native revival restored avatar for event " + state.Event + "; tombstone inventory untouched.")); } private void SyncResurrectedVisuals() { foreach (Player allPlayer in Player.GetAllPlayers()) { if (!Object.op_Implicit((Object)(object)allPlayer) || !Object.op_Implicit((Object)(object)Net(allPlayer)) || !Net(allPlayer).IsValid()) { continue; } if (((Character)allPlayer).IsDead()) { observedDead.Add(allPlayer); } else if (observedDead.Remove(allPlayer)) { GameObject visual = ((Character)allPlayer).GetVisual(); if (Object.op_Implicit((Object)(object)visual)) { visual.SetActive(true); } } } observedDead.RemoveWhere((Player x) => !Object.op_Implicit((Object)(object)x)); } internal static ZNetView Net(Player p) { if (!Object.op_Implicit((Object)(object)p)) { return null; } return ((Component)p).GetComponent<ZNetView>(); } internal static bool Owner(Player p) { if (Object.op_Implicit((Object)(object)p) && Object.op_Implicit((Object)(object)Net(p)) && Net(p).IsValid()) { return Net(p).IsOwner(); } return false; } internal static long Id(Player p) { if (!Object.op_Implicit((Object)(object)p)) { return 0L; } return p.GetPlayerID(); } internal PlayerState State(long id) { return ((IEnumerable<PlayerState>)View?.Players).FirstOrDefault((Func<PlayerState, bool>)((PlayerState p) => p.Id == id)); } internal bool Locked(Player p) { if (Object.op_Implicit((Object)(object)p)) { return ((Character)p).IsDead(); } return false; } internal bool Locked(Character c) { Player val = (Player)(object)((c is Player) ? c : null); if (val != null) { return Locked(val); } return false; } internal void EnterDeath(Player p) { nativeDeathDepth++; BeginNativeDeath(p); PrepareNativeDeath(p); } internal void ExitDeath() { nativeDeathDepth = Math.Max(0, nativeDeathDepth - 1); } private void Awake() { //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Expected O, but got Unknown //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Expected O, but got Unknown //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Expected O, but got Unknown //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Expected O, but got Unknown Instance = this; try { FieldInfo[] fields = typeof(Rules).GetFields(); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(bool)) { fieldInfo.SetValue(Rules, ((BaseUnityPlugin)this).Config.Bind<bool>("Server", fieldInfo.Name, (bool)fieldInfo.GetValue(Rules), "Server controlled gameplay rule.").Value); } else { fieldInfo.SetValue(Rules, ((BaseUnityPlugin)this).Config.Bind<double>("Server", fieldInfo.Name, (double)fieldInfo.GetValue(Rules), "Server controlled gameplay rule. See configuration reference for valid ranges.").Value); } } if (Rules.BleedoutSeconds == 75.0 && Rules.SecondKnockdownSeconds == 45.0 && Rules.ThirdKnockdownSeconds == 25.0) { Rules.BleedoutSeconds = (Rules.SecondKnockdownSeconds = (Rules.ThirdKnockdownSeconds = 60.0)); string[] array = new string[3] { "BleedoutSeconds", "SecondKnockdownSeconds", "ThirdKnockdownSeconds" }; foreach (string text in array) { ((BaseUnityPlugin)this).Config.Bind<double>("Server", text, 60.0, (ConfigDescription)null).Value = 60.0; } ((BaseUnityPlugin)this).Config.Save(); } Rules.Validate(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Invalid gameplay configuration: " + ex.Message + ". Revival disabled until corrected.")); Rules = new Rules { EnableRevival = false }; } hudScale = ((BaseUnityPlugin)this).Config.Bind<float>("Presentation", "HudScale", 1f, new ConfigDescription("HUD size", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.7f, 2f), Array.Empty<object>())); indicators = ((BaseUnityPlugin)this).Config.Bind<bool>("Presentation", "EnableDownedIndicators", true, (ConfigDescription)null); indicatorRange = ((BaseUnityPlugin)this).Config.Bind<float>("Presentation", "IndicatorRange", 80f, new ConfigDescription("World indicator range", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 150f), Array.Empty<object>())); audio = ((BaseUnityPlugin)this).Config.Bind<bool>("Presentation", "EnableDownedAudio", true, (ConfigDescription)null); audioVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Presentation", "SoundVolume", 0.3f, new ConfigDescription("Feedback volume", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>())); cycle = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Input", "CycleRevivalOption", new KeyboardShortcut((KeyCode)116, Array.Empty<KeyCode>()), "Choose next available healing mead or No potion. Hold the separate Revive key (R by default); E remains looting."); showWelcome = ((BaseUnityPlugin)this).Config.Bind<bool>("Presentation", "ShowWelcomeOnStartup", true, "Show the gameplay explanation when the main menu opens. The Viking Revival menu button can reopen it anytime."); reviveKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Input", "Revive", new KeyboardShortcut((KeyCode)114, Array.Empty<KeyCode>()), "Hold near a fallen player to revive. E remains normal looting."); harmony = new Harmony("com.vikingrevival"); harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Viking Revival 1.1.2 candidate loaded. Game: " + Version.GetVersionString(false) + ". Live multiplayer acceptance required.")); } private void OnDestroy() { nativeUi?.Dispose(); RestoreMenuInput(); Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } Instance = null; } private void Update() { TickNativeUi(); if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZRoutedRpc.instance == null || !Object.op_Implicit((Object)(object)Game.instance) || ZNet.instance.GetWorld() == null || ZNet.m_loadError) { return; } try { long worldUID = ZNet.instance.GetWorldUID(); if (worldUID == 0L) { return; } if (registered != ZRoutedRpc.instance || world != worldUID) { Initialize(worldUID); } BindConnections(); if (fault) { if (Owner(Player.m_localPlayer) && ((Character)Player.m_localPlayer).IsDead()) { TickNativeDeath(Player.m_localPlayer); } return; } if (ZNet.instance.IsServer()) { ServerTick(); } Player localPlayer = Player.m_localPlayer; if (Owner(localPlayer) && Id(localPlayer) != 0L) { if (Utc >= nextHello) { nextHello = Utc + 2.0; Send(new Wire { Op = "hello" }); } SyncResurrectedVisuals(); ApplyLocal(localPlayer); if (View != null && Time.realtimeSinceStartupAsDouble - receivedAt < 3.0) { UpdateReviveInput(localPlayer); } } } catch (Exception ex) { if (!fault) { ((BaseUnityPlugin)this).Logger.LogError((object)("Revival stopped after an integration error; native respawn remains available. " + ex)); } fault = true; } } private void Initialize(long id) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_01a5: Unknown result type (might be due to invalid IL or missing references) world = id; View = null; Engine = null; members.Clear(); lastMessage.Clear(); messageCount.Clear(); lastSequence = -1L; pending = ""; heldAttempt = ""; nextHello = 0.0; fault = false; if (registered != ZRoutedRpc.instance) { registered = ZRoutedRpc.instance; boundConnections.Clear(); } if (!ZNet.instance.IsServer()) { return; } session = System.Guid.NewGuid().ToString("N"); sequence = 0L; Engine = new RevivalEngine(Rules); journalPath = Path.Combine(Paths.ConfigPath, "VikingRevival", world + ".native-v2.json"); if (File.Exists(journalPath)) { Journal journal = JsonConvert.DeserializeObject<Journal>(File.ReadAllText(journalPath)); if (journal == null || journal.Protocol != 2 || journal.World != world) { throw new InvalidDataException("Invalid revival journal; preserve file and restore a verified backup."); } foreach (PlayerState player in journal.Players) { Engine.Players.Add(player.Id, player); } foreach (Attempt attempt in journal.Attempts) { if (RevivalEngine.Active(attempt)) { attempt.Phase = (TransactionPhase)3; } Engine.Attempts.Add(attempt.Id, attempt); } foreach (PlayerState value in Engine.Players.Values) { if (value.TimerPaused) { value.Deadline = Math.Min(Utc, value.PauseUntil) + value.PausedRemaining; } value.TimerPaused = false; value.Attempt = ""; } } Persist(); } private void Persist() { Storage.Save(journalPath, new Journal { World = world, Players = Engine.Players.Values.ToList(), Attempts = Engine.Attempts.Values.ToList() }); } private void BindConnections() { foreach (ZNetPeer peer2 in ZNet.instance.GetPeers()) { if (!boundConnections.Add(peer2.m_rpc)) { continue; } ZNetPeer peer = peer2; peer.m_rpc.Register<string>("VR2_Request", (Action<ZRpc, string>)delegate(ZRpc rpc, string json) { if (rpc == peer.m_rpc) { Receive(peer.m_uid, json); } }); peer.m_rpc.Register<string>("VR2_State", (Action<ZRpc, string>)delegate(ZRpc rpc, string json) { if (rpc == peer.m_rpc && peer == ZNet.instance.GetServerPeer()) { ReceiveState(peer.m_uid, json); } }); } } private void Send(Wire message) { if (fault || registered == null || ServerPeer == 0L) { return; } message.Session = View?.Session ?? ""; if (ZNet.instance.IsServer()) { Receive(ZNet.GetUID(), JsonConvert.SerializeObject((object)message)); return; } ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { serverPeer.m_rpc.Invoke("VR2_Request", new object[1] { JsonConvert.SerializeObject((object)message) }); } } private ZDO Character(long peer) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (peer == ZNet.GetUID() && Owner(Player.m_localPlayer)) { return Net(Player.m_localPlayer).GetZDO(); } ZNetPeer peer2 = ZNet.instance.GetPeer(peer); if (peer2 == null || peer2.m_characterID == ZDOID.None) { return null; } ZDO zDO = ZDOMan.instance.GetZDO(peer2.m_characterID); if (zDO == null || zDO.GetOwner() != peer) { return null; } return zDO; } private ZDO CharacterById(long id) { foreach (KeyValuePair<long, long> member in members) { if (member.Value == id) { return Character(member.Key); } } return null; } private bool InRange(long a, long b, double range, bool deadTarget = false, bool deadSource = false) { //IL_002f: 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) ZDO val = CharacterById(a); ZDO val2 = CharacterById(b); if (val != null && val2 != null) { return AuthorityRange.Allows(val.GetBool("dead", false), val2.GetBool("dead", false), (double)Vector3.Distance(val.GetPosition(), val2.GetPosition()), range, deadTarget, deadSource); } return false; } private bool Valid(Attempt a) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 ZDO obj = CharacterById(a.Target); if (obj != null && obj.GetBool("dead", false) && InRange(a.Rescuer, a.Target, Rules.ReviveRange, deadTarget: true)) { return (int)Engine.Get(a.Rescuer).Life == 0; } return false; } private void Receive(long sender, string json) { //IL_05ec: Expected O, but got Unknown //IL_0360: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_038b: Invalid comparison between Unknown and I4 //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03aa: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || Engine == null || fault || json == null || json.Length > 2048) { return; } try { if (!lastMessage.TryGetValue(sender, out var value) || Utc - value >= 1.0) { lastMessage[sender] = Utc; messageCount[sender] = 0; } if (++messageCount[sender] > 40) { return; } Wire wire = JsonConvert.DeserializeObject<Wire>(json); ZDO val = Character(sender); if (wire == null || wire.V != 2 || val == null || wire.Id == null || wire.Id.Length > 64 || wire.Event == null || wire.Event.Length > 64) { return; } long id = val.GetLong("playerID", 0L); if (id == 0L) { return; } if (wire.Op == "hello") { if (!members.Any((KeyValuePair<long, long> m) => m.Key != sender && m.Value == id)) { members[sender] = id; Broadcast(sender); } } else { if (!members.TryGetValue(sender, out var value2) || value2 != id || wire.Session != session) { return; } bool flag = false; switch (wire.Op) { case "down": if (Enum.IsDefined(typeof(Cause), wire.Cause)) { Eligibility val2 = new Eligibility { Connected = true, Dead = false, Downed = ((int)Engine.Get(id).Life > 0), DeepWater = wire.Water, Cause = (Cause)wire.Cause, HasNearbyTeammate = false }; flag = Engine.KnockDown(id, wire.Id, val2, Utc); } break; case "hit": flag = Engine.Hit(id, wire.Event, Utc, true); Engine.Interrupt(id, Utc); flag = true; break; case "hurt": Engine.Interrupt(id, Utc); flag = true; break; case "begin": if (Engine.Attempts.Count < 10000) { ZDO obj = CharacterById(wire.Target); if (obj != null && obj.GetBool("dead", false)) { flag = Engine.Begin(id, wire.Target, wire.Event, wire.Id, (Tier)wire.Tier, Utc, InRange(id, wire.Target, Rules.ReviveRange, deadTarget: true), wire.Available); } } break; case "hold": { if (Engine.Attempts.TryGetValue(wire.Id, out var value4)) { flag = Engine.Hold(id, wire.Id, Utc, wire.Available && Valid(value4)); } break; } case "cancel": flag = Engine.Cancel(id, wire.Id, Utc); break; case "receipt": { if (Engine.Attempts.TryGetValue(wire.Id, out var value3)) { flag = Engine.Commit(id, wire.Id, Utc, wire.Available, Valid(value3)); } break; } case "unsafe": Engine.ForceDeath(id); flag = true; break; case "giveup": flag = Engine.GiveUp(id, wire.Event); break; case "dead": Engine.AcknowledgeDeath(id, wire.Event); flag = true; break; } if (flag) { Persist(); Broadcast(0L); } } } catch (JsonException ex) { JsonException ex2 = ex; ((BaseUnityPlugin)this).Logger.LogWarning((object)("Rejected malformed revival request: " + ((Exception)(object)ex2).Message)); } catch (Exception ex3) { fault = true; ((BaseUnityPlugin)this).Logger.LogError((object)("Authority journal/RPC failure: " + ex3)); } } private void ServerTick() { if (Utc < nextTick) { return; } nextTick = Utc + 0.1; long[] array = members.Keys.ToArray(); foreach (long num in array) { if (Character(num) == null) { Engine.Disconnect(members[num], Utc); members.Remove(num); } } string text = JsonConvert.SerializeObject((object)new { P = Engine.Players, A = Engine.Attempts }); Engine.Tick(Utc, (Func<Attempt, bool>)Valid); string text2 = JsonConvert.SerializeObject((object)new { P = Engine.Players, A = Engine.Attempts }); if (text != text2) { Persist(); } if (Utc >= nextBroadcast) { nextBroadcast = Utc + 0.25; Broadcast(0L); } } private void Broadcast(long recipient = 0L) { string text = JsonConvert.SerializeObject((object)new Snapshot { Session = session, Sequence = ++sequence, World = world, Now = Utc, Rules = Rules, Players = Engine.Players.Values.ToList(), Attempts = Engine.Attempts.Values.ToList() }); long[] array = members.Keys.ToArray(); foreach (long num in array) { if (recipient != 0L && recipient != num) { continue; } if (num == ZNet.GetUID()) { ReceiveState(num, text); continue; } ZNetPeer peer = ZNet.instance.GetPeer(num); if (peer != null) { peer.m_rpc.Invoke("VR2_State", new object[1] { text }); } } } private void ReceiveState(long sender, string json) { if (sender != ServerPeer || json == null || json.Length > 4000000) { return; } try { Snapshot snapshot = JsonConvert.DeserializeObject<Snapshot>(json); if (snapshot == null || snapshot.V != 2 || snapshot.World != world || snapshot.Rules == null) { return; } snapshot.Rules.Validate(); if (View == null || !(snapshot.Session == View.Session) || snapshot.Sequence > lastSequence) { if (View != null && snapshot.Session != View.Session) { lastSequence = -1L; heldAttempt = ""; } View = snapshot; Rules = snapshot.Rules; lastSequence = snapshot.Sequence; receivedAt = Time.realtimeSinceStartupAsDouble; clockAtReceive = snapshot.Now; } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Rejected invalid authority snapshot: " + ex.Message)); } } internal static Cause Classify(HitData hit) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_002e: 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: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 if (hit != null) { if ((int)hit.m_hitType != 4 && (int)hit.m_hitType != 8 && (int)hit.m_hitType != 21) { if ((int)hit.m_hitType != 14 && (int)hit.m_hitType != 0 && (int)hit.m_hitType != 10) { if (!(hit.GetAttacker() is Player) && (int)hit.m_hitType != 2) { if (!((Object)(object)hit.GetAttacker() != (Object)null) && (int)hit.m_hitType != 1) { return (Cause)1; } return (Cause)0; } return (Cause)2; } return (Cause)4; } return (Cause)3; } return (Cause)4; } internal bool Damage(Player p, HitData hit) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Invalid comparison between Unknown and I4 //IL_00b2: 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_00c0: Invalid comparison between Unknown and I4 //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Invalid comparison between Unknown and I4 if (!Owner(p) || Bypass) { return true; } PlayerState val = State(Id(p)); if (val != null && val.ProtectedUntil > Now && (int)val.Life == 0) { return false; } if (Locked(p)) { if ((int)Classify(hit) == 3 || (int)Classify(hit) == 4) { Send(new Wire { Op = "unsafe" }); } else if ((int)Classify(hit) == 2 && !Rules.AllowPvPRevival) { Send(new Wire { Op = "unsafe" }); } else if (hit != null && hit.GetTotalDamage() > 0.1f && ((int)Classify(hit) == 0 || (int)hit.m_hitType == 7 || (int)hit.m_hitType == 5)) { Send(new Wire { Op = "hit", Event = (val?.Event ?? pending) }); } return false; } if (hit != null && hit.GetTotalDamage() > 0.1f) { CancelHeld(); Send(new Wire { Op = "hurt" }); } return true; } private void ApplyLocal(Player p) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 if (((Character)p).IsDead()) { TickNativeDeath(p); return; } if (HasCompletedEvent(p, State(Id(p))?.Event)) { PlayerState obj = State(Id(p)); if (obj == null || (int)obj.Life > 0) { AcknowledgeCompletedDeath(p); } } p.m_customData.Remove("VikingRevival.pending"); ProcessEscrow(p); } private string LocalEvent(Player p, string key) { if (p.m_customData.TryGetValue(key, out var value) && value.StartsWith(world + ":", StringComparison.Ordinal)) { return value.Substring(value.IndexOf(':') + 1); } return ""; } private bool HasCompletedEvent(Player p, string evt) { if (!string.IsNullOrEmpty(evt)) { return LocalEvent(p, "VikingRevival.deathCompleted") == evt; } return false; } private void PrepareNativeDeath(Player p) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown ref HitData reference = ref AccessTools.FieldRefAccess<Character, HitData>("m_lastHit").Invoke((Character)(object)p); if (reference == null) { reference = new HitData { m_hitType = (HitType)0 }; } string text = (nativeWaiting ? nativeDeathEvent : State(Id(p))?.Event); if (string.IsNullOrEmpty(text)) { text = LocalEvent(p, "VikingRevival.pending"); } if (!string.IsNullOrEmpty(text)) { p.m_customData["VikingRevival.death"] = world + ":" + text; } p.m_customData.Remove("VikingRevival.pending"); p.m_customData.Remove("VikingRevival.applied"); pending = ""; } private void AcknowledgeCompletedDeath(Player p) { string text = LocalEvent(p, "VikingRevival.deathCompleted"); if (!string.IsNullOrEmpty(text) && !(Utc < nextDeathAcknowledgement) && (!(State(Id(p))?.Event == text) || !State(Id(p)).DeathAcknowledged)) { nextDeathAcknowledgement = Utc + 1.0; Send(new Wire { Op = "dead", Event = text }); } } private ItemData FindMead(Player p, Tier tier) { //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) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 if ((int)tier != 3) { return ((IEnumerable<ItemData>)((Humanoid)p).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => Object.op_Implicit((Object)(object)i.m_dropPrefab) && ((Object)i.m_dropPrefab).name == Rules.Prefab(tier) && i.m_stack > 0)); } return null; } private void ProcessEscrow(Player p) { //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Invalid comparison between Unknown and I4 //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00c0: Invalid comparison between Unknown and I4 //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Invalid comparison between Unknown and I4 //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Invalid comparison between Unknown and I4 //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Expected O, but got Unknown if (View == null) { return; } Attempt val = ((IEnumerable<Attempt>)View.Attempts).FirstOrDefault((Func<Attempt, bool>)((Attempt a) => a.Rescuer == Id(p) && (int)a.Phase == 1)); if (val != null && !p.m_customData.ContainsKey("VikingRevival.escrow")) { if (heldAttempt != val.Id || !Holding() || !InLocalRange(p, val.Target)) { Send(new Wire { Op = "cancel", Id = val.Id }); return; } ItemData val2 = FindMead(p, val.Tier); if ((int)val.Tier != 3 && val2 == null) { Send(new Wire { Op = "cancel", Id = val.Id }); return; } string text = ""; if ((int)val.Tier != 3 && val2 != null) { ItemData obj = val2.Clone(); obj.m_stack = 1; ZPackage val3 = new ZPackage(); obj.Save(val3); text = Convert.ToBase64String(val3.GetArray()); restoring = true; try { if (!((Humanoid)p).GetInventory().RemoveItem(val2, 1)) { Send(new Wire { Op = "cancel", Id = val.Id }); return; } } finally { restoring = false; } } p.m_customData["VikingRevival.escrow"] = val.Id + ":" + text; Game.instance.SavePlayerProfile(false, false); } if (!p.m_customData.TryGetValue("VikingRevival.escrow", out var value)) { return; } int num = value.IndexOf(':'); if (num < 0) { throw new InvalidDataException("Invalid potion escrow receipt; preserve character save."); } string attempt = value.Substring(0, num); string text2 = value.Substring(num + 1); Attempt val4 = ((IEnumerable<Attempt>)View.Attempts).FirstOrDefault((Func<Attempt, bool>)((Attempt a) => a.Id == attempt)); if (val4 == null) { return; } if ((int)val4.Phase == 1) { Send(new Wire { Op = "receipt", Id = attempt, Available = true }); } else { if ((int)val4.Phase == 0) { return; } if ((int)val4.Phase == 3 && text2 != "") { (int, ItemData) tuple = ItemData.Load(new ZPackage(Convert.FromBase64String(text2)), (Item)110); GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(tuple.Item1); if (!Object.op_Implicit((Object)(object)itemPrefab)) { throw new InvalidDataException("Escrow item prefab missing."); } ItemData val5 = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); ItemData.Load(new ZPackage(Convert.FromBase64String(text2)), val5, (Item)110); val5.m_dropPrefab = itemPrefab; if (!((Humanoid)p).GetInventory().CanAddItem(val5, 1)) { feedback = "Make room for your refunded healing mead"; feedbackUntil = Utc + 2.0; return; } restoring = true; try { if (!((Humanoid)p).GetInventory().AddItem(val5)) { return; } } finally { restoring = false; } } p.m_customData.Remove("VikingRevival.escrow"); Game.instance.SavePlayerProfile(false, false); } } internal bool InventoryBlocked(Inventory inv) { if (!Bypass && !restoring && Owner(Player.m_localPlayer) && inv == ((Humanoid)Player.m_localPlayer).GetInventory()) { if (!Locked(Player.m_localPlayer)) { return Player.m_localPlayer.m_customData.ContainsKey("VikingRevival.escrow"); } return true; } return false; } private bool Holding() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = reviveKey.Value; return ((KeyboardShortcut)(ref value)).IsPressed(); } private bool InputAvailable() { if (!Menu.IsVisible() && !Console.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible()) { if (Object.op_Implicit((Object)(object)Chat.instance)) { return !Chat.instance.HasFocus(); } return true; } return false; } private bool InLocalRange(Player p, long target) { //IL_0032: 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) Player val = ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player x) => Id(x) == target)); if (Object.op_Implicit((Object)(object)val)) { return (double)Vector3.Distance(((Component)p).transform.position, ((Component)val).transform.position) <= Rules.ReviveRange; } return false; } internal Player Nearby(Player p) { return (from x in Player.GetAllPlayers().Where(delegate(Player x) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 //IL_004b: 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) if (Object.op_Implicit((Object)(object)x) && (Object)(object)x != (Object)(object)p && ((Character)x).IsDead()) { PlayerState obj = State(Id(x)); if (obj != null && (int)obj.Life == 1) { return (double)Vector3.Distance(((Component)p).transform.position, ((Component)x).transform.position) <= Rules.ReviveRange; } } return false; }) orderby Vector3.Distance(((Component)p).transform.position, ((Component)x).transform.position) select x).FirstOrDefault(); } private void UpdateReviveInput(Player p) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Invalid comparison between Unknown and I4 //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Expected I4, but got Unknown //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Invalid comparison between Unknown and I4 //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Invalid comparison between Unknown and I4 //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Invalid comparison between Unknown and I4 //IL_031d: Unknown result type (might be due to invalid IL or missing references) if (Locked(p) || ((Character)p).IsDead() || !InputAvailable()) { CancelHeld(); return; } if (heldAttempt == "" && (int)selected != 3 && FindMead(p, selected) == null) { selected = Enum.GetValues(typeof(Tier)).Cast<Tier>().First((Tier t) => (int)t == 3 || FindMead(p, t) != null); } Player val = Nearby(p); if (!Object.op_Implicit((Object)(object)val) || !Holding()) { CancelHeld(); if (Object.op_Implicit((Object)(object)val)) { KeyboardShortcut value = cycle.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Cycle(p); } } return; } if (heldAttempt != "" && (heldTarget != Id(val) || heldEvent != State(Id(val))?.Event)) { CancelHeld(); } if (heldAttempt == "") { if ((int)selected != 3 && FindMead(p, selected) == null) { selected = Enum.GetValues(typeof(Tier)).Cast<Tier>().FirstOrDefault((Func<Tier, bool>)((Tier t) => (int)t == 3 || FindMead(p, t) != null)); } heldStartedAt = Utc; heldAttempt = System.Guid.NewGuid().ToString("N"); heldTarget = Id(val); heldEvent = State(heldTarget).Event; Send(new Wire { Op = "begin", Id = heldAttempt, Target = heldTarget, Event = heldEvent, Tier = (int)selected, Available = ((int)selected == 3 || FindMead(p, selected) != null) }); } Attempt val2 = ((IEnumerable<Attempt>)View.Attempts).FirstOrDefault((Func<Attempt, bool>)((Attempt x) => x.Id == heldAttempt)); if (val2 == null && Utc - heldStartedAt > 1.0) { CancelHeld(); Notify("Revival not confirmed. Stay near the fallen player and keep holding R."); } else if (val2 != null && !RevivalEngine.Active(val2)) { if ((int)val2.Phase == 3) { Notify("Revival interrupted"); } heldAttempt = ""; } else if (Utc >= nextHold) { nextHold = Utc + 0.2; Send(new Wire { Op = "hold", Id = heldAttempt, Available = ((int)selected == 3 || p.m_customData.ContainsKey("VikingRevival.escrow") || FindMead(p, selected) != null) }); } } private void Cycle(Player p) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_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_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 //IL_001e: 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_0015: Unknown result type (might be due to invalid IL or missing references) for (int i = 1; i <= 4; i++) { Tier val = (Tier)((selected + i) % 4); if ((int)val == 3 || FindMead(p, val) != null) { selected = val; break; } } } private void CancelHeld() { if (heldAttempt != "") { Send(new Wire { Op = "cancel", Id = heldAttempt }); } heldAttempt = ""; } private void Notify(string text) { feedback = text; feedbackUntil = Utc + 3.0; if (audio.Value && audioVolume.Value > 0f && Object.op_Implicit((Object)(object)Player.m_localPlayer)) { AudioSource val = ((IEnumerable<AudioSource>)((Component)Player.m_localPlayer).GetComponentsInChildren<AudioSource>()).FirstOrDefault((Func<AudioSource, bool>)((AudioSource s) => Object.op_Implicit((Object)(object)s.clip) && !s.loop)); if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.clip)) { val.PlayOneShot(val.clip, audioVolume.Value); } } } internal bool InteractionBlocked(Player p) { return Locked(p); } private string Text(string key) { if (!wordsLoaded) { string path = Path.Combine(Paths.ConfigPath, "VikingRevival", "localization.json"); if (File.Exists(path)) { try { Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(path)); if (dictionary != null) { foreach (KeyValuePair<string, string> item in dictionary) { if (words.ContainsKey(item.Key) && !string.IsNullOrEmpty(item.Value)) { words[item.Key] = item.Value; } } } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Localization file ignored: " + ex.Message)); } } wordsLoaded = true; } return words[key]; } private void TickNativeUi() { if (Utc < nextUiAttempt || GUIManager.IsHeadless()) { return; } try { if (nativeUi == null) { nativeUi = new NativeInterface(this); } nativeUi.Tick(); } catch (Exception ex) { nextUiAttempt = Utc + 3.0; nativeUi?.Dispose(); nativeUi = null; ((BaseUnityPlugin)this).Logger.LogError((object)("Revival interface will retry after error: " + ex)); } } private void OnGUI() { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) if (!CanRespawnNow) { return; } NativeInterface nativeInterface = nativeUi; if (nativeInterface == null || !nativeInterface.DeathPanelVisible) { Rect val = default(Rect); ((Rect)(ref val))..ctor((float)Mathf.Max(8, (Screen.width - 480) / 2), Mathf.Max(8f, (float)Screen.height * 0.65f), 480f, 135f); GUI.Box(val, "Viking Revival - You died"); GUI.Label(new Rect(((Rect)(ref val)).x + 20f, ((Rect)(ref val)).y + 32f, 440f, 45f), WaitingForRevival ? "Waiting for a friend to hold R nearby. Your tombstone can be looted with E." : "You can respawn now."); if (GUI.Button(new Rect(((Rect)(ref val)).x + 140f, ((Rect)(ref val)).y + 88f, 200f, 35f), "Respawn now (Enter)")) { RespawnNow(); } if (!Console.IsVisible() && (!Object.op_Implicit((Object)(object)Chat.instance) || !Chat.instance.HasFocus()) && Input.GetKeyDown((KeyCode)13)) { RespawnNow(); } } } private void RestoreMenuInput() { nativeUi?.ReleaseInput(); } } [HarmonyPatch(typeof(Ragdoll), "DestroyNow")] internal static class WaitingRagdollPatch { private static bool Prefix(Ragdoll __instance) { if (!((Object)(object)Plugin.Instance == (Object)null) && Plugin.Instance.WaitingForRevival) { return (Object)(object)Player.m_localPlayer.GetRagdoll() != (Object)(object)__instance; } return true; } } [HarmonyPatch(typeof(Game), "RequestRespawn")] internal static class NativeRespawnPatch { private static bool Prefix(bool afterDeath) { if (!((Object)(object)Plugin.Instance == (Object)null)) { return !Plugin.Instance.DeferNativeRespawn(afterDeath); } return true; } } [HarmonyPatch(typeof(Hud), "UpdateBlackScreen")] internal static class WaitingDeathFadePatch { private static bool Prefix(Hud __instance) { if ((Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.WaitingForRevival || !Object.op_Implicit((Object)(object)Game.instance) || Game.instance.WaitingForRespawn() || Game.instance.IsShuttingDown()) { return true; } __instance.m_loadingScreen.alpha = 0f; ((Component)__instance.m_loadingScreen).gameObject.SetActive(false); return false; } } [HarmonyPatch(typeof(Character), "ApplyDamage")] internal static class DamagePatch { private static bool Prefix(Character __instance, HitData hit) { Player val = (Player)(object)((__instance is Player) ? __instance : null); if (val != null && !((Object)(object)Plugin.Instance == (Object)null)) { return Plugin.Instance.Damage(val, hit); } return true; } } [HarmonyPatch(typeof(Player), "OnDeath")] internal static class DeathPatch { private static void Prefix(Player __instance, out bool __state) { __state = Plugin.Owner(__instance) && (Object)(object)Plugin.Instance != (Object)null; if (__state) { Plugin.Instance.EnterDeath(__instance); } } private static void Postfix(Player __instance) { Plugin.Instance?.Died(__instance); } private static Exception Finalizer(Player __instance, bool __state, Exception __exception) { if (__state) { try { if (__exception != null) { Plugin.Instance?.NativeDeathFailed(__instance, __exception); } } finally { Plugin.Instance?.ExitDeath(); } } return __exception; } } [HarmonyPatch] internal static class ActionPatch { private static IEnumerable<MethodBase> TargetMethods() { (Type, string)[] array = new(Type, string)[12] { (typeof(Humanoid), "StartAttack"), (typeof(Humanoid), "UseItem"), (typeof(Humanoid), "EquipItem"), (typeof(Humanoid), "DropItem"), (typeof(Player), "TeleportTo"), (typeof(Player), "StartEmote"), (typeof(Player), "ActivateGuardianPower"), (typeof(Player), "UpdatePlacement"), (typeof(Player), "Dodge"), (typeof(Character), "Jump"), (typeof(Character), "ForceJump"), (typeof(Player), "UseStamina") }; (Type, string)[] array2 = array; for (int i = 0; i < array2.Length; i++) { (Type, string) tuple = array2[i]; MethodInfo methodInfo = AccessTools.Method(tuple.Item1, tuple.Item2, (Type[])null, (Type[])null); if (methodInfo == null) { throw new MissingMethodException(tuple.Item1.FullName, tuple.Item2); } yield return methodInfo; } } private static bool Prefix(Character __instance) { if (!((Object)(object)Plugin.Instance == (Object)null) && !Plugin.Instance.Bypass) { return !Plugin.Instance.Locked(__instance); } return true; } } [HarmonyPatch(typeof(Player), "Interact")] internal static class InteractPatch { private static bool Prefix(Player __instance) { if (!((Object)(object)Plugin.Instance == (Object)null)) { return !Plugin.Instance.InteractionBlocked(__instance); } return true; } } [HarmonyPatch(typeof(InventoryGui), "Show")] internal static class InventoryGuiPatch { private static bool Prefix() { if (!((Object)(object)Plugin.Instance == (Object)null)) { return !Plugin.Instance.Locked(Player.m_localPlayer); } return true; } } [HarmonyPatch] internal static class InventoryPatch { private static IEnumerable<MethodBase> TargetMethods() { return from m in AccessTools.GetDeclaredMethods(typeof(Inventory)) where m.Name == "AddItem" || m.Name == "RemoveItem" || m.Name == "RemoveOneItem" || m.Name == "RemoveAll" || m.Name == "RemoveUnequipped" || m.Name == "MoveAll" || m.Name == "MoveItemToThis" || m.Name == "MoveInventoryToGrave" select m; } private static bool Prefix(Inventory __instance, object[] __args) { Plugin instance = Plugin.Instance; if ((Object)(object)instance == (Object)null || instance.Bypass) { return true; } if (!instance.InventoryBlocked(__instance)) { return !__args.OfType<Inventory>().Any(instance.InventoryBlocked); } return false; } } public sealed class Journal { public int Protocol = 2; public long World; public List<PlayerState> Players = new List<PlayerState>(); public List<Attempt> Attempts = new List<Attempt>(); } public static class Storage { public static void Save(string path, object data) { Directory.CreateDirectory(Path.GetDirectoryName(path)); byte[] bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)); using (FileStream fileStream = new FileStream(path + ".tmp", FileMode.Create, FileAccess.Write, FileShare.None)) { fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(flushToDisk: true); } if (File.Exists(path)) { File.Replace(path + ".tmp", path, path + ".bak"); } else { File.Move(path + ".tmp", path); } } } public sealed class Wire { public int V = 2; public string Op = ""; public string Id = ""; public string Event = ""; public string Session = ""; public long Target; public int Tier; public int Cause; public bool Available; public bool Water; } public sealed class Snapshot { public int V = 2; public string Session = ""; public long Sequence; public long World; public double Now; public Rules Rules; public List<PlayerState> Players = new List<PlayerState>(); public List<Attempt> Attempts = new List<Attempt>(); } }