ModernUO/Projects/UOContent/Mobiles/Monsters/Reptile/Magic/Leviathan.cs
Sergi Rosell f4ba6036fe
fix: fishing catch chances by era and the leviathan's treasure map (#2709)
Part of #2687. Magic fish and footwear roll the same in every era: magic fish get rarer with skill (5% at 0 skill, 1% at GM) and footwear has a 20% chance at 0 skill. The leviathan rolls the generic 1% for its level 5 map.

A fishing table row can now end at an expansion, so each era gets its own magic fish and footwear rows. The chances at GM come from the Fishing Council of Britannia's cast counts before and after Publish 16, which came out in parts from April to December 2002, during LBR [O14]. The leviathan gets its 25% through a `BaseCreature.TreasureMapChance` override; leviathans only come from the fabled net, which already needs AOS.

| Change | Era | Source |
|---|---|---|
| Footwear: 75% at 0 skill, 2.5% at GM | T2A to UOTD | "The beginning fisherman will find this item on his hook most of the times" [S1–S7]; 2.5% of catches [F1] |
| Footwear: 75% at 0 skill, 10% at GM | LBR on | The same quote; 10.5% of catches [F2] |
| Magic fish: 80 skill, 2.5% at GM | T2A to UOTD | 80 from July 1999 to October 2002 [S1–S6, S9]; 2.4% of catches [F1] |
| Magic fish: no minimum, 5% at GM | LBR to SA | 0 from December 2002 [S7], so the minimum went in late 2002, during LBR; 5.6% of catches [F2] |
| Magic fish: 80 skill again | HS on | Still 0 in June 2010, during SA [S13]; 80 today [O9]. HS is the first expansion after that |
| Leviathan's map: 25% | AOS | Publish 27: "It also has a 25% chance to carry a level 5 treasure map." [O5] |

- These are chances per roll. The FCB's counts leave out empty casts, so they line up once the "nothing" row #2687 lists is gone (a follow-up PR removes it); while it stays, footwear is 3.2% of a GM's catches before LBR and 12% from LBR.
- No source counts footwear below GM, so 75% at 0 skill only follows Stratics' "most of the times". The numbers in my comment were placeholders.
- 80 again from HS is the one change beyond my comment, which proposed no minimum from LBR on.

The keys are #2687's, plus:
- [O14] uo.com, the 2002 publishes: Publish 16 in parts on April 23, July 23, September 20 and December 2, 2002. https://uo.com/wiki/ultima-online-wiki/technical/previous-publishes/2002-2/
- [F1] The Fishin' Statistician, 11,101 casts from August 2001 to April 2002, before Publish 16; skill not stated, most likely GM. https://web.archive.org/web/20020609120859/http://www.thefcb.com:80/novellettes/stats.html
- [F2] The same page, 7,371 casts from July 2002 to February 2003, after Publish 16; skill not stated either. https://web.archive.org/web/20030216074120/http://www.thefcb.com:80/novellettes/stats.html
- [S13] Stratics' fishing guide, captured June 5, 2010. https://web.archive.org/web/20100605065528/http://uo.stratics.com:80/content/skills/fishing.shtml
2026-10-17 14:41:58 -07:00

171 lines
5 KiB
C#

