/
roa/
roa/lib/boards/
roa/lib/config/
roa/lib/edits/
roa/lib/help/
roa/lib/misc/
roa/lib/plrobjs/
roa/lib/quests/
roa/lib/socials/
roa/lib/www/
roa/lib/www/LEDSign/
roa/lib/www/LEDSign/fonts/
roa/lib/www/LEDSign/scripts/
roa/src/s_inc/
roa/src/sclient/
roa/src/sclient/binary/
roa/src/sclient/text/
roa/src/util/
/************************************************************************
	Realms of Aurealis 		James Rhone aka Vall of RoA

shop.c					Shopkeeps and such.  Player comms
					to buy/sell/value/list and repair
					items interacting with specially
					flagged mobs.

		******** 100% Completely Original Code ********
		*** BE AWARE OF ALL RIGHTS AND RESERVATIONS ***
		******** Heavily modified and expanded ********
		        All rights reserved henceforth. 

    Please note that no guarantees are associated with any code from
Realms of Aurealis.  All code which has been released to the general
public has been done so with an 'as is' pretense.  RoA is based on both
Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well
as the RoA license.   *** Read, Learn, Understand, Improve ***
*************************************************************************/
#include "conf.h"
#include "sysdep.h"

#include "structures.h"
#include "comm.h"
#include "handler.h"
#include "db.h"
#include "interpreter.h"
#include "acmd.h"
#include "utils.h"
#include "lists.h"
#include "plshop.h"
#include "magic.h"
#include "global.h"

// external vars
extern struct str_app_type str_app[];

// external funcs
BOOL wandering_shopkeep(chdata *ch);

/*****************************************************/
/*  New shop code, OLCable, generic shopkeeper stuff */
/*  RoA, James Rhone				     */
/*****************************************************/

int same_type(chdata *keeper, obdata *ob);
int check_reaction(chdata *ch, char *str);
void perform_reaction(chdata *mob, char *str, chdata *ch, obdata *ob);
void do_mob_random(chdata *mob);

// minor bug: original sets not recursive thru containers.. will fix someday
// updated to not originalize plshopkeep items... those are 1 time only sells
// rather than repleneshing supplies of items 1/22/98  -jtrhone
void set_original_objects(int zone)
{
   chdata *mob;
   obdata *obj;

   if (zone == -1)   /* reset all of them */
   {
     for(mob = character_list; mob; mob = mob->next)
       if (SPC_FLAGGED(mob, SPC_SHOPKEEP) && !MOB_FLAGGED(mob, MOB_PLSHOPKEEP))
         for (obj = mob->carrying; obj; obj=obj->next_content)
	 {
           obj->original = TRUE;
           obj->touched = FALSE;
	 }
     return;
   }

   /* must be a reset_roazone or unlock -save methinks */
   if (REAL_ZONE(zone))
   {
     for(mob = character_list; mob; mob = mob->next)
      if (SPC_FLAGGED(mob, SPC_SHOPKEEP) && !MOB_FLAGGED(mob, MOB_PLSHOPKEEP))
       if (zone == GET_MOB_VNUM(mob) / 100)
         for (obj = mob->carrying; obj; obj=obj->next_content)
	 {
           obj->original = TRUE;
           obj->touched = FALSE;
	 }
   }
}

