From dd95a8a1e3d910aedc00fd8e8a52e8342c7fba77 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 15 May 2022 21:56:04 -0700 Subject: [PATCH] fix: Codegens Food & Beverages (#1021) - [X] Simplifies beverages - [X] Codegens food & beverages --- Projects/UOContent/Items/Food/Asian.cs | 482 ++--- Projects/UOContent/Items/Food/Beverage.cs | 1859 +++++++---------- .../Server.Items.AwaseMisoSoup.v0.json | 4 + .../Server.Items.BaseBeverage.v2.json | 32 + .../Migrations/Server.Items.BentoBox.v0.json | 4 + .../Server.Items.BeverageBottle.v0.json | 4 + .../Server.Items.CeramicMug.v0.json | 4 + .../Server.Items.EmptyBentoBox.v0.json | 4 + .../Migrations/Server.Items.GlassMug.v0.json | 4 + .../Migrations/Server.Items.Goblet.v0.json | 4 + .../Migrations/Server.Items.GreenTea.v0.json | 4 + .../Server.Items.GreenTeaBasket.v0.json | 4 + .../Migrations/Server.Items.Jug.v0.json | 4 + .../Migrations/Server.Items.MisoSoup.v0.json | 4 + .../Migrations/Server.Items.PewterMug.v0.json | 4 + .../Migrations/Server.Items.Pitcher.v0.json | 4 + .../Server.Items.RedMisoSoup.v0.json | 4 + .../Server.Items.SushiPlatter.v0.json | 4 + .../Server.Items.SushiRolls.v0.json | 4 + .../Migrations/Server.Items.Wasabi.v0.json | 4 + .../Server.Items.WasabiClumps.v0.json | 4 + .../Server.Items.WhiteMisoSoup.v0.json | 4 + Projects/UOContent/Misc/Poison.cs | 4 +- 23 files changed, 965 insertions(+), 1488 deletions(-) create mode 100644 Projects/UOContent/Migrations/Server.Items.AwaseMisoSoup.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.BaseBeverage.v2.json create mode 100644 Projects/UOContent/Migrations/Server.Items.BentoBox.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.BeverageBottle.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.CeramicMug.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.EmptyBentoBox.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.GlassMug.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.Goblet.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.GreenTea.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.GreenTeaBasket.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.Jug.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.MisoSoup.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.PewterMug.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.Pitcher.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.RedMisoSoup.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.SushiPlatter.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.SushiRolls.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.Wasabi.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.WasabiClumps.v0.json create mode 100644 Projects/UOContent/Migrations/Server.Items.WhiteMisoSoup.v0.json diff --git a/Projects/UOContent/Items/Food/Asian.cs b/Projects/UOContent/Items/Food/Asian.cs index b7ea61a6b..b80e5f2d9 100644 --- a/Projects/UOContent/Items/Food/Asian.cs +++ b/Projects/UOContent/Items/Food/Asian.cs @@ -1,346 +1,144 @@ -namespace Server.Items +using ModernUO.Serialization; + +namespace Server.Items; + +[SerializationGenerator(0, false)] +public partial class Wasabi : Food { - public class Wasabi : Item + [Constructible] + public Wasabi() : base(0x24E8) => Weight = 1.0; +} + +[SerializationGenerator(0, false)] +public partial class WasabiClumps : Food +{ + [Constructible] + public WasabiClumps() : base(0x24EB) { - [Constructible] - public Wasabi() : base(0x24E8) => Weight = 1.0; - - public Wasabi(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WasabiClumps : Food - { - [Constructible] - public WasabiClumps() : base(0x24EB) - { - Stackable = false; - Weight = 1.0; - FillFactor = 2; - } - - public WasabiClumps(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class EmptyBentoBox : Item - { - [Constructible] - public EmptyBentoBox() : base(0x2834) => Weight = 5.0; - - public EmptyBentoBox(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class BentoBox : Food - { - [Constructible] - public BentoBox() : base(0x2836) - { - Stackable = false; - Weight = 5.0; - FillFactor = 2; - } - - public BentoBox(Serial serial) : base(serial) - { - } - - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyBentoBox()); - return true; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SushiRolls : Food - { - [Constructible] - public SushiRolls() : base(0x283E) - { - Stackable = false; - Weight = 3.0; - FillFactor = 2; - } - - public SushiRolls(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class SushiPlatter : Food - { - [Constructible] - public SushiPlatter() : base(0x2840) - { - Stackable = Core.ML; - Weight = 3.0; - FillFactor = 2; - } - - public SushiPlatter(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class GreenTeaBasket : Item - { - [Constructible] - public GreenTeaBasket() : base(0x284B) => Weight = 10.0; - - public GreenTeaBasket(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class GreenTea : Food - { - [Constructible] - public GreenTea() : base(0x284C) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public GreenTea(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class MisoSoup : Food - { - [Constructible] - public MisoSoup() : base(0x284D) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public MisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class WhiteMisoSoup : Food - { - [Constructible] - public WhiteMisoSoup() : base(0x284E) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public WhiteMisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class RedMisoSoup : Food - { - [Constructible] - public RedMisoSoup() : base(0x284F) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public RedMisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class AwaseMisoSoup : Food - { - [Constructible] - public AwaseMisoSoup() : base(0x2850) - { - Stackable = false; - Weight = 4.0; - FillFactor = 2; - } - - public AwaseMisoSoup(Serial serial) : base(serial) - { - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } + Stackable = false; + Weight = 1.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class EmptyBentoBox : Item +{ + [Constructible] + public EmptyBentoBox() : base(0x2834) => Weight = 5.0; + +} + +[SerializationGenerator(0, false)] +public partial class BentoBox : Food +{ + [Constructible] + public BentoBox() : base(0x2836) + { + Stackable = false; + Weight = 5.0; + FillFactor = 2; + } + + public override bool Eat(Mobile from) + { + if (!base.Eat(from)) + { + return false; + } + + from.AddToBackpack(new EmptyBentoBox()); + return true; + } +} + +[SerializationGenerator(0, false)] +public partial class SushiRolls : Food +{ + [Constructible] + public SushiRolls() : base(0x283E) + { + Stackable = false; + Weight = 3.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class SushiPlatter : Food +{ + [Constructible] + public SushiPlatter() : base(0x2840) + { + Stackable = Core.ML; + Weight = 3.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class GreenTeaBasket : Item +{ + [Constructible] + public GreenTeaBasket() : base(0x284B) => Weight = 10.0; +} + +[SerializationGenerator(0, false)] +public partial class GreenTea : Food +{ + [Constructible] + public GreenTea() : base(0x284C) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class MisoSoup : Food +{ + [Constructible] + public MisoSoup() : base(0x284D) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class WhiteMisoSoup : Food +{ + [Constructible] + public WhiteMisoSoup() : base(0x284E) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class RedMisoSoup : Food +{ + [Constructible] + public RedMisoSoup() : base(0x284F) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; + } +} + +[SerializationGenerator(0, false)] +public partial class AwaseMisoSoup : Food +{ + [Constructible] + public AwaseMisoSoup() : base(0x2850) + { + Stackable = false; + Weight = 4.0; + FillFactor = 2; } } diff --git a/Projects/UOContent/Items/Food/Beverage.cs b/Projects/UOContent/Items/Food/Beverage.cs index 49280cbe8..10f71c6a6 100644 --- a/Projects/UOContent/Items/Food/Beverage.cs +++ b/Projects/UOContent/Items/Food/Beverage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using ModernUO.Serialization; using Server.Engines.Plants; using Server.Engines.Quests; using Server.Engines.Quests.Hag; @@ -9,1233 +10,803 @@ using Server.Multis; using Server.Network; using Server.Targeting; -namespace Server.Items +namespace Server.Items; + +public enum BeverageType { - public enum BeverageType + Ale, + Cider, + Liquor, + Milk, + Wine, + Water +} + +public interface IHasQuantity +{ + int Quantity { get; set; } +} + +public interface IWaterSource : IHasQuantity +{ +} + +// TODO: Flippable attributes +[SerializationGenerator(0, false)] +[TypeAlias("Server.Items.BottleAle", "Server.Items.BottleLiquor", "Server.Items.BottleWine")] +public partial class BeverageBottle : BaseBeverage +{ + [Constructible] + public BeverageBottle(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042959; // a bottle of Ale + public override int MaxQuantity => 5; + public override bool Fillable => false; + + public override int ComputeItemID() { - Ale, - Cider, - Liquor, - Milk, - Wine, - Water - } - - public interface IHasQuantity - { - int Quantity { get; set; } - } - - public interface IWaterSource : IHasQuantity - { - } - - // TODO: Flippable attributes - - [TypeAlias("Server.Items.BottleAle", "Server.Items.BottleLiquor", "Server.Items.BottleWine")] - public class BeverageBottle : BaseBeverage - { - [Constructible] - public BeverageBottle(BeverageType type) - : base(type) => - Weight = 1.0; - - public BeverageBottle(Serial serial) - : base(serial) + if (IsEmpty) { - } - - public override int BaseLabelNumber => 1042959; // a bottle of Ale - public override int MaxQuantity => 5; - public override bool Fillable => false; - - public override int ComputeItemID() - { - if (!IsEmpty) - { - switch (Content) - { - case BeverageType.Ale: return 0x99F; - case BeverageType.Cider: return 0x99F; - case BeverageType.Liquor: return 0x99B; - case BeverageType.Milk: return 0x99B; - case BeverageType.Wine: return 0x9C7; - case BeverageType.Water: return 0x99B; - } - } - return 0; } - public override void Serialize(IGenericWriter writer) + return Content switch { - base.Serialize(writer); + BeverageType.Ale => 0x99F, + BeverageType.Cider => 0x99F, + BeverageType.Liquor => 0x99B, + BeverageType.Milk => 0x99B, + BeverageType.Wine => 0x9C7, + BeverageType.Water => 0x99B, + _ => 0 + }; + } +} - writer.Write(1); // version +[SerializationGenerator(0, false)] +public partial class Jug : BaseBeverage +{ + [Constructible] + public Jug(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042965; // a jug of Ale + public override int MaxQuantity => 10; + public override bool Fillable => false; + + public override int ComputeItemID() => !IsEmpty ? 0x9C8 : 0; +} + +[SerializationGenerator(0, false)] +public partial class CeramicMug : BaseBeverage +{ + [Constructible] + public CeramicMug() => Weight = 1.0; + + [Constructible] + public CeramicMug(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042982; // a ceramic mug of Ale + public override int MaxQuantity => 1; + + public override int ComputeItemID() => ItemID is (< 0x995 or > 0x999) and not 0x9CA ? 0x995 : ItemID; +} + +[SerializationGenerator(0, false)] +public partial class PewterMug : BaseBeverage +{ + [Constructible] + public PewterMug() => Weight = 1.0; + + [Constructible] + public PewterMug(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1042994; // a pewter mug with Ale + public override int MaxQuantity => 1; + + public override int ComputeItemID() => ItemID is >= 0xFFF and <= 0x1002 ? ItemID : 0xFFF; +} + +[SerializationGenerator(0, false)] +public partial class Goblet : BaseBeverage +{ + [Constructible] + public Goblet() => Weight = 1.0; + + [Constructible] + public Goblet(BeverageType type) : base(type) => Weight = 1.0; + + public override int BaseLabelNumber => 1043000; // a goblet of Ale + public override int MaxQuantity => 1; + + public override int ComputeItemID() => ItemID is 0x99A or 0x9B3 or 0x9BF or 0x9CB ? ItemID : 0x99A; +} + +[TypeAlias( + "Server.Items.MugAle", + "Server.Items.GlassCider", + "Server.Items.GlassLiquor", + "Server.Items.GlassMilk", + "Server.Items.GlassWine", + "Server.Items.GlassWater" +)] +[SerializationGenerator(0, false)] +public partial class GlassMug : BaseBeverage +{ + [Constructible] + public GlassMug() => Weight = 1.0; + + [Constructible] + public GlassMug(BeverageType type) : base(type) => Weight = 1.0; + + public override int EmptyLabelNumber => 1022456; // mug + public override int BaseLabelNumber => 1042976; // a mug of Ale + public override int MaxQuantity => 5; + + public override int ComputeItemID() + { + if (IsEmpty) + { + return ItemID is >= 0x1F81 and <= 0x1F84 ? ItemID : 0x1F81; } - public override void Deserialize(IGenericReader reader) + return Content switch { - base.Deserialize(reader); + BeverageType.Ale => ItemID == 0x9EF ? 0x9EF : 0x9EE, + BeverageType.Cider => Math.Clamp(ItemID, 0x1F7D, 0x1F80), + BeverageType.Liquor => Math.Clamp(ItemID, 0x1F85, 0x1F88), + BeverageType.Milk => Math.Clamp(ItemID, 0x1F89, 0x1F8C), + BeverageType.Wine => Math.Clamp(ItemID, 0x1F8D, 0x1F90), + BeverageType.Water => Math.Clamp(ItemID, 0x1F91, 0x1F94), + _ => 0 + }; + } +} - var version = reader.ReadInt(); +[TypeAlias( + "Server.Items.PitcherAle", + "Server.Items.PitcherCider", + "Server.Items.PitcherLiquor", + "Server.Items.PitcherMilk", + "Server.Items.PitcherWine", + "Server.Items.PitcherWater", + "Server.Items.GlassPitcher" +)] +[SerializationGenerator(0, false)] +public partial class Pitcher : BaseBeverage +{ + [Constructible] + public Pitcher() => Weight = 2.0; - switch (version) - { - case 0: - { - if (CheckType("BottleAle")) - { - Quantity = MaxQuantity; - Content = BeverageType.Ale; - } - else if (CheckType("BottleLiquor")) - { - Quantity = MaxQuantity; - Content = BeverageType.Liquor; - } - else if (CheckType("BottleWine")) - { - Quantity = MaxQuantity; - Content = BeverageType.Wine; - } - else - { - throw new Exception("Invalid beverage type"); - } + [Constructible] + public Pitcher(BeverageType type) : base(type) => Weight = 2.0; - break; - } - } + public override int BaseLabelNumber => 1048128; // a Pitcher of Ale + public override int MaxQuantity => 5; + + public override int ComputeItemID() + { + if (IsEmpty) + { + return ItemID is 0x9A7 or 0xFF7 ? ItemID : 0xFF6; } + + return Content switch + { + BeverageType.Ale => ItemID == 0x1F96 ? ItemID : 0x1F95, + BeverageType.Cider => ItemID == 0x1F98 ? ItemID : 0x1F97, + BeverageType.Liquor => ItemID == 0x1F9A ? ItemID : 0x1F99, + BeverageType.Milk => ItemID == 0x9AD ? ItemID : 0x9F0, + BeverageType.Wine => ItemID == 0x1F9C ? ItemID : 0x1F9B, + BeverageType.Water => ItemID is 0xFF8 or 0xFF9 or 0x1F9E ? ItemID : 0x1F9D, + _ => 0 + }; + } +} + +[SerializationGenerator(2, false)] +public abstract partial class BaseBeverage : Item, IHasQuantity +{ + private readonly int[] _swampTiles = + { + 0x9C4, 0x9EB, + 0x3D65, 0x3D65, + 0x3DC0, 0x3DD9, + 0x3DDB, 0x3DDC, + 0x3DDE, 0x3EF0, + 0x3FF6, 0x3FF6, + 0x3FFC, 0x3FFE + }; + + private static readonly Dictionary m_Table = new(); + + [SerializableField(0)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Poison _poison; + + [SerializableField(1)] + [SerializableFieldAttr("[CommandProperty(AccessLevel.GameMaster)]")] + private Mobile _poisoner; + + [SerializableField(2, getter: "private", setter: "private")] + private BeverageType _rawContent; + + [SerializableField(3, getter: "private", setter: "private")] + private int _rawQuantity; + + public BaseBeverage() => ItemID = ComputeItemID(); + + public BaseBeverage(BeverageType type) + { + _rawContent = type; + _rawQuantity = MaxQuantity; + ItemID = ComputeItemID(); } - public class Jug : BaseBeverage + public override int LabelNumber => + IsEmpty || BaseLabelNumber == 0 ? EmptyLabelNumber : BaseLabelNumber + (int)_rawContent; + + public virtual bool ShowQuantity => MaxQuantity > 1; + public virtual bool Fillable => true; + public virtual bool Pourable => true; + + public virtual int EmptyLabelNumber => base.LabelNumber; + public virtual int BaseLabelNumber => 0; + + public abstract int MaxQuantity { get; } + + [CommandProperty(AccessLevel.GameMaster)] + public bool IsEmpty => _rawQuantity <= 0; + + [CommandProperty(AccessLevel.GameMaster)] + public bool ContainsAlcohol => !IsEmpty && _rawContent is not BeverageType.Milk and not BeverageType.Water; + + [CommandProperty(AccessLevel.GameMaster)] + public bool IsFull => _rawQuantity >= MaxQuantity; + + [CommandProperty(AccessLevel.GameMaster)] + public BeverageType Content { - [Constructible] - public Jug(BeverageType type) - : base(type) => - Weight = 1.0; - - public Jug(Serial serial) - : base(serial) + get => _rawContent; + set { - } + RawContent = value; - public override int BaseLabelNumber => 1042965; // a jug of Ale - public override int MaxQuantity => 10; - public override bool Fillable => false; + InvalidateProperties(); - public override int ComputeItemID() - { - if (!IsEmpty) + var itemID = ComputeItemID(); + + if (itemID > 0) { - return 0x9C8; - } - - return 0; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class CeramicMug : BaseBeverage - { - [Constructible] - public CeramicMug() => Weight = 1.0; - - [Constructible] - public CeramicMug(BeverageType type) - : base(type) => - Weight = 1.0; - - public CeramicMug(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1042982; // a ceramic mug of Ale - public override int MaxQuantity => 1; - - public override int ComputeItemID() - { - if (ItemID >= 0x995 && ItemID <= 0x999) - { - return ItemID; - } - - if (ItemID == 0x9CA) - { - return ItemID; - } - - return 0x995; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class PewterMug : BaseBeverage - { - [Constructible] - public PewterMug() => Weight = 1.0; - - [Constructible] - public PewterMug(BeverageType type) - : base(type) => - Weight = 1.0; - - public PewterMug(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1042994; // a pewter mug with Ale - public override int MaxQuantity => 1; - - public override int ComputeItemID() - { - if (ItemID >= 0xFFF && ItemID <= 0x1002) - { - return ItemID; - } - - return 0xFFF; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - public class Goblet : BaseBeverage - { - [Constructible] - public Goblet() => Weight = 1.0; - - [Constructible] - public Goblet(BeverageType type) - : base(type) => - Weight = 1.0; - - public Goblet(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1043000; // a goblet of Ale - public override int MaxQuantity => 1; - - public override int ComputeItemID() - { - if (ItemID is 0x99A or 0x9B3 or 0x9BF or 0x9CB) - { - return ItemID; - } - - return 0x99A; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - } - } - - [TypeAlias( - "Server.Items.MugAle", - "Server.Items.GlassCider", - "Server.Items.GlassLiquor", - "Server.Items.GlassMilk", - "Server.Items.GlassWine", - "Server.Items.GlassWater" - )] - public class GlassMug : BaseBeverage - { - [Constructible] - public GlassMug() => Weight = 1.0; - - [Constructible] - public GlassMug(BeverageType type) - : base(type) => - Weight = 1.0; - - public GlassMug(Serial serial) - : base(serial) - { - } - - public override int EmptyLabelNumber => 1022456; // mug - public override int BaseLabelNumber => 1042976; // a mug of Ale - public override int MaxQuantity => 5; - - public override int ComputeItemID() - { - if (IsEmpty) - { - return ItemID >= 0x1F81 && ItemID <= 0x1F84 ? ItemID : 0x1F81; - } - - return Content switch - { - BeverageType.Ale => ItemID == 0x9EF ? 0x9EF : 0x9EE, - BeverageType.Cider => ItemID >= 0x1F7D && ItemID <= 0x1F80 ? ItemID : 0x1F7D, - BeverageType.Liquor => ItemID >= 0x1F85 && ItemID <= 0x1F88 ? ItemID : 0x1F85, - BeverageType.Milk => ItemID >= 0x1F89 && ItemID <= 0x1F8C ? ItemID : 0x1F89, - BeverageType.Wine => ItemID >= 0x1F8D && ItemID <= 0x1F90 ? ItemID : 0x1F8D, - BeverageType.Water => ItemID >= 0x1F91 && ItemID <= 0x1F94 ? ItemID : 0x1F91, - _ => 0 - }; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - if (CheckType("MugAle")) - { - Quantity = MaxQuantity; - Content = BeverageType.Ale; - } - else if (CheckType("GlassCider")) - { - Quantity = MaxQuantity; - Content = BeverageType.Cider; - } - else if (CheckType("GlassLiquor")) - { - Quantity = MaxQuantity; - Content = BeverageType.Liquor; - } - else if (CheckType("GlassMilk")) - { - Quantity = MaxQuantity; - Content = BeverageType.Milk; - } - else if (CheckType("GlassWine")) - { - Quantity = MaxQuantity; - Content = BeverageType.Wine; - } - else if (CheckType("GlassWater")) - { - Quantity = MaxQuantity; - Content = BeverageType.Water; - } - else - { - throw new Exception("Invalid beverage type"); - } - - break; - } - } - } - } - - [TypeAlias( - "Server.Items.PitcherAle", - "Server.Items.PitcherCider", - "Server.Items.PitcherLiquor", - "Server.Items.PitcherMilk", - "Server.Items.PitcherWine", - "Server.Items.PitcherWater", - "Server.Items.GlassPitcher" - )] - public class Pitcher : BaseBeverage - { - [Constructible] - public Pitcher() => Weight = 2.0; - - [Constructible] - public Pitcher(BeverageType type) - : base(type) => - Weight = 2.0; - - public Pitcher(Serial serial) - : base(serial) - { - } - - public override int BaseLabelNumber => 1048128; // a Pitcher of Ale - public override int MaxQuantity => 5; - - public override int ComputeItemID() - { - if (IsEmpty) - { - if (ItemID is 0x9A7 or 0xFF7) - { - return ItemID; - } - - return 0xFF6; - } - - switch (Content) - { - case BeverageType.Ale: - { - if (ItemID == 0x1F96) - { - return ItemID; - } - - return 0x1F95; - } - case BeverageType.Cider: - { - if (ItemID == 0x1F98) - { - return ItemID; - } - - return 0x1F97; - } - case BeverageType.Liquor: - { - if (ItemID == 0x1F9A) - { - return ItemID; - } - - return 0x1F99; - } - case BeverageType.Milk: - { - if (ItemID == 0x9AD) - { - return ItemID; - } - - return 0x9F0; - } - case BeverageType.Wine: - { - if (ItemID == 0x1F9C) - { - return ItemID; - } - - return 0x1F9B; - } - case BeverageType.Water: - { - if (ItemID is 0xFF8 or 0xFF9 or 0x1F9E) - { - return ItemID; - } - - return 0x1F9D; - } - } - - return 0; - } - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(1); // version - } - - public override void Deserialize(IGenericReader reader) - { - if (CheckType("PitcherWater") || CheckType("GlassPitcher")) - { - InternalDeserialize(reader, false); + ItemID = itemID; } else { - InternalDeserialize(reader, true); - } - - var version = reader.ReadInt(); - - switch (version) - { - case 0: - { - if (CheckType("PitcherAle")) - { - Quantity = MaxQuantity; - Content = BeverageType.Ale; - } - else if (CheckType("PitcherCider")) - { - Quantity = MaxQuantity; - Content = BeverageType.Cider; - } - else if (CheckType("PitcherLiquor")) - { - Quantity = MaxQuantity; - Content = BeverageType.Liquor; - } - else if (CheckType("PitcherMilk")) - { - Quantity = MaxQuantity; - Content = BeverageType.Milk; - } - else if (CheckType("PitcherWine")) - { - Quantity = MaxQuantity; - Content = BeverageType.Wine; - } - else if (CheckType("PitcherWater")) - { - Quantity = MaxQuantity; - Content = BeverageType.Water; - } - else if (CheckType("GlassPitcher")) - { - Quantity = 0; - Content = BeverageType.Water; - } - else - { - throw new Exception("Invalid beverage type"); - } - - break; - } + Delete(); } } } - public abstract class BaseBeverage : Item, IHasQuantity + [CommandProperty(AccessLevel.GameMaster)] + public int Quantity { - private static readonly int[] m_SwampTiles = + get => _rawQuantity; + set { - 0x9C4, 0x9EB, - 0x3D65, 0x3D65, - 0x3DC0, 0x3DD9, - 0x3DDB, 0x3DDC, - 0x3DDE, 0x3EF0, - 0x3FF6, 0x3FF6, - 0x3FFC, 0x3FFE + RawQuantity = Math.Clamp(value, 0, MaxQuantity); + + InvalidateProperties(); + + var itemID = ComputeItemID(); + + if (itemID > 0) + { + ItemID = itemID; + } + else + { + Delete(); + } + } + } + + public abstract int ComputeItemID(); + + public virtual int GetQuantityDescription() + { + return (_rawQuantity * 100 / MaxQuantity) switch + { + <= 0 => 1042975, + <= 33 => 1042974, + <= 66 => 1042973, + _ => 1042972 }; + } - private static readonly Dictionary m_Table = new(); + public override void GetProperties(ObjectPropertyList list) + { + base.GetProperties(list); - private BeverageType m_Content; - private int m_Quantity; - - public BaseBeverage() => ItemID = ComputeItemID(); - - public BaseBeverage(BeverageType type) + if (ShowQuantity) { - m_Content = type; - m_Quantity = MaxQuantity; - ItemID = ComputeItemID(); + list.Add(GetQuantityDescription()); + } + } + + public override void OnSingleClick(Mobile from) + { + base.OnSingleClick(from); + + if (ShowQuantity) + { + LabelTo(from, GetQuantityDescription()); + } + } + + public virtual bool ValidateUse(Mobile from, bool message) + { + if (Deleted) + { + return false; } - public BaseBeverage(Serial serial) - : base(serial) + if (!Movable && !Fillable) { - } + var house = BaseHouse.FindHouseAt(this); - public override int LabelNumber - { - get - { - var num = BaseLabelNumber; - - if (IsEmpty || num == 0) - { - return EmptyLabelNumber; - } - - return BaseLabelNumber + (int)m_Content; - } - } - - public virtual bool ShowQuantity => MaxQuantity > 1; - public virtual bool Fillable => true; - public virtual bool Pourable => true; - - public virtual int EmptyLabelNumber => base.LabelNumber; - public virtual int BaseLabelNumber => 0; - - public abstract int MaxQuantity { get; } - - [CommandProperty(AccessLevel.GameMaster)] - public bool IsEmpty => m_Quantity <= 0; - - [CommandProperty(AccessLevel.GameMaster)] - public bool ContainsAlchohol => !IsEmpty && m_Content != BeverageType.Milk && m_Content != BeverageType.Water; - - [CommandProperty(AccessLevel.GameMaster)] - public bool IsFull => m_Quantity >= MaxQuantity; - - [CommandProperty(AccessLevel.GameMaster)] - public Poison Poison { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public Mobile Poisoner { get; set; } - - [CommandProperty(AccessLevel.GameMaster)] - public BeverageType Content - { - get => m_Content; - set - { - m_Content = value; - - InvalidateProperties(); - - var itemID = ComputeItemID(); - - if (itemID > 0) - { - ItemID = itemID; - } - else - { - Delete(); - } - } - } - - [CommandProperty(AccessLevel.GameMaster)] - public int Quantity - { - get => m_Quantity; - set - { - m_Quantity = Math.Clamp(value, 0, MaxQuantity); - - InvalidateProperties(); - - var itemID = ComputeItemID(); - - if (itemID > 0) - { - ItemID = itemID; - } - else - { - Delete(); - } - } - } - - public abstract int ComputeItemID(); - - public virtual int GetQuantityDescription() - { - var perc = m_Quantity * 100 / MaxQuantity; - - if (perc <= 0) - { - return 1042975; // It's empty. - } - - if (perc <= 33) - { - return 1042974; // It's nearly empty. - } - - if (perc <= 66) - { - return 1042973; // It's half full. - } - - return 1042972; // It's full. - } - - public override void GetProperties(ObjectPropertyList list) - { - base.GetProperties(list); - - if (ShowQuantity) - { - list.Add(GetQuantityDescription()); - } - } - - public override void OnSingleClick(Mobile from) - { - base.OnSingleClick(from); - - if (ShowQuantity) - { - LabelTo(from, GetQuantityDescription()); - } - } - - public virtual bool ValidateUse(Mobile from, bool message) - { - if (Deleted) - { - return false; - } - - if (!Movable && !Fillable) - { - var house = BaseHouse.FindHouseAt(this); - - if (house?.HasLockedDownItem(this) != true) - { - if (message) - { - from.SendLocalizedMessage(502946, "", 0x59); // That belongs to someone else. - } - - return false; - } - } - - if (from.Map != Map || !from.InRange(GetWorldLocation(), 2) || !from.InLOS(this)) + if (house?.HasLockedDownItem(this) != true) { if (message) { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + from.SendLocalizedMessage(502946, "", 0x59); // That belongs to someone else. } return false; } - - return true; } - public virtual void Fill_OnTarget(Mobile from, object targ) + if (from.Map != Map || !from.InRange(GetWorldLocation(), 2) || !from.InLOS(this)) { - if (!IsEmpty || !Fillable || !ValidateUse(from, false)) + if (message) { - return; - } - - if (targ is BaseBeverage bev) - { - if (bev.IsEmpty || !bev.ValidateUse(from, true)) - { - return; - } - - Content = bev.Content; - Poison = bev.Poison; - Poisoner = bev.Poisoner; - - if (bev.Quantity > MaxQuantity) - { - Quantity = MaxQuantity; - bev.Quantity -= MaxQuantity; - } - else - { - Quantity += bev.Quantity; - bev.Quantity = 0; - } - } - else if (targ is BaseWaterContainer bwc) - { - if (Quantity == 0 || Content == BeverageType.Water && !IsFull) - { - var iNeed = Math.Min(MaxQuantity - Quantity, bwc.Quantity); - - if (iNeed > 0 && !bwc.IsEmpty && !IsFull) - { - bwc.Quantity -= iNeed; - Quantity += iNeed; - Content = BeverageType.Water; - - from.PlaySound(0x4E); - } - } - } - else if (targ is Item item) - { - var src = item as IWaterSource; - - if (src == null && item is AddonComponent component) - { - src = component.Addon as IWaterSource; - } - - if (src == null || src.Quantity <= 0) - { - return; - } - - if (from.Map != item.Map || !from.InRange(item.GetWorldLocation(), 2) || !from.InLOS(item)) - { - from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. - return; - } - - Content = BeverageType.Water; - Poison = null; - Poisoner = null; - - if (src.Quantity > MaxQuantity) - { - Quantity = MaxQuantity; - src.Quantity -= MaxQuantity; - } - else - { - Quantity += src.Quantity; - src.Quantity = 0; - } - - from.SendLocalizedMessage(1010089); // You fill the container with water. - } - else if (targ is Cow cow) - { - if (cow.TryMilk(from)) - { - Content = BeverageType.Milk; - Quantity = MaxQuantity; - from.SendLocalizedMessage(1080197); // You fill the container with milk. - } - } - else if (targ is LandTarget target) - { - var tileID = target.TileID; - - if (from is PlayerMobile player) - { - var qs = player.Quest; - - if (qs is not WitchApprenticeQuest) - { - return; - } - - var obj = qs.FindObjective(); - - if (obj?.Completed == true && obj.Ingredient == Ingredient.SwampWater) - { - var contains = false; - - for (var i = 0; !contains && i < m_SwampTiles.Length; i += 2) - { - contains = tileID >= m_SwampTiles[i] && tileID <= m_SwampTiles[i + 1]; - } - - if (contains) - { - Delete(); - - player.SendLocalizedMessage( - 1055035 - ); // You dip the container into the disgusting swamp water, collecting enough for the Hag's vile stew. - obj.Complete(); - } - } - } - } - } - - public virtual void Pour_OnTarget(Mobile from, object targ) - { - if (IsEmpty || !Pourable || !ValidateUse(from, false)) - { - return; - } - - if (targ is BaseBeverage bev) - { - if (!bev.ValidateUse(from, true)) - { - return; - } - - if (bev.IsFull && bev.Content == Content) - { - from.SendLocalizedMessage(500848); // Couldn't pour it there. It was already full. - } - else if (!bev.IsEmpty) - { - from.SendLocalizedMessage(500846); // Can't pour it there. - } - else - { - bev.Content = Content; - bev.Poison = Poison; - bev.Poisoner = Poisoner; - - if (Quantity > bev.MaxQuantity) - { - bev.Quantity = bev.MaxQuantity; - Quantity -= bev.MaxQuantity; - } - else - { - bev.Quantity += Quantity; - Quantity = 0; - } - - from.PlaySound(0x4E); - } - } - else if (from == targ) - { - if (from.Thirst < 20) - { - from.Thirst += 1; - } - - if (ContainsAlchohol) - { - var bac = Content switch - { - BeverageType.Ale => 1, - BeverageType.Wine => 2, - BeverageType.Cider => 3, - BeverageType.Liquor => 4, - _ => 0 - }; - - from.BAC = Math.Min(from.BAC + bac, 60); - - CheckHeaveTimer(from); - } - - from.PlaySound(Utility.RandomList(0x30, 0x2D6)); - - if (Poison != null) - { - from.ApplyPoison(Poisoner, Poison); - } - - --Quantity; - } - else if (targ is BaseWaterContainer bwc) - { - if (Content != BeverageType.Water) - { - from.SendLocalizedMessage(500842); // Can't pour that in there. - } - else if (bwc.Items.Count != 0) - { - from.SendLocalizedMessage(500841); // That has something in it. - } - else - { - var itNeeds = Math.Min(bwc.MaxQuantity - bwc.Quantity, Quantity); - - if (itNeeds > 0) - { - bwc.Quantity += itNeeds; - Quantity -= itNeeds; - - from.PlaySound(0x4E); - } - } - } - else if (targ is PlantItem item) - { - item.Pour(from, this); - } - else if (targ is AddonComponent component && - component.Addon is WaterVatEast or WaterVatSouth && - Content == BeverageType.Water) - { - if (from is PlayerMobile player) - { - if (player.Quest is SolenMatriarchQuest qs) - { - QuestObjective obj = qs.FindObjective(); - - if (obj?.Completed == false) - { - var vat = component.Addon; - - if (vat.X > 5784 && vat.X < 5814 && vat.Y > 1903 && vat.Y < 1934 && - (qs.RedSolen && vat.Map == Map.Trammel || !qs.RedSolen && vat.Map == Map.Felucca)) - { - if (obj.CurProgress + Quantity > obj.MaxProgress) - { - var delta = obj.MaxProgress - obj.CurProgress; - - Quantity -= delta; - obj.CurProgress = obj.MaxProgress; - } - else - { - obj.CurProgress += Quantity; - Quantity = 0; - } - } - } - } - } - } - else - { - from.SendLocalizedMessage(500846); // Can't pour it there. - } - } - - public override void OnDoubleClick(Mobile from) - { - if (IsEmpty) - { - if (!Fillable || !ValidateUse(from, true)) - { - return; - } - - from.BeginTarget(-1, true, TargetFlags.None, Fill_OnTarget); - SendLocalizedMessageTo(from, 500837); // Fill from what? - } - else if (Pourable && ValidateUse(from, true)) - { - from.BeginTarget(-1, true, TargetFlags.None, Pour_OnTarget); - from.SendLocalizedMessage(1010086); // What do you want to use this on? - } - } - - public static bool ConsumeTotal(Container pack, BeverageType content, int quantity) => - ConsumeTotal(pack, typeof(BaseBeverage), content, quantity); - - public static bool ConsumeTotal(Container pack, Type itemType, BeverageType content, int quantity) - { - var items = pack.FindItemsByType(itemType); - - // First pass, compute total - var total = 0; - - for (var i = 0; i < items.Length; ++i) - { - if (items[i] is BaseBeverage bev && bev.Content == content && !bev.IsEmpty) - { - total += bev.Quantity; - } - } - - if (total >= quantity) - { - // We've enough, so consume it - - var need = quantity; - - for (var i = 0; i < items.Length; ++i) - { - if (items[i] is not BaseBeverage bev || bev.Content != content || bev.IsEmpty) - { - continue; - } - - var theirQuantity = bev.Quantity; - - if (theirQuantity < need) - { - bev.Quantity = 0; - need -= theirQuantity; - } - else - { - bev.Quantity -= need; - return true; - } - } + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. } return false; } - public override void Serialize(IGenericWriter writer) + return true; + } + + public virtual void Fill_OnTarget(Mobile from, object targ) + { + if (!IsEmpty || !Fillable || !ValidateUse(from, false)) { - base.Serialize(writer); - - writer.Write(1); // version - - writer.Write(Poisoner); - - writer.Write(Poison); - writer.Write((int)m_Content); - writer.Write(m_Quantity); + return; } - protected bool CheckType(string name) => GetType().FullName == $"Server.Items.{name}"; - - public override void Deserialize(IGenericReader reader) + if (targ is BaseBeverage bev) { - InternalDeserialize(reader, true); - } - - protected void InternalDeserialize(IGenericReader reader, bool read) - { - base.Deserialize(reader); - - if (!read) + if (bev.IsEmpty || !bev.ValidateUse(from, true)) { return; } - var version = reader.ReadInt(); + Content = bev.Content; + Poison = bev.Poison; + Poisoner = bev.Poisoner; - switch (version) + if (bev.Quantity > MaxQuantity) { - case 1: - { - Poisoner = reader.ReadEntity(); - goto case 0; - } - case 0: - { - Poison = reader.ReadPoison(); - m_Content = (BeverageType)reader.ReadInt(); - m_Quantity = reader.ReadInt(); - break; - } + Quantity = MaxQuantity; + bev.Quantity -= MaxQuantity; + } + else + { + Quantity += bev.Quantity; + bev.Quantity = 0; } } - - public static void Initialize() + else if (targ is BaseWaterContainer bwc) { - EventSink.Login += EventSink_Login; - } - - private static void EventSink_Login(Mobile m) - { - CheckHeaveTimer(m); - } - - public static void CheckHeaveTimer(Mobile from) - { - if (from.BAC > 0 && from.Map != Map.Internal && !from.Deleted) + if (Quantity == 0 || Content == BeverageType.Water && !IsFull) { - if (m_Table.ContainsKey(from)) + var iNeed = Math.Min(MaxQuantity - Quantity, bwc.Quantity); + + if (iNeed > 0 && !bwc.IsEmpty && !IsFull) + { + bwc.Quantity -= iNeed; + Quantity += iNeed; + Content = BeverageType.Water; + + from.PlaySound(0x4E); + } + } + } + else if (targ is Item item) + { + var src = item as IWaterSource; + + if (src == null && item is AddonComponent component) + { + src = component.Addon as IWaterSource; + } + + if (src is not { Quantity: > 0 }) + { + return; + } + + if (from.Map != item.Map || !from.InRange(item.GetWorldLocation(), 2) || !from.InLOS(item)) + { + from.LocalOverheadMessage(MessageType.Regular, 0x3B2, 1019045); // I can't reach that. + return; + } + + Content = BeverageType.Water; + Poison = null; + Poisoner = null; + + if (src.Quantity > MaxQuantity) + { + Quantity = MaxQuantity; + src.Quantity -= MaxQuantity; + } + else + { + Quantity += src.Quantity; + src.Quantity = 0; + } + + from.SendLocalizedMessage(1010089); // You fill the container with water. + } + else if (targ is Cow cow) + { + if (cow.TryMilk(from)) + { + Content = BeverageType.Milk; + Quantity = MaxQuantity; + from.SendLocalizedMessage(1080197); // You fill the container with milk. + } + } + else if (targ is LandTarget target) + { + var tileID = target.TileID; + + if (from is PlayerMobile player) + { + var qs = player.Quest; + + if (qs is not WitchApprenticeQuest) { return; } - if (from.BAC > 60) + var obj = qs.FindObjective(); + + if (obj?.Completed == true && obj.Ingredient == Ingredient.SwampWater) { - from.BAC = 60; - } + var contains = false; - Timer t = new HeaveTimer(from); - t.Start(); - - m_Table[from] = t; - } - else if (m_Table.Remove(from, out var t)) - { - t.Stop(); - - from.SendLocalizedMessage(500850); // You feel sober. - } - } - - private class HeaveTimer : Timer - { - private readonly Mobile m_Drunk; - - public HeaveTimer(Mobile drunk) - : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) - { - m_Drunk = drunk; - } - - protected override void OnTick() - { - if (m_Drunk.Deleted || m_Drunk.Map == Map.Internal) - { - Stop(); - m_Table.Remove(m_Drunk); - } - else if (m_Drunk.Alive) - { - if (m_Drunk.BAC > 60) + for (var i = 0; !contains && i < _swampTiles.Length; i += 2) { - m_Drunk.BAC = 60; + contains = tileID >= _swampTiles[i] && tileID <= _swampTiles[i + 1]; } - // chance to get sober - if (Utility.Random(100) < 10) + if (contains) { - --m_Drunk.BAC; - } + Delete(); - // lose some stats - m_Drunk.Stam -= 1; - m_Drunk.Mana -= 1; - - if (Utility.Random(1, 4) == 1) - { - if (!m_Drunk.Mounted) - { - // turn in a random direction - m_Drunk.Direction = (Direction)Utility.Random(8); - - // heave - m_Drunk.Animate(32, 5, 1, true, false, 0); - } - - // *hic* - m_Drunk.PublicOverheadMessage(MessageType.Regular, 0x3B2, 500849); - } - - if (m_Drunk.BAC <= 0) - { - Stop(); - m_Table.Remove(m_Drunk); - - m_Drunk.SendLocalizedMessage(500850); // You feel sober. + // You dip the container into the disgusting swamp water, collecting enough for the Hag's vile stew. + player.SendLocalizedMessage(1055035); + obj.Complete(); } } } } } + + public virtual void Pour_OnTarget(Mobile from, object targ) + { + if (IsEmpty || !Pourable || !ValidateUse(from, false)) + { + return; + } + + if (targ is BaseBeverage bev) + { + if (!bev.ValidateUse(from, true)) + { + return; + } + + if (bev.IsFull && bev.Content == Content) + { + from.SendLocalizedMessage(500848); // Couldn't pour it there. It was already full. + } + else if (!bev.IsEmpty) + { + from.SendLocalizedMessage(500846); // Can't pour it there. + } + else + { + bev.Content = Content; + bev.Poison = Poison; + bev.Poisoner = Poisoner; + + if (Quantity > bev.MaxQuantity) + { + bev.Quantity = bev.MaxQuantity; + Quantity -= bev.MaxQuantity; + } + else + { + bev.Quantity += Quantity; + Quantity = 0; + } + + from.PlaySound(0x4E); + } + } + else if (from == targ) + { + if (from.Thirst < 20) + { + from.Thirst += 1; + } + + if (ContainsAlcohol) + { + var bac = Content switch + { + BeverageType.Ale => 1, + BeverageType.Wine => 2, + BeverageType.Cider => 3, + BeverageType.Liquor => 4, + _ => 0 + }; + + from.BAC = Math.Min(from.BAC + bac, 60); + + CheckHeaveTimer(from); + } + + from.PlaySound(Utility.RandomList(0x30, 0x2D6)); + + if (Poison != null) + { + from.ApplyPoison(Poisoner, Poison); + } + + --Quantity; + } + else if (targ is BaseWaterContainer bwc) + { + if (Content != BeverageType.Water) + { + from.SendLocalizedMessage(500842); // Can't pour that in there. + } + else if (bwc.Items.Count != 0) + { + from.SendLocalizedMessage(500841); // That has something in it. + } + else + { + var itNeeds = Math.Min(bwc.MaxQuantity - bwc.Quantity, Quantity); + + if (itNeeds > 0) + { + bwc.Quantity += itNeeds; + Quantity -= itNeeds; + + from.PlaySound(0x4E); + } + } + } + else if (targ is PlantItem item) + { + item.Pour(from, this); + } + else if (targ is AddonComponent component && + component.Addon is WaterVatEast or WaterVatSouth && + Content == BeverageType.Water) + { + if (from is PlayerMobile { Quest: SolenMatriarchQuest qs }) + { + QuestObjective obj = qs.FindObjective(); + + if (obj?.Completed == false) + { + var vat = component.Addon; + + if (vat.X > 5784 && vat.X < 5814 && vat.Y > 1903 && vat.Y < 1934 && + (qs.RedSolen && vat.Map == Map.Trammel || !qs.RedSolen && vat.Map == Map.Felucca)) + { + if (obj.CurProgress + Quantity > obj.MaxProgress) + { + var delta = obj.MaxProgress - obj.CurProgress; + + Quantity -= delta; + obj.CurProgress = obj.MaxProgress; + } + else + { + obj.CurProgress += Quantity; + Quantity = 0; + } + } + } + } + } + else + { + from.SendLocalizedMessage(500846); // Can't pour it there. + } + } + + public override void OnDoubleClick(Mobile from) + { + if (IsEmpty) + { + if (!Fillable || !ValidateUse(from, true)) + { + return; + } + + from.BeginTarget(-1, true, TargetFlags.None, Fill_OnTarget); + SendLocalizedMessageTo(from, 500837); // Fill from what? + } + else if (Pourable && ValidateUse(from, true)) + { + from.BeginTarget(-1, true, TargetFlags.None, Pour_OnTarget); + from.SendLocalizedMessage(1010086); // What do you want to use this on? + } + } + + public static bool ConsumeTotal(Container pack, BeverageType content, int quantity) => + ConsumeTotal(pack, typeof(BaseBeverage), content, quantity); + + public static bool ConsumeTotal(Container pack, Type itemType, BeverageType content, int quantity) + { + var items = pack.FindItemsByType(itemType); + + // First pass, compute total + var total = 0; + + for (var i = 0; i < items.Length; ++i) + { + if (items[i] is BaseBeverage bev && bev.Content == content && !bev.IsEmpty) + { + total += bev.Quantity; + } + } + + if (total >= quantity) + { + // We've enough, so consume it + + var need = quantity; + + for (var i = 0; i < items.Length; ++i) + { + if (items[i] is not BaseBeverage bev || bev.Content != content || bev.IsEmpty) + { + continue; + } + + var theirQuantity = bev.Quantity; + + if (theirQuantity < need) + { + bev.Quantity = 0; + need -= theirQuantity; + } + else + { + bev.Quantity -= need; + return true; + } + } + } + + return false; + } + + private void Deserialize(IGenericReader reader, int version) + { + _poison = reader.ReadPoison(); + _poisoner = reader.ReadEntity(); + _rawContent = (BeverageType)reader.ReadInt(); + _rawQuantity = reader.ReadInt(); + } + + public static void Initialize() + { + EventSink.Login += EventSink_Login; + } + + private static void EventSink_Login(Mobile m) + { + CheckHeaveTimer(m); + } + + public static void CheckHeaveTimer(Mobile from) + { + if (from.BAC > 0 && from.Map != Map.Internal && !from.Deleted) + { + if (m_Table.ContainsKey(from)) + { + return; + } + + if (from.BAC > 60) + { + from.BAC = 60; + } + + m_Table[from] = new HeaveTimer(from).Start(); + } + else if (m_Table.Remove(from, out var t)) + { + t.Stop(); + + from.SendLocalizedMessage(500850); // You feel sober. + } + } + + private class HeaveTimer : Timer + { + private readonly Mobile m_Drunk; + + public HeaveTimer(Mobile drunk) : base(TimeSpan.FromSeconds(5.0), TimeSpan.FromSeconds(5.0)) => + m_Drunk = drunk; + + protected override void OnTick() + { + if (m_Drunk.Deleted || m_Drunk.Map == Map.Internal) + { + Stop(); + m_Table.Remove(m_Drunk); + } + else if (m_Drunk.Alive) + { + if (m_Drunk.BAC > 60) + { + m_Drunk.BAC = 60; + } + + // chance to get sober + if (Utility.Random(100) < 10) + { + --m_Drunk.BAC; + } + + // lose some stats + m_Drunk.Stam -= 1; + m_Drunk.Mana -= 1; + + if (Utility.Random(1, 4) == 1) + { + if (!m_Drunk.Mounted) + { + // turn in a random direction + m_Drunk.Direction = (Direction)Utility.Random(8); + + // heave + m_Drunk.Animate(32, 5, 1, true, false, 0); + } + + // *hic* + m_Drunk.PublicOverheadMessage(MessageType.Regular, 0x3B2, 500849); + } + + if (m_Drunk.BAC <= 0) + { + Stop(); + m_Table.Remove(m_Drunk); + + m_Drunk.SendLocalizedMessage(500850); // You feel sober. + } + } + } + } } diff --git a/Projects/UOContent/Migrations/Server.Items.AwaseMisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.AwaseMisoSoup.v0.json new file mode 100644 index 000000000..a66b23b95 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.AwaseMisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.AwaseMisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BaseBeverage.v2.json b/Projects/UOContent/Migrations/Server.Items.BaseBeverage.v2.json new file mode 100644 index 000000000..d47ccdf8d --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseBeverage.v2.json @@ -0,0 +1,32 @@ +{ + "version": 2, + "type": "Server.Items.BaseBeverage", + "properties": [ + { + "name": "Poison", + "type": "Server.Poison", + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Poison" + ] + }, + { + "name": "Poisoner", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + }, + { + "name": "RawContent", + "type": "Server.Items.BeverageType", + "rule": "EnumMigrationRule" + }, + { + "name": "RawQuantity", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BentoBox.v0.json b/Projects/UOContent/Migrations/Server.Items.BentoBox.v0.json new file mode 100644 index 000000000..9e0e46769 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BentoBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BentoBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.BeverageBottle.v0.json b/Projects/UOContent/Migrations/Server.Items.BeverageBottle.v0.json new file mode 100644 index 000000000..7075dfde9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BeverageBottle.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.BeverageBottle" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.CeramicMug.v0.json b/Projects/UOContent/Migrations/Server.Items.CeramicMug.v0.json new file mode 100644 index 000000000..b83ab8bad --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.CeramicMug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.CeramicMug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.EmptyBentoBox.v0.json b/Projects/UOContent/Migrations/Server.Items.EmptyBentoBox.v0.json new file mode 100644 index 000000000..334506c92 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.EmptyBentoBox.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.EmptyBentoBox" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GlassMug.v0.json b/Projects/UOContent/Migrations/Server.Items.GlassMug.v0.json new file mode 100644 index 000000000..31a38da4a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GlassMug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GlassMug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Goblet.v0.json b/Projects/UOContent/Migrations/Server.Items.Goblet.v0.json new file mode 100644 index 000000000..86789c133 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Goblet.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Goblet" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GreenTea.v0.json b/Projects/UOContent/Migrations/Server.Items.GreenTea.v0.json new file mode 100644 index 000000000..ca9179116 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GreenTea.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GreenTea" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.GreenTeaBasket.v0.json b/Projects/UOContent/Migrations/Server.Items.GreenTeaBasket.v0.json new file mode 100644 index 000000000..09ae11fea --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.GreenTeaBasket.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.GreenTeaBasket" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Jug.v0.json b/Projects/UOContent/Migrations/Server.Items.Jug.v0.json new file mode 100644 index 000000000..11aa8c3f9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Jug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Jug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.MisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.MisoSoup.v0.json new file mode 100644 index 000000000..4aaa4304a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.MisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.MisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.PewterMug.v0.json b/Projects/UOContent/Migrations/Server.Items.PewterMug.v0.json new file mode 100644 index 000000000..12010a8c9 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.PewterMug.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.PewterMug" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Pitcher.v0.json b/Projects/UOContent/Migrations/Server.Items.Pitcher.v0.json new file mode 100644 index 000000000..1b34123cc --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Pitcher.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Pitcher" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.RedMisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.RedMisoSoup.v0.json new file mode 100644 index 000000000..b1cdf2e03 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.RedMisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.RedMisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SushiPlatter.v0.json b/Projects/UOContent/Migrations/Server.Items.SushiPlatter.v0.json new file mode 100644 index 000000000..483d32d8e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SushiPlatter.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SushiPlatter" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.SushiRolls.v0.json b/Projects/UOContent/Migrations/Server.Items.SushiRolls.v0.json new file mode 100644 index 000000000..1028b71aa --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.SushiRolls.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.SushiRolls" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.Wasabi.v0.json b/Projects/UOContent/Migrations/Server.Items.Wasabi.v0.json new file mode 100644 index 000000000..0f8c5b12a --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.Wasabi.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.Wasabi" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WasabiClumps.v0.json b/Projects/UOContent/Migrations/Server.Items.WasabiClumps.v0.json new file mode 100644 index 000000000..084999ea5 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WasabiClumps.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WasabiClumps" +} \ No newline at end of file diff --git a/Projects/UOContent/Migrations/Server.Items.WhiteMisoSoup.v0.json b/Projects/UOContent/Migrations/Server.Items.WhiteMisoSoup.v0.json new file mode 100644 index 000000000..efe1f9e4b --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.WhiteMisoSoup.v0.json @@ -0,0 +1,4 @@ +{ + "version": 0, + "type": "Server.Items.WhiteMisoSoup" +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/Poison.cs b/Projects/UOContent/Misc/Poison.cs index 68d61d977..e0ce1542b 100644 --- a/Projects/UOContent/Misc/Poison.cs +++ b/Projects/UOContent/Misc/Poison.cs @@ -158,8 +158,8 @@ namespace Server AOS.Damage(m_Mobile, From, damage, 0, 0, 0, 100, 0); - if (Utility.RandomDouble() >= 0.60 - ) // OSI: randomly revealed between first and third damage tick, guessing 60% chance + // OSI: randomly revealed between first and third damage tick, guessing 60% chance + if (Utility.RandomDouble() >= 0.60) { m_Mobile.RevealingAction(); }