using System; using System.Collections.Generic; using ModernUO.Serialization; using Server.Items; namespace Server.Mobiles { public partial class GenericBuyInfo : IBuyItemInfo { private int m_Amount; private IEntity m_DisplayEntity; private int m_Price; public GenericBuyInfo(Type type, int price, int amount, int itemID, int hue, object[] args = null) : this( null, type, price, amount, itemID, hue, args ) { } public GenericBuyInfo(string name, Type type, int price, int amount, int itemID, int hue, object[] args = null) { Type = type; m_Price = price; MaxAmount = m_Amount = amount; ItemID = itemID; Hue = hue; Args = args; Name = name ?? (itemID < 0x4000 ? (1020000 + itemID).ToString() : (1078872 + itemID).ToString()); } public virtual bool CanCacheDisplay => false; public Type Type { get; set; } public int DefaultPrice { get; private set; } public object[] Args { get; set; } public virtual int ControlSlots => 0; public string Name { get; set; } public int PriceScalar { get => DefaultPrice; set => DefaultPrice = value; } public int Price { get { if (DefaultPrice != 0) { if (m_Price > 5000000) { long price = m_Price; price *= DefaultPrice; price += 50; price /= 100; if (price > int.MaxValue) { price = int.MaxValue; } return (int)price; } return (m_Price * DefaultPrice + 50) / 100; } return m_Price; } set => m_Price = value; } public int ItemID { get; set; } public int Hue { get; set; } public int Amount { get => m_Amount; set => m_Amount = Math.Max(value, 0); } public int MaxAmount { get; set; } // get a new instance of an object (we just bought it) public virtual IEntity GetEntity() => Type.CreateInstance(Args); // Attempt to restock with item, (return true if restock successful) public bool Restock(Item item, int amount) => false; public void OnRestock() { if (m_Amount <= 0) { MaxAmount = Math.Min(999, MaxAmount * 2); } else { /* NOTE: According to UO.com, the quantity is halved if the item does not reach 0 * Here we implement differently: the quantity is halved only if less than half * of the maximum quantity was bought. That is, if more than half is sold, then * there's clearly a demand and we should not cut down on the stock. */ var halfQuantity = MaxAmount; if (halfQuantity >= 999) { halfQuantity = 640; } else if (halfQuantity > 20) { halfQuantity /= 2; } if (m_Amount >= halfQuantity) { MaxAmount = halfQuantity; } } m_Amount = MaxAmount; } public void DeleteDisplayEntity() { m_DisplayEntity?.Delete(); m_DisplayEntity = null; } public IEntity GetDisplayEntity() { if (m_DisplayEntity?.Deleted == false) { return m_DisplayEntity; } var canCache = CanCacheDisplay; if (canCache) { m_DisplayEntity = DisplayCache.Cache.Lookup(Type); } if (m_DisplayEntity?.Deleted != false) { m_DisplayEntity = GetEntity(); DisplayCache.Cache.Store(Type, m_DisplayEntity, canCache); } return m_DisplayEntity; } [SerializationGenerator(0, false)] private partial class DisplayCache : Container { private static DisplayCache _cache; [SerializableField(0)] private List _mobiles; private Dictionary m_Table; private DisplayCache() : base(0) { m_Table = new Dictionary(); _mobiles = new List(); } public static DisplayCache Cache { get { if (_cache?.Deleted != false) { _cache = new DisplayCache(); } return _cache; } } public IEntity Lookup(Type key) { m_Table.TryGetValue(key, out var e); return e; } public void Store(Type key, IEntity obj, bool cache) { if (cache) { m_Table[key] = obj; } if (obj is Item item) { AddItem(item); } else if (obj is Mobile mobile) { _mobiles.Add(mobile); } } private void ClearContents() { for (var i = 0; i < _mobiles.Count; ++i) { _mobiles[i].Delete(); } _mobiles.Clear(); for (var i = Items.Count - 1; i >= 0; --i) { if (i < Items.Count) { Items[i].Delete(); } } } public override void OnAfterDelete() { base.OnAfterDelete(); ClearContents(); if (_cache == this) { _cache = null; } } [AfterDeserialization] private void AfterDeserialization() { if (_cache == this && Items.Count <= 0 && _mobiles.Count <= 0) { return; } Timer.StartTimer(() => { ClearContents(); if (_cache == null) { _cache = this; m_Table = []; } else { Delete(); } } ); } } } }