feat(throwing): wire SA loot flavor + Valkyrie's Glaive stealable (#2512)

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.
This commit is contained in:
Kamron Batman 2026-07-02 22:28:55 -07:00 committed by GitHub
parent e42a62b1d3
commit 8c6eab5fca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 66 additions and 7 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Items.ValkyriesGlaive"
}

View file

@ -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<BaseClothing>(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<BaseWeapon>(_saRangedWeaponTypes);
}
if (Core.ML && isMondain)
{
return Construct<BaseWeapon>(_mlRangedWeaponTypes);
@ -398,12 +415,18 @@ namespace Server
return Construct<BaseWeapon>(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<BaseWeapon>(_saWeaponTypes);
}
if (Core.ML && isMondain)
{
return Construct<BaseWeapon>(_mlWeaponTypes);

View file

@ -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))