/**************************************************************************
* 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;
}
}
}