toc/
toc/account/a/
toc/area/backup/
toc/area/imc/
toc/caste/
toc/caste/backup/
toc/clans/
toc/classes/
toc/crash/
toc/gods/
toc/guilds/
toc/lname/s/
toc/maps/backup/
toc/player/a/
toc/src/
toc/system/backup/
toc/tableprog/
/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 *			 Shop and repair shop module			    *
 ****************************************************************************/

#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "mud.h"



/*
 * Local functions
 */

#define	CD	CHAR_DATA
CD *find_keeper args((CHAR_DATA * ch));
CD *find_fixer args((CHAR_DATA * ch));
int get_cost args((CHAR_DATA * ch, CHAR_DATA * keeper, OBJ_DATA * obj, int fBuy));
int get_repaircost args((CHAR_DATA * keeper, OBJ_DATA * obj));

#undef CD

/*
 * Shopping commands.
 */
CHAR_DATA *find_keeper(CHAR_DATA * ch)
{
   char buf[MSL];
   CHAR_DATA *keeper, *whof;
   SHOP_DATA *pShop;
   int speakswell;

   pShop = NULL;
   for (keeper = ch->in_room->first_person; keeper; keeper = keeper->next_in_room)
      if (IS_NPC(keeper) && (pShop = keeper->pIndexData->pShop) != NULL)
         break;

   if (!pShop)
   {
      send_to_char("You can't do that here.\n\r", ch);
      return NULL;
   }


   /*
    * Disallow sales during battle
    */
   if ((whof = who_fighting(keeper)) != NULL)
   {
      if (whof == ch)
         send_to_char("I don't think that's a good idea...\n\r", ch);
      else
         do_say(keeper, "I'm too busy for that!");
      return NULL;
   }

   if (who_fighting(ch))
   {
      ch_printf(ch, "%s doesn't seem to want to get involved.\n\r", PERS(keeper, ch));
      return NULL;
   }

   /*
    * Check to see if show is open.
    * Supports closing times after midnight
    */
   if (pShop->open_hour > pShop->close_hour)
   {
      if (gethour() < pShop->open_hour && gethour() > pShop->close_hour)
      {
         do_say(keeper, "Sorry, come back later.");
         return NULL;
      }
   }
   else
   {
      if (gethour() < pShop->open_hour)
      {
         do_say(keeper, "Sorry, come back later.");
         return NULL;
      }
      if (gethour() > pShop->close_hour)
      {
         do_say(keeper, "Sorry, come back tomorrow.");
         return NULL;
      }
   }

   if (keeper->position == POS_SLEEPING)
   {
      send_to_char("While they're asleep?\n\r", ch);
      return NULL;
   }

   if (keeper->position < POS_SLEEPING)
   {
      send_to_char("I don't think they can hear you...\n\r", ch);
      return NULL;
   }

   /*
    * Invisible or hidden people.
    */
   if (!can_see(keeper, ch))
   {
      do_say(keeper, "I don't trade with folks I can't see.");
      return NULL;
   }

   speakswell = UMIN(knows_language(keeper, ch->speaking, ch), knows_language(ch, ch->speaking, keeper));

   if ((number_percent() % 65) > speakswell)
   {
      if (speakswell > 60)
         sprintf(buf, "%s Could you repeat that?  I didn't quite catch it.", ch->name);
      else if (speakswell > 50)
         sprintf(buf, "%s Could you say that a little more clearly please?", ch->name);
      else if (speakswell > 40)
         sprintf(buf, "%s Sorry... What was that you wanted?", ch->name);
      else
         sprintf(buf, "%s I can't understand you.", ch->name);
      do_tell(keeper, buf);
      return NULL;
   }

   return keeper;
}

/*
 * repair commands.
 */
CHAR_DATA *find_fixer(CHAR_DATA * ch)
{
   char buf[MSL];
   CHAR_DATA *keeper, *whof;
   REPAIR_DATA *rShop;
   int speakswell;

   rShop = NULL;
   for (keeper = ch->in_room->first_person; keeper; keeper = keeper->next_in_room)
      if (IS_NPC(keeper) && IN_SAME_ROOM(keeper, ch) && (rShop = keeper->pIndexData->rShop) != NULL)
         break;

   if (!rShop)
   {
      send_to_char("You can't do that here.\n\r", ch);
      return NULL;
   }


   /*
    * Disallow sales during battle
    */
   if ((whof = who_fighting(keeper)) != NULL)
   {
      if (whof == ch)
         send_to_char("I don't think that's a good idea...\n\r", ch);
      else
         do_say(keeper, "I'm too busy for that!");
      return NULL;
   }

   /* According to rlog, this is the second time I've done this
    * so mobiles can repair in combat.  -- Blod, 1/98
    */
   if (!IS_NPC(ch) && who_fighting(ch))
   {
      ch_printf(ch, "%s doesn't seem to want to get involved.\n\r", PERS(keeper, ch));
      return NULL;
   }

   /*
    * Check to see if show is open.
    * Supports closing times after midnight
    */
   if (rShop->open_hour > rShop->close_hour)
   {
      if (gethour() < rShop->open_hour && gethour() > rShop->close_hour)
      {
         do_say(keeper, "Sorry, come back later.");
         return NULL;
      }
   }
   else
   {
      if (gethour() < rShop->open_hour)
      {
         do_say(keeper, "Sorry, come back later.");
         return NULL;
      }
      if (gethour() > rShop->close_hour)
      {
         do_say(keeper, "Sorry, come back tomorrow.");
         return NULL;
      }
   }

   if (keeper->position == POS_SLEEPING)
   {
      send_to_char("While they're asleep?\n\r", ch);
      return NULL;
   }

   if (keeper->position < POS_SLEEPING)
   {
      send_to_char("I don't think they can hear you...\n\r", ch);
      return NULL;
   }

   /*
    * Invisible or hidden people.
    */
   if (!can_see(keeper, ch))
   {
      do_say(keeper, "I don't trade with folks I can't see.");
      return NULL;
   }

   speakswell = UMIN(knows_language(keeper, ch->speaking, ch), knows_language(ch, ch->speaking, keeper));

   if ((number_percent() % 65) > speakswell)
   {
      if (speakswell > 60)
         sprintf(buf, "%s Could you repeat that?  I didn't quite catch it.", ch->name);
      else if (speakswell > 50)
         sprintf(buf, "%s Could you say that a little more clearly please?", ch->name);
      else if (speakswell > 40)
         sprintf(buf, "%s Sorry... What was that you wanted?", ch->name);
      else
         sprintf(buf, "%s I can't understand you.", ch->name);
      do_tell(keeper, buf);
      return NULL;
   }

   return keeper;
}



int get_cost(CHAR_DATA * ch, CHAR_DATA * keeper, OBJ_DATA * obj, int fBuy)
{
   SHOP_DATA *pShop;
   int cost;
   int tax;
   int taxk;
   bool richcustomer;
   int profitmod;

   if (!obj || (pShop = keeper->pIndexData->pShop) == NULL)
      return 0;

   richcustomer = FALSE;

   if (fBuy == 1 || fBuy == 2)
   {
      profitmod = 0;
      cost = (int) (obj->cost * UMAX((pShop->profit_sell + 1), pShop->profit_buy + profitmod)) / 100;

      /* Thanks to Nick Gammon for pointing out this line
         (it was the first line in this block, making it useless) */

      switch (ch->race) /* racism... should compare against shopkeeper's race */
      {
         case (1):
            cost /= 1.1;
            break; /* elf */
         case (2):
            cost /= 0.97;
            break; /* dwarf */
         case (3):
            cost /= 1.02;
            break; /* halfling */
         case (4):
            cost /= 1.08;
            break; /* pixie */
         case (6):
            cost /= 0.92;
            break; /* half-ogre */
         case (7):
            cost /= 0.94;
            break; /* half-orc */
         case (8):
            cost /= 0.90;
            break; /* half-troll */
         case (9):
            cost /= 1.04;
            break; /* half-elf */
         case (10):
            cost /= 1.06;
            break; /* gith */
      }
   }
   else
   {
      OBJ_DATA *obj2;
      int itype;

      profitmod = get_curr_cha(ch) - 13 - (richcustomer ? 15 : 0);
      cost = 0;
      for (itype = 0; itype < MAX_TRADE; itype++)
      {
         if (obj->item_type == pShop->buy_type[itype])
         {
            cost = (int) (obj->cost * UMIN((pShop->profit_buy - 1), pShop->profit_sell + profitmod)) / 100;
            break;
         }
      }
      /* Checks to see if an obj is on the merchants vnums and if so, will check
         vnums and cvnum against eachother to see if they are a copy. -- Xerves 12/99 */
      for (obj2 = keeper->first_carrying; obj2; obj2 = obj2->next_content)
      {
         if (obj->pIndexData->vnum == obj2->pIndexData->vnum)
         {
            cost = 0;
            break;
         }
         if (obj2->pIndexData->vnum >= 12100 || obj2->pIndexData->vnum <= 13100)
         {
            if (obj2->pIndexData->cvnum == obj->pIndexData->vnum)
            {
               cost = 0;
               break;
            }
         }
      }
   }
   if (fBuy == 2)
   {
      if (ch->in_room->area->kingdom > 1)
      {
         taxk = (cost * kingdom_table[ch->in_room->area->kingdom]->salestax) / 1000;
         cost = cost + taxk;
         tax = (cost * ch->in_room->area->salestax) / 1000;
         cost = cost + tax;
      }
   }
   return cost;
}

