diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs index f9360a879..57762bad9 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs @@ -585,7 +585,7 @@ namespace Server.Engines.CannedEvil } } - int mobSubLevel = GetSubLevelfor (m) + 1; + int mobSubLevel = GetSubLevelfor(m) + 1; if (mobSubLevel >= 0) { @@ -604,12 +604,10 @@ namespace Server.Engines.CannedEvil m.SendLocalizedMessage(1054030); // You have gained in Valor! } - //No delay on Valor gains + // No delay on Valor gains } - ChampionTitleInfo info = pm.ChampionTitles; - - info.Award(m_Type, mobSubLevel); + pm.ChampionTitles.Award(m_Type, mobSubLevel); } } } diff --git a/Projects/UOContent/Engines/Virtues/Compassion.cs b/Projects/UOContent/Engines/Virtues/Compassion.cs index b80351d33..977d04cd6 100644 --- a/Projects/UOContent/Engines/Virtues/Compassion.cs +++ b/Projects/UOContent/Engines/Virtues/Compassion.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using Server.Mobiles; namespace Server @@ -18,25 +19,19 @@ namespace Server from.SendLocalizedMessage(1053001); // This virtue is not activated through the virtue menu. } - public static void CheckAtrophy(Mobile from) - { - if (from is not PlayerMobile pm) - { - return; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ShouldAtrophy(PlayerMobile pm) => pm.LastCompassionLoss + LossDelay < Core.Now; - try + public static void CheckAtrophy(PlayerMobile pm) + { + if (ShouldAtrophy(pm)) { - if (pm.LastCompassionLoss + LossDelay < Core.Now) + if (VirtueHelper.Atrophy(pm, VirtueName.Compassion, LossAmount)) { - VirtueHelper.Atrophy(from, VirtueName.Compassion, LossAmount); - // OSI has no cliloc message for losing compassion. Weird. - pm.LastCompassionLoss = Core.Now; + pm.SendLocalizedMessage(1114420); // You have lost some Compassion. } - } - catch - { - // ignored + + pm.LastCompassionLoss = Core.Now; } } } diff --git a/Projects/UOContent/Engines/Virtues/Honor.cs b/Projects/UOContent/Engines/Virtues/Honor.cs index 77b0040c7..de1636ac4 100644 --- a/Projects/UOContent/Engines/Virtues/Honor.cs +++ b/Projects/UOContent/Engines/Virtues/Honor.cs @@ -68,20 +68,13 @@ namespace Server public static void ActivateEmbrace(PlayerMobile pm) { var duration = GetHonorDuration(pm); - int usedPoints; - if (pm.Virtues.Honor < 4399) + int usedPoints = pm.Virtues.Honor switch { - usedPoints = 400; - } - else if (pm.Virtues.Honor < 10599) - { - usedPoints = 600; - } - else - { - usedPoints = 1000; - } + < 4399 => 400, + < 10599 => 600, + _ => 1000 + }; VirtueHelper.Atrophy(pm, VirtueName.Honor, usedPoints); diff --git a/Projects/UOContent/Engines/Virtues/Justice.cs b/Projects/UOContent/Engines/Virtues/Justice.cs index 7d6b6a900..d5779737d 100644 --- a/Projects/UOContent/Engines/Virtues/Justice.cs +++ b/Projects/UOContent/Engines/Virtues/Justice.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using Server.Gumps; using Server.Mobiles; using Server.Network; @@ -6,7 +7,7 @@ using Server.Targeting; namespace Server { -#pragma warning disable CA1052 +#pragma warning disable CA1052 // Cannot be static because its used as a generic for CanBeginAction. public class JusticeVirtue { private const int LossAmount = 950; @@ -71,14 +72,13 @@ namespace Server public static void OnVirtueTargeted(Mobile from, object obj) { - var protector = from as PlayerMobile; - var pm = obj as PlayerMobile; - - if (protector == null) + if (from is not PlayerMobile protector) { return; } + var pm = obj as PlayerMobile; + if (!VirtueHelper.IsSeeker(protector, VirtueName.Justice)) { protector.SendLocalizedMessage(1049610); // You must reach the first path in this virtue to invoke it. @@ -175,28 +175,20 @@ namespace Server } } - public static void CheckAtrophy(Mobile from) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ShouldAtrophy(PlayerMobile pm) => pm.LastJusticeLoss + LossDelay < Core.Now; + + + public static void CheckAtrophy(PlayerMobile pm) { - if (from is not PlayerMobile pm) + if (ShouldAtrophy(pm)) { - return; - } - - try - { - if (pm.LastJusticeLoss + LossDelay < Core.Now) + if (VirtueHelper.Atrophy(pm, VirtueName.Justice, LossAmount)) { - if (VirtueHelper.Atrophy(from, VirtueName.Justice, LossAmount)) - { - from.SendLocalizedMessage(1049373); // You have lost some Justice. - } - - pm.LastJusticeLoss = Core.Now; + pm.SendLocalizedMessage(1049373); // You have lost some Justice. } - } - catch - { - // ignored + + pm.LastJusticeLoss = Core.Now; } } } diff --git a/Projects/UOContent/Engines/Virtues/Sacrifice.cs b/Projects/UOContent/Engines/Virtues/Sacrifice.cs index 8679a0b06..500970016 100644 --- a/Projects/UOContent/Engines/Virtues/Sacrifice.cs +++ b/Projects/UOContent/Engines/Virtues/Sacrifice.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using Server.Gumps; using Server.Mobiles; using Server.Network; @@ -36,31 +37,22 @@ namespace Server } } - public static void CheckAtrophy(Mobile from) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ShouldAtrophy(PlayerMobile pm) => pm.LastSacrificeLoss + LossDelay < Core.Now; + + public static void CheckAtrophy(PlayerMobile pm) { - if (from is not PlayerMobile pm) + if (ShouldAtrophy(pm)) { - return; - } - - try - { - if (pm.LastSacrificeLoss + LossDelay < Core.Now) + if (VirtueHelper.Atrophy(pm, VirtueName.Sacrifice, LossAmount)) { - if (VirtueHelper.Atrophy(from, VirtueName.Sacrifice, LossAmount)) - { - from.SendLocalizedMessage(1052041); // You have lost some Sacrifice. - } - - var level = VirtueHelper.GetLevel(from, VirtueName.Sacrifice); - - pm.AvailableResurrects = (int)level; - pm.LastSacrificeLoss = Core.Now; + pm.SendLocalizedMessage(1052041); // You have lost some Sacrifice. } - } - catch - { - // ignored + + var level = VirtueHelper.GetLevel(pm, VirtueName.Sacrifice); + + pm.AvailableResurrects = (int)level; + pm.LastSacrificeLoss = Core.Now; } } @@ -183,15 +175,9 @@ namespace Server } } - public static bool ValidateCreature(Mobile m) - { - if (m is BaseCreature creature && (creature.Controlled || creature.Summoned)) - { - return false; - } - - return m is Lich or Succubus or Daemon or EvilMage or EnslavedGargoyle or GargoyleEnforcer; - } + public static bool ValidateCreature(Mobile m) => + (m is not BaseCreature creature || !creature.Controlled && !creature.Summoned) && + m is Lich or Succubus or Daemon or EvilMage or EnslavedGargoyle or GargoyleEnforcer; private class InternalTarget : Target { diff --git a/Projects/UOContent/Engines/Virtues/Valor.cs b/Projects/UOContent/Engines/Virtues/Valor.cs index 2db766cf1..b49a8a597 100644 --- a/Projects/UOContent/Engines/Virtues/Valor.cs +++ b/Projects/UOContent/Engines/Virtues/Valor.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; using Server.Engines.CannedEvil; using Server.Mobiles; using Server.Targeting; @@ -24,28 +25,19 @@ public static class ValorVirtue } } - public static void CheckAtrophy(Mobile from) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool ShouldAtrophy(PlayerMobile pm) => pm.LastValorLoss + LossDelay < Core.Now; + + public static void CheckAtrophy(PlayerMobile pm) { - if (from is not PlayerMobile pm) + if (ShouldAtrophy(pm)) { - return; - } - - try - { - if (pm.LastValorLoss + LossDelay < Core.Now) + if (VirtueHelper.Atrophy(pm, VirtueName.Valor, LossAmount)) { - if (VirtueHelper.Atrophy(from, VirtueName.Valor, LossAmount)) - { - from.SendLocalizedMessage(1054040); // You have lost some Valor. - } - - pm.LastValorLoss = Core.Now; + pm.SendLocalizedMessage(1054040); // You have lost some Valor. } - } - catch - { - // ignored + + pm.LastValorLoss = Core.Now; } } diff --git a/Projects/UOContent/Misc/Titles.cs b/Projects/UOContent/Misc/Titles.cs index 16b3316e5..c9f6f9db0 100644 --- a/Projects/UOContent/Misc/Titles.cs +++ b/Projects/UOContent/Misc/Titles.cs @@ -15,8 +15,8 @@ namespace Server.Misc public static string[] HarrowerTitles = { - "Spite", "Opponent", "Hunter", "Venom", "Executioner", "Annihilator", "Champion", "Assailant", "Purifier", - "Nullifier" + "Spite", "Opponent", "Hunter", "Venom", "Executioner", + "Annihilator", "Champion", "Assailant", "Purifier", "Nullifier" }; private static readonly string[,] m_Levels = @@ -267,11 +267,8 @@ namespace Server.Misc if (!Core.AOS && wasPositiveKarma && m.Karma < 0 && pm?.KarmaLocked == false) { pm.KarmaLocked = true; - m.SendLocalizedMessage( - 1042511, - "", - 0x22 - ); // Karma is locked. A mantra spoken at a shrine will unlock it again. + // Karma is locked. A mantra spoken at a shrine will unlock it again. + m.SendLocalizedMessage(1042511, "", 0x22); } } @@ -320,7 +317,7 @@ namespace Server.Misc if (info.Harrower > 0) { - title.AppendFormat(": {0} of Evil", HarrowerTitles[Math.Min(HarrowerTitles.Length, info.Harrower) - 1]); + title.Append($": {HarrowerTitles[Math.Min(HarrowerTitles.Length, info.Harrower) - 1]} of Evil"); } else { @@ -346,20 +343,18 @@ namespace Server.Misc if (offset > 0) { var champInfo = ChampionSpawnInfo.GetInfo((ChampionSpawnType)highestType); - title.AppendFormat( - ": {0} of the {1}", - champInfo.LevelNames[Math.Min(offset, champInfo.LevelNames.Length) - 1], - champInfo.Name + title.Append( + $": {champInfo.LevelNames[Math.Min(offset, champInfo.LevelNames.Length) - 1]} of the {champInfo.Name}" ); } } } - var customTitle = beheld.Title; + var customTitle = beheld.Title?.Trim(); - if ((customTitle = customTitle?.Trim())?.Length > 0) + if (customTitle?.Length > 0) { - title.AppendFormat(" {0}", customTitle); + title.Append($" {customTitle}"); } else if (showSkillTitle && beheld.Player) { @@ -367,7 +362,7 @@ namespace Server.Misc if (skillTitle != null) { - title.Append(", ").Append(skillTitle); + title.Append($", {skillTitle}"); } } diff --git a/Projects/UOContent/Mobiles/ChampionTitleInfo.cs b/Projects/UOContent/Mobiles/ChampionTitleInfo.cs index 357c4c0d9..d1bd93fff 100644 --- a/Projects/UOContent/Mobiles/ChampionTitleInfo.cs +++ b/Projects/UOContent/Mobiles/ChampionTitleInfo.cs @@ -1,5 +1,5 @@ using System; -using System.Linq; +using System.Runtime.CompilerServices; using Server.Engines.CannedEvil; namespace Server.Mobiles @@ -7,8 +7,8 @@ namespace Server.Mobiles [PropertyObject] public class ChampionTitleInfo { - public const int LossAmount = 90; - public static TimeSpan LossDelay = TimeSpan.FromDays(1.0); + private const int LossAmount = 90; + private static TimeSpan LossDelay = TimeSpan.FromDays(1.0); private TitleInfo[] m_Values; @@ -27,6 +27,12 @@ namespace Server.Mobiles Harrower = reader.ReadEncodedInt(); var length = reader.ReadEncodedInt(); + + if (length == 0) + { + break; + } + m_Values = new TitleInfo[length]; for (var i = 0; i < length; i++) @@ -109,88 +115,95 @@ namespace Server.Mobiles [CommandProperty(AccessLevel.GameMaster)] public int Harrower { get; set; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetValue(ChampionSpawnType type) => GetValue((int)type); - public void SetValue(ChampionSpawnType type, int value) - { - SetValue((int)type, value); - } - - public void Award(ChampionSpawnType type, int value) - { - Award((int)type, value); - } - public int GetValue(int index) { - if (m_Values == null || index < 0 || index >= m_Values.Length) + if (index < 0 || index >= m_Values.Length) { return 0; } - m_Values[index] ??= new TitleInfo(); - - return m_Values[index].Value; + return m_Values?[index]?.Value ?? 0; } public DateTime GetLastDecay(int index) { - if (m_Values == null || index < 0 || index >= m_Values.Length) + if (index < 0 || index >= m_Values.Length) { return DateTime.MinValue; } - m_Values[index] ??= new TitleInfo(); - - return m_Values[index].LastDecay; + return m_Values?[index]?.LastDecay ?? DateTime.MinValue; } - public void SetValue(int index, int value) + public void SetValue(ChampionSpawnType type, int value) { - m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; - - if (index < 0 || index >= m_Values.Length) + var index = (int)type; + if (index < 0 || index >= ChampionSpawnInfo.Table.Length) { return; } - m_Values[index] ??= new TitleInfo(); + m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; + var title = m_Values[index]; + if (title == null) + { + if (value > 0) + { + m_Values[index] = new TitleInfo(value, Core.Now); + } + return; + } - m_Values[index].Value = Math.Max(value, 0); + title.Value = Math.Max(value, 0); + title.LastDecay = title.Value == 0 ? DateTime.MinValue : Core.Now; } - public void Award(int index, int value) + public void Award(ChampionSpawnType type, int value) { - m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; - - if (index < 0 || index >= m_Values.Length || value <= 0) + var index = (int)type; + if (value <= 0 || index < 0 || index >= ChampionSpawnInfo.Table.Length) { return; } - m_Values[index] ??= new TitleInfo(); + m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; + var title = m_Values[index]; + if (title == null) + { + m_Values[index] = new TitleInfo(value, Core.Now); + return; + } - m_Values[index].Value += value; + title.Value += value; + if (title.LastDecay == DateTime.MinValue) + { + title.LastDecay = Core.Now; + } } public void Atrophy(int index, int value) { - m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; - - if (index < 0 || index >= m_Values.Length || value <= 0) + if (value <= 0 || index < 0 || index >= ChampionSpawnInfo.Table.Length) { return; } - m_Values[index] ??= new TitleInfo(); - - var before = m_Values[index].Value; - - m_Values[index].Value -= Math.Min(value, m_Values[index].Value); - - if (before != m_Values[index].Value) + var title = m_Values?[index]; + if (title == null) { - m_Values[index].LastDecay = Core.Now; + return; + } + + var before = title.Value; + + title.Value -= Math.Min(value, title.Value); + + if (before != title.Value) + { + title.LastDecay = Core.Now; } } @@ -213,27 +226,46 @@ namespace Server.Mobiles } } + public static bool ShouldAtrophy(PlayerMobile pm) + { + var t = pm.ChampionTitles; + if (t?.m_Values == null) + { + return false; + } + + for (var i = 0; i < t.m_Values.Length; i++) + { + var decay = t.GetLastDecay(i); + if (decay > DateTime.MinValue && decay + LossDelay < Core.Now) + { + return true; + } + } + + return false; + } + public static void CheckAtrophy(PlayerMobile pm) { var t = pm.ChampionTitles; - if (t == null) + if (t?.m_Values == null) { return; } - t.m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; - for (var i = 0; i < t.m_Values.Length; i++) { - if (t.GetLastDecay(i) + LossDelay < Core.Now) + var decay = t.GetLastDecay(i); + if (decay > DateTime.MinValue && decay + LossDelay < Core.Now) { t.Atrophy(i, LossAmount); } } } - public static void - AwardHarrowerTitle(PlayerMobile pm) // Called when killing a harrower. Will give a minimum of 1 point. + // Called when killing a harrower. Will give a minimum of 1 point. + public static void AwardHarrowerTitle(PlayerMobile pm) { var t = pm.ChampionTitles; if (t == null) @@ -241,9 +273,25 @@ namespace Server.Mobiles return; } + if (t.m_Values == null) + { + if (t.Harrower == 0) + { + t.Harrower = 1; + } + return; + } + t.m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; - var count = 1 + t.m_Values.Count(t1 => t1.Value > 900); + var count = 1; + for (var i = 0; i < t.m_Values.Length; i++) + { + if (t.m_Values[i].Value > 900) + { + count++; + } + } t.Harrower = Math.Max(count, t.Harrower); // Harrower titles never decay. } @@ -254,6 +302,12 @@ namespace Server.Mobiles { } + public TitleInfo(int value, DateTime lastDecay) + { + Value = value; + LastDecay = lastDecay; + } + public TitleInfo(IGenericReader reader) { var version = reader.ReadEncodedInt(); diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 1e830f36c..ffb2033d3 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -1247,8 +1247,6 @@ namespace Server.Mobiles private static void OnLogin(Mobile from) { - CheckAtrophies(from); - if (AccountHandler.LockdownLevel > AccessLevel.Player) { string notice; @@ -1286,6 +1284,7 @@ namespace Server.Mobiles if (from is PlayerMobile mobile) { + mobile.CheckAtrophies(); mobile.ClaimAutoStabledPets(); } } @@ -3243,7 +3242,8 @@ namespace Server.Mobiles } } - CheckAtrophies(this); + CheckKillDecay(); + CheckAtrophies(); if (Hidden) // Hiding is the only buff where it has an effect that's serialized. { @@ -3274,10 +3274,6 @@ namespace Server.Mobiles } } - CheckKillDecay(); - - CheckAtrophies(this); - base.Serialize(writer); writer.Write(29); // version @@ -3406,19 +3402,28 @@ namespace Server.Mobiles writer.Write(GameTime); } - public static void CheckAtrophies(Mobile m) + public bool ShouldAtrophy() { - SacrificeVirtue.CheckAtrophy(m); - JusticeVirtue.CheckAtrophy(m); - CompassionVirtue.CheckAtrophy(m); - ValorVirtue.CheckAtrophy(m); + var sacrifice = SacrificeVirtue.ShouldAtrophy(this); + var justice = JusticeVirtue.ShouldAtrophy(this); + var compassion = CompassionVirtue.ShouldAtrophy(this); + var valor = ValorVirtue.ShouldAtrophy(this); + var titles = ChampionTitleInfo.ShouldAtrophy(this); - if (m is PlayerMobile mobile) - { - ChampionTitleInfo.CheckAtrophy(mobile); - } + return sacrifice || justice || compassion || valor || titles; } + public void CheckAtrophies() + { + SacrificeVirtue.CheckAtrophy(this); + JusticeVirtue.CheckAtrophy(this); + CompassionVirtue.CheckAtrophy(this); + ValorVirtue.CheckAtrophy(this); + ChampionTitleInfo.CheckAtrophy(this); + } + + public bool ShouldKillDecay() => m_ShortTermElapse < GameTime || m_LongTermElapse < GameTime; + public void CheckKillDecay() { if (m_ShortTermElapse < GameTime)