diff --git a/Projects/Server/Mobiles/Mobile.Migrations.cs b/Projects/Server/Mobiles/Mobile.Migrations.cs index 81f56f930e..e7796e3973 100644 --- a/Projects/Server/Mobiles/Mobile.Migrations.cs +++ b/Projects/Server/Mobiles/Mobile.Migrations.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Server.Guilds; namespace Server; @@ -39,4 +40,26 @@ public static void AddToVirtueMigration(Mobile m, int[] values) VirtueMigrations[m] = values; } } + + public static List GuildMigrations { get; private set; } + + public static void AddToGuildMigration(GuildInfo info) + { + if (info != null) + { + GuildMigrations ??= []; + GuildMigrations.Add(info); + } + } + + public class GuildInfo + { + public Mobile GuildMember { get; } + public Mobile GuildFealty { get; internal set; } + public string GuildTitle { get; internal set; } + public bool DisplayGuildTitle { get; internal set; } + public BaseGuild Guild { get; internal set; } + + public GuildInfo(Mobile member) => GuildMember = member; + } } diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 66064b87b9..c5f1203a21 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -234,13 +234,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro private static readonly Queue m_DeltaQueue = new(); - private static readonly string[] m_GuildTypes = - { - "", - " (Chaos)", - " (Order)" - }; - private static bool _disableCastParalyze = true; public static void Configure() @@ -267,7 +260,6 @@ public static void Configure() private MobileDelta m_DeltaFlags; private Direction m_Direction; - private bool m_DisplayGuildTitle; private TimerExecutionToken _expireAggrTimerToken; private TimerExecutionToken _expireCombatantTimerToken; @@ -280,8 +272,6 @@ public static void Configure() private int m_Followers, m_FollowersMax; private bool m_Frozen; private TimerExecutionToken _frozenTimerToken; - private BaseGuild m_Guild; - private string m_GuildTitle; private VirtualHairInfo _hair; public VirtualHairInfo Hair => _hair ??= new VirtualHairInfo(HairItemID, HairHue); @@ -1340,44 +1330,6 @@ public string Language [CommandProperty(AccessLevel.GameMaster)] public int YellHue { get; set; } - [CommandProperty(AccessLevel.GameMaster)] - public string GuildTitle - { - get => m_GuildTitle; - set - { - var old = m_GuildTitle; - - if (old != value) - { - m_GuildTitle = value; - - if (m_Guild?.Disbanded == false && m_GuildTitle != null) - { - SendLocalizedMessage(1018026, true, m_GuildTitle); // Your guild title has changed : - } - - InvalidateProperties(); - - OnGuildTitleChange(old); - } - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public bool DisplayGuildTitle - { - get => m_DisplayGuildTitle; - set - { - m_DisplayGuildTitle = value; - InvalidateProperties(); - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile GuildFealty { get; set; } - [CommandProperty(AccessLevel.GameMaster)] public string NameMod { @@ -1463,30 +1415,6 @@ public DateTime LastStatGain } } - public BaseGuild Guild - { - get => m_Guild; - set - { - var old = m_Guild; - - if (old != value) - { - if (value == null) - { - GuildTitle = null; - } - - m_Guild = value; - - Delta(MobileDelta.Noto); - InvalidateProperties(); - - OnGuildChange(old); - } - } - } - public Region WalkRegion { get; set; } [CommandProperty(AccessLevel.GameMaster)] @@ -1704,8 +1632,6 @@ public IMount Mount public static bool AsciiClickMessage { get; set; } = true; - public static bool GuildClickMessage { get; set; } = true; - public static bool OldPropertyTitles { get; set; } public virtual bool ShowFameTitle // (m_Player || m_Body.IsHuman) && m_Fame >= 10000; } @@ -2283,7 +2209,7 @@ public virtual void GetProperties(IPropertyList list) public virtual void Serialize(IGenericWriter writer) { - writer.Write(36); // version + writer.Write(37); // version writer.WriteDeltaTime(LastStrGain); writer.WriteDeltaTime(LastIntGain); @@ -2330,12 +2256,6 @@ public virtual void Serialize(IGenericWriter writer) writer.Write(MagicDamageAbsorb); - writer.Write(GuildFealty); - - writer.Write(m_Guild); - - writer.Write(m_DisplayGuildTitle); - writer.Write(CanSwim); writer.Write(Squelched); @@ -2358,7 +2278,6 @@ public virtual void Serialize(IGenericWriter writer) writer.Write(m_Location); writer.Write(m_Body); writer.Write(m_Name); - writer.Write(m_GuildTitle); writer.Write(m_Criminal); writer.Write(m_Kills); writer.Write(SpeechHue); @@ -2438,8 +2357,6 @@ public virtual void Delete() SendRemovePacket(); - m_Guild?.OnDelete(this); - Deleted = true; m_Map?.OnLeave(this); @@ -3282,42 +3199,11 @@ public virtual void AddNameProperties(IPropertyList list) prefix = " "; } - var guild = m_Guild; var hasTitle = PropertyTitle && !string.IsNullOrEmpty(Title); - var hasGuild = guild != null && (m_Player || m_DisplayGuildTitle); - string suffix = hasTitle switch - { - true when hasGuild => $" {Title} [{guild.Abbreviation.FixHtmlFormattable()}]", - true => $" {Title}", - false when hasGuild => $" [{guild.Abbreviation.FixHtmlFormattable()}]", - _ => " " - }; + string suffix = hasTitle ? $"{Title}" : ""; - list.Add(1050045, $"{prefix}\t{Name ?? " "}\t{ApplyNameSuffix(suffix)}"); // ~1_PREFIX~~2_NAME~~3_SUFFIX~ - - if (guild != null && (m_DisplayGuildTitle || m_Player && guild.Type != GuildType.Regular)) - { - var type = guild.Type >= 0 && (int)guild.Type < m_GuildTypes.Length ? m_GuildTypes[(int)guild.Type] : ""; - - var guildTitle = GuildTitle?.Trim() ?? ""; - - if (guildTitle.Length > 0) - { - if (NewGuildDisplay) - { - list.Add($"{guildTitle.FixHtmlFormattable()}, {guild.Name.FixHtmlFormattable()}"); - } - else - { - list.Add($"{guildTitle.FixHtmlFormattable()}, {guild.Name.FixHtmlFormattable()} Guild{type}"); - } - } - else - { - list.Add(guild.Name.FixHtml()); - } - } + list.Add(1050045, $"{prefix}\t{Name ?? " "}\t{ApplyNameSuffix(suffix).DefaultIfNullOrEmpty(" ")}"); // ~1_PREFIX~~2_NAME~~3_SUFFIX~ } public virtual void GetChildProperties(IPropertyList list, Item item) @@ -6088,8 +5974,11 @@ public virtual void Deserialize(IGenericReader reader) { var version = reader.ReadInt(); + GuildInfo guildInfo = version <= 36 ? new GuildInfo(this) : null; + switch (version) { + case 37: // Moved guild to PlayerMobile case 36: // Moved virtues to VirtueSystem case 35: // Moved short term murders to PlayerMurderSystem case 34: // Moved Stabled to PlayerMobile @@ -6244,19 +6133,28 @@ public virtual void Deserialize(IGenericReader reader) } case 13: { - GuildFealty = reader.ReadEntity(); + if (guildInfo != null) + { + guildInfo.GuildFealty = reader.ReadEntity(); + } goto case 12; } case 12: { - m_Guild = reader.ReadEntity(); + if (guildInfo != null) + { + guildInfo.Guild = reader.ReadEntity(); + } goto case 11; } case 11: { - m_DisplayGuildTitle = reader.ReadBool(); + if (guildInfo != null) + { + guildInfo.DisplayGuildTitle = reader.ReadBool(); + } goto case 10; } @@ -6326,11 +6224,6 @@ public virtual void Deserialize(IGenericReader reader) } case 0: { - if (version < 11) - { - m_DisplayGuildTitle = true; - } - if (version < 3) { m_StatCap = 225; @@ -6345,7 +6238,12 @@ public virtual void Deserialize(IGenericReader reader) m_Location = reader.ReadPoint3D(); m_Body = new Body(reader.ReadInt()); m_Name = reader.ReadString(); - m_GuildTitle = reader.ReadString(); + + if (guildInfo != null) + { + guildInfo.GuildTitle = reader.ReadString(); + } + m_Criminal = reader.ReadBool(); m_Kills = reader.ReadInt(); SpeechHue = reader.ReadInt(); @@ -6445,6 +6343,11 @@ public virtual void Deserialize(IGenericReader reader) Utility.Intern(ref m_Title); Utility.Intern(ref m_Language); + + if (guildInfo != null) + { + AddToGuildMigration(guildInfo); + } } public void ConvertHair() @@ -7152,18 +7055,10 @@ public virtual bool CanSee(Mobile m) public virtual bool CanBeRenamedBy(Mobile from) => from.AccessLevel >= AccessLevel.GameMaster && from.m_AccessLevel > m_AccessLevel; - public virtual void OnGuildTitleChange(string oldTitle) - { - } - public virtual void OnAfterNameChange(string oldName, string newName) { } - public virtual void OnGuildChange(BaseGuild oldGuild) - { - } - public virtual int SafeBody(int body) { var delta = -1; @@ -7869,35 +7764,6 @@ public virtual void OnSingleClick(Mobile from) return; } - if (GuildClickMessage) - { - var guild = m_Guild; - - if (guild != null && (m_DisplayGuildTitle || m_Player && guild.Type != GuildType.Regular)) - { - var title = GuildTitle?.Trim() ?? ""; - string type; - - if (guild.Type >= 0 && (int)guild.Type < m_GuildTypes.Length) - { - type = m_GuildTypes[(int)guild.Type]; - } - else - { - type = ""; - } - - var text = string.Format( - title.Length <= 0 ? "[{1}]{2}" : "[{0}, {1}]{2}", - title, - guild.Abbreviation, - type - ); - - PrivateOverheadMessage(MessageType.Regular, SpeechHue, true, text, from.NetState); - } - } - int hue; if (NameHue != -1) diff --git a/Projects/UOContent/Configuration/ExpansionConfiguration.cs b/Projects/UOContent/Configuration/ExpansionConfiguration.cs index 2399a58b57..477ff67659 100644 --- a/Projects/UOContent/Configuration/ExpansionConfiguration.cs +++ b/Projects/UOContent/Configuration/ExpansionConfiguration.cs @@ -1,3 +1,4 @@ +using Server.Mobiles; using Server.Network; namespace Server @@ -10,7 +11,7 @@ public static void Configure() ObjectPropertyList.Enabled = ServerConfiguration.GetSetting("opl.enable", Core.AOS); var visibleDamage = ServerConfiguration.GetSetting("visibleDamage", Core.AOS); Mobile.VisibleDamageType = visibleDamage ? VisibleDamageType.Related : VisibleDamageType.None; - Mobile.GuildClickMessage = ServerConfiguration.GetSetting("guildClickMessage", !Core.AOS); + PlayerMobile.GuildClickMessage = ServerConfiguration.GetSetting("guildClickMessage", !Core.AOS); Mobile.AsciiClickMessage = ServerConfiguration.GetSetting("asciiClickMessage", !Core.AOS); Mobile.ActionDelay = ServerConfiguration.GetSetting("actionDelay", Core.AOS ? 1000 : 500); diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs index 088d267895..7c58302d5e 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Server.Gumps; +using Server.Mobiles; using Server.Network; namespace Server.Engines.ConPVP @@ -179,9 +180,9 @@ public LadderGump(Ladder ladder, int page = 0) : base(50, 50) var mob = entry.Mobile; - if (mob.Guild != null) + if (mob is PlayerMobile { Guild: not null } pm) { - AddBorderedText(x, y, 50, mob.Guild.Abbreviation.Center(), 0xFFFFFF, 0); + AddBorderedText(x, y, 50, pm.Guild.Abbreviation.Center(), 0xFFFFFF, 0); } x += 50; diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs index ae7d3daac0..f78d07061e 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs @@ -385,12 +385,14 @@ public TournamentBracketGump( var entry = ladder?.Find(mob); AddHtml(25, 53, 250, 20, $"Name: {mob.Name}"); + + var g = (mob as PlayerMobile)?.Guild; AddHtml( 25, 73, 250, 20, - $"Guild: {(mob.Guild == null ? "None" : $"{mob.Guild.Name} [{mob.Guild.Abbreviation}]")}" + $"Guild: {(g == null ? "None" : $"{g.Name} [{g.Abbreviation}]")}" ); AddHtml(25, 93, 250, 20, $"Rank: {(entry == null ? "N/A" : LadderGump.Rank(entry.Index + 1))}"); AddHtml(25, 113, 250, 20, $"Level: {(entry == null ? 0 : Ladder.GetLevel(entry.Experience))}"); diff --git a/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs b/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs index 249027dcff..c73757b89e 100644 --- a/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs +++ b/Projects/UOContent/Gumps/Guilds/DeclareFealtyGump.cs @@ -1,4 +1,5 @@ using Server.Guilds; +using Server.Mobiles; using Server.Network; namespace Server.Gumps @@ -39,9 +40,9 @@ public override void OnResponse(NetState state, in RelayInfo info) { var m = m_List[index]; - if (m?.Deleted == false) + if (m?.Deleted == false && state.Mobile is PlayerMobile pm) { - state.Mobile.GuildFealty = m; + pm.GuildFealty = m; } } } diff --git a/Projects/UOContent/Gumps/Guilds/GuildGump.cs b/Projects/UOContent/Gumps/Guilds/GuildGump.cs index 42e5bfa075..47e7eac67e 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildGump.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildGump.cs @@ -1,4 +1,5 @@ using Server.Guilds; +using Server.Mobiles; using Server.Network; namespace Server.Gumps @@ -21,7 +22,7 @@ public GuildGump(Mobile beholder, Guild guild) : base(20, 30) AddHtml(20, 15, 200, 35, guild.Name); - var leader = guild.Leader; + var leader = guild.Leader as PlayerMobile; if (leader != null) { @@ -35,7 +36,8 @@ public GuildGump(Mobile beholder, Guild guild) : base(20, 30) AddButton(20, 50, 4005, 4007, 1); AddHtmlLocalized(55, 50, 100, 20, 1013022); // Loyal to - var fealty = beholder.GuildFealty; + var pm = beholder as PlayerMobile; + var fealty = pm?.GuildFealty; if (fealty == null || !guild.IsMember(fealty)) { @@ -57,7 +59,7 @@ public GuildGump(Mobile beholder, Guild guild) : base(20, 30) AddButton(215, 50, 4005, 4007, 2); AddHtmlLocalized(250, 50, 170, 20, 1013023); // Display guild abbreviation - AddHtmlLocalized(250, 70, 50, 20, beholder.DisplayGuildTitle ? 1011262 : 1011263); // on/off + AddHtmlLocalized(250, 70, 50, 20, pm?.DisplayGuildTitle == true ? 1011262 : 1011263); // on/off AddButton(20, 100, 4005, 4007, 3); AddHtmlLocalized(55, 100, 470, 30, 1011086); // View the current roster. @@ -159,7 +161,10 @@ public override void OnResponse(NetState sender, in RelayInfo info) } case 2: // Toggle display abbreviation { - m_Mobile.DisplayGuildTitle = !m_Mobile.DisplayGuildTitle; + if (m_Mobile is PlayerMobile pm) + { + pm.DisplayGuildTitle = !pm.DisplayGuildTitle; + } EnsureClosed(m_Mobile); m_Mobile.SendGump(new GuildGump(m_Mobile, m_Guild)); diff --git a/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs b/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs index b77b977439..728e2efc51 100644 --- a/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs +++ b/Projects/UOContent/Gumps/Guilds/GuildTitlePrompt.cs @@ -1,4 +1,5 @@ using Server.Guilds; +using Server.Mobiles; using Server.Prompts; namespace Server.Gumps @@ -51,9 +52,9 @@ public override void OnResponse(Mobile from, string text) text = text[..20]; } - if (text.Length > 0) + if (text.Length > 0 && m_Target is PlayerMobile pm) { - m_Target.GuildTitle = text; + pm.GuildTitle = text; } GuildGump.EnsureClosed(m_Leader); diff --git a/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs b/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs index beda0ab270..2f65a2b233 100644 --- a/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs +++ b/Projects/UOContent/Gumps/Guilds/RecruitTarget.cs @@ -1,5 +1,6 @@ using Server.Factions; using Server.Guilds; +using Server.Mobiles; using Server.Targeting; namespace Server.Gumps @@ -52,7 +53,7 @@ protected override void OnTarget(Mobile from, object targeted) 501165 ); // They have already been accepted for membership, and merely need to use the Guildstone to gain full membership. } - else if (m.Guild != null) + else if ((m as PlayerMobile)?.Guild != null) { m_Mobile.SendLocalizedMessage(501166); // You can only recruit candidates who are not already in a guild. } diff --git a/Projects/UOContent/Gumps/Houses/SetSecureLevelGump.cs b/Projects/UOContent/Gumps/Houses/SetSecureLevelGump.cs index 5242fd1cec..9ee7094d30 100644 --- a/Projects/UOContent/Gumps/Houses/SetSecureLevelGump.cs +++ b/Projects/UOContent/Gumps/Houses/SetSecureLevelGump.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using Server.Guilds; +using Server.Mobiles; using Server.Multis; using Server.Network; @@ -53,7 +54,7 @@ protected override void BuildLayout(ref DynamicGumpBuilder builder) var houseOwner = _house.Owner; // Only the actual House owner AND guild master can set guild secures - if (Guild.NewGuildSystem && houseOwner?.Guild is Guild guild && guild.Leader == houseOwner) + if (Guild.NewGuildSystem && (houseOwner as PlayerMobile)?.Guild is Guild guild && guild.Leader == houseOwner) { builder.AddButton(10, 130, GetFirstID(SecureLevel.Guild), 4007, 5); builder.AddHtmlLocalized(45, 130, 150, 20, 1063455, GetColor(SecureLevel.Guild)); // Guild Members diff --git a/Projects/UOContent/Items/Guilds/GuildDeed.cs b/Projects/UOContent/Items/Guilds/GuildDeed.cs index ac0869a719..92c6510587 100644 --- a/Projects/UOContent/Items/Guilds/GuildDeed.cs +++ b/Projects/UOContent/Items/Guilds/GuildDeed.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Guilds; +using Server.Mobiles; using Server.Multis; using Server.Prompts; @@ -24,7 +25,7 @@ public override void OnDoubleClick(Mobile from) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - else if (from.Guild != null) + else if ((from as PlayerMobile)?.Guild != null) { from.SendLocalizedMessage(501137); // You must resign from your current guild before founding another! } @@ -60,7 +61,7 @@ private class InternalPrompt : Prompt public override void OnResponse(Mobile from, string text) { - if (m_Deed.Deleted) + if (m_Deed.Deleted || from is not PlayerMobile pm) { return; } @@ -69,7 +70,7 @@ public override void OnResponse(Mobile from, string text) { from.SendLocalizedMessage(1042001); // That must be in your pack for you to use it. } - else if (from.Guild != null) + else if (pm.Guild != null) { from.SendLocalizedMessage(501137); // You must resign from your current guild before founding another! } @@ -100,8 +101,8 @@ public override void OnResponse(Mobile from, string text) var guild = new Guild(from, text, "none"); - from.Guild = guild; - from.GuildTitle = "Guildmaster"; + pm.Guild = guild; + pm.GuildTitle = "Guildmaster"; var stone = new Guildstone(guild); diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs index 8ea7d1319d..70be46cb76 100644 --- a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs @@ -169,7 +169,9 @@ public Corpse(Mobile owner, VirtualHairInfo hair, VirtualHairInfo facialHair, Li _timeOfDeath = Core.Now; _accessLevel = owner.AccessLevel; - _guild = owner.Guild as Guild; + + // TODO: Should pet corpses be subject to guild notoriety? + _guild = (owner as PlayerMobile)?.Guild as Guild; _kills = owner.Kills; SetFlag(CorpseFlag.Criminal, owner.Criminal); diff --git a/Projects/UOContent/Items/Shields/ChaosShield.cs b/Projects/UOContent/Items/Shields/ChaosShield.cs index dd7707c955..ec548c588c 100644 --- a/Projects/UOContent/Items/Shields/ChaosShield.cs +++ b/Projects/UOContent/Items/Shields/ChaosShield.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Guilds; +using Server.Mobiles; namespace Server.Items; @@ -47,7 +48,7 @@ public virtual bool Validate(Mobile m) return true; } - if (m.Guild is not Guild { Type: GuildType.Chaos }) + if ((m as PlayerMobile)?.Guild is not Guild { Type: GuildType.Chaos }) { m.FixedEffect(0x3728, 10, 13); Delete(); diff --git a/Projects/UOContent/Items/Shields/OrderShield.cs b/Projects/UOContent/Items/Shields/OrderShield.cs index 04ba889098..9303f57fd8 100644 --- a/Projects/UOContent/Items/Shields/OrderShield.cs +++ b/Projects/UOContent/Items/Shields/OrderShield.cs @@ -1,5 +1,6 @@ using ModernUO.Serialization; using Server.Guilds; +using Server.Mobiles; namespace Server.Items; @@ -47,7 +48,7 @@ public virtual bool Validate(Mobile m) return true; } - if (m.Guild is not Guild { Type: GuildType.Order }) + if ((m as PlayerMobile)?.Guild is not Guild { Type: GuildType.Order }) { m.FixedEffect(0x3728, 10, 13); Delete(); diff --git a/Projects/UOContent/Misc/Guild.cs b/Projects/UOContent/Misc/Guild.cs index 66e4d5e2d0..79568dcae7 100644 --- a/Projects/UOContent/Misc/Guild.cs +++ b/Projects/UOContent/Misc/Guild.cs @@ -846,12 +846,11 @@ public void Disband() { m.SendLocalizedMessage(502131); // Your guild has disbanded. - if (m is PlayerMobile mobile) + if (m is PlayerMobile pm) { - mobile.GuildRank = RankDefinition.Lowest; + pm.GuildRank = RankDefinition.Lowest; + pm.Guild = null; } - - m.Guild = null; } Members.Clear(); @@ -1060,13 +1059,14 @@ public static void HandleDeath(Mobile victim, Mobile killer) killer ??= victim.FindMostRecentDamager(false); - if (killer?.Guild == null || victim.Guild == null) + if ((killer as PlayerMobile)?.Guild is not Guild killerGuild || + (victim as PlayerMobile)?.Guild is not Guild victimGuild) { return; } - var victimGuild = GetAllianceLeader(victim.Guild as Guild); - var killerGuild = GetAllianceLeader(killer.Guild as Guild); + victimGuild = GetAllianceLeader(victimGuild); + killerGuild = GetAllianceLeader(killerGuild); var war = killerGuild.FindActiveWar(victimGuild); @@ -1330,12 +1330,12 @@ private void VerifyGuild_Callback() public void AddMember(Mobile m) { - if (Members.Contains(m)) + if (m is not PlayerMobile pm || Members.Contains(m)) { return; } - var oldGuild = m.Guild as Guild; + var oldGuild = pm.Guild as Guild; if (oldGuild != this) { @@ -1343,14 +1343,10 @@ public void AddMember(Mobile m) } Members.Add(m); - m.Guild = this; - - m.GuildFealty = !NewGuildSystem ? m_Leader : null; + pm.Guild = this; - if (m is PlayerMobile pm) - { - pm.GuildRank = RankDefinition.Lowest; - } + pm.GuildFealty = !NewGuildSystem ? m_Leader : null; + pm.GuildRank = RankDefinition.Lowest; oldGuild?.InvalidateWarNotoriety(); InvalidateWarNotoriety(); @@ -1358,18 +1354,14 @@ public void AddMember(Mobile m) public void RemoveMember(Mobile m, int message = 1018028) // You have been dismissed from your guild. { - if (!Members.Remove(m)) + if (m is not PlayerMobile pm || !Members.Remove(m)) { return; } - var oldGuild = m.Guild as Guild; - m.Guild = null; - - if (m is PlayerMobile pm) - { - pm.GuildRank = RankDefinition.Lowest; - } + var oldGuild = pm.Guild as Guild; + pm.Guild = null; + pm.GuildRank = RankDefinition.Lowest; if (message > 0) { @@ -1492,12 +1484,12 @@ public void GuildChat(Mobile from, string text) } public bool CanVote(Mobile m) => - (!NewGuildSystem || m is PlayerMobile pm && pm.GuildRank.GetFlag(RankFlags.CanVote)) && - m?.Deleted == false && m.Guild == this; + m is PlayerMobile { Deleted: false } pm && pm.Guild == this && + (!NewGuildSystem || pm.GuildRank.GetFlag(RankFlags.CanVote)); public bool CanBeVotedFor(Mobile m) => - (!NewGuildSystem || m is PlayerMobile pm && pm.LastOnline + InactiveTime >= Core.Now) && - m?.Deleted == false && m.Guild == this; + m is PlayerMobile { Deleted: false } pm && pm.Guild == this && + (!NewGuildSystem || pm.LastOnline + InactiveTime >= Core.Now); public void CalculateGuildmaster() { @@ -1505,7 +1497,7 @@ public void CalculateGuildmaster() // When the leader resigns, this will be false var disbanded = Disbanded; - var hasLeader = !disbanded && m_Leader?.Guild == this; + var hasLeader = !disbanded && (m_Leader as PlayerMobile)?.Guild == this; var votingMembers = 0; for (var i = 0; i < Members.Count; ++i) @@ -1517,7 +1509,7 @@ public void CalculateGuildmaster() continue; } - var m = memb.GuildFealty; + var m = (memb as PlayerMobile)?.GuildFealty; if (!CanBeVotedFor(m)) { @@ -1588,9 +1580,9 @@ public GuildPropsTarget() : base(-1, true, TargetFlags.None) { } - protected override void OnTarget(Mobile from, object o) + protected override void OnTarget(Mobile from, object targeted) { - if (!BaseCommand.IsAccessible(from, o)) + if (!BaseCommand.IsAccessible(from, targeted)) { from.SendLocalizedMessage(500447); // That is not accessible. return; @@ -1598,7 +1590,7 @@ protected override void OnTarget(Mobile from, object o) Guild g = null; - if (o is Guildstone stone) + if (targeted is Guildstone stone) { if (stone.Guild.Disbanded) { @@ -1608,9 +1600,9 @@ protected override void OnTarget(Mobile from, object o) g = stone.Guild; } - else if (o is Mobile mobile) + else if (targeted is PlayerMobile targetedPM) { - g = mobile.Guild as Guild; + g = targetedPM.Guild as Guild; } if (g == null) diff --git a/Projects/UOContent/Misc/Keywords.cs b/Projects/UOContent/Misc/Keywords.cs index 541cf9e0c5..a203470c28 100644 --- a/Projects/UOContent/Misc/Keywords.cs +++ b/Projects/UOContent/Misc/Keywords.cs @@ -23,7 +23,7 @@ public static void EventSink_Speech(SpeechEventArgs args) { case 0x002A: // *i resign from my guild* { - ((Guild)from.Guild)?.RemoveMember(from); + ((from as PlayerMobile)?.Guild as Guild)?.RemoveMember(from); break; } diff --git a/Projects/UOContent/Misc/Notoriety.cs b/Projects/UOContent/Misc/Notoriety.cs index ff7924bb59..aa39e7e175 100644 --- a/Projects/UOContent/Misc/Notoriety.cs +++ b/Projects/UOContent/Misc/Notoriety.cs @@ -31,12 +31,14 @@ public static void Initialize() private static GuildStatus GetGuildStatus(Mobile m) { - if (m.Guild == null) + if (m is not PlayerMobile { Guild: not null } pm) { return GuildStatus.None; } - if (((Guild)m.Guild).Enemies.Count == 0 && m.Guild.Type == GuildType.Regular) + var g = (Guild)pm.Guild; + + if (g.Enemies.Count == 0 && g.Type == GuildType.Regular) { return GuildStatus.Peaceful; } @@ -142,7 +144,7 @@ public static bool Mobile_AllowBeneficial(Mobile from, Mobile target) return false; // Young players cannot perform beneficial actions towards non-young players or pets } - if (from.Guild is Guild fromGuild && target.Guild is Guild targetGuild && + if (pmFrom?.Guild is Guild fromGuild && pmTarg?.Guild is Guild targetGuild && (targetGuild == fromGuild || fromGuild.IsAlly(targetGuild))) { return true; // Guild members can be beneficial @@ -218,8 +220,8 @@ public static bool Mobile_AllowHarmful(Mobile from, Mobile target) (target as PlayerMobile)?.CheckYoungProtection(from) != true; } - var fromGuild = GetGuildFor(from.Guild as Guild, from); - var targetGuild = GetGuildFor(target.Guild as Guild, target); + var fromGuild = GetGuildFor(pmFrom?.Guild as Guild, from); + var targetGuild = GetGuildFor(pmTarg?.Guild as Guild, target); if (fromGuild != null && targetGuild != null && (fromGuild == targetGuild || fromGuild.IsAlly(targetGuild) || fromGuild.IsEnemy(targetGuild))) @@ -248,21 +250,19 @@ public static bool Mobile_AllowHarmful(Mobile from, Mobile target) public static Guild GetGuildFor(Guild def, Mobile m) { - if (m is not BaseCreature c || !c.Controlled || c.ControlMaster == null) + if (m is not BaseCreature c || !c.Controlled || c.ControlMaster is not PlayerMobile pm) { return def; } - c.DisplayGuildTitle = false; - if (c.Map != Map.Internal && (Core.AOS || Guild.NewGuildSystem || c.ControlOrder is OrderType.Attack or OrderType.Guard)) { - return (Guild)(c.Guild = c.ControlMaster.Guild); + return (Guild)pm.Guild; } - if (c.Map == Map.Internal || c.ControlMaster.Guild == null) + if (c.Map == Map.Internal || pm.Guild == null) { - return (Guild)(c.Guild = null); + return null; } return def; @@ -277,7 +277,7 @@ public static int CorpseNotoriety(Mobile source, Corpse target) Body body = target.Amount; - var sourceGuild = GetGuildFor(source.Guild as Guild, source); + var sourceGuild = GetGuildFor((source as PlayerMobile)?.Guild as Guild, source); var targetGuild = GetGuildFor(target.Guild, target.Owner); var srcFaction = Faction.Find(source, true, true); @@ -450,8 +450,8 @@ public static int MobileNotoriety(Mobile source, Mobile target) return Notoriety.Criminal; } - var sourceGuild = GetGuildFor(source.Guild as Guild, source); - var targetGuild = GetGuildFor(target.Guild as Guild, target); + var sourceGuild = GetGuildFor(pmFrom?.Guild as Guild, source); + var targetGuild = GetGuildFor(pmTarg?.Guild as Guild, target); if (sourceGuild != null && targetGuild != null) { diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index 6dde04f628..724fadc61d 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -3537,7 +3537,6 @@ public bool SetControlMaster(Mobile m) } } - Guild = null; ResetSpeeds(); Delta(MobileDelta.Noto); diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index 54c9fe4b0d..14bab5bd4c 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -143,6 +143,8 @@ public partial class PlayerMobile : Mobile, IHonorTarget, IHasSteps new(268, 624, 15) }; + public static bool GuildClickMessage { get; set; } = true; + private HashSet _acquiredRecipes; private HashSet _allFollowers; @@ -197,6 +199,12 @@ public partial class PlayerMobile : Mobile, IHonorTarget, IHasSteps private QuestArrow m_QuestArrow; + private BaseGuild _guild; + + private string _guildTitle; + + private bool _displayGuildTitle; + public PlayerMobile() { VisibilityList = new List(); @@ -373,6 +381,67 @@ public override bool Paralyzed } } + public BaseGuild Guild + { + get => _guild; + set + { + var old = _guild; + + if (old != value) + { + if (value == null) + { + GuildTitle = null; + } + + _guild = value; + + Delta(MobileDelta.Noto); + InvalidateProperties(); + + OnGuildChange(old); + } + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public string GuildTitle + { + get => _guildTitle; + set + { + var old = _guildTitle; + + if (old != value) + { + _guildTitle = value; + + if (_guild?.Disbanded == false && _guildTitle != null) + { + SendLocalizedMessage(1018026, true, _guildTitle); // Your guild title has changed : + } + + InvalidateProperties(); + OnGuildTitleChange(old); + } + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public bool DisplayGuildTitle + { + get => _displayGuildTitle; + set + { + _displayGuildTitle = value; + InvalidateProperties(); + } + } + + [CommandProperty(AccessLevel.GameMaster)] + public Mobile GuildFealty { get; set; } + [CommandProperty(AccessLevel.GameMaster)] public Player EthicPlayer { get; set; } @@ -933,6 +1002,11 @@ public void SetFlag(PlayerFlag flag, bool value) } } + public static void Configure() + { + + } + public static void Initialize() { EventSink.Logout += OnLogout; @@ -955,6 +1029,23 @@ public static void Initialize() } } } + + var guildMigrations = GuildMigrations; + if (guildMigrations?.Count > 0) + { + for (var i = 0; i < guildMigrations.Count; i++) + { + var guildInfo = guildMigrations[i]; + + if (guildInfo.GuildMember is PlayerMobile pm) + { + pm._guild = guildInfo.Guild; + pm._guildTitle = guildInfo.GuildTitle; + pm.GuildFealty = guildInfo.GuildFealty; + pm._displayGuildTitle = guildInfo.DisplayGuildTitle; + } + } + } } public static void TargetedSkillUse(Mobile from, IEntity target, int skillId) @@ -970,7 +1061,6 @@ public static void TargetedSkillUse(Mobile from, IEntity target, int skillId) { AnimalTaming.DisableMessage = true; } - // AnimalTaming.DeferredTarget = false; if (from.UseSkill(skillId)) { @@ -978,7 +1068,6 @@ public static void TargetedSkillUse(Mobile from, IEntity target, int skillId) } if (skillId == 35) - // AnimalTaming.DeferredTarget = true; { AnimalTaming.DisableMessage = false; } @@ -1974,9 +2063,9 @@ private void GetVendor() { var house = BaseHouse.FindHouseAt(this); - if (CheckAlive() && house?.IsOwner(this) == true && house.InternalizedVendors.Count > 0 && NetState is NetState { } ns) + if (CheckAlive() && house?.IsOwner(this) == true && house.InternalizedVendors.Count > 0) { - ns.SendGump(new ReclaimVendorGump(house)); + NetState?.SendGump(new ReclaimVendorGump(house)); } } @@ -2837,6 +2926,14 @@ public override void Deserialize(IGenericReader reader) switch (version) { + case 35: // Moved guild + { + GuildFealty = reader.ReadEntity(); + _guildTitle = reader.ReadString(); + _displayGuildTitle = reader.ReadBool(); + _guild = reader.ReadEntity(); + goto case 34; + } case 34: // Acquired Recipes is now a Set case 33: // Removes champion title case 32: // Removes virtue properties @@ -3181,7 +3278,12 @@ public override void Serialize(IGenericWriter writer) { base.Serialize(writer); - writer.Write(34); // version + writer.Write(35); // version + + writer.Write(GuildFealty); + writer.Write(_guildTitle); + writer.Write(_displayGuildTitle); + writer.Write(_guild); if (Stabled == null) { @@ -3426,6 +3528,11 @@ public override void GetProperties(IPropertyList list) public override void OnSingleClick(Mobile from) { + if (Deleted || AccessLevel == AccessLevel.Player && DisableHiddenSelfClick && Hidden && from == this) + { + return; + } + if (Map == Faction.Facet) { var pl = PlayerState.Find(this); @@ -3465,6 +3572,34 @@ public override void OnSingleClick(Mobile from) } } + if (GuildClickMessage) + { + var guild = _guild; + + if (guild != null && (_displayGuildTitle || guild.Type != GuildType.Regular)) + { + var title = GuildTitle?.Trim() ?? ""; + string type; + + if (guild.Type >= 0 && (int)guild.Type < _guildTypes.Length) + { + type = _guildTypes[(int)guild.Type]; + } + else + { + type = ""; + } + + var text = (title.Length <= 0) switch + { + true => $"[{guild.Abbreviation}] {type}", + false => $"[{title}, {guild.Abbreviation}] {type}" + }; + + PrivateOverheadMessage(MessageType.Regular, SpeechHue, true, text, from.NetState); + } + } + base.OnSingleClick(from); } @@ -4066,23 +4201,11 @@ public override void OnKillsChange(int oldValue) } } - public override void OnGenderChanged(bool oldFemale) - { - } - - public override void OnGuildChange(BaseGuild oldGuild) - { - } - - public override void OnGuildTitleChange(string oldTitle) + public void OnGuildTitleChange(string oldTitle) { } - public override void OnKarmaChange(int oldValue) - { - } - - public override void OnFameChange(int oldValue) + public void OnGuildChange(BaseGuild oldGuild) { } @@ -4114,6 +4237,7 @@ public override void OnRawStatChange(StatType stat, int oldValue) public override void OnDelete() { + _guild?.OnDelete(this); ReceivedHonorContext?.Cancel(); SentHonorContext?.Cancel(); @@ -4239,6 +4363,18 @@ private void InternalChangeHair(bool hair, int id, ref int storeID, ref int stor public override string ApplyNameSuffix(string suffix) { + var guild = _guild; + var hasGuild = guild != null && _displayGuildTitle; + + if (hasGuild) + { + suffix = (suffix.Length > 0) switch + { + true => $"{suffix} [{guild.Abbreviation.FixHtmlFormattable()}]", + false => $"[{guild.Abbreviation.FixHtmlFormattable()}]" + }; + } + if (Young) { suffix = suffix.Length == 0 ? "(Young)" : $"{suffix} (Young)"; @@ -4246,14 +4382,11 @@ public override string ApplyNameSuffix(string suffix) if (EthicPlayer != null) { - if (suffix.Length == 0) - { - suffix = EthicPlayer.Ethic.Definition.Adjunct.String; - } - else + suffix = (suffix.Length > 0) switch { - suffix = $"{suffix} {EthicPlayer.Ethic.Definition.Adjunct.String}"; - } + true => $"{suffix} {EthicPlayer.Ethic.Definition.Adjunct.String}", + false => EthicPlayer.Ethic.Definition.Adjunct.String, + }; } if (Core.ML && Map == Faction.Facet) @@ -4262,14 +4395,52 @@ public override string ApplyNameSuffix(string suffix) if (faction != null) { - var adjunct = $"[{faction.Definition.Abbreviation}]"; - suffix = suffix.Length == 0 ? adjunct : $"{suffix} {adjunct}"; + suffix = (suffix.Length > 0) switch + { + true => $"{suffix} [{faction.Definition.Abbreviation}]", + false => $"[{faction.Definition.Abbreviation}]", + }; } } return base.ApplyNameSuffix(suffix); } + private static readonly string[] _guildTypes = + { + "", + "(Chaos)", + "(Order)" + }; + + public virtual void AddNameProperties(IPropertyList list) + { + base.AddNameProperties(list); + + var guild = _guild; + if (guild == null || (!_displayGuildTitle && guild.Type == GuildType.Regular)) + { + return; + } + + var type = guild.Type >= 0 && (int)guild.Type < _guildTypes.Length ? _guildTypes[(int)guild.Type] : ""; + + var guildTitle = GuildTitle?.Trim() ?? ""; + + if (guildTitle.Length <= 0) + { + list.Add(guild.Name.FixHtml()); + } + else if (NewGuildDisplay) + { + list.Add($"{guildTitle.FixHtmlFormattable()}, {guild.Name.FixHtmlFormattable()}"); + } + else + { + list.Add($"{guildTitle.FixHtmlFormattable()}, {guild.Name.FixHtmlFormattable()} Guild {type}"); + } + } + public override TimeSpan GetLogoutDelay() { if (Young || BedrollLogout || TestCenter.Enabled) diff --git a/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs b/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs index c0511d5bfb..d9a0fbb667 100644 --- a/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs +++ b/Projects/UOContent/Mobiles/Special/BaseShieldGuard.cs @@ -128,7 +128,7 @@ public override void OnSpeech(SpeechEventArgs e) var from = e.Mobile; - if (from.Guild is not Guild g || g.Type != Type) + if ((from as PlayerMobile)?.Guild is not Guild g || g.Type != Type) { Say(SignupNumber); } diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index 095f955695..29cee52281 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -2454,7 +2454,8 @@ public virtual bool IsCombatRestricted(Mobile m) if (info.Defender.Player && info.Defender.Alive && Core.Now - info.LastCombatTime < HouseRegion.CombatHeatDelay && - (m.Guild is not Guild attackerGuild || info.Defender.Guild is not Guild defenderGuild || + ((m as PlayerMobile)?.Guild is not Guild attackerGuild || + (info.Defender as PlayerMobile)?.Guild is not Guild defenderGuild || defenderGuild != attackerGuild && !defenderGuild.IsEnemy(attackerGuild))) { return true; @@ -3532,7 +3533,8 @@ public bool IsCoOwner(Mobile m) => m != null && CoOwners != null && (IsOwner(m) || CoOwners.Contains(m) || !IsAosRules && AccountHandler.CheckAccount(m, m_Owner)); - public bool IsGuildMember(Mobile m) => m != null && Owner?.Guild != null && m.Guild == Owner.Guild; + public bool IsGuildMember(Mobile m) => (Owner as PlayerMobile)?.Guild is Guild ownerGuild && + (m as PlayerMobile)?.Guild == ownerGuild; public void RemoveKeys(Mobile m) { diff --git a/Projects/UOContent/Network/MapUO.cs b/Projects/UOContent/Network/MapUO.cs index 695d9fd01b..0ade12535b 100644 --- a/Projects/UOContent/Network/MapUO.cs +++ b/Projects/UOContent/Network/MapUO.cs @@ -18,6 +18,7 @@ using System.IO; using Server.Engines.PartySystem; using Server.Guilds; +using Server.Mobiles; namespace Server.Network; @@ -33,7 +34,7 @@ public static void QueryGuildMemberLocations(NetState state, SpanReader reader) { Mobile from = state.Mobile; - state.SendGuildMemberLocations(from, from.Guild as Guild, reader.ReadBoolean()); + state.SendGuildMemberLocations(from, (from as PlayerMobile)?.Guild as Guild, reader.ReadBoolean()); } public static void QueryPartyMemberLocations(NetState state, SpanReader reader) diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index 82de9a522e..5c9e8ab011 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -409,29 +409,18 @@ public static int GetOffset(Mobile caster, Mobile target, StatType type, bool cu public static Guild GetGuildFor(Mobile m) { - var g = m.Guild as Guild; - - if (g == null && m is BaseCreature c) + if (m is PlayerMobile pm) { - m = c.ControlMaster; - - if (m != null) - { - g = m.Guild as Guild; - } + return pm.Guild as Guild; + } - if (g == null) - { - m = c.SummonMaster; - if (m != null) - { - g = m.Guild as Guild; - } - } + if (m is BaseCreature c) + { + return GetGuildFor(c.GetMaster()); } - return g; + return null; } public static bool ValidIndirectTarget(Mobile from, Mobile to) diff --git a/Projects/UOContent/Spells/Necromancy/Exorcism.cs b/Projects/UOContent/Spells/Necromancy/Exorcism.cs index 298b76e18f..e06f3540e7 100644 --- a/Projects/UOContent/Spells/Necromancy/Exorcism.cs +++ b/Projects/UOContent/Spells/Necromancy/Exorcism.cs @@ -5,6 +5,7 @@ using Server.Factions; using Server.Guilds; using Server.Items; +using Server.Mobiles; using Server.Regions; namespace Server.Spells.Necromancy; @@ -148,15 +149,11 @@ private bool IsValidTarget(Mobile m) return false; } - if (m.Guild != null && Caster.Guild != null) + if ((m as PlayerMobile)?.Guild is Guild mGuild && + (Caster as PlayerMobile)?.Guild is Guild cGuild && + (mGuild.IsAlly(cGuild) || mGuild == cGuild)) { - var mGuild = m.Guild as Guild; - var cGuild = Caster.Guild as Guild; - - if (mGuild?.IsAlly(cGuild) == true || mGuild == cGuild) - { - return false; - } + return false; } var f = Faction.Find(m); @@ -208,4 +205,4 @@ private static Point3D GetNearestShrine(Mobile m) return closest; } -} \ No newline at end of file +}