1375 lines
No EOL
35 KiB
C#
1375 lines
No EOL
35 KiB
C#
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<SBInfo> 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<BuyItemState> list;
|
||
IBuyItemInfo[] buyInfo = this.GetBuyInfo();
|
||
IShopSellInfo[] sellInfo = this.GetSellInfo();
|
||
|
||
list = new List<BuyItemState>( buyInfo.Length );
|
||
Container cont = this.BuyPack;
|
||
|
||
List<ObjectPropertyList> 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<ObjectPropertyList>();
|
||
}
|
||
|
||
if ( disp is Item ) {
|
||
opls.Add( ( ( Item ) disp ).PropertyList );
|
||
} else if ( disp is Mobile ) {
|
||
opls.Add( ( ( Mobile ) disp ).PropertyList );
|
||
}
|
||
}
|
||
|
||
List<Item> 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<ObjectPropertyList>();
|
||
}
|
||
|
||
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<BuyItemResponse> 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<BuyItemResponse> 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<BuyItemResponse> validBuy = new List<BuyItemResponse>( 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<SellItemResponse> 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<SBInfo> sbInfos = this.SBInfos;
|
||
|
||
for ( int i = 0; sbInfos != null && i < sbInfos.Count; ++i )
|
||
{
|
||
SBInfo sbInfo = sbInfos[i];
|
||
List<GenericBuyInfo> 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<SBInfo> 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<GenericBuyInfo> 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<ContextMenuEntry> 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();
|
||
}
|
||
} |