1stMud/CVS/
1stMud/area/CVS/
1stMud/backup/CVS/
1stMud/bin/
1stMud/bin/CVS/
1stMud/bin/extras/
1stMud/bin/extras/CVS/
1stMud/data/CVS/
1stMud/data/i3/CVS/
1stMud/doc/1stMud/
1stMud/doc/1stMud/CVS/
1stMud/doc/CVS/
1stMud/doc/Diku/
1stMud/doc/Diku/CVS/
1stMud/doc/MPDocs/CVS/
1stMud/doc/Merc/CVS/
1stMud/doc/Rom/
1stMud/doc/Rom/CVS/
1stMud/log/CVS/
1stMud/notes/
1stMud/notes/CVS/
1stMud/player/CVS/
1stMud/player/backup/CVS/
1stMud/player/deleted/CVS/
1stMud/src/CVS/
1stMud/src/config/CVS/
1stMud/src/h/CVS/
1stMud/src/o/CVS/
1stMud/win/CVS/
/**************************************************************************
*  Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,        *
*  Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.   *
*                                                                         *
*  Merc Diku Mud improvements copyright (C) 1992, 1993 by Michael         *
*  Chastain, Michael Quan, and Mitchell Tse.                              *
*                                                                         *
*  In order to use any part of this Merc Diku Mud, you must comply with   *
*  both the original Diku license in 'license.doc' as well the Merc       *
*  license in 'license.txt'.  In particular, you may not remove either of *
*  these copyright notices.                                               *
*                                                                         *
*  Much time and thought has gone into this software and you are          *
*  benefiting.  We hope that you share your changes too.  What goes       *
*  around, comes around.                                                  *
***************************************************************************
*       ROM 2.4 is copyright 1993-1998 Russ Taylor                        *
*       ROM has been brought to you by the ROM consortium                 *
*           Russ Taylor (rtaylor@hypercube.org)                           *
*           Gabrielle Taylor (gtaylor@hypercube.org)                      *
*           Brian Moore (zump@rom.org)                                    *
*       By using this code, you have agreed to follow the terms of the    *
*       ROM license, in the file Rom24/doc/rom.license                    *
***************************************************************************
*          1stMud ROM Derivative (c) 2001-2004 by Markanth                *
*            http://www.firstmud.com/  <markanth@firstmud.com>            *
*         By using this code you have agreed to follow the term of        *
*             the 1stMud license in ../doc/1stMud/LICENSE                 *
***************************************************************************/

#include "merc.h"
#include "olc.h"
#include "magic.h"
#include "recycle.h"

AuctionData *
auction_lookup (int num)
{
  AuctionData *pAuc;

  for (pAuc = auction_first; pAuc != NULL; pAuc = pAuc->next)
    {
      if (pAuc->number == num)
	return pAuc;
    }
  return NULL;
}

int last_auc_id;

int
get_auc_id (void)
{
  last_auc_id++;

  if (last_auc_id > 999)
    last_auc_id = 1;

  return last_auc_id;
}

const char *
auc_type (auc_t type, bool fShort)
{
  switch (type)
    {
    default:
    case AUC_GOLD:
      return fShort ? "g" : " gold";
    case AUC_QUEST:
      return fShort ? "qp" : " quest points";
    case AUC_TRIVIA:
      return fShort ? "tp" : " trivia points";
    }
}

const char *
auc_color (auc_t type)
{
  switch (type)
    {
    default:
    case AUC_GOLD:
      return "{Y";
    case AUC_QUEST:
      return "{G";
    case AUC_TRIVIA:
      return "{R";
    }
}

const char *
auc_price (AuctionData * auc, bool fShort)
{
  return FORMATF ("%s%ld%s{x", auc_color (auc->type), auc->bid,
		  auc_type (auc->type, fShort));
}

