Fireball

Member
Hi guys,

I wonder if you could please help me with applying NPC guild discounts to vendor items?

I've got this working, but only on the second "vendor buy" command. My WriteLine debugging shows that on the first pass "pm" is null, meaning there is no focus mob yet, and no discount is shown. But on the second pass the discount is given and the priceScalar of 80 is displayed.

What am I doing wrong?

Thanks!

David

Here's the priceScalar method from my BaseVendor.cs

Code:
 public void UpdateBuyInfo()
 {
 int priceScalar = GetPriceScalar();

 PlayerMobile pm = FocusMob as PlayerMobile;

 if (pm == null)
 {
 Console.WriteLine("pm is null");
 return;
 }
 
 if ( (pm.NpcGuild !=null) && (this.NpcGuild !=null) && (pm.NpcGuild == this.NpcGuild ) )
 {
 SayTo( pm, 501528 ); // As thou'rt of my same guild, I shall discount my wares to thee.
 priceScalar = (int)(priceScalar * 0.8);
 Console.WriteLine(priceScalar);
 }

 IBuyItemInfo[] buyinfo = (IBuyItemInfo[])m_ArmorBuyInfo.ToArray( typeof( IBuyItemInfo ) );

 if ( buyinfo != null )
 {
 foreach ( IBuyItemInfo info in buyinfo )
 info.PriceScalar = priceScalar;
 }
 }
 
I might have found the problem. In VendorAI.cs, around line 125, there's this code:

Code:
                else if (e.HasKeyword(0x3C)) // *vendor buy*
                {
                    e.Handled = true;

                    ((BaseVendor)this.m_Mobile).VendorBuy(from);
                    this.m_Mobile.FocusMob = from;
                }

The vendor's focus mob is only set to the player AFTER calling the VendorBuy method. You could try changing it to this:

Code:
                else if (e.HasKeyword(0x3C)) // *vendor buy*
                {
                    e.Handled = true;

                    this.m_Mobile.FocusMob = from;
                    ((BaseVendor)this.m_Mobile).VendorBuy(from);  
                }
 
Thanks I was looking for that for about an hour!

Edit: This is also in WasNamed() for the next person wondering where this is!
 
Last edited:
IMO you shouldn't do this using FocusMob. There are a lot of ways for an NPC's focusmob to change. Let's say 2 players go to buy from the same NPC.

First player, who is a member of the mages guild says "vendor buy" and becomes the FocusMob. Second player who is not a member of the mage's guild says "vendor buy" and the FocusMob has now changed. The first player now clicks "okay" and purchases 999 black pearl which shows in his vendor window as costing 999 * 4gp = 3996 gold. But the vendor will actually charge 999 * 5gp = 4995gp. The reason for this is that the current FocusMob is player 2, who has reset the price scalars on all the buy info back to 1.0.

This also works in reverse, where we change the order of players saying "vendor buy", and now player 2 gets an unexpected discount. Note that when you update the price scalar on the BuyInfo for the product, it's updating the BuyInfo globally.

The methods "void VendorBuy" and "void OnBuyItems" both take an argument of "Mobile buyer" which is never null. You can apply the discounts here without altering the state of the existing BuyInfo and always be sure that you are giving the discount to the correct player.
 
Argh. I thought this would remain just in this purchase instance.

So I moved my code into VendorBuy and although it reports it will take 80 gold for 20 black pearl, it actually takes 100 and says it has taken 100.

I then put the code into OnBuyItems and then it reports full price of 100, but only takes 80 for the 20 black pearl.

So I put the code into BOTH methods and now it seems to be working right!

Is that what you expected? Or have I made a pig's ear of it? If I buy with a toon in the guild and at the same time buy with a toon not in the guild then they do both seem to pay the right price.

Thanks

David

PS thanks Jack for your continued help, not just for me but for all of us struggling to provide some fun to UO lovers. You put so much back into the community. It is very much appreciated and I hope one day that I am as knowledgeable.
 
