fix: Fixes champion titles atrophying immediately (#1210)

This commit is contained in:
Kamron Batman 2022-10-27 22:15:18 -07:00 • committed by GitHub
parent be19d9aae6
commit deabab575a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 196 additions and 186 deletions

View file

@ -585,7 +585,7 @@ namespace Server.Engines.CannedEvil
} }
} }
int mobSubLevel = GetSubLevelfor (m) + 1; int mobSubLevel = GetSubLevelfor(m) + 1;
if (mobSubLevel >= 0) if (mobSubLevel >= 0)
{ {
@ -604,12 +604,10 @@ namespace Server.Engines.CannedEvil
m.SendLocalizedMessage(1054030); // You have gained in Valor! m.SendLocalizedMessage(1054030); // You have gained in Valor!
} }
//No delay on Valor gains // No delay on Valor gains
} }
ChampionTitleInfo info = pm.ChampionTitles; pm.ChampionTitles.Award(m_Type, mobSubLevel);
info.Award(m_Type, mobSubLevel);
} }
} }
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using Server.Mobiles; using Server.Mobiles;
namespace Server namespace Server
@ -18,25 +19,19 @@ namespace Server
from.SendLocalizedMessage(1053001); // This virtue is not activated through the virtue menu. from.SendLocalizedMessage(1053001); // This virtue is not activated through the virtue menu.
} }
public static void CheckAtrophy(Mobile from) [MethodImpl(MethodImplOptions.AggressiveInlining)]
{ public static bool ShouldAtrophy(PlayerMobile pm) => pm.LastCompassionLoss + LossDelay < Core.Now;
if (from is not PlayerMobile pm)
{
return;
}
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); pm.SendLocalizedMessage(1114420); // You have lost some Compassion.
// OSI has no cliloc message for losing compassion. Weird.
pm.LastCompassionLoss = Core.Now;
} }
}
catch pm.LastCompassionLoss = Core.Now;
{
// ignored
} }
} }
} }

View file

@ -68,20 +68,13 @@ namespace Server
public static void ActivateEmbrace(PlayerMobile pm) public static void ActivateEmbrace(PlayerMobile pm)
{ {
var duration = GetHonorDuration(pm); var duration = GetHonorDuration(pm);
int usedPoints;
if (pm.Virtues.Honor < 4399) int usedPoints = pm.Virtues.Honor switch
{ {
usedPoints = 400; < 4399 => 400,
} < 10599 => 600,
else if (pm.Virtues.Honor < 10599) _ => 1000
{ };
usedPoints = 600;
}
else
{
usedPoints = 1000;
}
VirtueHelper.Atrophy(pm, VirtueName.Honor, usedPoints); VirtueHelper.Atrophy(pm, VirtueName.Honor, usedPoints);

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using Server.Gumps; using Server.Gumps;
using Server.Mobiles; using Server.Mobiles;
using Server.Network; using Server.Network;
@ -6,7 +7,7 @@ using Server.Targeting;
namespace Server namespace Server
{ {
#pragma warning disable CA1052 #pragma warning disable CA1052 // Cannot be static because its used as a generic for CanBeginAction.
public class JusticeVirtue public class JusticeVirtue
{ {
private const int LossAmount = 950; private const int LossAmount = 950;
@ -71,14 +72,13 @@ namespace Server
public static void OnVirtueTargeted(Mobile from, object obj) public static void OnVirtueTargeted(Mobile from, object obj)
{ {
var protector = from as PlayerMobile; if (from is not PlayerMobile protector)
var pm = obj as PlayerMobile;
if (protector == null)
{ {
return; return;
} }
var pm = obj as PlayerMobile;
if (!VirtueHelper.IsSeeker(protector, VirtueName.Justice)) if (!VirtueHelper.IsSeeker(protector, VirtueName.Justice))
{ {
protector.SendLocalizedMessage(1049610); // You must reach the first path in this virtue to invoke it. 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; if (VirtueHelper.Atrophy(pm, VirtueName.Justice, LossAmount))
}
try
{
if (pm.LastJusticeLoss + LossDelay < Core.Now)
{ {
if (VirtueHelper.Atrophy(from, VirtueName.Justice, LossAmount)) pm.SendLocalizedMessage(1049373); // You have lost some Justice.
{
from.SendLocalizedMessage(1049373); // You have lost some Justice.
}
pm.LastJusticeLoss = Core.Now;
} }
}
catch pm.LastJusticeLoss = Core.Now;
{
// ignored
} }
} }
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using Server.Gumps; using Server.Gumps;
using Server.Mobiles; using Server.Mobiles;
using Server.Network; 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; if (VirtueHelper.Atrophy(pm, VirtueName.Sacrifice, LossAmount))
}
try
{
if (pm.LastSacrificeLoss + LossDelay < Core.Now)
{ {
if (VirtueHelper.Atrophy(from, VirtueName.Sacrifice, LossAmount)) pm.SendLocalizedMessage(1052041); // You have lost some Sacrifice.
{
from.SendLocalizedMessage(1052041); // You have lost some Sacrifice.
}
var level = VirtueHelper.GetLevel(from, VirtueName.Sacrifice);
pm.AvailableResurrects = (int)level;
pm.LastSacrificeLoss = Core.Now;
} }
}
catch var level = VirtueHelper.GetLevel(pm, VirtueName.Sacrifice);
{
// ignored pm.AvailableResurrects = (int)level;
pm.LastSacrificeLoss = Core.Now;
} }
} }
@ -183,15 +175,9 @@ namespace Server
} }
} }
public static bool ValidateCreature(Mobile m) public static bool ValidateCreature(Mobile m) =>
{ (m is not BaseCreature creature || !creature.Controlled && !creature.Summoned) &&
if (m is BaseCreature creature && (creature.Controlled || creature.Summoned)) m is Lich or Succubus or Daemon or EvilMage or EnslavedGargoyle or GargoyleEnforcer;
{
return false;
}
return m is Lich or Succubus or Daemon or EvilMage or EnslavedGargoyle or GargoyleEnforcer;
}
private class InternalTarget : Target private class InternalTarget : Target
{ {

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Runtime.CompilerServices;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
using Server.Mobiles; using Server.Mobiles;
using Server.Targeting; 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; if (VirtueHelper.Atrophy(pm, VirtueName.Valor, LossAmount))
}
try
{
if (pm.LastValorLoss + LossDelay < Core.Now)
{ {
if (VirtueHelper.Atrophy(from, VirtueName.Valor, LossAmount)) pm.SendLocalizedMessage(1054040); // You have lost some Valor.
{
from.SendLocalizedMessage(1054040); // You have lost some Valor.
}
pm.LastValorLoss = Core.Now;
} }
}
catch pm.LastValorLoss = Core.Now;
{
// ignored
} }
} }