ACMD(do_list)
{
  obdata *obj;
  chdata *keeper;
  struct shop_data_type *shp;
  plshop *s = NULL;
  int cost;
  char *argu = argument;

  if (INVALID_ROOM(ch->in_room)) return;

  for(keeper = world[ch->in_room].people; keeper ; keeper = keeper->next_in_room)
    if (SPC_FLAGGED(keeper, SPC_SHOPKEEP))
      break;
    
  if (!keeper)   /* no shopkeeper present */
  {
    send_to_char("Nobody is selling anything here!\n\r",ch);
    return;
  }

  if (MOB_FLAGGED(keeper, MOB_PLSHOPKEEP))
    if (!(s = get_shopkeeps_plshop(GET_MOB_VNUM(keeper))))
    {
      sprintf(buf, "SYSERR: Unable to locate plshop for %s (#%d).", GET_NAME(keeper),
              GET_MOB_VNUM(keeper));
      mudlog(buf, BRF, LEV_IMM, TRUE);
      send_to_char("Unable to locate plshopkeep's shop, notify immortal.\n\r",ch);
      return;
    } 

  shp = keeper->npc_specials.shop_data;
  if (!shp)
  {
    sprintf(buf, "SYSERR: Undefined shopkeep: %s (#%d).", GET_NAME(keeper), GET_MOB_VNUM(keeper));
    mudlog(buf, BRF, LEV_IMM, TRUE);
    send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch);
    return;
  }
   
  if (!IS_IMMORTAL(ch) && s && !PLS_FLAGGED(s, PLS_OPEN))
  {
    send_to_char("This shop doesn't seem to be open...\n\r",ch);
    return;
  }

  /* only sell and what not if shopkeep is open and in shop */
  if (!wandering_shopkeep(keeper))
  if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop))
  {
    if (shp->vshop < 0)
      sprintf(buf, "I have retired!!  Seek an immortal :)");
    else
      sprintf(buf, "Find me at my shop around %d%s.", 
              (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open),
            shp->open > 12 ? "pm" : "am");
    do_say(keeper, buf, 0, SPEAKING(keeper));
    return;
  }

  if (!IS_IMMORTAL(ch) && s && !check_plshop_rcflags(ch, s))
  {
    act("$N refuses to do business with your type.", TRUE, ch, 0, keeper, TO_CHAR);
    return;
  }

  skip_spaces(&argu);

  if (keeper->carrying)
  {
     if (MSTR1(keeper))
       perform_reaction(keeper, MSTR1(keeper), ch, NULL);
     else
       send_to_char("These are the items for sale:\n\r",ch);

    for(*buf = '\0', obj = keeper->carrying; obj; obj = obj->next_content)
    {
      // in case they list a particular type... 2/5/98 -jtrhone
      if (*argu && !isname(argu, obj->name))
        continue;

      cost = obj->cost;

      if (s)
      {
        if (PLS_FLAGGED(s, PLS_ITEMTAX) && s->item_tax)
          cost += (int) (obj->cost * (s->item_tax / 100.0));
        if (s->keeper_cut)
          cost += (int) (obj->cost * (s->keeper_cut / 100.0));
        if (s->markup)
          cost += (int) (obj->cost * (s->markup / 100.0));
      }

      if (cost > 1)
        sprintf(buf2,"%s for %d %s.\n\r",obj->shdesc, cost, currency_name_plural);
      else
        sprintf(buf2,"%s for %d %s.\n\r",obj->shdesc, cost, currency_name);

      CCAP(buf2);
      if (strlen(buf) + strlen(buf2) < MAX_STRING_LENGTH - 10)
        strcat(buf, buf2);
    }
    if (*buf)
      page_string(ch->desc, buf, 1);
  }
  else
    act("$N has nothing to sell.",TRUE,ch,0,keeper,TO_CHAR);
}

