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 The Vengeance System v1.0.0
RevengeDeath.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: AssemblyCompany("RevengeDeath")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("RevengeDeath")] [assembly: AssemblyTitle("RevengeDeath")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 RevengeDeath { public static class GhostState { public static bool Active; public static Player Ghost; public static ZNetView Tombstone; public static float TimeLeft; private static bool s_forceVanillaDeath; private const string CooldownKey = "RevengeDeath_SpiritSpentUntil"; public static bool IsGhost(Character c) { if (Active && (Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)Ghost; } return false; } public static bool GhostInArena() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Ghost != (Object)null) { return Utils.DistanceXZ(((Component)Ghost).transform.position, RevengeTarget.DeathPosition) <= Plugin.LeashRadius.Value; } return false; } public static bool CanFight() { if (Active && GhostInArena()) { return RevengeTarget.InArena(); } return false; } public static void Enter(Player player) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Active = true; Ghost = player; TimeLeft = Plugin.GhostTimeout.Value; Tombstone = CreateTombStoneKeepEquipped(player); ((Character)player).m_isDead = false; ((Character)player).m_zanim.SetBool(Character.s_dead, false); ((Character)player).SetHealth(Mathf.Min(Plugin.GhostHealth.Value, ((Character)player).GetMaxHealth())); GhostSync.Publish(player, RevengeTarget.EnemyId, RevengeTarget.DeathPosition); GhostVisuals.Begin(player, RevengeTarget.Enemy, RevengeTarget.Name); Plugin.Log.LogInfo((object)string.Format("[RevengeDeath] Ghost state entered. HP {0}, timeout {1}s, tombstone: {2}", ((Character)player).GetHealth(), TimeLeft, ((Object)(object)Tombstone != (Object)null) ? "yes" : "none")); } private static ZNetView CreateTombStoneKeepEquipped(Player player) { //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if (instance.GetGlobalKey((GlobalKeys)23)) { return null; } if (((Humanoid)player).m_hiddenLeftItem != null || ((Humanoid)player).m_hiddenRightItem != null) { ((Humanoid)player).ShowHandItems(false, false); } if (instance.GetGlobalKey((GlobalKeys)20) || instance.GetGlobalKey((GlobalKeys)21)) { ((Humanoid)player).m_inventory.RemoveUnequipped(); } List<string> list = new List<string>(); int num = 0; foreach (ItemData allItem in ((Humanoid)player).m_inventory.GetAllItems()) { if (allItem.m_equipped) { list.Add(allItem.m_shared.m_name); } else if (!allItem.m_shared.m_questItem) { num++; } } Plugin.Log.LogInfo((object)string.Format("[RevengeDeath] Ghost keeps equipped: [{0}]; {1} item(s) go to the grave", string.Join(", ", list), num)); if (num == 0) { return null; } GameObject obj = Object.Instantiate<GameObject>(player.m_tombstone, ((Character)player).GetCenterPoint(), ((Component)player).transform.rotation); obj.GetComponent<Container>().GetInventory().MoveInventoryToGrave(((Humanoid)player).m_inventory); PlayerProfile playerProfile = Game.instance.GetPlayerProfile(); obj.GetComponent<TombStone>().Setup(playerProfile.GetName(), playerProfile.GetPlayerID()); return obj.GetComponent<ZNetView>(); } public static void OnGhostHitTarget() { float value = Plugin.BloodlustSeconds.Value; if (Active && !(value <= 0f) && CanFight()) { float timeLeft = TimeLeft; TimeLeft = Mathf.Min(TimeLeft + value, Plugin.GhostTimeout.Value); if (TimeLeft - timeLeft >= 0.5f) { Plugin.Log.LogInfo((object)$"[RevengeDeath] Bloodlust: +{TimeLeft - timeLeft:0.#}s (now {TimeLeft:0}s left)"); GhostVisuals.ShowBloodlust(Ghost, TimeLeft - timeLeft); } } } public static void Win() { //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_0028: Unknown result type (might be due to invalid IL or missing references) Player ghost = Ghost; ZNetView tombstone = Tombstone; Vector3 deathPosition = RevengeTarget.DeathPosition; Plugin.Log.LogInfo((object)"[RevengeDeath] Revenge! Target killed -> reviving player"); Cleanup(won: true); MovePlayerTo(ghost, deathPosition); float num = ((Character)ghost).GetMaxHealth() * Plugin.ReviveHealth.Value; ((Character)ghost).SetHealth(Mathf.Max(((Character)ghost).GetHealth(), num)); if ((Object)(object)tombstone != (Object)null && tombstone.IsValid()) { ((Component)tombstone).GetComponent<Container>().TakeAll((Humanoid)(object)ghost); } StartCooldown(ghost); Spoils.GiveVengeful(ghost, GhostVisuals.TargetIcon); GhostVisuals.PlayWin(ghost); } private static void MovePlayerTo(Player player, Vector3 pos) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)player == (Object)null) && !(Utils.DistanceXZ(((Component)player).transform.position, pos) <= 3f)) { ((Component)player).transform.position = pos; ((Character)player).m_body.position = pos; ((Character)player).m_body.linearVelocity = Vector3.zero; } } public static double CooldownLeft(Player player) { if ((Object)(object)player == (Object)null || !player.m_customData.TryGetValue("RevengeDeath_SpiritSpentUntil", out var value) || !double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return 0.0; } double num = result - ZNet.instance.GetTimeSeconds(); if (num <= 0.0 || num > (double)Plugin.RevengeCooldown.Value) { return 0.0; } return num; } private static void StartCooldown(Player player) { float value = Plugin.RevengeCooldown.Value; if (!(value <= 0f)) { double num = ZNet.instance.GetTimeSeconds() + (double)value; player.m_customData["RevengeDeath_SpiritSpentUntil"] = num.ToString("R", CultureInfo.InvariantCulture); GhostVisuals.ShowSpiritSpent(player, value); } } public static void FallBackToVanilla(string reason, string playerMessage) { Player ghost = Ghost; EndAsLoss(reason); if (!((Object)(object)ghost == (Object)null)) { ((Character)ghost).Message((MessageType)1, playerMessage, 0, (Sprite)null, false); s_forceVanillaDeath = true; ((Character)ghost).SetHealth(0f); } } public static void DieNow(string reason) { Player ghost = Ghost; EndAsLoss(reason); if (!((Object)(object)ghost == (Object)null)) { s_forceVanillaDeath = true; ((Character)ghost).SetHealth(0f); ((Character)ghost).m_isDead = true; ((Character)ghost).OnDeath(); } } public static void EndAsLoss(string reason) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)("[RevengeDeath] Revenge failed: " + reason + " -> vanilla respawn")); Player ghost = Ghost; Character enemy = RevengeTarget.Enemy; if ((Object)(object)ghost != (Object)null) { MoveKeptGearToGrave(ghost); MovePlayerTo(ghost, RevengeTarget.DeathPosition); } Cleanup(); Grudge.Apply(enemy, ghost); } private static void MoveKeptGearToGrave(Player player) { if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)19) || (Object)(object)Tombstone == (Object)null || !Tombstone.IsValid() || !Tombstone.IsOwner()) { return; } ((Humanoid)player).UnequipAllItems(); Inventory inventory = ((Component)Tombstone).GetComponent<Container>().GetInventory(); Inventory inventory2 = ((Humanoid)player).GetInventory(); int num = 0; int num2 = 0; ItemData[] array = inventory2.GetAllItems().ToArray(); foreach (ItemData val in array) { if (!val.m_shared.m_questItem) { if (inventory.AddItem(val)) { inventory2.RemoveItem(val); num++; } else { num2++; } } } Plugin.Log.LogInfo((object)($"[RevengeDeath] Moved {num} kept item(s) into the grave" + ((num2 > 0) ? $", {num2} didn't fit (vanilla makes a 2nd grave for them)" : ""))); } public static void Cleanup(bool won = false) { GhostVisuals.End(Ghost); GhostSync.Unpublish(Ghost, won); Active = false; Ghost = null; Tombstone = null; TimeLeft = 0f; RevengeTarget.Clear(); } public static bool ConsumeForceVanilla() { bool result = s_forceVanillaDeath; s_forceVanillaDeath = false; return result; } public static void Update(float dt) { if (!Active) { return; } if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)Ghost) { Plugin.Log.LogInfo((object)"[RevengeDeath] Ghost player gone (logout or world exit), clearing ghost state"); Cleanup(); return; } switch (RevengeTarget.GetStatus()) { case TargetStatus.Killed: Win(); return; case TargetStatus.Despawned: FallBackToVanilla("target despawned", RevengeTarget.TheNameCap + " has escaped"); return; } RevengeTarget.ControlTick(Ghost, CanFight()); float num = RevengeTarget.DistanceFromGrave(); if (num > Plugin.LeashRadius.Value * 2f) { FallBackToVanilla($"target is {num:0}m from the grave and couldn't be pulled back", RevengeTarget.TheNameCap + " has escaped"); return; } TimeLeft -= dt; if (TimeLeft <= 0f) { FallBackToVanilla("ghost timer ran out", "Your spirit fades"); } else { GhostVisuals.Tick(Ghost, TimeLeft, GhostInArena()); } } } public class GhostInfo { public Player Player; public int Fight; public ZDOID Killer; public Character KillerChar; public Vector3 Grave; public float Radius; public string ProvokedRaw = ""; public readonly HashSet<ZDOID> Provoked = new HashSet<ZDOID>(); } public static class GhostSync { private class Fight { public int Id; public Player Player; public Vector3 Grave; public readonly List<KeyValuePair<BaseAI, Vector3>> SentAway = new List<KeyValuePair<BaseAI, Vector3>>(); public readonly HashSet<Character> Calm = new HashSet<Character>(); public bool Over; public double CalmUntil; } private static readonly int FightKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Fight"); private static readonly KeyValuePair<int, int> KillerKey = ZDO.GetHashZDOID("RevengeDeath_Killer"); private static readonly int GraveKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Grave"); private static readonly int RadiusKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Radius"); private static readonly int ProvokedKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Provoked"); private static readonly int GraceKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_GraceUntil"); private static readonly Dictionary<Player, GhostInfo> s_ghosts = new Dictionary<Player, GhostInfo>(); private static readonly List<Player> s_gone = new List<Player>(); private static readonly Dictionary<int, Fight> s_fights = new Dictionary<int, Fight>(); private static readonly HashSet<BaseAI> s_alreadySent = new HashSet<BaseAI>(); private static readonly List<int> s_finished = new List<int>(); private static readonly Dictionary<Player, Outline> s_remoteOutlines = new Dictionary<Player, Outline>(); private static readonly List<Player> s_removeOutlines = new List<Player>(); public static bool Any { get { if (s_ghosts.Count <= 0) { return s_fights.Count > 0; } return true; } } public static void Publish(Player player, ZDOID killer, Vector3 grave) { //IL_0011: 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) ZDO val = OwnZdo(player); if (val != null) { val.Set(KillerKey, killer); val.Set(GraveKey, grave); val.Set(RadiusKey, Plugin.LeashRadius.Value); val.Set(ProvokedKey, ""); val.Set(GraceKey, 0L); val.Set(FightKey, Random.Range(1, int.MaxValue), false); RefreshGhosts(); } } public static void Unpublish(Player player, bool won) { ZDO val = OwnZdo(player); if (val != null) { long num = (won ? ((long)((ZNet.instance.GetTimeSeconds() + (double)Plugin.WinGracePeriod.Value) * 1000.0)) : 0); val.Set(GraceKey, num); val.Set(FightKey, 0, false); RefreshGhosts(); } } public static void OnLocalPlayerHit(Character c) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; ZDO val = OwnZdo(localPlayer); if (val != null && !((Object)(object)c == (Object)null) && !((Object)(object)c.m_nview == (Object)null) && c.m_nview.GetZDO() != null && (GhostState.Active || InGrace(localPlayer))) { ZDOID zDOID = c.GetZDOID(); string text = val.GetString(ProvokedKey, ""); if (!ListHas(text, zDOID)) { val.Set(ProvokedKey, text + $"{((ZDOID)(ref zDOID)).UserID}:{((ZDOID)(ref zDOID)).ID};"); Plugin.Log.LogInfo((object)("[RevengeDeath] Player attacked a bystander (" + c.m_name + "); it can fight back now")); } } } private static ZDO OwnZdo(Player player) { if ((Object)(object)player == (Object)null || (Object)(object)((Character)player).m_nview == (Object)null || !((Character)player).m_nview.IsValid() || !((Character)player).m_nview.IsOwner()) { return null; } return ((Character)player).m_nview.GetZDO(); } public static bool TryGet(Character c, out GhostInfo info) { info = null; Player val = (Player)(object)((c is Player) ? c : null); if (val != null) { return s_ghosts.TryGetValue(val, out info); } return false; } public static bool IsGhost(Character c) { Player val = (Player)(object)((c is Player) ? c : null); if (val != null) { return s_ghosts.ContainsKey(val); } return false; } public static bool IsKiller(GhostInfo g, Character c) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.GetZDO() != null) { return c.GetZDOID() == g.Killer; } return false; } public static bool CanFight(GhostInfo g) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)g.Player == (Object)null || (Object)(object)g.KillerChar == (Object)null) { return false; } if (Utils.DistanceXZ(((Component)g.Player).transform.position, g.Grave) <= g.Radius) { return Utils.DistanceXZ(((Component)g.KillerChar).transform.position, g.Grave) <= g.Radius; } return false; } public static bool IsProvoked(GhostInfo g, Character c) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.GetZDO() != null) { return g.Provoked.Contains(c.GetZDOID()); } return false; } private static bool InGrace(Player player) { ZDO val = (((Object)(object)player != (Object)null && (Object)(object)((Character)player).m_nview != (Object)null) ? ((Character)player).m_nview.GetZDO() : null); if (val != null) { return (double)val.GetLong(GraceKey, 0L) / 1000.0 > ZNet.instance.GetTimeSeconds(); } return false; } public static void RefreshGhosts() { //IL_0110: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) s_gone.Clear(); foreach (KeyValuePair<Player, GhostInfo> s_ghost in s_ghosts) { if ((Object)(object)s_ghost.Key == (Object)null || GetFight(s_ghost.Key) != s_ghost.Value.Fight) { s_gone.Add(s_ghost.Key); } } foreach (Player item in s_gone) { s_ghosts.Remove(item); } foreach (Player allPlayer in Player.GetAllPlayers()) { int fight = GetFight(allPlayer); if (fight != 0) { ZDO zDO = ((Character)allPlayer).m_nview.GetZDO(); if (!s_ghosts.TryGetValue(allPlayer, out var value)) { value = new GhostInfo { Player = allPlayer, Fight = fight, Killer = zDO.GetZDOID(KillerKey), Grave = zDO.GetVec3(GraveKey, ((Component)allPlayer).transform.position), Radius = zDO.GetFloat(RadiusKey, Plugin.LeashRadius.Value) }; s_ghosts[allPlayer] = value; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(value.Killer) : null); value.KillerChar = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null); string text = zDO.GetString(ProvokedKey, ""); if (text != value.ProvokedRaw) { value.ProvokedRaw = text; ParseProvoked(text, value.Provoked); } } } } private static int GetFight(Player p) { ZDO val = (((Object)(object)p != (Object)null && (Object)(object)((Character)p).m_nview != (Object)null) ? ((Character)p).m_nview.GetZDO() : null); if (val == null) { return 0; } return val.GetInt(FightKey, 0); } private static void ParseProvoked(string raw, HashSet<ZDOID> into) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) into.Clear(); string[] array = raw.Split(new char[1] { ';' }); foreach (string text in array) { int num = text.IndexOf(':'); if (num > 0 && long.TryParse(text.Substring(0, num), out var result) && uint.TryParse(text.Substring(num + 1), out var result2)) { into.Add(new ZDOID(result, result2)); } } } public static bool IsCalmToward(Character creature, Player player) { foreach (Fight value in s_fights.Values) { if (!((Object)(object)value.Player != (Object)(object)player) && value.Calm.Contains(creature)) { return !TryGetProvokedAfter(player, creature); } } return false; } private static bool TryGetProvokedAfter(Player player, Character creature) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) ZDO val = (((Object)(object)player != (Object)null && (Object)(object)((Character)player).m_nview != (Object)null) ? ((Character)player).m_nview.GetZDO() : null); if (val == null || (Object)(object)creature.m_nview == (Object)null || creature.m_nview.GetZDO() == null) { return false; } return ListHas(val.GetString(ProvokedKey, ""), creature.GetZDOID()); } private static bool ListHas(string raw, ZDOID id) { return (";" + raw).Contains($";{((ZDOID)(ref id)).UserID}:{((ZDOID)(ref id)).ID};"); } private static void StartFight(GhostInfo g) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) Fight fight = new Fight { Id = g.Fight, Player = g.Player, Grave = g.Grave }; s_fights[g.Fight] = fight; float value = Plugin.BystanderDistance.Value; float num = Mathf.Max(g.Radius, value) + 10f; foreach (BaseAI allInstance in BaseAI.GetAllInstances()) { Character character = allInstance.m_character; if ((Object)(object)character == (Object)null || character.IsTamed() || character.IsDead() || IsKiller(g, character) || s_alreadySent.Contains(allInstance) || Utils.DistanceXZ(((Component)character).transform.position, g.Grave) > num || (Object)(object)allInstance.m_nview == (Object)null || !allInstance.m_nview.IsOwner()) { continue; } GhostIsEnemyPatch.Bypass = true; bool num2 = BaseAI.IsEnemy(character, (Character)(object)g.Player); GhostIsEnemyPatch.Bypass = false; if (num2) { MonsterAI val = (MonsterAI)(object)((allInstance is MonsterAI) ? allInstance : null); if (val != null) { val.m_targetCreature = null; val.m_targetStatic = null; } allInstance.SetAlerted(false); Vector3 val2 = ((Component)character).transform.position - g.Grave; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.01f) { val2 = Random.insideUnitSphere; } val2.y = 0f; ((Vector3)(ref val2)).Normalize(); fight.SentAway.Add(new KeyValuePair<BaseAI, Vector3>(allInstance, allInstance.m_spawnPoint)); fight.Calm.Add(character); s_alreadySent.Add(allInstance); allInstance.m_spawnPoint = g.Grave + val2 * value; } } if (fight.SentAway.Count > 0) { Plugin.Log.LogInfo((object)$"[RevengeDeath] Sent {fight.SentAway.Count} creature(s) away from {g.Player.GetPlayerName()}'s grave"); } } private static void EndFight(Fight f) { //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) foreach (KeyValuePair<BaseAI, Vector3> item in f.SentAway) { BaseAI key = item.Key; if ((Object)(object)key != (Object)null && (Object)(object)key.m_nview != (Object)null && key.m_nview.IsOwner()) { key.m_spawnPoint = item.Value; } s_alreadySent.Remove(key); } if (f.SentAway.Count > 0) { Plugin.Log.LogInfo((object)"[RevengeDeath] Creatures sent away can roam home again"); } } private static void UpdateRemoteVisuals() { //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; foreach (GhostInfo value2 in s_ghosts.Values) { if ((Object)(object)value2.Player == (Object)(object)localPlayer) { continue; } if (!s_remoteOutlines.TryGetValue(value2.Player, out var value)) { value = new Outline(); value.Apply((Character)(object)value2.Player, value2.Player.GetPlayerName(), Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostVisuals.GhostGlowColor); s_remoteOutlines[value2.Player] = value; if ((Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)value2.Player).transform.position) < 100f) { ((Character)localPlayer).Message((MessageType)1, value2.Player.GetPlayerName() + " seeks vengeance", 0, (Sprite)null, false); } } else { value.Refresh(Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostVisuals.GhostGlowColor); } } s_removeOutlines.Clear(); foreach (KeyValuePair<Player, Outline> s_remoteOutline in s_remoteOutlines) { if ((Object)(object)s_remoteOutline.Key == (Object)null || !s_ghosts.ContainsKey(s_remoteOutline.Key)) { s_removeOutlines.Add(s_remoteOutline.Key); } } foreach (Player s_removeOutline in s_removeOutlines) { s_remoteOutlines[s_removeOutline].Remove(); s_remoteOutlines.Remove(s_removeOutline); } } public static void Update() { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null) { return; } RefreshGhosts(); foreach (GhostInfo value2 in s_ghosts.Values) { if (!s_fights.ContainsKey(value2.Fight)) { StartFight(value2); } } double timeSeconds = ZNet.instance.GetTimeSeconds(); s_finished.Clear(); foreach (Fight value3 in s_fights.Values) { if (!((Object)(object)value3.Player != (Object)null) || !s_ghosts.TryGetValue(value3.Player, out var value) || value.Fight != value3.Id) { if (!value3.Over) { value3.Over = true; ZDO val = (((Object)(object)value3.Player != (Object)null && (Object)(object)((Character)value3.Player).m_nview != (Object)null) ? ((Character)value3.Player).m_nview.GetZDO() : null); value3.CalmUntil = ((val != null) ? ((double)val.GetLong(GraceKey, 0L) / 1000.0) : 0.0); } if (timeSeconds >= value3.CalmUntil) { s_finished.Add(value3.Id); } } } foreach (int item in s_finished) { EndFight(s_fights[item]); s_fights.Remove(item); } UpdateRemoteVisuals(); } public static void Reset() { foreach (Fight value in s_fights.Values) { EndFight(value); } s_fights.Clear(); s_alreadySent.Clear(); foreach (Outline value2 in s_remoteOutlines.Values) { value2.Remove(); } s_remoteOutlines.Clear(); s_ghosts.Clear(); } } public static class GhostVisuals { private static readonly Outline s_targetOutline = new Outline(); private static readonly Outline s_ghostOutline = new Outline(); public static Sprite TargetIcon; private static StatusEffect s_template; private static StatusEffect s_status; private static Image s_vignette; private static float s_nextFarMessage; private static bool s_tensionWarned; private static float s_pulsePhase; private static float s_tensionColorBlend; private static readonly HashSet<string> s_missingEffects = new HashSet<string>(); private static StatusEffect s_spentTemplate; private static Sprite s_vignetteSprite; private static float s_vignetteReach = -1f; public static Color GhostGlowColor { get { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Color result = Plugin.GhostOutlineColor.Value * Plugin.GhostGlow.Value; result.a = 1f; return result; } } public static void Begin(Player player, Character target, string targetName) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) PlayDeathEffects(player); PlayEffect("vfx_ghost_spawn", ((Component)player).transform.position); PlayEffect("sfx_ghost_alert", ((Component)player).transform.position); s_targetOutline.Apply(target, targetName, Plugin.KillerOutlineColor.Value, Plugin.KillerOutlineWidth.Value, Color.black); s_ghostOutline.Apply((Character)(object)player, "the ghost", Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostGlowColor); AddStatusEffect(player, target, targetName); ShowVignette(); ((Character)player).Message((MessageType)2, "Avenge your death", 0, (Sprite)null, false); ((Character)player).Message((MessageType)1, "Slay " + RevengeTarget.TheName + " to rise again", 0, (Sprite)null, false); s_nextFarMessage = 0f; s_tensionWarned = false; } public static void End(Player player) { s_targetOutline.Remove(); s_ghostOutline.Remove(); if ((Object)(object)s_status != (Object)null && (Object)(object)player != (Object)null) { ((Character)player).GetSEMan().RemoveStatusEffect(s_status.NameHash(), true); } s_status = null; if ((Object)(object)s_vignette != (Object)null) { Object.Destroy((Object)(object)((Component)s_vignette).gameObject); } s_vignette = null; } public static void Tick(Player player, float timeLeft, bool inArena) { //IL_00ab: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) float value = Plugin.TensionSeconds.Value; float num = ((value > 0f && timeLeft < value) ? (1f - timeLeft / value) : 0f); if ((Object)(object)s_status != (Object)null) { s_status.m_time = Mathf.Clamp(s_status.m_ttl - timeLeft, 0f, s_status.m_ttl - 0.05f); s_status.m_flashIcon = !inArena || num > 0f; } if (num > 0f && !s_tensionWarned) { ((Character)player).Message((MessageType)2, "Your spirit is fading", 0, (Sprite)null, false); s_tensionWarned = true; } s_targetOutline.Refresh(Plugin.KillerOutlineColor.Value, Plugin.KillerOutlineWidth.Value, Color.black); s_ghostOutline.Refresh(Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostGlowColor); UpdateVignette(num); if (!inArena && Time.time >= s_nextFarMessage) { ((Character)player).Message((MessageType)2, "Return to your grave", 0, (Sprite)null, false); s_nextFarMessage = Time.time + 4f; } } public static void ShowBloodlust(Player player, float seconds) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player != (Object)null && (Object)(object)DamageText.instance != (Object)null) { DamageText.instance.ShowText((TextType)7, ((Character)player).GetTopPoint() + Vector3.up * 0.3f, $"+{Mathf.RoundToInt(seconds)}s", true); } if ((Object)(object)s_status != (Object)null) { s_status.m_isNew = true; } } public static void PlayWin(Player player) { //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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) player.m_spawnEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); ((Character)player).Message((MessageType)2, "Vengeance is yours", 0, (Sprite)null, false); } private static void AddStatusEffect(Player player, Character target, string targetName) { if ((Object)(object)s_template == (Object)null) { s_template = ScriptableObject.CreateInstance<StatusEffect>(); ((Object)s_template).name = "SE_RevengeDeath_Vengeance"; s_template.m_name = "Vengeance"; } TargetIcon = FindIcon(player, target); s_template.m_icon = TargetIcon; s_template.m_ttl = Plugin.GhostTimeout.Value; s_template.m_tooltip = RevengeTarget.TheNameCap + " took your life. Slay it before your spirit fades to rise again with your belongings."; s_status = ((Character)player).GetSEMan().AddStatusEffect(s_template, true, 0, 0f, (short)(-1)); } public static void ShowSpiritSpent(Player player, float seconds) { if (!((Object)(object)player == (Object)null) && !(seconds <= 0f)) { if ((Object)(object)s_spentTemplate == (Object)null) { s_spentTemplate = ScriptableObject.CreateInstance<StatusEffect>(); ((Object)s_spentTemplate).name = "SE_RevengeDeath_SpiritSpent"; s_spentTemplate.m_name = "Spirit spent"; s_spentTemplate.m_tooltip = "Your spirit has not yet recovered from its last return. If you fall before this fades, death will be final."; } s_spentTemplate.m_icon = WispIcon() ?? FindIcon(player, null); s_spentTemplate.m_ttl = seconds; ((Character)player).GetSEMan().AddStatusEffect(s_spentTemplate, true, 0, 0f, (short)(-1)); } } private static Sprite WispIcon() { GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("Wisp") : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if (!((Object)(object)val2 != (Object)null)) { return null; } return val2.m_itemData.GetIcon(); } private static Sprite FindIcon(Player player, Character target) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Invalid comparison between Unknown and I4 CharacterDrop val = (((Object)(object)target != (Object)null) ? ((Component)target).GetComponent<CharacterDrop>() : null); if ((Object)(object)val != (Object)null) { foreach (Drop drop in val.m_drops) { ItemDrop val2 = (((Object)(object)drop.m_prefab != (Object)null) ? drop.m_prefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 != (Object)null && (int)val2.m_itemData.m_shared.m_itemType == 13) { return val2.m_itemData.GetIcon(); } } } TombStone val3 = (((Object)(object)player.m_tombstone != (Object)null) ? player.m_tombstone.GetComponent<TombStone>() : null); if (!((Object)(object)val3 != (Object)null) || !((Object)(object)val3.m_lootStatusEffect != (Object)null)) { return null; } return val3.m_lootStatusEffect.m_icon; } private static void ShowVignette() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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) Image val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_damageScreen : null); if (!((Object)(object)val == (Object)null)) { GameObject val2 = new GameObject("RevengeGhostVignette", new Type[1] { typeof(RectTransform) }); RectTransform val3 = (RectTransform)val2.transform; ((Transform)val3).SetParent(((Component)val).transform.parent, false); val3.anchorMin = Vector2.zero; val3.anchorMax = Vector2.one; val3.offsetMin = Vector2.zero; val3.offsetMax = Vector2.zero; ((Transform)val3).SetSiblingIndex(((Component)val).transform.GetSiblingIndex()); s_vignette = val2.AddComponent<Image>(); ((Graphic)s_vignette).raycastTarget = false; s_tensionColorBlend = 0f; UpdateVignette(0f); } } private static void UpdateVignette(float tension) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: 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_0106: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)s_vignette == (Object)null)) { s_vignette.sprite = GetVignetteSprite(Plugin.EdgeReach.Value); float num = Mathf.Lerp(1.5f, 8f, tension); s_pulsePhase += Time.deltaTime * num; float num2 = Mathf.Lerp(Plugin.EdgePulse.Value, Mathf.Max(Plugin.EdgePulse.Value, 0.2f), tension); float num3 = Plugin.EdgeStrength.Value + 0.2f * tension; s_tensionColorBlend = Mathf.MoveTowards(s_tensionColorBlend, (tension > 0f) ? 1f : 0f, Time.deltaTime / 1.5f); Color color = Color.Lerp(Plugin.EdgeColor.Value, Plugin.TensionColor.Value, s_tensionColorBlend); color.a = ((Plugin.EdgeStrength.Value <= 0f) ? 0f : Mathf.Clamp01(num3 + num2 * Mathf.Sin(s_pulsePhase))); ((Graphic)s_vignette).color = color; } } private static Sprite GetVignetteSprite(float reach) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_008f: Expected O, but got Unknown //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)s_vignetteSprite != (Object)null && Mathf.Approximately(reach, s_vignetteReach)) { return s_vignetteSprite; } if ((Object)(object)s_vignetteSprite != (Object)null) { Object.Destroy((Object)(object)s_vignetteSprite.texture); Object.Destroy((Object)(object)s_vignetteSprite); } s_vignetteReach = reach; float num = Mathf.Lerp(1.25f, 0.2f, reach); float num2 = Mathf.Min(num + 0.7f, 1.6f); Texture2D val = new Texture2D(256, 256, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; Color32[] array = (Color32[])(object)new Color32[65536]; for (int i = 0; i < 256; i++) { for (int j = 0; j < 256; j++) { float num3 = ((float)j + 0.5f) / 256f * 2f - 1f; float num4 = ((float)i + 0.5f) / 256f * 2f - 1f; float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4); float num6 = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(num, num2, num5)); array[i * 256 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num6 * 255f)); } } val.SetPixels32(array); val.Apply(); s_vignetteSprite = Sprite.Create(val, new Rect(0f, 0f, 256f, 256f), new Vector2(0.5f, 0.5f)); return s_vignetteSprite; } private static void PlayDeathEffects(Player player) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) List<EffectData> list = new List<EffectData>(); EffectData[] effectPrefabs = ((Character)player).m_deathEffects.m_effectPrefabs; foreach (EffectData val in effectPrefabs) { if (val.m_enabled && (Object)(object)val.m_prefab != (Object)null && (Object)(object)val.m_prefab.GetComponent<Ragdoll>() == (Object)null) { list.Add(val); } } new EffectList { m_effectPrefabs = list.ToArray() }.Create(((Component)player).transform.position, ((Component)player).transform.rotation, ((Component)player).transform, 1f, -1, default(ZDOID)); } private static void PlayEffect(string prefabName, Vector3 position) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabName) : null); if ((Object)(object)val == (Object)null) { if (s_missingEffects.Add(prefabName)) { Plugin.Log.LogWarning((object)("[RevengeDeath] Effect '" + prefabName + "' not found, skipping it")); } } else { EffectList val2 = new EffectList(); val2.m_effectPrefabs = (EffectData[])(object)new EffectData[1] { new EffectData { m_prefab = val } }; val2.Create(position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID)); } } } public static class Grudge { private static readonly int NameKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_GrudgeName"); private static readonly int WinsKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_GrudgeWins"); public static readonly int MarkedKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Marked"); private static readonly string[] Epithets = new string[15] { "Gravemaker", "Bonebreaker", "Skullsplitter", "Soulreaver", "Widowmaker", "Oathbreaker", "Deathbringer", "Hearthbane", "Ravenfeeder", "Shieldbreaker", "Corpsemaker", "Dreadborn", "Unforgiven", "Wraithmaker", "Spiritbane" }; public static string GetName(Character c) { ZDO val = (((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null) ? c.m_nview.GetZDO() : null); if (val == null) { return ""; } return val.GetString(NameKey, ""); } public static bool HasGrudge(Character c) { return !string.IsNullOrEmpty(GetName(c)); } public static void Apply(Character enemy, Player player) { if (!Plugin.GrudgeEnabled.Value || (Object)(object)enemy == (Object)null || enemy.IsDead() || enemy.IsBoss()) { return; } ZNetView nview = enemy.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } if (!nview.IsOwner()) { nview.ClaimOwnership(); } if (nview.IsOwner()) { ZDO zDO = nview.GetZDO(); int num = zDO.GetInt(WinsKey, 0) + 1; zDO.Set(WinsKey, num, false); zDO.Set(MarkedKey, true); string text = zDO.GetString(NameKey, ""); if (string.IsNullOrEmpty(text)) { text = enemy.GetHoverName() + " the " + Epithets[Random.Range(0, Epithets.Length)]; zDO.Set(NameKey, text); } int num2 = Plugin.GrudgeMaxStars.Value + 1; bool flag = enemy.GetLevel() < num2; if (flag) { enemy.SetLevel(enemy.GetLevel() + 1); enemy.SetHealth(enemy.GetMaxHealth()); } if ((Object)(object)player != (Object)null) { ((Character)player).Message((MessageType)1, flag ? (text + " grows stronger") : (text + " remembers you"), 0, (Sprite)null, false); } Plugin.Log.LogInfo((object)$"[RevengeDeath] Grudge: {text} has beaten a player {num} time(s), now level {enemy.GetLevel()}"); } } public static void OnCreatureDied(Character c) { //IL_003f: 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) string name = GetName(c); if (!string.IsNullOrEmpty(name)) { Plugin.Log.LogInfo((object)("[RevengeDeath] Grudge settled: " + name + " died")); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)c).transform.position) < 50f) { ((Character)localPlayer).Message((MessageType)1, name + " has fallen. The grudge is settled", 0, (Sprite)null, false); } } } } public static class Spoils { private static SE_Stats s_vengeful; public static void AddTrophy(CharacterDrop drop, List<KeyValuePair<GameObject, int>> list) { if (!Plugin.SpoilsTrophy.Value || (Object)(object)drop == (Object)null || (Object)(object)drop.m_character == (Object)null) { return; } ZDO val = (((Object)(object)drop.m_character.m_nview != (Object)null) ? drop.m_character.m_nview.GetZDO() : null); if ((val == null || !val.GetBool(Grudge.MarkedKey, false)) && !RevengeTarget.IsTarget(drop.m_character)) { return; } GameObject val2 = FindTrophy(drop); if ((Object)(object)val2 == (Object)null) { return; } foreach (KeyValuePair<GameObject, int> item in list) { if ((Object)(object)item.Key == (Object)(object)val2) { return; } } list.Add(new KeyValuePair<GameObject, int>(val2, 1)); Plugin.Log.LogInfo((object)("[RevengeDeath] Spoils: guaranteed " + ((Object)val2).name)); } public static GameObject FindTrophy(CharacterDrop drop) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 if ((Object)(object)drop == (Object)null) { return null; } foreach (Drop drop2 in drop.m_drops) { ItemDrop val = (((Object)(object)drop2.m_prefab != (Object)null) ? drop2.m_prefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val != (Object)null && (int)val.m_itemData.m_shared.m_itemType == 13) { return drop2.m_prefab; } } return null; } public static void GiveVengeful(Player player, Sprite icon) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) float value = Plugin.VengefulDuration.Value; if (!((Object)(object)player == (Object)null) && !(value <= 0f)) { if ((Object)(object)s_vengeful == (Object)null) { s_vengeful = ScriptableObject.CreateInstance<SE_Stats>(); ((Object)s_vengeful).name = "SE_RevengeDeath_Vengeful"; ((StatusEffect)s_vengeful).m_name = "Vengeful"; ((StatusEffect)s_vengeful).m_tooltip = "The blood of your killer still burns in you."; s_vengeful.m_modifyAttackSkill = (SkillType)999; } ((StatusEffect)s_vengeful).m_icon = icon; ((StatusEffect)s_vengeful).m_ttl = value; s_vengeful.m_damageModifier = 1f + Plugin.VengefulDamage.Value; ((Character)player).GetSEMan().AddStatusEffect((StatusEffect)(object)s_vengeful, true, 0, 0f, (short)(-1)); } } } public class Outline { private static readonly int EmissionId = Shader.PropertyToID("_EmissionColor"); private Color m_color; private float m_thickness; private Color m_glow; private Character m_target; private string m_label; private readonly List<GameObject> m_hulls = new List<GameObject>(); private readonly List<Mesh> m_meshes = new List<Mesh>(); private GameObject m_glowTarget; private Material m_material; public void Apply(Character target, string label, Color color, float thickness, Color glow, bool log = true) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) Remove(); m_target = target; m_label = label; m_color = color; m_thickness = thickness; m_glow = glow; if ((Object)(object)target == (Object)null) { return; } int num = 0; if (thickness > 0f) { num = TryAddHulls(target); if (num > 0) { if (log) { Plugin.Log.LogInfo((object)$"[RevengeDeath] Outline added to {label} ({num} mesh(es))"); } } else if (glow == Color.black) { glow = color * 0.8f; if (log) { Plugin.Log.LogInfo((object)("[RevengeDeath] Outline not possible for " + label + ", using a glow instead")); } } } if (glow != Color.black) { MaterialMan.instance.SetValue<Color>(((Component)target).gameObject, EmissionId, glow, true); m_glowTarget = ((Component)target).gameObject; } } public void Refresh(Color color, float thickness, Color glow) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)m_target == (Object)null) { return; } if (thickness != m_thickness || glow != m_glow) { Apply(m_target, m_label, color, thickness, glow, log: false); } else if (color != m_color) { m_color = color; if ((Object)(object)m_material != (Object)null) { m_material.color = color; } } } public void Remove() { m_target = null; foreach (GameObject hull in m_hulls) { if ((Object)(object)hull != (Object)null) { Object.Destroy((Object)(object)hull); } } foreach (Mesh mesh in m_meshes) { if ((Object)(object)mesh != (Object)null) { Object.Destroy((Object)(object)mesh); } } m_hulls.Clear(); m_meshes.Clear(); if ((Object)(object)m_glowTarget != (Object)null) { MaterialMan.instance.ResetValue(m_glowTarget, EmissionId); } m_glowTarget = null; } private int TryAddHulls(Character target) { //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) Material material = GetMaterial(); if ((Object)(object)material == (Object)null) { return 0; } HashSet<Renderer> hashSet = new HashSet<Renderer>(); LODGroup componentInChildren = ((Component)target).GetComponentInChildren<LODGroup>(); if ((Object)(object)componentInChildren != (Object)null) { LOD[] lODs = componentInChildren.GetLODs(); HashSet<Renderer> hashSet2 = new HashSet<Renderer>((IEnumerable<Renderer>)((lODs.Length != 0) ? ((Array)lODs[0].renderers) : ((Array)new Renderer[0]))); for (int i = 1; i < lODs.Length; i++) { Renderer[] renderers = lODs[i].renderers; foreach (Renderer val in renderers) { if ((Object)(object)val != (Object)null && !hashSet2.Contains(val)) { hashSet.Add(val); } } } } int num = 0; SkinnedMeshRenderer[] componentsInChildren = ((Component)target).GetComponentsInChildren<SkinnedMeshRenderer>(false); foreach (SkinnedMeshRenderer val2 in componentsInChildren) { if (!((Renderer)val2).enabled || hashSet.Contains((Renderer)(object)val2) || ((Object)((Component)val2).gameObject).name == "RevengeOutline") { continue; } Mesh sharedMesh = val2.sharedMesh; if ((Object)(object)sharedMesh == (Object)null || !sharedMesh.isReadable) { continue; } Vector3[] vertices = sharedMesh.vertices; Vector3[] normals = sharedMesh.normals; if (vertices.Length == 0 || normals.Length != vertices.Length) { continue; } Bounds bounds = sharedMesh.bounds; Vector3 size = ((Bounds)(ref bounds)).size; float magnitude = ((Vector3)(ref size)).magnitude; bounds = ((Renderer)val2).bounds; size = ((Bounds)(ref bounds)).size; float magnitude2 = ((Vector3)(ref size)).magnitude; if (!(magnitude <= 0f) && !(magnitude2 <= 0f)) { float num2 = m_thickness * magnitude / magnitude2; for (int k = 0; k < vertices.Length; k++) { ref Vector3 reference = ref vertices[k]; reference += normals[k] * num2; } Mesh val3 = Object.Instantiate<Mesh>(sharedMesh); val3.vertices = vertices; m_meshes.Add(val3); GameObject val4 = new GameObject("RevengeOutline"); val4.transform.SetParent(((Component)val2).transform.parent, false); val4.transform.localPosition = ((Component)val2).transform.localPosition; val4.transform.localRotation = ((Component)val2).transform.localRotation; val4.transform.localScale = ((Component)val2).transform.localScale; SkinnedMeshRenderer val5 = val4.AddComponent<SkinnedMeshRenderer>(); val5.sharedMesh = val3; val5.bones = val2.bones; val5.rootBone = val2.rootBone; ((Renderer)val5).localBounds = ((Renderer)val2).localBounds; val5.updateWhenOffscreen = val2.updateWhenOffscreen; ((Renderer)val5).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)val5).receiveShadows = false; Material[] array = (Material[])(object)new Material[val3.subMeshCount]; for (int l = 0; l < array.Length; l++) { array[l] = material; } ((Renderer)val5).sharedMaterials = array; m_hulls.Add(val4); num++; } } return num; } private Material GetMaterial() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown if ((Object)(object)m_material != (Object)null) { m_material.color = m_color; return m_material; } Shader val = Shader.Find("Hidden/Internal-Colored"); if ((Object)(object)val == (Object)null) { return null; } m_material = new Material(val) { color = m_color }; m_material.SetInt("_Cull", 1); m_material.SetInt("_ZWrite", 1); m_material.SetInt("_ZTest", 4); m_material.SetInt("_SrcBlend", 1); m_material.SetInt("_DstBlend", 0); return m_material; } } [HarmonyPatch(typeof(Player), "OnDeath")] public static class PlayerOnDeathPatch { private static bool Prefix(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !((Character)__instance).m_nview.IsOwner()) { return true; } Plugin.Log.LogInfo((object)("[RevengeDeath] Player.OnDeath fired for " + __instance.GetPlayerName())); if (GhostState.ConsumeForceVanilla()) { return true; } if (GhostState.Active) { GhostState.EndAsLoss("ghost was killed"); return true; } if (RevengeTarget.Capture(__instance)) { GhostState.Enter(__instance); return false; } return true; } } [HarmonyPatch(typeof(Game), "Shutdown")] public static class GhostLogoutPatch { private static void Prefix(Game __instance) { if (!__instance.m_shuttingDown) { if (GhostState.Active) { GhostState.DieNow("logged out or quit as a ghost"); } GhostSync.Reset(); } } } [HarmonyPatch(typeof(Player), "OnSpawned")] public static class SpiritSpentOnSpawnPatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { double num = GhostState.CooldownLeft(__instance); if (num > 0.0) { GhostVisuals.ShowSpiritSpent(__instance, (float)num); } } } } [HarmonyPatch(typeof(BaseAI), "MoveAwayAndDespawn")] public static class TargetNoFleePatch { private static bool Prefix(BaseAI __instance) { if (GhostState.Active) { return !RevengeTarget.IsTarget(__instance.m_character); } return true; } } [HarmonyPatch(typeof(Character), "OnDeath")] public static class TargetOnDeathPatch { private static void Prefix(Character __instance) { if (RevengeTarget.IsTarget(__instance)) { RevengeTarget.Killed = true; Plugin.Log.LogInfo((object)("[RevengeDeath] Target " + RevengeTarget.Name + " died")); } Grudge.OnCreatureDied(__instance); } } [HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[] { typeof(Character), typeof(Character) })] public static class GhostIsEnemyPatch { public static bool Bypass; private static void Postfix(Character a, Character b, ref bool __result) { if (!__result || Bypass || !GhostSync.Any) { return; } GhostInfo info2; if (GhostSync.TryGet(a, out var info)) { if (GhostSync.IsKiller(info, b) && !TargetHudAlwaysPatch.DrawingBars) { __result = GhostSync.CanFight(info); } } else if (GhostSync.TryGet(b, out info2)) { if (GhostSync.IsKiller(info2, a)) { __result = GhostSync.CanFight(info2); } else if (!GhostSync.IsProvoked(info2, a)) { __result = false; } } else { Player val = (Player)(object)((b is Player) ? b : null); if (val != null && GhostSync.IsCalmToward(a, val)) { __result = false; } } } } [HarmonyPatch(typeof(Character), "Damage")] public static class PlayerHitPatch { private static void Prefix(Character __instance, HitData hit) { //IL_0019: 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) Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance == (Object)(object)localPlayer) && !(hit.m_attacker != ((Character)localPlayer).GetZDOID())) { if (RevengeTarget.IsTarget(__instance)) { GhostState.OnGhostHitTarget(); } else if (!__instance.IsPlayer()) { GhostSync.OnLocalPlayerHit(__instance); } } } } [HarmonyPatch(typeof(Character), "RPC_Damage")] public static class GhostDamagePatch { private static bool Prefix(Character __instance, HitData hit) { if (!GhostSync.Any) { return true; } Character attacker = hit.GetAttacker(); if (GhostSync.TryGet(__instance, out var info)) { if ((Object)(object)attacker == (Object)null) { return true; } if (attacker.IsPlayer()) { return false; } if (GhostSync.IsKiller(info, attacker)) { return GhostSync.CanFight(info); } return GhostSync.IsProvoked(info, attacker); } if (__instance.IsPlayer() && (Object)(object)attacker != (Object)null && GhostSync.IsGhost(attacker)) { return false; } return true; } } [HarmonyPatch(typeof(Character), "GetHoverName")] public static class TargetNamePatch { private static void Postfix(Character __instance, ref string __result) { string name = Grudge.GetName(__instance); if (!string.IsNullOrEmpty(name)) { __result = name; } if (GhostState.Active && RevengeTarget.IsTarget(__instance)) { __result = "<color=#ff4040>" + __result + "</color>"; } } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] public static class SpoilsTrophyPatch { private static void Postfix(CharacterDrop __instance, List<KeyValuePair<GameObject, int>> __result) { Spoils.AddTrophy(__instance, __result); } } [HarmonyPatch(typeof(EnemyHud), "TestShow")] public static class TargetHudRangePatch { private static void Postfix(Character c, ref bool __result) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!__result && GhostState.Active && RevengeTarget.IsTarget(c) && !((Object)(object)Player.m_localPlayer == (Object)null)) { float num = Plugin.LeashRadius.Value * 2f; Vector3 val = ((Component)c).transform.position - ((Component)Player.m_localPlayer).transform.position; __result = ((Vector3)(ref val)).sqrMagnitude < num * num; } } } [HarmonyPatch(typeof(EnemyHud), "UpdateHuds")] public static class TargetHudAlwaysPatch { public static bool DrawingBars; private static void Prefix(EnemyHud __instance) { DrawingBars = true; if (GhostState.Active && (Object)(object)RevengeTarget.Enemy != (Object)null && __instance.m_huds.TryGetValue(RevengeTarget.Enemy, out var value)) { value.m_hoverTimer = 0f; } } private static Exception Finalizer(Exception __exception) { DrawingBars = false; return __exception; } } [HarmonyPatch(typeof(Character), "Heal")] public static class NoGhostHealPatch { private static bool Prefix(Character __instance) { return !GhostState.IsGhost(__instance); } } [HarmonyPatch(typeof(Humanoid), "Pickup")] public static class NoGhostPickupPatch { private static bool Prefix(Humanoid __instance, ref bool __result) { if (!GhostState.IsGhost((Character)(object)__instance)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(Player), "AutoPickup")] public static class NoGhostAutoPickupPatch { private static bool Prefix(Player __instance) { return !GhostState.IsGhost((Character)(object)__instance); } } [HarmonyPatch(typeof(TombStone), "Interact")] public static class NoGhostTombstonePatch { private static bool Prefix(Humanoid character, ref bool __result) { if (!GhostState.IsGhost((Character)(object)character)) { return true; } ((Character)character).Message((MessageType)2, "A spirit cannot carry anything", 0, (Sprite)null, false); __result = false; return false; } } [HarmonyPatch(typeof(Player), "TeleportTo")] public static class NoGhostTeleportPatch { private static bool Prefix(Player __instance, ref bool __result) { if (!GhostState.IsGhost((Character)(object)__instance)) { return true; } ((Character)__instance).Message((MessageType)2, "A spirit cannot travel", 0, (Sprite)null, false); __result = false; return false; } } [BepInPlugin("com.siddiq.vengeancesystem", "The Vengeance System", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.siddiq.vengeancesystem"; public const string Name = "The Vengeance System"; public const string Version = "1.0.0"; internal static ManualLogSource Log; private Harmony _harmony; internal static ConfigEntry<bool> ModEnabled; internal static ConfigEntry<float> LeashRadius; internal static ConfigEntry<float> GhostTimeout; internal static ConfigEntry<float> GhostHealth; internal static ConfigEntry<bool> AllowBosses; internal static ConfigEntry<float> RevengeCooldown; internal static ConfigEntry<float> ReviveHealth; internal static ConfigEntry<float> BloodlustSeconds; internal static ConfigEntry<float> TensionSeconds; internal static ConfigEntry<bool> SkipAtSea; internal static ConfigEntry<float> SeaDepth; internal static ConfigEntry<bool> GrudgeEnabled; internal static ConfigEntry<int> GrudgeMaxStars; internal static ConfigEntry<bool> SpoilsTrophy; internal static ConfigEntry<float> VengefulDuration; internal static ConfigEntry<float> VengefulDamage; internal static ConfigEntry<float> BystanderDistance; internal static ConfigEntry<float> WinGracePeriod; internal static ConfigEntry<Color> EdgeColor; internal static ConfigEntry<float> EdgeStrength; internal static ConfigEntry<float> EdgeReach; internal static ConfigEntry<float> EdgePulse; internal static ConfigEntry<Color> TensionColor; internal static ConfigEntry<Color> KillerOutlineColor; internal static ConfigEntry<float> KillerOutlineWidth; internal static ConfigEntry<Color> GhostOutlineColor; internal static ConfigEntry<float> GhostOutlineWidth; internal static ConfigEntry<float> GhostGlow; private void Awake() { //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Expected O, but got Unknown //IL_02ec: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03f5: Unknown result type (might be due to invalid IL or missing references) //IL_0460: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04ea: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the revenge death mechanic on or off."); LeashRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "LeashRadius", 30f, "Size (meters) of the fight area around your grave. You and your killer can only hurt each other inside it; a killer that strays out walks back (and is pulled back if it goes far)."); GhostTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("General", "GhostTimeout", 120f, "Seconds you have as a ghost to kill the target before the normal respawn."); GhostHealth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "GhostHealth", 50f, "Health of the ghost. If it hits zero, normal respawn."); AllowBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowBosses", false, "Allow revenge fights when a boss kills you."); RevengeCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RevengeCooldown", 300f, "After winning a revenge fight, your spirit is spent for this many seconds: dying again in that time is a normal death. 0 = no cooldown."); ReviveHealth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ReviveHealth", 0.5f, Ranged("Health you come back with after a win, as a fraction of max health (0.5 = half). Never less than the ghost had.", 0.05f, 1f)); BloodlustSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BloodlustSeconds", 5f, Ranged("Seconds added to the ghost timer each time you hit your killer (never above GhostTimeout). 0 = off.", 0f, 30f)); TensionSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "TensionSeconds", 15f, Ranged("In the last this-many seconds the screen edge pulses like a heartbeat and the icon flashes. 0 = off.", 0f, 60f)); SkipAtSea = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SkipAtSea", true, "Dying at sea (swimming or on a boat over deep water) is always a normal death."); SeaDepth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SeaDepth", 4f, "How deep (meters) the water under you must be to count as 'at sea'."); BystanderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Bystanders", "SendAwayDistance", 40f, "Other creatures near the grave (not your killer) wander off to about this far (meters) from the grave during the fight."); WinGracePeriod = ((BaseUnityPlugin)this).Config.Bind<float>("Bystanders", "WinGracePeriod", 30f, "Seconds after a win that the creatures sent away keep ignoring you, so they don't swarm you right away."); GrudgeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Grudge and Spoils", "Grudge", true, "If you lose a revenge fight, your killer gains a star, heals up and earns a name."); GrudgeMaxStars = ((BaseUnityPlugin)this).Config.Bind<int>("Grudge and Spoils", "GrudgeMaxStars", 2, new ConfigDescription("Most stars a grudge can give a creature (the game's normal maximum is 2).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>())); SpoilsTrophy = ((BaseUnityPlugin)this).Config.Bind<bool>("Grudge and Spoils", "GuaranteedTrophy", true, "Your killer (and any grudge creature) always drops its trophy when it dies."); VengefulDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Grudge and Spoils", "VengefulDuration", 60f, "Seconds of the 'Vengeful' damage boost after winning a revenge fight. 0 = off."); VengefulDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Grudge and Spoils", "VengefulDamage", 0.2f, Ranged("Extra damage while Vengeful (0.2 = +20%).", 0f, 1f)); EdgeColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Screen edge", "Color", new Color(0.55f, 0.8f, 1f, 1f), "Color of the frost around the screen edges while you're a ghost."); EdgeStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Screen edge", "Strength", 0.23f, Ranged("How strong the colored edge is. 0 = off.", 0f, 1f)); EdgeReach = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Screen edge", "Reach", 0.25f, Ranged("How far the edge reaches toward the middle. 0 = only the very corners, 1 = most of the screen.", 0f, 1f)); EdgePulse = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Screen edge", "Pulse", 0.08f, Ranged("How much the edge slowly brightens and fades. 0 = steady.", 0f, 0.3f)); TensionColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Screen edge", "TensionColor", new Color(0.85f, 0.1f, 0.1f, 1f), "Color the edge turns in the last seconds (see TensionSeconds)."); KillerOutlineColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Outlines", "KillerColor", new Color(1f, 0.1f, 0.1f, 1f), "Outline color of the creature that killed you."); KillerOutlineWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Outlines", "KillerWidth", 0.015f, Ranged("Outline width (meters, roughly) on your killer. 0 = no outline.", 0f, 0.1f)); GhostOutlineColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Outlines", "GhostColor", new Color(0.55f, 0.85f, 1f, 1f), "Outline (and glow) color on you while you're a ghost."); GhostOutlineWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Outlines", "GhostWidth", 0.017f, Ranged("Outline width (meters, roughly) on you as a ghost. 0 = no outline.", 0f, 0.1f)); GhostGlow = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Outlines", "GhostGlow", 0.35f, Ranged("How much your body glows in the ghost color. 0 = no glow.", 0f, 1f)); _harmony = new Harmony("com.siddiq.vengeancesystem"); _harmony.PatchAll(); Log.LogInfo((object)"[RevengeDeath] loaded"); } private static ConfigDescription Ranged(string text, float min, float max) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown return new ConfigDescription(text, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()); } private void Update() { GhostState.Update(Time.deltaTime); GhostSync.Update(); } private void OnDestroy() { GhostState.Cleanup(); GhostSync.Reset(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public enum TargetStatus { Alive, Killed, Despawned } public static class RevengeTarget { public static Character Enemy; public static ZDOID EnemyId; public static string Name = ""; public static string TheName = ""; public static Vector3 DeathPosition; public static bool HasTarget; public static bool Killed; private static bool s_homeSet; private static Vector3 s_originalHome; private static bool s_weOwnIt; public static string TheNameCap { get { if (TheName.Length <= 0) { return TheName; } return char.ToUpper(TheName[0]) + TheName.Substring(1); } } public static bool IsTarget(Character c) { if (HasTarget && (Object)(object)c != (Object)null) { return (Object)(object)c == (Object)(object)Enemy; } return false; } public static TargetStatus GetStatus() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (Killed) { return TargetStatus.Killed; } if ((Object)(object)Enemy != (Object)null) { if (!Enemy.IsDead() && !(Enemy.GetHealth() <= 0f)) { return TargetStatus.Alive; } return TargetStatus.Killed; } if (ZDOMan.instance.GetZDO(EnemyId) != null) { return TargetStatus.Despawned; } if (!s_weOwnIt) { return TargetStatus.Killed; } return TargetStatus.Despawned; } public static float DistanceFromGrave() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Enemy == (Object)null) { return -1f; } return Utils.DistanceXZ(((Component)Enemy).transform.position, DeathPosition); } public static bool InArena() { float num = DistanceFromGrave(); if (num >= 0f) { return num <= Plugin.LeashRadius.Value; } return false; } public static bool Capture(Player player) { //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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) Clear(); DeathPosition = ((Component)player).transform.position; if (!Plugin.ModEnabled.Value) { return Reject("mod disabled in config"); } HitData lastHit = ((Character)player).m_lastHit; if (lastHit == null) { return Reject("no last hit recorded"); } Character attacker = lastHit.GetAttacker(); Plugin.Log.LogInfo((object)string.Format("[RevengeDeath] Death at {0}, hit type {1}, attacker: {2}", DeathPosition, lastHit.m_hitType, ((Object)(object)attacker != (Object)null) ? (attacker.GetHoverName() + " (" + attacker.m_name + ")") : "none")); if ((Object)(object)attacker == (Object)null) { return Reject("no attacker (environment death or attacker gone)"); } if (attacker.IsPlayer()) { return Reject("killed by a player"); } if (attacker.IsTamed()) { return Reject("killed by a tame"); } if (attacker.IsDead() || attacker.GetHealth() <= 0f) { return Reject("killer died at the same moment"); } if (attacker.IsBoss() && !Plugin.AllowBosses.Value) { return Reject("killer is a boss and bosses are disabled in config"); } if (Plugin.SkipAtSea.Value && IsAtSea(DeathPosition)) { ((Character)player).Message((MessageType)1, "The sea claims your spirit", 0, (Sprite)null, false); return Reject("died at sea"); } double num = GhostState.CooldownLeft(player); if (num > 0.0) { ((Character)player).Message((MessageType)1, "Your spirit is too weak to linger", 0, (Sprite)null, false); return Reject($"spirit spent ({num:0}s of cooldown left)"); } Enemy = attacker; EnemyId = lastHit.m_attacker; Name = attacker.GetHoverName(); TheName = (Grudge.HasGrudge(attacker) ? Name : ("the " + Name)); HasTarget = true; Plugin.Log.LogInfo((object)$"[RevengeDeath] Revenge target set: {Name} (id {EnemyId})"); return true; } public static void ControlTick(Player ghost, bool canFight) { //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Enemy == (Object)null) { return; } ZNetView nview = Enemy.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } if (!nview.IsOwner()) { nview.ClaimOwnership(); } s_weOwnIt = nview.IsOwner(); if (!s_weOwnIt) { return; } BaseAI baseAI = Enemy.GetBaseAI(); if (!nview.GetZDO().GetBool(Grudge.MarkedKey, false)) { nview.GetZDO().Set(Grudge.MarkedKey, true); } if (!s_homeSet && (Object)(object)baseAI != (Object)null) { s_originalHome = baseAI.m_spawnPoint; baseAI.m_spawnPoint = DeathPosition; s_homeSet = true; Plugin.Log.LogInfo((object)("[RevengeDeath] " + Name + "'s home point moved to the grave")); } float num = DistanceFromGrave(); if (num > Plugin.LeashRadius.Value * 1.5f) { ((Component)Enemy).transform.position = DeathPosition; if ((Object)(object)Enemy.m_body != (Object)null) { Enemy.m_body.position = DeathPosition; Enemy.m_body.linearVelocity = Vector3.zero; } Plugin.Log.LogInfo((object)$"[RevengeDeath] {Name} was {num:0}m away, teleported back to the grave"); } MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null); if (val == null) { return; } if (canFight) { if ((Object)(object)val.m_targetCreature != (Object)(object)ghost) { val.m_targetCreature = (Character)(object)ghost; val.m_targetStatic = null; } if (!((BaseAI)val).IsAlerted()) { ((BaseAI)val).SetAlerted(true); } } else if ((Object)(object)val.m_targetCreature == (Object)(object)ghost) { val.m_targetCreature = null; } } private static void ReleaseControl() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) if (s_homeSet && (Object)(object)Enemy != (Object)null && (Object)(object)Enemy.m_nview != (Object)null && Enemy.m_nview.IsOwner()) { BaseAI baseAI = Enemy.GetBaseAI(); if ((Object)(object)baseAI != (Object)null) { baseAI.m_spawnPoint = s_originalHome; } } s_homeSet = false; } public static void Clear() { //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) ReleaseControl(); Enemy = null; EnemyId = ZDOID.None; Name = ""; TheName = ""; HasTarget = false; Killed = false; s_weOwnIt = false; } private static bool IsAtSea(Vector3 pos) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) float waterLevel = ZoneSystem.instance.m_waterLevel; if (ZoneSystem.instance.GetGroundHeight(pos) < waterLevel - Plugin.SeaDepth.Value) { return pos.y < waterLevel + 10f; } return false; } private static bool Reject(string reason) { Plugin.Log.LogInfo((object)("[RevengeDeath] No revenge: " + reason + " -> vanilla respawn")); return false; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }