using System; using System.Collections; using System.Collections.Generic; using Server.Items; using Server.Network; using Server.ContextMenus; using Server.Mobiles; using Server.Misc; using Server.Regions; namespace Server.Mobiles { public abstract class BaseVendor : BaseCreature, IVendor { public DateTime m_NextAction; public DateTime NextAction{ get{ return m_NextAction; } set{ m_NextAction = value; } } private const int MaxSell = 500; protected abstract List SBInfos { get; } private ArrayList m_ArmorBuyInfo = new ArrayList(); private ArrayList m_ArmorSellInfo = new ArrayList(); private DateTime m_LastRestock; public override bool CanTeach { get { return true; } } public override bool BardImmune { get { return true; } } public override bool PlayerRangeSensitive { get { return true; } } public virtual bool IsActiveVendor { get { return true; } } public virtual bool IsActiveBuyer { get { return IsActiveVendor; } } // response to vendor SELL public virtual bool IsActiveSeller { get { return IsActiveVendor; } } // repsonse to vendor BUY public virtual NpcGuild NpcGuild { get { return NpcGuild.None; } } public override bool ShowFameTitle { get { return false; } } public BaseVendor( string title ): base( AIType.AI_Vendor, FightMode.None, 2, 1, 0.5, 2 ) { LoadSBInfo(); Invulnerable = true; Clan = Clan.Citizen; SetStr( 100 ); SetDex( 100 ); SetInt( 100 ); this.Title = title; SpeechHue = Utility.RandomSpeechHue(); InitBody(); InitOutfit(); Container pack; //these packs MUST exist, or the client will crash when the packets are sent pack = new Backpack(); pack.Layer = Layer.ShopBuy; pack.Movable = false; pack.Visible = false; AddItem( pack ); pack = new Backpack(); pack.Layer = Layer.ShopResale; pack.Movable = false; pack.Visible = false; AddItem( pack ); m_LastRestock = DateTime.Now; } public BaseVendor( Serial serial ): base( serial ) { } public DateTime LastRestock { get { return m_LastRestock; } set { m_LastRestock = value; } } public virtual TimeSpan RestockDelay { get { return TimeSpan.FromHours( 1 ); } } public Container BuyPack { get { Container pack = FindItemOnLayer( Layer.ShopBuy ) as Container; if ( pack == null ) { pack = new Backpack(); pack.Layer = Layer.ShopBuy; pack.Visible = false; AddItem( pack ); } return pack; } } public abstract void InitSBInfo(); protected void LoadSBInfo() { m_LastRestock = DateTime.Now; for ( int i = 0; i < m_ArmorBuyInfo.Count; ++i ) { GenericBuyInfo buy = m_ArmorBuyInfo[i] as GenericBuyInfo; if ( buy != null ) buy.DeleteDisplayEntity(); } SBInfos.Clear(); InitSBInfo(); m_ArmorBuyInfo.Clear(); m_ArmorSellInfo.Clear(); for ( int i = 0; i < SBInfos.Count; i++ ) { SBInfo sbInfo = (SBInfo)SBInfos[i]; m_ArmorBuyInfo.AddRange( sbInfo.BuyInfo ); m_ArmorSellInfo.Add( sbInfo.SellInfo ); } } public virtual bool GetGender() { return Utility.RandomBool(); } public virtual void InitBody() { InitStats( 100, 100, 25 ); SpeechHue = Utility.RandomSpeechHue(); Hue = Utility.RandomSkinHue(); if ( Invulnerable ) NameHue = 0x35; int hair = Utility.RandomHairHue(); if ( this.Female = Utility.RandomBool() || this is Gypsy ) { Female = true; Body = 0x191; Name = NameList.RandomName( "female" ); } else { Body = 0x190; Name = NameList.RandomName( "male" ); if ( Utility.RandomBool() ){ Utility.AssignRandomFacialHair( this, hair ); } } Utility.AssignRandomHair( this, hair ); } public static void DoJokes( Mobile m ) { int act = Utility.Random( 28 ); if ( m is PlayerMobile ){ act = Utility.Random( 22 ); } switch ( act ) { case 0: m.Say("Why did the king go to the dentist? To get his teeth crowned."); break; case 1: m.Say("When a knight in armor was killed in battle, what sign did they put on his grave? Rust in peace!"); break; case 2: m.Say("What do you call a mosquito in a tin suit? A bite in shining armor."); break; case 3: m.Say("There are many castles in the world, but who is strong enough to move one? Any chess player"); break; case 4: m.Say("What king was famous because he spent so many nights at his Round Table writing books? King Author!"); break; case 5: m.Say("How do you find a princess? You follow the foot prince."); break; case 6: m.Say("Why were the early days called the dark ages? Because there were so many knights!"); break; case 7: m.Say("Why did Arthur have a round table? So no one could corner him!"); break; case 8: m.Say("Who invented King Arthur's round table? Sir Cumference!"); break; case 9: m.Say("Why did the knight run about shouting for a tin opener? He had a bee in his suit of armor!"); break; case 10: m.Say("What was Camelot famous for? It's knight life!"); break; case 11: m.Say("What did the toad say when the princess would not kiss him? Warts the matter with you?"); break; case 12: m.Say("What do you call the young royal who keeps falling down? Prince Harming!"); break; case 13: m.Say("What do you call a cat that flies over the castle wall? A cat-a-pult!"); break; case 14: m.Say("What game do the fish play in the moat? Trout or dare!"); break; case 15: m.Say("What did the fish say to the other when the horse fell in the moat? See horse!"); break; case 16: m.Say("What do you call an angry princess just awakened from a long sleep? Slapping beauty!"); break; case 17: m.Say("How did the prince get into the castle when the drawbridge was broken? He used a rowmoat!"); break; case 18: m.Say("How did the girl dragon win the beauty contest? She was the beast of the show!"); break; case 19: m.Say("Why did the dinosaur live longer than the dragon? Because it didn’t smoke!"); break; case 20: m.Say("What did the dragon say when it saw the Knight? 'Not more tinned food!'"); break; case 21: m.Say("What do you do with a green dragon? Wait until it ripens!"); break; case 22: m.PlaySound( m.Female ? 780 : 1051 ); m.Say( "*claps*" ); break; case 23: m.Say( "*bows*" ); m.Animate( 32, 5, 1, true, false, 0 ); break; case 24: m.PlaySound( m.Female ? 794 : 1066 ); m.Say( "*giggles*" ); break; case 25: m.PlaySound( m.Female ? 801 : 1073 ); m.Say( "*laughs*" ); break; case 26: m.PlaySound( 792 ); m.Say( "*sticks out tongue*" ); break; case 27: m.PlaySound( m.Female ? 783 : 1054 ); m.Say( "*woohoo!*" ); break; }; if ( act < 22 && Utility.RandomBool() ) { switch ( Utility.Random( 6 )) { case 0: m.PlaySound( m.Female ? 780 : 1051 ); break; case 1: m.Animate( 32, 5, 1, true, false, 0 ); break; case 2: m.PlaySound( m.Female ? 794 : 1066 ); break; case 3: m.PlaySound( m.Female ? 801 : 1073 ); break; case 4: m.PlaySound( 792 ); break; case 5: m.PlaySound( m.Female ? 783 : 1054 ); break; }; } } public override void OnThink() { bool ran = false; if ( DateTime.Now >= m_NextAction && ( this is Jester || ( this.Location == this.Home && this.RangeHome == 0 ) ) ) { int range = 1; if ( this is Fisherman || this is Ranger || this is Bowyer ) range = 7; m_NextAction = (DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 5, 15 ) ) ); if ( this is Jester ) { DoJokes( this ); m_NextAction = (DateTime.Now + TimeSpan.FromSeconds( Utility.RandomMinMax( 25, 35 ) ) ); ran = true; } else { foreach ( Item act in this.GetItemsInRange( range ) ) { if ( act.Map == this.Map && act is ArcheryButte && ( this is Ranger || this is Bowyer ) && ( act.X == X || act.Y == Y ) ) { Server.Items.Actions.EquipVendor( this, "bow" ); ran = true; if ( this is Bowyer ){ this.Title = "the archer"; } act.OnDoubleClick( this ); } else if ( act.Map == this.Map && act is TrainingDummy && this is Fighter ) { ran = true; act.OnDoubleClick( this ); } else if ( act.Map == this.Map && act is Actions ) { int z = Z - act.Z; if ( z < 0 ) z = act.Z - Z; if ( z < 15 ) { ran = true; act.OnDoubleClick( this ); } } } } if ( !ran ) m_NextAction = (DateTime.Now).AddYears(1); } base.OnThink(); } public override void OnAfterSpawn() { } protected override void OnMapChange( Map oldMap ) { base.OnMapChange( oldMap ); LoadSBInfo(); } public virtual void CapitalizeTitle() { string title = this.Title; if ( title == null ) return; string[] split = title.Split( ' ' ); for ( int i = 0; i < split.Length; ++i ) { if ( Insensitive.Equals( split[i], "the" ) ) continue; if ( split[i].Length > 1 ) split[i] = Char.ToUpper( split[i][0] ) + split[i].Substring( 1 ); else if ( split[i].Length > 0 ) split[i] = Char.ToUpper( split[i][0] ).ToString(); } this.Title = String.Join( " ", split ); } public virtual void InitOutfit() { switch ( Utility.Random( 5 ) ) { case 0: AddItem( new FancyShirt( Utility.RandomHue() ) ); break; case 1: AddItem( new Doublet( Utility.RandomHue() ) ); break; case 2: AddItem( new Shirt( Utility.RandomHue() ) ); break; case 3: AddItem( new Tunic( Utility.RandomHue() ) ); break; case 4: AddItem( new Surcoat( Utility.RandomHue() ) ); break; } switch ( Utility.Random( 4 ) ) { case 0: AddItem( new Shoes( Utility.RandomNeutralHue() ) ); break; case 1: AddItem( new Boots( Utility.RandomNeutralHue() ) ); break; case 2: AddItem( new Sandals( Utility.RandomNeutralHue() ) ); break; case 3: AddItem( new ThighBoots( Utility.RandomNeutralHue() ) ); break; } int hairHue = Utility.RandomHairHue(); Utility.AssignRandomHair( this, hairHue ); Utility.AssignRandomFacialHair( this, hairHue ); if ( Female ) { switch ( Utility.Random( 4 ) ) { case 0: AddItem( new LongPants( Utility.RandomNeutralHue() ) ); break; case 1: AddItem( new ShortPants( Utility.RandomNeutralHue() ) ); break; case 2: AddItem( new Kilt( Utility.RandomNeutralHue() ) ); break; case 3: AddItem( new Skirt( Utility.RandomHue() ) ); break; } } else { switch ( Utility.Random( 3 ) ) { case 0: AddItem( new LongPants( Utility.RandomNeutralHue() ) ); break; case 1: AddItem( new ShortPants( Utility.RandomNeutralHue() ) ); break; case 2: AddItem( new Kilt( Utility.RandomNeutralHue() ) ); break; } } int robeHue = Utility.RandomHue(); if ( this is BaseHealer || this is Mage || this is Scribe || this is Alchemist || this is MageGuildmaster || this is LibraryGuildmaster || this is HealerGuildmaster || this is AlchemistGuildmaster || Utility.RandomMinMax(1,10) == 1 ) { if ( Female ) { switch ( Utility.Random( 3 ) ) { case 0: AddItem( new Robe( robeHue ) ); break; case 1: AddItem( new PlainDress( robeHue ) ); break; case 2: AddItem( new FancyDress( robeHue ) ); break; } } else { AddItem( new Robe( robeHue ) ); } } if ( this is Mage || this is MageGuildmaster ) AddItem( new WizardsHat( robeHue ) ); else if ( this is Farmer && Utility.RandomBool() ) { switch ( Utility.Random( 4 ) ) { case 0: AddItem( new WideBrimHat( Utility.RandomHue() ) ); break; case 1: AddItem( new FloppyHat( Utility.RandomHue() ) ); break; case 2: AddItem( new StrawHat() ); break; case 3: AddItem( new TallStrawHat() ); break; } } else if ( this is Blacksmith || this is Carpenter || this is IronWorker || this is Armorer || this is Weaponsmith || this is CarpentryGuildmaster || this is BlacksmithGuildmaster || this is Bowyer ) { switch ( Utility.Random( 2 ) ) { case 0: AddItem( new Bandana( Utility.RandomHue() ) ); break; case 1: AddItem( new SkullCap( Utility.RandomHue() ) ); break; } } } public virtual void Restock() { m_LastRestock = DateTime.Now; IBuyItemInfo[] buyInfo = this.GetBuyInfo(); foreach ( IBuyItemInfo bii in buyInfo ) bii.OnRestock(); } private static TimeSpan InventoryDecayTime = TimeSpan.FromHours( 1.0 ); public virtual void VendorBuy( Mobile from ) { if ( !IsActiveSeller ) return; if ( !from.CheckAlive() ) return; if ( !CheckVendorAccess( from ) ) { Say( 501522 ); // I shall not deal with scum like thee! return; } if ( DateTime.Now - m_LastRestock > RestockDelay ) Restock(); int count = 0; List list; IBuyItemInfo[] buyInfo = this.GetBuyInfo(); IShopSellInfo[] sellInfo = this.GetSellInfo(); list = new List( buyInfo.Length ); Container cont = this.BuyPack; List opls = null; for ( int idx = 0; idx < buyInfo.Length; idx++ ) { IBuyItemInfo buyItem = (IBuyItemInfo)buyInfo[idx]; if ( buyItem.Amount <= 0 || list.Count >= 250 ) continue; // NOTE: Only GBI supported; if you use another implementation of IBuyItemInfo, this will crash GenericBuyInfo gbi = (GenericBuyInfo)buyItem; IEntity disp = gbi.GetDisplayEntity(); list.Add( new BuyItemState( buyItem.Name, cont.Serial, disp == null ? (Serial)0x7FC0FFEE : disp.Serial, buyItem.Price, buyItem.Amount, buyItem.ItemID, buyItem.Hue ) ); count++; if ( opls == null ) { opls = new List(); } if ( disp is Item ) { opls.Add( ( ( Item ) disp ).PropertyList ); } else if ( disp is Mobile ) { opls.Add( ( ( Mobile ) disp ).PropertyList ); } } List playerItems = cont.Items; for ( int i = playerItems.Count - 1; i >= 0; --i ) { if ( i >= playerItems.Count ) continue; Item item = playerItems[i]; if ( ( item.LastMoved + InventoryDecayTime ) <= DateTime.Now ) item.Delete(); } for ( int i = 0; i < playerItems.Count; ++i ) { Item item = playerItems[i]; int price = 0; string name = null; foreach ( IShopSellInfo ssi in sellInfo ) { if ( ssi.IsSellable( item ) ) { price = ssi.GetBuyPriceFor( item ); name = ssi.GetNameFor( item ); break; } } if ( name != null && list.Count < 250 ) { list.Add( new BuyItemState( name, cont.Serial, item.Serial, price, item.Amount, item.ItemID, item.Hue ) ); count++; if ( opls == null ) { opls = new List(); } opls.Add( item.PropertyList ); } } //one (not all) of the packets uses a byte to describe number of items in the list. Osi = dumb. //if ( list.Count > 255 ) // Console.WriteLine( "Vendor Warning: Vendor {0} has more than 255 buy items, may cause client errors!", this ); if ( list.Count > 0 ) { list.Sort( new BuyItemStateComparer() ); SendPacksTo( from ); NetState ns = from.NetState; if ( ns == null ) return; if ( ns.ContainerGridLines ) from.Send( new VendorBuyContent6017( list ) ); else from.Send( new VendorBuyContent( list ) ); from.Send( new VendorBuyList( this, list ) ); if ( ns.HighSeas ) from.Send( new DisplayBuyListHS( this ) ); else from.Send( new DisplayBuyList( this ) ); from.Send( new MobileStatusExtended( from ) );//make sure their gold amount is sent if ( opls != null ) { for ( int i = 0; i < opls.Count; ++i ) { from.Send( opls[i] ); } } SayTo( from, 500186 ); // Greetings. Have a look around. } } public virtual void SendPacksTo( Mobile from ) { Item pack = FindItemOnLayer( Layer.ShopBuy ); if ( pack == null ) { pack = new Backpack(); pack.Layer = Layer.ShopBuy; pack.Movable = false; pack.Visible = false; AddItem( pack ); } from.Send( new EquipUpdate( pack ) ); pack = FindItemOnLayer( Layer.ShopSell ); if ( pack != null ) from.Send( new EquipUpdate( pack ) ); pack = FindItemOnLayer( Layer.ShopResale ); if ( pack == null ) { pack = new Backpack(); pack.Layer = Layer.ShopResale; pack.Movable = false; pack.Visible = false; AddItem( pack ); } from.Send( new EquipUpdate( pack ) ); } public virtual void VendorSell( Mobile from ) { if ( !IsActiveBuyer ) return; if ( !from.CheckAlive() ) return; if ( !CheckVendorAccess( from ) ) { Say( 501522 ); // I shall not deal with scum like thee! return; } Container pack = from.Backpack; if ( pack != null ) { IShopSellInfo[] info = GetSellInfo(); Hashtable table = new Hashtable(); foreach ( IShopSellInfo ssi in info ) { Item[] items = pack.FindItemsByType( ssi.Types ); foreach ( Item item in items ) { if ( item is Container && ( (Container)item ).Items.Count != 0 ) continue; if ( item.IsStandardLoot() && item.Movable && ssi.IsSellable( item ) ) table[item] = new SellItemState( item, ssi.GetSellPriceFor( item ), ssi.GetNameFor( item ) ); } } if ( table.Count > 0 ) { SendPacksTo( from ); from.Send( new VendorSellList( this, table ) ); } else { Say( true, "You have nothing I would be interested in." ); } } } private GenericBuyInfo LookupDisplayObject( object obj ) { IBuyItemInfo[] buyInfo = this.GetBuyInfo(); for ( int i = 0; i < buyInfo.Length; ++i ) { GenericBuyInfo gbi = (GenericBuyInfo)buyInfo[i]; if ( gbi.GetDisplayEntity() == obj ) return gbi; } return null; } private void ProcessSinglePurchase( BuyItemResponse buy, IBuyItemInfo bii, List validBuy, ref int controlSlots, ref bool fullPurchase, ref int totalCost ) { int amount = buy.Amount; if ( amount > bii.Amount ) amount = bii.Amount; if ( amount <= 0 ) return; int slots = bii.ControlSlots * amount; if ( controlSlots >= slots ) { controlSlots -= slots; } else { fullPurchase = false; return; } totalCost += bii.Price * amount; validBuy.Add( buy ); } public void GemSet( Item item ) { if ( item is BaseJewel ) ((BaseJewel)item).GemType = GemType.None; } private void ProcessValidPurchase( int amount, IBuyItemInfo bii, Mobile buyer, Container cont ) { if ( amount > bii.Amount ) amount = bii.Amount; if ( amount < 1 ) return; bii.Amount -= amount; IEntity o = bii.GetEntity(); if ( o is Item ) { Item item = (Item)o; if ( item.Stackable ) { item.Amount = amount; GemSet( item ); if ( cont == null || !cont.TryDropItem( buyer, item, false ) ) item.MoveToWorld( buyer.Location, buyer.Map ); } else { item.Amount = 1; GemSet( item ); if ( cont == null || !cont.TryDropItem( buyer, item, false ) ) item.MoveToWorld( buyer.Location, buyer.Map ); for ( int i = 1; i < amount; i++ ) { item = bii.GetEntity() as Item; if ( item != null ) { item.Amount = 1; GemSet( item ); if ( cont == null || !cont.TryDropItem( buyer, item, false ) ) item.MoveToWorld( buyer.Location, buyer.Map ); } } } } else if ( o is Mobile ) { Mobile m = (Mobile)o; m.Direction = (Direction)Utility.Random( 8 ); m.MoveToWorld( buyer.Location, buyer.Map ); m.PlaySound( m.GetIdleSound() ); if ( m is BaseCreature ) ( (BaseCreature)m ).SetControlMaster( buyer ); for ( int i = 1; i < amount; ++i ) { m = bii.GetEntity() as Mobile; if ( m != null ) { m.Direction = (Direction)Utility.Random( 8 ); m.MoveToWorld( buyer.Location, buyer.Map ); if ( m is BaseCreature ) ( (BaseCreature)m ).SetControlMaster( buyer ); } } } } public virtual bool OnBuyItems( Mobile buyer, List list ) { if ( !IsActiveSeller ) return false; if ( !buyer.CheckAlive() ) return false; if ( !CheckVendorAccess( buyer ) ) { Say( 501522 ); // I shall not deal with scum like thee! return false; } IBuyItemInfo[] buyInfo = this.GetBuyInfo(); IShopSellInfo[] info = GetSellInfo(); int totalCost = 0; List validBuy = new List( list.Count ); Container cont; bool bought = false; bool fromInn = false; bool fullPurchase = true; int controlSlots = buyer.FollowersMax - buyer.Followers; foreach ( BuyItemResponse buy in list ) { Serial ser = buy.Serial; int amount = buy.Amount; if ( ser.IsItem ) { Item item = World.FindItem( ser ); if ( item == null ) continue; GenericBuyInfo gbi = LookupDisplayObject( item ); if ( gbi != null ) { ProcessSinglePurchase( buy, gbi, validBuy, ref controlSlots, ref fullPurchase, ref totalCost ); } else if ( item != this.BuyPack && item.IsChildOf( this.BuyPack ) ) { if ( amount > item.Amount ) amount = item.Amount; if ( amount <= 0 ) continue; foreach ( IShopSellInfo ssi in info ) { if ( ssi.IsSellable( item ) ) { if ( ssi.IsResellable( item ) ) { totalCost += ssi.GetBuyPriceFor( item ) * amount; validBuy.Add( buy ); break; } } } } } else if ( ser.IsMobile ) { Mobile mob = World.FindMobile( ser ); if ( mob == null ) continue; GenericBuyInfo gbi = LookupDisplayObject( mob ); if ( gbi != null ) ProcessSinglePurchase( buy, gbi, validBuy, ref controlSlots, ref fullPurchase, ref totalCost ); } }//foreach if ( fullPurchase && validBuy.Count == 0 ) SayTo( buyer, 500190 ); // Thou hast bought nothing! else if ( validBuy.Count == 0 ) SayTo( buyer, 500187 ); // Your order cannot be fulfilled, please try again. if ( validBuy.Count == 0 ) return false; bought = ( buyer.AccessLevel >= AccessLevel.GameMaster ); cont = buyer.Backpack; if ( !bought && cont != null ) { if ( cont.ConsumeTotal( typeof( Gold ), totalCost ) ) bought = true; else if ( totalCost < 2000 ) SayTo( buyer, 500192 );//Begging thy pardon, but thou casnt afford that. } if ( !bought && totalCost >= 2000 ) { cont = buyer.FindInnNoCreate(); if ( cont != null && cont.ConsumeTotal( typeof( Gold ), totalCost ) ) { bought = true; fromInn = true; } else { SayTo( buyer, 500191 ); //Begging thy pardon, but thy inn chest lacks these funds. } } if ( !bought ) return false; else buyer.PlaySound( 0x32 ); cont = buyer.Backpack; if ( cont == null ) cont = buyer.InnBox; foreach ( BuyItemResponse buy in validBuy ) { Serial ser = buy.Serial; int amount = buy.Amount; if ( amount < 1 ) continue; if ( ser.IsItem ) { Item item = World.FindItem( ser ); if ( item == null ) continue; GenericBuyInfo gbi = LookupDisplayObject( item ); if ( gbi != null ) { ProcessValidPurchase( amount, gbi, buyer, cont ); } else { if ( amount > item.Amount ) amount = item.Amount; foreach ( IShopSellInfo ssi in info ) { if ( ssi.IsSellable( item ) ) { if ( ssi.IsResellable( item ) ) { Item buyItem; if ( amount >= item.Amount ) { buyItem = item; } else { buyItem = Mobile.LiftItemDupe( item, item.Amount - amount ); if ( buyItem == null ) buyItem = item; } GemSet( buyItem ); if ( cont == null || !cont.TryDropItem( buyer, buyItem, false ) ) buyItem.MoveToWorld( buyer.Location, buyer.Map ); break; } } } } } else if ( ser.IsMobile ) { Mobile mob = World.FindMobile( ser ); if ( mob == null ) continue; GenericBuyInfo gbi = LookupDisplayObject( mob ); if ( gbi != null ) ProcessValidPurchase( amount, gbi, buyer, cont ); } }//foreach if ( fullPurchase ) { if ( buyer.AccessLevel >= AccessLevel.GameMaster ) SayTo( buyer, true, "I would not presume to charge thee anything. Here are the goods you requested." ); else if ( fromInn ) SayTo( buyer, true, "The total of thy purchase is {0} gold, which has been withdrawn from your inn chest. My thanks for the patronage.", totalCost ); else SayTo( buyer, true, "The total of thy purchase is {0} gold. My thanks for the patronage.", totalCost ); } else { if ( buyer.AccessLevel >= AccessLevel.GameMaster ) SayTo( buyer, true, "I would not presume to charge thee anything. Unfortunately, I could not sell you all the goods you requested." ); else if ( fromInn ) SayTo( buyer, true, "The total of thy purchase is {0} gold, which has been withdrawn from your inn chest. My thanks for the patronage. Unfortunately, I could not sell you all the goods you requested.", totalCost ); else SayTo( buyer, true, "The total of thy purchase is {0} gold. My thanks for the patronage. Unfortunately, I could not sell you all the goods you requested.", totalCost ); } return true; } public virtual bool CheckVendorAccess( Mobile from ) { PlayerMobile pm = (PlayerMobile)from; if ( this is BaseGuildmaster && this.NpcGuild == pm.NpcGuild && ( this.NpcGuild == NpcGuild.AssassinsGuild || this.NpcGuild == NpcGuild.ThievesGuild ) ) // THESE GUILDS DEAL WITH GUILD MEMBERS NO MATTER WHAT return true; if ( from.Criminal && !Server.Misc.NotorietyHandlers.CriminalTolerated( from ) ) return false; if ( from.Kills >= 5 && !Server.Misc.NotorietyHandlers.CriminalTolerated( from ) ) return false; if ( this is BaseGuildmaster && this.NpcGuild != pm.NpcGuild ) // ONLY GUILD MEMBERS CAN BUY FROM GUILD MASTERS return false; return true; } public virtual bool OnSellItems( Mobile seller, List list ) { if ( !IsActiveBuyer ) return false; if ( !seller.CheckAlive() ) return false; if ( !CheckVendorAccess( seller ) ) { Say( 501522 ); // I shall not deal with scum like thee! return false; } seller.PlaySound( 0x32 ); IShopSellInfo[] info = GetSellInfo(); IBuyItemInfo[] buyInfo = this.GetBuyInfo(); int GiveGold = 0; int Sold = 0; Container cont; foreach ( SellItemResponse resp in list ) { if ( resp.Item.RootParent != seller || resp.Amount <= 0 || !resp.Item.IsStandardLoot() || !resp.Item.Movable || ( resp.Item is Container && ( (Container)resp.Item ).Items.Count != 0 ) ) continue; foreach ( IShopSellInfo ssi in info ) { if ( ssi.IsSellable( resp.Item ) ) { Sold++; break; } } } if ( Sold > MaxSell ) { SayTo( seller, true, "You may only sell {0} items at a time!", MaxSell ); return false; } else if ( Sold == 0 ) { return true; } foreach ( SellItemResponse resp in list ) { if ( resp.Item.RootParent != seller || resp.Amount <= 0 || !resp.Item.IsStandardLoot() || !resp.Item.Movable || ( resp.Item is Container && ( (Container)resp.Item ).Items.Count != 0 ) ) continue; foreach ( IShopSellInfo ssi in info ) { if ( ssi.IsSellable( resp.Item ) ) { int amount = resp.Amount; if ( amount > resp.Item.Amount ) amount = resp.Item.Amount; if ( ssi.IsResellable( resp.Item ) ) { bool found = false; foreach ( IBuyItemInfo bii in buyInfo ) { if ( bii.Restock( resp.Item, amount ) ) { resp.Item.Consume( amount ); found = true; break; } } if ( !found ) { cont = this.BuyPack; if ( amount < resp.Item.Amount ) { Item item = Mobile.LiftItemDupe( resp.Item, resp.Item.Amount - amount ); if ( item != null ) { item.SetLastMoved(); cont.DropItem( item ); } else { resp.Item.SetLastMoved(); cont.DropItem( resp.Item ); } } else { resp.Item.SetLastMoved(); cont.DropItem( resp.Item ); } } } else { if ( amount < resp.Item.Amount ) resp.Item.Amount -= amount; else resp.Item.Delete(); } GiveGold += ssi.GetSellPriceFor( resp.Item ) * amount; break; } } } if ( GiveGold > 0 ) { while ( GiveGold > 60000 ) { seller.AddToBackpack( new Gold( 60000 ) ); GiveGold -= 60000; } seller.AddToBackpack( new Gold( GiveGold ) ); seller.PlaySound( 0x0037 );//Gold dropping sound } return true; } public override void Serialize( GenericWriter writer ) { base.Serialize( writer ); writer.Write( (int)1 ); // version List sbInfos = this.SBInfos; for ( int i = 0; sbInfos != null && i < sbInfos.Count; ++i ) { SBInfo sbInfo = sbInfos[i]; List buyInfo = sbInfo.BuyInfo; for ( int j = 0; buyInfo != null && j < buyInfo.Count; ++j ) { GenericBuyInfo gbi = (GenericBuyInfo)buyInfo[j]; int maxAmount = gbi.MaxAmount; int doubled = 0; switch ( maxAmount ) { case 40: doubled = 1; break; case 80: doubled = 2; break; case 160: doubled = 3; break; case 320: doubled = 4; break; case 640: doubled = 5; break; case 999: doubled = 6; break; } if ( doubled > 0 ) { writer.WriteEncodedInt( 1 + ( ( j * sbInfos.Count ) + i ) ); writer.WriteEncodedInt( doubled ); } } } writer.WriteEncodedInt( 0 ); } public override void Deserialize( GenericReader reader ) { base.Deserialize( reader ); int version = reader.ReadInt(); LoadSBInfo(); List sbInfos = this.SBInfos; switch ( version ) { case 1: { int index; while ( ( index = reader.ReadEncodedInt() ) > 0 ) { int doubled = reader.ReadEncodedInt(); if ( sbInfos != null ) { index -= 1; int sbInfoIndex = index % sbInfos.Count; int buyInfoIndex = index / sbInfos.Count; if ( sbInfoIndex >= 0 && sbInfoIndex < sbInfos.Count ) { SBInfo sbInfo = sbInfos[sbInfoIndex]; List buyInfo = sbInfo.BuyInfo; if ( buyInfo != null && buyInfoIndex >= 0 && buyInfoIndex < buyInfo.Count ) { GenericBuyInfo gbi = (GenericBuyInfo)buyInfo[buyInfoIndex]; int amount = 20; switch ( doubled ) { case 1: amount = 40; break; case 2: amount = 80; break; case 3: amount = 160; break; case 4: amount = 320; break; case 5: amount = 640; break; case 6: amount = 999; break; } gbi.Amount = gbi.MaxAmount = amount; } } } } break; } } } public void Speak() { switch( Utility.Random( 8 ) ) { case 0: PlaySound( Female ? 797 : 1069 ); // hey break; case 1: PlaySound( Female ? 778 : 1049 ); // ah break; case 2: PlaySound( Female ? 799 : 1071 ); // huh break; case 3: PlaySound( Female ? 803 : 1075 ); // oh break; case 4: PlaySound( Female ? 811 : 1085 ); // oooh break; case 5: PlaySound( Female ? 816 : 1090 ); // sigh break; } } public override void AddCustomContextEntries( Mobile from, List list ) { if ( from.Alive && IsActiveVendor ) { if ( IsActiveSeller ) list.Add( new VendorBuyEntry( from, this ) ); if ( IsActiveBuyer ) list.Add( new VendorSellEntry( from, this ) ); } base.AddCustomContextEntries( from, list ); } public virtual IShopSellInfo[] GetSellInfo() { return (IShopSellInfo[])m_ArmorSellInfo.ToArray( typeof( IShopSellInfo ) ); } public virtual IBuyItemInfo[] GetBuyInfo() { return (IBuyItemInfo[])m_ArmorBuyInfo.ToArray( typeof( IBuyItemInfo ) ); } } } namespace Server.ContextMenus { public class VendorBuyEntry : ContextMenuEntry { private BaseVendor m_Vendor; public VendorBuyEntry( Mobile from, BaseVendor vendor ) : base( 6103, 8 ) { m_Vendor = vendor; Enabled = vendor.CheckVendorAccess( from ); } public override void OnClick() { m_Vendor.Speak(); m_Vendor.VendorBuy( this.Owner.From ); } } public class VendorSellEntry : ContextMenuEntry { private BaseVendor m_Vendor; public VendorSellEntry( Mobile from, BaseVendor vendor ) : base( 6104, 8 ) { m_Vendor = vendor; Enabled = vendor.CheckVendorAccess( from ); } public override void OnClick() { m_Vendor.VendorSell( this.Owner.From ); } } } namespace Server { public interface IShopSellInfo { //get display name for an item string GetNameFor( Item item ); //get price for an item which the player is selling int GetSellPriceFor( Item item ); //get price for an item which the player is buying int GetBuyPriceFor( Item item ); //can we sell this item to this vendor? bool IsSellable( Item item ); //What do we sell? Type[] Types { get; } //does the vendor resell this item? bool IsResellable( Item item ); } public interface IBuyItemInfo { //get a new instance of an object (we just bought it) IEntity GetEntity(); int ControlSlots { get; } int PriceScalar { get; set; } //display price of the item int Price { get; } //display name of the item string Name { get; } //display hue int Hue { get; } //display id int ItemID { get; } //amount in stock int Amount { get; set; } //max amount in stock int MaxAmount { get; } //Attempt to restock with item, (return true if restock sucessful) bool Restock( Item item, int amount ); //called when its time for the whole shop to restock void OnRestock(); } }