/* ************************************************************************
* File: shop.c Part of CircleMUD *
* Usage: spec-procs and other funcs for shops and shopkeepers *
* *
* All rights reserved. See license.doc for complete information. *
* *
* Copyright (C) 1993 by the Trustees of the Johns Hopkins University *
* CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. *
************************************************************************ */
/* Archipelago changes by Alastair J. Neil Copyright (C) 1993, 94, 95, 96 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "structs.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "utils.h"
#include "shop.h"
ACMD(do_tell);
ACMD(do_action);
ACMD(do_emote);
ACMD(do_say);
void drop_excess_gold(struct char_data *ch, int amount);
char *report_cost(int gold);
int report_money_weight(int amount);
int report_highest_value_for_weight(int weight);
int report_pennies(int amount);
int report_groats(int amount);
int report_crowns(int amount);
int real_shop(int virtual);
extern struct str_app_type str_app[];
extern struct index_data *mob_index;
extern const char *object_sizes[];
extern struct index_data *obj_index;
extern struct char_data *martha;
extern struct char_data *reimund;
extern struct char_data *aelfric;
extern struct room_data *world;
extern struct time_info_data time_info;
struct shop_data *shop_index;
int number_of_shops = 0;
int real_shop(int virtual)
{
int i,j=-1;
for (i =0;i<=number_of_shops;i++)
if (shop_index[i].virtual == virtual) {
j = i;
break;
}
return(j);
}
int is_ok(struct char_data *keeper, struct char_data *ch, int shop_nr)
{
if (shop_index[shop_nr].open1 > time_info.hours) {
do_say(keeper, "Come back later!", 17, 0);
return(FALSE);
} else if (shop_index[shop_nr].close1 < time_info.hours)
if (shop_index[shop_nr].open2 > time_info.hours) {
do_say(keeper, "Sorry, we have closed, but come back later.", 17, 0);
return(FALSE);
}
else if (shop_index[shop_nr].close2 < time_info.hours) {
do_say(keeper, "Sorry, come back tomorrow.", 17, 0);
return(FALSE);
};
if (!(CAN_SEE(keeper, ch))) {
do_say(keeper, "I don't trade with someone I can't see!", 17, 0);
return(FALSE);
};
switch (shop_index[shop_nr].with_who) {
case 0 :
return(TRUE);
case 1 :
return(TRUE);
default :
return(TRUE);
};
}
int trade_with(struct char_data *keeper, struct char_data *customer, struct obj_data *item, int shop_nr)
{
if (item->obj_flags.cost < 1)
return(FALSE);
if ((shop_index[shop_nr].tradetype > 0
&& IS_SET(shop_index[shop_nr].tradetype,
(1 << item->obj_flags.type_flag)))
|| shop_index[shop_nr].tradetype == 0) {
if ((GET_OBJ_SIZE(item) == 1 || GET_OBJ_SIZE(item) == 0 || GET_OBJ_SIZE(item) == 3 || GET_OBJ_SIZE(item) == 2)
&& (mob_index[keeper->nr].virtual == 1834)) {
sprintf(buf2, "%s That item is far too big!" , GET_NAME(customer));
do_tell(keeper, buf2, 0, 0);
sprintf(buf2, "%s My customers would never buy the like.",
GET_NAME(customer));
do_tell(keeper, buf2, 0, 0);
return(FALSE);
}
return(TRUE);
}
return(FALSE);
}
int shop_producing(struct obj_data *item, int shop_nr)
{
int counter;
if (item->item_number < 0)
return(FALSE);
for (counter = 0; counter < MAX_PROD; counter++)
if (shop_index[shop_nr].producing[counter] == item->item_number)
return(TRUE);
return(FALSE);
}
void shopping_buy( char *arg, struct char_data *ch,
struct char_data *keeper, int shop_nr)
{
char argm[100], *ptr=0;
int objs=0, chars=0;
struct obj_data *temp1;
int amount, number=1, tot_amount=0;
if (!(is_ok(keeper, ch, shop_nr)))
return;
one_argument(arg, argm);
if (!(*argm)) {
sprintf(buf2, "%s what do you want to buy??", GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
};
if ((ptr = strchr(argm,'*')) != 0) {
*ptr = '\0';
ptr++;
if (is_number(argm))
number = atoi(argm);
}
else
ptr = argm;
if (!( temp1 = get_obj_in_list_vis(ch, ptr, keeper->inventory))) {
sprintf(buf2, shop_index[shop_nr].no_such_item1 , GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
if (IS_GODITEM(temp1) && GET_LEVEL(ch) < LEVEL_BUILDER){
sprintf(buf2, "%s You aint holy enough to buy this item.\r\n", GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
if (temp1->obj_flags.cost <= 0) {
sprintf(buf2, shop_index[shop_nr].no_such_item1 , GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
extract_obj(temp1,0);
return;
}
while (number--) {
if (!( temp1 = get_obj_in_list_vis(ch, ptr, keeper->inventory))) {
sprintf(buf2, shop_index[shop_nr].no_such_item1 , GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
sprintf(buf2, shop_index[shop_nr].message_buy, GET_NAME(ch),
report_cost(tot_amount));
do_tell(keeper, buf2, 19, 0);
return;
}
amount = (int) (temp1->obj_flags.cost * shop_index[shop_nr].profit_buy);
if ((amount < 10) ||
((keeper == martha) || (keeper == aelfric) || (keeper == reimund)))
amount = 0;
if (GET_GOLD(ch) < amount && GET_LEVEL(ch)
< LEVEL_GOD && amount) {
sprintf(buf2, shop_index[shop_nr].message_buy, GET_NAME(ch),
report_cost(amount));
do_tell(keeper, buf2, 19, 0);
sprintf(buf2, shop_index[shop_nr].missing_cash2, GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
switch (shop_index[shop_nr].temper1) {
case 0:
do_action(keeper, GET_NAME(ch), 116, 0);
return;
case 1:
switch(GET_SEX(keeper)){
case SEX_MALE: do_emote(keeper, "smokes on his joint.", 36, 0);
break;
case SEX_FEMALE: do_emote(keeper, "smokes on her joint.", 36, 0);
break;
case SEX_NEUTRAL: do_emote(keeper, "smokes on its joint.", 36, 0);
break;
}
return;
default:
return;
}
}
if ((IS_CARRYING_N(ch) + 1 > CAN_CARRY_N(ch))) {
sprintf(buf2, "%s : You can't carry that many items.\r\n", fname(temp1->name));
send_to_char(buf2, ch);
sprintf(buf2, shop_index[shop_nr].message_buy, GET_NAME(ch),
report_cost(tot_amount));
do_tell(keeper, buf2, 19, 0);
return;
}
if ((IS_CARRYING_W(ch) + temp1->obj_flags.weight) > CAN_CARRY_W(ch)) {
sprintf(buf2, "%s : You can't carry that much weight.\r\n", fname(temp1->name));
send_to_char(buf2, ch);
sprintf(buf2, shop_index[shop_nr].message_buy, GET_NAME(ch),
report_cost(tot_amount));
do_tell(keeper, buf2, 19, 0);
return;
}
act("$n buys $p.", FALSE, ch, temp1, 0, TO_ROOM);
sprintf(buf2, "You now have %s.\r\n", temp1->short_description);
send_to_char(buf2, ch);
if (GET_LEVEL(ch) < LEVEL_GOD){
amount = (int)(temp1->obj_flags.cost * shop_index[shop_nr].profit_buy);
if (amount < 10)
amount = 0;
tot_amount += amount;
change_gold(ch, -amount);
}
change_gold(keeper, amount);
/* If the shopkeeper has more than 15000 coins, put it in the bank! */
/* disabled because keepers have so many HP now -je
if (GET_GOLD(keeper) > 15000) {
shop_index[shop_nr].bankAccount += (GET_GOLD(keeper) - 15000);
GET_GOLD(keeper) = 15000;
}
*/
/* Test if producing shop ! */
if (shop_producing(temp1, shop_nr))
temp1 = read_object(temp1->item_number, REAL,0);
else
obj_from_char(temp1,0);
if ((GET_ITEM_TYPE(temp1) != ITEM_FOOD) &&
(GET_ITEM_TYPE(temp1) != ITEM_WEAPON) &&
(GET_ITEM_TYPE(temp1) != ITEM_BOAT) &&
(GET_ITEM_TYPE(temp1) != ITEM_PHILTRE) &&
(GET_ITEM_TYPE(temp1) != ITEM_CANTRIP) &&
(GET_ITEM_TYPE(temp1) != ITEM_ROD) &&
(GET_ITEM_TYPE(temp1) != ITEM_DRINKCON) &&
((GET_ITEM_TYPE(temp1) == ITEM_CONTAINER) &&
(temp1->obj_flags.wear_flags > 0))){
objs = temp1->obj_flags.value[5];
chars = GET_SIZE(ch);
if (IS_SET(temp1->obj_flags.extra_flags, ITEM_RESIZED))
{
do_say(keeper,"This object has already been worked on.",0,0);
do_say(keeper,"I can't resize it for you, sorry.",0,0);
}
else if (chars < objs && abs(chars - objs) > 3){
do_say(keeper,"Sorry it is too small to make fit.",0,0);
do_say(keeper,"I can't resize it for you, sorry.",0,0);
}
else {
temp1->obj_flags.value[5] = GET_SIZE(ch);
GET_OBJ_WEIGHT(temp1)
= (GET_OBJ_WEIGHT(temp1)*(9 - GET_OBJ_SIZE(temp1)))/7;
SET_BIT(temp1->obj_flags.extra_flags,ITEM_RESIZED);
act("$N sizes $n.",FALSE,ch,temp1,keeper,TO_ROOM);
act("$N sizes you.",FALSE,ch,temp1,keeper,TO_CHAR);
act("$N fiddles with $p for a while.",FALSE,ch,temp1,keeper,TO_ROOM);
act("$N fiddles with $p for a while.",FALSE,ch,temp1,keeper,TO_CHAR);
do_say(keeper,"There all sized to fit.",0,0);
}
}
obj_to_char(temp1, ch,0);
}
sprintf(buf2, shop_index[shop_nr].message_buy, GET_NAME(ch),
report_cost(tot_amount));
do_tell(keeper, buf2, 19, 0);
return;
}
void shopping_sell( char *arg, struct char_data *ch,
struct char_data *keeper, int shop_nr)
{
char argm[100], *ptr;
struct obj_data *temp1, *tmp;
int amount, tot_amount=0, number=1;
if (!(is_ok(keeper, ch, shop_nr)))
return;
one_argument(arg, argm);
if (!(*argm)) {
sprintf(buf2, "%s What do you want to sell??" , GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
if ((ptr = strchr(argm,'*')) != 0) {
*ptr = '\0';
ptr++;
if (is_number(argm))
number = atoi(argm);
}
else
ptr = argm;
if (!( temp1 = get_obj_in_list_vis(ch, ptr, ch->inventory))) {
sprintf(buf2, shop_index[shop_nr].no_such_item2 , GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
if (IS_SET(temp1->obj_flags.extra_flags, ITEM_NODROP) && (GET_LEVEL(ch) < LEVEL_BUILDER)) {
sprintf(buf2, "You can't let go of %s, it must be CURSED!\r\n", OBJS(temp1, ch));
send_to_char(buf2, ch);
return;
}
if (!(trade_with(keeper,ch,temp1, shop_nr)) || (temp1->obj_flags.cost < 1)) {
sprintf(buf2, shop_index[shop_nr].do_not_buy, GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
while (number--) {
if (!( temp1 = get_obj_in_list_vis(ch, ptr, ch->inventory))) {
sprintf(buf2, shop_index[shop_nr].no_such_item2 , GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
sprintf(buf2, shop_index[shop_nr].message_sell, GET_NAME(ch),
report_cost(tot_amount));
do_tell(keeper, buf2, 19, 0);
return;
}
amount = (int) (temp1->obj_flags.cost * shop_index[shop_nr].profit_sell);
if ((amount < 10)
|| ((keeper == martha) || (keeper == aelfric) || (keeper == reimund)))
amount = 0;
if ((GET_GOLD(keeper) + shop_index[shop_nr].bankAccount) < amount) {
sprintf(buf2, shop_index[shop_nr].missing_cash1 , GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
sprintf(buf2, shop_index[shop_nr].message_sell, GET_NAME(ch),
report_cost(tot_amount));
do_tell(keeper, buf2, 19, 0);
return;
}
act("$n sells $p.", FALSE, ch, temp1, 0, TO_ROOM);
sprintf(buf2,"The shopkeeper now has %s.\r\n", temp1->short_description);
send_to_char(buf2, ch);
drop_excess_gold(ch,amount);
tot_amount += amount;
/* Get money from the bank, buy the obj, then put money back. */
change_gold(keeper, shop_index[shop_nr].bankAccount);
shop_index[shop_nr].bankAccount = 0;
change_gold(keeper, -amount);
/* If the shopkeeper has more than 15000 coins, put it in the bank! */
/* disabled since keepers have so many HP now
if (GET_GOLD(keeper) > 15000) {
shop_index[shop_nr].bankAccount += (GET_GOLD(keeper) - 15000);
GET_GOLD(keeper) = 15000;
}
*/
obj_from_char(temp1,0);
if (GET_ITEM_TYPE(temp1) == ITEM_TRASH) {
extract_obj(temp1,0);
temp1=0;
}
else {
for (tmp = keeper->inventory; tmp; tmp = tmp->next_content) {
if (obj_index[temp1->item_number].virtual ==
obj_index[tmp->item_number].virtual) {
extract_obj(temp1,0);
temp1=0;
break;
}
}
}
if (temp1 != 0)
obj_to_char(temp1, keeper,0);
}
sprintf(buf2, shop_index[shop_nr].message_sell, GET_NAME(ch),
report_cost(tot_amount));
do_tell(keeper, buf2, 19, 0);
return;
}
void shopping_value( char *arg, struct char_data *ch,
struct char_data *keeper, int shop_nr)
{
char argm[100];
struct obj_data *temp1;
if (!(is_ok(keeper, ch, shop_nr)))
return;
one_argument(arg, argm);
if (!(*argm)) {
sprintf(buf2, "%s What do you want me to valuate??", GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
if (!( temp1 = get_obj_in_list_vis(ch, argm, ch->inventory))) {
sprintf(buf2, shop_index[shop_nr].no_such_item2, GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
if (!(trade_with(keeper, ch, temp1, shop_nr))) {
sprintf(buf2, shop_index[shop_nr].do_not_buy, GET_NAME(ch));
do_tell(keeper, buf2, 19, 0);
return;
}
sprintf(buf2, "%s I'll give you %s for that!", GET_NAME(ch), report_cost( (int)(temp1->obj_flags.cost * shop_index[shop_nr].profit_sell)));
do_tell(keeper, buf2, 19, 0);
return;
}
void shopping_list( char *arg, struct char_data *ch,
struct char_data *keeper, int shop_nr)
{
char buf[MAX_STRING_LENGTH], buf2[100], buf3[100], buf4[100];
struct obj_data *temp1;
int found_obj;
if (!(is_ok(keeper, ch, shop_nr)))
return;
strcpy(buf, "You can buy:\r\n");
found_obj = FALSE;
if (keeper->inventory)
for (temp1 = keeper->inventory; temp1; temp1 = temp1->next_content)
if ((CAN_SEE_OBJ(ch, temp1)) && (temp1->obj_flags.cost > 0)) {
found_obj = TRUE;
if (temp1->obj_flags.type_flag != ITEM_DRINKCON){
if (IS_SET(temp1->obj_flags.extra_flags, ITEM_RESIZED))
sprintf(buf4,"(%s, resized)",object_sizes[GET_OBJ_SIZE(temp1)]);
else
sprintf(buf4,"(%s)",object_sizes[GET_OBJ_SIZE(temp1)]);
sprintf(buf2, "%s %s for %s.\r\n" , (temp1->short_description),buf4,report_cost( (int)(temp1->obj_flags.cost *
shop_index[shop_nr].profit_buy)));
}
else {
/* if (temp1->obj_flags.value[1])
sprintf(buf3, "%s of %s", (temp1->short_description), drinks[temp1->obj_flags.value[2]]);
else
removed the drink deal by general request AJN 30th Sept. 94*/
sprintf(buf3, "%s", (temp1->short_description));
sprintf(buf2, "%s for %s.\r\n", buf3, report_cost((int)(temp1->obj_flags.cost * shop_index[shop_nr].profit_buy)));
}
strcat(buf, CAP(buf2));
};
if (!found_obj)
strcat(buf, "Nothing!\r\n");
send_to_char(buf, ch);
return;
}
void shopping_kill( char *arg, struct char_data *ch,
struct char_data *keeper, int shop_nr)
{
char buf[100];
switch (shop_index[shop_nr].temper2) {
case 0:
sprintf(buf, "%s Don't ever try that again!", GET_NAME(ch));
do_tell(keeper, buf, 19, 0);
return;
case 1:
sprintf(buf, "%s Scram - midget!", GET_NAME(ch));
do_tell(keeper, buf, 19, 0);
return;
default :
return;
}
}
int shop_keeper(struct char_data *ch, void *me, int cmd, char *arg)
{
char argm[100];
struct char_data *keeper;
int shop_nr;
keeper = (struct char_data *)me;
if (cmd < -1)
return 0;
if (cmd == -1) {
if (mob_index[keeper->nr].virtual == 1834) {
do_say(keeper,"A customer! It seems like an eternity since I last did any business!",0,0);
do_say(keeper,"What fine armour can I interest you in today?",0,0);
return(0);
}
}
for (shop_nr = 0 ; shop_index[shop_nr].keeper != keeper->nr && shop_nr <= number_of_shops; shop_nr++)
;
if ((cmd == 56) && (ch->in_room == real_room(shop_index[shop_nr].in_room) || (shop_index[shop_nr].in_room == 0))) {/* Buy */
shopping_buy(arg, ch, keeper, shop_nr);
return(TRUE);
}
if ((cmd == 57 ) &&
((ch->in_room == real_room(shop_index[shop_nr].in_room))
|| shop_index[shop_nr].in_room == 0)) {/* Sell */
shopping_sell(arg, ch, keeper, shop_nr);
return(TRUE);
}
if ((cmd == 58) && ((ch->in_room == real_room(shop_index[shop_nr].in_room))|| (shop_index[shop_nr].in_room == 0))) {/* value */
shopping_value(arg, ch, keeper, shop_nr);
return(TRUE);
}
if ((cmd == 59) && ((ch->in_room == real_room(shop_index[shop_nr].in_room))|| (shop_index[shop_nr].in_room == 0))) {/* List */
shopping_list(arg, ch, keeper, shop_nr);
return(TRUE);
}
if ((cmd == 25) || (cmd == 70)) /* Kill or Hit */ {
one_argument(arg, argm);
if (keeper == get_char_room(argm, ch->in_room) && shop_index[shop_nr].in_room != 0) {
shopping_kill(arg, ch, keeper, shop_nr);
return(TRUE);
}
}/* Cast, recite, use */
/* else if (((cmd == 344) || (cmd == 207) || (cmd == 172))
&& shop_index[shop_nr].in_room != 0) {
act("$N tells you 'No magic here - kid!'.", FALSE, ch, 0, keeper, TO_CHAR);
return TRUE;
} */
if (shop_index[shop_nr].shp_func)
if ((*shop_index[shop_nr].shp_func)(keeper,keeper,cmd,""))
return TRUE;
return(FALSE);
}
void boot_the_shops(FILE *shop_f, char *filename)
{
char *buf, buf2[150];
int temp, count, nr;
long templ;
bool new = FALSE;
sprintf(buf2, "beginning of shop file %s", filename);
for (; ; ) {
buf = fread_string(shop_f, buf2);
if (*buf == '#') /* a new shop */ {
if (!number_of_shops) /* first shop */
CREATE(shop_index, struct shop_data, 1);
else if (!(shop_index = (struct shop_data *) realloc( shop_index, (number_of_shops + 1) * sizeof(struct shop_data )))) {
perror("Error in boot shop\n");
exit(0);
}
new = FALSE;
if (strstr(buf,"NEW")){
new = TRUE;
sscanf(buf, "#%d %s\n", &nr, buf2);
}
else
sscanf(buf, "#%d\n", &nr);
sprintf(buf2, "shop #%d in shop file %s", nr, filename);
shop_index[number_of_shops].virtual = nr;
for (count = 0; count < MAX_PROD; count++) {
fscanf(shop_f, "%d \n", &temp);
if (temp >= 0)
shop_index[number_of_shops].producing[count] = real_object(temp);
else
shop_index[number_of_shops].producing[count] = temp;
}
fscanf(shop_f, "%f \n", &shop_index[number_of_shops].profit_buy);
fscanf(shop_f, "%f \n", &shop_index[number_of_shops].profit_sell);
if (!new){
shop_index[number_of_shops].tradetype = 0;
for (count = 0; count < MAX_TRADE; count++) {
fscanf(shop_f, "%d \n", &temp);
if (temp > 0 &&
!IS_SET(shop_index[number_of_shops].tradetype,(1 << temp)))
SET_BIT(shop_index[number_of_shops].tradetype,(1 << temp));
}
}
else{
fscanf(shop_f, "%ld \n", &templ);
shop_index[number_of_shops].tradetype = templ;
}
shop_index[number_of_shops].no_such_item1= fread_string(shop_f, buf2);
shop_index[number_of_shops].no_such_item2= fread_string(shop_f, buf2);
shop_index[number_of_shops].do_not_buy = fread_string(shop_f, buf2);
shop_index[number_of_shops].missing_cash1= fread_string(shop_f, buf2);
shop_index[number_of_shops].missing_cash2= fread_string(shop_f, buf2);
shop_index[number_of_shops].message_buy = fread_string(shop_f, buf2);
shop_index[number_of_shops].message_sell = fread_string(shop_f, buf2);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].temper1);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].temper2);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].keeper);
shop_index[number_of_shops].keeper = real_mobile(shop_index[number_of_shops].keeper);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].with_who);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].in_room);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].open1);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].close1);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].open2);
fscanf(shop_f, "%d \n", &shop_index[number_of_shops].close2);
shop_index[number_of_shops].shp_func = 0;
shop_index[number_of_shops].bankAccount = 0;
number_of_shops++;
} else if (*buf == '$') /* EOF */
break;
}
}
void assign_the_shopkeepers(void)
{
int temp1;
for (temp1 = 0 ; temp1 < number_of_shops ; temp1++)
mob_index[shop_index[temp1].keeper].func = shop_keeper;
}