diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index cfaecb41a8..8736341a75 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -4124,17 +4124,7 @@ public virtual void OnAosSingleClick(Mobile from) } } - public static void AppendWithSpace(StringBuilder builder, string text) - { - if (!string.IsNullOrEmpty(text)) - { - return; - } - if (builder.Length > 0) builder.Append(" "); - builder.Append(text); - } - - public virtual void OnSingleClickPreAOS(Mobile from) + public virtual void OnSingleClickPreUOTD(Mobile from) { return; } diff --git a/Projects/Server/Text/StringHelpers.cs b/Projects/Server/Text/StringHelpers.cs index 3304587272..99c5ce0c64 100644 --- a/Projects/Server/Text/StringHelpers.cs +++ b/Projects/Server/Text/StringHelpers.cs @@ -18,6 +18,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Server.Buffers; +using Server.Text; namespace Server; @@ -286,4 +287,18 @@ public static char[] ToPooledArray(this string str) str.CopyTo(chars); return chars; } + + public static void AppendWithArticle(this ref ValueStringBuilder builder, string text, bool articleAn) + { + if (builder.Length == 0) + { + builder.Append(articleAn ? "an " : "a "); + } + else + { + builder.Append(' '); + } + + builder.Append(text); + } } diff --git a/Projects/UOContent/Items/Armor/BaseArmor.cs b/Projects/UOContent/Items/Armor/BaseArmor.cs index 29aee742ff..2bc9ab1b90 100644 --- a/Projects/UOContent/Items/Armor/BaseArmor.cs +++ b/Projects/UOContent/Items/Armor/BaseArmor.cs @@ -1454,7 +1454,7 @@ public override void OnSingleClick(Mobile from) { if (!Core.AOS) { - OnSingleClickPreAOS(from); + OnSingleClickPreUOTD(from); return; } @@ -1520,7 +1520,7 @@ public override void OnSingleClick(Mobile from) from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs); } - public override void OnSingleClickPreAOS(Mobile from) + public override void OnSingleClickPreUOTD(Mobile from) { string prefix = null; string suffix = null; diff --git a/Projects/UOContent/Items/Clothing/BaseClothing.cs b/Projects/UOContent/Items/Clothing/BaseClothing.cs index e1eaabec4e..7bf31db439 100644 --- a/Projects/UOContent/Items/Clothing/BaseClothing.cs +++ b/Projects/UOContent/Items/Clothing/BaseClothing.cs @@ -909,7 +909,7 @@ public override void OnSingleClick(Mobile from) { if (!Core.AOS) { - OnSingleClickPreAOS(from); + OnSingleClickPreUOTD(from); return; } var attrs = new List(); @@ -936,7 +936,7 @@ public override void OnSingleClick(Mobile from) from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs); } - public override void OnSingleClickPreAOS(Mobile from) + public override void OnSingleClickPreUOTD(Mobile from) { var qualityText = Quality != ClothingQuality.Regular ? Localization.GetText(1018305 - (int)Quality, from.Language)?.ToLowerInvariant() ?? "" : ""; diff --git a/Projects/UOContent/Items/Wands/BaseWand.cs b/Projects/UOContent/Items/Wands/BaseWand.cs index 15f9d1eab0..6586cd3602 100644 --- a/Projects/UOContent/Items/Wands/BaseWand.cs +++ b/Projects/UOContent/Items/Wands/BaseWand.cs @@ -171,7 +171,7 @@ public override void OnSingleClick(Mobile from) { if (!Core.AOS) { - OnSingleClickPreAOS(from); + OnSingleClickPreUOTD(from); return; } @@ -237,7 +237,7 @@ public override void OnSingleClick(Mobile from) from.NetState.SendDisplayEquipmentInfo(Serial, number, Crafter, false, attrs); } - public override void OnSingleClickPreAOS(Mobile from) + public override void OnSingleClickPreUOTD(Mobile from) { string prefix = null; string suffix = null; diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 45fbd604b3..91f22c42d2 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Text; using ModernUO.Serialization; using Server.Collections; using Server.Engines.Craft; @@ -18,6 +17,7 @@ using Server.Spells.Ninjitsu; using Server.Spells.Sixth; using Server.Spells.Spellweaving; +using Server.Text; namespace Server.Items; @@ -3333,9 +3333,9 @@ out var direct public override void OnSingleClick(Mobile from) { - if (!Core.AOS) + if (Core.Expansion < Expansion.UOTD) { - OnSingleClickPreAOS(from); + OnSingleClickPreUOTD(from); return; } @@ -3430,91 +3430,116 @@ public override void OnSingleClick(Mobile from) from.NetState.SendDisplayEquipmentInfo(Serial, number, _crafter, false, attrs); } - public override void OnSingleClickPreAOS(Mobile from) + public override void OnSingleClickPreUOTD(Mobile from) { - string prefix = null; - string suffix = null; - string slayerSuffix = SlayerNameExtensions.GetSlayerNamePreAOS(_slayer, from); - - var isMagicItem = _durabilityLevel != WeaponDurabilityLevel.Regular || - _accuracyLevel != WeaponAccuracyLevel.Regular || - _damageLevel != WeaponDamageLevel.Regular || - slayerSuffix != "none"; - - // Construct prefix and suffix - var prefixBuilder = new StringBuilder(); - var suffixBuilder = new StringBuilder(); + var articleAnName = (TileData.ItemTable[ItemID].Flags & TileFlag.ArticleAn) != 0; - if (isMagicItem && !_identified) + if (!_identified) { - prefix = Localization.GetText(1038000, from.Language)?.ToLowerInvariant(); + LabelTo(from, $"an unidentified {Name ?? Localization.GetText(LabelNumber)}"); + return; } - else + + var isMagicItem = _durabilityLevel > WeaponDurabilityLevel.Regular || + _accuracyLevel > WeaponAccuracyLevel.Regular || + _damageLevel > WeaponDamageLevel.Regular; + + if (isMagicItem) { - var qualityText = Quality != WeaponQuality.Regular - ? Localization.GetText(1018305 - (int)Quality, from.Language)?.ToLowerInvariant() ?? "" : ""; + var builder = ValueStringBuilder.Create(128); - var durabilityText = _durabilityLevel != WeaponDurabilityLevel.Regular - ? Localization.GetText(1038000 + (int)_durabilityLevel, from.Language)?.ToLowerInvariant() ?? "" : ""; + var durabilityText = DurabilityText(out var articleAnDurability); + if (durabilityText != null) + { + builder.AppendWithArticle(durabilityText, articleAnDurability); + } - var accuracyText = _accuracyLevel != WeaponAccuracyLevel.Regular - ? Localization.GetText(1038010 + (int)_accuracyLevel, from.Language)?.ToLowerInvariant() ?? "" : ""; + var accuracyText = AccuracyText(out var articleAnAccuracy); + if (accuracyText != null) + { + builder.AppendWithArticle(accuracyText, articleAnAccuracy); + } - var damageText = _damageLevel != WeaponDamageLevel.Regular - ? Localization.GetText(1038015 + (int)_damageLevel, from.Language)?.ToLowerInvariant() ?? "" : ""; + var slayerText = SlayerGroup.GetEntryByName(_slayer).SlayerText(out var articleAnSlayer); + if (slayerText != null) + { + builder.AppendWithArticle(slayerText, articleAnSlayer); + } - // Append text - AppendWithSpace(prefixBuilder, qualityText); - AppendWithSpace(prefixBuilder, durabilityText); - AppendWithSpace(prefixBuilder, accuracyText); - AppendWithSpace(suffixBuilder, damageText); + builder.AppendWithArticle(Name ?? Localization.GetText(LabelNumber), articleAnName); - // Append slayer type - if (!string.Equals(slayerSuffix, "none", StringComparison.OrdinalIgnoreCase)) + var weaponLevelText = WeaponLevelText(); + if (weaponLevelText != null) { - if (suffixBuilder.Length > 0) - { - suffixBuilder.Append(" and "); - } - suffixBuilder.Append(slayerSuffix); + builder.Append($" of {weaponLevelText}"); } - // Convert to strings - prefix = prefixBuilder.Length > 0 ? prefixBuilder.ToString() : null; - suffix = suffixBuilder.Length > 0 ? suffixBuilder.ToString() : null; - } + // TODO: Spells (of Ghoul's Touch) - // Add any unique name - if (Name != null && _identified) - { - LabelTo(from, Name); + LabelTo(from, builder.ToString()); + builder.Dispose(); + return; } - // Add label - if (prefix != null && suffix != null) // ~1_PREFIX~ ~2_ITEM~ of ~3_SUFFIX~ - { - LabelTo(from, 1151756, $"{prefix}\t#{LabelNumber}\t{suffix}"); - } - else if (prefix != null && suffix == null) // ~1_PREFIX~ ~2_ITEM~ - { - LabelTo(from, 1151757, $"{prefix}\t#{LabelNumber}"); - } - else if (prefix == null && suffix != null) // ~1_ITEM~ of ~2_SUFFIX~ + var name = Name ?? + $"{(articleAnName ? "an" : "a")} {Localization.GetText(LabelNumber)}"; + + if (Crafter == null) { - LabelTo(from, 1151758, $"#{LabelNumber}\t{suffix}"); + LabelTo(from, Quality == WeaponQuality.Exceptional ? $"{name} of exceptional quality" : name); + return; } - else + + LabelTo( + from, + Quality == WeaponQuality.Exceptional + ? $"{name} crafted with exceptional quality by {Crafter}" + : $"{name} crafted by {Crafter}" + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private string DurabilityText(out bool articleAn) + { + articleAn = _durabilityLevel is WeaponDurabilityLevel.Indestructible; + return _durabilityLevel switch { - LabelTo(from, LabelNumber); - } + WeaponDurabilityLevel.Durable => "durable", + WeaponDurabilityLevel.Substantial => "substantial", + WeaponDurabilityLevel.Massive => "massive", + WeaponDurabilityLevel.Fortified => "fortified", + WeaponDurabilityLevel.Indestructible => "indestructible", + _ => null + }; + } - // Add maker's mark - if (PlayerConstructed && Crafter != null) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private string AccuracyText(out bool articleAn) + { + articleAn = _accuracyLevel is WeaponAccuracyLevel.Accurate or WeaponAccuracyLevel.Eminently; + return _accuracyLevel switch { - LabelTo(from, 1050043, Crafter.ToString()); // crafted by ~1_NAME~ - } + WeaponAccuracyLevel.Accurate => "accurate", + WeaponAccuracyLevel.Surpassingly => "surpassingly accurate", + WeaponAccuracyLevel.Eminently => "eminently accurate", + WeaponAccuracyLevel.Exceedingly => "exceedingly accurate", + WeaponAccuracyLevel.Supremely => "supremely accurate", + _ => null + }; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private string WeaponLevelText() => + _damageLevel switch + { + WeaponDamageLevel.Ruin => "ruin", + WeaponDamageLevel.Might => "might", + WeaponDamageLevel.Force => "force", + WeaponDamageLevel.Power => "power", + WeaponDamageLevel.Vanq => "vanquishing", + _ => null + }; + public virtual int GetHitAttackSound(Mobile attacker, Mobile defender) { var sound = attacker.GetAttackSound(); diff --git a/Projects/UOContent/Items/Weapons/SlayerEntry.cs b/Projects/UOContent/Items/Weapons/SlayerEntry.cs index 1acec3c599..ccd1a7682a 100644 --- a/Projects/UOContent/Items/Weapons/SlayerEntry.cs +++ b/Projects/UOContent/Items/Weapons/SlayerEntry.cs @@ -88,6 +88,20 @@ public int Title } } + public string SlayerText(out bool articleAn) + { + if (Name == SlayerName.None) + { + articleAn = false; + return null; + } + + articleAn = Name is SlayerName.OrcSlaying or SlayerName.OgreTrashing or SlayerName.Exorcism or SlayerName.Ophidian + or SlayerName.ArachnidDoom or SlayerName.ElementalBan or SlayerName.ElementalHealth or SlayerName.EarthShatter; + + return Localization.GetText(Title)?.ToLowerInvariant(); + } + public bool Slays(Mobile m) { var t = m.GetType(); diff --git a/Projects/UOContent/Items/Weapons/SlayerName.cs b/Projects/UOContent/Items/Weapons/SlayerName.cs index 01d104076f..f42fe151d9 100644 --- a/Projects/UOContent/Items/Weapons/SlayerName.cs +++ b/Projects/UOContent/Items/Weapons/SlayerName.cs @@ -1,77 +1,33 @@ -namespace Server.Items -{ - public enum SlayerName - { - None, - Silver, - OrcSlaying, - TrollSlaughter, - OgreTrashing, - Repond, - DragonSlaying, - Terathan, - SnakesBane, - LizardmanSlaughter, - ReptilianDeath, - DaemonDismissal, - GargoylesFoe, - BalronDamnation, - Exorcism, - Ophidian, - SpidersDeath, - ScorpionsBane, - ArachnidDoom, - FlameDousing, - WaterDissipation, - Vacuum, - ElementalHealth, - EarthShatter, - BloodDrinking, - SummerWind, - ElementalBan, // Bane? - Fey - } - - public static class SlayerNameExtensions - { - public static string GetSlayerNamePreAOS(this SlayerName slayerName, Mobile from) - { - if (slayerName == SlayerName.None) - { - return "none"; - } +namespace Server.Items; - return slayerName switch - { - SlayerName.Silver => Localization.GetText(1017384, from.Language).ToLowerInvariant(), - SlayerName.OrcSlaying => Localization.GetText(1017385, from.Language).ToLowerInvariant(), - SlayerName.TrollSlaughter => Localization.GetText(1017386, from.Language).ToLowerInvariant(), - SlayerName.OgreTrashing => Localization.GetText(1017387, from.Language).ToLowerInvariant(), - SlayerName.Repond => Localization.GetText(1017388, from.Language).ToLowerInvariant(), - SlayerName.DragonSlaying => Localization.GetText(1017389, from.Language).ToLowerInvariant(), - SlayerName.Terathan => Localization.GetText(1017390, from.Language).ToLowerInvariant(), - SlayerName.SnakesBane => Localization.GetText(1017391, from.Language).ToLowerInvariant(), - SlayerName.LizardmanSlaughter => Localization.GetText(1017392, from.Language).ToLowerInvariant(), - SlayerName.ReptilianDeath => Localization.GetText(1017393, from.Language).ToLowerInvariant(), - SlayerName.DaemonDismissal => Localization.GetText(1017394, from.Language).ToLowerInvariant(), - SlayerName.GargoylesFoe => Localization.GetText(1017395, from.Language).ToLowerInvariant(), - SlayerName.BalronDamnation => Localization.GetText(1017396, from.Language).ToLowerInvariant(), - SlayerName.Exorcism => Localization.GetText(1017397, from.Language).ToLowerInvariant(), - SlayerName.Ophidian => Localization.GetText(1017398, from.Language).ToLowerInvariant(), - SlayerName.SpidersDeath => Localization.GetText(1017399, from.Language).ToLowerInvariant(), - SlayerName.ScorpionsBane => Localization.GetText(1017400, from.Language).ToLowerInvariant(), - SlayerName.ArachnidDoom => Localization.GetText(1017401, from.Language).ToLowerInvariant(), - SlayerName.FlameDousing => Localization.GetText(1017402, from.Language).ToLowerInvariant(), - SlayerName.WaterDissipation => Localization.GetText(1017403, from.Language).ToLowerInvariant(), - SlayerName.Vacuum => Localization.GetText(1017404, from.Language).ToLowerInvariant(), - SlayerName.ElementalHealth => Localization.GetText(1017405, from.Language).ToLowerInvariant(), - SlayerName.EarthShatter => Localization.GetText(1017406, from.Language).ToLowerInvariant(), - SlayerName.BloodDrinking => Localization.GetText(1017407, from.Language).ToLowerInvariant(), - SlayerName.SummerWind => Localization.GetText(1017408, from.Language).ToLowerInvariant(), - SlayerName.ElementalBan => Localization.GetText(1017409, from.Language).ToLowerInvariant(), - SlayerName.Fey => Localization.GetText(1070855, from.Language).ToLowerInvariant(), - _ => "unknown slayer" - }; - } - } +public enum SlayerName +{ + None, + Silver, + OrcSlaying, + TrollSlaughter, + OgreTrashing, + Repond, + DragonSlaying, + Terathan, + SnakesBane, + LizardmanSlaughter, + ReptilianDeath, + DaemonDismissal, + GargoylesFoe, + BalronDamnation, + Exorcism, + Ophidian, + SpidersDeath, + ScorpionsBane, + ArachnidDoom, + FlameDousing, + WaterDissipation, + Vacuum, + ElementalHealth, + EarthShatter, + BloodDrinking, + SummerWind, + ElementalBan, // Bane? + Fey }