int get_repaircost(CHAR_DATA * keeper, OBJ_DATA * obj)
{
   REPAIR_DATA *rShop;
   int cost;
   int itype;
   bool found;

   if (!obj || (rShop = keeper->pIndexData->rShop) == NULL)
      return 0;

   cost = 0;
   found = FALSE;
   for (itype = 0; itype < MAX_FIX; itype++)
   {
      if (obj->item_type == rShop->fix_type[itype])
      {
         cost = (int) (obj->cost * rShop->profit_fix / 1000);
         found = TRUE;
         break;
      }
   }

   if (!found)
      cost = -1;

   if (cost == 0)
      cost = 1;

   if (found && cost > 0)
   {
      switch (obj->item_type)
      {
         case ITEM_ARMOR:
            if (IS_SET(obj->wear_flags, ITEM_WEAR_SHIELD))
            {
               if (obj->value[0] == obj->value[1])
                  cost = -2;
               else
                  cost = cost * (obj->value[1] - obj->value[0]) / obj->value[1];
            }
            else
            {
               if (obj->value[3] == INIT_ARMOR_CONDITION)
                  cost = -2;
               else
                  cost = cost * (1000 - obj->value[3]) / INIT_ARMOR_CONDITION;
            }
            break;
         case ITEM_WEAPON:
            if (INIT_ARMOR_CONDITION == obj->value[0])
               cost = -2;
            else
               cost = cost * (1000 - obj->value[0]) / INIT_ARMOR_CONDITION;
            break;
         case ITEM_SHEATH:
            if (obj->value[3] == INIT_ARMOR_CONDITION)
               cost = -2;
            else
               cost = cost * (INIT_ARMOR_CONDITION - obj->value[3]);
            break;
      }
   }

   return cost;
}


/*****************************************
    Below five functions are used in
    shopkeeper options for their
    Owners (Merchant Job) -- Xerves
 *****************************************/