Correct, it needs to be in both methods. One is called when the player originally requests the buy window, the other is when the player completes the purchase. The correct price needs to be calculated both times.

One other thing I noticed but didn't mention before is this:

Code:
pm.NpcGuild!=null

NpcGuild is an enum and can not be null. You should check the guild is not NpcGuild.None, rather than null. That means this code is always true:

Code:
(pm.NpcGuild!=null)&&(this.NpcGuild!=null)

And hence if both the player and NPC are part of NpcGuild.None, the player will receive a discount.
 
Last edited:
@Fireball would you mind showing me a diff of the change you ended up with? Mine is complaining about the priceScaler and NpcGuild not being defined for Server.Mobile. Little too new at C# and ServUO in general to figure out the best way to resolve those issues.
 
@Fireball would you mind showing me a diff of the change you ended up with? Mine is complaining about the priceScaler and NpcGuild not being defined for Server.Mobile. Little too new at C# and ServUO in general to figure out the best way to resolve those issues.

I am using RunUO. Are you using ServUO? Might be different.

In BaseVendor.cs find the VendorBuy method. You are basically replacing the UpdateBuyInfo() line, so you will see I commented that out and added the new method right there above it.

I'm using 15% instead of the 10% that I think it used to be, because it is integered and so 5 gp = 4.5 which gets rounded right back up to 5 again. I wanted to ensure people actually got some discount so I just fixed it that way because it was easiest!

Code:
 public virtual void VendorBuy( Mobile from )
 {
 if ( !IsActiveSeller )
 return;

 if ( !from.CheckAlive() )
 return;

 if ( !CheckVendorAccess( from ) )
 {
 Say( 501522 ); // I shall not treat with scum like thee!
 return;
 }

 if ( DateTime.UtcNow - m_LastRestock > RestockDelay )
 Restock();

// David: Apply 15% discount to NPC guild members
 int priceScalar = GetPriceScalar();
 PlayerMobile pm = from as PlayerMobile;

 if ( (pm.NpcGuild != NpcGuild.None ) && (this.NpcGuild != NpcGuild.None ) && (pm.NpcGuild == this.NpcGuild ) )
 {
 SayTo( pm, 501528 ); // As thou'rt of my same guild, I shall discount my wares to thee.
 priceScalar = (int)(priceScalar * 0.85);
 }

 IBuyItemInfo[] buyinfo = (IBuyItemInfo[])m_ArmorBuyInfo.ToArray( typeof( IBuyItemInfo ) );

 if ( buyinfo != null )
 {
 foreach ( IBuyItemInfo info in buyinfo )
 info.PriceScalar = priceScalar;
 }

// UpdateBuyInfo();

The rest of the VendorBuy method is the same.

And here is the almost identical code for the OnBuyItems method:

Code:
 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 treat with scum like thee!
 return false;
 }

// David: Apply 15% discount to NPC guild members
 int priceScalar = GetPriceScalar();
 PlayerMobile pm = buyer as PlayerMobile;

 if ( ( pm.NpcGuild != NpcGuild.None ) && ( this.NpcGuild != NpcGuild.None ) && ( pm.NpcGuild == this.NpcGuild ) )
 {
 SayTo( pm, 501528 ); // As thou'rt of my same guild, I shall discount my wares to thee.
 priceScalar = (int)(priceScalar * 0.85);
 }

 IBuyItemInfo[] xbuyinfo = (IBuyItemInfo[])m_ArmorBuyInfo.ToArray( typeof( IBuyItemInfo ) );

 if ( xbuyinfo != null )
 {
 foreach ( IBuyItemInfo xinfo in xbuyinfo )
 xinfo.PriceScalar = priceScalar;
 }

// UpdateBuyInfo();

again the rest of the code is unchanged.

Good luck!
David
 

Active Shards

Donations

Total amount
$0.00
Goal
$1,000.00
Back