bool
check_bid (CharData * ch, money_t bid, auc_t type)
{
  if (!ch)
    return true;

  switch (type)
    {
    default:
    case AUC_GOLD:
      return check_worth (ch, bid, VALUE_DEFAULT);
    case AUC_QUEST:
      return bid < (unsigned int) ch->pcdata->quest.points;
    case AUC_TRIVIA:
      return bid < (unsigned int) ch->pcdata->trivia;
    }
}

void
pay_bid (AuctionData * auc)
{
  CharData *bidder = auc->high_bidder;
  CharData *seller = auc->owner;

  switch (auc->type)
    {
    default:
    case AUC_GOLD:
      add_cost (seller, auc->bid * 9 / 10, VALUE_DEFAULT);
      deduct_cost (bidder, auc->bid, VALUE_DEFAULT);
      break;

    case AUC_QUEST:
      seller->pcdata->quest.points += auc->bid;
      bidder->pcdata->quest.points -= auc->bid;
      break;
    case AUC_TRIVIA:
      seller->pcdata->trivia += auc->bid;
      bidder->pcdata->trivia -= auc->bid;
      break;
    }

  if (auc->item->in_room)
    obj_from_room (auc->item);
  else if (auc->item->in_obj)
    obj_from_obj (auc->item);
  else
    obj_from_char (auc->item);

  obj_to_char (auc->item, bidder);

  announce (bidder, INFO_AUCTION | INFO_PRIVATE,
	    "You are sold %s for %s.", auc->item->short_descr,
	    auc_price (auc, false));

  if (auc->type == AUC_GOLD)
    auc->bid = (auc->bid * 9) / 10;

  chprintlnf (seller, "You receive %s for the sale of %s.",
	      auc_price (auc, false), auc->item->short_descr);
}

money_t
bid_incr (auc_t type)
{
  switch (type)
    {
    default:
      return 10;
    case AUC_TRIVIA:
      return 1;
    }
}