void do_keeperstat(CHAR_DATA * ch, char *argument)
{
   SHOP_DATA *shop;
   CHAR_DATA *keeper;
   int found = 0;

   if (IS_NPC(ch))
   {
      send_to_char("Mobs cannot have shopkeepers\n\r", ch);
      return;
   }
   if (!ch->pcdata || !ch->pcdata->keeper || (ch->pcdata->caste < 7))
   {
      send_to_char("You need to have a shop keeper to see its stats.\n\r", ch);
      return;
   }

   for (keeper = ch->in_room->first_person; keeper; keeper = keeper->next_in_room)
   {
      if ((IS_NPC(keeper)) && (xIS_SET(keeper->act, ACT_CASTEMOB)) && (keeper->pIndexData->vnum == ch->pcdata->keeper))
      {
         found = 1;
         break;
      }
   }
   if (found == 0)
   {
      send_to_char("Either you don't have a shopkeeper or you are not in the room with it\n\r", ch);
      return;
   }
   if (!keeper->pIndexData->pShop)
   {
      send_to_char("This mobile doesn't keep a shop.\n\r", ch);
      return;
   }
   shop = keeper->pIndexData->pShop;

   ch_printf_color(ch, "&G&WKeeper: &c&w%d  %s\n\r", shop->keeper, keeper->short_descr);
   ch_printf_color(ch,
      "&G&Wbuy0 &c&w[%s]  &G&Wbuy1 &c&w[%s]  &G&Wbuy2 &c&w[%s]  \n\r&G&Wbuy3 &c&w[%s]  &G&Wbuy4 &c&w[%s]  &G&Wgold &c&w[&Y%d&c&w]\n\r",
      o_types[shop->buy_type[0]], o_types[shop->buy_type[1]], o_types[shop->buy_type[2]], o_types[shop->buy_type[3]], o_types[shop->buy_type[4]],
      keeper->gold);
   ch_printf_color(ch, "&G&WProfit:  &c&wbuy &R%3d%%  &c&wsell &R%3d%%\n\r", shop->profit_buy, shop->profit_sell);
   ch_printf_color(ch, "&G&WHours:   &c&wopen &R%2d  &c&wclose &R%2d\n\r", shop->open_hour, shop->close_hour);
   ch_printf_color(ch, "&G&WMinGold: &Y%d  &G&WMaxGold: &Y%d  &G&WLimitGold: &Y%d\n\r", keeper->pIndexData->m1, keeper->pIndexData->m2,
      keeper->pIndexData->m3);
   ch_printf_color(ch, "&G&WHoldamt: &g&w%d\n\r", keeper->pIndexData->m6);
   return;
}
void do_keeperset(CHAR_DATA * ch, char *argument)
{
   SHOP_DATA *shop;
   CHAR_DATA *keeper;
   int found = 0;
   char arg1[MIL];
   int value;

   argument = one_argument(argument, arg1);


   if (IS_NPC(ch))
   {
      send_to_char("Mobs cannot have a shopkeeper\n\r", ch);
      return;
   }
   if (!ch->pcdata || !ch->pcdata->keeper || (ch->pcdata->caste < 7))
   {
      send_to_char("You need to have a shop keeper to see its stats.\n\r", ch);
      return;
   }

   for (keeper = ch->in_room->first_person; keeper; keeper = keeper->next_in_room)
   {
      if ((IS_NPC(keeper)) && (xIS_SET(keeper->act, ACT_CASTEMOB)) && (keeper->pIndexData->vnum == ch->pcdata->keeper))
      {
         found = 1;
         break;
      }
   }
   if (found == 0)
   {
      send_to_char("Either you don't have a shopkeeper or you are not in the room with it\n\r", ch);
      return;
   }
   if (!keeper->pIndexData->pShop)
   {
      send_to_char("This mobile doesn't keep a shop.\n\r", ch);
      bug("Mob %d is not a keeper, but set as a player keeper", keeper->pIndexData->vnum);
      return;
   }
   if (arg1[0] == '\0')
   {
      send_to_char("Usage: keeperset <field> value\n\r", ch);
      send_to_char("\n\rField being one of:\n\r", ch);
      send_to_char("buy0 buy1 buy2 buy3 buy4 buy sell open close\n\r", ch);
      send_to_char("gold0 gold1 gold2\n\r", ch);
      send_to_char("holdamt\n\r", ch);
      return;
   }
   shop = keeper->pIndexData->pShop;
   value = atoi(argument);

   if (!str_cmp(arg1, "buy0"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[0] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "buy1"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[1] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "buy2"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[2] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "buy3"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[3] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "buy4"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[4] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "buy"))
   {
      if (value <= (shop->profit_sell + 5) || value > 1000)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->profit_buy = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "sell"))
   {
      if (value < 0 || value >= (shop->profit_buy - 5))
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->profit_sell = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "open"))
   {
      if (value < 0 || value > 23)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->open_hour = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg1, "close"))
   {
      if (value < 0 || value > 23)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->close_hour = value;
      send_to_char("Done.\n\r", ch);
      return;
   }
   if (!str_cmp(arg1, "gold0"))
   {
      if (value > keeper->pIndexData->m2)
      {
         send_to_char("Your minimum cannot be higher than the maximum\n\r", ch);
         return;
      }
      if (value < 1)
      {
         send_to_char("Value needs to be 1 or greater\n\r", ch);
         return;
      }
      keeper->pIndexData->m1 = value;
      send_to_char("Done.\n\r", ch);
      return;
   }
   if (!str_cmp(arg1, "gold1"))
   {
      if (value < keeper->pIndexData->m1)
      {
         send_to_char("You maximum cannot be lower than the minimum\n\r", ch);
         return;
      }
      if (value < 1)
      {
         send_to_char("Value needs to be 1 or greater\n\r", ch);
         return;
      }
      keeper->pIndexData->m2 = value;
      send_to_char("Done.\n\r", ch);
      return;
   }
   if (!str_cmp(arg1, "gold2"))
   {
      if (value < 1)
      {
         send_to_char("Value needs to be 1 or greater\n\r", ch);
         return;
      }
      keeper->pIndexData->m3 = value;
      send_to_char("Done.\n\r", ch);
      return;
   }
   if (!str_cmp(arg1, "holdamt"))
   {
      if (value < 1 || value > 300)
      {
         send_to_char("Range is 1 to 300\n\r", ch);
         return;
      }
      keeper->pIndexData->m6 = value;
      send_to_char("Done.\n\r", ch);
      return;
   }
   do_keeperset(ch, "");
   return;
}
void do_goldtake(CHAR_DATA * ch, char *argument)
{
   CHAR_DATA *keeper;
   char arg[MIL];
   int found = 0;
   int amount;

   argument = one_argument(argument, arg);

   if (IS_NPC(ch))
   {
      send_to_char("Mobs cannot have shopkeepers\n\r", ch);
      return;
   }
   if (!ch->pcdata || !ch->pcdata->keeper || (ch->pcdata->caste < 7))
   {
      send_to_char("You need to have a shop keeper to exchange cash.\n\r", ch);
      return;
   }

   for (keeper = ch->in_room->first_person; keeper; keeper = keeper->next_in_room)
   {
      if ((IS_NPC(keeper)) && (xIS_SET(keeper->act, ACT_CASTEMOB)) && (keeper->pIndexData->vnum == ch->pcdata->keeper))
      {
         found = 1;
         break;
      }
   }
   if (found == 0)
   {
      send_to_char("Either you don't have a shopkeeper or you are not in the room with it\n\r", ch);
      return;
   }
   if (arg[0] == '\0')
   {
      send_to_char("Syntax: goldtake <amount>\n\r", ch);
      return;
   }
   amount = atoi(arg);
   if (!is_number(arg))
   {
      send_to_char("Amount needs to be a number\n\r", ch);
      return;
   }
   if (amount > keeper->gold || amount > keeper->pIndexData->gold)
   {
      send_to_char("Your keeper does not have that much gold.\n\r", ch);
      return;
   }
   ch->gold += amount;
   keeper->pIndexData->gold -= amount;
   keeper->gold -= amount;
   send_to_char("Amount transfered\n\r", ch);
   return;
}
void do_goldgive(CHAR_DATA * ch, char *argument)
{
   CHAR_DATA *keeper;
   char arg[MIL];
   int found = 0;
   int amount;

   argument = one_argument(argument, arg);

   if (IS_NPC(ch))
   {
      send_to_char("Mobs cannot have shopkeepers\n\r", ch);
      return;
   }
   if (!ch->pcdata || !ch->pcdata->keeper || (ch->pcdata->caste < 7))
   {
      send_to_char("You need to have a shop keeper to exchange cash.\n\r", ch);
      return;
   }

   for (keeper = ch->in_room->first_person; keeper; keeper = keeper->next_in_room)
   {
      if ((IS_NPC(keeper)) && (xIS_SET(keeper->act, ACT_CASTEMOB)) && (keeper->pIndexData->vnum == ch->pcdata->keeper))
      {
         found = 1;
         break;
      }
   }
   if (found == 0)
   {
      send_to_char("Either you don't have a shopkeeper or you are not in the room with it\n\r", ch);
      return;
   }
   if (arg[0] == '\0')
   {
      send_to_char("Syntax: goldgive <amount>\n\r", ch);
      return;
   }
   amount = atoi(arg);
   if (!is_number(arg))
   {
      send_to_char("Amount needs to be a number\n\r", ch);
      return;
   }
   if (amount > ch->gold)
   {
      send_to_char("You do not have that much gold on you.\n\r", ch);
      return;
   }
   ch->gold -= amount;
   keeper->pIndexData->gold += amount;
   keeper->gold += amount;
   send_to_char("Amount transfered\n\r", ch);
   return;
}
void do_makekeeper(CHAR_DATA * ch, char *argument)
{
   char arg1[MIL];
   char arg2[MIL];
   int amount;
   CHAR_DATA *victim;

   if (IS_NPC(ch))
   {
      send_to_char("Mobs cannot give a player a keeper.\n\r", ch);
      return;
   }
   if (argument[0] == '\0')
   {
      send_to_char("Syntax: makekeeper <char> <vnum>\n\r", ch);
      return;
   }

   argument = one_argument(argument, arg1);
   argument = one_argument(argument, arg2);

   if (arg1 == '\0' || arg2 == '\0')
   {
      send_to_char("Syntax: makekeeper <char> <vnum>\n\r", ch);
      return;
   }

   if ((victim = get_char_world(ch, arg1)) == NULL)
   {
      send_to_char("They aren't here.\n\r", ch);
      return;
   }

   if (IS_NPC(victim))
   {
      send_to_char("NOT ON NPCs.\n\r", ch);
      return;
   }

   if (get_trust(ch) <= get_trust(victim) && ch != victim)
   {
      send_to_char("Don't do that AGAIN!.\n\r", ch);
      return;
   }

   if (!is_number(arg2))
   {
      send_to_char("Syntax: makekeeper <char> <vnum>\n\r", ch);
      return;
   }

   amount = atoi(arg2);

   if (amount < 1 || amount > MAX_VNUM)
   {
      send_to_char("Not a valid vnum\n\r", ch);
      return;
   }
   victim->pcdata->keeper = amount;
   send_to_char("Keeper set\n\r", ch);
   send_to_char("You now have a keeper\n\r", victim);
   return;
}
void do_pcshops(CHAR_DATA * ch, char *argument)
{
   SHOP_DATA *shop;
   MOB_INDEX_DATA *keeper;

   if (!first_shop)
   {
      send_to_char("There are no shops.\n\r", ch);
      return;
   }
   set_char_color(AT_NOTE, ch);
   send_to_char("--------------------------------------------------------------------------\n\r", ch);
   for (shop = first_shop; shop; shop = shop->next)
   {
      if ((keeper = get_mob_index(shop->keeper)) == NULL)
      {
         bug("Shop %d found, but keeper doesn't exsist", shop->keeper);
         send_to_char("Error, returning out of pcshops", ch);
      }
      if (xIS_SET(keeper->act, ACT_CASTEMOB))
      {
         ch_printf(ch,
            "&GKeeper: %5d &G&WBuy: &B%3d &G&WSell: &B%3d &G&WOpen: &C%2d &G&WClose: &C%2d &G&WBuy: &c%2d %2d %2d %2d %2d\n\r              &G&WMinGold: &Y%-8d &G&WMaxGold: &Y%-8d &G&WLimGold: &Y%-8d\n\r              &G&WMinLevel: &c&w%2d &G&WMaxlevel: &c&w%2d\n\r",
            shop->keeper, shop->profit_buy, shop->profit_sell, shop->open_hour, shop->close_hour, shop->buy_type[0], shop->buy_type[1],
            shop->buy_type[2], shop->buy_type[3], shop->buy_type[4], keeper->m1, keeper->m2, keeper->m3, keeper->m4, keeper->m5);
         send_to_char("&G--------------------------------------------------------------------------\n\r", ch);
      }
   }
   return;
}
void do_buy(CHAR_DATA * ch, char *argument)
{
   char arg[MIL];
   int maxgold;
   int ctax = 0;
   int tax = 0;
   int taxk = 0;
   TOWN_DATA *town;
   int cost;
   TOWN_DATA *dtown;
   int level;

   argument = one_argument(argument, arg);

   if (arg[0] == '\0')
   {
      send_to_char("Buy what?\n\r", ch);
      return;
   }
   
   if (xIS_SET(ch->in_room->room_flags, ROOM_MOUNT_SHOP))
   {
      char buf[MSL];
      CHAR_DATA *mount;
      ROOM_INDEX_DATA *pRoomIndexNext;
      ROOM_INDEX_DATA *in_room;

      if (IS_NPC(ch))
         return;

      pRoomIndexNext = get_room_index(ch->in_room->vnum + 1);
      if (!pRoomIndexNext)
      {
         bug("Do_buy: bad mount shop at vnum %d.", ch->in_room->vnum);
         send_to_char("Sorry, you can't buy that here.\n\r", ch);
         return;
      }

      in_room = ch->in_room;
      ch->in_room = pRoomIndexNext;
      mount = get_char_room_new(ch, arg, 1);
      ch->in_room = in_room;

      if (mount == NULL || !IS_NPC(mount) || !xIS_SET(mount->act, ACT_MOUNTSAVE))
      {
         send_to_char("Sorry, you can't buy that here.\n\r", ch);
         return;
      }

      if (xIS_SET(ch->act, PLR_BOUGHT_MOUNT))
      {
         send_to_char("You already have a mount\n\r", ch);
         return;
      }
      if (ch->in_room->area->kingdom > 1)
      {
         tax = kingdom_table[ch->in_room->area->kingdom]->salestax;
         taxk = ch->in_room->area->salestax;
      }
      else
      {
         tax = 0;
         taxk = 0;
      }
      cost = mount->m2;
      if (ch->race == RACE_HOBBIT)
         cost = cost * 85 / 100;
      if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
      {
         level = POINT_LEVEL(LEARNED(ch, gsn_haggling), MASTERED(ch, gsn_haggling));
         cost = cost * (100-(level/2)) / 100;
      }
      ctax = (cost * tax) / 1000;
      taxk = (cost * taxk) / 1000;
      if (ch->gold < (mount->m2 + ctax + taxk))
      {
         send_to_char("You can't afford it.\n\r", ch);
         return;
      }
      if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
         learn_from_success(ch, gsn_haggling, NULL);
      maxgold = cost;
      ch->gold -= (maxgold + ctax + taxk);
      
      if (IN_WILDERNESS(ch))
      {
         if ((town = find_town(ch->coord->x, ch->coord->y, ch->map)) != NULL)
         {         
            if (tax)
            {
               if ((dtown = find_town(ch->coord->x, ch->coord->y, ch->map)) != NULL)
               {
                  if ((get_current_hold(dtown) + (ctax/100)) <= dtown->hold)
                     dtown->coins += ctax;
               }
            }
            if (taxk)
            {
               if ((get_current_hold(town) + (taxk/100)) <= town->hold)
                  town->coins += taxk;
            }
         }
      }
      boost_economy(ch->in_room->area, maxgold);
      mount = create_mobile(mount->pIndexData);
      xSET_BIT(ch->act, PLR_BOUGHT_MOUNT);
      xSET_BIT(mount->act, ACT_MOUNTSAVE);
      xSET_BIT(mount->affected_by, AFF_CHARM);
      mount->mover = mount->m3;
      mount->m4 = ch->pcdata->hometown;
/*	This isn't needed anymore since you can order your pets --Shaddai
	xSET_BIT(pet->affected_by, AFF_CHARM);
*/

      argument = one_argument(argument, arg);
      if (arg[0] != '\0')
      {
         sprintf(buf, "%s %s", mount->name, arg);
         STRFREE(mount->name);
         mount->name = STRALLOC(buf);
      }

      sprintf(buf, "%sA neck tag says 'I am a mount, don't kill me'.\n\r", mount->description);
      STRFREE(mount->description);
      mount->description = STRALLOC(buf);

      char_to_room(mount, ch->in_room);
      add_follower(mount, ch);
      send_to_char("Enjoy your mount.\n\r", ch);
      act(AT_ACTION, "$n bought $N as a mount.", ch, NULL, mount, TO_ROOM);
      return;
   }
   else
   {
      CHAR_DATA *keeper;
      OBJ_DATA *obj;
      OBJ_DATA *fobj;
      AREA_DATA *pArea; /* Passing resets to special mobs */
      ROOM_INDEX_DATA *pRoom;
      int cost;
      int noi = 1; /* Number of items */
      sh_int mnoi = 20; /* Max number of items to be bought at once */

      if ((keeper = find_keeper(ch)) == NULL)
         return;

      pRoom = keeper->in_room;
      pArea = pRoom->area;
      maxgold = 5000000;

      if (is_number(arg))
      {
         noi = atoi(arg);
         argument = one_argument(argument, arg);
         if (noi > mnoi)
         {
            act(AT_TELL, "$n tells you 'I don't sell that many items at" " once.'", keeper, NULL, ch, TO_VICT);
            ch->reply = keeper;
            return;
         }
      }

      obj = get_obj_carry(keeper, arg);
      cost = (get_cost(ch, keeper, obj, TRUE) * noi);
      if (cost <= 0 || !can_see_obj(ch, obj))
      {
         act(AT_TELL, "$n tells you 'I don't sell that -- try 'list'.'", keeper, NULL, ch, TO_VICT);
         ch->reply = keeper;
         return;
      }

      if (!IS_OBJ_STAT(obj, ITEM_INVENTORY) && (noi > 1))
      {
         interpret(keeper, "laugh");
         act(AT_TELL, "$n tells you 'I don't have enough of those in stock" " to sell more than one at a time.'", keeper, NULL, ch, TO_VICT);
         ch->reply = keeper;
         return;
      }
      if (IS_UNIQUE(ch, obj))
      {
         act(AT_TELL, "$n tells you 'You can only have 1 of those items on you at a time!", keeper, NULL, ch, TO_VICT);
         ch->reply = keeper;
         return;
      }
      if (ch->race == RACE_HOBBIT)
      {
         cost = cost * 85 / 100;
         if (cost < obj->cost)
            cost = obj->cost;
      }
      if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
      {
         level = POINT_LEVEL(LEARNED(ch, gsn_haggling), MASTERED(ch, gsn_haggling));
         cost = cost * (100-(level/2)) / 100;
         //Cannot go below actual cost of the item
         if (cost < obj->cost)
            cost = obj->cost;
         if (MASTERED(ch, gsn_haggling) == 4 && cost > obj->cost)
            cost = obj->cost;
      }
      if (ch->in_room->area->kingdom > 1)
      {
         tax = kingdom_table[ch->in_room->area->kingdom]->salestax;
         taxk = ch->in_room->area->salestax;
      }
      else
      {
         tax = 0;
         taxk = 0;
      }

      ctax = (cost * tax) / 1000;
      taxk = (cost * taxk) / 1000;
      if (ch->gold < (cost + ctax + taxk))
      {
         act(AT_TELL, "$n tells you 'You can't afford to buy $p.'", keeper, obj, ch, TO_VICT);
         ch->reply = keeper;
         return;
      }
      if (obj->level > ch->level)
      {
         act(AT_TELL, "$n tells you 'You can't use $p yet.'", keeper, obj, ch, TO_VICT);
         ch->reply = keeper;
         return;
      }

      if (IS_OBJ_STAT(obj, ITEM_PROTOTYPE) && get_trust(ch) < LEVEL_IMMORTAL)
      {
         act(AT_TELL, "$n tells you 'This is a only a prototype!  I can't sell you that...'", keeper, NULL, ch, TO_VICT);
         ch->reply = keeper;
         return;
      }

      if (get_ch_carry_number(ch) + get_obj_number(obj) > can_carry_n(ch))
      {
         send_to_char("You can't carry that many items.\n\r", ch);
         return;
      }

      if (get_ch_carry_weight(ch) + (get_obj_weight(obj) * noi) + (noi > 1 ? 2 : 0) > can_carry_w(ch))
      {
         send_to_char("You can't carry that much weight.\n\r", ch);
         return;
      }
      if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
         learn_from_success(ch, gsn_haggling, NULL);
      if (noi == 1)
      {
         act(AT_ACTION, "$n buys $p.", ch, obj, NULL, TO_ROOM);
         act(AT_ACTION, "You buy $p.", ch, obj, NULL, TO_CHAR);
      }
      else
      {
         sprintf(arg, "$n buys %d $p%s.", noi, (obj->short_descr[strlen(obj->short_descr) - 1] == 's' ? "" : "s"));
         act(AT_ACTION, arg, ch, obj, NULL, TO_ROOM);
         sprintf(arg, "You buy %d $p%s.", noi, (obj->short_descr[strlen(obj->short_descr) - 1] == 's' ? "" : "s"));
         act(AT_ACTION, arg, ch, obj, NULL, TO_CHAR);
         act(AT_ACTION, "$N puts them into a bag and hands it to you.", ch, NULL, keeper, TO_CHAR);
      }
      if (ch->pcdata->keeper == keeper->pIndexData->vnum)
      {
         cost = 0;
      }
      ch->gold -= (cost + ctax + taxk);
      keeper->gold += cost;
      
      if (IN_WILDERNESS(ch))
      {
         if ((town = find_town(ch->coord->x, ch->coord->y, ch->map)) != NULL)
         {         
            if (tax)
            {
               if ((dtown = find_town(ch->coord->x, ch->coord->y, ch->map)) != NULL)
               {
                  if ((get_current_hold(dtown) + (ctax/100)) <= dtown->hold)
                     dtown->coins += ctax;
               }
            }
            if (taxk)
            {
               if ((get_current_hold(town) + (taxk/100)) <= town->hold)
                  town->coins += taxk;
            }
         }
      }
      
      if (xIS_SET(keeper->act, ACT_CASTEMOB))
         keeper->pIndexData->gold += cost;

      if (!xIS_SET(keeper->act, ACT_CASTEMOB))
      {
         if (keeper->gold > maxgold)
         {
            boost_economy(keeper->in_room->area, keeper->gold - maxgold / 2);
            keeper->gold = maxgold / 2;
            act(AT_ACTION, "$n puts some gold into a large safe.", keeper, NULL, NULL, TO_ROOM);
         }
      }

      if (IS_OBJ_STAT(obj, ITEM_INVENTORY))
      {
         OBJ_DATA *buy_obj, *bag;
         SLAB_DATA *slab;
         int race;

         buy_obj = create_object(obj->pIndexData, obj->level);
         if (obj->value[6] > 99 && (obj->item_type == ITEM_WEAPON || obj->item_type == ITEM_ARMOR) && xIS_SET(obj->extra_flags, ITEM_FORGEABLE))
         {
            for (slab = first_slab; slab; slab = slab->next)
            {
               if (slab->vnum == obj->value[6])
                  break;
            }
            if (!slab)
            {
               bug("Do_Buy, Obj %d on Mob %d has an invalid slab.", obj->pIndexData->vnum, keeper->pIndexData->vnum);
            }
            else
            {         
               race = keeper->race;
               if (keeper->race < 0 || keeper->race >= MAX_RACE)
                  keeper->race = 0; //Needs a valid race      
               alter_forge_obj(keeper, buy_obj, create_object(get_obj_index(slab->vnum), 1), slab);	
               keeper->race = race;
            }
         }         

         /*
          * Due to grouped objects and carry limitations in SMAUG
          * The shopkeeper gives you a bag with multiple-buy,
          * and also, only one object needs be created with a count
          * set to the number bought.  -Thoric
          */
         if (noi > 1)
         {
            bag = create_object(get_obj_index(OBJ_VNUM_SHOPPING_BAG), 1);
            xSET_BIT(bag->extra_flags, ITEM_GROUNDROT);
            //bag->timer = 10; /* Blodkai, 4/97 */
            /* perfect size bag ;) */
            bag->value[0] = bag->weight + (buy_obj->weight * noi);
            buy_obj->count = noi;
            obj->pIndexData->count += (noi - 1);
            numobjsloaded += (noi - 1);
            obj_to_obj(buy_obj, bag);
            obj_to_char(bag, ch);
         }
         else
            obj_to_char(buy_obj, ch);
      }
      else
      {
         if (xIS_SET(keeper->act, ACT_CASTEMOB))
         {
            fobj = shop_oclean(keeper, obj);
            fobj->level = 1;
            obj_from_char(obj);
            obj_to_char(fobj, ch);
            delete_obj(obj->pIndexData);
            kupkeep(pArea, pRoom);
            fdarea(keeper, pArea->filename);
         }
         else
         {
            obj_from_char(obj);
            obj_to_char(obj, ch);
         }
      }

      return;
   }
}

