deltamud/deltamud/
deltamud/deltamud/bin/
deltamud/deltamud/cnf/
deltamud/deltamud/lib/
deltamud/deltamud/lib/etc/
deltamud/deltamud/lib/misc/
deltamud/deltamud/lib/plrobjs/
deltamud/deltamud/lib/text/
deltamud/deltamud/lib/text/help/
deltamud/deltamud/lib/world/
deltamud/deltamud/lib/world/trg/
#include "conf.h"
#include "sysdep.h"

#include "structs.h"
#include "utils.h"
#include "comm.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "screen.h"

#include "auction.h"

extern struct descriptor_data *descriptor_list;
extern struct char_data *character_list;
extern struct room_data *world;

/* The storage struct itself */
struct auction_data auction;

/*
 * auction_output : takes two strings and dispenses them to everyone connected
 *              based on if they have color on or not.  Note that the buf's are
 *              commonly used *color and *black so I allocate my own buffer.
 */
void
auction_output (char *color, char *black)
{
  char buffer[MAX_STRING_LENGTH];
  struct descriptor_data *d;

  if (!auction.auctioneer)
    auction.auctioneer = str_dup (DEFAULT_AUCTIONEER);

  for (d = descriptor_list; d; d = d->next)
    if (!d->connected && d->character &&
	!PLR_FLAGGED (d->character, PLR_WRITING) &&
	!PRF_FLAGGED (d->character, PRF_NOAUCT) &&
	!ROOM_FLAGGED (d->character->in_room, ROOM_SOUNDPROOF))
      {
	sprintf (buffer, "&c[&YAUCTION&c]&n %s%s%s: '%s%s%s'%s\r\n",
		 CCMAG (d->character, C_NRM), auction.auctioneer,
		 CCCYN (d->character, C_NRM), CCNRM (d->character, C_NRM),
		 (COLOR_LEV (d->character) > C_NRM) ? color : black,
		 CCMAG (d->character, C_NRM), CCNRM (d->character, C_NRM));
	send_to_char (buffer, d->character);
      }
}