using ModernUO.Serialization;
using System;
using Server.Items;
namespace Server.Mobiles
{
[SerializationGenerator(0, false)]
public partial class Leviathan : BaseCreature
{
[Constructible]
public Leviathan(Mobile fisher = null) : base(AIType.AI_Mage)
{
Fisher = fisher;
// May not be OSI accurate; mostly copied from krakens
Body = 77;
BaseSoundID = 353;
Hue = 0x481;
SetStr(1000);
SetDex(501, 520);
SetInt(501, 515);
SetHits(1500);
SetDamage(25, 33);
SetDamageType(ResistanceType.Physical, 70);
SetDamageType(ResistanceType.Cold, 30);
SetResistance(ResistanceType.Physical, 55, 65);
SetResistance(ResistanceType.Fire, 45, 55);
SetResistance(ResistanceType.Cold, 45, 55);
SetResistance(ResistanceType.Poison, 35, 45);
SetResistance(ResistanceType.Energy, 25, 35);
SetSkill(SkillName.EvalInt, 97.6, 107.5);
SetSkill(SkillName.Magery, 97.6, 107.5);
SetSkill(SkillName.MagicResist, 97.6, 107.5);
SetSkill(SkillName.Meditation, 97.6, 107.5);
SetSkill(SkillName.Tactics, 97.6, 107.5);
SetSkill(SkillName.Wrestling, 97.6, 107.5);
Fame = 24000;
Karma = -24000;
VirtualArmor = 50;
CanSwim = true;
CantWalk = true;
PackItem(new Rope { ItemID = 0x14F8 });
PackItem(new Rope { ItemID = 0x14FA });
}
public override string CorpseName => "a leviathan corpse";
public Mobile Fisher { get; set; }
public override string DefaultName => "a leviathan";
public override int TreasureMapLevel => 5;
public override double TreasureMapChance => 0.25;
public override bool OnBeforeDeath()
{
PackItem(new MessageInABottle(Map == Map.Felucca ? Map.Felucca : Map.Trammel));
return base.OnBeforeDeath();
}
public static Type[] Artifacts { get; } =
{
// Decorations
typeof(CandelabraOfSouls),
typeof(GhostShipAnchor),
typeof(GoldBricks),
typeof(PhillipsWoodenSteed),
typeof(SeahorseStatuette),
typeof(ShipModelOfTheHMSCape),
typeof(AdmiralsHeartyRum),
// Equipment
typeof(AlchemistsBauble),
typeof(ArcticDeathDealer),
typeof(BlazeOfDeath),
typeof(BurglarsBandana),
typeof(CaptainQuacklebushsCutlass),
typeof(CavortingClub),
typeof(DreadPirateHat),
typeof(EnchantedTitanLegBone),
typeof(GwennosHarp),
typeof(IolosLute),
typeof(LunaLance),
typeof(NightsKiss),
typeof(NoxRangersHeavyCrossbow),
typeof(PolarBearMask),
typeof(VioletCourage)
};
private static MonsterAbility[] _abilities = { new LeviathanBreath() };
public override MonsterAbility[] GetMonsterAbilities() => _abilities;
public override void GenerateLoot()
{
AddLoot(LootPack.FilthyRich, 5);
}
public static void GiveArtifactTo(Mobile m)
{
var item = Loot.Construct(Artifacts);
if (item == null)
{
return;
}
// TODO: Confirm messages
if (m.AddToBackpack(item))
{
m.SendMessage("As a reward for slaying the mighty leviathan, an artifact has been placed in your backpack.");
}
else
{
m.SendMessage(
"As your backpack is full, your reward for destroying the legendary leviathan has been placed at your feet."
);
}
}
public override void OnKilledBy(Mobile mob)
{
base.OnKilledBy(mob);
if (Paragon.CheckArtifactChance(mob, this))
{
GiveArtifactTo(mob);
if (mob == Fisher)
{
Fisher = null;
}
}
}
public override void OnDeath(Container c)
{
base.OnDeath(c);
if (Fisher != null && Utility.Random(100) < 25)
{
GiveArtifactTo(Fisher);
}
Fisher = null;
}
private class LeviathanBreath : FireBreath
{
public override int PhysicalDamage => 70;
public override int ColdDamage => 30;
public override int FireDamage => 0;
public override int BreathEffectHue => 0x1ED;
public override double BreathDamageScalar => 0.05;
public override TimeSpan MinTriggerCooldown => TimeSpan.FromSeconds(5.0);
public override TimeSpan MaxTriggerCooldown => TimeSpan.FromSeconds(7.5);
}
}
}