From 8c6eab5fca9a5129fab1202c071e243b036994f0 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 2 Jul 2026 22:28:55 -0700 Subject: [PATCH] feat(throwing): wire SA loot flavor + Valkyrie's Glaive stealable (#2512) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2 follow-up to #2510 — wires acquisition for the gargoyle Throwing content that Phase 1 deliberately left out. ## Changes - **SA loot flavor (`IsStygian`)** — completes the dead loot path from the original PR: adds `SAWeaponTypes`/`SARangedWeaponTypes` pools + `isStygian` branches to `Loot.RandomWeapon`/`RandomRangedWeapon`, **and** the missing piece — computes `IsStygian` (`map == Map.TerMur`) in `LootPackEntry.Construct` and threads it through `LootPackItem.Construct`. Ter Mur creatures now roll SA gear + the three throwing weapons (Boomerang/Cyclone/SoulGlaive) from their normal `BaseWeapon`/`BaseRanged` loot entries. Conservative `TerMur`-only (not ServUO's extra `|| RandomBool()`). - **Valkyrie's Glaive** — re-added as a self-contained Ter Mur stealable artifact at `(843, 665, 27)`, matching ServUO/OSI, with the previously-missing `ArtifactRarity => 5`. ## Deferred (documented) The 5 host-dependent artifacts (Raptor Claw, Stone Slith Claw, Storm Caller, Banshee's Call, Wind of Corruption) are intentionally NOT included — their OSI drop sources (Raptor, StoneSlith, and the renowned/boss creatures + a `BaseRenowned` artifact-list system) don't exist in ModernUO yet, so re-adding the items now would leave them `[add`-only. They'll follow a dedicated SA-creatures effort. ## Notes - No new tests: both changes are property/plumbing with RNG-driven output — no deterministic complex logic to assert (consistent with the repo's test-scope conventions). - Verified: server + test project compile; throwing suite 14/14. --- .../Engines/Stealables/StealableArtifacts.cs | 5 +++- .../Items/Weapons/Throwing/ValkyriesGlaive.cs | 26 ++++++++++++++++++ .../Server.Items.ValkyriesGlaive.v0.json | 4 +++ Projects/UOContent/Misc/Loot.cs | 27 +++++++++++++++++-- Projects/UOContent/Misc/LootPack.cs | 11 +++++--- 5 files changed, 66 insertions(+), 7 deletions(-) create mode 100644 Projects/UOContent/Items/Weapons/Throwing/ValkyriesGlaive.cs create mode 100644 Projects/UOContent/Migrations/Server.Items.ValkyriesGlaive.v0.json diff --git a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs index 1ac218ad2..396b87dcd 100644 --- a/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs +++ b/Projects/UOContent/Engines/Stealables/StealableArtifacts.cs @@ -170,7 +170,10 @@ public class StealableArtifacts : GenericPersistence new(Map.Malas, new Point3D(157, 608, -1), 18432, 27648, typeof(SwordDisplay5WestArtifact)), new(Map.Malas, new Point3D(187, 643, 1), 18432, 27648, typeof(Painting6NorthArtifact)), new(Map.Malas, new Point3D(146, 623, 1), 18432, 27648, typeof(Painting6WestArtifact)), - new(Map.Malas, new Point3D(178, 629, -1), 18432, 27648, typeof(ManStatuetteEastArtifact)) + new(Map.Malas, new Point3D(178, 629, -1), 18432, 27648, typeof(ManStatuetteEastArtifact)), + + // Stygian Abyss (Ter Mur) - Artifact rarity 5 + new(Map.TerMur, new Point3D(843, 665, 27), 1152, 1728, typeof(ValkyriesGlaive)) }; public static Type[] TypesOfEntries diff --git a/Projects/UOContent/Items/Weapons/Throwing/ValkyriesGlaive.cs b/Projects/UOContent/Items/Weapons/Throwing/ValkyriesGlaive.cs new file mode 100644 index 000000000..beded2540 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/Throwing/ValkyriesGlaive.cs @@ -0,0 +1,26 @@ +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0)] +public partial class ValkyriesGlaive : SoulGlaive +{ + [Constructible] + public ValkyriesGlaive() + { + Hue = 1651; + Attributes.SpellChanneling = 1; + Attributes.BonusStr = 5; + Attributes.WeaponSpeed = 20; + Attributes.WeaponDamage = 20; + Slayer = SlayerName.Silver; + WeaponAttributes.HitFireball = 40; + } + + public override int LabelNumber => 1113531; // Valkyrie's Glaive + + public override int ArtifactRarity => 5; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; +} diff --git a/Projects/UOContent/Migrations/Server.Items.ValkyriesGlaive.v0.json b/Projects/UOContent/Migrations/Server.Items.ValkyriesGlaive.v0.json new file mode 100644 index 000000000..227fe419a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.ValkyriesGlaive.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.ValkyriesGlaive" +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/Loot.cs b/Projects/UOContent/Misc/Loot.cs index b641400a2..2477c25b5 100644 --- a/Projects/UOContent/Misc/Loot.cs +++ b/Projects/UOContent/Misc/Loot.cs @@ -6,6 +6,17 @@ namespace Server { public static class Loot { + public static Type[] SAWeaponTypes { get; } = + { + typeof(DiscMace), typeof(GargishTalwar), typeof(DualPointedSpear), + typeof(GlassStaff), typeof(DualShortAxes), typeof(GlassSword) + }; + + public static Type[] SARangedWeaponTypes { get; } = + { + typeof(Boomerang), typeof(Cyclone), typeof(SoulGlaive) + }; + public static Type[] MLWeaponTypes { get; } = { typeof(AssassinSpike), typeof(DiamondMace), typeof(ElvenMachete), @@ -374,12 +385,18 @@ namespace Server return Construct(ClothingTypes); } + private static readonly Type[][] _saRangedWeaponTypes = [SARangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes]; private static readonly Type[][] _mlRangedWeaponTypes = [MLRangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes]; private static readonly Type[][] _seRangedWeaponTypes = [SERangedWeaponTypes, AosRangedWeaponTypes, RangedWeaponTypes]; private static readonly Type[][] _aosRangedWeaponTypes = [AosRangedWeaponTypes, RangedWeaponTypes]; - public static BaseWeapon RandomRangedWeapon(bool inTokuno = false, bool isMondain = false) + public static BaseWeapon RandomRangedWeapon(bool inTokuno = false, bool isMondain = false, bool isStygian = false) { + if (Core.SA && isStygian) + { + return Construct(_saRangedWeaponTypes); + } + if (Core.ML && isMondain) { return Construct(_mlRangedWeaponTypes); @@ -398,12 +415,18 @@ namespace Server return Construct(RangedWeaponTypes); } + private static readonly Type[][] _saWeaponTypes = [SAWeaponTypes, AosWeaponTypes, WeaponTypes]; private static readonly Type[][] _mlWeaponTypes = [MLWeaponTypes, AosWeaponTypes, WeaponTypes]; private static readonly Type[][] _seWeaponTypes = [SEWeaponTypes, AosWeaponTypes, WeaponTypes]; private static readonly Type[][] _aosWeaponTypes = [AosWeaponTypes, WeaponTypes]; - public static BaseWeapon RandomWeapon(bool inTokuno = false, bool isMondain = false) + public static BaseWeapon RandomWeapon(bool inTokuno = false, bool isMondain = false, bool isStygian = false) { + if (Core.SA && isStygian) + { + return Construct(_saWeaponTypes); + } + if (Core.ML && isMondain) { return Construct(_mlWeaponTypes); diff --git a/Projects/UOContent/Misc/LootPack.cs b/Projects/UOContent/Misc/LootPack.cs index e5bd810b0..92cb06eb2 100644 --- a/Projects/UOContent/Misc/LootPack.cs +++ b/Projects/UOContent/Misc/LootPack.cs @@ -619,6 +619,9 @@ namespace Server private static bool IsMondain(Mobile m) => MondainsLegacy.IsMLRegion(m.Region); + // Stygian Abyss loot flavor: creatures in Ter Mur draw from the SA gear pools. + private static bool IsStygian(Mobile m) => m.Map == Map.TerMur; + public Item Construct(Mobile from, int luckChance, bool spawning) { if (m_AtSpawnTime != spawning) @@ -641,7 +644,7 @@ namespace Server if (rnd < item.Chance) { - return Mutate(from, luckChance, item.Construct(IsInTokuno(from), IsMondain(from))); + return Mutate(from, luckChance, item.Construct(IsInTokuno(from), IsMondain(from), IsStygian(from))); } rnd -= item.Chance; @@ -970,18 +973,18 @@ namespace Server return Loot.RandomScroll(minCircle * 8, maxCircle * 8 + 7, SpellbookType.Regular); } - public Item Construct(bool inTokuno, bool isMondain) + public Item Construct(bool inTokuno, bool isMondain, bool isStygian) { try { if (Type == typeof(BaseRanged)) { - return Loot.RandomRangedWeapon(inTokuno, isMondain); + return Loot.RandomRangedWeapon(inTokuno, isMondain, isStygian); } if (Type == typeof(BaseWeapon)) { - return Loot.RandomWeapon(inTokuno, isMondain); + return Loot.RandomWeapon(inTokuno, isMondain, isStygian); } if (Type == typeof(BaseArmor))