void
auction_update (void)
{
  if (auction.ticks == AUC_NONE)	/* No auction */
    return;

  /* Seller left! */
  if (!get_ch_by_id_desc (auction.seller) && !get_ch_by_id (auction.seller))
    {
      if (auction.obj)
	extract_obj (auction.obj);
      auction_reset ();
      return;
    }

  /* If there is an auction but it's not sold yet */
  if (auction.ticks >= AUC_BID && auction.ticks <= AUC_SOLD)
    {
      struct char_data *bidder = get_ch_by_id (auction.bidder);
      struct char_data *seller = get_ch_by_id (auction.seller);
      /* If there is a bidder and it's not sold yet */
      if (bidder && (auction.ticks < AUC_SOLD))
	{
	  /* Non colored message */
	  sprintf (buf, "%s is going %s%s%s to %s for %ld coin%s.",
		   auction.obj->short_description,
		   auction.ticks == AUC_BID ? "once" : "",
		   auction.ticks == AUC_ONCE ? "twice" : "",
		   auction.ticks == AUC_TWICE ? "for the last call" : "",
	      GET_NAME (bidder), auction.bid, auction.bid != 1 ? "s" : " ");
	  /* Colored message */
	  sprintf (buf2, "\x1B[1;37m%s\x1B[35m is going \x1B[1;37m%s%s%s\x1B[35m to \x1B[1;37m%s\x1B[35m for \x1B[1;37m%ld\x1B[35m coin%s.",
		   auction.obj->short_description,
		   auction.ticks == AUC_BID ? "once" : "",
		   auction.ticks == AUC_ONCE ? "twice" : "",
		   auction.ticks == AUC_TWICE ? "for the last call" : "",
	      GET_NAME (bidder), auction.bid, auction.bid != 1 ? "s" : " ");
	  /* send the output */
	  auction_output (buf2, buf);
	  /* Increment timer */
	  auction.ticks++;
	  return;
	}

      /* If there is no bidder and we ARE in the sold state */
      if (!bidder && (auction.ticks == AUC_SOLD))
	{
	  /* Colored message */
	  sprintf (buf2, "\x1B[1;37m%s\x1B[35m is \x1B[1;37mSOLD\x1B[35m to \x1B[1;37mno one\x1B[35m for \x1B[1;37m%ld\x1B[35m coin%s.",
		   auction.obj->short_description,
		   auction.bid, auction.bid != 1 ? "s" : " ");
	  /* No color message */
	  sprintf (buf, "%s is SOLD to no one for %ld coin%s.",
		   auction.obj->short_description,
		   auction.bid,
		   auction.bid != 1 ? "s" : " ");
	  /* Send the output away */
	  auction_output (buf2, buf);
	  /* Give the poor fellow his unsold goods back */
	  if (seller)
	    obj_to_char (auction.obj, seller);
	  /* He's not around to get it back, destroy the object */
	  else
	    extract_obj (auction.obj);
	  /* Reset the auction for next time */
	  auction_reset ();
	  return;
	}

      /* If there is no bidder and we are not in the sold state */
      if (!bidder && (auction.ticks < AUC_SOLD))
	{
	  /* Colored output message */
	  sprintf (buf2, "\x1B[1;37m%s\x1B[35m is going \x1B[1;37m%s%s%s\x1B[35m to \x1B[1;37mno one\x1B[35m for \x1B[1;37m%ld\x1B[35m coin%s.",
		   auction.obj->short_description,
		   auction.ticks == AUC_BID ? "once" : "",
		   auction.ticks == AUC_ONCE ? "twice" : "",
		   auction.ticks == AUC_TWICE ? "for the last call" : "",
		   auction.bid, auction.bid != 1 ? "s" : "");
	  /* No color output message */
	  sprintf (buf, "%s is going %s%s%s to no one for %ld coin%s.",
		   auction.obj->short_description,
		   auction.ticks == AUC_BID ? "once" : "",
		   auction.ticks == AUC_ONCE ? "twice" : "",
		   auction.ticks == AUC_TWICE ? "for the last call" : "",
		   auction.bid, auction.bid != 1 ? "s" : "");
	  /* Send output away */
	  auction_output (buf2, buf);
	  /* Increment timer */
	  auction.ticks++;
	  return;
	}

      /* Sold */
      if (bidder && (auction.ticks >= AUC_SOLD))
	{
	  /* Colored output */
	  sprintf (buf2, "\x1B[1;37m%s\x1B[35m is \x1B[1;37mSOLD\x1B[35m to \x1B[1;37m%s\x1B[35m for \x1B[1;37m%ld\x1B[35m coin%s.",
		   auction.obj->short_description ? auction.obj->short_description : "something",
		   bidder->player.name ? bidder->player.name : "someone",
		   auction.bid, auction.bid != 1 ? "s" : "");
	  /* Non color output */
	  sprintf (buf, "%s is SOLD to %s for %ld coin%s.",
		   auction.obj->short_description ? auction.obj->short_description : "something",
		   bidder->player.name ? bidder->player.name : "someone",
		   auction.bid, auction.bid != 1 ? "s" : "");
	  /* Send the output */
	  auction_output (buf2, buf);

	  /* If the seller is still around we give him the money */
	  if (seller)
	    {

	      sprintf (buf, "[WATCHDOG] %s auctions %s to %s for %ld coin%s.",
		       seller->player.name ? seller->player.name : "someone",
		       auction.obj->short_description ? 
		       auction.obj->short_description : "something",
		       bidder->player.name ? bidder->player.name : "someone",
		       auction.bid, auction.bid != 1 ? "s" : "");
	      if (GET_LEVEL(bidder) >= LVL_IMMORT 
		  || GET_LEVEL(seller) >= LVL_IMMORT)
		mudlog(buf, CMP, LVL_IMPL, TRUE);
	      GET_GOLD (seller) += auction.bid;
	      act ("Congrats! You have sold $p!", 
		   FALSE, seller, auction.obj, 0, TO_CHAR);
	    }
	  /* If the bidder is here he gets the object */
	  if (bidder)
	    {
	      obj_to_char (auction.obj, bidder);
	      act ("Congrats! You now have $p!", FALSE, bidder, auction.obj, 0, TO_CHAR);
	    }
	  /* Restore the status of the auction */
	  auction_reset ();
	  return;
	}
    }
  return;
}