Do_Fun (do_auction)
{
  AuctionData *auc;
  ObjData *obj = NULL;
  money_t minbid = 1;
  char arg1[MIL];
  char arg2[MIL];
  char arg3[MIL];
  auc_t type;

  argument = first_arg (argument, arg1, false);
  argument = first_arg (argument, arg2, false);
  first_arg (argument, arg3, false);

  if (ch == NULL)
    return;

  if (NullStr (arg1))
    {
      if (IsSet (ch->info_settings, INFO_AUCTION))
	{
	  RemBit (ch->info_settings, INFO_AUCTION);
	  chprintln (ch, "{YAUCTION{x channel is now ON.");
	}
      else
	{
	  SetBit (ch->info_settings, INFO_AUCTION);
	  chprintln (ch, "{YAUCTION{x channel is now OFF.");
	}
      return;
    }
  else if (!str_cmp (arg1, "stop") && IsImmortal (ch))
    {

      if (NullStr (arg2) || !is_number (arg2))
	{
	  chprintln (ch, "Stop which auction?");
	  return;
	}
      if ((auc = auction_lookup (atoi (arg2))) == NULL)
	{
	  chprintln (ch, "No such auction.");
	  return;
	}
      if (auc->item)
	{
	  announce (ch, INFO_AUCTION,
		    "$n has stopped the auction and confiscated %s!",
		    auc->item->short_descr);
	  announce (ch, INFO_AUCTION | INFO_PRIVATE,
		    "You have stopped the auction and confiscated %s!",
		    auc->item->short_descr);
	  obj_from_char (auc->item);
	  obj_to_char (auc->item, ch);
	}
      reset_auc (auc, true);
      return;
    }

  if ((obj = get_obj_carry (ch, arg1, ch)) == NULL)
    {
      chprintln (ch, "You aren't carrying that item.");
      return;
    }

  if (is_name (arg2, "gold coin"))
    type = AUC_GOLD;
  else if (is_name (arg2, "qp questpoints"))
    type = AUC_QUEST;
  else if (is_name (arg2, "tp triviapoints"))
    type = AUC_TRIVIA;
  else if (IsObjStat (obj, ITEM_QUEST))
    type = AUC_QUEST;
  else
    {
      chprintln
	(ch,
	 "What kind of currency do you want to use? (gold, quest points, or trivia points)");
      return;
    }

  if (IsObjStat (obj, ITEM_AUCTIONED))
    {
      chprintln (ch, "That items is already being auctioned.");
      return;
    }

  if (IsObjStat (obj, ITEM_NODROP) && !IsObjStat (obj, ITEM_QUEST))
    {
      chprintln (ch, "You can't let go of that item.");
      return;
    }

  if (obj->item_type == ITEM_CORPSE_PC || obj->item_type == ITEM_CORPSE_NPC)
    {
      chprintln (ch, "Not a good idea....");
      return;
    }

  if (count_auc (ch) >= 3)
    {
      chprintln (ch, "You can only auction up to 3 items at a time!");
      return;
    }

  if (!NullStr (arg3))
    minbid = atol (arg3);
  else if (IsObjStat (obj, ITEM_QUEST))
    minbid = obj_cost (obj) / 3;

  if (minbid > 1000000 || minbid < 1)
    {
      chprintln (ch,
		 "Minumum bids can't be higher than 1000000 or less than 1.");
      return;
    }

  auc = new_auction ();
  Link (auc, auction, next, prev);
  SetBit (obj->extra_flags, ITEM_AUCTIONED);
  auc->owner = ch;
  auc->item = obj;
  auc->type = type;
  auc->bid = minbid;
  auc->number = get_auc_id ();
  auc->status = AUCTION_LENGTH;

  announce (auc->owner, INFO_AUCTION,
	    "$n is auctioning %s (Level %d, Num %d). Current bid is %s.",
	    auc->item->short_descr, auc->item->level, auc->number,
	    auc_price (auc, false));
  announce (auc->owner, INFO_AUCTION | INFO_PRIVATE,
	    "You are auctioning %s (Level %d, Num %d). Current bid is %ld%s.",
	    auc->item->short_descr, auc->item->level, auc->number,
	    auc_price (auc, false));
  mud_info.stats.auctions++;
  return;
}

void
auction_update (void)
{
  AuctionData *auc, *auc_next;

  if (!auction_first)
    return;

  for (auc = auction_first; auc != NULL; auc = auc_next)
    {
      auc_next = auc->next;

      if (auc->status == 0)
	continue;

      auc->status--;

      if (auc->status < 0)
	auc->status = 0;

      if (!auc->item)
	{
	  reset_auc (auc, true);
	  bugf ("Auction with no item, reseting.");
	  continue;
	}
      else if (auc->status % PULSE_PER_SECOND == 0)
	{
	  switch (auc->status / PULSE_PER_SECOND)
	    {
	    case 0:
	      if (auc->high_bidder == NULL)
		{
		  announce (NULL, INFO_AUCTION,
			    "No bids recieved on %s, sale stopped.",
			    auc->item->short_descr);
		  reset_auc (auc, true);
		}
	      else if (!check_bid (auc->high_bidder, auc->bid, auc->type))
		{
		  announce (auc->high_bidder, INFO_AUCTION,
			    "$n can't cover their stake in the auction, sale stopped.");
		  announce (auc->high_bidder, INFO_AUCTION | INFO_PRIVATE,
			    "You can't cover your stake in the auction, sale stopped.");

		  reset_auc (auc, true);
		}
	      else
		{
		  announce (auc->high_bidder, INFO_AUCTION,
			    "%s SOLD to $n for %s.",
			    auc->item->short_descr, auc_price (auc, false));
		  reset_auc (auc, false);
		  mud_info.stats.aucsold++;
		}
	      break;
	    case 32:
	      announce (NULL, INFO_AUCTION,
			"Going once %s (Level %d, Num %d). Current bid is %s.",
			auc->item->short_descr, auc->item->level,
			auc->number, auc_price (auc, false));
	      break;
	    case 15:
	      announce (NULL, INFO_AUCTION,
			"Going twice %s (Level %d, Num %d). Current bid is %s.",
			auc->item->short_descr, auc->item->level,
			auc->number, auc_price (auc, false));
	      break;
	    }
	}
    }
  return;
}