ACMD(do_buy)
{
  obdata *obj = NULL, *tmp = NULL;
  chdata *keeper = NULL;
  struct shop_data_type *shp = NULL;
  int i, quantity = 0;
  char *argu = argument;
  plshop *s = NULL;
  int pc_cost = 0, cost = 0, itax = 0, scut = 0, markup = 0;

  if (IS_NPC(ch) || !ch->desc)
    return;

  if (INVALID_ROOM(ch->in_room)) return;

  for(keeper = world[ch->in_room].people; keeper ; keeper = keeper->next_in_room)
    if (SPC_FLAGGED(keeper, SPC_SHOPKEEP))
      break;
    
  if (!keeper || !keeper->carrying)   /* no shopkeeper present */
  {
    send_to_char("Nobody is selling anything here!\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("What do you want to buy?\n\r",ch);
    return;
  }

  if (MOB_FLAGGED(keeper, MOB_PLSHOPKEEP))
    if (!(s = get_shopkeeps_plshop(GET_MOB_VNUM(keeper))))
    {
      sprintf(buf, "SYSERR: Unable to locate plshop for %s (#%d).", GET_NAME(keeper),
              GET_MOB_VNUM(keeper));
      mudlog(buf, BRF, LEV_IMM, TRUE);
      send_to_char("Unable to locate plshopkeep's shop, notify immortal.\n\r",ch);
      return;
    } 

  shp = keeper->npc_specials.shop_data;
  if (!shp)
  {
    send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch);
    return;
  }
   
  if (!IS_IMMORTAL(ch) && s && !PLS_FLAGGED(s, PLS_OPEN))
  {
    send_to_char("This shop doesn't seem to be open...\n\r",ch);
    return;
  }

  /* only sell and what not if shopkeep is open and in shop */
  if (!wandering_shopkeep(keeper))
  if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop))
  {
    if (shp->vshop < 0)
      sprintf(buf, "I have retired!!  Seek an immortal :)");
    else
      sprintf(buf, "Find me at my shop around %d%s.", 
             (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open),
              shp->open > 12 ? "pm" : "am");
    do_say(keeper, buf, 0, SPEAKING(keeper));
    return;
  }

  if (!IS_IMMORTAL(ch) && s && !check_plshop_rcflags(ch, s))
  {
    act("$N refuses to do business with your type.", TRUE, ch, 0, keeper, TO_CHAR);
    return;
  }

  half_chop(argu, arg, buf2);

  // check for quantity arg first
  if (is_number(arg))
  {
    if (!(quantity = atoi(arg)) || !*buf2)
    {
      send_to_char("Buy what?\n\r",ch);
      return;
    }
    obj = get_obj_in_list_vis(keeper, buf2, keeper->carrying); 
  }
  else
  {
    obj = get_obj_in_list_vis(keeper, arg, keeper->carrying); 
    quantity = 1;
  }
  
  if (!obj)
  {  
    if (MSTR3(keeper))
      perform_reaction(keeper, MSTR3(keeper), ch, NULL);
    else
      act("$N does not have that item.",TRUE,ch,0,keeper,TO_CHAR);
    return;
  }

  // cant buy multiple unoriginal shopkeep items
  if (!obj->original)
    quantity = 1;
   
  // ok, now calculate cost....
  // updated for plshops 1/23/98 -jtrhone
  pc_cost = cost = obj->cost*quantity;

  if (s)
  {
    if (PLS_FLAGGED(s, PLS_ITEMTAX) && s->item_tax)
    {
      itax = (int) (cost * (s->item_tax / 100.0));
      pc_cost += itax;
    }

    if (s->keeper_cut)
    {
      scut = (int) (cost * (s->keeper_cut / 100.0));
      pc_cost += scut;
    }

    if (s->markup)
    {
      markup = (int) (cost * (s->markup / 100.0));
      pc_cost += markup;
    }
  }

  if (GET_GOLD(ch) < pc_cost)
  {
    sprintf(buf, "You don't have enough %s!\n\r", currency_name_plural);
    S2C();
    return;
  }

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

  if (obj->min_level > GET_LEVEL(ch))
  {
     if (MSTR5(keeper))
       perform_reaction(keeper, MSTR5(keeper), ch, obj);
     else
       act("$p is too powerful for you.",FALSE,ch,obj,keeper,TO_CHAR);
     return;
  }

  /* GENERIC 50% markoff on items that keepers will buy */
  if (quantity > 1)
  {
    if (pc_cost > 1)
       sprintf(buf,"$N sells you [%%B%%6%d%%0] $p for %d %s.", quantity, pc_cost, 
               currency_name_plural); 
    else
       sprintf(buf,"$N sells you [%%B%%6%d%%0] $p for %d %s.", quantity, pc_cost, currency_name); 
  }
  else
  {
    if (pc_cost > 1)
      sprintf(buf,"$N sells you $p for %d %s.", pc_cost, currency_name_plural); 
    else
      sprintf(buf,"$N sells you $p for %d %s.", pc_cost, currency_name); 
  }

  act(buf, FALSE, ch, obj, keeper, TO_CHAR);

  GET_GOLD(ch) -= pc_cost;
  
  // if there is a plshop, calc stats..
  if (s)
  {
    s->gross_income += pc_cost;
    s->net_income += cost + markup;   // if they marked down, (-markup) lose net gain
    s->gold_today += cost + markup;   // even though, really, they don't  3/15/98
    s->gold       += cost + markup; 
    s->item_tax_paid += itax;
    s->keeper_paid   += scut;
  }

  if (MSTR4(keeper))
    perform_reaction(keeper, MSTR4(keeper), ch, obj);

  if (!obj->original)  /* if not original item give that item to ch */
  {
     obj_from_char(obj);
     obj_to_char(obj,ch);
  }
  else  /* if original shopkeeper object simply clone and give it to ch*/
  if (obj->item_number > 0)
  {
    for (i = 0; i < quantity; i++)
    {
      tmp = read_object(obj->item_number, REAL);
      obj_to_char(tmp,ch);
    }
  }
  else
  {
    sprintf(buf, "SYSERR: Shopkeep #%d trying to sell non-virtual object.", GET_MOB_VNUM(keeper));
    mudlog(buf, BRF, LEV_IMM, FALSE);
  }
}