/*
 * do_bid : user interface to place a bid.
 */
ACMD (do_bid)
{
  long bid;

  /* NPC's can not bid or auction due to lack of idnum */
  if (IS_NPC (ch))
    {
      send_to_char ("You aren't unique enough to bid.\r\n", ch);
      return;
    }

  /* There isn't an auction */
  if (auction.ticks == AUC_NONE)
    {
      send_to_char ("Nothing is up for sale.\r\n", ch);
      return;
    }

  if PRF_FLAGGED (ch, PRF_NOAUCT){
    send_to_char ("You can't bid when you're not on the channel\r\n", ch);
    return;
  }

  one_argument (argument, buf);
  bid = atoi (buf);

  /* They didn't type anything else */
  if (!*buf)
    {
      sprintf (buf2, "Current bid: %ld coin%s\r\n", auction.bid,
	       auction.bid != 1 ? "s." : ".");
      send_to_char (buf2, ch);
      /* The current bidder is this person */
    }
  else if (ch == get_ch_by_id_desc (auction.bidder))
    send_to_char ("You're trying to outbid yourself.\r\n", ch);
  /* The seller is the person who tried to bid */
  else if (ch == get_ch_by_id_desc (auction.seller))
    send_to_char ("You can't bid on your own item.\r\n", ch);
  /* Tried to auction below the minimum */
  else if ((bid < auction.bid) && auction.bidder < 0)
    {
      sprintf (buf2, "The minimum is currently %ld coins.\r\n", auction.bid);
      send_to_char (buf2, ch);
      /* Tried to bid below the minimum where there is a bid, 5% increases */
    }
  else if ((bid < (auction.bid * 1.05) && auction.bidder >= 0) || bid == 0)
    {
      sprintf (buf2, "Try bidding at least 5%% over the current bid of %ld. (%.0f coins).\r\n",
	       auction.bid, auction.bid * 1.05 + 1);
      send_to_char (buf2, ch);
      /* Not enough gold on hand! */
    }
  else if (GET_GOLD (ch) < bid)
    {
      sprintf (buf2, "You have only %d coins on hand.\r\n", GET_GOLD (ch));
      send_to_char (buf2, ch);
      /* it's an ok bid */
    }
  else
    {
      /* Give last bidder money back if he's around! */
      if (auction.bidder >= 0 && get_ch_by_id (auction.bidder))
	GET_GOLD (get_ch_by_id (auction.bidder)) += auction.bid;
      /* This is the bid value */
      auction.bid = bid;
      /* The bidder is this guy */
      auction.bidder = GET_IDNUM (ch);
      /* This resets the auction to first chance bid */
      auction.ticks = AUC_BID;
      /* Get money from new bidder. */
      GET_GOLD (ch) -= auction.bid;
      /* Prepare colored message */
      sprintf (buf2, "\x1B[1;37m%s\x1B[35m bids \x1B[1;37m%ld\x1B[35m coin%s on \x1B[1;37m%s\x1B[35m.",
	       GET_NAME (ch), auction.bid, auction.bid != 1 ? "s" : "",
	       auction.obj->short_description);
      /* Prepare non-colored message */
      sprintf (buf, "%s bids %ld coin%s on %s.",
	       GET_NAME (ch), auction.bid, auction.bid != 1 ? "s" : "",
	       auction.obj->short_description);
      /* Send the output away */
      auction_output (buf2, buf);
    }
}

/*
 * do_auction : user interface for placing an item up for sale
 */