/*
 * This is a new list function which allows level limits to follow as
 * arguments.  This code relies heavily on the items held by the shopkeeper
 * being sorted in descending order by level.  obj_to_char in handler.c was
 * modified to achieve this.  Anyways, this list command will now throw flags
 * at the levels passed as arguments.  This helps pick out equipment which is
 * usable by the char, etc.  This was done rather than just producing a list
 * of equip at desired level because there would be an inconsistency between
 * #.item on one list and #.item on the other.
 * Syntax:
 *      list            -       list the items for sale, should be sorted by
 *                              level
 *      list #          -       list items and throw a flag at #
 *      list #1 #2      -       list items and throw flags at #1 and #2
 * Note that this won't work in pets stores. Since you can't control
 * the order in which the pets repop you can't guarantee a sorted list.
 * Last Modified: May 25, 1997 -- Fireblade
 */
void do_list(CHAR_DATA * ch, char *argument)
{
   /* Constants for producing the flags */
   char *divleft = "-----------------------------------[ ";
   char *divright = " ]-----------------------------------";
   int cost;
   int level;


   if (xIS_SET(ch->in_room->room_flags, ROOM_MOUNT_SHOP))
   {
      ROOM_INDEX_DATA *pRoomIndexNext;
      CHAR_DATA *mount;
      bool found;

      pRoomIndexNext = get_room_index(ch->in_room->vnum + 1);
      if (!pRoomIndexNext)
      {
         bug("Do_list: bad mount shop at vnum %d.", ch->in_room->vnum);
         send_to_char("You can't do that here.\n\r", ch);
         return;
      }

      found = FALSE;

      for (mount = pRoomIndexNext->first_person; mount; mount = mount->next_in_room)
      {
         if (xIS_SET(mount->act, ACT_MOUNTSAVE) && IS_NPC(mount))
         {
            if (!found)
            {
               found = TRUE;
               send_to_pager("Mounts for sale:\n\r", ch);
               send_to_pager("Endurance  Armor  HP     MaxWeight  Cost     Name\n\r", ch);
               send_to_pager("----------------------------------------------------------------\n\r", ch);
            }
            cost = mount->m2;
            if (ch->race == RACE_HOBBIT)
            {
               cost = cost * 85 / 100;
            }
            if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
            {
               level = POINT_LEVEL(LEARNED(ch, gsn_haggling), MASTERED(ch, gsn_haggling));
               cost = cost * (100-(level/2)) / 100;
            }
            pager_printf(ch, "%s[%2d]       [%2d]   [%3d]  [%4d]    %8d  %s\n\r", char_color_str(AT_PERSON, ch), mount->m3, mount->armor, mount->max_hit, can_carry_w(mount), mount->m2, mount->short_descr);
         }
      }
      if (!found)
         send_to_char("Sorry, we're out of mounts right now.\n\r", ch);
      return;
   }
   else
   {
      char arg[MIL];
      char *rest;
      CHAR_DATA *keeper;
      OBJ_DATA *obj;
      bool found;

/*      bool listall; */
      int lower, upper;

      rest = one_argument(argument, arg);

      if ((keeper = find_keeper(ch)) == NULL)
         return;

      found = FALSE;
      lower = -2;
      upper = -1;

      /* Get the level limits for the flags */
      if (is_number(arg))
      {
         lower = atoi(arg);
         rest = one_argument(rest, arg);

         if (is_number(arg))
         {
            upper = atoi(arg);
            rest = one_argument(rest, arg);
         }
      }

      /* Fix the limits if reversed */
      if (lower >= upper)
      {
         int temp;

         temp = lower;
         lower = upper;
         upper = temp;
      }

      /* Loop until you see an object higher level than char */
      /* Note that this depends on the keeper having a sorted list */
      for (obj = keeper->first_carrying; obj; obj = obj->next_content)
      {
         if (obj->wear_loc == WEAR_NONE
            && can_see_obj(ch, obj) && (cost = get_cost(ch, keeper, obj, 2)) > 0 && (arg[0] == '\0' || nifty_is_name(arg, obj->name)))
         {
            if (!found)
            {
               found = TRUE;
               send_to_pager("&c&w[Lv Price] Item\n\r---------------\n\r", ch);
            }

            if (obj->level <= upper)
            {
               pager_printf(ch, "%s%2d%s\n\r", divleft, upper, divright);
               upper = -1;
            }

            if (obj->level < lower)
            {
               pager_printf(ch, "%s%2d%s\n\r", divleft, lower, divright);
               lower = -1;
            }
            if (ch->race == RACE_HOBBIT)
            {
               cost = cost * 85 / 100;
               if (cost < obj->cost)
                  cost = obj->cost;
            }
            if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
            {
               level = POINT_LEVEL(LEARNED(ch, gsn_haggling), MASTERED(ch, gsn_haggling));
               cost = cost * (100-(level/2)) / 100;
               //Cannot go below actual cost of the item
               if (cost < obj->cost)
                  cost = obj->cost;
               if (MASTERED(ch, gsn_haggling) == 4 && cost > obj->cost)
                  cost = obj->cost;
            }
            pager_printf( ch, "[%2d %5d] "MXPTAG ("list '%s' '%s'")"%s"MXPTAG ("/list") ".\n\r",obj->level, cost, obj->name, 
              obj->short_descr, capitalize( obj->short_descr ) );
         }
      }

      if (lower >= 0)
      {
         pager_printf(ch, "%s%2d%s\n\r", divleft, lower, divright);
      }

      if (!found)
      {
         if (arg[0] == '\0')
            send_to_char("You can't buy anything here.\n\r", ch);
         else
            send_to_char("You can't buy that here.\n\r", ch);
      }
      return;
   }
}