// TODO: shopkeep should offer based on item condition...
ACMD(do_sell)
{
  int room;
  char *argu = argument;
  obdata *obj;
  chdata *keeper;
  struct shop_data_type *shp;

  if (IS_NPC(ch) || !ch->desc)
    return;

  room = ch->in_room;

  if (!room) return;

  for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room)
    if (SPC_FLAGGED(keeper, SPC_SHOPKEEP))
      break;
    
  if (!keeper)   /* no shopkeeper present */
  {
    send_to_char("Nobody is selling anything here!\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("What do you want to sell?\n\r",ch);
    return;
  }

  shp = keeper->npc_specials.shop_data;
  if (!shp)
  {
    send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch);
    return;
  }
   
  /* only sell and what not if shopkeep is open and in shop */
  if (!wandering_shopkeep(keeper))
  if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop))
  {
    if (shp->vshop < 0)
      sprintf(buf, "I have retired!!  Seek an immortal :)");
    else
      sprintf(buf, "Find me at my shop around %d%s.", 
	     (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open),
             shp->open > 12 ? "pm" : "am");
    do_say(keeper, buf, 0, SPEAKING(keeper));
    return;
  }

  one_argument(argu, arg);

  if (!( obj =  get_obj_in_list_vis(ch, arg, ch->carrying))) 
  {
    sprintf(buf, "You don't have %s %s!\n\r", AN(arg), arg);
    S2C();
    return;
  }

  if (!obj->cost || !same_type(keeper, obj) || SPC_FLAGGED(keeper, SPC_NOBUY) || 
      !OBJ_HITS(obj) || MOB_FLAGGED(keeper, MOB_PLSHOPKEEP))
  {
    if (MSTR2(keeper))
      perform_reaction(keeper, MSTR2(keeper), ch, obj);
    else
      act("$N does not want $p.",TRUE,ch, obj, keeper, TO_CHAR);
    return;
  }

   /* GENERIC 50% markoff on items that keepers will buy */
  if (obj->cost/2 > 1)
    sprintf(buf,"$N pays you %d %s for $p.", obj->cost / 2, currency_name_plural);
  else
    sprintf(buf,"$N pays you %d %s for $p.", obj->cost / 2, currency_name);
  act(buf, FALSE, ch, obj, keeper, TO_CHAR);

  if (MSTR4(keeper))
    perform_reaction(keeper, MSTR4(keeper), ch, obj);

  GET_GOLD(ch) += (obj->cost / 2);
  obj_from_char(obj);
  obj->touched = FALSE;
  obj->original = FALSE;  /* this item wasn't on shopkeeper originally */
  obj_to_char(obj,keeper);
}