ACMD (do_auction)
{
  struct obj_data *obj;
  struct char_data *seller;

  /* NPC's can not bid or auction due to lack of idnum */
  if (IS_NPC (ch))
    {
      send_to_char ("You're not unique enough to auction.\r\n", ch);
      return;
    }

  two_arguments (argument, buf1, buf2);

  if PRF_FLAGGED (ch, PRF_NOAUCT){
    send_to_char ("You can't auction when you're not on the channel\r\n", ch);
    return;
  }

  /* There is nothing they typed */
  if (!*buf1)
    send_to_char ("Auction what for what minimum?\r\n", ch);
  /* Hrm...logic error? */
  else if (auction.ticks != AUC_NONE)
    {
      /* If seller is no longer present, auction continues with no seller */
      if ((seller = get_ch_by_id (auction.seller)))
	sprintf (buf2, "%s is currently auctioning %s for %ld coins.\r\n",
	    GET_NAME (seller), auction.obj->short_description, auction.bid);
      else
	sprintf (buf2, "No one is currently auctioning %s for %ld coins.\r\n",
		 auction.obj->short_description, auction.bid);
      send_to_char (buf2, ch);
      /* Person doesn't have that item */
    }
  else if ((obj = get_obj_in_list_vis (ch, buf1, ch->carrying)) == NULL)
    send_to_char ("You don't seem to have that to sell.\r\n", ch);
  /* Can not auction corpses because they may decompose */
  else if ((GET_OBJ_TYPE (obj) == ITEM_CONTAINER) && (GET_OBJ_VAL (obj, 3)))
    send_to_char ("You can not auction corpses.\n\r", ch);
  /* It's valid */
  else
    {
      /* First bid attempt */
      auction.ticks = AUC_BID;
      /* This guy is selling it */
      auction.seller = GET_IDNUM (ch);
      /* Can not make the minimum less than 0 --KR */
      auction.bid = (atoi (buf2) > 0 ? atoi (buf2) : 1);
      /* Pointer to object */
      auction.obj = obj;
      /* Get the object from the character, so they cannot drop it! */
      obj_from_char (auction.obj);
      /* Prepare color message for those with it */
      sprintf (buf2, "\x1B[1;37m%s\x1B[35m puts \x1B[1;37m%s\x1B[35m up for sale, minimum bid \x1B[1;37m%ld\x1B[35m coin%s",
	       GET_NAME (ch), auction.obj->short_description, auction.bid,
	       auction.bid != 1 ? "s." : ".");
      /* Make a message sans-color for those whole have it off */
      sprintf (buf, "%s puts %s up for sale, minimum bid %ld coin%s",
	       GET_NAME (ch), auction.obj->short_description, auction.bid,
	       auction.bid != 1 ? "s." : ".");
      /* send out the messages */
      auction_output (buf2, buf);
    }
}

/*
 * auction_reset : returns the auction structure to a non-bidding state
 */
void
auction_reset (void)
{
  auction.bidder = -1;
  auction.seller = -1;
  auction.obj = NULL;
  auction.ticks = AUC_NONE;
  auction.bid = 0;
}

/*
 * get_ch_by_id_desc : given an ID number, searches every descriptor for a
 *              character with that number and returns a pointer to it.
 */
struct char_data *
get_ch_by_id_desc (long idnum)
{
  struct descriptor_data *d;
  for (d = descriptor_list; d; d = d->next)
    if (d && d->character && GET_IDNUM (d->character) == idnum)
      return (d->character);

  return NULL;
}

/*
 * get_ch_by_id: searches the character list for a pc
 */
struct char_data *
get_ch_by_id (long idnum)
{
  struct char_data *tch;
  for (tch = character_list; tch; tch = tch->next)
    if (tch && !IS_NPC (tch) && GET_IDNUM (tch) == idnum)
      return (tch);

  return NULL;
}

/*
 * do_auctioneer: Changes the name used on the auction channel.
 */
ACMD (do_auctioneer)
{
  skip_spaces (&argument);

  if (!argument || !*argument)
    send_to_char ("Must have a name!\r\n", ch);
  else
    {
      if (auction.auctioneer)
	free (auction.auctioneer);
      auction.auctioneer = str_dup (argument);
      send_to_char (OK, ch);
    }
}

ACMD (do_stop_auction)
{
  struct char_data *tch;

  if (auction.obj)
    {
      if ((tch = get_ch_by_id (auction.seller)))
	obj_to_char (auction.obj, tch);
      else
	extract_obj (auction.obj);
    }

  if (auction.bid)
    if ((tch = get_ch_by_id (auction.bidder)))
      GET_GOLD (tch) += auction.bid;

  auction_reset ();
  send_to_char (OK, ch);
}