void do_sell(CHAR_DATA * ch, char *argument)
{
   AREA_DATA *pArea; /* Passing resets to special mobs */
   ROOM_INDEX_DATA *pRoom;
   char buf[MSL];
   char arg[MIL];
   CHAR_DATA *keeper;
   OBJ_DATA *obj;
   OBJ_DATA *iobj;
   OBJ_DATA *fobj;
   int cost;
   int outcome = 0;
   int objcount = 0;
   int maxaobj = 0;
   int level;

   one_argument(argument, arg);

   if (arg[0] == '\0')
   {
      send_to_char("Sell what?\n\r", ch);
      return;
   }

   if ((keeper = find_keeper(ch)) == NULL)
      return;

   if ((obj = get_obj_carry(ch, arg)) == NULL)
   {
      act(AT_TELL, "$n tells you 'You don't have that item.'", keeper, NULL, ch, TO_VICT);
      ch->reply = keeper;
      return;
   }

   if (!can_drop_obj(ch, obj))
   {
      send_to_char("You can't let go of it!\n\r", ch);
      return;
   }

/* Prevents giving of special items (rare items) -- Xerves 3/24/98*/
   if (IS_OBJ_STAT(obj, ITEM_NOGIVE))
   {
      send_to_char("Xerves should personally smack you for trying to find a loophole.  Cannot sell this item.\n\r", ch);
      return;
   }


   if (obj->timer > 0)
   {
      act(AT_TELL, "$n tells you, '$p is depreciating in value too quickly...'", keeper, obj, ch, TO_VICT);
      return;
   }

   if ((cost = get_cost(ch, keeper, obj, FALSE)) <= 0)
   {
      act(AT_ACTION, "$n looks uninterested in $p.", keeper, obj, ch, TO_VICT);
      return;
   }
   if (ch->race == RACE_HOBBIT)
   {
      cost = cost * 115 / 100;
      if (cost > obj->cost)
         cost = obj->cost;
   }
   if (!IS_NPC(ch) && LEARNED(ch, gsn_swindling) > 0)
   {
      level = POINT_LEVEL(LEARNED(ch, gsn_swindling), MASTERED(ch, gsn_swindling));
      cost = cost * (100+(level/2)) / 100;
      //Cannot go above actual cost of the item
      if (cost > obj->cost)
         cost = obj->cost;
      if (MASTERED(ch, gsn_swindling) == 4 && cost < obj->cost)
         cost = obj->cost;
      learn_from_success(ch, gsn_swindling, NULL);
   }
   if (xIS_SET(keeper->act, ACT_CASTEMOB))
      if ((keeper->pIndexData->m1 != 0)
         && (keeper->pIndexData->m2 != 0) && (keeper->pIndexData->m3 != 0) && (keeper->pIndexData->m4 != 0) && (keeper->pIndexData->m5 != 0))
      {
         outcome = keeper->gold - cost;
         if (outcome < keeper->m3)
         {
            act(AT_TELL, "$n tells you, 'I cannot afford to sell you $p, sorry'", keeper, obj, ch, TO_VICT);
            return;
         }
         if (obj->cost < keeper->m1 || obj->cost > keeper->m2)
         {
            sprintf(buf, "$n tells you, '$p is not worth %d to %d gold'", keeper->m1, keeper->m2);
            act(AT_TELL, buf, keeper, obj, ch, TO_VICT);
            return;
         }
      }
   if (xIS_SET(keeper->act, ACT_CASTEMOB))
   {
      if (keeper->pIndexData->m6 == 0)
         maxaobj = 20;
      else
         maxaobj = keeper->pIndexData->m6;

      for (iobj = keeper->first_carrying; iobj; iobj = iobj->next_content)
      {
         if (iobj->wear_loc == WEAR_NONE)
            objcount++;
      }
      if (objcount + 1 > maxaobj)
      {
         sprintf(buf, "$n tells you, '$p exceeds my limit of %d items.'", maxaobj);
         act(AT_TELL, buf, keeper, obj, ch, TO_VICT);
         return;
      }
   }


   if (cost >= keeper->gold)
   {
      act(AT_TELL, "$n tells you, '$p is worth more than I can afford...'", keeper, obj, ch, TO_VICT);
      return;
   }

   separate_obj(obj);
   act(AT_ACTION, "$n sells $p.", ch, obj, NULL, TO_ROOM);
   sprintf(buf, "You sell $p for %d gold piece%s.", cost, cost == 1 ? "" : "s");
   act(AT_ACTION, buf, ch, obj, NULL, TO_CHAR);
   if (ch->pcdata->keeper == keeper->pIndexData->vnum)
   {
      cost = 0;
   }
   ch->gold += cost;
   keeper->gold -= cost;
   if (xIS_SET(keeper->act, ACT_CASTEMOB))
      keeper->pIndexData->gold -= cost;
   if (keeper->gold < 0)
      keeper->gold = 0;

   pRoom = keeper->in_room;
   pArea = pRoom->area;

   if (obj->item_type == ITEM_TRASH)
   {
      extract_obj(obj);
      if (xIS_SET(keeper->act, ACT_CASTEMOB))
         kupkeep(pArea, pRoom);
   }
   else
   {
      if (xIS_SET(keeper->act, ACT_CASTEMOB))
      {
         fobj = shop_ocreate(keeper, obj);
         obj_from_char(obj);
         extract_obj(obj);
         obj_to_char(fobj, keeper);
         kupkeep(pArea, pRoom);
         fdarea(keeper, pArea->filename);

      }
      else
      {
         obj_from_char(obj);
         obj_to_char(obj, keeper);
      }

   }

   return;
}

void do_value(CHAR_DATA * ch, char *argument)
{
   char buf[MSL];
   CHAR_DATA *keeper;
   OBJ_DATA *obj;
   int level;
   int cost;

   if (argument[0] == '\0')
   {
      send_to_char("Value what?\n\r", ch);
      return;
   }

   if ((keeper = find_keeper(ch)) == NULL)
      return;

   if ((obj = get_obj_carry(ch, argument)) == NULL)
   {
      act(AT_TELL, "$n tells you 'You don't have that item.'", keeper, NULL, ch, TO_VICT);
      ch->reply = keeper;
      return;
   }

   if (!can_drop_obj(ch, obj))
   {
      send_to_char("You can't let go of it!\n\r", ch);
      return;
   }

   if ((cost = get_cost(ch, keeper, obj, FALSE)) <= 0)
   {
      act(AT_ACTION, "$n looks uninterested in $p.", keeper, obj, ch, TO_VICT);
      return;
   }
   if (ch->race == RACE_HOBBIT)
   {
      cost = cost * 115 / 100;
      if (cost > obj->cost)
         cost = obj->cost;
   }
   if (!IS_NPC(ch) && LEARNED(ch, gsn_swindling) > 0)
   {
      level = POINT_LEVEL(LEARNED(ch, gsn_swindling), MASTERED(ch, gsn_swindling));
      cost = cost * (100+(level/2)) / 100;
      //Cannot go above actual cost of the item
      if (cost > obj->cost)
         cost = obj->cost;
      if (MASTERED(ch, gsn_swindling) == 4 && cost < obj->cost)
         cost = obj->cost;
   }
   sprintf(buf, "$n tells you 'I'll give you %d gold coins for $p.'", cost);
   act(AT_TELL, buf, keeper, obj, ch, TO_VICT);
   ch->reply = keeper;

   return;
}

/*
 * Repair a single object. Used when handling "repair all" - Gorog
 */
