SmaugWizard/Backup/
SmaugWizard/Backup/L/
SmaugWizard/Boards/
SmaugWizard/Building/
SmaugWizard/Corpses/
SmaugWizard/Councils/
SmaugWizard/Deity/
SmaugWizard/Gods/
SmaugWizard/MudProgs/
SmaugWizard/Player/L/
SmaugWizard/Src/
SmaugWizard/Src/res/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |				*
 * -----------------------------------------------------------|   \\._.//	*
 * SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version)   |  (0...0)	*
 * -----------------------------------------------------------|    ).:.(	*
 * SMAUG (C) 1994, 1995, 1996 by Derek Snider                 |    {o o}	*
 * -----------------------------------------------------------|   / ' ' \	*
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |~'~.VxvxV.~'~*
 * Scryn, Swordbearer, Rennard, Tricops, and Gorog.           |				*
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik Staerfeldt, Tom Madsen, and Katja Nyboe.    *
 ****************************************************************************/
// Auction.cpp

#include	"stdafx.h"
#include	"smaug.h"
#include	"sysdata.h"
#include	"area.h"
#include	"objects.h"
#include	"rooms.h"
#include	"skill.h"
#include	"auction.h"
#include	"bet.h"
#include	"character.h"


// put an item on auction, or see the stats on the current item or bid
void do_auction (CCharacter *ch, char *argument)
{
	CObjData	*obj;
	char		arg1 [MAX_INPUT_LENGTH];
	char		arg2 [MAX_INPUT_LENGTH];
	char		buf [MAX_STRING_LENGTH];

	argument = one_argument (argument, arg1);

	// NPC can be extracted at any time and thus can't auction!
	if (ch->IsNpc ())
		return;

	if ((time_info.hour > 18 || time_info.hour < 9) && ! auction->IsActive ()) {
		set_char_color (AT_LBLUE, ch);
		ch->SendText ("\n\rThe auctioneer has retired for the evening...\n\r");
		return;
	}

	if (arg1 [0] == '\0') {
		if (auction->IsActive ()) {
			obj = auction->GetItem ();

			// show item data here
			if (auction->GetBid () > 0)
				sprintf (buf, "Current bid on this item is %d gold.\n\r",
					auction->GetBid ());
			else
				sprintf (buf, "No bids on this item have been received.\n\r");
			set_char_color (AT_BLUE, ch);
			ch->SendText (buf);

			sprintf (buf,
				"Object '%s' is %s, special properties:%s%s%s.\n\r"
				"Its weight is %d, value is %d, and level is %d.\n\r",
				obj->GetName (), aoran (item_type_name (obj)),
				NCCP obj->AntiClassNames (),
				ExtraBitName (obj->m_ExtraFlags),
				magic_bit_name (obj->magic_flags),
				obj->weight, obj->cost, obj->level);
			set_char_color (AT_LBLUE, ch);
			ch->SendText (buf);

			sprintf (buf, "Worn on: %s\n\r", 
				flag_string (obj->wear_flags -1, w_flags));
			ch->SendText (buf);

			set_char_color (AT_BLUE, ch);

			switch (obj->item_type) {
			  case ITEM_PILL:
			  case ITEM_SCROLL:
			  case ITEM_POTION:
				sprintf (buf, "Level %d spells of:", obj->value [0]);
				ch->SendText (buf);

				if (obj->value [1] >= 0
				  && obj->value [1] < SkillTable.GetCount ()) {
					ch->SendText (" '");
					ch->SendText (SkillTable.GetName (obj->value [1]));
					ch->SendText ("'");
				}

				if (obj->value [2] >= 0
				  && obj->value [2] < SkillTable.GetCount ()) {
					ch->SendText (" '");
					ch->SendText (SkillTable.GetName (obj->value [2]));
					ch->SendText ("'");
				}

				if (obj->value [3] >= 0
				  && obj->value [3] < SkillTable.GetCount ()) {
					ch->SendText (" '");
					ch->SendText (SkillTable.GetName (obj->value [3]));
					ch->SendText ("'");
				}

				ch->SendText (".\n\r");
				break;

			  case ITEM_WAND:
			  case ITEM_STAFF:
				sprintf (buf, "Has %d (%d) charges of level %d",
					obj->value [1], obj->value [2], obj->value [0]);
				ch->SendText (buf);

				if (obj->value [3] >= 0
				  && obj->value [3] < SkillTable.GetCount ()) {
					ch->SendText (" '");
					ch->SendText (SkillTable.GetName (obj->value [3]));
					ch->SendText ("'");
				}

				ch->SendText (".\n\r");
				break;

			  case ITEM_WEAPON:
				sprintf (buf, "Damage is %d to %d (average %d).\n\r",
					obj->value [1], obj->value [2],
					(obj->value [1] + obj->value [2]) / 2);   
				ch->SendText (buf);
				break;

			  case ITEM_ARMOR:
				sprintf (buf, "Armor class is %d.\n\r", obj->value [0]);
				ch->SendText (buf);
				break;
			}

			CAffectList		&IList = obj->pIndexData->AffList;
			POSITION		apos = IList.GetHeadPosition ();
			while (apos)
				IList.GetNext (apos)->ShowAffect (ch);

			CAffectList		&AList = obj->AffList;
			apos = AList.GetHeadPosition ();
			while (apos)
				AList.GetNext (apos)->ShowAffect (ch);

			if (obj->item_type == ITEM_CONTAINER && ! obj->IsEmpty ()) {
				set_char_color (AT_OBJECT, ch);
				ch->SendText ("Contents:\n\r");
				ShowListToChar (obj->GetContentList (), ch, TRUE, FALSE);
			}

			if (ch->IsImmortal ()) {
				sprintf (buf, "Seller: %s.  Bidder: %s.  Round: %d.\n\r",
					auction->GetSeller ()->GetName (),
					auction->GetBuyer ()->GetName (),
					(auction->GetCount () + 1));
				ch->SendText (buf);
				sprintf (buf, "Time left in round: %d.\n\r",
					auction->GetPulse ());
				ch->SendText (buf);
			}
			return;
		} else {
			set_char_color (AT_LBLUE, ch);
			ch->SendText ("\n\rThere is nothing being auctioned right now.  "
				"What would you like to auction?\n\r");
			return;
		}
	}


	CCharacter	&Seller = *auction->GetSeller ();
	CCharacter	&Buyer = *auction->GetBuyer ();

	if (ch->IsImmortal () && !str_cmp (arg1, "stop"))
		if (! auction->IsActive ()) {
			ch->SendText ("There is no auction to stop.\n\r");
			return;
		} else {		// stop the auction
			set_char_color (AT_LBLUE, ch);
			sprintf (buf, "Sale of %s has been stopped by an Immortal.",
				auction->GetItem ()->GetShortDescr ());
			talk_auction (buf);
			obj_to_char (auction->GetItem (), &Seller);
			if (SysData.IsSaveOnAuction ())
				save_char_obj (&Seller);
			auction->Stop ();
			return;
		}

	if (!str_cmp (arg1, "bid"))
		if (auction->IsActive ()) {
			int		newbet;

			if (ch == &Seller) {
				ch->SendText ("You can't bid on your own item!\n\r");
				return;
			}

			// make - perhaps - a bid now
			if (argument [0] == '\0') {
				ch->SendText ("Bid how much?\n\r");
				return;
			}

			newbet = parsebet (auction->GetBid (), argument);
			//ch_printf (ch, "Bid: %d\n\r", newbet);

			if (newbet < auction->GetStartPrice ()) {
				ch->SendText ("You must place a bid that is higher than "
					"the starting price.\n\r");
				return;
			}

			// to avoid slow auction, use a bigger amount than 100
			// if the bet is higher up.
			// changed to 10000 for our high economy.
			if (newbet < (auction->GetBid () + 10000)) {
				ch->SendText ("You must at least bid 10000 coins over "
					"the current bid.\n\r");
				return;
			}

			if (newbet > ch->GetGold ()) {
				ch->SendText ("You don't have that much money!\n\r");
				return;
			}

			if (newbet > 2000000000) {
				ch->SendText ("You can't bid over 2 billion coins.\n\r");
				return;
			}

			// the actual bet is OK!
			// return the gold to the last buyer, if one exists
			if (auction->IsBuyer () && &Buyer != &Seller)
				Buyer.AddGold (auction->GetBid ());

			ch->AddGold (-newbet);	// subtract the gold - important :)
			if (SysData.IsSaveOnAuction ())
				save_char_obj (ch);

			auction->Reset (ch, newbet);	// start the auction over again

			sprintf (buf, "A bid of %d gold has been received on %s.\n\r",
				newbet, auction->GetItem ()->GetShortDescr ());
			talk_auction (buf);
			return;
		} else {
			ch->SendText ("There isn't anything being auctioned right now.\n\r");
			return;
		}

	// finally...
	if (ms_find_obj (ch))
		return;

	obj = get_obj_carry (ch, arg1);		// does char have the item ?

	if (obj == NULL) {
		ch->SendText ("You aren't carrying that.\n\r");
		return;
	}

	if (obj->timer > 0) {
		ch->SendText ("You can't auction objects that are decaying.\n\r");
		return;
	}

	argument = one_argument (argument, arg2);

	if (arg2 [0] == '\0') {
		auction->SetStartPrice (0);
		strcpy (arg2, "0");
	}

	if (!is_number (arg2)) {
		ch->SendText ("You must input a number at which to start the "
			"auction.\n\r");
		return;
	}

	if (atoi (arg2) < 0) {
		ch->SendText ("You can't auction something for less than 0 gold!\n\r");
		return;
	}

	if (! auction->IsActive ())
		switch (obj->item_type) {

		  default:
			act (AT_TELL, "You cannot auction $Ts.", ch, NULL,
				item_type_name (obj), TO_CHAR);
			return;

			// insert any more item types here...
			// items with a timer MAY NOT BE AUCTIONED! 
		  case ITEM_LIGHT:
		  case ITEM_TREASURE:    
		  case ITEM_POTION:
		  case ITEM_CONTAINER:
		  case ITEM_DRINK_CON:
		  case ITEM_FOOD:
		  case ITEM_PEN:
		  case ITEM_BOAT:
		  case ITEM_PILL:
		  case ITEM_PIPE:
		  case ITEM_HERB_CON:
		  case ITEM_INCENSE:
		  case ITEM_FIRE:
		  case ITEM_RUNEPOUCH:
		  case ITEM_MAP:
		  case ITEM_BOOK:
		  case ITEM_RUNE:
		  case ITEM_MATCH:
		  case ITEM_HERB:
		  case ITEM_WEAPON:
		  case ITEM_ARMOR:
		  case ITEM_STAFF:
		  case ITEM_WAND:
		  case ITEM_SCROLL:
			separate_obj (obj);
			obj_from_char (obj);
			if (SysData.IsSaveOnAuction ())
				save_char_obj (ch);
			auction->SetItem (obj);
			auction->SetSeller (ch);
			auction->Reset (ch, 0);

			int		Start = atoi (arg2);
			auction->SetStartPrice (Start);
			if (Start > 0)
				auction->SetBid (Start);

			sprintf (buf, "A new item is being auctioned: %s at %d gold.",
				obj->GetShortDescr (), Start);
			talk_auction (buf);

			return;

		} // switch
		else {
			act (AT_TELL,
				"Try again later - $p is being auctioned right now!",
				ch, auction->GetItem (), NULL, TO_CHAR);
			WAIT_STATE (ch, (short) (1.5 * PULSE_VIOLENCE));
			return;
		}
}


// the auction update
void CAuctionData::Update ()
{
	int		tax, pay;
	char	buf [MAX_STRING_LENGTH];

	if (! m_pItem) then return;
	if (--m_Pulse > 0) then return;

	m_Pulse = PULSE_AUCTION;

	switch (++m_Going) {		// increase the going state
	  case 1 :	// going once
	  case 2 :	// going twice
		if (m_Bid > m_Start)
			sprintf (buf, "%s: going %s for %d.", m_pItem->GetShortDescr (),
				((m_Going == 1) ? "once" : "twice"), m_Bid);
		else
			sprintf (buf, "%s: going %s (bid not received yet).",
				m_pItem->GetShortDescr (), ((m_Going == 1) ? "once" : "twice"));

		talk_auction (buf);
		break;

	  case 3 : /* SOLD! */
		if (! m_pBuyer && m_Bid) {
			bug ("Auction code reached SOLD, with NULL buyer, but %d gold "
				"bid", m_Bid);
			m_Bid = 0;
		}
		if (m_Bid > 0 && m_pBuyer != m_pSeller) {
			sprintf (buf, "%s sold to %s for %d.", m_pItem->GetShortDescr (),
				m_pBuyer->IsNpc () ? m_pBuyer->GetShortDescr () :
				m_pBuyer->GetName (), m_Bid);
			talk_auction (buf);

			act (AT_ACTION, "The auctioneer materializes before you, and "
				"hands you $p.", m_pBuyer, m_pItem, NULL, TO_CHAR);
			act (AT_ACTION, "The auctioneer materializes before $n, and "
				"hands $m $p.", m_pBuyer, m_pItem, NULL, TO_ROOM);

			if ((m_pBuyer->GetCarryWeight () + get_obj_weight (m_pItem))
			  > can_carry_w (m_pBuyer)) {
				act (AT_PLAIN, "$p is too heavy for you to carry with your "
					"current inventory.", m_pBuyer, m_pItem, NULL, TO_CHAR);
				act (AT_PLAIN, "$n is carrying too much to also carry $p, "
					"and $e drops it.", m_pBuyer, m_pItem, NULL, TO_ROOM);
				obj_to_room (m_pItem, m_pBuyer->GetInRoom ());
			}
			else
				obj_to_char (m_pItem, m_pBuyer);

			pay = (int) (m_Bid * 0.9);
			tax = (int) (m_Bid * 0.1);
			m_pSeller->GetInRoom ()->GetArea ()->BoostEconomy (tax);
			m_pSeller->AddGold (pay);	// give him the money, tax 10 %
			sprintf (buf, "The auctioneer pays you %d gold, charging an "
				"auction fee of %d.\n\r", pay, tax);
			m_pSeller->SendText (buf);
			m_pItem = NULL;							// reset item
			if (SysData.IsSaveOnAuction ()) {
				save_char_obj (m_pBuyer);
				save_char_obj (m_pSeller);
			}
		} else {	// not sold
			sprintf (buf, "No bids received for %s - object has been "
				"removed from auction\n\r.", m_pItem->GetShortDescr ());
			talk_auction (buf);
			act (AT_ACTION, "The auctioneer appears before you to "
				"return $p to you.", m_pSeller, m_pItem, NULL, TO_CHAR);
			act (AT_ACTION, "The auctioneer appears before $n to "
				"return $p to $m.", m_pSeller, m_pItem, NULL, TO_ROOM);
			if ((m_pSeller->GetCarryWeight () + get_obj_weight (m_pItem))
			  > can_carry_w (m_pSeller)) {
				act (AT_PLAIN, "You drop $p as it is just too much to carry"
					" with everything else you're carrying.", m_pSeller,
					m_pItem, NULL, TO_CHAR);
				act (AT_PLAIN, "$n drops $p as it is too much extra weight"
					" for $m with everything else.", m_pSeller,
					m_pItem, NULL, TO_ROOM);
				obj_to_room (m_pItem, m_pSeller->GetInRoom ());
			}
			else
				obj_to_char (m_pItem, m_pSeller);

			tax = (int) (m_pItem->cost * 0.05);
			m_pSeller->GetInRoom ()->GetArea ()->BoostEconomy (tax);
			sprintf (buf, "The auctioneer charges you an auction fee of "
				"%d.\n\r", tax);
			m_pSeller->SendText (buf);
			if ((m_pSeller->GetGold () - tax) < 0)
				m_pSeller->SetGold (0);
			else
				m_pSeller->AddGold (-tax);
			if (SysData.IsSaveOnAuction ())
				save_char_obj (m_pSeller);
		} // else
		m_pItem = NULL;		// clear auction
	} // switch
}


void CAuctionData::Stop ()
{
	if (IsBuyer () && m_pBuyer != m_pSeller) {
		m_pBuyer->AddGold (m_Bid);
		m_pBuyer->SendText ("Your money has been returned.\n\r");
	}
	m_pItem = NULL;
}