// TODO: shopkeep should offer based on item condition...
ACMD(do_value)
{
  int room;
  char *argu = argument;
  obdata *obj;
  chdata *keeper;

  if (IS_NPC(ch) || !ch->desc)
    return;

  room = ch->in_room;

  if (!room) return;

  for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room)
    if (SPC_FLAGGED(keeper, SPC_SHOPKEEP))
      break;
    
  if (!keeper)   /* no shopkeeper present */
  {
    send_to_char("There is no shopkeeper here!\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("What do you want to have appraised?\n\r",ch);
    return;
  }

  one_argument(argu, arg);

  if (!( obj =  get_obj_in_list_vis(ch, arg, ch->carrying))) 
  {
    send_to_char("You don't have that item!\n\r",ch);
    return;
  }

  if (!obj->cost || !same_type(keeper, obj) || SPC_FLAGGED(keeper, SPC_NOBUY) || 
      !OBJ_HITS(obj) || MOB_FLAGGED(keeper, MOB_PLSHOPKEEP))
  {
    if (MSTR2(keeper))
      perform_reaction(keeper,MSTR2(keeper), ch, obj);
    else
      act("$N doesn't want $p.", FALSE, ch, obj, keeper, TO_CHAR);
     return;
  }

  /* GENERIC 50% markoff on items that keepers will buy */
  if (obj->cost/2 > 1)
    sprintf(buf,"$N will pay you %d %s for $p.", obj->cost / 2, currency_name_plural); 
  else
    sprintf(buf,"$N will pay you %d %s for $p.", obj->cost / 2, currency_name); 
  act(buf, FALSE, ch, obj, keeper, TO_CHAR);
}

