/** * @file shop.c * @ingroup common * * Shop code * * This shop system is a completely re-coded one for CircleMUD^2, but was * based around the system originally created for CircleMUD by Jeff Fink. * * The largest change with this code is it no longer works off of the * inventory of the keeper mobile. * * @author Greg Buxton * * @par Copyright: * Copyright (C) 2006 Geoff Davis <geoff@circlemudsquared.org><br> * Greg Buxton <greg@circlemudsquared.org> * * @par * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University<br> * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * * @par * All rights reserved. See license.doc for complete information. * * @package cs * @version 1.0 */ #include "base.h" #include "conf.h" #include "sysdep.h" #include "structs.h" #include "comm.h" #include "command.h" #include "handler.h" #include "db.h" #include "interpreter.h" #include "utils.h" #include "shop.h" #include "constants.h" #include "log.h" #include "zone.h" #include "room.h" #include "item.h" #include "room.h" #include "mobile.h" /* * Internal Functions */ shopData_t *shopData_findInZone(zoneData_t *zone, int number); shopData_t *shopData_new(zoneData_t *zone, int number); void shopData_free(void *s); void shop_addProducingItemByString(shopData_t *shop, char *sItem); void shop_alterItemInventoryCount(shopData_t *shop, itemData_t *item, int change); void shop_addTypeByString(shopData_t *shop, char *sType); void shop_addRoomByString(shopData_t *shop, char *sRoom); /* * External Variables */ extern timeInfoData_t time_info; /* * Internal Variables */ /** Shop Trades type string values */ const char *trades_with[] = { "NOGOOD", "NOEVIL", "NONEUTRAL", "NOMAGIC_USER", "NO_CLERIC", "NOTHIEF", "NOWARRIOR", "\n" }; /* ************************************************************************ */ /* Save Shop Data to DAO */ /* ************************************************************************ */ /** * Save the main shop data to DAO * * @param parentDao DAO to save shop data to * @param shop Shop who's data is being saved * @return none */ void shopMain_toDao(daoData_t *parentDao, shopData_t *shop) { dao_newScalar(parentDao, "keeper", "%s:%d", (shop->keeper)->zone->keyword, (shop->keeper)->vnum); dao_newScalar(parentDao, "buyProfit", "%f", shop->profitForSale); dao_newScalar(parentDao, "sellProfit", "%f", shop->profitForPurchase); dao_newScalar(parentDao, "hourOpen1", "%d", shop->open1); dao_newScalar(parentDao, "hourClose1", "%d", shop->close1); dao_newScalar(parentDao, "hourOpen2", "%d", shop->open2); dao_newScalar(parentDao, "hourClose2", "%d", shop->close2); } /** * Save shop messages to DAO * * @param parentDao DAO to save shop messages to * @param shop Shop who's data is being saved * @return none */ void shopMessages_toDao(daoData_t *parentDao, shopData_t *shop) { daoData_t *subDao = NULL; subDao = dao_newChild(parentDao, "messgaes"); if (subDao == NULL) { log("shopMessages_toDao(): dao_newChild() failed. Aborting."); return; } dao_newScalar(subDao, "shopMissingItem", "%s", shop->shopMissingItem); dao_newScalar(subDao, "buyerMissingItem", "%s", shop->buyerMissingItem); dao_newScalar(subDao, "noBuy", "%s", shop->noBuy); dao_newScalar(subDao, "shopCantAfford", "%s", shop->shopCantAfford); dao_newScalar(subDao, "playerCantAfford", "%s", shop->playerCantAfford); dao_newScalar(subDao, "itemSold", "%s", shop->itemSold); dao_newScalar(subDao, "itemBought", "%s", shop->itemBought); } /** * Save shop producing items to DAO * * @param parentDao DAO to save shop items to * @param shop Shop who's data is being saved * @return none * * @todo Recode so producing[] is item pointers */ void shopItems_toDao(daoData_t *parentDao, shopData_t *shop) { daoData_t *subDao = NULL; shopItems_t *temp; int i = 0; if (shop->producing) { temp = shop->producing; while (temp) { char buf[MAX_STRING_LENGTH] = { "\0" }; if (subDao == NULL) { subDao = dao_newChild(parentDao, "items"); if (subDao == NULL) { log("shopItems_toDao(): dao_newChild() failed. Aborting."); return; } } i++; /* Save the entry */ snprintf(buf, sizeof(buf), "%d", i + 1); if (temp->item != NULL) { dao_newScalar(subDao, buf, "%s:%d", temp->item->zone->keyword, temp->item->vnum); } else { dao_newScalar(subDao, buf, "%s", temp->sItem); } temp = temp->next; } } } /** * Save shop trades-in-types to DAO * * @param parentDao DAO to save shop types to * @param shop Shop who's data is being saved * @return none */ void shopTypes_toDao(daoData_t *parentDao, shopData_t *shop) { if (shop == NULL) { log("shopTypes_toDao(): Invalid shopData_t 'shop'."); } else if (parentDao == NULL) { log("shopTypes_toDao(): Invalid daoData_t 'parentDao'."); } else if (shop->type != NULL) { daoData_t *subDao = NULL; shopBuyData_t *temp = NULL; int i = 0; /* Create the DAO container */ subDao = dao_newChild(parentDao, "types"); if (subDao == NULL) { log("shopTypes_toDao(): dao_newChild() failed. Aborting."); return; } for (temp = shop->type; temp; temp = temp->next) { char buf[MAX_STRING_LENGTH] = { "\0" }; snprintf(buf, sizeof(buf), "%d", ++i); dao_newScalar(subDao, buf, "%s", item_types[(temp->type)]); } } } /** * Save shop rooms to DAO * * @param parentDao DAO to save shop rooms to * @param shop Shop who's data is being saved * @return none */ void shopRooms_toDao(daoData_t *parentDao, shopData_t *shop) { daoData_t *subDao = NULL; shopRooms_t *temp = NULL; int i = 0; if (shop->inRoom) { subDao = dao_newChild(parentDao, "rooms"); if (subDao == NULL) { log("shopRooms_toDao(): dao_newChild() failed. Aborting."); return; } for (temp = shop->inRoom; temp; temp = temp->next) { char buf[MAX_STRING_LENGTH] = { "\0" }; i++; snprintf(buf, sizeof(buf), "%d", i + 1); dao_newScalar(subDao, buf, "%s:%d", temp->room->zone->keyword, temp->room->number); } } } /** * Save shop trade-flags to DAO * * @param parentDao DAO to save shop trade-flags to * @param shop Shop who's data is being saved * @return none */ void shopTrades_toDao(daoData_t *parentDao, shopData_t *shop) { daoData_t *subDao = NULL; if (shop->flags != 0) { subDao = dao_newChild(parentDao, "flags"); dao_newScalar(subDao, "NOGOOD", "%s", YESNO(IS_SET(shop->flags, TRADE_NOGOOD))); dao_newScalar(subDao, "NOEVIL", "%s", YESNO(IS_SET(shop->flags, TRADE_NOEVIL))); dao_newScalar(subDao, "NONEUTRAL", "%s", YESNO(IS_SET(shop->flags, TRADE_NONEUTRAL))); dao_newScalar(subDao, "NOMAGIC_USER", "%s", YESNO(IS_SET(shop->flags, TRADE_NOMAGIC_USER))); dao_newScalar(subDao, "NOCLERIC", "%s", YESNO(IS_SET(shop->flags, TRADE_NOCLERIC))); dao_newScalar(subDao, "NOTHIEF", "%s", YESNO(IS_SET(shop->flags, TRADE_NOTHIEF))); dao_newScalar(subDao, "NOWARRIOR", "%s", YESNO(IS_SET(shop->flags, TRADE_NOWARRIOR))); } } /** * Save a shop to DAO * * This is broken out into the sub functions above for readability. * * @param parentDao DAO to save shop to * @param shop Shop being saved * @return none */ void shopData_toDao(daoData_t *parentDao, shopData_t *shop) { if (shop == NULL) { log("shopData_toDao(): invalid 'shop' shopData_t."); } else if (parentDao == NULL) { log("shopData_toDao(): invalid 'parentDao' daoData_t."); } else { /* Declare some dao pointers */ daoData_t *shopDao = NULL; /* Create DAO for the shop */ shopDao = dao_newChild(parentDao, "%d", shop->vnum); if (shopDao == NULL) { log("shopData_toDao(): dao_newChild() failed. Aborting."); return; } /* Main shop data */ shopMain_toDao(shopDao, shop); /* Messages */ shopMessages_toDao(shopDao, shop); /* Shop Producing Items */ shopItems_toDao(shopDao, shop); /* Item Types the shop trades in */ shopTypes_toDao(shopDao, shop); /* Shop rooms */ shopRooms_toDao(shopDao, shop); /* Trades */ shopTrades_toDao(shopDao, shop); } } /* ************************************************************************ */ /* Load Shop Data from DAO */ /* ************************************************************************ */ /** * Load a shop's messages from their DAO representation * * @param shop Shop we're loading messages for * @param messageDao Messages in DAO format * @return none */ void shopMessages_fromDao(shopData_t *shop, daoData_t *messageDao) { if (shop == NULL) { log("shopMessages_fromDao(): Invalid shopData_t 'shop'."); } else if (messageDao != NULL) { const char *shopMissingItem = dao_queryString(messageDao, "shopMissingItem", NULL); const char *buyerMissingItem = dao_queryString(messageDao, "buyerMissingItem", NULL); const char *noBuy = dao_queryString(messageDao, "noBuy", NULL); const char *shopCantAfford = dao_queryString(messageDao, "shopCantAfford", NULL); const char *playerCantAfford = dao_queryString(messageDao, "playerCantAfford", NULL); const char *itemSold = dao_queryString(messageDao, "itemSold", NULL); const char *itemBought = dao_queryString(messageDao, "itemBought", NULL); if (shopMissingItem) shop->shopMissingItem = strdup(shopMissingItem); if (buyerMissingItem) shop->buyerMissingItem = strdup(buyerMissingItem); if (noBuy) shop->noBuy = strdup(noBuy); if (shopCantAfford) shop->shopCantAfford = strdup(shopCantAfford); if (playerCantAfford) shop->playerCantAfford = strdup(playerCantAfford); if (itemSold) shop->itemSold = strdup(itemSold); if (itemBought) shop->itemBought = strdup(itemBought); } } /** * Load the items sold in a shop * * @param shop Shop to load producing items for * @param itemsDao DAO representation of items * @return none */ void shopItems_fromDao(shopData_t *shop, daoData_t *itemsDao) { if (shop == NULL) { log("shopItems_fromDao(): Invalid shopData_t 'shop'."); } else if (itemsDao != NULL) { daoData_t *subDao = NULL; /* Load the data */ for (subDao = itemsDao->children; subDao; subDao = subDao->next) { const char *oStr = dao_valueString(subDao, NULL); if (oStr != NULL) { shop_addProducingItemByString(shop, (char *)oStr); } } } } /** * Load the item types a shop trades in * * @param shop Shop to load trades types for * @param typesDao DAO representation of types * @return none */ void shopTypes_fromDao(shopData_t *shop, daoData_t *typesDao) { if (shop == NULL) { log("shopTypes_fromDao(): Invalid shopData_t 'shop'."); } else if (typesDao != NULL) { daoData_t *subDao = NULL; for (subDao = typesDao->children; subDao; subDao = subDao->next) { const char *tStr = dao_valueString(subDao, NULL); if (tStr != NULL) { shop_addTypeByString(shop, (char *)tStr); } } } } /** * Load the rooms a shop functions in * * @param shop Shop to load rooms for * @param roomsDao DAO representation of rooms * @return none */ void shopRooms_fromDao(shopData_t *shop, daoData_t *roomsDao) { if (shop == NULL) { log("shopRooms_fromDao(): Invalid shopData_t 'shop'."); } else if (roomsDao != NULL) { daoData_t *subDao = NULL; for (subDao = roomsDao->children; subDao; subDao = subDao->next) { const char *rStr = dao_valueString(subDao, NULL); if (rStr != NULL) { shop_addRoomByString(shop, (char *)rStr); } } } } /** * Load a shop from it's DAO representation * * @param zone Zone we're loading shops in * @param shopDao DAO representation of the shop to load * @return none */ void shopData_fromDao(zoneData_t *zone, daoData_t *shopDao) { if (zone == NULL) { log("shopData_fromDao(): Invalid zoneData_t 'zone'."); } else if (shopDao == NULL) { log("shopData_fromDao(): Invalid daoData_t 'shopDao'."); } else { shopData_t *shop = shopData_new(zone, dao_keyInt(shopDao, -1)); if (shop != NULL) { /* Temp var holding the string ID of the keeper */ const char *sKeeper = dao_queryString(shopDao, "keeper", NULL); /* Let's load the main shop data */ if (sKeeper) shop->sKeeper = strdup(sKeeper); shop->profitForSale = dao_queryDouble(shopDao, "buyProfit", 1); shop->profitForPurchase = dao_queryDouble(shopDao, "seeProfit", 1); shop->open1 = dao_queryInt(shopDao, "hourOpen1", 0); shop->open2 = dao_queryInt(shopDao, "hourOpen2", 0); shop->close1 = dao_queryInt(shopDao, "hourClose1", 0); shop->close2 = dao_queryInt(shopDao, "hourClose2", 0); /* Load messages */ shopMessages_fromDao(shop, dao_query(shopDao, "messages")); /* Load "producing" items */ shopItems_fromDao(shop, dao_query(shopDao, "items")); /* Load the types */ shopTypes_fromDao(shop, dao_query(shopDao, "types")); /* Load the rooms */ shopRooms_fromDao(shop, dao_query(shopDao, "rooms")); /* Load the trades-with flags */ shop->flags = dao_queryBits(shopDao, "flags", trades_with, 0); } } } /** * Load all shops in a zone DAO file * * @param zone Zone we're loading shops in * @param shopListDao DAO data to load the shops from * @return none */ void shopData_loadShopsInZone(zoneData_t *zone, daoData_t *shopListDao) { if (zone == NULL) { log("shopData_loadShopsInZone(): Invalid zoneData_t 'zone'."); } else { if (shopListDao != NULL) { daoData_t *entDao = NULL; for (entDao = shopListDao->children; entDao; entDao = entDao->next) { shopData_fromDao(zone, entDao); } } } } /* ************************************************************************ */ /* General Shop Functions */ /* ************************************************************************ */ /** * Free a shop's strings * * @param shop Shop to free strings on * @return none */ void shopData_freeStrings(shopData_t *shop) { if (shop == NULL) { log("shopData_freeStrings(): Invalid shopData_t 'shop'."); } else { /* Messages */ if (shop->shopMissingItem) free(shop->shopMissingItem); if (shop->buyerMissingItem) free(shop->buyerMissingItem); if (shop->shopCantAfford) free(shop->shopCantAfford); if (shop->playerCantAfford) free(shop->playerCantAfford); if (shop->noBuy) free(shop->noBuy); if (shop->itemSold) free(shop->itemSold); if (shop->itemBought) free(shop->itemBought); /* Shopkeeper string */ if (shop->sKeeper) free(shop->sKeeper); shop->shopMissingItem = NULL; shop->buyerMissingItem = NULL; shop->shopCantAfford = NULL; shop->playerCantAfford = NULL; shop->noBuy = NULL; shop->itemSold = NULL; shop->itemBought = NULL; shop->sKeeper = NULL; } } /** * Free a shop's producing items * * @param shop Shop to free producing items on * @return none */ void shopData_freeProducingItems(shopData_t *shop) { if (shop == NULL) { log("shopData_freeProducingItems(): Invalid shopData_t 'shop'."); } else if (shop->producing != NULL) { shopItems_t *temp = NULL; while (shop->producing) { temp = shop->producing; shop->producing = temp->next; if (temp->sItem) free(temp->sItem); temp->item = NULL; temp->next = NULL; free(temp); temp = NULL; } } } /** * Free a shop's rooms * * @param shop Shop to free the rooms on * @return none */ void shopData_freeRooms(shopData_t *shop) { if (shop == NULL) { log("shopData_freeRooms(): Invalid shopData_t 'shop'."); } else if (shop->inRoom != NULL) { shopRooms_t *temp = NULL; while (shop->inRoom) { temp = shop->inRoom; shop->inRoom = temp->next; if (temp->sRoom) free(temp->sRoom); temp->room = NULL; temp->next = NULL; free(temp); temp = NULL; } } } /** * Free the trades types list * * @param shop Shop to free trade types on * @return none */ void shopData_freeTrades(shopData_t *shop) { if (shop == NULL) { log("shopData_freeTrades(): Invalid shopData_t 'shop'."); } else if (shop->type != NULL) { shopBuyData_t *temp = NULL; while (shop->type) { temp = shop->type; shop->type = temp->next; temp->next = NULL; free(temp); temp = NULL; } } } /** * Free the elements of a shop, but not the shop's allocation itself * * @param shop Shop to clear * @return none */ void shopData_clear(shopData_t *shop) { if (shop == NULL) { log("shopData_clear(): Invalid shopData_t 'shop'."); } else { /* Free the strings on the shop */ shopData_freeStrings(shop); /* Free shop trade types */ shopData_freeTrades(shop); /* Free producing items */ shopData_freeProducingItems(shop); /* Free rooms */ shopData_freeRooms(shop); } } /** * Add a new shop to a zone, by number * * This function will re-use an existing memory entry if one already exists * for that number. This prevents pointers from breaking. * * @param zone Zone to create the new shop in * @param number Number of the shop to create * @return pointer to the new shop entry */ shopData_t *shopData_new(zoneData_t *zone, int number) { shopData_t *shop = NULL; if (zone == NULL) { log("shopData_new(): Invalid zoneData_t 'zone'."); } else if (number < 0) { log("shopData_new(): Invalid int 'number'."); } else { /* If the zone's shops hashMap doesn't exist, create it */ if (zone->shops == NULL) { zone->shops = hashMap_create(0, hashVnum, NULL, shopData_free); if (zone->shops == NULL) { log("shopData_new(): NULL returned from hashMap_create(). Aborting."); exit(1); } } /* Attempt to get an item with this number from the zone */ shop = shopData_findInZone(zone, number); if (shop == NULL) { CREATE(shop, shopData_t, 1); shop->vnum = number; hashMap_insert(zone->shops, &shop->vnum, shop); } else { /* Free the elements of the shop, but not it's pointer */ shopData_clear(shop); } /* Set the zone */ shop->zone = zone; } return (shop); } /** * List a single shop to a buffer * * @param buf Buffer to list shop in * @param shop Shop to list * @return none * * @todo Cleanup the pointers with alias wrappers */ void shop_addToList(char *buf, shopData_t *shop) { if (shop == NULL) { log("shop_addToList(): Invalid shopData_t 'shop'."); } else { snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%15s:%-7d -- Keeper: %s (%s:%d)\r\n", shop->zone->keyword, shop->vnum, shop->keeper->player.short_descr, shop->keeper->zone->keyword, shop->keeper->vnum); } } /** * List all shops in a zone into a buffer * * @param buf Buffer to generate list in * @param zone Zone who's shops to list * @return none */ void shops_listInZone(char *buf, zoneData_t *zone) { if (zone == NULL) { log("shops_listInZone(): Invalid zoneData_t 'zone'."); } else if (zone->shops != NULL) { hashMapIterator_t *iter = hashMapIterator_create(zone->shops); while (iter && hashMapIterator_getValue(iter)) { shopData_t *shop = (shopData_t *)hashMapIterator_getValue(iter); if (shop != NULL) { shop_addToList(buf, shop); } shop = NULL; hashMapIterator_next(iter); } hashMapIterator_free(iter); } } /** * Find a shop in a zone by number * * @param zone Zone to search for the shop in * @param number Numer of the shop to find * @return pointer to the shop or NULL */ shopData_t *shopData_findInZone(zoneData_t *zone, int number) { return (shopData_t*) hashMap_getValue(zone->shops, &number, NULL); } /** * Free a shop * * This function takes a void pointer for use in the hashMap * * @param s Shop to be freed, void pointer * @return none */ void shopData_free(void *s) { shopData_t *shop = (shopData_t *)(s); if (shop != NULL) { /* Free the parts of the shop */ shopData_clear(shop); /* Free the shop itself */ free(shop); shop = NULL; } } /** * Add a producing item to a shop by string ID * * This function is used in shop loading from DAO. * In-game (OLC) would pass an item pointer to: shop_addProducingItem() * * @param shop Shop to add the producing item to * @param sItem String ID of item * @return none * * @todo Update to linkedList_t system */ void shop_addProducingItemByString(shopData_t *shop, char *sItem) { if (shop == NULL) { log("shop_addProducingItemByString(): Invalid shopData_t 'shop'."); } else if (sItem == NULL || *sItem == '\0') { log("shop_addProducingItemByString(): Invalid char 'sItem'."); } else { shopItems_t *new = NULL, *temp = NULL; /* Create and populate */ CREATE(new, shopItems_t, 1); new->sItem = strdup(sItem); new->item = NULL; new->next = NULL; if (shop->producing) { temp = shop->producing; while (temp && temp->next) { temp = temp->next; } temp->next = new; } else { shop->producing = new; } } } /** * Add a trade type to a shop by string * * This function does a search block on item_types and adds * a new entry to the linked list if that type was found. * * @param shop Shop to add type to * @param sType String value of type * @return none * * @todo Update to linkedList_t system */ void shop_addTypeByString(shopData_t *shop, char *sType) { if (shop == NULL) { log("shop_addTradeTypeByString(): Invalid shopData_t 'shop'."); } else if (sType == NULL || *sType == '\0') { log("shop_addTradeTypeByString(): Invalid char 'sType'."); } else { int tNum = search_block(sType, item_types, TRUE); if (tNum != -1) { shopBuyData_t *new = NULL, *temp = NULL; CREATE(new, shopBuyData_t, 1); new->type = tNum; new->next = NULL; if (shop->type) { temp = shop->type; while (temp && temp->next) { temp = temp->next; } temp->next = new; } else { shop->type = new; } } } } /** * Add a room to a shop by string * * This is used in loading, for OLC later add a version to add by pointer * * @param shop Shop to add the room to * @param sRoom String value of room * @return none * * @todo Update to linkedList_t system */ void shop_addRoomByString(shopData_t *shop, char *sRoom) { if (shop == NULL) { log("shop_addRoomByString(): Invalid shopData_t 'shop'."); } else if (sRoom == NULL || *sRoom == '\0') { log("shop_addRoomByString(): Invalid char 'sRoom'."); } else { shopRooms_t *new = NULL, *temp = NULL; CREATE(new, shopRooms_t, 1); new->sRoom = strdup(sRoom); new->room = NULL; new->next = NULL; if (shop->inRoom) { temp = shop->inRoom; while (temp && temp->next) { temp = temp->next; } temp->next = new; } else { shop->inRoom = new; } } } /** * Add a producing item to a shop * * @param shop Shop to add the producing item to * @param item Item to add (pointer to prototype) * @return none */ void shop_addProducingItem(shopData_t *shop, itemData_t *item) { if (shop == NULL) { log("shop_addProducingItem(): Invalid shopData_t 'shop'."); } else if (item == NULL) { log("shop_addProducingItem(): Invalid itemData_t 'item'."); } else { shopItems_t *new = NULL, *temp = NULL; /* Create and populate */ CREATE(new, shopItems_t, 1); new->sItem = NULL; new->item = item; new->next = NULL; if (new) { if (shop->producing) { temp = shop->producing; while (temp && temp->next) { temp = temp->next; } temp->next = new; } else { shop->producing = new; } } } } /** * Check if a shop produces an item * * @param shop Shop to check * @param item Item to check for * @return TRUE or FALSE */ bool shop_hasItemInProducing(shopData_t *shop, itemData_t *item) { if (shop == NULL) { log("shop_hasItemInProducing(): Invalid shopData_t 'shop'."); } else if (item == NULL) { log("shop_hasItemInProducing(): Invalid itemData_t 'item'."); } else if (shop->producing) { shopItems_t *temp = NULL; temp = shop->producing; while (temp) { if (temp->item && (temp->item == item || (item->prototype && temp->item == item->prototype))) { return (TRUE); } temp = temp->next; } } return (FALSE); } /** * Check if a shop trades in an item * * @param shop Shop to check * @param item Item to see if shop trades in * @return TRUE or FALSE */ bool shop_tradesInItem(shopData_t *shop, itemData_t *item) { if (shop == NULL) { log("shop_tradesInItem(): Invalid shopData_t 'shop'."); } else if (item == NULL) { log("shop_tradesInItem(): Invalid itemData_t 'item'."); } else { shopBuyData_t *temp = NULL; temp = shop->type; while (temp) { if (temp->type == GET_ITEM_TYPE(item)) { return (TRUE); } temp = temp->next; } } return (FALSE); } /** * Check if shopkeeper is in a room it can operate in * * @param keeper Shopkeeper * @return TRUE or FALSE */ bool shop_keeperInShopRoom(charData_t *keeper) { if (keeper == NULL) { log("shop_KeeperInShopRoom(): Invalid charData_t 'keeper'."); } else { if (keeper) { if (keeper->shop->inRoom == NULL) { return (TRUE); } else { shopRooms_t *temp = NULL; temp = keeper->shop->inRoom; while(temp) { if (keeper->room == temp->room) { return (TRUE); } temp = temp->next; } } } } return (FALSE); } /** * Check if an item is in a shop's inventory list * * @param shop Shop to check * @param item Item to check for * @return TRUE or FALSE */ bool shop_hasItemInInventory(shopData_t *shop, itemData_t *item) { if (shop == NULL) { log("shop_hasItemInInventory(): Invalid shopData_t 'shop'."); } else if (item == NULL) { log("shop_hasItemInInventory(): Invalid itemData_t 'item'."); } else if (shop->inventory) { shopInventory_t *temp = NULL; temp = shop->inventory; while (temp) { if (temp->item && (temp->item == item || (item->prototype && temp->item == item->prototype))) { return (TRUE); } temp = temp->next; } } return (FALSE); } /** * Add an inventory item to a shop * * @param shop Shop to add the inventory item to * @param item Item to add * @return none */ void shop_addInventoryItem(shopData_t *shop, itemData_t *item) { if (shop == NULL) { log("shop_addInventoryItem(): Invalid shopData_t 'shop'."); } else if (item == NULL) { log("shop_addInventoryItem(): Invalid itemData_t 'item'."); } else if (shop_hasItemInInventory(shop, item) == TRUE) { shop_alterItemInventoryCount(shop, item, 1); } else { shopInventory_t *new = NULL, *temp = NULL; CREATE(new, shopInventory_t, 1); if (item->prototype) { new->item = item->prototype; } else { new->item = item; } new->count = 1; new->next = NULL; if (shop->inventory) { temp = shop->inventory; while (temp && temp->next) { temp = temp->next; } temp->next = new; } else { shop->inventory = new; } } } /** * Remove an inventory item from a shop * * @param shop Shop to remove the inventory item from * @param item Item to remove * @return none */ void shop_removeInventoryItem(shopData_t *shop, itemData_t *item) { if (shop == NULL) { log("shop_removeInventoryItem(): Invalid shopData_t 'shop'."); } else if (item == NULL) { log("shop_removeInventoryItem(): Invalid itemData_t 'item'."); } else if (shop_hasItemInInventory(shop, item) == TRUE) { shopInventory_t *temp = NULL, *last = NULL; temp = shop->inventory; while (temp) { if (temp->item && (temp->item == item || (item->prototype && temp->item == item->prototype))) { if (last == NULL) { shop->inventory = temp->next; } else { last->next = temp->next; } temp->next = NULL; temp->item = NULL; free(temp); temp = NULL; return; } last = temp; temp = temp->next; } } } /** * Update the count of an item in a shop's inventory * * @param shop Shop to check * @param item Item to change the count of * @param change Amount to change inventory by * @return none */ void shop_alterItemInventoryCount(shopData_t *shop, itemData_t *item, int change) { if (shop == NULL) { log("shop_alterItemInventoryCount(): Invalid shopData_t 'shop'."); } else if (item == NULL) { log("shop_alterItemInventoryCount(): Invalid itemData_t 'item'."); } else if (shop_hasItemInInventory(shop, item) == TRUE) { shopInventory_t *temp = NULL; temp = shop->inventory; while (temp) { if (temp->item && (temp->item == item || (item->prototype && temp->item == item->prototype))) { temp->count += change; if (temp->count <= 0) { shop_removeInventoryItem(shop, item); } return; } temp = temp->next; } } else { shop_addInventoryItem(shop, item); } } /** * Resolve item pointers in the producing items list * * @param shop Shop to resolve producing item pointers on * @return none */ void shopData_resolveItemPointers(shopData_t *shop) { if (shop == NULL) { log("shopData_resolveItemPointers(): Invalid shopData_t 'shop'."); } else { if (shop->producing) { shopItems_t *temp = NULL; temp = shop->producing; while (temp) { if (temp->item == NULL && temp->sItem && *temp->sItem) { temp->item = itemData_find(temp->sItem); if (temp->item == NULL) { log("shopData_resolveItemPointers(): Unable to find item '%s'.", temp->sItem); } } temp = temp->next; } } } } /** * Resolve room pointers in the inRoom rooms list * * @param shop Shop to resolve the room pointers on * @return none */ void shopData_resolveRoomPointers(shopData_t *shop) { if (shop == NULL) { log("shopData_resolveRoomPointers(): Invalid shopData_t 'shop'."); } else { if (shop->inRoom) { shopRooms_t *temp = NULL; temp = shop->inRoom; while (temp) { if (temp->room == NULL && temp->sRoom && *temp->sRoom) { temp->room = roomData_find(temp->sRoom); } if (temp->room == NULL) { log("shopData_resolveRoomPointers(): Unable to find room '%s'.", temp->sRoom); } temp = temp->next; } } } } /** * Resolve pointers in a shop * * @param shop Shop to resolve * @return none */ void shopData_resolvePointers(shopData_t *shop) { if (shop == NULL) { log("shopData_resolvePointers(): Invalid shopData_t 'shop'."); } else { /* Resolve the keeper */ if (shop->sKeeper && *shop->sKeeper) { /* Set the pointer to the keeper */ shop->keeper = charData_findMobilePrototype(shop->sKeeper); /* Set the keeper's shop pointer */ shop->keeper->shop = shop; } /* Resolve the producing items */ shopData_resolveItemPointers(shop); /* Resolve the operating rooms */ shopData_resolveRoomPointers(shop); } } /** * Pass all shops in a zone to shopData_resolvePointers() * * @param zone Zone to iterate through shops in * @return none */ void shopData_resolveInZone(zoneData_t *zone) { if (zone != NULL && zone->shops != NULL) { hashMapIterator_t *iter = hashMapIterator_create(zone->shops); while (iter && hashMapIterator_getValue(iter)) { shopData_t *shop = (shopData_t *)hashMapIterator_getValue(iter); /* Pass the shop */ shopData_resolvePointers(shop); hashMapIterator_next(iter); } hashMapIterator_free(iter); } } /** * Adjust price of an item based on charisma * * Shopkeeper higher charisma (markup) * ^ 15: 1.2142 14: 1.2000 13: 1.1857 12: 1.1714 11: 1.1571 * | 10: 1.1428 9: 1.1285 8: 1.1142 7: 1.1000 6: 1.0857 * | 5: 1.0714 4: 1.0571 3: 1.0428 2: 1.0285 1: 1.0142 * + 0: 1.0000 * | -1: 0.9858 -2: 0.9715 -3: 0.9572 -4: 0.9429 -5: 0.9286 * | -6: 0.9143 -7: 0.9000 -8: 0.8858 -9: 0.8715 -10: 0.8572 * v -11: 0.8429 -12: 0.8286 -13: 0.8143 -14: 0.8000 -15: 0.7858 * Player higher charisma (discount) * * Most mobiles have 11 charisma so an 18 charisma player would get a 10% * discount beyond the basic price. That assumes they put a lot of points * into charisma, because on the flip side they'd get 11% inflation by * having a 3. * * @param item Item who's price is being modified * @param keeper Shopkeeper * @param buyer Character buying the item * @return modified price */ int shop_sellPrice(itemData_t *item, charData_t *keeper, charData_t *buyer) { return (int) (item->cost * (keeper->shop)->profitForSale) * (1 + (GET_CHA(keeper) - GET_CHA(buyer)) / (float)70); } /** * Reverse of shop_sellPrice(). * * When the shopkeeper is buying, we reverse the discount. Also make sure * we don't buy for more than we sell for, to prevent infinite money-making. * * @param item Item who's price is being modified * @param keeper Shopkeeper * @param buyer Character buying the item * @return modified price */ int shop_buyPrice(itemData_t *item, charData_t *keeper, charData_t *seller) { int buyPrice = (int) (item->cost * (keeper->shop)->profitForPurchase) * (1 - (GET_CHA(keeper) - GET_CHA(seller)) / (float)70); int sellPrice = shop_sellPrice(item, keeper, seller); if (buyPrice > sellPrice) buyPrice = sellPrice; return (buyPrice); } /** * Find an item in a shop by it's # (from LIST) * * @param shop Shop to find the item in * @param itemNum Number of the item to return * @return Pointer to the item (prototype) or NULL */ itemData_t *shop_findItemByNumber(shopData_t *shop, int itemNum) { if (shop == NULL) { log("shop_findItemByNumber(): Invalid shopData_t 'shop'."); } else if (itemNum > 0) { shopItems_t *tA = NULL; shopInventory_t *tB = NULL; int n = 0; if (shop->producing != NULL) { tA = shop->producing; while (tA) { n++; if (n == itemNum) return (tA->item); tA = tA->next; } } if (shop->inventory != NULL) { tB = shop->inventory; while (tB) { n++; if (n == itemNum) return (tB->item); tB = tB->next; } } } return (NULL); } /** * Add a shop's producing items to a buffer for LIST * * @param buf Buffer to add output to * @param ch Character who will receive the list * @param shop Shop to add items from * @param number Shop item number we're up to * @return Number of items listed to the buffer */ int shopData_addProducingItemsToBuf(char *buf, charData_t *ch, shopData_t *shop, int *num) { int numAdded = 0; if (buf == NULL) { log("shopData_addProducingItemsToBuf(): Invalid char 'buf'."); } else if (shop == NULL) { log("shopData_addProducingItemsToBuf(): Invalid shopData_t 'shop'."); } else { shopItems_t *temp = NULL; if (shop->producing != NULL) { temp = shop->producing; while(temp) { if (temp->item) { (*num)++; numAdded++; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%3d) Unlimited %-50s %7d\r\n", (*num), temp->item->shortDescription, shop_sellPrice(temp->item, shop->keeper, ch)); } temp = temp->next; } } } return (numAdded); } /** * Add a shop's inventory items to a buffer for LIST * * @param buf Buffer to add output to * @param ch Character who will receive the list * @param shop Shop to add items from * @param number Shop item number we're up to * @return Number of items listed to the buffer */ int shopData_addInventoryItemsToBuf(char *buf, charData_t *ch, shopData_t *shop, int *num) { int numAdded = 0; if (buf == NULL) { log("shopData_addInventoryItemsToBuf(): Invalid char 'buf'."); } else if (shop == NULL) { log("shopData_addInventoryItemsToBuf(): Invalid shopData_t 'shop'."); } else { shopInventory_t *temp = NULL; if (shop->inventory != NULL) { temp = shop->inventory; while(temp) { if (temp->item) { (*num)++; numAdded++; snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), "%3d) %-9d %-50s %7d\r\n", (*num), temp->count, temp->item->shortDescription, shop_sellPrice(temp->item, shop->keeper, ch)); } temp = temp->next; } } } return (numAdded); } /** * List items for sale in a shop to a character * * @param ch Character to receive the listing * @param shop Shop to list items in * @return none */ void shopData_listItemsToChar(charData_t *ch, shopData_t *shop) { if (ch == NULL) { log("shopData_listItemsToChar(): Invalid charData_t 'ch'."); } else if (shop == NULL) { log("shopData_listItemsToChar(): Invalid shopData_t 'shop'."); } else { char output[MAX_STRING_LENGTH] = { "\0" }; int ret = 0, num = 0; /* Build the header listing */ snprintf(output, sizeof(output), "Items that %s is selling:\r\n\r\n" "Num Quantity Item Cost\r\n" "---- --------- -------------------------------------------------- -------\r\n", shop->keeper->player.short_descr); /* Add items the shop produces */ if (shop->producing) { ret += shopData_addProducingItemsToBuf(output, ch, shop, &num); } /* Add items the shop has in inventory */ if (shop->inventory) { ret += shopData_addInventoryItemsToBuf(output, ch, shop, &num); } if (ret > 0) { page_string(ch->desc, output, TRUE); } else { send_to_char(ch, "It seems %s doesn't have anything for sale!\r\n", HSSH(shop->keeper)); } } } /** * Check if a character can shop at a shop * * @param shop Shop to check * @param ch Character to check * @return TRUE or FALSE */ bool shop_canCharShop(shopData_t *shop, charData_t *ch) { if (shop == NULL) { log("shop_canCharShop(): Invalid shopData_t 'shop'."); } else if (ch == NULL) { log("shop_canCharShop(): Invalid charData_t 'ch'."); } else { if (GET_AUTH(ch) >= AUTH_OWNER) return (TRUE); if ((IS_GOOD(ch) && SHOP_TRADES(shop, TRADE_NOGOOD)) || (IS_EVIL(ch) && SHOP_TRADES(shop, TRADE_NOEVIL)) || (IS_NEUTRAL(ch) && SHOP_TRADES(shop, TRADE_NONEUTRAL)) || (IS_MAGIC_USER(ch) && SHOP_TRADES(shop, TRADE_NOMAGIC_USER)) || (IS_CLERIC(ch) && SHOP_TRADES(shop, TRADE_NOCLERIC)) || (IS_THIEF(ch) && SHOP_TRADES(shop, TRADE_NOTHIEF)) || (IS_WARRIOR(ch) && SHOP_TRADES(shop, TRADE_NOWARRIOR))) return (FALSE); return (TRUE); } return (FALSE); } /** * Check if a character can sell an item to a shop * * This is where you can put in critera for if a shop can buy an item. * For instance, trades-types, money, or if it's a used item. * * @param shop Shop to check * @param keeper Shopkeeper to check * @param ch Character to check * @param item Item to check * @return TRUE or FALSE */ bool shop_canCharSellItem(shopData_t *shop, charData_t *keeper, charData_t *ch, itemData_t *item) { bool ret = FALSE; if (shop == NULL) { log("shop_canCharSellItem(): Invalid shopData_t 'shop'."); } else if (ch == NULL) { log("shop_canCharSellItem(): Invalid charData_t 'ch'."); } else if (item == NULL) { log("shop_canCharSellItem(): Invalid itemData_t 'item'."); } else { char bStr[MAX_STRING_LENGTH] = { "\0" }; if (shop->type == NULL) { snprintf(bStr, sizeof(bStr), "%s I don't buy things!", GET_NAME(ch)); do_tell(keeper, bStr, find_command("tell")); } else if (shop_tradesInItem(shop, item) == FALSE) { snprintf(bStr, sizeof(bStr), "%s I don't deal in those!", GET_NAME(ch)); do_tell(keeper, bStr, find_command("tell")); } else if (GET_GOLD(keeper) + GET_BANK_GOLD(keeper) < shop_buyPrice(item, keeper, ch)) { snprintf(bStr, sizeof(bStr), "%s I can't afford to buy any of those", GET_NAME(ch)); do_tell(keeper, bStr, find_command("tell")); } else if ((GET_ITEM_TYPE(item) == ITEM_WAND || GET_ITEM_TYPE(item) == ITEM_STAFF) && (GET_ITEM_VAL(item, 2) < GET_ITEM_VAL(item, 1))) { snprintf(bStr, sizeof(bStr), "%s I don't buy used goods.", GET_NAME(ch)); do_tell(keeper, bStr, find_command("tell")); } else { ret = TRUE; } } return (ret); } /** * Number of shopkeepers in a room * * @param ch Character looking * @return number of shopkeepers found */ int shopKeeper_numInRoom(charData_t *ch) { charData_t *temp = NULL; int num = 0; if (ch->room->people) for (temp = ch->room->people; temp; temp = temp->next_in_room) if (IS_NPC(temp) && CAN_SEE(ch, temp) && temp->shop != NULL) num++; return (num); } /** * Get first shopkeeper from the room ch can see * * @param ch Character looking * @return pointer to first shopkeeper in the room or NULL */ charData_t *shopKeeper_findFirstInRoom(charData_t *ch) { charData_t *temp = NULL; if (ch->room->people) for (temp = ch->room->people; temp; temp = temp->next_in_room) if (IS_NPC(temp) && CAN_SEE(ch, temp) && temp->shop != NULL) return (temp); return (NULL); } /** * Check if a shop is open or closed * * @param shop Shop to check * @return TRUE or FALSE */ bool shop_isOpen(shopData_t *shop) { bool retValue = FALSE; if (shop == NULL) { log("shop_isOpen(): Invalid shopData_t 'shop'."); } else { if (shop->open1 <= time_info.hours && time_info.hours <= shop->close1) { retValue = TRUE; } else if (shop->open2 > 0 && shop->close2 > 0 && (shop->open2 <= time_info.hours && time_info.hours <= shop->close2)) { retValue = TRUE; } } return (retValue); } /** * Try to find the shopkeeper the character wants * * @param ch Character looking * @param name Name of keeper to look for or NULL * @return pointer to shopkeeper or NULL */ charData_t *shopKeeper_findInRoom(charData_t *ch, char *name) { char bStr[MAX_STRING_LENGTH] = { "\0" }; int numKeepers = shopKeeper_numInRoom(ch); charData_t *keeper = NULL; if (numKeepers == 0) { send_to_char(ch, "There doesn't seem to be anyone here selling anything.\r\n"); } else if (numKeepers == 1) { keeper = shopKeeper_findFirstInRoom(ch); } else if (numKeepers > 1) { if (name && *name) { keeper = get_char_room_vis(ch, name, NULL); if (keeper && (IS_NPC(keeper) == FALSE || keeper->shop == FALSE)) { send_to_char(ch, "%s don't appear to be selling anything.\r\n", UHSSH(keeper)); keeper = NULL; } } else { send_to_char(ch, "Which shopkeeper are you trying to work with?\r\n"); } } /* Found a keeper ch can see, let's see if we need to block transacations */ if (keeper) { if (shop_canCharShop(keeper->shop, ch) == FALSE) { snprintf(bStr, sizeof(bStr), "%s I don't deal with your kind!", GET_NAME(ch)); do_tell(keeper, bStr, find_command("tell")); keeper = NULL; } else if (shop_isOpen(keeper->shop) == FALSE) { snprintf(bStr, sizeof(bStr), "%s %s", GET_NAME(ch), MSG_NOT_OPEN_YET); do_tell(keeper, bStr, find_command("tell")); keeper = NULL; } else if (shop_keeperInShopRoom(keeper) == FALSE) { snprintf(bStr, sizeof(bStr), "%s I don't have a permit to sell here.", GET_NAME(ch)); do_tell(keeper, bStr, find_command("tell")); keeper = NULL; } } return (keeper); } /** * Add or remove gold from a shopkeeper * * @param keeper Keeper to modify * @param amount Amount to modify keeper's gold by * @return none */ void shop_changeKeeperGold(charData_t *keeper, int amount) { if (keeper == NULL) { log("shop_changeKeeperGold(): Invalid charData_t 'keeper'."); } else { if (amount > 0) { /* Add gold to keeper */ GET_GOLD(keeper) += amount; } else { /* We're taking the keeper's gold, from them or the bank */ /* First, switch it to a positive amount */ amount *= -1; /* Shift from the bank if neither covers */ if (GET_GOLD(keeper) < amount && GET_BANK_GOLD(keeper) < amount) { GET_GOLD(keeper) += GET_BANK_GOLD(keeper); GET_BANK_GOLD(keeper) = 0; } if (GET_GOLD(keeper) >= amount) { GET_GOLD(keeper) -= amount; } else if (GET_BANK_GOLD(keeper) >= amount) { GET_BANK_GOLD(keeper) -= amount; } else { log("shop_changeKeeperGold(): Reached change request illegally."); } } } } /** * LIST command * * This command lets a user list the items in a shop * * @param ch the character performing the command * @param argument the additional text passed after the command * @param cmd the command object that was performed * @return none */ ACMD(do_list) { charData_t *keeper = NULL; skip_spaces(&argument); keeper = shopKeeper_findInRoom(ch, argument); if (keeper) { shopData_listItemsToChar(ch, keeper->shop); } } /** * SELL command * * @param ch the character performing the command * @param argument the additional text passed after the command * @param cmd the command object that was performed * @return none */ ACMD(do_sell) { char kStr[MAX_INPUT_LENGTH] = { "\0" }; char iStr[MAX_INPUT_LENGTH] = { "\0" }; char qStr[MAX_INPUT_LENGTH] = { "\0" }; char bStr[MAX_STRING_LENGTH] = { "\0" }; charData_t *keeper = NULL; itemData_t *item = NULL; int quantity = 0, nArgs = 0, nGold = 0, i = 0; /* Parse and organize arguments */ skip_spaces(&argument); nArgs = numWords(argument); if (nArgs == 1) { one_argument(argument, iStr); quantity = 1; } else if (nArgs == 2) { argument = one_argument(argument, kStr); if (kStr && *kStr && isNumber(kStr)) quantity = atoi(kStr); else quantity = 1; one_argument(argument, iStr); } else if (nArgs >= 3) { argument = one_argument(argument, kStr); argument = one_argument(argument, qStr); if (qStr && *qStr && isNumber(qStr)) quantity = atoi(qStr); one_argument(argument, iStr); } else { send_to_char(ch, "Usage\r\n\r\n SELL [keeper] [qty] item\r\n"); return; } /* Find the keeper if any */ keeper = shopKeeper_findInRoom(ch, kStr); if (quantity < 1) { send_to_char(ch, "How many do you want to sell?\r\n"); } else if (get_item_in_list_vis(ch, iStr, NULL, ch->carrying) == NULL) { send_to_char(ch, "You don't seem to have any of those.\r\n"); } else if (keeper && shop_canCharSellItem(keeper->shop, keeper, ch, get_item_in_list_vis(ch, iStr, NULL, ch->carrying))) { /* Let's loop through and sell quantity number of items if we can. */ for (i = 0; i < quantity; i++) { item = get_item_in_list_vis(ch, iStr, NULL, ch->carrying); if (item == NULL) break; if (GET_GOLD(keeper) + GET_BANK_GOLD(keeper) < shop_buyPrice(item, keeper, ch)) break; if (item->prototype == NULL) continue; if (shop_tradesInItem(keeper->shop, item) == FALSE) continue; if (shop_canCharSellItem(keeper->shop, keeper, ch, item) == FALSE) continue; /* Take the money from the shopkeeper */ shop_changeKeeperGold(keeper, (-1 * (shop_buyPrice(item, keeper, ch)))); /* Tally the value */ nGold += shop_buyPrice(item, keeper, ch); /* Kick item over to shop */ shop_addInventoryItem(keeper->shop, item); /* Remove the item from the seller */ itemData_fromChar(item); itemData_extract(item); } /* Build the keeper's message to the player */ if (i != quantity) { /* We didn't sell all the ones the char wanted to sell */ /* If i < quantity then we broke out of the loop before freeing the item if we had one */ if (item == NULL) { snprintf(bStr, sizeof(bStr), "%s You only have %d of those!", GET_NAME(ch), i); } else if (GET_GOLD(keeper) + GET_BANK_GOLD(keeper) < shop_buyPrice(item, keeper, ch)) { snprintf(bStr, sizeof(bStr), "%s I can only afford to buy %d of those.", GET_NAME(ch), i); } else { snprintf(bStr, sizeof(bStr), "%s For some reason I'm only buying %d.", GET_NAME(ch), i); } } else { snprintf(bStr, sizeof(bStr), keeper->shop->itemBought, GET_NAME(ch), nGold); } /* Send the message */ do_tell(keeper, bStr, find_command("tell")); /* Give the player the gold */ GET_GOLD(ch) += nGold; /* Message the room */ act("$n performs a transaction with $N.", FALSE, ch, NULL, keeper, TO_ROOM); } } /** * APPRAISE command * * @param ch the character performing the command * @param argument the additional text passed after the command * @param cmd the command object that was performed * @return none */ ACMD(do_appraise) { char kStr[MAX_INPUT_LENGTH] = { "\0" }; char iStr[MAX_INPUT_LENGTH] = { "\0" }; char bStr[MAX_STRING_LENGTH] = { "\0" }; charData_t *keeper = NULL; itemData_t *item = NULL; int nArgs = 0; /* Parse and organize arguments */ skip_spaces(&argument); nArgs = numWords(argument); if (nArgs == 1) { one_argument(argument, iStr); } else if (nArgs == 2) { argument = one_argument(argument, kStr); one_argument(argument, iStr); } else { send_to_char(ch, "Usage\r\n\r\n APPRAISE [keeper] item\r\n"); return; } /* Find the keeper if any */ keeper = shopKeeper_findInRoom(ch, kStr); /* Find the item if any */ item = get_item_in_list_vis(ch, iStr, NULL, ch->carrying); if (item == NULL) { send_to_char(ch, "You don't seem to have any of those.\r\n"); } else if (keeper && shop_canCharSellItem(keeper->shop, keeper, ch, item)) { snprintf(bStr, sizeof(bStr), "%s I'd pay %d for one of those.", GET_NAME(ch), shop_buyPrice(item, keeper, ch)); do_tell(keeper, bStr, find_command("tell")); } } /** * BUY command * * @param ch the character performing the command * @param argument the additional text passed after the command * @param cmd the command object that was performed * @return none */ ACMD(do_buy) { char kStr[MAX_INPUT_LENGTH] = { "\0" }; char iStr[MAX_INPUT_LENGTH] = { "\0" }; char qStr[MAX_INPUT_LENGTH] = { "\0" }; char bStr[MAX_STRING_LENGTH] = { "\0" }; charData_t *keeper = NULL; itemData_t *item = NULL; int quantity = 0, itemNum = 0, nArgs = 0, count = 0; /* Parse and organize arguments */ skip_spaces(&argument); nArgs = numWords(argument); if (nArgs == 1) { one_argument(argument, iStr); quantity = 1; } else if (nArgs == 2) { argument = one_argument(argument, kStr); if (kStr && *kStr && isNumber(kStr)) quantity = atoi(kStr); else quantity = 1; one_argument(argument, iStr); } else if (nArgs >= 3) { argument = one_argument(argument, kStr); argument = one_argument(argument, qStr); if (qStr && *qStr && isNumber(qStr)) quantity = atoi(qStr); one_argument(argument, iStr); } else { send_to_char(ch, "Usage\r\n\r\n BUY [keeper] [qty] item\r\n"); return; } if (iStr && *iStr && isNumber(iStr)) { itemNum = atoi(iStr); } else { itemNum = -1; } /* Find the keeper if any */ keeper = shopKeeper_findInRoom(ch, kStr); if (keeper) { if ((item = shop_findItemByNumber(keeper->shop, itemNum)) == NULL) { send_to_char(ch, "What are you trying to buy?\r\n"); } else if (quantity < 1) { send_to_char(ch, "How many are you trying to buy?\r\n"); } else if (IS_CARRYING_N(ch) + 1 > CAN_CARRY_N(ch)) { send_to_char(ch, "You arms are full!\r\n"); } else if (IS_CARRYING_W(ch) + GET_ITEM_WEIGHT(item) > CAN_CARRY_W(ch)) { send_to_char(ch, "You couldn't lift it!\r\n"); } else if (GET_AUTH(ch) < AUTH_OWNER && GET_GOLD(ch) < shop_sellPrice(item, keeper, ch)) { send_to_char(ch, "You can't afford it!\r\n"); } else { int totalCost = 0; for (count = 0; count < quantity; count++) { itemData_t *tItem = NULL; item = shop_findItemByNumber(keeper->shop, itemNum); if (item == FALSE) { snprintf(bStr, sizeof(bStr), "%s I only had %d of those.", GET_NAME(ch), count); break; } else if (GET_AUTH(ch) < AUTH_OWNER && GET_GOLD(ch) < shop_sellPrice(item, keeper, ch)) { snprintf(bStr, sizeof(bStr), "%s You could only afford %d of those.", GET_NAME(ch), count); break; } else if (IS_CARRYING_N(ch) + 1 > CAN_CARRY_N(ch)) { snprintf(bStr, sizeof(bStr), "%s You could only carry %d of them.", GET_NAME(ch), count); break; } else if (IS_CARRYING_W(ch) + GET_ITEM_WEIGHT(item) > CAN_CARRY_W(ch)) { snprintf(bStr, sizeof(bStr), "%s You could only carry %d of them.", GET_NAME(ch), count); break; } if (shop_hasItemInInventory(keeper->shop, item) == TRUE) shop_alterItemInventoryCount(keeper->shop, item, -1); if (GET_AUTH(ch) < AUTH_OWNER) { GET_GOLD(ch) -= shop_sellPrice(item, keeper, ch); totalCost += shop_sellPrice(item, keeper, ch); } tItem = read_item(item); itemData_toChar(tItem, ch); } if (count == quantity) { if (GET_AUTH(ch) < AUTH_OWNER) { snprintf(bStr, sizeof(bStr), "%s There you go, that'll be %d coins.", GET_NAME(ch), totalCost); } else { snprintf(bStr, sizeof(bStr), "%s Take %s with my blessing, my liege.", GET_NAME(ch), count > 1 ? "them" : "it"); } } do_tell(keeper, bStr, find_command("tell")); } } }