void
reset_auc (AuctionData * auc, bool forced)
{
  if (auc->item != NULL)
    {
      if (IsObjStat (auc->item, ITEM_AUCTIONED))
	RemBit (auc->item->extra_flags, ITEM_AUCTIONED);
      else
	bug ("item not flagged auction item");

      if (!forced && auc->high_bidder != NULL && auc->bid > 0)
	pay_bid (auc);

      else if (auc->owner != NULL)
	chprintlnf (auc->owner, "Sale of %s has been stopped.",
		    auc->item->short_descr);

    }
  UnLink (auc, auction, next, prev);
  free_auction (auc);
  return;
}

int
count_auc (CharData * ch)
{
  AuctionData *q;
  int count;

  q = auction_first;

  if (!q)
    return 0;

  for (count = 0; q; q = q->next)
    {
      if (q->owner == ch)
	count++;
    }

  return count;
}

money_t
advatoi (const char *s)
{
  const char *sptr;
  money_t number;
  money_t multiplier;

  for (sptr = s, number = 0; *sptr != NUL && isdigit (*sptr); sptr++)
    number = (number * 10) + (*sptr - '0');

  switch (toupper (*sptr))
    {
    case 'K':
      multiplier = 1000;
      number *= multiplier;
      sptr++;
      break;
    case 'M':
      multiplier = 1000 * 1000;
      number *= multiplier;
      sptr++;
      break;
    case '\0':
      multiplier = 0;
      break;
    default:
      return 0;

    }

  while (*sptr != NUL && isdigit (*sptr) && (multiplier > 1))
    {
      multiplier = multiplier / 10;
      number = number + ((*sptr - '0') * multiplier);
      sptr++;
    }

  if (*sptr != '\0' && !isdigit (*sptr))
    return 0;

  return number;
}

money_t
parsebet (const money_t currentbet, const char *argument)
{
  money_t newbet = 0;
  const char *strptr = argument;

  if (*strptr)
    {
      if (isdigit (*strptr))
	newbet = advatoi (strptr);
      else if (*strptr == '+')
	{
	  if (strlen (strptr) == 1)
	    newbet = (currentbet * 125) / 100;
	  else
	    newbet = (currentbet * (100 + atoi (++strptr))) / 100;
	}
      else
	{
	  if ((*strptr == '*') || (*strptr == 'x'))
	    {
	      if (strlen (strptr) == 1)
		newbet = currentbet * 2;
	      else
		newbet = currentbet * atoi (++strptr);
	    }
	}
    }

  return newbet;
}

