fix: Overhauls virtue system (#1376)
## MAJOR CHANGE (API BREAKING) Moved the virtues to it's own system _VirtueSystem_. This should make it easier to extend or remove the virtue system. Virtues will be saved to a new folder called _Virtues_. ### Motivation The motivation was also two-fold, performance/stability, and to fix bugs. First, virtues is the second system (first is murders), that has a pre-world-save check on _every mobile in the game_ to atrophy virtue stats. This is taxing since it freexes the world and makes world saves take longer. Every mobile had Gain/Loss dates for each virtue, whether they needed them or not. Most players don't even use the virtue system, so this will increase performance considerably. Second, when I tried to optimize/refactor the code, I found several bugs that needed to be fixed. ### Major API Changes - [X] The properties on players related to virtues are gone. Use `pm.GetVirtues()?.<PropertyName>` instead. - [X] Virtues were removed from non-player Mobiles. - [X] `pm.JusticeProtectors` was removed. Use `JusticeVirtue.GetProtector()` or `JusticeVirtue.GetProtected()` instead. ### Screenshots <img width="221" alt="Props-1" src="https://github.com/modernuo/ModernUO/assets/3953314/5c09cb83-b8d5-44a2-b899-a7ed7cd736ac"> <img width="220" alt="props-2" src="https://github.com/modernuo/ModernUO/assets/3953314/aeade4d3-8df0-47bd-955a-a864d0946cf7">
This commit is contained in:
parent
063d878276
commit
f77dd91811
37 changed files with 2105 additions and 1839 deletions
|
|
@ -78,16 +78,6 @@ public static partial class EventSink
|
|||
public static event Action<Mobile> PlayerDeath;
|
||||
public static void InvokePlayerDeath(Mobile m) => PlayerDeath?.Invoke(m);
|
||||
|
||||
public static event Action<Mobile, Mobile> VirtueGumpRequest;
|
||||
|
||||
public static void InvokeVirtueGumpRequest(Mobile beholder, Mobile beheld) =>
|
||||
VirtueGumpRequest?.Invoke(beholder, beheld);
|
||||
|
||||
public static event Action<Mobile, Mobile, int> VirtueItemRequest;
|
||||
|
||||
public static void InvokeVirtueItemRequest(Mobile beholder, Mobile beheld, int gumpID) =>
|
||||
VirtueItemRequest?.Invoke(beholder, beheld, gumpID);
|
||||
|
||||
public static event Action<Mobile, int> VirtueMacroRequest;
|
||||
|
||||
public static void InvokeVirtueMacroRequest(Mobile mobile, int virtueID) =>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public partial class Mobile
|
|||
// Migrating murders to the murder system
|
||||
public static Dictionary<Mobile, int> MurderMigrations { get; private set; }
|
||||
|
||||
public static void AddToMurderMigrations(Mobile m, int shortTermMurders)
|
||||
public static void AddToMurderMigration(Mobile m, int shortTermMurders)
|
||||
{
|
||||
if (shortTermMurders > 0)
|
||||
{
|
||||
|
|
@ -27,4 +27,16 @@ public partial class Mobile
|
|||
MurderMigrations[m] = shortTermMurders;
|
||||
}
|
||||
}
|
||||
|
||||
// Migrating VirtueInfo to VirtueSystem
|
||||
public static Dictionary<Mobile, int[]> VirtueMigrations { get; private set; }
|
||||
|
||||
public static void AddToVirtueMigration(Mobile m, int[] values)
|
||||
{
|
||||
if (values != null)
|
||||
{
|
||||
VirtueMigrations ??= new Dictionary<Mobile, int[]>();
|
||||
VirtueMigrations[m] = values;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -426,9 +426,6 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public VirtueInfo Virtues { get; private set; }
|
||||
|
||||
public object Party { get; set; }
|
||||
|
||||
public List<SkillMod> SkillMods => _skillMods;
|
||||
|
|
@ -2264,7 +2261,7 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
|
||||
public virtual void Serialize(IGenericWriter writer)
|
||||
{
|
||||
writer.Write(35); // version
|
||||
writer.Write(36); // version
|
||||
|
||||
writer.WriteDeltaTime(LastStrGain);
|
||||
writer.WriteDeltaTime(LastIntGain);
|
||||
|
|
@ -2302,7 +2299,7 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
|
||||
writer.Write(CantWalk);
|
||||
|
||||
VirtueInfo.Serialize(writer, Virtues);
|
||||
// VirtueInfo.Serialize(writer, Virtues);
|
||||
|
||||
writer.Write(Thirst);
|
||||
writer.Write(BAC);
|
||||
|
|
@ -6044,6 +6041,7 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
|
||||
switch (version)
|
||||
{
|
||||
case 36: // Moved virtues to VirtueSystem
|
||||
case 35: // Moved short term murders to PlayerMurderSystem
|
||||
case 34: // Moved Stabled to PlayerMobile
|
||||
case 33: // Removed created
|
||||
|
|
@ -6129,7 +6127,27 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
case 19: // Just removed variables
|
||||
case 18:
|
||||
{
|
||||
Virtues = new VirtueInfo(reader);
|
||||
if (version < 36)
|
||||
{
|
||||
reader.ReadByte(); // VirtueInfo version
|
||||
|
||||
int mask = reader.ReadByte();
|
||||
|
||||
if (mask != 0)
|
||||
{
|
||||
var virtueValues = new int[8];
|
||||
|
||||
for (var i = 0; i < 8; ++i)
|
||||
{
|
||||
if ((mask & (1 << i)) != 0)
|
||||
{
|
||||
virtueValues[i] = reader.ReadInt();
|
||||
}
|
||||
}
|
||||
|
||||
AddToVirtueMigration(this, virtueValues);
|
||||
}
|
||||
}
|
||||
|
||||
goto case 17;
|
||||
}
|
||||
|
|
@ -6145,7 +6163,7 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
if (version < 35)
|
||||
{
|
||||
// Migrated to PlayerMurderSystem
|
||||
AddToMurderMigrations(this, reader.ReadInt());
|
||||
AddToMurderMigration(this, reader.ReadInt());
|
||||
}
|
||||
|
||||
if (version <= 24)
|
||||
|
|
@ -6257,11 +6275,6 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
}
|
||||
case 0:
|
||||
{
|
||||
if (version < 18)
|
||||
{
|
||||
Virtues = new VirtueInfo();
|
||||
}
|
||||
|
||||
if (version < 11)
|
||||
{
|
||||
m_DisplayGuildTitle = true;
|
||||
|
|
@ -7687,7 +7700,6 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
|||
AutoPageNotify = true;
|
||||
Aggressors = new List<AggressorInfo>();
|
||||
Aggressed = new List<AggressorInfo>();
|
||||
Virtues = new VirtueInfo();
|
||||
DamageEntries = new List<DamageEntry>();
|
||||
|
||||
NextSkillTime = Core.TickCount;
|
||||
|
|
|
|||
|
|
@ -1,149 +0,0 @@
|
|||
namespace Server;
|
||||
|
||||
[PropertyObject]
|
||||
public class VirtueInfo
|
||||
{
|
||||
public VirtueInfo()
|
||||
{
|
||||
}
|
||||
|
||||
public VirtueInfo(IGenericReader reader)
|
||||
{
|
||||
int version = reader.ReadByte();
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case 1: // Changed the values throughout the virtue system
|
||||
case 0:
|
||||
{
|
||||
int mask = reader.ReadByte();
|
||||
|
||||
if (mask != 0)
|
||||
{
|
||||
Values = new int[8];
|
||||
|
||||
for (var i = 0; i < 8; ++i)
|
||||
{
|
||||
if ((mask & (1 << i)) != 0)
|
||||
{
|
||||
Values[i] = reader.ReadInt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (version == 0)
|
||||
{
|
||||
Compassion *= 200;
|
||||
Sacrifice *= 250; // Even though 40 (the max) only gives 10k, It's because it was formerly too easy
|
||||
|
||||
// No direct conversion factor for Justice, this is just an approximation
|
||||
Justice *= 500;
|
||||
|
||||
// All the other virtues haven't been defined at 'version 0' point in time in the scripts.
|
||||
}
|
||||
}
|
||||
|
||||
public int[] Values { get; private set; }
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Humility
|
||||
{
|
||||
get => GetValue(0);
|
||||
set => SetValue(0, value);
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Sacrifice
|
||||
{
|
||||
get => GetValue(1);
|
||||
set => SetValue(1, value);
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Compassion
|
||||
{
|
||||
get => GetValue(2);
|
||||
set => SetValue(2, value);
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Spirituality
|
||||
{
|
||||
get => GetValue(3);
|
||||
set => SetValue(3, value);
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Valor
|
||||
{
|
||||
get => GetValue(4);
|
||||
set => SetValue(4, value);
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Honor
|
||||
{
|
||||
get => GetValue(5);
|
||||
set => SetValue(5, value);
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Justice
|
||||
{
|
||||
get => GetValue(6);
|
||||
set => SetValue(6, value);
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)]
|
||||
public int Honesty
|
||||
{
|
||||
get => GetValue(7);
|
||||
set => SetValue(7, value);
|
||||
}
|
||||
|
||||
public int GetValue(int index) => Values?[index] ?? 0;
|
||||
|
||||
public void SetValue(int index, int value)
|
||||
{
|
||||
Values ??= new int[8];
|
||||
Values[index] = value;
|
||||
}
|
||||
|
||||
public override string ToString() => "...";
|
||||
|
||||
public static void Serialize(IGenericWriter writer, VirtueInfo info)
|
||||
{
|
||||
writer.Write((byte)1); // version
|
||||
|
||||
if (info.Values == null)
|
||||
{
|
||||
writer.Write((byte)0);
|
||||
}
|
||||
else
|
||||
{
|
||||
var mask = 0;
|
||||
|
||||
for (var i = 0; i < 8; ++i)
|
||||
{
|
||||
if (info.Values[i] != 0)
|
||||
{
|
||||
mask |= 1 << i;
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write((byte)mask);
|
||||
|
||||
for (var i = 0; i < 8; ++i)
|
||||
{
|
||||
if (info.Values[i] != 0)
|
||||
{
|
||||
writer.Write(info.Values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue