feat(throwing): add Raptor & StoneSlith and their SA artifact claws

Port Raptor and StoneSlith from ServUO (stat-for-stat) so the throwing
artifacts RaptorClaw and StoneSlithClaw finally have a real OSI drop source.
Both creatures spawn automatically from the Raptor/StoneSlith entries that
already exist in Distribution/Data/Spawns/post-uoml/termur/TerMur.json, and
each drops its claw from OnDeath at the ServUO 0.5% rate (uncontrolled only).
StoneSlith keeps its GraspingClaw monster ability; both use BleedAttack.

Idiomatic ModernUO: default range perception (16) and fight range, derived
GetSpeeds, [SerializationGenerator(0)] codegen, collection-expression arrays.

Documented omissions vs ServUO (missing from ModernUO, flagged as TODO):
Raptor's friend-spawn timer + 25% AncientPotteryFragments drop; StoneSlith's
TailSwipe, DragonBlood, and SlithEye/TatteredAncientScroll/AncientPottery
drops; plus HideType.Horned/Spined and PackInstinct.Ostard.

Co-Authored-By: Joe Valentine <jwvalentine@outlook.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kamron Batman 2026-07-02 23:20:30 -07:00
parent 8c6eab5fca
commit 0ddc044ba0
8 changed files with 209 additions and 0 deletions

View file

@ -0,0 +1,23 @@
using ModernUO.Serialization;
namespace Server.Items;
[SerializationGenerator(0)]
public partial class RaptorClaw : Boomerang
{
[Constructible]
public RaptorClaw()
{
Hue = 53;
Attributes.AttackChance = 12;
Attributes.WeaponSpeed = 30;
Attributes.WeaponDamage = 35;
Slayer = SlayerName.Silver;
WeaponAttributes.HitLeechStam = 40;
}
public override int LabelNumber => 1112394; // Raptor Claw
public override int InitMinHits => 255;
public override int InitMaxHits => 255;
}

View file

@ -0,0 +1,23 @@
using ModernUO.Serialization;
namespace Server.Items;
[SerializationGenerator(0)]
public partial class StoneSlithClaw : Cyclone
{
[Constructible]
public StoneSlithClaw()
{
Hue = 1150;
Attributes.WeaponSpeed = 25;
Attributes.WeaponDamage = 45;
Slayer = SlayerName.DaemonDismissal;
WeaponAttributes.HitHarm = 40;
WeaponAttributes.HitLowerDefend = 40;
}
public override int LabelNumber => 1112393; // Stone Slith Claw
public override int InitMinHits => 255;
public override int InitMaxHits => 255;
}

View file

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

View file

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

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.Raptor"
}

View file

@ -0,0 +1,4 @@
{
"version": 0,
"type": "Server.Mobiles.StoneSlith"
}

View file

@ -0,0 +1,75 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class Raptor : BaseCreature
{
[Constructible]
public Raptor() : base(AIType.AI_Melee, FightMode.Closest)
{
Body = 730;
SetStr(404, 471);
SetDex(132, 155);
SetInt(105, 145);
SetHits(343, 400);
SetDamage(11, 17);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 45, 50);
SetResistance(ResistanceType.Fire, 50, 60);
SetResistance(ResistanceType.Cold, 40, 50);
SetResistance(ResistanceType.Poison, 20, 30);
SetResistance(ResistanceType.Energy, 30, 40);
SetSkill(SkillName.MagicResist, 75.1, 90.0);
SetSkill(SkillName.Tactics, 75.1, 100.0);
SetSkill(SkillName.Wrestling, 70.1, 95.1);
Fame = 7500;
Karma = -7500;
Tamable = true;
MinTameSkill = 107.1;
ControlSlots = 2;
// TODO(SA-creatures): ServUO Raptors summon up to 2 "friend" raptors on entering combat
// (InternalTimer/CheckFriends). Omitted pending a ModernUO timer/MonsterAbility adaptation.
// Also unported vs ServUO: the 25% AncientPotteryFragments corpse drop (item class not yet in
// ModernUO), PackInstinct.Ostard, and HideType.Horned (no ModernUO equivalent yet).
}
public override string CorpseName => "a raptor corpse";
public override string DefaultName => "a raptor";
public override int TreasureMapLevel => 3;
public override int Meat => 7;
public override int Hides => 11;
public override WeaponAbility GetWeaponAbility() => WeaponAbility.BleedAttack;
public override int GetIdleSound() => 1573;
public override int GetAngerSound() => 1570;
public override int GetHurtSound() => 1572;
public override int GetDeathSound() => 1571;
public override void GenerateLoot()
{
AddLoot(LootPack.Rich, 2);
}
public override void OnDeath(Container c)
{
base.OnDeath(c);
if (!Controlled && Utility.RandomDouble() <= 0.005)
{
c.DropItem(new RaptorClaw());
}
}
}

View file

@ -0,0 +1,72 @@
using ModernUO.Serialization;
using Server.Items;
namespace Server.Mobiles;
[SerializationGenerator(0)]
public partial class StoneSlith : BaseCreature
{
private static readonly MonsterAbility[] _abilities = [MonsterAbilities.GraspingClaw];
[Constructible]
public StoneSlith() : base(AIType.AI_Melee, FightMode.Closest)
{
Body = 734;
SetStr(250, 300);
SetDex(76, 90);
SetInt(34, 69);
SetHits(154, 166);
SetStam(76, 90);
SetMana(34, 69);
SetDamage(6, 24);
SetDamageType(ResistanceType.Physical, 100);
SetResistance(ResistanceType.Physical, 50, 55);
SetResistance(ResistanceType.Fire, 20, 30);
SetResistance(ResistanceType.Cold, 10, 20);
SetResistance(ResistanceType.Poison, 30, 40);
SetResistance(ResistanceType.Energy, 30, 40);
SetSkill(SkillName.MagicResist, 86.8, 95.1);
SetSkill(SkillName.Tactics, 82.6, 88.6);
SetSkill(SkillName.Wrestling, 75.8, 87.4);
SetSkill(SkillName.Anatomy, 0.0, 2.9);
Tamable = true;
MinTameSkill = 65.1;
ControlSlots = 2;
// TODO(SA-creatures): unported vs ServUO — SpecialAbility.TailSwipe (no ModernUO equivalent),
// DragonBlood carving, HideType.Spined, and the minor drops SlithEye / TatteredAncientScroll /
// AncientPotteryFragments (item classes not yet in ModernUO).
}
public override string CorpseName => "a slith corpse";
public override string DefaultName => "a stone slith";
public override int Meat => 1;
public override int Hides => 12;
public override WeaponAbility GetWeaponAbility() => WeaponAbility.BleedAttack;
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
public override void GenerateLoot()
{
AddLoot(LootPack.Average, 2);
}
public override void OnDeath(Container c)
{
base.OnDeath(c);
if (!Controlled && Utility.RandomDouble() <= 0.005)
{
c.DropItem(new StoneSlithClaw());
}
}
}