/* ************************************************************************
*   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.               *
************************************************************************ */

#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"

#define MAX_TRADE 5
#define MAX_PROD 5

ACMD(do_tell);
ACMD(do_action);
ACMD(do_emote);
ACMD(do_say);

extern struct str_app_type str_app[];
extern struct index_data *mob_index;

struct shop_data {
   int	producing[MAX_PROD];/* Which item to produce (virtual)      */
   float	profit_buy;       /* Factor to multiply cost with.        */
   float	profit_sell;      /* Factor to multiply cost with.        */
   byte type[MAX_TRADE];   /* Which item to trade.                 */
   char	*no_such_item1;    /* Message if keeper hasn't got an item */
   char	*no_such_item2;    /* Message if player hasn't got an item */
   char	*missing_cash1;    /* Message if keeper hasn't got cash    */
   char	*missing_cash2;    /* Message if player hasn't got cash    */
   char	*do_not_buy;	/* If keeper dosn't buy such things. 	*/
   char	*message_buy;      /* Message when player buys item        */
   char	*message_sell;     /* Message when player sells item       */
   int	temper1;           	/* How does keeper react if no money    */
   int	temper2;           	/* How does keeper react when attacked  */
   int	keeper;             /* The mobil who owns the shop (virtual)*/
   int	with_who;		/* Who does the shop trade with?	*/
   int	in_room;		/* Where is the shop?			*/
   int	open1, open2;	/* When does the shop open?		*/
   int	close1, close2;	/* When does the shop close?		*/
   int	bankAccount;  	/* Store all gold over 15000    */
};


extern struct room_data *world;
extern struct time_info_data time_info;

