/*
 * static char *rcsid_input_c =
 *   "$Id: input.c,v 1.61 1997/04/24 09:38:48 master Exp $";
 */
/*
    CrossFire, A Multiplayer game for X-windows

    Copryight (C) 1994 Mark Wedel
    Copyright (C) 1992 Frank Tore Johansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    The author can be reached via e-mail to master@rahul.net
*/

#include <global.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <version.h>
#include <main.h>
#include <spells.h>
#include <skills.h>
#include <newclient.h>

#ifdef SET_TITLE
extern void redraw_title(object *pl);
#endif /* SET_TITLE */

int onoff_value(char *line)
{
  int i;

  if (sscanf(line, "%d", &i))
    return (i != 0);
  switch (line[0]) {
  case 'o':
    switch (line[1]) {
    case 'n': return 1;		/* on */
    default:  return 0;		/* o[ff] */
    }
  case 'y':			/* y[es] */
  case 'k':			/* k[ylla] */
  case 's':
  case 'd':
    return 1;
  case 'n':			/* n[o] */
  case 'e':			/* e[i] */
  case 'u':
  default:
    return 0;
  }
}

/*
 * flags:
 *  0 - inv & below
 *  1 - below & inv
 *  2 - inv
 *  3 - below
 */
object **find_objects(object *op, char *line, int flags)
{

  return NULL;
}

/*
 * Check if an item op can be put into a sack. If pl exists then tell
 * a player the reason of failure.
 * returns 1 if it will fit, 0 if it will not.  nrof is the number of
 * objects (op) we want to put in.  We specify it separately instead of
 * using op->nrof because often times, a player may have specified a
 * certain number of objects to drop, so we can pass that number, and
 * not need to use split_ob and stuff.
 */
int sack_can_hold (object *pl, object *sack, object *op, int nrof) {
    char buf[MAX_BUF];
    buf[0] = 0;

    if (! QUERY_FLAG (sack, FLAG_APPLIED))
	sprintf (buf, "%s is not active.", query_name(sack));
    if (sack == op)
	sprintf (buf, "You can't put %s into itself.", query_name(sack));
    if (sack->race && (sack->race != op->race || op->type == CONTAINER
		       || (sack->stats.food && sack->stats.food != op->type)))
	sprintf (buf, "You can put only %s into %s.", sack->race,
		 query_name(sack));
    if (op->type == SPECIAL_KEY && sack->slaying && op->slaying)
	sprintf (buf, "You don't want put the key into %s.", query_name(sack));
    if (sack->weight_limit && sack->carrying + (nrof ? nrof : 1) * 
	op->weight * (100 - sack->stats.Str) / 100  > sack->weight_limit)
	sprintf (buf, "That won't fit in %s!", query_name(sack));
    if (buf[0]) {
	if (pl)
	    new_draw_info(NDI_UNIQUE, 0,pl, buf);
	return 0;
    }
    return 1;
}


void lock_inv(object *op, object *item) {
  SET_FLAG(item,FLAG_INV_LOCKED);
  new_draw_info(NDI_UNIQUE, 0,op,"Item has been locked");
  draw_inventory(op);
  return;
}

void unlock_inv(object *op, object *item) {
  CLEAR_FLAG(item,FLAG_INV_LOCKED);
  new_draw_info(NDI_UNIQUE, 0,op,"Item has been unlocked");
  draw_inventory(op);
  return;
}


/* Eneq(@csd.uu.se): Had to remove rotate_right and replace it with a version
   which doesn't use insert_ob_in_ob since I redid that to add things last in
   the inventory, or next to objects of it's own type. This one now works just
   like rotate_left but reversed :-) 
   [ And now handles invisible objects. Tero.Haatanen@lut.fi ] */

void rotate_right(object *op) {
  object *tmp;
  char buf[MAX_BUF];
  int items=0;

  for (tmp=op->inv;tmp;tmp=tmp->below)
    if (!tmp->invisible)
      items++;

  if(items==0) {
    new_draw_info(NDI_UNIQUE, 0,op,"You don't carry anything.");
    return;
  }
  if(items==1) {
    new_draw_info(NDI_UNIQUE, 0,op,"You only carry one thing.");
    return;
  }
  op->contr->last_used=NULL;
  op->contr->last_used_id=0;
  for (tmp=op->inv; tmp && tmp->below; tmp=tmp->below);
  while(tmp!=NULL&&tmp->invisible)
    tmp=tmp->above;
  /* tmp points now the last visible item in the inventory */
  tmp->above->below=tmp->below;
  if (tmp->below)
    tmp->below->above=tmp->above;
  tmp->above=NULL;
  tmp->below=op->inv;
  op->inv->above=tmp;
  op->inv=tmp;

  if(QUERY_FLAG(op, FLAG_WIZ))
    sprintf(buf,"The %s (%d) is now on top.",
            query_name(op->inv),op->inv->count);
  else
    sprintf(buf,"The %s is now on top.",query_name(op->inv));
  new_draw_info(NDI_UNIQUE, 0,op,buf);
  draw_inventory(op);
}

/*
 * [ Now rotate_left handles invisible objects too. Tero.Haatanen@lut.fi ]
 */

void rotate_left(object *op) {
  object *tmp,*tmp2;
  char buf[MAX_BUF];
  int items=0;

  for (tmp=op->inv;tmp;tmp=tmp->below)
    if (!tmp->invisible)
      items++;

  if(items==0) {
    new_draw_info(NDI_UNIQUE, 0,op,"You don't carry anything.");
    return;
  }
  if(items==1) {
    new_draw_info(NDI_UNIQUE, 0,op,"You only carry one thing.");
    return;
  }
  op->contr->last_used=NULL;
  op->contr->last_used_id=0;
  for (tmp=op->inv; tmp && tmp->below; tmp=tmp->below);
  for (tmp2=op->inv->below; tmp2 && tmp2->invisible; tmp2=tmp2->below);

  tmp2->above->below=NULL;
  tmp2->above=NULL;
  tmp->below=op->inv;
  op->inv->above=tmp;
  op->inv=tmp2;

  if(QUERY_FLAG(op, FLAG_WIZ))
    sprintf(buf,"The %s (%d) is now on top.",
            query_name(op->inv),op->inv->count);
  else
    sprintf(buf,"The %s is now on top.",query_name(op->inv));
  new_draw_info(NDI_UNIQUE, 0,op,buf);
  draw_inventory(op);
}

void drop_inventory(object *op) {
  object *inv,*n=NULL;
  if(op->contr->last_used!=NULL) {
    inv=op->contr->last_used;
    if(QUERY_FLAG(inv, FLAG_FREED)||inv->count!=op->contr->last_used_id||
       inv->env==NULL||(inv->env!=op&&inv->env->env!=op)) {
      op->contr->last_used=NULL;
      op->contr->last_used_id=0;
      drop_inventory(op);
      return;
    }
    inv=op->contr->last_used;
    if(inv->below!=NULL)
      n=inv->below;
    else if(inv->above!=NULL)
      n=inv->above;
    if(n!=NULL) {
      op->contr->last_used=n;
      op->contr->last_used_id=n->count;
    }
  } else {
    inv=op->inv;
    while(inv&&inv->invisible)
      inv=inv->below;
  }
  if(inv)
    drop(op,inv);
  else
    new_draw_info(NDI_UNIQUE, 0,op,"You have nothing to drop!");
}


void examine_monster(object *op,object *tmp) {
  object *mon=tmp->head?tmp->head:tmp;
  archetype *at=tmp->arch;
  if(QUERY_FLAG(mon,FLAG_UNDEAD))
    new_draw_info(NDI_UNIQUE, 0,op,"It is an undead force.");
  if(mon->level>op->level)
    new_draw_info(NDI_UNIQUE, 0,op,"It is likely more powerful than you.");
  else if(mon->level<op->level)
    new_draw_info(NDI_UNIQUE, 0,op,"It is likely less powerful than you.");
  else
    new_draw_info(NDI_UNIQUE, 0,op,"It is probably as powerful as you.");
  if(mon->attacktype&AT_ACID)
    new_draw_info(NDI_UNIQUE, 0,op,"You seem to smell an acrid odor.");
  if(tmp->type!=PLAYER)
    return;
  switch((mon->stats.hp+1)*4/(at->clone.stats.hp+1)) { /* From 1-4 */
  case 1:
    new_draw_info(NDI_UNIQUE, 0,op,"It is in a bad shape.");
    break;
  case 2:
    new_draw_info(NDI_UNIQUE, 0,op,"It is hurt.");
    break;
  case 3:
    new_draw_info(NDI_UNIQUE, 0,op,"It is somewhat hurt.");
    break;
  case 4:
    new_draw_info(NDI_UNIQUE, 0,op,"It is in excellent shape.");
    break;
  }
  if(present_in_ob(POISONING,mon)!=NULL)
    new_draw_info(NDI_UNIQUE, 0,op,"It looks very ill.");
}

