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