struct shop_data *shop_index;
int	number_of_shops = 0;

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 obj_data *item, int shop_nr)
{
   int	counter;

   if (item->obj_flags.cost < 1)
      return(FALSE);

   for (counter = 0; counter < MAX_TRADE; counter++)
      if (shop_index[shop_nr].type[counter] == item->obj_flags.type_flag)
	 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], buf[MAX_STRING_LENGTH];
   struct obj_data *temp1;

   if (!(is_ok(keeper, ch, shop_nr)))
      return;

   one_argument(arg, argm);
   if (!(*argm)) {
      sprintf(buf, "%s what do you want to buy??", GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   };
   if (!( temp1 =  get_obj_in_list_vis(ch, argm, keeper->carrying))) {
      sprintf(buf, shop_index[shop_nr].no_such_item1 , GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   if (temp1->obj_flags.cost <= 0) {
      sprintf(buf, shop_index[shop_nr].no_such_item1 , GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      extract_obj(temp1);
      return;
   }

   if (GET_GOLD(ch) < (int) (temp1->obj_flags.cost *  shop_index[shop_nr].profit_buy) && GET_LEVEL(ch) < LEVEL_GOD) {
      sprintf(buf, shop_index[shop_nr].missing_cash2, GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);

      switch (shop_index[shop_nr].temper1) {
      case 0:
	 do_action(keeper, GET_NAME(ch), 30, 0);
	 return;
      case 1:
	 do_emote(keeper, "smokes on his joint.", 36, 0);
	 return;
      default:
	 return;
      }
   }

   if ((IS_CARRYING_N(ch) + 1 > CAN_CARRY_N(ch))) {
      sprintf(buf, "%s : You can't carry that many items.\n\r",  fname(temp1->name));
      send_to_char(buf, ch);
      return;
   }

   if ((IS_CARRYING_W(ch) + temp1->obj_flags.weight) > CAN_CARRY_W(ch)) {
      sprintf(buf, "%s : You can't carry that much weight.\n\r",  fname(temp1->name));
      send_to_char(buf, ch);
      return;
   }


   act("$n buys $p.", FALSE, ch, temp1, 0, TO_ROOM);

   sprintf(buf, shop_index[shop_nr].message_buy, GET_NAME(ch), (int) (temp1->obj_flags.cost *  shop_index[shop_nr].profit_buy));
   do_tell(keeper, buf, 19, 0);
   sprintf(buf, "You now have %s.\n\r", temp1->short_description);
   send_to_char(buf, ch);
   if (GET_LEVEL(ch) < LEVEL_GOD)
      GET_GOLD(ch) -= (int)(temp1->obj_flags.cost *  shop_index[shop_nr].profit_buy);

   GET_GOLD(keeper) += (int)(temp1->obj_flags.cost *  shop_index[shop_nr].profit_buy);

   /* 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);
   else
      obj_from_char(temp1);

   obj_to_char(temp1, ch);

   return;
}


void	shopping_sell( char *arg, struct char_data *ch,
struct char_data *keeper, int shop_nr)
{
   char	argm[100], buf[MAX_STRING_LENGTH];
   struct obj_data *temp1;

   if (!(is_ok(keeper, ch, shop_nr)))
      return;

   one_argument(arg, argm);

   if (!(*argm)) {
      sprintf(buf, "%s What do you want to sell??" , GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   if (!( temp1 = get_obj_in_list_vis(ch, argm, ch->carrying))) {
      sprintf(buf, shop_index[shop_nr].no_such_item2 , GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   if (!(trade_with(temp1, shop_nr)) || (temp1->obj_flags.cost < 1)) {
      sprintf(buf, shop_index[shop_nr].do_not_buy, GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   if ((GET_GOLD(keeper) + shop_index[shop_nr].bankAccount) <  (int) (temp1->obj_flags.cost *  shop_index[shop_nr].profit_sell)) {
      sprintf(buf, shop_index[shop_nr].missing_cash1 , GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   act("$n sells $p.", FALSE, ch, temp1, 0, TO_ROOM);

   sprintf(buf, shop_index[shop_nr].message_sell, GET_NAME(ch), (int) (temp1->obj_flags.cost *  shop_index[shop_nr].profit_sell));
   do_tell(keeper, buf, 19, 0);
   sprintf(buf, "The shopkeeper now has %s.\n\r", temp1->short_description);
   send_to_char(buf, ch);
   GET_GOLD(ch) += (int) (temp1->obj_flags.cost *  shop_index[shop_nr].profit_sell);

   /* Get money from the bank, buy the obj, then put money back. */
   GET_GOLD(keeper) += shop_index[shop_nr].bankAccount;
   shop_index[shop_nr].bankAccount = 0;

   GET_GOLD(keeper) -= (int) (temp1->obj_flags.cost *  shop_index[shop_nr].profit_sell);

   /* 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;
        } 
*/
   if ((get_obj_in_list(argm, keeper->carrying)) ||  (GET_ITEM_TYPE(temp1) == ITEM_TRASH))
      extract_obj(temp1);
   else {
      obj_from_char(temp1);
      obj_to_char(temp1, keeper);
   }

   return;
}


void	shopping_value( char *arg, struct char_data *ch, 
struct char_data *keeper, int shop_nr)
{
   char	argm[100], buf[MAX_STRING_LENGTH];
   struct obj_data *temp1;

   if (!(is_ok(keeper, ch, shop_nr)))
      return;

   one_argument(arg, argm);

   if (!(*argm)) {
      sprintf(buf, "%s What do you want me to valuate??", GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   if (!( temp1 = get_obj_in_list_vis(ch, argm, ch->carrying))) {
      sprintf(buf, shop_index[shop_nr].no_such_item2, GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   if (!(trade_with(temp1, shop_nr))) {
      sprintf(buf, shop_index[shop_nr].do_not_buy, GET_NAME(ch));
      do_tell(keeper, buf, 19, 0);
      return;
   }

   sprintf(buf, "%s I'll give you %d gold coins for that!", GET_NAME(ch), (int) (temp1->obj_flags.cost *  shop_index[shop_nr].profit_sell));
   do_tell(keeper, buf, 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];
   struct obj_data *temp1;
   extern char	*drinks[];
   int	found_obj;

   if (!(is_ok(keeper, ch, shop_nr)))
      return;

   strcpy(buf, "You can buy:\n\r");
   found_obj = FALSE;
   if (keeper->carrying)
      for (temp1 = keeper->carrying; 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)
	       sprintf(buf2, "%s for %d gold coins.\n\r" , (temp1->short_description), (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
		  sprintf(buf3, "%s", (temp1->short_description));
	       sprintf(buf2, "%s for %d gold coins.\n\r", buf3, (int)(temp1->obj_flags.cost * shop_index[shop_nr].profit_buy));
	    }
	    strcat(buf, CAP(buf2));
	 };

   if (!found_obj)
      strcat(buf, "Nothing!\n\r");

   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, int cmd, char *arg)
{
   char	argm[100];
   struct char_data *temp_char;
   struct char_data *keeper;
   int	shop_nr;

   keeper = 0;

   for (temp_char = world[ch->in_room].people; (!keeper) && (temp_char) ; temp_char = temp_char->next_in_room)
      if (IS_MOB(temp_char))
	 if (mob_index[temp_char->nr].func == shop_keeper)
	    keeper = temp_char;

   for (shop_nr = 0 ; shop_index[shop_nr].keeper != keeper->nr; shop_nr++)
      ;

   if ((cmd == 56) && (ch->in_room ==  real_room(shop_index[shop_nr].in_room))) {/* Buy */

      shopping_buy(arg, ch, keeper, shop_nr);
      return(TRUE);
   }

   if ((cmd == 57 ) && (ch->in_room ==  real_room(shop_index[shop_nr].in_room))) {/* Sell */

      shopping_sell(arg, ch, keeper, shop_nr);
      return(TRUE);
   }

   if ((cmd == 58) && (ch->in_room ==  real_room(shop_index[shop_nr].in_room))) {/* value */

      shopping_value(arg, ch, keeper, shop_nr);
      return(TRUE);
   }

   if ((cmd == 59) && (ch->in_room ==  real_room(shop_index[shop_nr].in_room))) {/* 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)) {
	 shopping_kill(arg, ch, keeper, shop_nr);
	 return(TRUE);
      }
   } else if ((cmd == 84) || (cmd == 207) || (cmd == 172)) {   /* Cast, recite, use */
      act("$N tells you 'No magic here - kid!'.", FALSE, ch, 0, keeper, TO_CHAR);
      return TRUE;
   }

   return(FALSE);
}


void	boot_the_shops(FILE *shop_f, char *filename)
{
   char	*buf, buf2[150];
   int	temp, count, nr;

   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);
	 }

	 sscanf(buf, "#%d\n", &nr);
	 sprintf(buf2, "shop #%d in shop file %s", nr, filename);

	 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);
	 for (count = 0; count < MAX_TRADE; count++) {
	    fscanf(shop_f, "%d \n", &temp);
	    shop_index[number_of_shops].type[count] =  (byte) temp;
	 }
	 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].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;
}