void repair_one_obj(CHAR_DATA * ch, CHAR_DATA * keeper, OBJ_DATA * obj, char *arg, int maxgold, char *fixstr, char *fixstr2)
{
   char buf[MSL];
   char arg1[MIL];
   int cost = 0;
   int level;
   
   if (obj->item_type == ITEM_WEAPON && (obj->value[4] > 0  || obj->value[5] > 0))
   {
      act(AT_TELL, "$n tells you, 'Sorry, I cannot repair $p because a great magical power prevents me.'", keeper, obj, ch, TO_VICT);
      return;
   }

   if (!can_drop_obj(ch, obj))
      ch_printf(ch, "You can't let go of %s.\n\r", obj->name);
   else if ((cost = get_repaircost(keeper, obj)) < 0)
   {
      if (cost != -2)
         act(AT_TELL, "$n tells you, 'Sorry, I can't do anything with $p.'", keeper, obj, ch, TO_VICT);
      else
         act(AT_TELL, "$n tells you, '$p looks fine to me!'", keeper, obj, ch, TO_VICT);
   }
   /* "repair all" gets a 10% surcharge - Gorog */

   else if ((cost = strcmp("all", arg) ? cost : 11 * cost / 10) > ch->gold)
   {
      if (ch->race == RACE_HOBBIT)
      {
         cost = cost * 85 / 100;
      }
      if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
      {
         level = POINT_LEVEL(LEARNED(ch, gsn_haggling), MASTERED(ch, gsn_haggling));
         cost = cost * (100-(level/2)) / 100;
      }
      sprintf(buf, "$N tells you, 'It will cost %d piece%s of gold to %s %s...'", cost, cost == 1 ? "" : "s", fixstr, obj->name);
      act(AT_TELL, buf, ch, NULL, keeper, TO_CHAR);
      act(AT_TELL, "$N tells you, 'Which I see you can't afford.'", ch, NULL, keeper, TO_CHAR);
   }
   else
   {
      if (ch->race == RACE_HOBBIT)
      {
         cost = cost * 85 / 100;
      }
      if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
      {
         level = POINT_LEVEL(LEARNED(ch, gsn_haggling), MASTERED(ch, gsn_haggling));
         cost = cost * (100-(level/2)) / 100;
      }
      one_argument(arg, arg1);
      if (!str_cmp(arg1, "cost"))
      {
         sprintf(buf, "$N informs you it will cost %d to fix $p.", cost);
         act(AT_ACTION, buf, ch, obj, keeper, TO_CHAR);
         return;
      }
      if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
         learn_from_success(ch, gsn_haggling, NULL);
      sprintf(buf, "$n gives $p to $N, who quickly %s it.", fixstr2);
      act(AT_ACTION, buf, ch, obj, keeper, TO_ROOM);
      sprintf(buf, "$N charges you %d gold piece%s to %s $p.", cost, cost == 1 ? "" : "s", fixstr);
      act(AT_ACTION, buf, ch, obj, keeper, TO_CHAR);
      ch->gold -= cost;
      keeper->gold += cost;
      if (keeper->gold < 0)
         keeper->gold = 0;
      else if (keeper->gold > maxgold)
      {
         boost_economy(keeper->in_room->area, keeper->gold - maxgold / 2);
         keeper->gold = maxgold / 2;
         act(AT_ACTION, "$n puts some gold into a large safe.", keeper, NULL, NULL, TO_ROOM);
      }

      switch (obj->item_type)
      {
         default:
            send_to_char("For some reason, you think you got ripped off...\n\r", ch);
            break;
         case ITEM_ARMOR:
            if (IS_SET(obj->wear_flags, ITEM_WEAR_SHIELD))
            {
               obj->value[0] = obj->value[1];
            }
            else
               obj->value[3] = INIT_ARMOR_CONDITION;
            break;
         case ITEM_WEAPON:
            obj->value[0] = INIT_ARMOR_CONDITION;
            break;
         case ITEM_SHEATH:
            obj->value[3] = INIT_ARMOR_CONDITION;
      }

      oprog_repair_trigger(ch, obj);
   }
}

void do_break(CHAR_DATA *ch, char *argument)
{
    int scnt;
    OBJ_DATA *first_obj, *fobj;
    SLAB_DATA *slab;
    FORGE_DATA *forge;
    char arg[MIL];
    int race;
    int slabs;
    char *wbuf;
    char wname[MIL]; //for stripping to check race
    OBJ_DATA *repair;
    int chance;
    
    argument = one_argument(argument, arg);
       
    for(first_obj = ch->first_carrying; first_obj; first_obj = first_obj->next_content)
    {
       if( is_name(arg, first_obj->name) && xIS_SET(first_obj->extra_flags, ITEM_FORGEABLE)
       &&  first_obj->wear_loc == WEAR_NONE )
          break;
    }  
    if (!first_obj)
    {  
       for(first_obj = ch->in_room->first_content; first_obj; first_obj = first_obj->next_content)
       {
          if( is_name(arg, first_obj->name) && xIS_SET(first_obj->extra_flags, ITEM_FORGEABLE)
          &&  first_obj->wear_loc == WEAR_NONE )
             break;
       }          
    }
    if (!first_obj)
    {
       send_to_char("That item is either not in your inventory or on the ground.\n\r", ch);
       return;
    }       
    if (first_obj->item_type == ITEM_PROJECTILE)
    {
       send_to_char("You cannot break down arrows.\n\r", ch);
       return;
    }
    if (IS_OBJ_STAT(first_obj, ITEM_KINGDOMEQ))
    {
       send_to_char("Cannot break this item, it is kingdom eq\n\r", ch);
       return;
    }
    for(slab = first_slab; slab; slab = slab->next)
    {
       if(is_name(slab->adj, first_obj->name) )
          break;
    }
    if (!slab)
    {
       send_to_char("Error:  Did not find the Ore, tell an immortal.\n\r", ch);
       bug("forge: Forgeable has an invalid ore.");
       return;
    }
    for (forge = first_forge; forge; forge = forge->next)
    {
       if (forge->vnum == first_obj->pIndexData->vnum)
          break;
    }
    if (!forge)
    {
       send_to_char("Error:  Error with the weapon/armor, tell an immotal.\n\r", ch);
       bug("Forge: Could not find the weapon/armor in the forge list");
       return;
    }
    for (repair = ch->first_carrying; repair; repair = repair->next_content)
    {
       if (repair->item_type == ITEM_REPAIR)
          break;
    }
    if (!repair)
    {
      send_to_char("You do not have a hammer in which to break with.\n\r", ch);
       return;
    }
    separate_obj(first_obj);
    separate_obj(repair);

    wbuf = first_obj->name;
    wbuf = one_argument(wbuf, wname);
    wbuf = one_argument(wbuf, wname);
    wname[0] = UPPER(wname[0]);
    if (!str_prefix(wname, "Fairy"))
       race = 5;
    else if (!str_prefix(wname, "Hobbit"))
       race = 4;
    else if (!str_prefix(wname, "Ogre"))
       race = 3;
    else if (!str_prefix(wname, "Dwarven"))
       race = 2;
    else if (!str_prefix(wname, "Elven"))
       race = 1;
    else if (!str_prefix(wname, "Human"))
       race = 0;
    else
    {
       bug("Invalid Race Name %s, on player %s", wname, ch->name);
       send_to_char("Error: Invalid Race Name, tell an immortal.\n\r", ch);
       return;
    }
       
    if (race == 5) //Fairy
       slabs = forge->slabnum * 4 / 10;
    else if (race == 4) //Hobbit
       slabs = forge->slabnum * 6 / 10;
    else if (race == 1) //Elf
       slabs = forge->slabnum * 85 / 100;
    else if (race == 2) //Dwarf
       slabs = forge->slabnum * 12 / 10;
    else if (race == 3) //Ogre
       slabs = forge->slabnum * 15 / 10;
    else //Human
       slabs = forge->slabnum;
       
    if (ch->race == RACE_DWARF)
       slabs = slabs * number_range(58, 63) / 100;
    else
       slabs = slabs * number_range(40, 53) / 100;
       
    if (slabs < 1)
       slabs = 1;
       
    chance = 70;
    if (ch->race == RACE_DWARF)
       chance = 95;
    if (number_range(1, 100) <= chance)
    {        
       for (scnt = 0; scnt < slabs; scnt++)
       {         
          fobj = create_object(get_obj_index(slab->vnum), 1);
          obj_to_room(fobj, ch->in_room, ch);           
       }    
       ch_printf(ch, "You pull out your handy repair hammer and begin to beat the hell out of %s.\n\r", first_obj->short_descr);
       ch_printf(ch, "You get lucky and successfully split it into %d slabs.\n\r", slabs);
       extract_obj(first_obj);
       chance = 20;
       if (ch->race == RACE_DWARF)
          chance = 5;
       if (number_range(1, 100) <= chance)
       {
          act(AT_RED, "$p starts to show some wear after its use.", ch, repair, NULL, TO_CHAR);
          if (--repair->value[0] <= 0)
          {
             act(AT_RED, "With the last strike, $p breaks and becomes useless.", ch, repair, NULL, TO_CHAR);
             separate_obj(repair);
             extract_obj(repair);
          }
       }
       return; 
    }
    else
    {
       ch_printf(ch, "You pull out your handy repair hammer and begin to beat the hell out of %s.\n\r", first_obj->short_descr);
       ch_printf(ch, "However, the only thing you get is a broken, useless %s.\n\r", first_obj->short_descr);
       extract_obj(first_obj);
       if (number_range(1, 100) <= 20)
       {
          act(AT_RED, "$p starts to show some wear after its use.", ch, repair, NULL, TO_CHAR);
          if (--repair->value[0] <= 0)
          {
             act(AT_RED, "With the last strike, $p breaks and becomes useless.", ch, repair, NULL, TO_CHAR);
             separate_obj(repair);
             extract_obj(repair);
          }
       }
    }
}
void do_fix(CHAR_DATA *ch, char *argument)
{
   OBJ_DATA *obj;
   OBJ_DATA *repair;
   int points;
   char arg[MIL];
   if (argument[0] == '\0')
   {
      send_to_char("Syntax:  fix <item needing repaired> <item which to do the repairing>\n\r", ch);
      return;
   }
   argument = one_argument(argument, arg);
   
   if ((obj = get_obj_carry(ch, arg)) == NULL)
   {
      if ((obj= get_obj_wear(ch, arg)) == NULL)
      {
         send_to_char("The item you are looking for is not in your inventory or being wielded.\n\r", ch);
         return;
      }
   }
   if (obj->item_type != ITEM_ARMOR && obj->item_type != ITEM_WEAPON && obj->item_type != ITEM_SHEATH)
   {
      send_to_char("You can only fix armor, weapons, and sheaths.\n\r", ch);
      return;
   }
   for (repair = ch->first_carrying; repair; repair = repair->next_content)
   {
      if (repair->item_type == ITEM_REPAIR)
         break;
   }
   if (!repair)
   {
      send_to_char("You do not have a hammer in which to repair with.\n\r", ch);
      return;
   }
   if (obj->item_type == ITEM_WEAPON && (obj->value[4] > 0  || obj->value[5] > 0))
   {
      send_to_char("There is a magical property to this weapon preventing normal repairs.\n\r", ch);
      return;
   }
   if (obj->item_type == ITEM_WEAPON)
   {
      if (obj->value[0] == INIT_ARMOR_CONDITION)
      {
         send_to_char("That item is fully repaired!\n\r", ch);
         return;
      }
   }       
   if (obj->item_type == ITEM_ARMOR && !IS_SET(obj->wear_flags, ITEM_WEAR_SHIELD))
   {
      if (obj->value[3] == INIT_ARMOR_CONDITION)
      {
         send_to_char("That item is fully repaired!\n\r", ch);
         return;
      }
   }
   if (obj->item_type == ITEM_ARMOR && IS_SET(obj->wear_flags, ITEM_WEAR_SHIELD))
   {
      if (obj->value[0] == obj->value[1])
      {
         send_to_char("That item is fully repaired!\n\r", ch);
         return;
      }
   }
   if (obj->item_type == ITEM_SHEATH)
   {
      if (obj->value[3] == INIT_ARMOR_CONDITION)
      {
         send_to_char("That item is fully repaired!\n\r", ch);
         return;
      }
   }
   points = POINT_LEVEL(GET_POINTS(ch, gsn_repair, 0, 1), GET_MASTERY(ch, gsn_repair, 0, 1));  
   separate_obj(obj);
   separate_obj(repair);
   if (ch->race == RACE_DWARF)
      points += 60;
   if (number_range(1, 100) <= UMIN(95, repair->value[2] + (points / 3)))
   {
      int fixed = 0;
      int repairv = number_range(70*repair->value[3], 130*repair->value[3]);
      repairv = repairv / 100;
      repairv = repairv+points/2;
      if (obj->item_type == ITEM_WEAPON)
      {
         obj->value[0] += UMAX(1, repairv);
         if (obj->value[0] >= INIT_ARMOR_CONDITION)
         {
            obj->value[0] = INIT_ARMOR_CONDITION;
            fixed = 1;
         }
      }
      if (obj->item_type == ITEM_ARMOR && !IS_SET(obj->wear_flags, ITEM_WEAR_SHIELD))
      {
         obj->value[3] += UMAX(1, repairv);
         if (obj->value[3] >= INIT_ARMOR_CONDITION)
         {
            obj->value[3] = INIT_ARMOR_CONDITION;
            fixed = 1;
         }
      }
      if (obj->item_type == ITEM_ARMOR && IS_SET(obj->wear_flags, ITEM_WEAR_SHIELD))
      {
         obj->value[0] += UMAX(1, repairv * obj->value[1] / 1000);
         if (obj->value[0] >= obj->value[1])
         {
            obj->value[0] = obj->value[1];
            fixed = 1;
         }
      }
      if (obj->item_type == ITEM_SHEATH)
      {
         obj->value[3] += UMAX(1, repairv);
         if (obj->value[3] >= INIT_ARMOR_CONDITION)
         {
            obj->value[3] = INIT_ARMOR_CONDITION;
            fixed = 1;
         }
      }
      if (fixed == 1)
         act(AT_GREEN, "You successfully repair $p to its perfect form.", ch, obj, NULL, TO_CHAR);
      else
         act(AT_GREEN, "You successfully repair $p.", ch, obj, NULL, TO_CHAR);
      learn_from_success(ch, gsn_repair, NULL);  
   }
   else
   {
      act(AT_DGREEN, "You fail to repair $p,", ch, obj, NULL, TO_CHAR);
      learn_from_failure(ch, gsn_repair, NULL);  
   }
   points = POINT_LEVEL(GET_POINTS(ch, gsn_repair, 0, 1), GET_MASTERY(ch, gsn_repair, 0, 1));  
   if (ch->race == RACE_DWARF)
      points += 60;
   points = 60+(points/3);
   points += URANGE(7, get_curr_lck(ch)-14, -5);
   points = URANGE(60, points, 95);
   if (number_range(1,100) > points)
   {
      act(AT_RED, "$p starts to show some wear after its use.", ch, repair, NULL, TO_CHAR);
      if (--repair->value[0] <= 0)
      {
         act(AT_RED, "With the last strike, $p breaks and becomes useless.", ch, repair, NULL, TO_CHAR);
         extract_obj(repair);
      }
   }
   return;
}  
      
