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