// let players identify an object before they buy for 10% of its value 
// 3/18/98 -jtrhone
ACMD(do_listid)
{
  obdata *obj = NULL;
  chdata *keeper = NULL;
  struct shop_data_type *shp = NULL;
  plshop *s = NULL;
  char *argu = argument;
  int pc_cost = 0, cost = 0, itax = 0, scut = 0, markup = 0;

  if (IS_NPC(ch) || !ch->desc)
    return;

  if (INVALID_ROOM(ch->in_room)) return;

  for(keeper = world[ch->in_room].people; keeper ; keeper = keeper->next_in_room)
    if (SPC_FLAGGED(keeper, SPC_SHOPKEEP))
      break;
    
  if (!keeper || !keeper->carrying)   /* no shopkeeper present */
  {
    send_to_char("Nobody is selling anything here!\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("What do you want to identify?\n\r",ch);
    return;
  }

  if (MOB_FLAGGED(keeper, MOB_PLSHOPKEEP))
    if (!(s = get_shopkeeps_plshop(GET_MOB_VNUM(keeper))))
    {
      sprintf(buf, "SYSERR: Unable to locate plshop for %s (#%d).", GET_NAME(keeper),
              GET_MOB_VNUM(keeper));
      mudlog(buf, BRF, LEV_IMM, TRUE);
      send_to_char("Unable to locate plshopkeep's shop, notify immortal.\n\r",ch);
      return;
    } 

  shp = keeper->npc_specials.shop_data;
  if (!shp)
  {
    send_to_char("This shopkeep is yet undefined, notify an immortal.\n\r",ch);
    return;
  }
   
  if (!IS_IMMORTAL(ch) && s && !PLS_FLAGGED(s, PLS_OPEN))
  {
    send_to_char("This shop doesn't seem to be open...\n\r",ch);
    return;
  }

  /* only sell and what not if shopkeep is open and in shop */
  if (!wandering_shopkeep(keeper))
  if (!SHOPKEEP_OPEN(keeper) || keeper->in_room != real_room(shp->vshop))
  {
    if (shp->vshop < 0)
      sprintf(buf, "I have retired!!  Seek an immortal :)");
    else
      sprintf(buf, "Find me at my shop around %d%s.", 
             (!shp->open) ? 12 : ((shp->open > 12) ? shp->open - 12 : shp->open),
              shp->open > 12 ? "pm" : "am");
    do_say(keeper, buf, 0, SPEAKING(keeper));
    return;
  }

  if (!IS_IMMORTAL(ch) && s && !check_plshop_rcflags(ch, s))
  {
    act("$N refuses to do business with your type.", TRUE, ch, 0, keeper, TO_CHAR);
    return;
  }

  obj = get_obj_in_list_vis(keeper, argu, keeper->carrying); 
  if (!obj)
  {  
    if (MSTR3(keeper))
      perform_reaction(keeper, MSTR3(keeper), ch, NULL);
    else
      act("$N does not have that item.",TRUE,ch,0,keeper,TO_CHAR);
    return;
  }

  // ok, now calculate cost....
  // updated for plshops 1/23/98 -jtrhone
  // only charge 10% to id the item
  // knock everything down by 10%, not just pc_cost, moneybugfix  8/3/98 -jtrhone
  pc_cost = cost = MAX(1, (obj->cost / 10));

  if (s)
  {
    if (PLS_FLAGGED(s, PLS_ITEMTAX) && s->item_tax)
    {
      itax = (int) (cost * (s->item_tax / 100.0));
      pc_cost += itax;
    }

    if (s->keeper_cut)
    {
      scut = (int) (cost * (s->keeper_cut / 100.0));
      pc_cost += scut;
    }

    if (s->markup)
    {
      markup = (int) (cost * (s->markup / 100.0));
      pc_cost += markup;
    }
  }

  if (GET_GOLD(ch) < pc_cost)
  {
    sprintf(buf, "You don't have enough %s!\n\r", currency_name_plural);
    S2C();
    return;
  }

  if (pc_cost > 1)
    sprintf(buf,"$N describes $p to you for %d %s.", pc_cost, currency_name_plural); 
  else
    sprintf(buf,"$N describes $p to you for %d %s.", pc_cost, currency_name); 

  act(buf, FALSE, ch, obj, keeper, TO_CHAR);

  GET_GOLD(ch) -= pc_cost;
  
  // if there is a plshop, calc stats..
  if (s)
  {
    s->gross_income += pc_cost;
    s->net_income += cost + markup;   // if they marked down, (-markup) lose net gain
    s->gold_today += cost + markup;   // even though, really, they don't  3/15/98
    s->gold       += cost + markup; 
    s->item_tax_paid += itax;
    s->keeper_paid   += scut;
  }

  if (MSTR4(keeper))
    perform_reaction(keeper, MSTR4(keeper), ch, obj);

  // have them cast the spell
  (*spell_info[SPELL_IDENTIFY].spell_pointer) (ch, "", NULL, obj);
}

int same_type(chdata *keeper, obdata *ob)
{
  int type = ob->type_flag;
  obdata *obj;

  for(obj = keeper->carrying; obj; obj = obj->next_content)
    if (obj->type_flag == type && !obj->touched)
      return TRUE;

  return FALSE;
}

/* thats it folks, eliminated a ton of code for slightly less features */
/* however, we can now edit shopkeepers online, something IMPOSSIBLE */
/* or at least a giant pain in the ass to do with the old code      */
/* James Rhone   aka Vall @ RoA */

/* shopkeep like code for mobs who can REPAIR objects */
// cost gets an estimate from the fixer on how much itll cost to fix...
ACMD(do_cost)
{
  int room, charge;
  char *argu = argument;
  obdata *obj;
  chdata *keeper;

  if (IS_NPC(ch) || !ch->desc)
    return;

  room = ch->in_room;

  if (!room) return;

  for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room)
    if (SPC_FLAGGED(keeper, SPC_FIXER))
      break;
    
  if (!keeper)   /* no shopkeeper present */
  {
    send_to_char("Nobody here can repair items.\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("What do you want to have appraised?\n\r",ch);
    return;
  }

  one_argument(argu, arg);
  if (!( obj =  get_obj_in_list_vis(ch, arg, ch->carrying))) 
  {
    do_say(keeper,"You don't have that item!\n\r",0, SPEAKING(keeper));
    return;
  }

  if (!same_type(keeper, obj))
  {
    do_say(keeper, "I don't know how to fix that!\n\r",0, SPEAKING(keeper));
    return;
  }

  if (MAX_OBJ_HITS(obj) == -1 || OBJ_HITS(obj) >= MAX_OBJ_HITS(obj))
  {
    do_say(keeper,"That doesn't need repair!",0, SPEAKING(keeper));
    return;
  }

  if (OBJ_HITS(obj) <= 0)
  {
    do_say(keeper,"That's beyond repair!",0, SPEAKING(keeper));
    return;
  }

  charge = (MAX_OBJ_HITS(obj) - OBJ_HITS(obj)) * 10; /* why not? :) */
  if (charge > 1)
    sprintf(buf,"That'll cost you %d %s to repair.", charge, currency_name_plural); 
  else
    sprintf(buf,"That'll cost you %d %s to repair.", charge, currency_name); 
  do_say(keeper,buf,0, SPEAKING(keeper));
}

// let the shopkeep fix the item...
ACMD(do_repair)
{
  int room, charge;
  char *argu = argument;
  obdata *obj;
  chdata *keeper;

  if (IS_NPC(ch) || !ch->desc)
    return;

  room = ch->in_room;

  if (!room) return;

  for(keeper = world[room].people; keeper ; keeper = keeper->next_in_room)
    if (SPC_FLAGGED(keeper, SPC_FIXER))
      break;
    
  if (!keeper)   /* no shopkeeper present */
  {
    send_to_char("Nobody here can repair items.\n\r",ch);
    return;
  }

  skip_spaces(&argu);
  if (!*argu)
  {
    send_to_char("What do you want to have repaired?\n\r",ch);
    return;
  }

  one_argument(argu, arg);
  if (!( obj =  get_obj_in_list_vis(ch, arg, ch->carrying))) 
  {
    do_say(keeper,"You don't have that item!\n\r",0, SPEAKING(keeper));
    return;
  }

  if (!same_type(keeper, obj))
  {
    do_say(keeper,"I don't know how to fix that!\n\r",0, SPEAKING(keeper));
    return;
  }

  if (MAX_OBJ_HITS(obj) == -1 || OBJ_HITS(obj) >= MAX_OBJ_HITS(obj))
  {
    do_say(keeper,"That doesn't need repair!",0, SPEAKING(keeper));
    return;
  }

  if (OBJ_HITS(obj) <= 0)
  {
    do_say(keeper,"That's beyond repair!",0, SPEAKING(keeper));
    return;
  }

  charge = (MAX_OBJ_HITS(obj) - OBJ_HITS(obj)) * 10;
  if (charge > 1)
    sprintf(buf, "That'll cost you %d %s to repair.",charge, currency_name_plural);
  else
    sprintf(buf, "That'll cost you %d %s to repair.",charge, currency_name);

  do_say(keeper, buf, 0, SPEAKING(keeper));

  if (GET_GOLD(ch) < charge)
  {
    sprintf(buf, "You don't have enough %s!\n\r", currency_name_plural);
    S2C();
    return;
  }

  GET_GOLD(ch) -= charge;
  act("$N quickly repairs $p for $n.", FALSE, ch, obj, keeper, TO_ROOM);
  do_say(keeper, "There you go!", 0, SPEAKING(keeper));
  OBJ_HITS(obj) = MAX_OBJ_HITS(obj);
}