void do_repair(CHAR_DATA * ch, char *argument)
{
   CHAR_DATA *keeper;
   OBJ_DATA *obj;
   char *fixstr;
   char arg1[MSL];
   char arg2[MSL];
   char *arg2p = arg2;
   char *fixstr2;
   int maxgold;

   if (argument[0] == '\0')
   {
      send_to_char("Repair what?\n\r", ch);
      return;
   }

   if ((keeper = find_fixer(ch)) == NULL)
      return;

   maxgold = 10000000;
   switch (keeper->pIndexData->rShop->shop_type)
   {
      default:
      case SHOP_FIX:
         fixstr = "repair";
         fixstr2 = "repairs";
         break;
      case SHOP_RECHARGE:
         fixstr = "recharge";
         fixstr2 = "recharges";
         break;
   }

   if (!strcmp(argument, "all"))
   {
      for (obj = ch->first_carrying; obj; obj = obj->next_content)
      {
         if (obj->wear_loc == WEAR_NONE && can_see_obj(ch, obj) && can_see_obj(keeper, obj) && (obj->item_type == ITEM_ARMOR
               ||   obj->item_type == ITEM_WEAPON
               || obj->item_type == ITEM_SHEATH))
            repair_one_obj(ch, keeper, obj, argument, maxgold, fixstr, fixstr2);
      }
      return;
   }
   one_argument(argument, arg1);
   if (!str_cmp(arg1, "cost"))
      arg2p = one_argument(argument, arg1);
   else
      arg2p = argument;
   if ((obj = get_obj_carry(ch, arg2p)) == NULL)
   {
      act(AT_TELL, "$n tells you 'You don't have that item.'", keeper, NULL, ch, TO_VICT);
      ch->reply = keeper;
      return;
   }

   repair_one_obj(ch, keeper, obj, argument, maxgold, fixstr, fixstr2);
}

void appraise_all(CHAR_DATA * ch, CHAR_DATA * keeper, char *fixstr)
{
   OBJ_DATA *obj;
   char buf[MSL], *pbuf = buf;
   int cost = 0, total = 0;

   for (obj = ch->first_carrying; obj != NULL; obj = obj->next_content)
   {
      if (obj->wear_loc == WEAR_NONE
         && can_see_obj(ch, obj)
         && (obj->item_type == ITEM_ARMOR || obj->item_type == ITEM_WEAPON))
      {
         if (!can_drop_obj(ch, obj))
            ch_printf(ch, "You can't let go of %s.\n\r", obj->name);
         else if ((cost = get_repaircost(keeper, obj)) < 0)
         {
            if (cost != -2)
               act(AT_TELL, "$n tells you, 'Sorry, I can't do anything with $p.'", keeper, obj, ch, TO_VICT);
            else
               act(AT_TELL, "$n tells you, '$p looks fine to me!'", keeper, obj, ch, TO_VICT);
         }
         else
         {
            sprintf(buf, "$N tells you, 'It will cost %d piece%s of gold to %s %s'", cost, cost == 1 ? "" : "s", fixstr, obj->name);
            act(AT_TELL, buf, ch, NULL, keeper, TO_CHAR);
            total += cost;
         }
      }
   }
   if (total > 0)
   {
      send_to_char("\n\r", ch);
      sprintf(buf, "$N tells you, 'It will cost %d piece%s of gold in total.'", total, cost == 1 ? "" : "s");
      act(AT_TELL, buf, ch, NULL, keeper, TO_CHAR);
      strcpy(pbuf, "$N tells you, 'Remember there is a 10% surcharge for repair all.'");
      act(AT_TELL, buf, ch, NULL, keeper, TO_CHAR);
   }
}


void do_appraise(CHAR_DATA * ch, char *argument)
{
   char buf[MSL];
   char arg[MIL];
   CHAR_DATA *keeper;
   OBJ_DATA *obj;
   int cost;
   char *fixstr;
   int level;

   one_argument(argument, arg);

   if (arg[0] == '\0')
   {
      send_to_char("Appraise what?\n\r", ch);
      return;
   }

   if ((keeper = find_fixer(ch)) == NULL)
      return;

   switch (keeper->pIndexData->rShop->shop_type)
   {
      default:
      case SHOP_FIX:
         fixstr = "repair";
         break;
      case SHOP_RECHARGE:
         fixstr = "recharge";
         break;
   }

   if (!strcmp(arg, "all"))
   {
      appraise_all(ch, keeper, fixstr);
      return;
   }

   if ((obj = get_obj_carry(ch, arg)) == NULL)
   {
      act(AT_TELL, "$n tells you 'You don't have that item.'", keeper, NULL, ch, TO_VICT);
      ch->reply = keeper;
      return;
   }

   if (!can_drop_obj(ch, obj))
   {
      send_to_char("You can't let go of it.\n\r", ch);
      return;
   }

   if ((cost = get_repaircost(keeper, obj)) < 0)
   {
      if (cost != -2)
         act(AT_TELL, "$n tells you, 'Sorry, I can't do anything with $p.'", keeper, obj, ch, TO_VICT);
      else
         act(AT_TELL, "$n tells you, '$p looks fine to me!'", keeper, obj, ch, TO_VICT);
      return;
   }
   if (ch->race == RACE_HOBBIT)
   {
     cost = cost * 85 / 100;
   }
   if (!IS_NPC(ch) && LEARNED(ch, gsn_haggling) > 0)
   {
      level = POINT_LEVEL(LEARNED(ch, gsn_haggling), MASTERED(ch, gsn_haggling));
      cost = cost * (100-(level/2)) / 100;
   }
   sprintf(buf, "$N tells you, 'It will cost %d piece%s of gold to %s that...'", cost, cost == 1 ? "" : "s", fixstr);
   act(AT_TELL, buf, ch, NULL, keeper, TO_CHAR);
   if (cost > ch->gold)
      act(AT_TELL, "$N tells you, 'Which I see you can't afford.'", ch, NULL, keeper, TO_CHAR);

   return;
}


/* ------------------ Shop Building and Editing Section ----------------- */