char *long_desc(object *tmp) {
  static char buf[MAX_BUF];
  char *cp;
  if(tmp==NULL)
    return "";
  buf[0]='\0';
  switch(tmp->type) {
  case RING:
  case SKILL:
  case WEAPON:
  case ARMOUR:
  case BRACERS:
  case HELMET:
  case SHIELD:
  case BOOTS:
  case GLOVES:
  case AMULET:
  case GIRDLE:
  case BOW:
  case ARROW:
  case CLOAK:
  case FOOD:
  case DRINK:
  case FLESH:
    if(*(cp=describe_item(tmp))!='\0')
      sprintf(buf,"%s %s",query_name(tmp),cp);
    break;
  }
  if(buf[0]=='\0')
    sprintf(buf,"%s",query_name(tmp));

  return buf;
}

void examine(object *op, object *tmp) {
  char buf[VERY_BIG_BUF];

  if (tmp == NULL || tmp->type == CLOSE_CON)
    return;
/* Eneq(csd.uu.se): If NO_PRETEXT is defined we should only print the name. */
  if (QUERY_FLAG(tmp, FLAG_NO_PRETEXT))
    sprintf(buf, "%s.", long_desc(tmp));
  else
    sprintf(buf, "That is %s.", long_desc(tmp));
  new_draw_info(NDI_UNIQUE, 0,op,buf);
  buf[0]='\0';
  switch(tmp->type) {
  case SPELLBOOK:
    if(QUERY_FLAG(tmp, FLAG_IDENTIFIED) && tmp->stats.sp > 0 && tmp->stats.sp <= NROFREALSPELLS )
      if(!strcmp(tmp->arch->name,"cleric_book"))
        sprintf(buf,"%s is a %d level prayer.",
              spells[tmp->stats.sp].name,spells[tmp->stats.sp].level);
      else
        sprintf(buf,"%s is a %d level spell.",
              spells[tmp->stats.sp].name,spells[tmp->stats.sp].level);
    break;
  case BOOK:
    if(tmp->msg!=NULL)
      strcpy(buf,"Something is written in it.");
    break;
  case CONTAINER:
    if(tmp->race!=NULL)
      if(tmp->weight_limit && tmp->stats.Str<100)
        sprintf (buf,"It can hold only %s and its weight limit is %.1f kg.", 
		 tmp->race, tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
      else
        sprintf (buf,"It can hold only %s.", tmp->race);
    else
      if(tmp->weight_limit && tmp->stats.Str<100)
        sprintf (buf,"Its weight limit is %.1f kg.", 
		tmp->weight_limit/(10.0 * (100 - tmp->stats.Str)));
    break;
  case WAND:
    if(QUERY_FLAG(tmp, FLAG_IDENTIFIED))
      sprintf(buf,"It has %d charges left.",tmp->stats.food);
    break;
  }
  if(buf[0]!='\0')
    new_draw_info(NDI_UNIQUE, 0,op,buf);
  if(tmp->material) {
    strcpy(buf,"It is made of: ");
    if(tmp->material&M_PAPER)
      strcat(buf,"paper ");
    if(tmp->material&M_IRON)
      strcat(buf,"iron ");
    if(tmp->material&M_GLASS)
      strcat(buf,"glass ");
    if(tmp->material&M_LEATHER)
      strcat(buf,"leather ");
    if(tmp->material&M_WOOD)
      strcat(buf,"wood ");
    if(tmp->material&M_ORGANIC)
      strcat(buf,"organics ");
    if(tmp->material&M_STONE)
      strcat(buf,"stone ");
    if(tmp->material&M_CLOTH)
      strcat(buf,"cloth ");
    if(tmp->material&M_ADAMANT)
      strcat(buf,"adamantite ");
    new_draw_info(NDI_UNIQUE, 0,op,buf);
  }
  if(tmp->weight) {
    sprintf(buf,"%s weighs %3.3f kg.", tmp->nrof>1?"They":"It",
            (tmp->nrof?tmp->weight*tmp->nrof:tmp->weight)/1000.0);
    new_draw_info(NDI_UNIQUE, 0,op,buf);
  }
  if(tmp->value&&!QUERY_FLAG(tmp, FLAG_STARTEQUIP)) {
    if(QUERY_FLAG(tmp, FLAG_UNPAID))
      sprintf(buf,"%s would cost you %s.",
              tmp->nrof>1?"They":"It",query_cost_string(tmp,op,F_BUY));
    else
      sprintf(buf,"You would get %s for %s.",
              query_cost_string(tmp,op,F_SELL), tmp->nrof>1?"them":"it");
    new_draw_info(NDI_UNIQUE, 0,op,buf);
  }
  if(QUERY_FLAG(tmp, FLAG_MONSTER))
    examine_monster(op,tmp);
  if(tmp->msg && tmp->type != EXIT && tmp->type != BOOK && tmp->type != CORPSE
     && !QUERY_FLAG(tmp, FLAG_WALK_ON)) {
    if (need_identify(tmp) && QUERY_FLAG(tmp, FLAG_IDENTIFIED))
      new_draw_info(NDI_UNIQUE, 0,op, "The object has a story:");
#if 0 /* it might not be written on it */
    else
      new_draw_info(NDI_UNIQUE, 0,op,"Something is written on it:");
#endif
    new_draw_info(NDI_UNIQUE, 0,op,tmp->msg);
  }
  new_draw_info(NDI_UNIQUE, 0,op," "); /* Blank line */
}

/*
 * inventory prints object's inventory. If inv==NULL then print player's
 * inventory. 
 * [ Only items which are applied are showed. Tero.Haatanen@lut.fi ]
 */
void inventory(object *op,object *inv) {
  object *tmp;
  char buf[MAX_BUF], *in;
  int items = 0, length;

  if (inv==NULL && op==NULL) {
    new_draw_info(NDI_UNIQUE, 0,op,"Inventory of what object?");
    return;
  }
  tmp = inv ? inv->inv : op->inv;

  while (tmp) {
    if ((!tmp->invisible && 
        (inv==NULL || inv->type == CONTAINER || QUERY_FLAG(tmp, FLAG_APPLIED)))
         || (!op || QUERY_FLAG(op, FLAG_WIZ)))
      items++;
    tmp=tmp->below;
  }
  if (inv==NULL) { /* player's inventory */
    if (items==0) {
      new_draw_info(NDI_UNIQUE, 0,op,"You carry nothing.");
      return;
    } else {
      length = INFOCHARS-19;
      in = "";
      if (op)
        clear_win_info(op);
      new_draw_info(NDI_UNIQUE, 0,op,"Inventory:");
    }
  } else {
    if (items==0) 
      return;
    else { 
      length = INFOCHARS-21;
      in = "  ";
    }
  }
  for (tmp=inv?inv->inv:op->inv; tmp; tmp=tmp->below) {
    if((!op||!QUERY_FLAG(op, FLAG_WIZ)) && (tmp->invisible || 
       (inv && inv->type != CONTAINER && !QUERY_FLAG(tmp, FLAG_APPLIED))))
      continue;
    if((!op || QUERY_FLAG(op, FLAG_WIZ)))
      (void) sprintf(buf,"%s- %-*s (%5d) %-8s", in, length, query_name(tmp),
                     tmp->count,query_weight(tmp));
    else
      (void) sprintf(buf,"%s- %-*s %-8s", in, length+8, query_name(tmp),
                     query_weight(tmp));
    new_draw_info(NDI_UNIQUE, 0,op,buf);
  }
  if(!inv && op) {
    sprintf(buf,"%-*s %-8s",
            INFOCHARS-9,"Total weight :",query_weight(op));
    new_draw_info(NDI_UNIQUE, 0,op,buf);
  }
}

/*
 * look_at prints items on the specifc square.
 * [ removed EARTHWALL check and added check for containers inventory.
 *   Tero.Haatanen@lut.fi ]
 */
void look_at(object *op,int dx,int dy) {
  object *tmp;
  char buf[MAX_BUF];
  int flag=0;

  if (out_of_map(op->map, op->x+dx, op->y+dy)) return;
  if(op->above!=NULL) {
    remove_ob(op);
    insert_ob_in_map(op,op->map);
  }
  if(dx||dy) 
    for(tmp=get_map_ob(op->map,op->x+dx,op->y+dy);tmp!=NULL&&tmp->above!=NULL;
      tmp=tmp->above);
  else
    tmp=op->below;
  while(tmp && (QUERY_FLAG(op, FLAG_WIZ) ||(!tmp->invisible && tmp!=op))) {
    if(!flag) {
      if(dx||dy)
        new_draw_info(NDI_UNIQUE, 0,op,"There you see:");
      else {
        clear_win_info(op);
        new_draw_info(NDI_UNIQUE, 0,op,"You see:");
      }
      flag=1;
    }
    if(QUERY_FLAG(op, FLAG_WIZ))
      (void) sprintf(buf,"- %s (%d).",query_name(tmp),tmp->count);
    else
      (void) sprintf(buf,"- %s.",query_name(tmp));
    new_draw_info(NDI_UNIQUE, 0,op,buf);
    if((tmp->inv!=NULL || (tmp->head && tmp->head->inv)) && 
       ( (!dx&&!dy) || tmp->type != CONTAINER || QUERY_FLAG(op, FLAG_WIZ)
/* added some cases here -b.t. */
	|| !(tmp->type) || tmp->type!=FLESH ))
      inventory(op,tmp->head==NULL?tmp:tmp->head);
    if(QUERY_FLAG(tmp, FLAG_IS_FLOOR)&&!QUERY_FLAG(op, FLAG_WIZ))	/* don't continue under the floor */
      break;
    tmp=tmp->below;
  }
  if(!flag)
    if(dx||dy)
      new_draw_info(NDI_UNIQUE, 0,op,"You see nothing there.");
    else
      new_draw_info(NDI_UNIQUE, 0,op,"You see nothing.");
}



void receive_player_name(object *op,char k) {
  if(k!=13) {
    if((k==8||k==127)&&(op->contr->writing==1))
      return;
    if(k!=8&&k!=127&&!((k>='a'&&k<='z')||(k>='A'&&k<='Z'))&&k!='-'&&k!='_')
      return;
    write_ch(op,k);
    return;
  }
  if(op->contr->writing<=1) {
    if (op->contr->eric_server>0) get_name(op);
    return;
  }
  if(!check_name(op->contr,op->contr->write_buf+1)) {
      get_name(op);
      return;
  }
  if(op->name!=NULL)
    free_string(op->name);
  op->name=add_string(op->contr->write_buf+1);
  new_draw_info(NDI_UNIQUE, 0,op,op->contr->write_buf);
  op->contr->last_value= -1; /* Flag: redraw all stats */
  draw_stats(op);
  op->contr->name_changed=1;
  get_password(op);
}

void receive_player_password(object *op,char k) {
  if(k!=13) {
    if(k!=8&&k!=127&&(!((k>='a'&&k<='z')||(k>='A'&&k<='Z'))||
       op->contr->writing>8)) /* Max 8 characters in password */
      return;
    write_ch(op,k);
    return;
  }
  op->contr->no_echo=0;
  if(op->contr->writing<=1) {
    remove_lock(op->contr);
    get_name(op);
    return;
  }
  op->contr->writing=0;
  new_draw_info(NDI_UNIQUE, 0,op,"          "); /* To hide the password better */
  if(op->contr->state==ST_CONFIRM_PASSWORD) {
    if(!check_password(op->contr->write_buf+1,op->contr->password)) {
      new_draw_info(NDI_UNIQUE, 0,op,"The passwords did not match.");
      remove_lock(op->contr);
      get_name(op);
      return;
    }
    clear_win_info(op);
    display_motd(op);
    new_draw_info(NDI_UNIQUE, 0,op," ");
    new_draw_info(NDI_UNIQUE, 0,op,"Welcome, Brave New Warrior!");
    new_draw_info(NDI_UNIQUE, 0,op," ");
    Roll_Again(op);
    op->contr->state=ST_ROLL_STAT;
    return;
  }
  strcpy(op->contr->password,crypt_string(op->contr->write_buf+1,NULL));
  op->contr->state=ST_ROLL_STAT;
  check_login(op);
  return;
}


void set_pickup_mode(object *op,int i) {
  switch(op->contr->mode=i) {
    case 0:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Don't pick up.");
      break;
    case 1:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item.");
      break;
    case 2:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up one item and stop.");
      break;
    case 3:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Stop before picking up.");
      break;
    case 4:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items.");
      break;
    case 5:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all items and stop.");
      break;
    case 6:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all magic items.");
      break;
    case 7:
      new_draw_info(NDI_UNIQUE, 0,op,"Mode: Pick up all coins and gems");
      break;
    }
}

int explore_mode() {
#ifdef EXPLORE_MODE
  player *pl;
  for (pl = first_player; pl != (player *) NULL; pl = pl->next)
    if (pl->explore)
      return 1;
#endif
  return 0;
}

/*
 * Actual commands.
 * Those should be in small separate files (c_object.c, c_wiz.c, cmove.c,...)
 */


static void help_topics(object *op, int what)
{
  XDIR *dirp;
  struct xdirect *de;
  char filename[MAX_BUF], line[80];
  int namelen, linelen=0;
  
  switch (what) {
  case 1:
    sprintf(filename, "%s/wizhelp", settings.libdir);
    new_draw_info(NDI_UNIQUE, 0,op, "      Wiz commands:");
    break;
  case 2:
    sprintf(filename, "%s/sockethelp", settings.libdir);
    new_draw_info(NDI_UNIQUE, 0,op, "      Socket commands:");
    break;
  case 3:
    sprintf(filename, "%s/mischelp", settings.libdir);
    new_draw_info(NDI_UNIQUE, 0,op, "      Misc help:");
    break;
  default:
    sprintf(filename, "%s/help", settings.libdir);
    new_draw_info(NDI_UNIQUE, 0,op, "      Commands:");
    break;
}
  if (!(dirp=xopendir(filename)))
    return;
  line[0] ='\0';
  for (de = xreaddir(dirp, 1); de; de = xreaddir(dirp, 1)) {
    namelen = de->d_namlen;
    if (namelen <= 2 && *de->d_name == '.' &&
	(namelen == 1 || de->d_name[1] == '.' ) )
      continue;
    linelen +=namelen+1;
    if (linelen > 42) {
      new_draw_info(NDI_UNIQUE, 0,op, line);
      sprintf(line, " %s", de->d_name);
      linelen =namelen+1;
      continue;
    }
    strcat(line, " ");
    strcat(line, de->d_name);
  }	       
  new_draw_info(NDI_UNIQUE, 0,op, line);
  xclosedir(dirp);
}

static void show_commands(object *op, int what)
{
  char line[80];
  int i, size, namelen, linelen=0;
  CommArray_s *ap;
  extern CommArray_s Commands[], WizCommands[], SocketCommands[];
  extern const int CommandsSize, WizCommandsSize, SocketCommandsSize;
  
  switch (what) {
  case 1:
    ap =WizCommands;
    size =WizCommandsSize;
    new_draw_info(NDI_UNIQUE, 0,op, "      Wiz commands:");
    break;
  case 2:
    ap =SocketCommands;
    size = SocketCommandsSize;
    new_draw_info(NDI_UNIQUE, 0,op, "      Socket commands:");
    break;
  default:
    ap =Commands;
    size =CommandsSize;
    new_draw_info(NDI_UNIQUE, 0,op, "      Commands:");
    break;
  }

  line[0] ='\0';
  for (i=0; i<size; i++) {
    namelen = strlen(ap[i].name);
    linelen +=namelen+1;
    if (linelen > 42) {
      new_draw_info(NDI_UNIQUE, 0,op, line);
      sprintf(line, " %s", ap[i].name);
      linelen =namelen+1;
      continue;
    }
    strcat(line, " ");
    strcat(line, ap[i].name);
  }	       
  new_draw_info(NDI_UNIQUE, 0,op, line);
}

int command_help (object *op, char *params)
{
  struct stat st;
  FILE *fp;
  char filename[MAX_BUF], line[MAX_BUF];
  int len;

  if(op != NULL)
    clear_win_info(op);

/*
   * Main help page?
 */
  if (!params) {
    sprintf(filename, "%s/def_help", settings.libdir);
    if ((fp=fopen(filename, "r")) == NULL) {
      LOG(llevError, "Can't open %s\n", filename);
      perror("Can't read default help");
      return 0;
    }
    while (fgets(line, MAX_BUF, fp)) {
      line[MAX_BUF-1] ='\0';
      len =strlen(line)-1;
      if (line[len] == '\n')
	line[len] ='\0';
      new_draw_info(NDI_UNIQUE, 0,op, line);
    }
    fclose(fp);
    return 0;
  }

  /*
   * Topics list
   */
  if (!strcmp(params, "topics")) {
    help_topics(op, 3);
    if (!op) {
      help_topics(op, 2);
      if (active_socket->wiz)
	help_topics(op, 1);
      return 0;
    }
    help_topics(op, 0);
    if (QUERY_FLAG(op, FLAG_WIZ))
      help_topics(op, 1);
    return 0;
    }
  
  /*
   * Commands list
   */
  if (!strcmp(params, "commands")) {
    if (!op) {
      show_commands(op, 2);
      if (active_socket->wiz)
	show_commands(op, 1);
      return 0;
  }
    show_commands(op, 0);
    if (QUERY_FLAG(op, FLAG_WIZ))
      show_commands(op, 1);
    return 0;
  }

  /*
   * User wants info about command
   */
  if (strchr(params, '.') || strchr(params, ' ') || strchr(params, '/')) {
    sprintf(line, "Illegal characters in '%s'", params);
    new_draw_info(NDI_UNIQUE, 0,op, line);
    return 0;
  }

  sprintf(filename, "%s/mischelp/%s", settings.libdir, params);
  if (stat(filename, &st) || !S_ISREG(st.st_mode)) {
    if (op) {
      sprintf(filename, "%s/help/%s", settings.libdir, params);
      if (stat(filename, &st) || !S_ISREG(st.st_mode)) {
	if (QUERY_FLAG(op, FLAG_WIZ)) {
	  sprintf(filename, "%s/wizhelp/%s", settings.libdir, params);
	  if (stat(filename, &st) || !S_ISREG(st.st_mode))
	    goto nohelp;
	} else
	  goto nohelp;
      }
          } else {
      sprintf(filename, "%s/sockethelp/%s", settings.libdir, params);
      if (stat(filename, &st) || !S_ISREG(st.st_mode)) {
	if (active_socket->wiz) {
	  sprintf(filename, "%s/wizhelp/%s", settings.libdir, params);
	  if (stat(filename, &st) || !S_ISREG(st.st_mode))
	    goto nohelp;
	} else
	  goto nohelp;
          }
  }
  }

  /*
   * Found that. Just cat it to screen.
   */
  if ((fp=fopen(filename, "r")) == NULL) {
    LOG(llevError, "Can't open %s\n", filename);
    perror("Can't read helpfile");
    return 0;
      }
  sprintf(line, "Help about '%s'", params);
  new_draw_info(NDI_UNIQUE, 0,op, line);
  while (fgets(line, MAX_BUF, fp)) {
    line[MAX_BUF-1] ='\0';
    len =strlen(line)-1;
    if (line[len] == '\n')
      line[len] ='\0';
    new_draw_info(NDI_UNIQUE, 0,op, line);
    }
  fclose(fp);
  return 0;

  /*
   * No_help -escape
   */
 nohelp:
  sprintf(line, "No help availble on '%s'", params);
  new_draw_info(NDI_UNIQUE, 0,op, line);
  return 0;
}


int command_chrfont (object *op, char *params)
{
  XFontStruct *tmp_font;
  
  if (params != NULL) {
    if ((tmp_font=XLoadQueryFont(op->contr->gdisp, params)) != NULL) {
      strcpy (op->contr->font_str, params);
      XFreeFont(op->contr->gdisp, tmp_font);
    } else {
      LOG(llevError, "Crossfire: Couldn't load font %s.\n", params);
      new_draw_info(NDI_UNIQUE, 0,op, "Couldn't load font.");
    }
  } else new_draw_info(NDI_UNIQUE, 0,op, "What font?");
      return 1;
}


int command_invoke(object *op, char *params)
{
	return command_cast_spell(op, params, 'i');
}

int command_cast(object *op, char *params)
{
	return command_cast_spell(op, params, 'c');
}

int command_prepare(object *op, char *params)
{
	return command_cast_spell(op, params, 'p');
}

/* object *op is the caster, params is the spell name.  We return the index
 * value of the spell in the spells array for a match, -1 if there is no
 * match, -2 if there are multiple matches.  Note that 0 is a valid entry, so
 * we can't use that as failure.
 */
static int find_spell_byname(object *op, char *params)
{
    int numknown; /* number of spells known by op */
    int spnum;  /* number of spell that is being cast */
    int match=-1,i;

    if(QUERY_FLAG(op, FLAG_WIZ))
	numknown = NROFREALSPELLS;
    else 
	numknown = op->contr->nrofknownspells;

    for(i=0;i<numknown;i++){
	if (QUERY_FLAG(op, FLAG_WIZ)) spnum = i;
	else spnum = op->contr->known_spells[i];

	if (!strncmp(params, spells[spnum].name, strlen(params))) {
	    /* We already found a match previously - thus params is not
	     * not unique, so return -2 stating this.
	     */
	    if (match>=0) return -2;
	    else match=spnum;
	}
    }
    return match;
}

/* Shows all spells that op knows.  If params is supplied, the must match
 * that.  If cleric is 1, show cleric spells, if not set, show mage
 * spells.
 */
static void show_matching_spells(object *op, char *params, int cleric)
{
    int i,spnum,first_match=0;
    char lev[80];

    for (i=0; i<(QUERY_FLAG(op, FLAG_WIZ)?NROFREALSPELLS:op->contr->nrofknownspells); i++) {	
	if (QUERY_FLAG(op,FLAG_WIZ)) spnum=i;
	else spnum = op->contr->known_spells[i];

	if (spells[spnum].cleric != cleric) continue;
	if (params && strncmp(spells[spnum].name,params, strlen(params)))
		continue;
	if (!first_match) {
	    first_match=1;
	    if (!cleric)
		new_draw_info(NDI_UNIQUE, 0, op, "Mage spells");
	    else
		new_draw_info(NDI_UNIQUE, 0, op, "Priest spells");
	    new_draw_info(NDI_UNIQUE, 0,op,"[sp] [lev] spell name");
	}
	if (spells[spnum].path & op->path_denied)
	    strcpy(lev,"den");
	else
	    sprintf(lev," %02d",spells[spnum].level);

	new_draw_info_format(NDI_UNIQUE,0,op,"[%02d] [%s] %s",
		SP_level_spellpoint_cost(op,op,spnum),
		lev,
		spells[spnum].name);
    }
}



/* sets up to cast a spell.  op is the caster, params is the spell name,
 * and command is the first letter of the spell type (c=cast, i=invoke, 
 * p=prepare).  Invoke casts a spell immediately, where as cast (and I believe
 * prepare) just set up the range type.
 */

int command_cast_spell (object *op, char *params, char command)
{
    int castnow=0;
    char *cp=NULL;
    int spnum=-1;  /* number of spell that is being cast */

    if(!op->contr->nrofknownspells&&!QUERY_FLAG(op, FLAG_WIZ)) {
	new_draw_info(NDI_UNIQUE, 0,op,"You don't know any spells.");
        return 1;
    }
    /* Remove control of the golem */
    if(op->contr->golem!=NULL) {
        remove_friendly_object(op->contr->golem);
        remove_ob(op->contr->golem);
        free_object(op->contr->golem);
        op->contr->golem=NULL;
    }

    if (command=='i') castnow = 1;
    if(params!=NULL) {

	/* rune of fire, rune of ... are special cases, break it into 'rune' and
	 * then put 'fire, marking, whatever' in cp. */
	if (strncmp(params,"rune",4)) {
	    cp =strstr(params, " of ");
	    if (cp) {
		*cp='\0';
		cp +=4;
	    }
	}
	spnum = find_spell_byname(op, params);
	if (spnum>=0) {
	    rangetype orig_rangetype=op->contr->shoottype;
	    op->contr->shoottype=range_magic;
#ifdef ALLOW_SKILLS 
	    if(op->type==PLAYER&&!QUERY_FLAG(op,FLAG_WIZ)) { 
		 /* if we don't change to the correct spell numb,
		  * check_skill_to_fire will be confused as to which
		  * spell casting skill to ready for the player!
		  * I set the code to change back to the old spellnum
		  * after we check, but is this really needed?? -b.t. */

		int orig_spn = op->contr->chosen_spell;
		op->contr->chosen_spell=spnum;
		if(!check_skill_to_fire(op)) {  
		    op->contr->shoottype=orig_rangetype;
		    return 0; 
		    }
		op->contr->chosen_spell=orig_spn;
	    }
#endif
	    if (castnow) { 
		int value;

		    /* Need to switch shoottype to range_magic - otherwise 
                     * cast_spell doesn't check to see if the character 
                     * has enough spellpoints. 
		     * Note: now done above this -b.t. */
		    /* op->contr->shoottype=range_magic; */ 

		value = cast_spell(op,op,op->facing,spnum,0,spellNormal,cp);
		op->contr->shoottype=orig_rangetype;

		if(spells[spnum].cleric) 
			op->stats.grace -= value;
		else 
			op->stats.sp -= value;
	    } 
	    /* We are not casting now */
	    else op->contr->chosen_spell=spnum; 
	    
	    draw_stats(op);
	    return 1;
	} /* found a matching spell */
    } /* params supplied */

    /* We get here if cast was given without options or we could not find
     * the requested spell.  List all the spells the player knows (if
     * spnum = -1) or spells matching params if spnum=-2
     */

    new_draw_info(NDI_UNIQUE, 0,op,"Cast what spell?  Choose one of:");
    show_matching_spells(op, (spnum==-2)?params:NULL, 0);
    new_draw_info(NDI_UNIQUE,0,op,"");
    show_matching_spells(op, (spnum==-2)?params:NULL, 1);

    return 1;
}

#ifdef SET_TITLE
int command_title (object *op, char *params)
{
      char buf[MAX_BUF];
  if(params == NULL) {
	if(op->contr->own_title[0]=='\0')
	  sprintf(buf,"Your title is '%s'.",
		  op->contr->title);
	else
	  sprintf(buf,"Your title is '%s'.",
		  op->contr->own_title);
	new_draw_info(NDI_UNIQUE, 0,op,buf);
	return 1;
      }
  if(strcmp(params, "clear")==0 || strcmp(params, "default")==0) {
	if(op->contr->own_title[0]=='\0')
	  new_draw_info(NDI_UNIQUE, 0,op,"Your title is the default title.");
	else
	  new_draw_info(NDI_UNIQUE, 0,op,"Title set to default.");
	op->contr->own_title[0]='\0';
	redraw_title(op);
	return 1;
      }
  if((int)strlen(params) >= MAX_NAME) {
	new_draw_info(NDI_UNIQUE, 0,op,"Title too long.");
	return 1;
      }
  strcpy(op->contr->own_title, params);
      redraw_title(op);
      return 1;
    }
#endif /* SET_TITLE */

int command_save (object *op, char *params)
{
      if(save_player(op,1))
	new_draw_info(NDI_UNIQUE, 0,op,"You have been saved.");
      else
	new_draw_info(NDI_UNIQUE, 0,op,"SAVE FAILED!");
      return 1;
}

#ifdef SEARCH_ITEMS
int command_search_items (object *op, char *params)
{
      char buf[MAX_BUF];
  if(params == NULL) {
	if(op->contr->search_str[0]=='\0') {
	  new_draw_info(NDI_UNIQUE, 0,op,"Example: search magic+1");
	  new_draw_info(NDI_UNIQUE, 0,op,"Would automatically pick up all");
	  new_draw_info(NDI_UNIQUE, 0,op,"items containing the word 'magic+1'.");
	  return 1;
	}
	op->contr->search_str[0]='\0';
	new_draw_info(NDI_UNIQUE, 0,op,"Search mode turned off.");
	fix_player(op);
	draw_stats(op);
	return 1;
  }
  if((int)strlen(params) >= MAX_BUF) {
	new_draw_info(NDI_UNIQUE, 0,op,"Search string too long.");
	return 1;
      }
  strcpy(op->contr->search_str, params);
      sprintf(buf,"Searching for '%s'.",op->contr->search_str);
      new_draw_info(NDI_UNIQUE, 0,op,buf);
  fix_player(op);
  draw_stats(op);
      return 1;
    }
#endif /* SEARCH_ITEMS */

int command_peaceful (object *op, char *params)
{
      if((op->contr->peaceful=!op->contr->peaceful))
        new_draw_info(NDI_UNIQUE, 0,op,"You will not attack other players.");
      else
        new_draw_info(NDI_UNIQUE, 0,op,"You will attack other players.");
      return 1;
    }

int command_scroll (object *op, char *params)
{
      if((op->contr->scroll=!op->contr->scroll)) {
	new_draw_info(NDI_UNIQUE, 0,op,"Scroll is enabled.");
	if (op->contr->infofull) {
		op->contr->infoline = op->contr->infolines - 1;
		draw_all_info(op);
	}
      }
      else {
	/* simply put, in scroll mode, infopos does not have to be equal
	 * to infoline.  However, infopos must equal infoline in wrap
	 * mode.  A simple infopos=infoline solves this, but if a redraw
	 * was needed, the redraw wouldn't look right, so we copy the
	 * info array to make things right.
	 * Mark Wedel (master@cats.ucsc.edu)
	 */
	int i;
	char	**info;
	if ((op->contr->infofull) &&
	   (op->contr->infoline != op->contr->infopos)){
		info = (char **) malloc(sizeof(char *) * op->contr->infolines);
      for (i=0; i<(int)op->contr->infolines; i++) {
			info[i] = malloc(sizeof(char) * (op->contr->infochars+1));
	strncpy(info[i], op->contr->info[(i+op->contr->infopos) % (int)op->contr->infolines],op->contr->infochars);
			info[i][op->contr->infochars] = '\0';
		}
      for (i=0; i<(int)op->contr->infolines; i++)
			free(op->contr->info[i]);
		free(op->contr->info);
		op->contr->info = info;
		op->contr->infopos = op->contr->infoline;
		draw_all_info(op);
	}
        new_draw_info(NDI_UNIQUE, 0,op,"Scroll is disabled.");
      }
      return 1;
    }

int command_berzerk (object *op, char *params)
{
      if((op->contr->berzerk=!op->contr->berzerk))
        new_draw_info(NDI_UNIQUE, 0,op,"Berzerk is enabled.");
      else
        new_draw_info(NDI_UNIQUE, 0,op,"Berzerk is disabled.");
      return 1;
    }

int command_strength (object *op, char *params)
{
      int i;
  if(params==NULL || !sscanf(params, "%d", &i) ||
         (!QUERY_FLAG(op, FLAG_WIZ)&&(i<5||i>op->stats.maxsp))) {
        new_draw_info(NDI_UNIQUE, 0,op,"Set which strength?");
        return 1;
      }
      op->contr->shootstrength=i;
      new_draw_info(NDI_UNIQUE, 0,op,"OK.");
      return 1;
    }

int command_pickup (object *op, char *params)
{
      int i;

  if(!params) {
    op->contr->count_left=0;
    set_pickup_mode(op, (op->contr->mode > 6)? 0: op->contr->mode+1);
    return 0;
  }
  if(params==NULL || !sscanf(params, "%d", &i) || i<0 ) {
        new_draw_info(NDI_UNIQUE, 0,op,"Usage: pickup <0-7> or <value_density> .");
        return 1;
      }
      set_pickup_mode(op,i);
      return 1;
}

int command_protocol (object *op, char *params)
{
  int i;
  if (params == NULL || !sscanf(params, "%d", &i)) {
      new_draw_info(NDI_UNIQUE, 0,op,"Set what protocol?");
      return 1;
  }
  set_protocol(active_socket, i);
  return 1;
}

int command_set (object *op, char *params)
{
  if (params == NULL) {
	/* Lets tell what options are available */
	new_draw_info(NDI_UNIQUE, 0,op, "Options you can set are:");
	new_draw_info(NDI_UNIQUE, 0,op, "font:    Use normal font mode");
	new_draw_info(NDI_UNIQUE, 0,op, "pixmaps: Use pixmaps instead of font");
	new_draw_info(NDI_UNIQUE, 0,op, "split:   Start in split window mode");
#ifdef Xpm_Pix
	new_draw_info(NDI_UNIQUE, 0,op, "xpm: Use X Pixmaps for display");
#endif
        return 1;
      }

      /* Lets not be picky about case */

  if(!strcasecmp(params, "font")) {
        active_socket->use_pix = 0;
	active_socket->color_pix = 0;
        new_draw_info(NDI_UNIQUE, 0,op, "OK.");
        return 1;
      }
  if(!strcasecmp(params, "pixmaps")) {
        active_socket->use_pix = 1;
	active_socket->color_pix = 0;
        new_draw_info(NDI_UNIQUE, 0,op, "OK.");
        return 1;
      }
  if(!strcasecmp(params, "split")) {
        active_socket->split = 1;
        new_draw_info(NDI_UNIQUE, 0,op, "OK.");
        return 1;
      }
#ifdef Xpm_Pix
  if (!strcasecmp(params, "xpm")) {
	active_socket->color_pix = 1;
        active_socket->use_pix = 0;
	new_draw_info(NDI_UNIQUE, 0,op, "OK.");
	return 1;
      }
#endif
      new_draw_info(NDI_UNIQUE, 0,op,"Unknown option.\n");
      return 1;
    }

int command_unset (object *op, char *params)
{
  if (params == NULL) {
        new_draw_info(NDI_UNIQUE, 0,op, "Unset what option?");
        return 1;
      }
  if(!strcmp(params,"pixmaps")) {
        active_socket->use_pix = 0;
        new_draw_info(NDI_UNIQUE, 0,op, "OK.");
        return 1;
      }
  if(!strcmp(params,"split")) {
        active_socket->split = 0;
        new_draw_info(NDI_UNIQUE, 0,op, "OK.");
        return 1;
      }
#ifdef Xpm_Pix
  if (!strcasecmp(params,"xpm")) {
	active_socket->color_pix = 0;
	new_draw_info(NDI_UNIQUE, 0,op, "OK.");
	return 1;
      }
#endif
      new_draw_info(NDI_UNIQUE, 0,op,"Unknown option.\n");
      return 1;
    }

/*
 * Now follows non-dm-commands which are also acceptable from sockets
 */

int command_sync (object *op, char *params)
{
    int i;
    char buf[MAX_BUF];
  if (params==NULL || !sscanf(params, "%d", &i)) {
      sprintf(buf, "Your current sync is %d.",
#ifdef SERVER
	    op ? op->contr->sync : active_socket->sync
#else
	    op->contr->sync
#endif
	    );
      new_draw_info(NDI_UNIQUE, 0,op, buf);
    }
#ifdef SERVER
    if (!op)
      active_socket->sync = i;
    else
#endif
      op->contr->sync = i;
    new_draw_info(NDI_UNIQUE, 0,op,"OK.");
    return 1;
  }

int command_name (object *op, char *params)
{
  if(params == NULL) {
      new_draw_info(NDI_UNIQUE, 0,op,"Change name to what?");
      return 1;
    }
    if(op == NULL) {
    strncpy(active_socket->name, params, 8);
      active_socket->name[8]='\0';
      new_draw_info(NDI_UNIQUE, 0,NULL,"OK.");
      return 1;
    }
    new_draw_info(NDI_UNIQUE, 0,op,"You can't change your name.");
    return 1;
  }


int command_wimpy (object *op, char *params)
{
    int i;
  char buf[MAX_BUF];

  if (params==NULL || !sscanf(params, "%d", &i)) {
      sprintf(buf, "Your current wimpy level is %d.", op->run_away);
      new_draw_info(NDI_UNIQUE, 0,op, buf);
      return 1;
    }
    sprintf(buf, "Your new wimpy level is %d.", i);
    new_draw_info(NDI_UNIQUE, 0,op, buf);
    op->run_away = i;
    return 1;
  }

int command_quit (object *op, char *params)
{
    if(op == NULL) {
      new_draw_info(NDI_UNIQUE, 0,NULL,"Goodbye.");
      active_socket->quit = 1;
      return 1;
    }
    if(op->stats.exp&&!QUERY_FLAG(op, FLAG_WAS_WIZ)) {
      new_draw_info(NDI_UNIQUE, 0,op,"This will delete your character!");
      new_draw_info(NDI_UNIQUE, 0,op,"Are you still sure you want to quit(y/n)?");
    } else
      new_draw_info(NDI_UNIQUE, 0,op,"Are you sure you want to quit(y/n)?");

    if (op->contr->eric_server>0)
	send_query(op->contr->eric_server,CS_QUERY_SINGLECHAR,"");

    op->contr->state = ST_CONFIRM_QUIT;
    return 1;
  }

#ifdef EXPLORE_MODE
/*
 * don't allow people to exit explore mode.  It otherwise becomes
 * really easy to abuse this.
 */
int command_explore (object *op, char *params)
{
  /*
   * I guess this is the best way to see if we are solo or not.  Actually,
   * are there any cases when first_player->next==NULL and we are not solo?
   */
      if ((first_player!=op->contr) || (first_player->next!=NULL)) {
	  new_draw_info(NDI_UNIQUE, 0,op,"You can not enter explore mode if you are in a party");
      }
      else if (op->contr->explore)
              new_draw_info(NDI_UNIQUE, 0,op, "There is no return from explore mode");
      else {
		op->contr->explore=1;
		new_draw_info(NDI_UNIQUE, 0,op, "You are now in explore mode");
      }
      return 1;
    }
#endif

#ifdef SOUND_EFFECTS
int command_sound (object *op, char *params)
{
    if ((op->contr->rplay_fd = - op->contr->rplay_fd) > 0)
      new_draw_info(NDI_UNIQUE, 0,op, "The sounds are enabled.");
    else
      new_draw_info(NDI_UNIQUE, 0,op, "Silence is golden...");
    return 1;
  }
#endif

int command_add (object *op, char *params)
{
    char username[9];
    Settings oldsettings=settings;
    int i;

#ifdef EXPLORE_MODE
    if (explore_mode()) {
	new_draw_info(NDI_UNIQUE, 0,op,"You are not able to add new players in explore mode.");
	return 1;
    }
#endif
#ifdef NO_ADD
    if(op != NULL) {
      new_draw_info(NDI_UNIQUE, 0,op, "Add is disabled.  Use the client.");
      return 1;
    }
#endif

  if(params == NULL) {
      if(op != NULL)
        op->contr->writing=0;
      new_draw_info(NDI_UNIQUE, 0,op,"Add which display?");
      return 1;
    }
    if (active_socket != (sockets *) NULL)
    {
	if (active_socket->color_pix) settings.displaymode=Dm_Pixmap;
	else if (active_socket->use_pix) settings.displaymode=Dm_Bitmap;
	settings.splitwindows=active_socket->split;
	settings.synchronize=active_socket->sync;
	strcpy(username, active_socket->name);
    }
    else
	*username = '\0';
    i=add_player(params, *username ? username : NULL, NULL,0);
    settings = oldsettings;

    /* This player can be freed during a add_player() */
    if(op == NULL || (op->type == PLAYER && !QUERY_FLAG(op,FLAG_FREED)))
      new_draw_info(NDI_UNIQUE, 0,op, (i ? errmsg : "OK."));
    return 1;
  }

int command_shout (object *op, char *params)
{
    char buf[MAX_BUF];
  if (params == NULL) {
      new_draw_info(NDI_UNIQUE, 0,op,"Shout what?");
      return 1;
    }
    if (op == NULL) {
      strcpy(buf,active_socket->name);
      strcat(buf," shouts: ");
    } else {
      strcpy(buf,op->name);
      strcat(buf," shouts: ");
    }
  strncat(buf, params, MAX_BUF-30);
    buf[MAX_BUF - 1] = '\0';
    new_draw_info(NDI_UNIQUE | NDI_ALL | NDI_RED, 1, NULL, buf);
    return 1;
  }

int command_tell (object *op, char *params)
{
    char buf[MAX_BUF],*name = NULL ,*msg = NULL;
    player *pl;
#ifdef SERVER
    sockets *s;
#endif /* SERVER */
    if ( params != NULL){
        name = params;
        msg = strchr(name, ' ');
        if(msg){
	     *(msg++)=0;
	     if(*msg == 0)
		msg = NULL;
        }
    }

    if( name == NULL ){
	new_draw_info(NDI_UNIQUE, 0,op,"Tell whom what?");
	return 1;
    } else if ( msg == NULL){
	sprintf(buf, "Tell %s what?", name);
	new_draw_info(NDI_UNIQUE, 0,op,buf);
	return 1;
    }


    if (op == NULL)
      sprintf(buf,"%s tells you: %s",active_socket->name,msg);
    else
      sprintf(buf,"%s tells you: %s",op->name,msg);
    for(pl=first_player;pl!=NULL;pl=pl->next)
      if(strncasecmp(pl->ob->name,name,MAX_NAME)==0)
      {
	new_draw_info(NDI_UNIQUE | NDI_WHITE, 0, pl->ob, buf);
        return 1;
      }
#ifdef SERVER
    for(s = first_socket; s != (sockets *) NULL; s = s->next)
      if(strncasecmp(s->name,name,MAX_NAME)==0)
      {
        draw_socket(s->fd,buf);
        return 1;
      }
    new_draw_info(NDI_UNIQUE, 0,op,"No such player or socket.");
#else
    new_draw_info(NDI_UNIQUE, 0,op,"No suck player.");
#endif /* SERVER */
    return 1;
  }

int command_bell (object *op, char *params)
{
    char buf[MAX_BUF];
    player *pl;
#ifdef SERVER
    sockets *s;
#endif /* SERVER */
  if (params == NULL) {
      new_draw_info(NDI_UNIQUE, 0,op,"Bell whom?");
      return 1;
    }
    for(pl=first_player;pl!=NULL;pl=pl->next)
    if(strncasecmp(pl->ob->name, params, MAX_NAME)==0)
      {
        if (op == NULL)
          sprintf(buf,"%s bells you.",active_socket->name);
        else
          sprintf(buf,"%s bells you.",op->name);
        new_draw_info(NDI_UNIQUE, 0,pl->ob,buf);
        XBell(pl->ob->contr->gdisp,100);
        return 1;
      }
#ifdef SERVER
    for(s = first_socket; s != (sockets *) NULL; s = s->next)
    if(strncasecmp(s->name, params, MAX_NAME)==0)
      {
        if (op == NULL)
          sprintf(buf,"%s bells you.%c",active_socket->name,7);
        else
          sprintf(buf,"%s bells you.%c",op->name,7);
        draw_socket(s->fd,buf);
        return 1;
      }
#endif /* SERVER */
  return 0;
  }

/**************************************************************************/

/* Returns TRUE if the range specified (int r) is legal - that is,
 * the character has an item that is equipped for that range type.
 * return 0 if there is no item of that range type that is usable.
 */

int legal_range(object *op,int r) {
  int i;
  object *tmp;

  switch(r) {
  case range_none: /* "Nothing" is always legal */
    return 1;
  case range_bow: /* bows */
    for (tmp=op->inv; tmp!=NULL; tmp=tmp->below)
      if (tmp->type == BOW && QUERY_FLAG(tmp, FLAG_APPLIED))
	return 1;
    return 0;
  case range_magic: /* cast spells */
    if (op->contr->nrofknownspells == 0)
      return 0;
    for (i = 0; i < op->contr->nrofknownspells; i++)
      if (op->contr->known_spells[i] == op->contr->chosen_spell)
        return 1;
    op->contr->chosen_spell = op->contr->known_spells[0];
    return 1;
  case range_wand: /* use wands */
    for (tmp=op->inv; tmp!=NULL; tmp=tmp->below)
      if (tmp->type == WAND && QUERY_FLAG(tmp, FLAG_APPLIED)) {
        if (QUERY_FLAG(tmp, FLAG_BEEN_APPLIED) || QUERY_FLAG(tmp, FLAG_IDENTIFIED))
          op->contr->known_spell = 1;
        else
          op->contr->known_spell = 0;
        op->contr->chosen_item_spell=tmp->stats.sp;
        return 1;
      }
    return 0;
  case range_rod:
    for (tmp=op->inv; tmp!=NULL; tmp=tmp->below)
      if (tmp->type == ROD && QUERY_FLAG(tmp, FLAG_APPLIED)) {
        if (QUERY_FLAG(tmp,FLAG_BEEN_APPLIED) || QUERY_FLAG(tmp, FLAG_IDENTIFIED))
          op->contr->known_spell = 1;
        else
          op->contr->known_spell = 0;
        op->contr->chosen_item_spell=tmp->stats.sp;
        return 1;
      }
    return 0;
  case range_horn:
    for (tmp=op->inv; tmp!=NULL; tmp=tmp->below)
      if (tmp->type == HORN && QUERY_FLAG(tmp, FLAG_APPLIED)) {
        if (QUERY_FLAG(tmp,FLAG_BEEN_APPLIED) || QUERY_FLAG(tmp, FLAG_IDENTIFIED))
          op->contr->known_spell = 1;
        else
          op->contr->known_spell = 0;
        op->contr->chosen_item_spell=tmp->stats.sp;
        return 1;
      }
    return 0;
  case range_scroll: /* Use scrolls */
    return 0;
  case range_skill:
      for (tmp = op->inv; tmp!=NULL; tmp=tmp->below) {
	  if (tmp->type == SKILL) { 
	      return 1;
	  }
      }
      op->chosen_skill=NULL;	/* they have lost all skills :) */ 
      return 0;
  }
  return 0;
}

void change_spell(object *op,char k) {
  char buf[MAX_BUF];
  if(op->contr->golem!=NULL) {
    remove_friendly_object(op->contr->golem);
    remove_ob(op->contr->golem);
    free_object(op->contr->golem);
    op->contr->golem=NULL;
  }
  do {
    op->contr->shoottype += ((k == '+') ? 1 : -1);
    if(op->contr->shoottype >= range_size)
      op->contr->shoottype = range_none;
    else if (op->contr->shoottype <= range_bottom)
      op->contr->shoottype = (rangetype) range_size-1;
  } while (!legal_range(op,op->contr->shoottype));
  switch(op->contr->shoottype) {
  case range_none:
    strcpy(buf,"No ranged attack chosen.");
    break;
  case range_bow: {
	object *tmp;
	for (tmp = op->inv; tmp; tmp = tmp->below)
	  if (tmp->type == BOW && QUERY_FLAG (tmp, FLAG_APPLIED))
	    break;
	sprintf (buf, "Switched to %s and %s.", query_name(tmp),
		 tmp && tmp->race ? tmp->race : "nothing");
    }
    break;
  case range_magic:
    sprintf(buf,"Switched to spells (%s).",
            spells[op->contr->chosen_spell].name);
    new_draw_info(NDI_UNIQUE, 0,op,buf);
    break;
  case range_wand:
    sprintf(buf,"Switched to wand (%s).",
            op->contr->known_spell ?
              spells[op->contr->chosen_item_spell].name : "unknown");
    break;
  case range_rod:
    sprintf(buf, "Switched to rod (%s).",
            op->contr->known_spell ?
            spells[op->contr->chosen_item_spell].name : "unknown");
    break;
  case range_horn:
    sprintf(buf, "Switched to horn (%s).",
            op->contr->known_spell ?
            spells[op->contr->chosen_item_spell].name : "unknown");
    break;
  case range_skill: 
    sprintf (buf, "Switched to skill: %s", op->chosen_skill ?  
		 op->chosen_skill->name : "none");
    break;
  default:
    break;
  }
  new_draw_info(NDI_UNIQUE, 0,op,buf);
  draw_stats(op);
}

#ifdef SAVE_WINDOW_POSITIONS
void get_window_coord(object *op, Window win,
		 int *x,int *y,
		 int *wx,int *wy,
		 unsigned int *w,unsigned int *h)
{
  Window root,child;
  unsigned int tmp;
  XGetGeometry(op->contr->gdisp,win,
	       &root,
	       x,y,w,h,
	       &tmp,&tmp);
  XTranslateCoordinates(op->contr->gdisp,win,root,
			0,0,wx,wy,
			&child);
}
#endif /* SAVE_WINDOW_POSITIONS */

int command_rotateinventory (object *op, char *params)
{
  int size, i, j=1;
  object *head, *tail, *tmp;
  char buf[MAX_BUF];

  if (params && params[0])
    sscanf(params, "%d", &j);

  if (!j)
    return 0;
  if(!op->inv) {
    new_draw_info(NDI_UNIQUE, 0,op,"You don't carry anything.");
    return 0;
  }

  for (size=0,tmp=op->inv; tmp; tmp=tmp->below)
    if (!tmp->invisible)
      size++;
  if (size < 2) {
    new_draw_info(NDI_UNIQUE, 0,op,"You only carry one thing.");
    return 0;
  }
  if (FABS(j) >= size) {
    new_draw_info(NDI_UNIQUE, 0,op, "Value too big");
    return 0;
  }

  op->contr->last_used =NULL;
  op->contr->last_used_id =0;

  if (j<0) {
    for (i=0,head=NULL,tmp=op->inv; tmp->below; tmp=tmp->below)
      if (!tmp->invisible)
	if (i-- == j)
	  head =tmp;
    tail =tmp;
  } else {
    for (tmp=op->inv; tmp->below; tmp=tmp->below)
      ;
    tail =tmp;
    for (i=0; tmp; tmp=tmp->above)
      if (!tmp->invisible)
	if (++i == j)
      break;
    head =tmp;
  }
  if (!head)
    return 0;

  tmp =op->inv;
  op->inv =head;
  head->above->below =NULL;
  head->above =NULL;
  tail->below =tmp;
  tmp->above =tail;
  
  if(QUERY_FLAG(op, FLAG_WIZ))
    sprintf(buf,"Rotate %d. The %s (%d) is now on top.",
            j, query_name(op->inv),op->inv->count);
  else
    sprintf(buf,"Rotate %d. The %s is now on top.", j, query_name(op->inv));
  new_draw_info(NDI_UNIQUE, 0,op,buf);
  draw_inventory(op);
      op->contr->count_left=0;
  return 0;
}

int command_invisible (object *op, char *params)
{
  if (!op)
    return 0;
      op->invisible+=100;
      update_object(op);
      new_draw_info(NDI_UNIQUE, 0,op,"You turn invisible.");
  return 0;
}

int command_rotateshoottype (object *op, char *params)
{
  if (!params)
      change_spell(op,'+');
  else
    change_spell(op, params[0]);
  return 0;
}

int command_show (object *op, char *params)
{

  if(!params) {
      if (++op->contr->show_what > show_nonmagical)
        op->contr->show_what = show_all;
      draw_all_inventory(op);
    return 1;
  }

  if (!strncmp(params, "all", strlen(params)))
	op->contr->show_what = show_all;
  else if (!strncmp(params, "applied", strlen(params)))
	op->contr->show_what = show_applied;
  else if (!strncmp(params, "unapplied", strlen(params)))
	op->contr->show_what = show_unapplied;
  else if (!strncmp(params, "unpaid", strlen(params)))
	op->contr->show_what = show_unpaid;
  else if (!strncmp(params, "cursed", strlen(params)))
	op->contr->show_what = show_cursed;
  else if (!strncmp(params, "magical", strlen(params)))
	op->contr->show_what = show_magical;
  else if (!strncmp(params, "nonmagical", strlen(params)))
	op->contr->show_what = show_nonmagical;

  draw_all_inventory(op);
  return 1;
}

int command_throw (object *op, char *params)
{

#ifdef ALLOW_SKILLS
   if(!change_skill(op,SK_THROWING))
        return 0;
   else {
        int success = do_skill(op,op->facing,NULL);
        return success;
   }
#else
  return 0;
#endif
}

int command_brace (object *op, char *params)
{
  if (!params)
    op->contr->braced =!op->contr->braced;
  else
    op->contr->braced =onoff_value(params);

  if(op->contr->braced)
    new_draw_info(NDI_UNIQUE, 0,op, "You are braced.");
  else
    new_draw_info(NDI_UNIQUE, 0,op, "Not braced.");

      fix_player(op);
  return 0;
}

int command_rotatespells (object *op, char *params)
{
  player *pl=op->contr;
  int i, j;

  if(pl->shoottype != range_magic) {
    if(pl->nrofknownspells > 0) {
      pl->shoottype = range_magic;
      pl->chosen_spell = pl->known_spells[0];
    } else
          new_draw_info(NDI_UNIQUE, 0,op,"You know no spells.");
    return 0;
  }

  for(i=0;i<pl->nrofknownspells;i++)
    if(pl->known_spells[i]==pl->chosen_spell)
	    {
	j =1;
	if(params)
	  sscanf(params, "%d", &j);
	i +=j + (int)pl->nrofknownspells;
	i = i % (int)pl->nrofknownspells;
	pl->chosen_spell=pl->known_spells[i];
	draw_stats(op);
	return 1;
	    }
  pl->chosen_spell=pl->known_spells[0];
  draw_stats(op);
  return 1;
}

#ifdef SAVE_WINDOW_POSITIONS
int command_savewinpos (object *op, char *params)
{
  player *pl=op->contr;
	int i,eq=1;

  if(!(pl->split_window)) {
	  new_draw_info(NDI_UNIQUE, 0,op,"You can't save window positions in this mode.");
    return 0;
	}
  if(pl->valid_save_positions) {
    pl->valid_save_positions=0;
	  new_draw_info(NDI_UNIQUE, 0,op,"Window positions will no longer be saved.");
    return 0;
	}

  get_window_coord(op,pl->win_game,
		   &pl->win_pos[0].x,&pl->win_pos[0].y,
		   &pl->win_pos[0].wx,&pl->win_pos[0].wy,
		   &pl->win_pos[0].w,&pl->win_pos[0].h);
  get_window_coord(op,pl->win_stats,
		   &pl->win_pos[1].x,&pl->win_pos[1].y,
		   &pl->win_pos[1].wx,&pl->win_pos[1].wy,
		   &pl->win_pos[1].w,&pl->win_pos[1].h);
  get_window_coord(op,pl->win_info,
		   &pl->win_pos[2].x,&pl->win_pos[2].y,
		   &pl->win_pos[2].wx,&pl->win_pos[2].wy,
		   &pl->win_pos[2].w,&pl->win_pos[2].h);
  get_window_coord(op,pl->win_inv,
		   &pl->win_pos[3].x,&pl->win_pos[3].y,
		   &pl->win_pos[3].wx,&pl->win_pos[3].wy,
		   &pl->win_pos[3].w,&pl->win_pos[3].h);
  get_window_coord(op,pl->win_look,
		   &pl->win_pos[4].x,&pl->win_pos[4].y,
		   &pl->win_pos[4].wx,&pl->win_pos[4].wy,
		   &pl->win_pos[4].w,&pl->win_pos[4].h);
  get_window_coord(op,pl->win_message,
		   &pl->win_pos[5].x,&pl->win_pos[5].y,
		   &pl->win_pos[5].wx,&pl->win_pos[5].wy,
		   &pl->win_pos[5].w,&pl->win_pos[5].h);
    for(i=0;i<5;i++)	/* try to figure out the window coords */
    if(pl->win_pos[i].x!=pl->win_pos[i+1].x ||
       pl->win_pos[i].y!=pl->win_pos[i+1].y)
	    {
	      eq=0;			/* window positions were different */
	return 0;
	    }
	if(eq)			/* need to shift window positions (wm frame) */
	  for(i=0;i<6;i++)
	    {
#ifdef FUNNY_WINDOW_MANAGER /* try defining this if the other doesn't work */
	pl->win_pos[i].x=pl->win_pos[i].wx-
	  pl->win_pos[i].x;
	pl->win_pos[i].y=pl->win_pos[i].wy-
	  pl->win_pos[i].y;
#else /* FUNNY_WINDOW_MANAGER */
	pl->win_pos[i].x=pl->win_pos[i].wx;
	pl->win_pos[i].y=pl->win_pos[i].wy;
#endif /* FUNNY_WINDOW_MANAGER */
	    }
    pl->valid_save_positions=1;
	new_draw_info(NDI_UNIQUE, 0,op,"Current window positions will be saved.");
        return 0;
}
#endif /* SAVE_WINDOW_POSITIONS */