Do_Fun (do_bid)
{
  char arg1[MIL];
  char arg2[MIL];
  AuctionData *auc, *auc_next;

  argument = one_argument (argument, arg1);
  argument = one_argument (argument, arg2);

  if (auction_first == NULL)
    {
      chprintln (ch, "There's nothing up for auction right now.");
      return;
    }

  if (NullStr (arg1))
    {
      chprintln (ch, stringf (ch, 0, Center, NULL,
			      "{Y[ Auction - Current List of Inventory ]{x"));
      chprintln
	(ch,
	 "{GNum   Seller       Buyer        Item Description                   Lvl   Last Bid    Time{x");
      chprintln (ch,
		 "{W---  ------------ ------------ ----------------------------------- --- ------------- ------------{x");
      for (auc = auction_first; auc; auc = auc_next)
	{
	  auc_next = auc->next;

	  if (!auc->item)
	    {
	      reset_auc (auc, true);
	      continue;
	    }

	  chprintlnf (ch, "{R%3d{x - %-12.12s %-12.12s" MXPTAG ("Bid %d")
		      "%35.35s" MXPTAG ("/Bid") " %3d" " {R%13s{G %s{x",
		      auc->number, GetName (auc->owner),
		      auc->high_bidder ? GetName (auc->high_bidder) : "None",
		      auc->number, stringf (ch, 35, Left, NULL,
					    auc->item->short_descr),
		      auc->item->level, auc_price (auc, true),
		      format_pulse (auc->status));
	}
      chprintln
	(ch,
	 "{W---  ------------ ------------ ----------------------------------- --- ------------- ------------{x");
      chprintlnf (ch,
		  "Type: '%s <num>' to see stats and '%s <num> <amount>' to bid on an item.",
		  n_fun, n_fun);
      return;
    }
  else if ((auc = auction_lookup (atoi (arg1))) != NULL)
    {
      if (!auc->item)
	{
	  reset_auc (auc, true);
	  chprintln (ch, "No such item.");
	  return;
	}

      if (NullStr (arg2))
	{
	  if (ch == auc->owner)
	    chprintlnf (ch, "You're auctioning %s.", auc->item->short_descr);
	  spell_identify (0, ch->level, ch, auc->item, TARGET_OBJ);
	  chprintf (ch, "Current bid is %s", auc_price (auc, false));
	  if (auc->high_bidder)
	    chprintlnf (ch, ", placed by %s.", Pers (auc->high_bidder, ch));
	  else
	    chprintln (ch, ".");
	  return;
	}
      else
	{
	  money_t bid = 0;

	  if (ch == auc->high_bidder)
	    {
	      chprintln (ch, "You already have the highest bid!");
	      return;
	    }

	  if (ch == auc->owner)
	    {
	      chprintln (ch, "You cannot bid on your own items!");
	      return;
	    }

	  bid = parsebet (auction_first->bid, arg2);

	  if (bid < 0 || bid > 200000000)
	    {
	      chprintln (ch, "Invalid bid.");
	      return;
	    }

	  if (!check_bid (ch, bid, auc->type))
	    {
	      chprintln (ch, "You can't cover that bid.");
	      return;
	    }

	  if (bid < auc->bid)
	    {
	      chprintlnf (ch, "The minimum bid is %s.",
			  intstr (auc->bid, auc_type (auc->type, false)));
	      return;
	    }

	  if (bid < (auc->bid + bid_incr (auc->type)))
	    {
	      chprintlnf (ch, "You must outbid %s by at least %ld.",
			  intstr (auc->bid, auc_type (auc->type, false)),
			  bid_incr (auc->type));
	      return;
	    }
	  announce (ch, INFO_AUCTION, "$n has offered %s for %s.",
		    auc_price (auc, false), auc->item->short_descr);
	  announce (ch, INFO_AUCTION | INFO_PRIVATE,
		    "You place a %s bid on %s.", auc_price (auc, false),
		    auc->item->short_descr);
	  auc->high_bidder = ch;
	  auc->bid = bid;
	  auc->status = AUCTION_LENGTH;
	  return;
	}
    }
  else
    chprintln
      (ch, "Bid on what object? (type 'bid', nothing else for a list)");
}

bool
has_auction (CharData * ch)
{
  AuctionData *auc;

  for (auc = auction_first; auc != NULL; auc = auc->next)
    {
      if (auc->owner == ch || auc->high_bidder == ch)
	return true;
    }

  return false;
}

void
extract_auc (CharData * ch)
{
  AuctionData *auc, *auc_next;

  for (auc = auction_first; auc != NULL; auc = auc_next)
    {
      auc_next = auc->next;

      if (auc->owner == ch)
	{
	  reset_auc (auc, true);
	  continue;
	}

      if (auc->high_bidder == ch)
	{
	  reset_auc (auc, true);
	  continue;
	}
    }
}