void do_makeshop(CHAR_DATA * ch, char *argument)
{
   SHOP_DATA *shop;
   sh_int vnum;
   MOB_INDEX_DATA *mob;

   if (!argument || argument[0] == '\0')
   {
      send_to_char("Usage: makeshop <mobvnum>\n\r", ch);
      return;
   }

   vnum = atoi(argument);

   if ((mob = get_mob_index(vnum)) == NULL)
   {
      send_to_char("Mobile not found.\n\r", ch);
      return;
   }

   if (!can_medit(ch, mob))
      return;

   if (mob->pShop)
   {
      send_to_char("This mobile already has a shop.\n\r", ch);
      return;
   }

   CREATE(shop, SHOP_DATA, 1);

   LINK(shop, first_shop, last_shop, next, prev);
   shop->keeper = vnum;
   shop->profit_buy = 120;
   shop->profit_sell = 90;
   shop->open_hour = 0;
   shop->close_hour = 23;
   mob->pShop = shop;
   send_to_char("Done.\n\r", ch);
   return;
}


void do_shopset(CHAR_DATA * ch, char *argument)
{
   SHOP_DATA *shop;
   MOB_INDEX_DATA *mob, *mob2;
   char arg1[MIL];
   char arg2[MIL];
   sh_int vnum;
   int value;

   argument = one_argument(argument, arg1);
   argument = one_argument(argument, arg2);

   if (arg1[0] == '\0' || arg2[0] == '\0')
   {
      send_to_char("Usage: shopset <mob vnum> <field> value\n\r", ch);
      send_to_char("\n\rField being one of:\n\r", ch);
      send_to_char("  buy0 buy1 buy2 buy3 buy4 buy sell open close keeper\n\r", ch);
      return;
   }

   vnum = atoi(arg1);

   if ((mob = get_mob_index(vnum)) == NULL)
   {
      send_to_char("Mobile not found.\n\r", ch);
      return;
   }

   if (!can_medit(ch, mob))
      return;

   if (!mob->pShop)
   {
      send_to_char("This mobile doesn't keep a shop.\n\r", ch);
      return;
   }
   shop = mob->pShop;
   value = atoi(argument);

   if (!str_cmp(arg2, "buy0"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[0] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "buy1"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[1] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "buy2"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[2] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "buy3"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[3] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "buy4"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      shop->buy_type[4] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "buy"))
   {
      if (value <= (shop->profit_sell + 5) || value > 1000)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->profit_buy = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "sell"))
   {
      if (value < 0 || value >= (shop->profit_buy - 5))
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->profit_sell = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "open"))
   {
      if (value < 0 || value > 23)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->open_hour = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "close"))
   {
      if (value < 0 || value > 23)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      shop->close_hour = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "keeper"))
   {
      if ((mob2 = get_mob_index(vnum)) == NULL)
      {
         send_to_char("Mobile not found.\n\r", ch);
         return;
      }
      if (!can_medit(ch, mob))
         return;
      if (mob2->pShop)
      {
         send_to_char("That mobile already has a shop.\n\r", ch);
         return;
      }
      mob->pShop = NULL;
      mob2->pShop = shop;
      shop->keeper = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   do_shopset(ch, "");
   return;
}


void do_shopstat(CHAR_DATA * ch, char *argument)
{
   SHOP_DATA *shop;
   MOB_INDEX_DATA *mob;
   sh_int vnum;

   if (argument[0] == '\0')
   {
      send_to_char("Usage: shopstat <keeper vnum>\n\r", ch);
      return;
   }

   vnum = atoi(argument);

   if ((mob = get_mob_index(vnum)) == NULL)
   {
      send_to_char("Mobile not found.\n\r", ch);
      return;
   }

   if (!mob->pShop)
   {
      send_to_char("This mobile doesn't keep a shop.\n\r", ch);
      return;
   }
   shop = mob->pShop;

   ch_printf(ch, "Keeper: %d  %s\n\r", shop->keeper, mob->short_descr);
   ch_printf(ch, "buy0 [%s]  buy1 [%s]  buy2 [%s]  buy3 [%s]  buy4 [%s]\n\r",
      o_types[shop->buy_type[0]], o_types[shop->buy_type[1]], o_types[shop->buy_type[2]], o_types[shop->buy_type[3]], o_types[shop->buy_type[4]]);
   ch_printf(ch, "Profit:  buy %3d%%  sell %3d%%\n\r", shop->profit_buy, shop->profit_sell);
   ch_printf(ch, "Hours:   open %2d  close %2d\n\r", shop->open_hour, shop->close_hour);
   return;
}


void do_shops(CHAR_DATA * ch, char *argument)
{
   SHOP_DATA *shop;

   if (!first_shop)
   {
      send_to_char("There are no shops.\n\r", ch);
      return;
   }

   set_char_color(AT_NOTE, ch);
   for (shop = first_shop; shop; shop = shop->next)
      ch_printf(ch, "Keeper: %5d Buy: %3d Sell: %3d Open: %2d Close: %2d Buy: %2d %2d %2d %2d %2d\n\r",
         shop->keeper, shop->profit_buy, shop->profit_sell,
         shop->open_hour, shop->close_hour, shop->buy_type[0], shop->buy_type[1], shop->buy_type[2], shop->buy_type[3], shop->buy_type[4]);
   return;
}

/* -------------- Repair Shop Building and Editing Section -------------- */


void do_makerepair(CHAR_DATA * ch, char *argument)
{
   REPAIR_DATA *repair;
   sh_int vnum;
   MOB_INDEX_DATA *mob;

   if (!argument || argument[0] == '\0')
   {
      send_to_char("Usage: makerepair <mobvnum>\n\r", ch);
      return;
   }

   vnum = atoi(argument);

   if ((mob = get_mob_index(vnum)) == NULL)
   {
      send_to_char("Mobile not found.\n\r", ch);
      return;
   }

   if (!can_medit(ch, mob))
      return;

   if (mob->rShop)
   {
      send_to_char("This mobile already has a repair shop.\n\r", ch);
      return;
   }

   CREATE(repair, REPAIR_DATA, 1);

   LINK(repair, first_repair, last_repair, next, prev);
   repair->keeper = vnum;
   repair->profit_fix = 100;
   repair->shop_type = SHOP_FIX;
   repair->open_hour = 0;
   repair->close_hour = 23;
   mob->rShop = repair;
   send_to_char("Done.\n\r", ch);
   return;
}


void do_repairset(CHAR_DATA * ch, char *argument)
{
   REPAIR_DATA *repair;
   MOB_INDEX_DATA *mob, *mob2;
   char arg1[MIL];
   char arg2[MIL];
   sh_int vnum;
   int value;

   argument = one_argument(argument, arg1);
   argument = one_argument(argument, arg2);

   if (arg1[0] == '\0' || arg2[0] == '\0')
   {
      send_to_char("Usage: repairset <mob vnum> <field> value\n\r", ch);
      send_to_char("\n\rField being one of:\n\r", ch);
      send_to_char("  fix0 fix1 fix2 profit type open close keeper\n\r", ch);
      return;
   }

   vnum = atoi(arg1);

   if ((mob = get_mob_index(vnum)) == NULL)
   {
      send_to_char("Mobile not found.\n\r", ch);
      return;
   }

   if (!can_medit(ch, mob))
      return;

   if (!mob->rShop)
   {
      send_to_char("This mobile doesn't keep a repair shop.\n\r", ch);
      return;
   }
   repair = mob->rShop;
   value = atoi(argument);

   if (!str_cmp(arg2, "fix0"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      repair->fix_type[0] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "fix1"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      repair->fix_type[1] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "fix2"))
   {
      if (!is_number(argument))
         value = get_otype(argument);
      if (value < 0 || value > MAX_ITEM_TYPE)
      {
         send_to_char("Invalid item type!\n\r", ch);
         return;
      }
      repair->fix_type[2] = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "profit"))
   {
      if (value < 1 || value > 1000)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      repair->profit_fix = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "type"))
   {
      if (value < 1 || value > 2)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      repair->shop_type = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "open"))
   {
      if (value < 0 || value > 23)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      repair->open_hour = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "close"))
   {
      if (value < 0 || value > 23)
      {
         send_to_char("Out of range.\n\r", ch);
         return;
      }
      repair->close_hour = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   if (!str_cmp(arg2, "keeper"))
   {
      if ((mob2 = get_mob_index(vnum)) == NULL)
      {
         send_to_char("Mobile not found.\n\r", ch);
         return;
      }
      if (!can_medit(ch, mob))
         return;
      if (mob2->rShop)
      {
         send_to_char("That mobile already has a repair shop.\n\r", ch);
         return;
      }
      mob->rShop = NULL;
      mob2->rShop = repair;
      repair->keeper = value;
      send_to_char("Done.\n\r", ch);
      return;
   }

   do_repairset(ch, "");
   return;
}


void do_repairstat(CHAR_DATA * ch, char *argument)
{
   REPAIR_DATA *repair;
   MOB_INDEX_DATA *mob;
   sh_int vnum;

   if (argument[0] == '\0')
   {
      send_to_char("Usage: repairstat <keeper vnum>\n\r", ch);
      return;
   }

   vnum = atoi(argument);

   if ((mob = get_mob_index(vnum)) == NULL)
   {
      send_to_char("Mobile not found.\n\r", ch);
      return;
   }

   if (!mob->rShop)
   {
      send_to_char("This mobile doesn't keep a repair shop.\n\r", ch);
      return;
   }
   repair = mob->rShop;

   ch_printf(ch, "Keeper: %d  %s\n\r", repair->keeper, mob->short_descr);
   ch_printf(ch, "fix0 [%s]  fix1 [%s]  fix2 [%s]\n\r", o_types[repair->fix_type[0]], o_types[repair->fix_type[1]], o_types[repair->fix_type[2]]);
   ch_printf(ch, "Profit: %3d%%  Type: %d\n\r", repair->profit_fix, repair->shop_type);
   ch_printf(ch, "Hours:   open %2d  close %2d\n\r", repair->open_hour, repair->close_hour);
   return;
}


void do_repairshops(CHAR_DATA * ch, char *argument)
{
   REPAIR_DATA *repair;

   if (!first_repair)
   {
      send_to_char("There are no repair shops.\n\r", ch);
      return;
   }

   set_char_color(AT_NOTE, ch);
   for (repair = first_repair; repair; repair = repair->next)
      ch_printf(ch, "Keeper: %5d Profit: %3d Type: %d Open: %2d Close: %2d Fix: %2d %2d %2d\n\r",
         repair->keeper, repair->profit_fix, repair->shop_type,
         repair->open_hour, repair->close_hour, repair->fix_type[0], repair->fix_type[1], repair->fix_type[2]);
   return;
}