View file

@ -15,8 +15,8 @@ namespace Server.Misc
public static string[] HarrowerTitles = public static string[] HarrowerTitles =
{ {
"Spite", "Opponent", "Hunter", "Venom", "Executioner", "Annihilator", "Champion", "Assailant", "Purifier", "Spite", "Opponent", "Hunter", "Venom", "Executioner",
"Nullifier" "Annihilator", "Champion", "Assailant", "Purifier", "Nullifier"
}; };
private static readonly string[,] m_Levels = private static readonly string[,] m_Levels =
@ -267,11 +267,8 @@ namespace Server.Misc
if (!Core.AOS && wasPositiveKarma && m.Karma < 0 && pm?.KarmaLocked == false) if (!Core.AOS && wasPositiveKarma && m.Karma < 0 && pm?.KarmaLocked == false)
{ {
pm.KarmaLocked = true; pm.KarmaLocked = true;
m.SendLocalizedMessage( // Karma is locked. A mantra spoken at a shrine will unlock it again.
1042511, m.SendLocalizedMessage(1042511, "", 0x22);
"",
0x22
); // Karma is locked. A mantra spoken at a shrine will unlock it again.
} }
} }
@ -320,7 +317,7 @@ namespace Server.Misc
if (info.Harrower > 0) 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 else
{ {
@ -346,20 +343,18 @@ namespace Server.Misc
if (offset > 0) if (offset > 0)
{ {
var champInfo = ChampionSpawnInfo.GetInfo((ChampionSpawnType)highestType); var champInfo = ChampionSpawnInfo.GetInfo((ChampionSpawnType)highestType);
title.AppendFormat( title.Append(
": {0} of the {1}", $": {champInfo.LevelNames[Math.Min(offset, champInfo.LevelNames.Length) - 1]} of the {champInfo.Name}"
champInfo.LevelNames[Math.Min(offset, champInfo.LevelNames.Length) - 1],
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) else if (showSkillTitle && beheld.Player)
{ {
@ -367,7 +362,7 @@ namespace Server.Misc
if (skillTitle != null) if (skillTitle != null)
{ {
title.Append(", ").Append(skillTitle); title.Append($", {skillTitle}");
} }
} }

View file

@ -1,5 +1,5 @@
using System; using System;
using System.Linq; using System.Runtime.CompilerServices;
using Server.Engines.CannedEvil; using Server.Engines.CannedEvil;
namespace Server.Mobiles namespace Server.Mobiles
@ -7,8 +7,8 @@ namespace Server.Mobiles
[PropertyObject] [PropertyObject]
public class ChampionTitleInfo public class ChampionTitleInfo
{ {
public const int LossAmount = 90; private const int LossAmount = 90;
public static TimeSpan LossDelay = TimeSpan.FromDays(1.0); private static TimeSpan LossDelay = TimeSpan.FromDays(1.0);
private TitleInfo[] m_Values; private TitleInfo[] m_Values;
@ -27,6 +27,12 @@ namespace Server.Mobiles
Harrower = reader.ReadEncodedInt(); Harrower = reader.ReadEncodedInt();
var length = reader.ReadEncodedInt(); var length = reader.ReadEncodedInt();
if (length == 0)
{
break;
}
m_Values = new TitleInfo[length]; m_Values = new TitleInfo[length];
for (var i = 0; i < length; i++) for (var i = 0; i < length; i++)
@ -109,88 +115,95 @@ namespace Server.Mobiles
[CommandProperty(AccessLevel.GameMaster)] [CommandProperty(AccessLevel.GameMaster)]
public int Harrower { get; set; } public int Harrower { get; set; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetValue(ChampionSpawnType type) => GetValue((int)type); 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) public int GetValue(int index)
{ {
if (m_Values == null || index < 0 || index >= m_Values.Length) if (index < 0 || index >= m_Values.Length)
{ {
return 0; return 0;
} }
m_Values[index] ??= new TitleInfo(); return m_Values?[index]?.Value ?? 0;
return m_Values[index].Value;
} }
public DateTime GetLastDecay(int index) 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; return DateTime.MinValue;
} }
m_Values[index] ??= new TitleInfo(); return m_Values?[index]?.LastDecay ?? DateTime.MinValue;
return m_Values[index].LastDecay;
} }
public void SetValue(int index, int value) public void SetValue(ChampionSpawnType type, int value)
{ {
m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; var index = (int)type;
if (index < 0 || index >= ChampionSpawnInfo.Table.Length)
if (index < 0 || index >= m_Values.Length)
{ {
return; 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]; var index = (int)type;
if (value <= 0 || index < 0 || index >= ChampionSpawnInfo.Table.Length)
if (index < 0 || index >= m_Values.Length || value <= 0)
{ {
return; 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) public void Atrophy(int index, int value)
{ {
m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; if (value <= 0 || index < 0 || index >= ChampionSpawnInfo.Table.Length)
if (index < 0 || index >= m_Values.Length || value <= 0)
{ {
return; return;
} }
m_Values[index] ??= new TitleInfo(); var title = m_Values?[index];
if (title == null)
var before = m_Values[index].Value;
m_Values[index].Value -= Math.Min(value, m_Values[index].Value);
if (before != m_Values[index].Value)
{ {
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) public static void CheckAtrophy(PlayerMobile pm)
{ {
var t = pm.ChampionTitles; var t = pm.ChampionTitles;
if (t == null) if (t?.m_Values == null)
{ {
return; return;
} }
t.m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length];
for (var i = 0; i < t.m_Values.Length; i++) 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); t.Atrophy(i, LossAmount);
} }
} }
} }
public static void // Called when killing a harrower. Will give a minimum of 1 point.
AwardHarrowerTitle(PlayerMobile pm) // Called when killing a harrower. Will give a minimum of 1 point. public static void AwardHarrowerTitle(PlayerMobile pm)
{ {
var t = pm.ChampionTitles; var t = pm.ChampionTitles;
if (t == null) if (t == null)
@ -241,9 +273,25 @@ namespace Server.Mobiles
return; return;
} }
if (t.m_Values == null)
{
if (t.Harrower == 0)
{
t.Harrower = 1;
}
return;
}
t.m_Values ??= new TitleInfo[ChampionSpawnInfo.Table.Length]; 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. 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) public TitleInfo(IGenericReader reader)
{ {
var version = reader.ReadEncodedInt(); var version = reader.ReadEncodedInt();

View file

@ -1247,8 +1247,6 @@ namespace Server.Mobiles
private static void OnLogin(Mobile from) private static void OnLogin(Mobile from)
{ {
CheckAtrophies(from);
if (AccountHandler.LockdownLevel > AccessLevel.Player) if (AccountHandler.LockdownLevel > AccessLevel.Player)
{ {
string notice; string notice;
@ -1286,6 +1284,7 @@ namespace Server.Mobiles
if (from is PlayerMobile mobile) if (from is PlayerMobile mobile)
{ {
mobile.CheckAtrophies();
mobile.ClaimAutoStabledPets(); 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. 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); base.Serialize(writer);
writer.Write(29); // version writer.Write(29); // version
@ -3406,19 +3402,28 @@ namespace Server.Mobiles
writer.Write(GameTime); writer.Write(GameTime);
} }
public static void CheckAtrophies(Mobile m) public bool ShouldAtrophy()
{ {
SacrificeVirtue.CheckAtrophy(m); var sacrifice = SacrificeVirtue.ShouldAtrophy(this);
JusticeVirtue.CheckAtrophy(m); var justice = JusticeVirtue.ShouldAtrophy(this);
CompassionVirtue.CheckAtrophy(m); var compassion = CompassionVirtue.ShouldAtrophy(this);
ValorVirtue.CheckAtrophy(m); var valor = ValorVirtue.ShouldAtrophy(this);
var titles = ChampionTitleInfo.ShouldAtrophy(this);
if (m is PlayerMobile mobile) return sacrifice || justice || compassion || valor || titles;
{
ChampionTitleInfo.CheckAtrophy(mobile);
}
} }
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() public void CheckKillDecay()
{ {
if (m_ShortTermElapse < GameTime) if (m_ShortTermElapse < GameTime)