/*
* commerce.cpp
* Functions to handle buying/selling/trading from shops/monsters.
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* Permission to use, modify and distribute is granted via the
* Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
* http://creativecommons.org/licenses/by-nc-sa/3.0/
*
* Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell
* Contributions by Tim Callahan, Jonathan Hseu
* Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
*
*/
#include "mud.h"
#include "commands.h"
#include "factions.h"
#include "property.h"
#include "guilds.h"
#include "unique.h"
#define TAX .06
//*********************************************************************
// buyAmount
//*********************************************************************
Money buyAmount(const Player* player, const Monster *monster, const Object* object, bool sell) {
Money cost = Faction::adjustPrice(player, monster->getPrimeFaction(), object->value, true);
cost.set(MAX(10,cost[GOLD]), GOLD);
return(cost);
}
Money buyAmount(const Player* player, const Room *room, const Object* object, bool sell) {
Money cost = Faction::adjustPrice(player, room->getFaction(), object->value, true);
cost.set(MAX(10,cost[GOLD]), GOLD);
return(cost);
}
//*********************************************************************
// sellAmount
//*********************************************************************
Money sellAmount(const Player* player, const Room *room, const Object* object, bool sell) {
Money value = Faction::adjustPrice(player, room->getFaction(), object->value, sell);
value.set(MIN(value[GOLD] / 2, MAXPAWN), GOLD);
return(value);
}
//*********************************************************************
// bailCost
//*********************************************************************
Money bailCost(const Player* player) {
Money cost;
cost.set(2000*player->getLevel(), GOLD);
return(cost);
}
//*********************************************************************
// Dice
//*********************************************************************
Dice::Dice() {
clear();
}
Dice::Dice(unsigned short n, unsigned short s, short p) {
number = sides = plus = 0;
setNumber(n);
setSides(s);
setPlus(p);
}
void Dice::clear() {
number = sides = plus = 0;
}
bool Dice::operator==(const Dice& d) const {
if( number != d.getNumber() ||
plus != d.getPlus() ||
sides != d.getSides()
)
return(false);
return(true);
}
bool Dice::operator!=(const Dice& d) const {
return(!(*this==d));
}
void Dice::load(xmlNodePtr curNode) {
xmlNodePtr childNode = curNode->children;
clear();
while(childNode) {
if(NODE_NAME(childNode, "Number")) setNumber(xml::toNum<unsigned short>(childNode));
else if(NODE_NAME(childNode, "Sides")) setSides(xml::toNum<unsigned short>(childNode));
else if(NODE_NAME(childNode, "Plus")) setPlus(xml::toNum<short>(childNode));
childNode = childNode->next;
}
}
void Dice::save(xmlNodePtr curNode, const char* name) const {
if(!number && !sides && !plus)
return;
xmlNodePtr childNode = xml::newStringChild(curNode, name);
xml::saveNonZeroNum(childNode, "Number", number);
xml::saveNonZeroNum(childNode, "Sides", sides);
xml::saveNonZeroNum(childNode, "Plus", plus);
}
int Dice::roll() const {
return(dice(number, sides, plus));
}
int Dice::average() const {
return((number + number * sides) / 2 + plus);
}
int Dice::low() const {
return(number + plus);
}
int Dice::high() const {
return(number * sides + plus);
}
bstring Dice::str() const {
std::ostringstream oStr;
oStr << number << "d" << sides;
if(plus)
oStr << (plus > 0 ? "+" : "-") << plus;
return(oStr.str());
}
unsigned short Dice::getNumber() const { return(number); }
unsigned short Dice::getSides() const { return(sides); }
short Dice::getPlus() const { return(plus); }
void Dice::setNumber(unsigned short n) { number = n; }
void Dice::setSides(unsigned short s) { sides = s; }
void Dice::setPlus(short p) { plus = p; }
//*********************************************************************
// Money
//*********************************************************************
Money::Money() {
zero();
}
void Money::load(xmlNodePtr curNode) {
zero();
xml::loadNumArray<unsigned long>(curNode, m, "Coin", MAX_COINS+1);
}
void Money::save(const char* name, xmlNodePtr curNode) const {
saveULongArray(curNode, name, "Coin", m, MAX_COINS+1);
}
bool Money::isZero() const {
for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
if(m[i])
return(false);
return(true);
}
void Money::zero() { ::zero(m, sizeof(m)); }
bool Money::operator==(const Money& mn) const {
for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
if(m[i] != mn.get(i))
return(false);
return(true);
}
bool Money::operator!=(const Money& mn) const{
return(!(*this==mn));
}
unsigned long Money::operator[](Coin c) const { return(m[c]); }
unsigned long Money::get(Coin c) const { return(m[c]); }
void Money::set(unsigned long n, Coin c) { m[c] = MIN(2000000000, n); }
void Money::add(unsigned long n, Coin c) { set(m[c] + n, c); }
void Money::sub(unsigned long n, Coin c) { set(m[c] - n, c); }
void Money::set(Money mn) {
for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
set(mn[i], i);
}
void Money::add(Money mn) {
for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
add(mn[i], i);
}
void Money::sub(Money mn) {
for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1))
sub(mn[i], i);
}
bstring Money::str() const {
bool found=false;
std::stringstream oStr;
for(Coin i = MIN_COINS; i < MAX_COINS; i = (Coin)((int)i + 1)) {
if(m[i]) {
if(found)
oStr << ", ";
found = true;
oStr << m[i] << " " << coinNames(i).toLower() << " coin" << (m[i] != 1 ? "s" : "");
}
}
if(!found)
oStr << "0 coins";
return(oStr.str());
}
bstring Money::coinNames(Coin c) {
switch(c) {
case COPPER:
return("Copper");
case SILVER:
return("Silver");
case GOLD:
return("Gold");
case PLATINUM:
return("Platinum");
case ALANTHIUM:
return("Alanthium");
default:
break;
}
return("");
}
const int SHOP_FOUND = 1,
SHOP_STOCK = 2,
SHOP_PRICE = 3,
SHOP_REMOVE = 4,
SHOP_NAME = 5,
SHOP_GUILD = 6;
const char *shopSyntax =
"Syntax: shop ^e<^xstock^e> <^citem^e> [^c$price^e]^x\n"
" ^e<^xprice^e> <^citem^e> <^c$price^e>^x\n"
" ^e<^xremove^e> <(^citem^e>^x\n"
" ^e<^xname^e> <^cshop name^e>^x\n"
" ^e<^xguild^e> <^xassign^e|^xremove^e>^x\n"
" ^e<^xsurvey^e>^x\n"
" ^e<^xhelp^e>^x\n"
"\n"
" ^e<^xhire^e> <^cplayer^e> -^x add a partial owner\n"
" ^e<^xfire^e> <^cplayer^e> -^x remove a partial owner\n"
" ^e<^xflags^e>^x ^e-^x view / set flags on partial owners\n"
" ^e<^xlog^e>^x ^e<^xdelete^e|^xrecord^e>^x ^e-^x view / delete / set log type\n"
" ^e<^xdestroy^e>^x ^e-^x close this shop down\n"
"\n";
//*********************************************************************
// shopProfit
//*********************************************************************
long shopProfit(Object* object) {
return((long)(object->getShopValue() - (TAX * object->getShopValue())));
}
//*********************************************************************
// getCondition
//*********************************************************************
bstring getCondition(Object* object) {
int percent = -1;
// possible division by 0
if(object->getShotscur() > 0 && object->getShotsmax() > 0)
percent = 100 * object->getShotscur() / object->getShotsmax();
if( object->getType() == WEAPON ||
object->getType() == ARMOR ||
object->getType() == LIGHTSOURCE ||
object->getType() == WAND ||
object->getType() == KEY ||
object->getType() == POISON
) {
if(percent >= 90)
return("Pristine");
else if(percent >= 75)
return("Excellent");
else if(percent >= 60)
return("Good");
else if(percent >= 45)
return("Slightly Scratched");
else if(percent >= 30)
return("Badly Dented");
else if(percent >= 10)
return("Horrible");
else if(percent >= 1)
return("Terrible");
else if(object->getShotscur() < 1)
return("Broken");
}
return("Pristine");
}
//*********************************************************************
// setupShop
//*********************************************************************
void setupShop(Player* player, Guild* guild, Room* shop, Room* storage) {
if(guild)
sprintf(shop->name, "%s", guild->name.c_str());
else
sprintf(shop->name, "%s", player->name);
strcat(shop->name, "'s Shop of Miscellaneous Wares");
// handle shop description
shop->setShortDescription("You are in ");
if(guild)
shop->appendShortDescription(guild->name);
else
shop->appendShortDescription(player->name);
shop->appendShortDescription("'s shop of magnificent wonders.");
// switch to long description
shop->setLongDescription("In ");
if(guild)
shop->appendLongDescription(guild->name);
else
shop->appendLongDescription(player->name);
shop->appendLongDescription("'s shop, you may find many things which you need.\n");
if(guild)
shop->appendLongDescription("The guild");
else
shop->appendLongDescription(player->upHeShe());
shop->appendLongDescription(" should be keeping it well stocked in order to maintain\ngood business. Should you have any complaints, you should go to\nthe nearest post office and mudmail the ");
if(guild) {
shop->appendLongDescription("the guild contact, ");
shop->appendLongDescription(player->name);
shop->appendLongDescription(".");
} else
shop->appendLongDescription("proprietor.");
if(guild)
sprintf(storage->name, "%s", guild->name.c_str());
else
sprintf(storage->name, "%s", player->name);
strcat(storage->name, "'s Shop Storage Room");
shop->saveToFile(0);
storage->saveToFile(0);
}
//*********************************************************************
// shopRemoveGuild
//*********************************************************************
void shopRemoveGuild(Property *p, Player* player, Room* shop, Room* storage) {
p->setGuild(0);
p->appendLog("", "This property is no longer associated with a guild.");
if(!shop || !storage) {
CatRef cr = p->ranges.front().low;
if(loadRoom(cr, &shop)) {
cr.id++;
if(!loadRoom(cr, &storage))
return;
}
}
setupShop(player, 0, shop, storage);
}
//*********************************************************************
// shopStorageRoom
//*********************************************************************
CatRef shopStorageRoom(Room *shop) {
CatRef cr;
if(shop->getTrapExit().id && !shop->flagIsSet(R_LOG_INTO_TRAP_ROOM))
cr = shop->getTrapExit();
else {
cr = shop->info;
cr.id++;
}
return(cr);
}
//*********************************************************************
// playerShopSame
//*********************************************************************
bool playerShopSame(Player* player, Object* obj1, Object* obj2) {
return( obj1->showAsSame(player, obj2) &&
getCondition(obj1) == getCondition(obj2) &&
obj1->getShopValue() == obj2->getShopValue()
);
}
//*********************************************************************
// objShopName
//*********************************************************************
bstring objShopName(Object* object, int m, int flags, int pad) {
bstring name = object->getObjStr(NULL, flags, m);
pad -= stripColor(name).getLength();
for(int i=0; i<pad; i++)
name += " ";
return(name);
}
//*********************************************************************
// tooManyItemsInShop
//*********************************************************************
bool tooManyItemsInShop(const Player* player, const Room* storage) {
int numObjects=0, numLines=0;
otag *op = storage->first_obj;
while(op) {
numObjects++;
numLines++;
while(op->next_tag) {
if(playerShopSame(0, op->obj, op->next_tag->obj)) {
numObjects++;
op = op->next_tag;
} else
break;
}
if(numObjects >= gConfig->getShopNumObjects() || numLines >= gConfig->getShopNumLines()) {
player->print("There are too many items in this shop.\n");
player->print("Please remove some before continuing.\n");
return(true);
}
op = op->next_tag;
}
return(false);
}
//*********************************************************************
// cmdShop
//*********************************************************************
int cmdShop(Player* player, cmd* cmnd) {
Room* room = player->parent_rom, *storage=0;
Object *deed=0;
otag *op;
int action=0;
int flags = 0, len = strlen(cmnd->str[1]);
if(!len) {
player->printColor(shopSyntax);
return(0);
}
if(!strncmp(cmnd->str[1], "help", len)) {
strcpy(cmnd->str[1], "shops");
return(cmdHelp(player, cmnd));
} else if(
!strcmp(cmnd->str[1], "hire") ||
!strcmp(cmnd->str[1], "fire") ||
!strcmp(cmnd->str[1], "flags") ||
!strcmp(cmnd->str[1], "log") ||
!strcmp(cmnd->str[1], "destroy")
) {
// give them a cross-command shortcut
return(cmdProperties(player, cmnd));
}
if(!needUniqueRoom(player))
return(0);
if(!strncmp(cmnd->str[1], "survey", len)) {
if(!room->flagIsSet(R_BUILD_SHOP))
player->print("You are unable to build a shop here.\n");
else
player->print("This site is open for shop construction.\n");
return(0);
}
if(cmnd->num < 3) {
player->printColor(shopSyntax);
return(0);
}
flags |= MAG;
if(!strncmp(cmnd->str[1], "found", len)) {
if(player->getLevel() < MINSHOPLEVEL && !player->isDm()) {
player->print("You must be level %d to found a shop!\n", MINSHOPLEVEL);
return(0);
}
if(!room->flagIsSet(R_BUILD_SHOP)) {
player->print("This section of town isn't zoned for shops!\n");
return(0);
}
op = player->first_obj;
while(op) {
deed = op->obj;
if(!strcmp(deed->name, "shop deed"))
if(deed->deed.belongs(room->info))
break; // We got one!
op = op->next_tag;
deed = NULL;
}
if(!deed) {
player->print("You need a shop deed to build a shop in this town!\nVisit the local real estate office.\n");
return(0);
}
action = SHOP_FOUND;
} else if( !strncmp(cmnd->str[1], "stock", len) ||
!strncmp(cmnd->str[1], "add", len) ||
!strcmp(cmnd->str[1], "sell")
)
action = SHOP_STOCK;
else if(!strncmp(cmnd->str[1], "price", len) || !strncmp(cmnd->str[1], "value", len))
action = SHOP_PRICE;
else if(!strncmp(cmnd->str[1], "remove", len))
action = SHOP_REMOVE;
else if(!strncmp(cmnd->str[1], "name", len))
action = SHOP_NAME;
else if(!strncmp(cmnd->str[1], "guild", len))
action = SHOP_GUILD;
Property* p = gConfig->getProperty(room->info);
if(action != SHOP_FOUND) {
if( !p ||
p->getType() != PROP_SHOP ||
( !player->isDm() &&
!p->isOwner(player->name) &&
!p->isPartialOwner(player->name)
)
) {
player->print("This is not your shop!\n");
return(0);
}
}
PartialOwner partial;
PartialOwner* po=0;
if(p && !p->isOwner(player->name)) {
po = p->getPartialOwner(player->name);
// get default flags if not on the list
if(!po) {
partial.defaultFlags(p->getType());
po = &partial;
}
}
if(!loadRoom(shopStorageRoom(room), &storage)) {
if( action == SHOP_STOCK ||
action == SHOP_REMOVE ||
action == SHOP_PRICE ||
action == SHOP_NAME
) {
player->print("The storage room to this shop could not be loaded!\n");
return(0);
}
}
// see if they have the authorization to do the command they want to do
if(po) {
if(action == SHOP_NAME || action == SHOP_GUILD) {
player->print("Only the owner of this shop may perform that action.\n");
return(0);
}
if( (action == SHOP_STOCK && !po->flagIsSet(PROP_SHOP_CAN_STOCK)) ||
(action == SHOP_REMOVE && !po->flagIsSet(PROP_SHOP_CAN_REMOVE)) ||
(action == SHOP_PRICE && (!po->flagIsSet(PROP_SHOP_CAN_STOCK) || !po->flagIsSet(PROP_SHOP_CAN_REMOVE)))
) {
if(!player->checkStaff("You don't have the proper authorization to perform that action.\n"))
return(0);
}
}
bool limited=false;
if(action == SHOP_FOUND) {
Room *shop=0;
storage = 0;
bstring xname = getFullstrText(cmnd->fullstr, 2);
xname.Replace("_", " ");
if(!Property::goodExit(player, room, "Shop", xname))
return(0);
CatRef cr = gConfig->getAvailableProperty(PROP_SHOP, 2);
if(cr.id < 1) {
player->print("Sorry, you can't found new shops right now, try again later!\n");
return(0);
}
logn("log.shop", "%s just founded a shop! (%s - %s).\n", player->name, xname.c_str(), cr.str().c_str());
shop = new Room;
shop->info = cr;
link_rom(shop, room->info, "out");
link_rom(room, shop->info, xname.c_str());
shop->setFlag(R_SHOP);
shop->setFlag(R_INDOORS);
storage = new Room;
storage->info = shop->info;
storage->info.id++;
link_rom(storage, shop->info, "out");
storage->setFlag(R_SHOP_STORAGE);
storage->setTrapExit(shop->info);
// everything to do with the player's name
setupShop(player, 0, shop, storage);
room->saveToFile(0);
p = new Property;
p->setOwner(player->name);
p->setName(shop->name);
p->setDateFounded();
p->setLocation(room->name);
p->addRange(shop->info.area, shop->info.id, shop->info.id+1);
p->setType(PROP_SHOP);
gConfig->addProperty(p);
player->print("Congratulations! You are now the owner of a brand new shop.\n");
logn("log.shops", "*** %s just built a shop! (%s - %s) (Shop %s).\n",
player->name, room->info.str().c_str(), xname.c_str(), shop->info.str().c_str());
broadcast(player->getSock(), player->getRoom(), "%M just opened a shop!", player );
if(!player->flagIsSet(P_DM_INVIS))
broadcast("### %s just opened a shop! It's located at: %s.", player->name, room->name);
delete shop;
delete storage;
player->delObj(deed, true);
delete deed;
player->parent_rom->clearFlag(R_BUILD_SHOP);
player->parent_rom->setFlag(R_WAS_BUILD_SHOP);
player->parent_rom->saveToFile(0);
return(0);
} else if(action == SHOP_GUILD) {
if(!strncmp(cmnd->str[2], "assign", strlen(cmnd->str[2]))) {
if(!player->getGuild()) {
player->print("You are not part of a guild.\n");
return(0);
}
if(p->getGuild() == player->getGuild()) {
player->print("This property is already assigned to your guild.\n");
return(0);
}
Guild* guild = gConfig->getGuild(player->getGuild());
if(!guild) {
player->print("There was an error loading your guild.\n");
return(0);
}
p->setGuild(guild->num);
setupShop(player, guild, player->parent_rom, storage);
player->print("This property is now associated with the guild %s.\n", guild->name.c_str());
p->appendLog("", "This property is now associated with the guild %s.", guild->name.c_str());
broadcastGuild(guild->num, 1, "### %s has assigned the shop located at %s as partially run by the guild.",
player->name, p->getLocation().c_str());
} else if(!strncmp(cmnd->str[2], "remove", strlen(cmnd->str[2]))) {
if(!p->getGuild()) {
player->print("This property is not assigned to a guild.\n");
return(0);
}
broadcastGuild(p->getGuild(), 1, "### %s's shop located at %s is no longer partially run by the guild.",
player->name, p->getLocation().c_str());
shopRemoveGuild(p, player, player->parent_rom, storage);
player->print("This property is no longer associated with a guild.\n");
} else {
player->print("Command not understood.\n");
player->printColor("You may ^Wassign^x this shop to a guild or ^Wremove^x it from a guild.\n");
return(0);
}
gConfig->saveProperties();
} else if(action == SHOP_STOCK) {
Object *obj = findObject(player, player->first_obj, cmnd, 2);
int value=0;
if(!obj) {
player->print("You don't have any object with that name.\n");
return(0);
}
if(obj->flagIsSet(O_NO_DROP) || obj->getType() == LOTTERYTICKET) {
player->print("That item cannot be stocked.\n");
return(0);
}
if(obj->first_obj) {
player->print("You need to empty it before it can be stocked.\n");
return(0);
}
if(obj->flagIsSet(O_STARTING)) {
player->print("Starting items cannot be stocked.\n");
return(0);
}
limited = Limited::isLimited(obj);
if(limited && (!p || !p->isOwner(player->name))) {
player->print("You may only handle limited items in shops where you are the primary owner.\n");
return(0);
}
if(obj->getShopValue()) {
player->print("Why would you want to sell second hand trash?\n");
return(0);
}
if(obj->getShotscur() < 1 && obj->getShotscur() != obj->getShotsmax() && obj->getType() != CONTAINER) {
player->print("Why would you want to sell such trash in your shop?\n");
return(0);
}
if(tooManyItemsInShop(player, storage))
return(0);
if(cmnd->num > 3 && cmnd->str[3][0] == '$') {
value = atoi(cmnd->str[3]+1);
value = MAX(0, value);
}
if(!value)
value = obj->value[GOLD];
obj->setShopValue(value);
obj->setFlag(O_PERM_ITEM);
player->delObj(obj);
obj->addToRoom(storage);
p->appendLog(player->name, "%s stocked %s for $%d.", player->name, obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
player->printColor("You stock %s in the store for $%d.\n", obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
broadcast(player->getSock(), player->getRoom(), "%M just stocked something in this store.", player);
// obj->shopValue
if(limited)
player->save(true);
storage->saveToFile(0);
} else if(action == SHOP_PRICE) {
Object *obj = findObject(player, storage->first_obj, cmnd, 2);
int value=0;
if(!obj) {
player->print("You're not selling anything with that name.\n");
return(0);
}
if(cmnd->str[3][0] == '$') {
value = atoi(cmnd->str[3]+1);
value = MAX(0, value);
}
if(cmnd->str[3][0] != '$' || !value) {
player->printColor(shopSyntax);
return(0);
}
obj->setShopValue(value);
p->appendLog(player->name, "%s set the price for %s to $%d.", player->name, obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
player->printColor("You set the price for %s to $%d.\n", obj->getObjStr(NULL, flags, 1).c_str(), obj->getShopValue());
broadcast(player->getSock(), player->getRoom(), "%M just updated the prices in this store.", player);
} else if(action == SHOP_REMOVE) {
Object *obj = findObject(player, storage->first_obj, cmnd, 2);
if(!obj) {
player->print("You're not selling anything with that name.\n");
return(0);
}
if(player->getWeight() + obj->getActualWeight() > player->maxWeight()) {
player->print("That'd be too heavy for you to carry right now.\n");
return(0);
}
if(player->tooBulky(obj->getActualBulk())) {
player->print("That would be too bulky for you to carry.\n");
return(0);
}
limited = Limited::isLimited(obj);
if(limited && (!p || !p->isOwner(player->name))) {
player->print("You may only handle unique items in shops where you are the primary owner.\n");
return(0);
}
obj->deleteFromRoom();
player->addObj(obj);
p->appendLog(player->name, "%s removed %s.", player->name, obj->getObjStr(NULL, flags, 1).c_str());
player->printColor("You remove %s from your store.\n", obj->getObjStr(NULL, flags, 1).c_str());
broadcast(player->getSock(), player->getRoom(), "%M just removed something from this store.", player);
obj->clearFlag(O_PERM_ITEM);
obj->setShopValue(0);
if(limited)
player->save(true);
storage->saveToFile(0);
} else if(action == SHOP_NAME) {
bstring name = getFullstrText(cmnd->fullstr, 2);
if(!Property::goodNameDesc(player, name, "Rename shop to what?", "room name"))
return(0);
if(!p->getGuild()) {
if(name.find(player->name) == bstring::npos) {
player->print("Your shop name must contain your name.\n");
return(0);
}
} else {
Guild* guild = gConfig->getGuild(p->getGuild());
if(!guild) {
player->print("Error loading guild.\n");
return(0);
}
if(name.find(guild->name) == bstring::npos) {
player->print("Your shop name must contain your guild's name.\n");
return(0);
}
}
strcpy(player->parent_rom->name, name.c_str());
p->setName(name);
p->appendLog(player->name, "%s renamed the shop to %s.", player->name, player->parent_rom->name);
logn("log.shops", "%s renamed shop %s to %s.\n",
player->name, player->parent_rom->info.str().c_str(), player->parent_rom->name);
player->print("Shop renamed to '%s'.\n", player->parent_rom->name);
player->parent_rom->saveToFile(0);
return(0);
} else {
player->printColor(shopSyntax);
}
return(0);
}
//*********************************************************************
// doFilter
//*********************************************************************
bool doFilter(const Object* object, const bstring& filter) {
if(filter == "")
return(false);
if(filter == object->getSubType())
return(false);
if( (filter == "weapon" && object->getType() == WEAPON) ||
(filter == "armor" && object->getType() == ARMOR) ||
(filter == "potion" && object->getType() == POTION) ||
(filter == "scroll" && object->getType() == SCROLL) ||
(filter == "wand" && object->getType() == WAND) ||
(filter == "container" && object->getType() == CONTAINER) ||
(filter == "key" && object->getType() == KEY) ||
((filter == "light" || filter == "lightsource") && object->getType() == LIGHTSOURCE) ||
(filter == "song" && object->getType() == SONGSCROLL) ||
(filter == "poison" && object->getType() == POISON) ||
(filter == "bandage" && object->getType() == BANDAGE)
)
return(false);
if(filter != "none" && filter == object->getWeaponCategory())
return(false);
return(true);
}
//*********************************************************************
// cmdList
//*********************************************************************
// This function allows a player to list the items for sale within a
// shop.
int cmdList(Player* player, cmd* cmnd) {
Room* room = player->parent_rom, *storage=0;
Object* object=0;
otag *op=0;
int n=0;
bstring filter = "";
// interchange list and selection
if(cmnd->num == 2 && cmnd->str[1][0] != '-')
return(cmdSelection(player, cmnd));
if(cmnd->num == 2)
filter = &cmnd->str[1][1];
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(!needUniqueRoom(player))
return(0);
if(!room->flagIsSet(R_SHOP)) {
player->print("This is not a shop.\n");
return(0);
}
if(!loadRoom(shopStorageRoom(room), &storage)) {
player->print("Nothing to buy.\n");
return(0);
}
if(!storage->flagIsSet(R_SHOP_STORAGE)) {
player->print("This is not a shop.\n");
return(0);
}
Property* p = gConfig->getProperty(room->info);
// not a player run shop
if(!p || p->getType() != PROP_SHOP) {
Money cost;
if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
player->print("The shopkeeper refuses to do business with you.\n");
return(0);
}
storage->addPermObj();
op = storage->first_obj;
if(op) {
player->print("You may buy:");
if(filter != "")
player->printColor(" ^Y(filtering on \"%s\")", filter.c_str());
player->print("\n");
while(op) {
object = op->obj;
op = op->next_tag;
if(doFilter(object, filter))
continue;
if(!strcmp(object->name, "bail"))
object->value = bailCost(player);
cost = buyAmount(player, player->parent_rom, object, true);
// even if they love you, lottery tickets are the same price
if(object->getType() == LOTTERYTICKET) {
object->value.set(gConfig->getLotteryTicketPrice(), GOLD);
cost = object->value;
}
player->printColor(" %s Cost: %s\n",
objShopName(object, 1, CAP | (player->isEffected("detect-magic") ? MAG : 0), 52).c_str(),
cost.str().c_str());
}
player->print("\n");
} else
player->print("There is nothing for sale.\n");
} else {
// We've got a player run shop here...different formating them
int num = 0, flags = 0, m=0;
bool owner=false;
flags |= CAP;
flags |= MAG;
if(p->isOwner(player->name)) {
player->print("You are selling:");
owner = true;
} else {
player->print("%s is selling:", p->getOwner().c_str());
if(p->isPartialOwner(player->name))
owner = true;
}
if(filter != "")
player->printColor(" ^Y(filtering on \"%s\")", filter.c_str());
player->print("\n");
op = storage->first_obj;
while(op) {
n++;
m=1;
while(op->next_tag) {
if(playerShopSame(player, op->obj, op->next_tag->obj)) {
m++;
op = op->next_tag;
} else
break;
}
object = op->obj;
op = op->next_tag;
num++;
if(doFilter(object, filter))
continue;
if(n == 1)
player->printColor("^WNum Item Price Condition\n");
player->printColor("%2d> %s $%-9ld %-12s", num,
objShopName(object, m, flags, 35).c_str(),
object->getShopValue(), getCondition(object).c_str());
if(owner)
player->print(" Profit: %ld", shopProfit(object));
player->print("\n");
}
if(!n)
player->print("Absolutely nothing!\n");
}
return(0);
}
//*********************************************************************
// cmdPurchase
//*********************************************************************
// purchase allows a player to buy an item from a monster. The
// purchase item flag must be set, and the monster must have an
// object to sell. The object for sale is determined by the first
// object listed in carried items.
int cmdPurchase(Player* player, cmd* cmnd) {
Monster *creature=0;
Object *object=0;
int maxitem=0;
CatRef obj_num[10];
Money cost;
int i=0, j=0, found=0, match=0;
if(cmnd->num == 2)
return(cmdBuy(player, cmnd));
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 2) {
player->print("Syntax: purchase <item> <monster>\n");
return(0);
}
if(cmnd->num < 3 ) {
player->print("Syntax: purchase <item> <monster>\n");
return(0);
}
creature = player->getRoom()->findMonster(player, cmnd, 2);
if(!creature) {
player->print("That is not here.\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, creature->getPrimeFaction())) {
player->print("%M refuses to do business with you.\n", creature);
return(0);
}
if(!creature->flagIsSet(M_CAN_PURCHASE_FROM)) {
player->print("You cannot purchase objects from %N.\n",creature);
return(0);
}
for(i=0; i<10; i++) {
if(creature->carry[i].info.id > 0) {
found = 0;
for(j=0; j< maxitem; j++)
if(creature->carry[i].info == obj_num[j])
found = 1;
if(!found) {
maxitem++;
obj_num[i] = creature->carry[i].info;
}
}
}
if(!maxitem) {
player->print("%M has nothing to sell.\n",creature);
return(0);
}
found = 0;
for(i=0; i<maxitem; i++) {
if(loadObject(creature->carry[i].info, &object)) {
if(player->canSee(object) && keyTxtEqual(object, cmnd->str[1])) {
match++;
if(match == cmnd->val[1]) {
found = 1;
break;
} else
delete object;
} else
delete object;
}
}
if(!found) {
player->print("%M says, \"Sorry, I don't have any of those to sell.\"\n", creature);
return(0);
}
object->init();
if(player->getWeight() + object->getActualWeight() > player->maxWeight()) {
player->print("That would be too much for you to carry.\n");
delete object;
return(0);
}
if(player->tooBulky(object->getActualBulk())) {
player->print("That would be too bulky.\nClean your inventory first.\n");
delete object;
return(0);
}
if(!Unique::canGet(player, object)) {
player->print("You are unable to purchase that limited item at this time.\n");
delete object;
return(0);
}
if(!Lore::canHave(player, object, false)) {
player->print("You cannot purchased that item.\nIt is a limited item of which you cannot carry any more.\n");
delete object;
return(0);
}
if(!Lore::canHave(player, object, true)) {
player->print("You cannot purchased that item.\nIt contains a limited item that you cannot carry.\n");
delete object;
return(0);
}
cost = buyAmount(player, creature, object, true);
if(player->coins[GOLD] < cost[GOLD]) {
player->print("%M says \"The price is %s, and not a copper piece less!\"\n", creature, cost.str().c_str());
delete object;
} else {
player->print("You pay %N %s gold pieces.\n", creature, cost.str().c_str());
player->printColor("%M says, \"%sHere is your %s.\"\n", creature,
Faction::getAttitude(player->getFactionStanding(creature->getPrimeFaction())) < Faction::INDIFFERENT ? "Hrmph. " : "Thank you very much. ",
object->name);
broadcast(player->getSock(), player->getRoom(), "%M pays %N %s for %P.\n", player, creature, cost.str().c_str(), object);
player->coins.sub(cost);
player->doHaggling(creature, object, BUY);
Limited::addOwner(player, object);
player->addObj(object);
}
return(0);
}
//*********************************************************************
// cmdSelection
//*********************************************************************
// The selection command lists all the items a monster is selling.
// The monster needs the M_CAN_PURCHASE_FROM flag set to denote it can sell.
int cmdSelection(Player* player, cmd* cmnd) {
Monster *creature=0;
Object *object=0;
CatRef obj_list[10];
int i=0, j=0, found=0, maxitem=0;
bstring filter = "";
// interchange list and selection
if(cmnd->num == 1 || cmnd->str[1][0] == '-')
return(cmdList(player, cmnd));
if(cmnd->num == 3)
filter = &cmnd->str[2][1];
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
creature = player->getRoom()->findMonster(player, cmnd);
if(!creature) {
player->print("That is not here.\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, creature->getPrimeFaction())) {
player->print("%M refuses to do business with you.\n", creature);
return(0);
}
if(!creature->flagIsSet(M_CAN_PURCHASE_FROM)) {
player->print("%M is not selling anything.\n",creature);
return(0);
}
for(i=0; i<10; i++) {
if(creature->carry[i].info.id > 0) {
found = 0;
for(j=0; j< maxitem; j++)
if(creature->carry[i].info == obj_list[j])
found = 1;
if(!found) {
maxitem++;
obj_list[i] = creature->carry[i].info;
}
}
}
if(!maxitem) {
player->print("%M has nothing to sell.\n", creature);
return(0);
}
player->print("%M is currently selling:", creature);
if(filter != "")
player->printColor(" ^Y(filtering on \"%s\")", filter.c_str());
player->print("\n");
Money cost;
for(i=0; i<maxitem; i++) {
if(!creature->carry[i].info.id || !loadObject(creature->carry[i].info, &object)) {
player->print("%d) Out of stock.\n", i+1);
} else {
if(!doFilter(object, filter)) {
cost = buyAmount(player, creature, object, true);
player->printColor("%d) %s %s\n", i+1, objShopName(object, 1, 0, 35).c_str(),
cost.str().c_str());
}
delete object;
}
}
player->print("\n");
return(0);
}
//*********************************************************************
// cmdBuy
//*********************************************************************
// This function allows a player to buy something from a shop.
int cmdBuy(Player* player, cmd* cmnd) {
Room* room = player->parent_rom, *storage=0;
Object *object=0, *object2=0;
otag *op=0;
int num=0, n=1;
if(cmnd->num == 3)
return(cmdPurchase(player, cmnd));
if(!needUniqueRoom(player))
return(0);
if(!player->ableToDoCommand())
return(0);
if(player->getClass() == BUILDER) {
player->print("You may not buy things from shops.\n");
return(0);
}
if(!room->flagIsSet(R_SHOP)) {
player->print("This is not a shop.\n");
return(0);
}
if(cmnd->num < 2) {
player->print("Buy what?\n");
return(0);
}
if(!loadRoom(shopStorageRoom(room), &storage)) {
player->print("Nothing to buy.\n");
return(0);
}
if(!storage->flagIsSet(R_SHOP_STORAGE)) {
player->print("This is not a shop.\n");
return(0);
}
//*********************************************************************
Property* p = gConfig->getProperty(room->info);
if(p && p->getType() == PROP_SHOP) {
// We have a player run shop here!!!
Player* owner=0;
Guild* guild=0;
int flags=0;
long deposit=0;
bool online=false;
if(p->isOwner(player->name)) {
player->print("Why would you want to buy stuff from your own shop?\n");
return(0);
}
flags |= MAG;
if(cmnd->str[1][0] != '#' || !cmnd->str[1][1]) {
player->print("What item would you like to buy?\n");
return(0);
}
num = atoi(cmnd->str[1]+1);
op = storage->first_obj;
while(op && num != n) {
while(op->next_tag) {
if((playerShopSame(player, op->obj, op->next_tag->obj))) {
op = op->next_tag;
} else
break;
}
n++;
op = op->next_tag;
}
if(!op) {
player->print("That isn't being sold here.\n");
return(0);
}
object = op->obj;
// load the guild a little bit earlier
if(p->getGuild())
guild = gConfig->getGuild(p->getGuild());
if(player->coins[GOLD] < object->getShopValue()) {
if(guild)
player->print("You can't afford %s's outrageous prices!\n", guild->name.c_str());
else
player->print("You can't afford %s's outrageous prices!\n", p->getOwner().c_str());
return(0);
}
if(player->getWeight() + object->getActualWeight() > player->maxWeight()) {
player->print("You couldn't possibly carry anymore.\n");
return(0);
}
if(player->tooBulky(object->getActualBulk()) ) {
player->print("No more will fit in your inventory right now.\n");
return(0);
}
if(!Unique::canGet(player, object, true)) {
player->print("You are unable to purchase that limited item at this time.\n");
return(0);
}
if(!Lore::canHave(player, object, false)) {
player->print("You cannot purchased that item.\nIt is a limited item of which you cannot carry any more.\n");
return(0);
}
if(!Lore::canHave(player, object, true)) {
player->print("You cannot purchased that item.\nIt contains a limited item that you cannot carry.\n");
return(0);
}
player->printColor("You buy a %s.\n", object->getObjStr(NULL, flags, 1).c_str());
broadcast(player->getSock(), player->getRoom(), "%M just bought %1P.", player, object);
object->clearFlag(O_PERM_INV_ITEM);
object->clearFlag(O_PERM_ITEM);
object->clearFlag(O_TEMP_PERM);
player->coins.sub(object->getShopValue(), GOLD);
if(object->getType() == LOTTERYTICKET || object->getType() == BANDAGE)
object->setShopValue(0);
owner = gServer->findPlayer(p->getOwner().c_str());
if(!owner) {
if(!loadPlayer(p->getOwner().c_str(), &owner)) {
loge("Shop (%s) without a valid owner (%s).\n",
room->info.str().c_str(), p->getOwner().c_str());
player->save(true);
return(0);
}
} else
online = true;
object->deleteFromRoom();
Limited::transferOwner(owner, player, object);
player->addObj(object);
deposit = shopProfit(object);
if(!guild) {
owner->bank.add(deposit, GOLD);
banklog(owner->name, "PROFIT from sale of %s to %s: %ld [Balance: %s]\n",
object->getObjStr(NULL, flags, 1).c_str(), player->name, deposit, owner->bank.str().c_str());
if(online && !owner->flagIsSet(P_DONT_SHOW_SHOP_PROFITS))
owner->print("*** You made $%d profit from the sale of %s to %s.\n",
deposit, object->getObjStr(NULL, flags, 1).c_str(), player->name);
owner->save(online);
} else {
guild->bank.add(deposit, GOLD);
guildBankLog(guild->num, "PROFIT from sale of %s to %s: %ld [Balance: %s]\n",
object->getObjStr(NULL, flags, 1).c_str(), player->name, deposit, guild->bank.str().c_str());
gConfig->saveGuilds();
}
if(!online)
free_crt(owner);
player->save(true);
return(0);
//*********************************************************************
} else {
// Not a player run shop
object = findObject(player, storage->first_obj, cmnd);
if(!object) {
player->print("That's not being sold.\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
player->print("The shopkeeper refuses to do business with you.\n");
return(0);
}
if(!strcmp(object->name, "storage room")) {
if(!room->flagIsSet(R_STORAGE_ROOM_SALE)) {
player->print("You can't buy storage room's here.\n");
return(0);
}
CatRef cr = gConfig->getSingleProperty(player, PROP_STORAGE);
if(cr.id) {
player->print("You are already affiliated with a storage room.\nOnly one is allowed per player.\n");
return(0);
}
}
if(!strcmp(object->name, "bail"))
object->value = bailCost(player);
Money cost = buyAmount(player, player->parent_rom, object, true);
// even if they love you, lottery tickets are the same price
if(object->getType() == LOTTERYTICKET) {
object->value.set(gConfig->getLotteryTicketPrice(), GOLD);
cost = object->value;
}
if(!strcmp(object->name, "bail")) {
// Depends on level for bail
// 2k per lvl to get out
if(player->coins[GOLD] < cost[GOLD]) {
player->print("You don't have enough gold to post bail.\n");
return(0);
}
} else {
if(player->coins[GOLD] < cost[GOLD]) {
player->print("You don't have enough gold.\n");
return(0);
}
if( player->getWeight() + object->getActualWeight() > player->maxWeight() &&
!player->checkStaff("You can't carry anymore.\n") )
return(0);
if(player->tooBulky(object->getActualBulk()) ) {
player->print("No more will fit in your inventory right now.\n");
return(0);
}
}
// Not buying a storage room or bail
if(strcmp(object->name, "storage room") && strcmp(object->name, "bail")) {
player->unhide();
if(object->getType() == LOTTERYTICKET) {
if(gConfig->getLotteryEnabled() == 0) {
player->print("Sorry, lottery tickets are not currently being sold.\n");
return(0);
}
if(createLotteryTicket(&object2, player->name) < 0) {
player->print("Sorry, lottery tickets are not currently being sold.\n");
return(0);
}
// Not a lottery Ticket
} else {
object2 = new Object;
if(!object2)
merror("buy", FATAL);
*object2 = *object;
object2->clearFlag(O_PERM_INV_ITEM);
object2->clearFlag(O_PERM_ITEM);
object2->clearFlag(O_TEMP_PERM);
}
if(object2)
object2->init();
if(!Unique::canGet(player, object)) {
player->print("You are unable to purchase that limited item at this time.\n");
if(object2)
delete object2;
return(0);
}
if(!Lore::canHave(player, object, false)) {
player->print("You cannot purchased that item.\nIt is a limited item of which you cannot carry any more.\n");
if(object2)
delete object2;
return(0);
}
if(!Lore::canHave(player, object, true)) {
player->print("You cannot purchased that item.\nIt contains a limited item that you cannot carry.\n");
if(object2)
delete object2;
return(0);
}
bool isDeed = object2->deed.low.id || object2->deed.high;
// buying a deed
if(isDeed && player->getLevel() < MINSHOPLEVEL) {
player->print("You must be at least level %d to buy a shop deed.\n", MINSHOPLEVEL);
if(object2)
delete object2;
return(0);
}
Limited::addOwner(player, object2);
player->addObj(object2);
player->printColor("You bought %1P.\n", object2);
player->coins.sub(cost);
player->doHaggling(0, object2, BUY);
if(object2->getType() == LOTTERYTICKET || object2->getType() == BANDAGE)
object2->value.zero();
player->print("You have %ld gold left.", player->coins[GOLD]);
if(object2->getType() == LOTTERYTICKET)
player->print(" Your numbers are %02d %02d %02d %02d %02d (%02d).", object2->getLotteryNumbers(0),
object2->getLotteryNumbers(1), object2->getLotteryNumbers(2),
object2->getLotteryNumbers(3), object2->getLotteryNumbers(4), object2->getLotteryNumbers(5));
player->print("\n");
if(strcmp(object2->name, "storage room") && strcmp(object2->name, "bail") && object2->getType() != LOTTERYTICKET)
object2->setFlag(O_JUST_BOUGHT);
broadcast(player->getSock(), player->getRoom(), "%M bought %1P.", player, object2);
player->bug("%s just bought %s for %s in room %s.\n",
player->name, object2->name, cost.str().c_str(), player->getRoom()->fullName().c_str());
logn("log.commerce", "%s just bought %s for %s in room %s.\n",
player->name, object2->name, cost.str().c_str(), player->getRoom()->fullName().c_str());
if(isDeed) {
int flag=0;
bstring type = "";
bstring name = object2->name;
if(name == "shop deed") {
type = "shop";
flag = R_BUILD_SHOP;
} else if(name.left(14) == "guildhall deed") {
type = "guildhall";
flag = R_BUILD_GUILDHALL;
}
if(flag) {
player->printColor("^WNote:^x Please wait while the mud searches for available space to build a %s.\n", type.c_str());
player->print("If it cannot find empty space, you may refund the deed.\n");
gServer->isAvailableSpace(player->name, object2->deed, flag);
}
}
} else if(!strcmp(object->name, "storage room")) {
CatRef cr = gConfig->getAvailableProperty(PROP_STORAGE, 1);
if(cr.id < 1) {
player->print("The shopkeeper says, \"Sorry, I don't have any more rooms available!\"\n");
return(0);
}
// TODO: Dom: hardcoded?
player->coins.sub(250000, GOLD);
player->print("The shopkeeper says, \"Congratulations, you are now the proud owner of a new storage room. Don't forget, to get in you must also buy a key.\"\n");
player->print("You have %s left.\n", player->coins.str().c_str());
broadcast(player->getSock(), player->getRoom(), "%M just bought a storage room.", player);
logn("log.storage", "%s bought storage room %s.\n",
player->name, cr.str().c_str());
createStorage(cr, player->name);
} else {
// Buying bail to get outta jail!
Exit *exit=0;
// Find the cell door
exit = findExit(player, "cell", 1);
player->unhide();
if(!exit || !exit->flagIsSet(X_LOCKED)) {
player->print("You are in no need of bail!\n");
return(0);
}
exit->clearFlag(X_LOCKED);
exit->ltime.ltime = time(0);
player->coins.sub(cost);
player->print("You bail yourself out for %s gold.\n", cost.str().c_str());
broadcast("### %M just bailed %sself out of jail!", player, player->himHer());
}
}
return(0);
}
//*********************************************************************
// isAvailableSpace
//*********************************************************************
bstring Config::isAvailableSpace(Range range, int flag) {
if(range.low.id > range.high)
return("0");
Room* room=0;
CatRef cr;
int high = range.high;
cr.setArea(range.low.area);
cr.id = range.low.id;
if(range.low.id == -1 && range.high == -1) {
cr.id = 1;
high = RMAX;
}
for(; cr.id < high; cr.id++) {
if(!loadRoom(cr, &room))
continue;
if(room->flagIsSet(flag))
return(cr.rstr());
}
return("0");
}
void Server::isAvailableSpace(bstring mover, Range range, int flag) {
// prepare to split the mud process
int fds[2];
if(pipe(fds) == -1) {
printf("RS: Error with pipe!\n");
abort();
}
int pid = fork();
if(!pid) {
// this is the child process
// Close the reading end, we'll only be writing
close(fds[0]);
// Remap stdout to fds[1] so cout will print to fds[1] and we can
// read it in from the mud
if(dup2(fds[1], STDOUT_FILENO) != STDOUT_FILENO) {
std::cout << "RS: Error with dup2.\n";
return;
}
std::cout << gConfig->isAvailableSpace(range, flag);
exit(0);
} else {
// this is the parent process
// Close the writing end, we'll only be reading
close(fds[1]);
nonBlock(fds[0]);
std::cout << "Watching Child FindSpace for(" << mover << ") running with pid " << pid
<< " reading from fd " << fds[0] << std::endl;
// Let the server know we're monitoring this child process
gServer->addChild(pid, CHILD_FIND_SPACE, fds[0], mover);
}
}
void Server::isAvailableSpace(bstring mover, bool isSpace) {
const Player* player = gServer->findPlayer(mover);
if(player) {
if(isSpace) {
player->printColor("^WThere is available space.\n");
} else {
player->printColor("^WNo available space was found. You should refund the deed.\n");
}
}
}
//*********************************************************************
// cmdSell
//*********************************************************************
// This function will allow a player to sell an object in a pawn shop
int cmdSell(Player* player, cmd* cmnd) {
Object *object=0;
bool poorquality=false;
Money value;
if(player->getClass() == BUILDER)
return(0);
if(!player->ableToDoCommand())
return(0);
if(!player->getRoom()->flagIsSet(R_PAWN_SHOP)) {
player->print("This is not a pawn shop.\n");
return(0);
}
if(cmnd->num < 2) {
player->print("Sell what?\n");
return(0);
}
player->unhide();
object = findObject(player, player->first_obj, cmnd);
if(!object) {
player->print("You don't have that.\n");
return(0);
}
if(object->flagIsSet(O_KEEP)) {
player->printColor("%O is currently in safe keeping. Unkeep it to sell it.\n", object);
return(0);
}
if(object->first_obj) {
player->print("You don't want to sell that!\nThere's something inside it!\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
player->print("The shopkeeper refuses to do business with you.\n");
return(0);
}
value = sellAmount(player, player->parent_rom, object, false);
player->computeLuck();
// Luck for sale of items
// gold = ((Ply[fd].extr->getLuck()*gold)/100);
if((object->getType() == WEAPON || object->getType() == ARMOR) && object->getShotscur() <= object->getShotsmax()/8)
poorquality = true;
if((object->getType() == WAND || object->getType() > MISC || object->getType() == KEY) && object->getShotscur() < 1)
poorquality = true;
if( value[GOLD] < 20 ||
poorquality ||
object->getType() == SCROLL ||
// objects flagged as eatable/drinkable can be sold
(object->getType() == POTION && !object->flagIsSet(O_EATABLE) && !object->flagIsSet(O_DRINKABLE)) ||
object->getType() == SONGSCROLL ||
object->flagIsSet(O_STARTING)
) {
switch(mrand(1,5)) {
case 1:
player->print("The shopkeep says, \"I'm not interested in that.\"\n");
break;
case 2:
player->print("The shopkeep says, \"I don't want that.\"\n");
break;
case 3:
player->print("The shopkeep says, \"I don't have any use for that.\"\n");
break;
case 4:
player->print("The shopkeep says, \"You must be joking; that's worthless!\"\n");
break;
default:
player->print("The shopkeep says, \"I won't buy that crap from you.\"\n");
break;
}
return(0);
}
// This prevents high level thieves from possibly cashing in on shoplifting.
if(object->flagIsSet(O_WAS_SHOPLIFTED)) {
player->print("The shopkeep says, \"That was stolen from a shop somewhere. I won't buy it!\"\n");
return(0);
}
if(object->flagIsSet(O_NO_PAWN)) {
player->print("The shopkeep says, \"I refuse to buy that! Get out!\"\n");
return(0);
}
player->printColor("The shopkeep gives you %s for %P.\n", value.str().c_str(), object);
broadcast(player->getSock(), player->getRoom(), "%M sells %1P.", player, object);
object->refund.zero();
object->refund.set(value);
player->doHaggling(0, object, SELL);
player->bug("%s sold %s in room %s.\n", player->name, object->name,
player->getRoom()->fullName().c_str());
logn("log.commerce", "%s sold %s in room %s.\n", player->name, object->name,
player->getRoom()->fullName().c_str());
player->coins.add(value);
player->delObj(object, true);
player->setLastPawn(object);
return(0);
}
//*********************************************************************
// cmdValue
//*********************************************************************
// This function allows a player to find out the pawn-shop value of an
// object, if they are in the pawn shop.
int cmdValue(Player* player, cmd* cmnd) {
Object *object=0;
Money value;
if(!player->ableToDoCommand())
return(0);
if(!player->getRoom()->flagIsSet(R_PAWN_SHOP)) {
player->print("You must be in a pawn shop.\n");
return(0);
}
if(cmnd->num < 2) {
player->print("Value what?\n");
return(0);
}
player->unhide();
object = findObject(player, player->first_obj, cmnd);
if(!object) {
player->print("You don't have that.\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, player->parent_rom->getFaction())) {
player->print("The shopkeeper refuses to do business with you.\n");
return(0);
}
value = sellAmount(player, player->parent_rom, object, false);
player->printColor("The shopkeep says, \"%O's worth %s.\"\n", object, value.str().c_str());
broadcast(player->getSock(), player->getRoom(), "%M gets %P appraised.", player, object);
return(0);
}
//*********************************************************************
// cmdRefund
//*********************************************************************
int cmdRefund(Player* player, cmd* cmnd) {
Object *object=0;
if(player->getClass() == BUILDER)
return(0);
if(!player->ableToDoCommand())
return(0);
if(!player->getRoom()->flagIsSet(R_SHOP)) {
player->print("This is not a shop.\n");
return(0);
}
if(cmnd->num < 2) {
player->print("Refund what?\n");
return(0);
}
object = findObject(player, player->first_obj, cmnd);
if(!object) {
player->print("You don't have that in your inventory.\n");
return(0);
}
if(!object->flagIsSet(O_JUST_BOUGHT) || (object->getShotscur() < object->getShotsmax()) || object->getShopValue()) {
player->print("The shopkeep says, \"I don't return money for used goods!\"\n");
return(0);
}
if(object->getRecipe()) {
player->print("You cannot return intellectual property.\n");
return(0);
}
if(!object->refund.isZero())
object->value.add(object->refund);
player->coins.add(object->value);
player->printColor("The shopkeep takes the %s from you and returns %s to you.\n", object->name, object->value.str().c_str());
broadcast(player->getSock(), player->getRoom(), "%M refunds %P.", player, object);
player->delObj(object, true);
delete object;
return(0);
}
//*********************************************************************
// failTrade
//*********************************************************************
void failTrade(const Player* player, const Object* object, const Monster* target, Object* trade=0, bool useHeShe=true) {
if(useHeShe)
player->printColor("%s gives you back %P.\n", target->upHeShe(), object);
else
player->printColor("%M gives you back %P.\n", target, object);
broadcast(player->getSock(), player->getRoom(), "%M tried to trade %P with %N.",
player, object, target);
if(trade)
delete trade;
}
//*********************************************************************
// canReceiveObject
//*********************************************************************
// Determine if the player can receive the object (as a result of a trade
// or completing a quest. This code does NOT handle bulk/weight, as that
// is tracked on a broader scale, rather than for each item.
bool canReceiveObject(const Player* player, Object* object, const Monster* monster, bool isTrade, bool doQuestOwner, bool maybeTryAgainMsg) {
bstring tryAgain = "";
// if the result of the quest/trade is a random item and we fail based on that random item,
// they may be able to retry the quest/trade.
if(maybeTryAgainMsg) {
if(isTrade)
tryAgain = "As the result of the trade is a random item, you may be able to attempt the trade again.\n";
else
tryAgain = "As the quest reward is a random item, you may be able to attempt to complete the quest again.\n";
}
// check for unique items
if(!Unique::canGet(player, object)) {
if(isTrade)
player->print("%M cannot trade for that right now.\n", monster);
else
player->print("You cannot complete that quest right now.\nThe unique item reward is already in the game.\n");
if(tryAgain != "")
player->print(tryAgain.c_str());
return(false);
}
// check for lore items
if(!Lore::canHave(player, object, false)) {
if(isTrade)
player->printColor("%M cannot trade for that right now, as you already have %P.\n", monster, object);
else
player->printColor("You cannot complete that quest right now, as you already have %P.\n", object);
if(tryAgain != "")
player->print(tryAgain.c_str());
return(false);
}
// check for quest items
if( object->getQuestnum() && player->questIsSet(object->getQuestnum()-1) &&
!player->checkStaff("You have already fulfilled that quest.\n")
) {
if(tryAgain != "")
player->print(tryAgain.c_str());
return(false);
}
// Quest results set player ownership, so this will always be true. If this is a trade,
// this will be true if the object the player is giving the monster is owned by them.
if(doQuestOwner)
object->setQuestOwner(player);
return(true);
}
//*********************************************************************
// abortItemList
//*********************************************************************
// If we cannot give them the objects for whatever reason, we must
// delete all the objects we have already loaded.
void abortItemList(std::list<Object*> &objects) {
Object* object=0;
while(objects.size()) {
object = objects.front();
delete object;
objects.pop_front();
}
objects.clear();
}
//*********************************************************************
// prepareItemList
//*********************************************************************
// When attempting to trade or complete a quest, we first check to see if they are
// able to receive all of the items as a result. If they cannot, we prevent them
// from completing the trade or quest, give them a reason why and return false. If
// they are able to receive all the items, we return a list of objects they will
// receive in the "objects" parameter.
bool prepareItemList(const Player* player, std::list<Object*> &objects, Object* object, const Monster* monster, bool isTrade, bool doQuestOwner, int totalBulk) {
std::list<CatRef>::const_iterator it;
Object* toGive=0;
// if they're getting a random result of a quest or trade,
// they might be able to try again should it fail
bool maybeTryAgainMsg = !object->flagIsSet(O_LOAD_ALL) && object->randomObjects.size();
// count the bulk we will be getting rid of if this transaction is successful
if(totalBulk > 0)
totalBulk *= -1;
object->init();
// if they can't get this object, we need to delete everything
// we've already loaded so far
if(!canReceiveObject(player, object, monster, isTrade, doQuestOwner, maybeTryAgainMsg)) {
abortItemList(objects);
delete object;
return(false);
}
if(object->flagIsSet(O_LOAD_ALL) && object->randomObjects.size()) {
// in this case, we give them a lot of items
for(it = object->randomObjects.begin(); it != object->randomObjects.end(); it++) {
if(!loadObject(*it, &toGive))
continue;
objects.push_back(toGive);
totalBulk += toGive->getActualBulk();
// if they can't get this object, we need to delete everything
// we've already loaded so far
if(!canReceiveObject(player, toGive, monster, isTrade, doQuestOwner, false)) {
abortItemList(objects);
delete object;
return(false);
}
}
// they aren't actually getting the "object" item
delete object;
} else {
// the simple, common scenario
objects.push_back(object);
totalBulk += object->getActualBulk();
}
// check total bulk: if they can't get the objects, we need to delete everything
// we've already loaded so far
if(player->tooBulky(totalBulk)) {
if(isTrade)
player->print("What %N wants to give you is too bulky for you.\n", monster);
abortItemList(objects);
return(false);
}
return(true);
}
//*********************************************************************
// cmdTrade
//*********************************************************************
int cmdTrade(Player* player, cmd* cmnd) {
Monster *creature=0;
Object *object=0, *trade=0;
Carry invTrade, invResult;
int i=0, numTrade = cmnd->val[0];
bool badNumTrade=false, found=false, hasTrade=false, doQuestOwner=false;
int flags = player->displayFlags();
player->clearFlag(P_AFK);
if(!player->ableToDoCommand())
return(0);
if(cmnd->num < 2) {
player->print("Trade what?\n");
return(0);
}
if(cmnd->num < 3) {
player->print("Syntax: trade [num] <item> <monster>\n");
return(0);
}
creature = player->getRoom()->findMonster(player, cmnd, 2);
if(!creature) {
player->print("That is not here.\n");
return(0);
}
if(!creature->flagIsSet(M_TRADES)) {
player->print("You can't trade with %N.\n", creature);
return(0);
}
object = findObject(player, player->first_obj, cmnd);
if(!object) {
player->print("You don't have that.\n");
return(0);
}
if(object->flagIsSet(O_BROKEN_BY_CMD)) {
player->print("Who would want that? It's broken!\n");
return(0);
}
if(!Faction::willDoBusinessWith(player, creature->getPrimeFaction())) {
player->print("%M refuses to do business with you.\n", creature);
return(0);
}
/*
for(i=0; i<5; i++) {
if(creature->carry[i].info.id > 0) {
found = 0;
for(j=0; j< maxitem; j++)
if(creature->carry[i].info == obj_list[j][0].info)
found = 1;
if(!found) {
maxitem++;
obj_list[i][0] = creature->carry[i];
obj_list[i][1] = creature->carry[i+5];
}
}
}
if(!maxitem) {
player->print("%M has nothing to trade.\n", creature);
return(0);
}
*/
for(i=0; i<5; i++) {
// See if they have ANY items to trade - we'll give them a special message if they don't
if(!creature->carry[i].info.id)
continue;
hasTrade = true;
// If this the item we want?
if(creature->carry[i].info != object->info)
continue;
found = true;
invTrade = creature->carry[i];
invResult = creature->carry[i+5];
if(invTrade.numTrade > 1 && invTrade.numTrade != numTrade) {
// If we found a match, but it didnt have all the items we wanted,
// flag this so we can print a message about it later.
badNumTrade = true;
} else {
// If we found an exact match, stop now and ignore any previous partial matches
badNumTrade = false;
break;
}
}
if(!hasTrade) {
player->print("%M has nothing to trade.\n", creature);
return(0);
}
if(!found || !object->info.id || ((object->getShotscur() <= object->getShotsmax()/10) && object->getType() != MISC)) {
player->print("%M says, \"I don't want that!\"\n", creature);
failTrade(player, object, creature);
return(0);
}
if(badNumTrade) {
player->print("%M says, \"That's now how many I want!\"\n", creature);
failTrade(player, object, creature);
return(0);
}
if(!object->isQuestOwner(player)) {
player->print("%M says, \"That doesn't belong to you!\"\n", creature);
failTrade(player, object, creature);
return(0);
}
if(!loadObject((invResult.info), &trade)) {
player->print("%M says, \"Sorry, I don't have anything for you in return for that. Keep it.\"\n", creature);
failTrade(player, object, creature);
return(0);
}
std::list<Object*> objects;
std::list<Object*>::const_iterator oIt;
// If the quest owner is set on the item we are giving, set the quest owner
// on the item(s) we are receiving.
doQuestOwner = (object->getQuestOwner() != "");
if(!prepareItemList(player, objects, trade, creature, true, doQuestOwner, object->getActualBulk())) {
// in the event of failure, prepareItemList() will delete the trade object
failTrade(player, object, creature);
return(0);
}
// are they trying to trade multiple objects
if(numTrade > 1) {
otag *marker=0, *op = player->first_obj;
Object *toDelete=0;
int numObjects=0;
// find the first occurance of the object
while(op && op->obj->info != object->info)
op = op->next_tag;
if(!op) {
player->print("%M gets confused and cannot trade for that right now.\n", creature);
failTrade(player, object, creature, trade);
return(0);
}
// save our position to make this faster
marker = op;
// see if we have enough objects
while(op && op->obj->info == object->info && numObjects != numTrade) {
numObjects++;
if(!op->obj->isQuestOwner(player)) {
player->print("%M says, \"That doesn't belong to you!\"\n", creature);
failTrade(player, object, creature, trade);
return(0);
}
op = op->next_tag;
}
if(numObjects != numTrade) {
player->print("You don't have that many items.\n");
failTrade(player, object, creature, trade, false);
return(0);
}
// if everything is successful, start from the marker and start removing objects
while(numObjects) {
toDelete = marker->obj;
marker = marker->next_tag;
player->delObj(toDelete, true);
creature->addObj(toDelete);
numObjects--;
}
} else {
// the simple, common scenario
player->delObj(object, true);
creature->addObj(object);
}
player->printColor("%M says, \"Thank you for retrieving %s for me.\n", creature, object->getObjStr(player, flags, numTrade).c_str());
player->printColor("For it, I will reward you.\"\n");
broadcast(player->getSock(), player->getRoom(), "%M trades %P to %N.", player,object,creature);
/*
if(trade->flagIsSet(O_LOAD_ALL) && trade->randomObjects.size()) {
// in this case, we give them a lot of items
std::list<CatRef>::const_iterator it;
for(it = trade->randomObjects.begin(); it != trade->randomObjects.end(); it++) {
if(!loadObject(*it, &addTrade))
continue;
endTrade(player, creature, addTrade, doQuestOwner);
}
// they aren't actually getting the "trade" item
delete trade;
} else {
// the simple, common scenario
endTrade(player, creature, trade, doQuestOwner);
}
*/
for(oIt = objects.begin(); oIt != objects.end(); oIt++) {
player->printColor("%M gives you %P in trade.\n", creature, *oIt);
doGetObject(*oIt, player);
}
if(creature->ttalk[0])
player->print("%M says, \"%s\"\n", creature, creature->ttalk);
return(0);
}
//*********************************************************************
// cmdAuction
//*********************************************************************
int cmdAuction(Player* player, cmd* cmnd) {
int i=0, flags=0;
long amnt=0, batch=1, each=0;
Object *object=0;
player->clearFlag(P_AFK);
if(player->getClass() == BUILDER || player->inJail()) {
player->print("You cannot do that.\n");
return(PROMPT);
}
if(cmnd->num < 3) {
player->print("Syntax: auction (item)[which|#num] $(amount) [each|total]\n");
player->print(" auction self $(amount)\n");
return(0);
}
if(player->flagIsSet(P_MISTED) || player->isInvisible()) {
player->print("You must be visible to everyone in order to auction.\n");
return(0);
}
if(player->getLevel() < 7 && !player->isCt()) {
player->print("You must be at least level 7 to auction items.\n");
return(0);
}
if(player->flagIsSet(P_CANT_BROADCAST)) {
player->print("Due to abuse, you no longer have that privilage.\n");
return(0);
}
// use i to point to where the money will be
i = 2;
if(strcmp(cmnd->str[1], "self")) {
object = findObject(player, player->first_obj, cmnd);
if(!object) {
player->print("That object is not in your inventory.\n");
return(0);
}
// batch auction
if(cmnd->str[2][0] == '#') {
i = 3;
batch = atoi(&cmnd->str[2][1]);
if(batch < 1) {
player->print("You must sell atleast 1 item.\n");
return(0);
}
if(batch > 100) {
player->print("That's too many!\n");
return(0);
}
if(cmnd->num==5 && !strcmp(cmnd->str[4], "each"))
each = 1;
else
each = -1;
}
}
if(cmnd->str[i][0] != '$') {
player->print("Syntax: auction (item)[which|#num] $(amount) [each|total]\n");
player->print(" auction self $(amount)\n");
return(0);
}
amnt = atol(&cmnd->str[i][1]);
if(amnt <= 0) {
player->print("A price must be specified to auction.\n");
return(0);
}
if( amnt < 500 &&
!player->checkStaff("Items must be auctioned for at least 500 gold.\n")
)
return(0);
if(player->isEffected("detect-magic"))
flags |= MAG;
std::pair<bstring, Player*> p;
Player* target=0;
foreach(p, gServer->players) {
target = p.second;
if(!target->isConnected())
continue;
if(target->flagIsSet(P_NO_AUCTIONS))
continue;
if(!strcmp(cmnd->str[1], "self")) {
target->printColor("*** %M is auctioning %sself for %ldgp.\n", player, player->himHer(), amnt);
continue;
}
target->printColor("*** %M is auctioning %s for %ldgp%s.\n",
player, object->getObjStr(NULL, flags, batch).c_str(), amnt, each ? each==1 ? " each" : " total" : "");
}
return(0);
}
//*********************************************************************
// setLastPawn
//*********************************************************************
void Player::setLastPawn(Object* object) {
// uniques and quests cannot be reclaimed
if(object && (object->flagIsSet(O_UNIQUE) || object->getQuestnum())) {
delete object;
return;
}
if(lastPawn) {
delete lastPawn;
lastPawn = 0;
}
lastPawn = object;
}
//*********************************************************************
// restoreLastPawn
//*********************************************************************
bool Player::restoreLastPawn() {
if(!getRoom()->flagIsSet(R_PAWN_SHOP) || !getRoom()->flagIsSet(R_DUMP_ROOM)) {
print("You must be in a pawn shop to reclaim items.\n");
return(false);
}
if(!lastPawn) {
print("You have no items to reclaim.\n");
return(false);
}
printColor("You negotiate with the shopkeeper to reclaim %P.\n", lastPawn);
if(lastPawn->refund[GOLD] > coins[GOLD]) {
print("You don't have enough money to reclaim that item!\n");
return(false);
}
if(!Unique::canGet(this, lastPawn) || !Lore::canHave(this, lastPawn)) {
print("You currently cannot reclaim that limited object.\n");
return(false);
}
coins.sub(lastPawn->refund);
printColor("You give the shopkeeper %d gold and reclaim %P.\n", lastPawn->refund[GOLD], lastPawn);
lastPawn->refund.zero();
doGetObject(lastPawn, this, true, true);
lastPawn = 0;
return(true);
}
//*********************************************************************
// cmdReclaim
//*********************************************************************
int cmdReclaim(Player* player, cmd* cmnd) {
player->restoreLastPawn();
return(0);
}
//********************************************************************
// doHaggling
//********************************************************************
// This function is called whenever money changes hands for a player. It is specifically
// written for clerics of the god Jakar, but is being designed so it can be applied to
// all players in general if desired. It will make a player haggle to get more money if
// selling, and pay less money if buying, up to 30% discount. -TC
void Creature::doHaggling(Creature *vendor, Object* object, int trans) {
int chance=0, npcVendor=0, roll=0, discount=0, lck=0;
float amt=0.0, percent=0.0;
long modAmt = 0, val = 0;
if(vendor)
npcVendor = 1;
if(trans != 0 && trans != 1) {
broadcast(::isDm, "^g*** Invalid function call: doHaggle. No transaction type specified. Aborting.");
return;
}
/* Remove this to allow all players to haggle */
if(!(isCt() ||
getClass() == BARD ||
(getClass() == CLERIC && getDeity() == JAKAR)) )
return;
if(chkSave(LCK, 0, -1))
lck = 25;
else
lck = 0;
if(npcVendor) {
chance = 50 + lck + 5*((int)vendor->getLevel() - (int)getLevel());
} else
chance = MIN(95,(getLevel()*2 + saves[LCK].chance/10));
if(trans == SELL)
val = MIN(MAXPAWN,object->value[GOLD]/2);
else
val = object->value[GOLD]/2;
roll = mrand(1,100);
if(isCt()) {
print("haggle chance: %d\n", chance);
print("roll: %d\n", roll);
}
if(roll <= chance) {
discount = MAX(1,mrand(getLevel()/2, 1+getLevel()));
percent = (float)(discount);
percent /= 100;
amt = (float)val;
amt *= percent;
modAmt = (long)amt;
if(modAmt >= 10000)
modAmt += (mrand(1,10000)-1);
else if(modAmt >= 100)
modAmt += (mrand(1,1000)-1);
else if(modAmt >= 100)
modAmt += (mrand(1,100)-1);
else if(modAmt >= 10)
modAmt += (mrand(1,10)-1);
if(isCt()) {
print("discount: %d%\n", discount);
print("percent: %f, amt: %f\n", percent, amt);
print("modAmt: %ld\n", modAmt);
}
if(modAmt) {
if(npcVendor)
broadcast(getSock(), getRoom(), "%M haggles over prices with %N.", this, vendor);
switch(trans) {
case BUY:
printColor("^yYour haggling skills saved you %ld gold coins.\n", modAmt);
coins.add(modAmt, GOLD);
object->refund.set(object->value);
object->refund.sub(modAmt, GOLD);
break;
case SELL:
printColor("^yYour haggling skills gained you an extra %ld gold coins.\n", modAmt);
object->refund.set(object->value);
object->refund.add(modAmt, GOLD);
coins.add(modAmt, GOLD);
break;
default:
break;
}
}
}
}