/*
 * static char *rcsid_xio_c =
 *   "$Id: xio.c,v 1.52 1997/11/11 11:34:59 master Exp $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    Copyright (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
*/

#ifdef ds
#include <stddef.h>
#endif
#include <bitmaps.h>
#include <global.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <xio.h>
#include <spells.h>
#include <skills.h>
#include <object.h>
#include <stdarg.h>

static char font_text_stats[]="8x13";
static char font_text_info[]="8x13";
static char font_inv_text[]="8x13";

/*#ifdef USE_BUTTONS*/
static char *dirnames [NUMDIRBUTTS] = {
        "NW","No","NE","We","Br","Ea","SW","So","SE"};

static char *opnames [NUMOPBUTTS] = {
        "Change","Apply","Peace ","Talk  "};

int dirX [NUMDIRBUTTS] = {186, 216, 246,186,216,246,186,216,246};
int dirY [NUMDIRBUTTS] = {1,1,1,21,21,21,41,41,41};
int dirW = 20;

int opX [NUMOPBUTTS] = {158,218,158,218};
int opY [NUMOPBUTTS] = {61,61,81,81};
/*#endif*/



int IOerrors(Display *d) {
  player *pl;
  char buf[MAX_BUF];

  LOG(llevError,"Fatal error on display.\n");
/* Give pass 1 to emergency save - this tells it that it is only a backup
 * copy should be made, and that it shouldn't free any data.  Since we want
 * to try and continue from this error, that is what we want.
 */
  emergency_save(1);

  if(editor)
    exit(-1);
  for(pl=first_player;pl!=NULL;pl=pl->next)
    if(pl->gdisp==d)
      break;
  if(pl==NULL)
    return 0;

  SET_FLAG(pl->ob, FLAG_NO_FIX_PLAYER);
  /* It is possible that the player is removed (at login/logout screen)
   * If so, we don't want to save him/remove objects - this has
   * already been done.
   */
  if (!QUERY_FLAG(pl->ob, FLAG_REMOVED) && !save_player(pl->ob, 1)) {
	object *tmp;

	LOG(llevError,"save failed - trying manual removal of objects\n");
	while ((tmp=pl->ob->inv))
	    destroy_object(tmp);
  }
  /* Needs to be done again, because save_player clears this */
  SET_FLAG(pl->ob, FLAG_NO_FIX_PLAYER);

  LOG(llevError,"Player %s lost the display.\n",pl->name);
  (void) sprintf(buf,"%s lost connection.",pl->name);
  remove_lock(pl);

  /* prevent updating inventory of player for whom there is no longer a
  ** functional display */
  pl->freeze_inv = 1;

  if(pl->ob->map!=NULL)
    pl->ob->map->players--;
  if(pl->ob->state== ST_PLAYING ||pl->ob->state==ST_CHANGE_CLASS ||
	pl->ob->state==ST_CONFIGURE)
	    if( !QUERY_FLAG(pl->ob,FLAG_REMOVED)) remove_ob(pl->ob);


  if (!QUERY_FLAG(pl->ob, FLAG_FREED)) {
    destroy_object(pl->ob);
    pl->ob=NULL;    /* So free_player doesn't try to do it again */
  }

  free_player(pl);
/*
 * If in a server-mode, I assume a shell-script is restarting it.
 * Thus I exit if I can, since some variables can be messed up by the
 * longjmp() below.
 *
 * If we are in server mode, don't exit.  This way, maps don't reset.
 */
  if(first_player==NULL && settings.servermode !=SERVER_ENABLED) {
    LOG(llevError,"Only 1 player - exiting the program.\n");
    exit(0);
  }
  new_draw_info(NDI_UNIQUE | NDI_ALL, 5, NULL, buf);

#ifdef LONGJUMP
  longjmp(jump_addr,1);
#else
  fatal_signal(0, 1);
#endif
  return 0;
}

int Xerrors(Display *d,XErrorEvent *err) {
  char buf[MAX_BUF];

  XGetErrorText(d,err->error_code,buf,MAX_BUF);
  LOG(llevError,"Error code %s\n",buf);
  return 0;
}

void setuperrors() {
  XSetIOErrorHandler(IOerrors);
  XSetErrorHandler(Xerrors);
}

int get_root_display(player *p, char *display) {
#ifdef HAVE_SAVE_UID
  uid_t olduid;
#endif
  char *cp;
#if 0
  XSetWindowAttributes attr;
#endif

#ifdef HAVE_SAVE_UID
  olduid = geteuid();		/* I like to run it suid :) */
  setuid(getuid());		/* Have to this to open display, */
				/* if anyone is using xauth like me. */
#endif

  p->gdisp=XOpenDisplay(display);

  if (!p->gdisp) {
    sprintf(errmsg, "Can't open display %s.", display);
    return 1;
  }

  if ((cp=XGetDefault(p->gdisp,name,"splitwindow")) != NULL)
    if (!strcmp("on",cp) || !strcmp("yes",cp))
      p->split_window = 1;
    else if (!strcmp("off",cp) || !strcmp("no",cp))
      p->split_window = 0;
  if ((cp=XGetDefault(p->gdisp,"crossfire","pixmap")) != NULL)
    if (!strcmp("on",cp) || !strcmp("yes",cp))
      p->use_pixmaps = 1;
    else if (!strcmp("off",cp) || !strcmp("no",cp))
      p->use_pixmaps = 0;


#ifdef HAVE_SAVE_UID
  setuid(olduid);		/* Back to owner of suid crossfire */
#else
  setuid(geteuid());		/* We want both ruid == euid */
#endif

/*
  XAutoRepeatOff(p->gdisp);
*/
  p->gscreen=DefaultScreen(p->gdisp);
  p->gbackground=WhitePixel(p->gdisp,p->gscreen);
  p->gforeground=BlackPixel(p->gdisp,p->gscreen);
  p->roothint.x=0,p->roothint.y=0;
  p->roothint.width=582+6+INFOCHARS*FONTWIDTH;
  p->roothint.height=482;
  p->roothint.max_width=p->roothint.min_width=p->roothint.width;
  p->roothint.max_height=p->roothint.min_height=p->roothint.height;
  p->roothint.flags=PPosition | PSize;
  if(!p->split_window) {
    p->win_root=XCreateSimpleWindow(p->gdisp,DefaultRootWindow(p->gdisp),
        p->roothint.x,p->roothint.y,p->roothint.width,p->roothint.height,2,
        p->gbackground,p->gforeground);
    p->iscolor=allocate_colors(p->gdisp, p->win_root, p->gscreen,
	&p->colormap, p->discolor);
    if (p->iscolor){
	p->gforeground=p->discolor[DEFAULT_PLAYER_FG].pixel;
	p->gbackground=p->discolor[DEFAULT_PLAYER_BG].pixel;
	/* We need to set this here - while we pretend to set it in the
	 * XCreateSimpleWindow above, we actually haven't set the colors yet.
	 */
	XSetWindowBackground(p->gdisp, p->win_root, p->gforeground);
	XSetWindowBorder(p->gdisp, p->win_root, p->gbackground);
    }
    p->pixmap=XCreateBitmapFromData(p->gdisp,p->win_root,
          (_Xconst char *) crossfire_bits,
	  (unsigned int) crossfire_width, (unsigned int)crossfire_height);
    XSetStandardProperties(p->gdisp,p->win_root,name,name,
                           p->pixmap,settings.argv,settings.argc,&(p->roothint));
    p->gc_root=XCreateGC(p->gdisp,p->win_root,0,0);
    XSetForeground(p->gdisp,p->gc_root,p->gforeground);
    XSetBackground(p->gdisp,p->gc_root,p->gbackground);
  }
  if(!p->use_pixmaps)
	p->use_pixmaps = fixfontpath(p->gdisp,p->name);

  if(!p->split_window) {
    if(!p->use_pixmaps) {
      p->font=XLoadFont(p->gdisp,font_graphic);
      XSetFont(p->gdisp,p->gc_root,p->font);
    }
    XSelectInput(p->gdisp,p->win_root,KeyPressMask|
                 KeyReleaseMask|ExposureMask|StructureNotifyMask);
    XMapRaised(p->gdisp,p->win_root);
    XNextEvent(p->gdisp,&p->gevent);
  }
  setuperrors();
  return 0;
}

int get_game_display(player *p,char *display) {
  char *cp;
   
  p->gamehint.x=305,p->gamehint.y=104;
  p->gamehint.width=269,p->gamehint.height=269;
  p->gamehint.max_width=p->gamehint.min_width=p->gamehint.width;
  p->gamehint.max_height=p->gamehint.min_height=p->gamehint.height;
  p->gamehint.flags=PPosition | PSize;
  p->win_game=XCreateSimpleWindow(p->gdisp,
      p->split_window?DefaultRootWindow(p->gdisp):p->win_root,
      p->gamehint.x,p->gamehint.y,p->gamehint.width,p->gamehint.height,2,
      p->gbackground,p->gforeground);
  p->pixmap=XCreateBitmapFromData(p->gdisp,p->win_game,
          (_Xconst char *) crossfire_bits,
	  (unsigned int) crossfire_width, (unsigned int)crossfire_height);
  if (p->split_window) {
    p->iscolor=allocate_colors(p->gdisp, p->win_game, p->gscreen,
	&p->colormap, p->discolor);
    if (p->iscolor){
	p->gforeground=p->discolor[0].pixel;
	p->gbackground=p->discolor[8].pixel;
	/* We need to set this here - while we pretend to set it in the
	 * XCreateSimpleWindow above, we actually haven't set the colors yet.
	 */
	XSetWindowBackground(p->gdisp, p->win_game, p->gforeground);
	XSetWindowBorder(p->gdisp, p->win_game, p->gbackground);
    }
  } else if (p->iscolor) 
    XSetWindowColormap(p->gdisp, p->win_game, p->colormap);

  XSetStandardProperties(p->gdisp,p->win_game,"Crossfire",name,
                         p->pixmap,settings.argv,settings.argc, &(p->gamehint));

  /* Eneq(@csd.uu.se): Has to load the chrfont from the resources here. */

  if((cp=XGetDefault(p->gdisp,name,"chrfont"))!=NULL||settings.chrfont!=NULL) {
      XFontStruct *tmp;

      if (settings.chrfont!=NULL)
        cp=settings.chrfont;

      if ((tmp=XLoadQueryFont(p->gdisp, cp)) != NULL) {
        strcpy (p->font_str, cp);
        XFreeFont(p->gdisp, tmp);
      } else
        LOG(llevError, "Crossfire: Couldn't load font %s.\n", cp);
  }

  p->gc_game=XCreateGC(p->gdisp,p->win_game,0,0);
  if (p->iscolor){
     XSetForeground(p->gdisp,p->gc_game,p->discolor[DEFAULT_PLAYER_FG].pixel);
     XSetBackground(p->gdisp,p->gc_game,p->discolor[DEFAULT_PLAYER_FIELD].pixel);
  }
  else{
     XSetForeground(p->gdisp,p->gc_game,p->gforeground);
     XSetBackground(p->gdisp,p->gc_game,p->gbackground);
  }
  XSetGraphicsExposures(p->gdisp, p->gc_game, False);
  if(!p->use_pixmaps) {
    p->game_font=p->font=XLoadFont(p->gdisp,font_graphic);
    XSetFont(p->gdisp,p->gc_game,p->font);
  }
  if (p->color_pixmaps) {
	p->gc_xpm_floor = XCreateGC(p->gdisp,p->win_game,0,0);
	p->gc_xpm_object = XCreateGC(p->gdisp,p->win_game,0,0);
	XSetGraphicsExposures(p->gdisp, p->gc_xpm_floor, False);
	XSetGraphicsExposures(p->gdisp, p->gc_xpm_object, False);
	XSetClipOrigin(p->gdisp, p->gc_xpm_object,
		0, 0);
	p->xpm_pixmap = XCreatePixmap(p->gdisp, RootWindow(p->gdisp,
		DefaultScreen(p->gdisp)), 24, 24, DefaultDepth(p->gdisp,
		DefaultScreen(p->gdisp)));
  }

  XSelectInput(p->gdisp,p->win_game,
               ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask);
  XMapRaised(p->gdisp,p->win_game);
  return 0;
}

int get_info_display(player *p,char *name) {
  p->infohint.x=579,p->infohint.y=0;
  p->infohint.width=6+INFOCHARS*FONTWIDTH,p->infohint.height=11+p->infolines*13;
  p->infohint.min_width=100;
  p->infohint.min_height=30;
  p->infohint.flags=PPosition | PSize;
  p->win_info=XCreateSimpleWindow(p->gdisp,
      p->split_window?DefaultRootWindow(p->gdisp):p->win_root,
      p->infohint.x,p->infohint.y,p->infohint.width,p->infohint.height,2,
      p->gforeground,p->gbackground);
  if (p->iscolor)
    XSetWindowColormap(p->gdisp, p->win_info, p->colormap);
  p->pixmap=XCreateBitmapFromData(p->gdisp,p->win_info,
          (_Xconst char *) crossfire_bits,
	  (unsigned int) crossfire_width, (unsigned int)crossfire_height);
  XSetStandardProperties(p->gdisp,p->win_info,"Crossfire - text","Crosstext",
			 p->pixmap,settings.argv,settings.argc,&(p->infohint));
  p->gc_info=XCreateGC(p->gdisp,p->win_info,0,0);
  XSetForeground(p->gdisp,p->gc_info,p->gforeground);
  XSetBackground(p->gdisp,p->gc_info,p->gbackground);
  p->font=XLoadFont(p->gdisp,font_text_info);
  XSetFont(p->gdisp,p->gc_info,p->font);
  XSelectInput(p->gdisp,p->win_info,
               ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask|
               StructureNotifyMask);
  XMapRaised(p->gdisp,p->win_info);
  return 0;
}

void resize_win_info(player *p,XEvent *event) {
  int chars=(event->xconfigure.width/FONTWIDTH)-1;
  int lines=(event->xconfigure.height/FONTHEIGHT);
  int i;
  char **info;
  if(chars==p->infochars&&lines==p->infolines)
    return;
  if(chars<3||lines<3)
    return;
  info=(char **) malloc(sizeof(char *) * lines);

  /* If the new window is smaller, we want to lose the oldest lines
   * in the window.  This is not necessarily the same thing as the
   * bottom lines of the display.
   * If the buffer hasn't filled up yet (infofull==0), then
   * the oldes lines are those starting at 0.
   * If in wrap mode, the oldest lines are those after infoline.  If
   * in scroll mode, the oldest are those after infopos.
   * I had to re-write this function pretty majorly to do this.
   * Mark S. Wedel (master@rahul.net)
   */
  if (lines<p->infolines) {
	int diff = p->infolines - lines,start;

	if (!p->infofull) {
		if (p->infoline>lines) start=p->infoline-lines;
		else start=0;
	}
	else if (!p->scroll) start = p->infoline + diff;
	else start = p->infopos + diff;

	for (i=0; i<lines; i++) {
	    info[i]= (char *) malloc(sizeof(char) * (chars+1));
	    strncpy(info[i], p->info[(i+start) % p->infolines],chars);
	    info[i][chars]='\0';
	}
	if (p->infoline>=lines) p->infoline = lines-1;
	if (p->infopos>=lines) p->infopos = lines-1;
	p->infofull = 0;

  }
  else {
  /* If the window is getting bigger, things are a little simpler
   * than above.  However, for the data in scroll mode to make
   * sense, we do need to re-copy it in a make sense way.
   * MSW (master@cats.ucsc.edu)
   */
    int start;

    if (!p->infofull) start=0;
    else {
	if (p->scroll)
		start = p->infopos;
	else start = p->infoline;
	/* the way we copy the lines makes it so the oldest is
 	 * line 0, and the newest is the last line.  so update
	 * p->infoline accordingly
	 */
	p->infoline = lines-1; 
	p->infopos = lines - 1;
    }

	for (i=0; i<p->infolines; i++) {
	    info[i]= (char *) malloc(sizeof(char) * (chars+1));
	    strncpy(info[i], p->info[(i+start) % p->infolines], chars);
	    info[i][chars]='\0';
	}
	for (i=p->infolines; i<lines; i++) {
	    info[i]= (char *) malloc(sizeof(char) * (chars+1));
	    info[i][0]='\0';
	}
	p->infofull = 0;
  }
  for(i=0;i<p->infolines;i++)
    free(p->info[i]);
  free(p->info);

  if(p->writing>=chars)
    p->writing=chars-1;
  p->info=info;
  p->infochars=chars;
  p->infolines=lines;
  refresh_win_info(p->ob);
}

void resize_win_inv(player *p,XEvent *event) {
  int x=event->xconfigure.width;
  int y=event->xconfigure.height;
  int lines=(y-FONTHEIGHT-8)/24;

  if(x==p->invhint.x&&y==p->invhint.y)
    return;
  if(lines>MAX_INV_SIZE)
    lines=MAX_INV_SIZE;
  p->inv_size=lines;
  if (p->show_inv_icon)
    p->inv_chars=(x-88)/FONTWIDTH;
  else
    p->inv_chars=(x-60)/FONTWIDTH;
  p->invhint.width=x;
  p->invhint.height=y;
  p->barlength_inv=lines*24;
  sprintf(p->format_inv,"%%-%d.%ds%%-6s",p->inv_chars-6,p->inv_chars-6);
  draw_all_inventory(p->ob);
}

void resize_win_look(player *p,XEvent *event) {
  int x=event->xconfigure.width;
  int y=event->xconfigure.height;
  int lines=(y-FONTHEIGHT-8)/24;
  int chars=(x-60)/FONTWIDTH;
  if(x==p->lookhint.x&&y==p->lookhint.y)
    return;
  if(lines>MAX_LOOK_SIZE)
    lines=MAX_LOOK_SIZE;
  p->look_size=lines;
  p->look_chars=chars;
  p->lookhint.width=x;
  p->lookhint.height=y;
  p->barlength_look=lines*24;
  sprintf(p->format_look,"%%-%d.%ds%%-6s",chars-6,chars-6);
  draw_all_look(p->ob);
}

int get_message_display(player *p,char *name) { /* Misc-window */
  p->messagehint.x=303,p->messagehint.y=378;
  p->messagehint.width=273,p->messagehint.height=101;
  p->messagehint.max_width=p->messagehint.min_width=p->messagehint.width;
  p->messagehint.max_height=p->messagehint.min_height=p->messagehint.height;
  p->messagehint.flags=PPosition | PSize;
  p->win_message=XCreateSimpleWindow(p->gdisp,
      p->split_window?DefaultRootWindow(p->gdisp):p->win_root,
      p->messagehint.x,p->messagehint.y,p->messagehint.width,
      p->messagehint.height,2,p->gforeground,p->gbackground);
  if (p->iscolor)
    XSetWindowColormap(p->gdisp, p->win_message, p->colormap);
  p->pixmap=XCreateBitmapFromData(p->gdisp,p->win_message,
          (_Xconst char *) crossfire_bits,
	  (unsigned int) crossfire_width, (unsigned int)crossfire_height);
  XSetStandardProperties(p->gdisp,p->win_message,"Crossfire - vitals",
                         "crossvitals",p->pixmap,
                         settings.argv,settings.argc,&(p->messagehint));
  p->gc_message=XCreateGC(p->gdisp,p->win_message,0,0);
  XSetForeground(p->gdisp,p->gc_message,p->gforeground);
  XSetBackground(p->gdisp,p->gc_message,p->gbackground);
  p->font=XLoadFont(p->gdisp,font_text_info);
  XSetFont(p->gdisp,p->gc_message,p->font);
  XSelectInput(p->gdisp,p->win_message,
               ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask);
  XMapRaised(p->gdisp,p->win_message);
  return 0;
}

int get_inv_display(player *p,char *name) {
#ifdef OBWIN
  p->inv=create_obwin(p,0,0,300,310,p->split_window,"inventory");
#else
  p->invhint.x=0,p->invhint.y=0;
  p->invhint.width=300,p->invhint.height=310;
  p->invhint.min_width=60+10*FONTWIDTH;
  p->invhint.min_height=FONTHEIGHT+8+24*2;
  p->invhint.flags=PPosition | PSize;
  p->win_inv=XCreateSimpleWindow(p->gdisp,
      p->split_window?DefaultRootWindow(p->gdisp):p->win_root,
      p->invhint.x,p->invhint.y,p->invhint.width,p->invhint.height,2,
      p->gforeground,p->gbackground);
  if (p->iscolor)
    XSetWindowColormap(p->gdisp, p->win_inv, p->colormap);
  p->pixmap=XCreateBitmapFromData(p->gdisp,p->win_inv,
          (_Xconst char *) crossfire_bits,
	  (unsigned int) crossfire_width, (unsigned int)crossfire_height);
  XSetStandardProperties(p->gdisp,p->win_inv,"Crossfire - inventory",
                         "crossinventory",p->pixmap,settings.argv,settings.argc,
                         &(p->invhint));
  XSelectInput(p->gdisp,p->win_inv,
               ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask|
               StructureNotifyMask);
  XMapRaised(p->gdisp,p->win_inv);
  p->gc_inv_text=XCreateGC(p->gdisp,p->win_inv,0,0);
  p->gc_inv_icon=XCreateGC(p->gdisp,p->win_inv,0,0);

  /* Always create these, even thow we may not need them.  The space they use
   * is probably minimal, and to create/destroy them each time the user
   * switches would get pretty messy.
   */
  p->gc_inv_status_icon = XCreateGC(p->gdisp,p->win_inv,0,0);
  XSetForeground(p->gdisp,p->gc_inv_status_icon,p->gforeground);
  XSetBackground(p->gdisp,p->gc_inv_status_icon,p->gbackground);
  XSetGraphicsExposures(p->gdisp, p->gc_inv_status_icon, False);

  XSetForeground(p->gdisp,p->gc_inv_text,p->gforeground);
  XSetBackground(p->gdisp,p->gc_inv_text,p->gbackground);
  XSetForeground(p->gdisp,p->gc_inv_icon,p->gforeground);
  XSetBackground(p->gdisp,p->gc_inv_icon,p->gbackground);
  XSetGraphicsExposures(p->gdisp, p->gc_inv_icon, False);
  p->font=XLoadFont(p->gdisp,font_inv_text);
  XSetFont(p->gdisp,p->gc_inv_text,p->font);
  if(!p->use_pixmaps) {
    p->font=XLoadFont(p->gdisp,font_graphic);
    XSetFont(p->gdisp,p->gc_inv_status_icon,p->font);
    XSetFont(p->gdisp,p->gc_inv_icon,p->font);
  }
#endif
  return 0;
}

int get_look_display(player *p,char *name) {
  p->lookhint.x=0,p->lookhint.y=313;
  p->lookhint.width=300,p->lookhint.height=166;
  p->lookhint.min_width=60+10*FONTWIDTH;
  p->lookhint.min_height=FONTHEIGHT+8+24*2;
  p->lookhint.flags=PPosition | PSize;
  p->win_look=XCreateSimpleWindow(p->gdisp,
      p->split_window?DefaultRootWindow(p->gdisp):p->win_root,
      p->lookhint.x,p->lookhint.y,p->lookhint.width,p->lookhint.height,2,
      p->gforeground,p->gbackground);
  if (p->iscolor)
    XSetWindowColormap(p->gdisp, p->win_look, p->colormap);
  p->pixmap=XCreateBitmapFromData(p->gdisp,p->win_look,
          (_Xconst char *) crossfire_bits,
	  (unsigned int) crossfire_width, (unsigned int)crossfire_height);
  XSetStandardProperties(p->gdisp,p->win_look,"Crossfire - look",
                         "crosslook",p->pixmap,settings.argv,settings.argc,
                         &(p->lookhint));
  p->gc_look_text=XCreateGC(p->gdisp,p->win_look,0,0);
  p->gc_look_icon=XCreateGC(p->gdisp,p->win_look,0,0);
  XSetForeground(p->gdisp,p->gc_look_text,p->gforeground);
  XSetBackground(p->gdisp,p->gc_look_text,p->gbackground);
  XSetForeground(p->gdisp,p->gc_look_icon,p->gforeground);
  XSetBackground(p->gdisp,p->gc_look_icon,p->gbackground);
  XSetGraphicsExposures(p->gdisp, p->gc_look_icon, False);
  p->font=XLoadFont(p->gdisp,font_inv_text);
  XSetFont(p->gdisp,p->gc_look_text,p->font);
  if(!p->use_pixmaps) {
    p->font=XLoadFont(p->gdisp,font_graphic);
    XSetFont(p->gdisp,p->gc_look_icon,p->font);
  }
  XSelectInput(p->gdisp,p->win_look,
               ButtonPressMask|KeyPressMask|KeyReleaseMask|ExposureMask|
               StructureNotifyMask);
  XMapRaised(p->gdisp,p->win_look);
  return 0;
}

int get_stats_display(player *p,char *name) {
  p->stathint.x=303,p->stathint.y=0;
  p->stathint.width=273,p->stathint.height=100;
  p->stathint.min_width=p->stathint.max_width=p->stathint.width;
  p->stathint.min_height=p->stathint.max_height=p->stathint.height;
  p->stathint.flags=PPosition | PSize;
  p->win_stats=XCreateSimpleWindow(p->gdisp,
      p->split_window?DefaultRootWindow(p->gdisp):p->win_root,
      p->stathint.x,p->stathint.y,p->stathint.width,p->stathint.height,2,
      p->gforeground,p->gbackground);
  if (p->iscolor)
    XSetWindowColormap(p->gdisp, p->win_stats, p->colormap);
  p->pixmap=XCreateBitmapFromData(p->gdisp,p->win_stats,
          (_Xconst char *) crossfire_bits,
	  (unsigned int) crossfire_width, (unsigned int)crossfire_height);
  XSetStandardProperties(p->gdisp,p->win_stats,"Crossfire - status",
                         "crosstatus",p->pixmap,settings.argv,settings.argc,
                         &(p->stathint));
  p->gc_stats=XCreateGC(p->gdisp,p->win_stats,0,0);
  XSetForeground(p->gdisp,p->gc_stats,p->gforeground);
  XSetBackground(p->gdisp,p->gc_stats,p->gbackground);
  p->font=XLoadFont(p->gdisp,font_text_stats);
  XSetFont(p->gdisp,p->gc_stats,p->font);
  XSelectInput(p->gdisp,p->win_stats,
               KeyPressMask|KeyReleaseMask|ExposureMask);
  XMapRaised(p->gdisp,p->win_stats);
  return 0;
}

#ifdef SET_TITLE
void redraw_title(object *pl)
{
  char buf[MAX_BUF];
  if (pl->contr->own_title[0]=='\0')
    sprintf(buf,"Player: %s the %s",pl->name,pl->contr->title);
  else
    sprintf(buf,"Player: %s the %s",pl->name,pl->contr->own_title);
  strcat(buf,"                    ");
  XDrawImageString(pl->contr->gdisp,pl->contr->win_stats,
                  pl->contr->gc_stats,10,10,buf,strlen(buf));
}
#endif /* SET_TITLE */

void rangetostring(object *pl,char *obuf)
{
  int chosen_spell;

  chosen_spell = (pl->contr->shoottype==range_magic)? pl->contr->chosen_spell :
      pl->contr->chosen_item_spell;
  switch(pl->contr->shoottype) {
   case range_none:
    strcpy(obuf,"Range: nothing");
    break;
   case range_bow: {
     char *s;
     object *op;
     for (op = pl->inv; op; op=op->below)
       if (op->type == BOW && QUERY_FLAG (op, FLAG_APPLIED))
       break;
     if(op==NULL) break;
     s = query_name(op);
#if 1                         /* Hack to remove (readied) from a bow description */
     if (strcmp (s + strlen (s) - 10, " (readied)") == 0)
       s[strlen (s) - 10] = 0;
#endif
     sprintf (obuf, "Range: %s (%s)", s, 
            op && op->race ? op->race : "nothing");
   }
    break;
   case range_magic:
#ifdef CASTING_TIME
    if (pl->casting > -1) {
      if (pl->casting == 0)
      sprintf(obuf,"Range: Holding spell (%s)",
              pl->spell->name);
      else
      sprintf(obuf,"Range: Casting spell (%s)",
              pl->spell->name);
    }
    else
#endif
      sprintf(obuf,"Range: spell (%s)",
              spells[pl->contr->chosen_spell].name);
    break;
   case range_wand:
    sprintf(obuf,"Range: wand (%s)",
          pl->contr->known_spell ?
          spells[pl->contr->chosen_item_spell].name : "unknown");
    break;
   case range_rod:
    sprintf(obuf,"Range: rod (%s)",
          pl->contr->known_spell ?
          spells[pl->contr->chosen_item_spell].name : "unknown");
    break;
   case range_horn:
    sprintf(obuf,"Range: horn (%s)",
          pl->contr->known_spell ?
          spells[pl->contr->chosen_item_spell].name : "unknown");
    break;
    /* range_scroll is only used for controlling golems.  If the
     * the player does not have a golem, reset some things.
     */
   case range_scroll:
    if (pl->contr->golem!=NULL)
      sprintf(obuf,"Range: golem (%s)",pl->contr->golem->name);
    else {
      pl->contr->shoottype = range_none;
      strcpy(obuf,"Range: nothing");
    }
    break;
   case range_skill:
       sprintf(obuf,"Skill: %s", pl->chosen_skill!=NULL ?
		skills[pl->chosen_skill->stats.sp].name : "none");
    break;
   default:
    strcpy(obuf,"Range: illegal");
  }
  pl->contr->last_known_spell = pl->contr->known_spell;
  pl->contr->last_shoot=pl->contr->shoottype;
  pl->contr->last_spell=chosen_spell;
}

void set_title(object *pl,char *buf)
{
  if(pl->contr->last_value==-1) {
    /* Eneq(@csd.uu.se): Let players define their own titles. */

    if (pl->contr->own_title[0]=='\0')
      sprintf(buf,"Player: %s the %s",pl->name,pl->contr->title);
    else
      sprintf(buf,"Player: %s the %s",pl->name,pl->contr->own_title);
  }
}



void draw_stats(object *pl) {
  char buff[7][MAX_BUF];
  int flag[7];
  int i, chosen_spell;

  if(pl->type!=PLAYER) {
    LOG(llevError,"Warning: draw_stats() in non-player: %s (%d)\n",
            pl->name,pl->count);
    return;
  }

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    esrv_update_stats(pl->contr->eric_server,pl);
    return;
  }

  for(i=0;i<7;i++)
    flag[i]=0;

  if(pl->contr->last_value==-1) {
    flag[0]=1;
    set_title(pl,buff[0]);
  }
  if(pl->contr->last_value==-1||pl->stats.exp!=pl->contr->last_stats.exp||
     pl->contr->last_level!=pl->level) {
    sprintf(buff[1],"Score: %5d  Level: %d",pl->stats.exp,pl->level);
    flag[1]=1;
    pl->contr->last_stats.exp=pl->stats.exp;
    pl->contr->last_level=pl->level;
  }
  if(pl->contr->last_value==-1||
     pl->stats.hp!=pl->contr->last_stats.hp||
     pl->stats.maxhp!=pl->contr->last_stats.maxhp||
     pl->stats.sp!=pl->contr->last_stats.sp||
     pl->stats.maxsp!=pl->contr->last_stats.maxsp||
	  pl->stats.grace!=pl->contr->last_stats.grace||
	  pl->stats.maxgrace!=pl->contr->last_stats.maxgrace) {
    sprintf(buff[2],"Hp:%d/%d Sp:%d/%d Gr:%d/%d",
            pl->stats.hp,pl->stats.maxhp,pl->stats.sp,pl->stats.maxsp,
				pl->stats.grace,pl->stats.maxgrace);
    flag[2]=1;
    pl->contr->last_stats.hp=pl->stats.hp;
    pl->contr->last_stats.maxhp=pl->stats.maxhp;
    pl->contr->last_stats.sp=pl->stats.sp;
    pl->contr->last_stats.maxsp=pl->stats.maxsp;
	 pl->contr->last_stats.grace=pl->stats.grace;
	 pl->contr->last_stats.maxgrace=pl->stats.maxgrace;
  }
  if(pl->contr->last_value==-1||
    pl->stats.Dex!=pl->contr->last_stats.Dex ||
    pl->stats.Con!=pl->contr->last_stats.Con ||
    pl->stats.Str!=pl->contr->last_stats.Str ||
    pl->stats.Int!=pl->contr->last_stats.Int ||
    pl->stats.Wis!=pl->contr->last_stats.Wis ||
    pl->stats.Pow!=pl->contr->last_stats.Pow ||
    pl->stats.Cha!=pl->contr->last_stats.Cha) {
    sprintf(buff[3],"S%2d D%2d Co%2d I%2d W%2d P%2d Ch%2d",
            pl->stats.Str,pl->stats.Dex,pl->stats.Con,
            pl->stats.Int,pl->stats.Wis,pl->stats.Pow,
	    pl->stats.Cha);
    flag[3]=1;
    pl->contr->last_stats.Str=pl->stats.Str;
    pl->contr->last_stats.Con=pl->stats.Con;
    pl->contr->last_stats.Dex=pl->stats.Dex;
    pl->contr->last_stats.Int=pl->stats.Int;
    pl->contr->last_stats.Wis=pl->stats.Wis;
    pl->contr->last_stats.Pow=pl->stats.Pow;
    pl->contr->last_stats.Cha=pl->stats.Cha;
  }
  if(pl->contr->last_value==-1||
     pl->stats.wc!=pl->contr->last_stats.wc||
     pl->stats.ac!=pl->contr->last_stats.ac||
     pl->armour!=pl->contr->last_armour||
     pl->stats.dam!=pl->contr->last_stats.dam) {
     sprintf(buff[4],"Wc:%3d Dam:%3d Ac:%3d Arm:%3d",
             pl->stats.wc,pl->stats.dam,pl->stats.ac,pl->armour);
     flag[4]=1;
     pl->contr->last_stats.wc=pl->stats.wc;
     pl->contr->last_stats.ac=pl->stats.ac;
     pl->contr->last_stats.dam=pl->stats.dam;
     pl->contr->last_armour=pl->armour;
   }
  if(pl->contr->last_value==-1||pl->contr->last_speed!=pl->speed||
     pl->stats.food!=pl->contr->last_stats.food||
     pl->contr->last_weapon_sp!=pl->contr->weapon_sp) {
     flag[5]=1;
     if(pl->stats.food<100&&(pl->stats.food&4))
       sprintf(buff[5],"Speed: %3.2f (%1.2f) Food: *%d* HUNGRY!",
               pl->speed,pl->speed/pl->contr->weapon_sp,pl->stats.food);
     else
       sprintf(buff[5],"Speed: %3.2f (%1.2f)  Food: %3d",
               pl->speed,pl->speed/pl->contr->weapon_sp,pl->stats.food);
     pl->contr->last_stats.food=pl->stats.food;
     pl->contr->last_speed=pl->speed;
     pl->contr->last_weapon_sp=pl->contr->weapon_sp;
  }
  chosen_spell = (pl->contr->shoottype==range_magic)? pl->contr->chosen_spell :
	pl->contr->chosen_item_spell;
#ifdef CASTING_TIME
  if(pl->spell_state||pl->contr->last_value==-1||
	pl->contr->last_shoot!=pl->contr->shoottype||
     ((pl->contr->shoottype>range_bow && pl->contr->shoottype < range_size)&&
       pl->contr->last_spell!=chosen_spell)) {
    pl->spell_state = 0;
#else
  if(pl->contr->last_value==-1||pl->contr->last_shoot!=pl->contr->shoottype||
     ((pl->contr->shoottype>range_bow && pl->contr->shoottype < range_size)&&
       pl->contr->last_spell!=chosen_spell)) {
#endif
    flag[6]=1;
     rangetostring(pl,buff[6]);
  }

  if(pl->contr->shoottype==range_skill) {
    flag[6]=1;
     rangetostring(pl,buff[6]);
  }

  for(i=0;i<7;i++)
    if(flag[i]) {
      strcat(buff[i],"                     "); /* In case it became shorter */
      XDrawImageString(pl->contr->gdisp,pl->contr->win_stats,
        pl->contr->gc_stats,10,i*14+10, buff[i],strlen(buff[i]));
    }
  if(pl->contr->last_value!= -1)
    draw_message_window(pl); /* Update bars */
  pl->contr->last_value=pl->value;
}

void draw_all_info(object *pl) {
  int i;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"draw_all_info called in eric_server mode.\n");
    return;
  }

  XClearWindow(pl->contr->gdisp,pl->contr->win_info);
  if ((pl->contr->scroll) && (pl->contr->infofull)) {
     int j;
     for (i=0; i<pl->contr->infolines; i++) {
	j = (pl->contr->infopos+i + 1) % pl->contr->infolines;
        XDrawImageString(pl->contr->gdisp,pl->contr->win_info,
         pl->contr->gc_info,FONTWIDTH,(i+1)*FONTHEIGHT,
         pl->contr->info[j],  strlen(pl->contr->info[j]));
     }
  }
  else 
      for(i=0;i<pl->contr->infolines;i++)
        XDrawImageString(pl->contr->gdisp,pl->contr->win_info,
         pl->contr->gc_info,FONTWIDTH,(i+1)*FONTHEIGHT,
         pl->contr->info[i],strlen(pl->contr->info[i]));
}

#define draw_inv_face(pl,win,gc,x,y,face) \
{ \
  if (pl->color_pixmaps) \
  { \
    XSetClipMask(pl->gdisp, gc, pl->masks[(face)]); \
    XSetClipOrigin(pl->gdisp, gc, x, (y) - 24); \
    XCopyArea(pl->gdisp, pl->pixmaps[(face)], win, gc, 0, 0, 24, 24, \
        (unsigned int) (x), (unsigned int) ((y) - 24)); \
  } \
  else if(pl->use_pixmaps) \
  { \
    XSetClipMask(pl->gdisp, gc, pl->pixmaps[(face)]); \
    XSetClipOrigin(pl->gdisp, gc, x, (y) - 24); \
    XCopyPlane(pl->gdisp,pl->pixmaps[(face)],win,gc,0,0,24,24, \
               (unsigned int) (x),(unsigned int) ((y) - 24),1); \
  } \
  else \
  { \
    XChar buf; \
    buf = FontindexToXChar((Fontindex) (face)); \
    XDrawString16(pl->gdisp,win,gc,x,y,&buf,1); \
  } \
}


/* This routine has gotten a little more complicated with the addition
 * of color pixmaps (XPM).  This is because normally, drawing a face
 * would overwrite the old face drawn there.  However, with shape masks,
 * this does not happen.  So in order to keep things looking right, a
 * XClearArea is called to clear the face area before draw_face is
 * called.  This is only done if the user is using color pixmaps.
 * Mark Wedel (master@rahul.net)
 */
/* Also, I changed the start height values to start at 16 + 24*(other stuff)
 * This is because the draw_face calls start at 40 + 24*(other_stuff).
 * The draw_face macro then subtracts 24.  40-24 = 16.  Without this
 * changed, not all information was being erased (This is because of
 * the change to use XClearArea instead of overdrawing the area with
 * another face.  The later case did not look nice when the color
 * pixmap (XPM) additions.  MSW (master@rahul.net)
 */
void draw_inventory(object *pl) {
  signed short items;
  int i,j;
  object *tmp;
  char buf[MAX_BUF], *obj_name, name_buf[MAX_BUF];
  player *p=pl->contr;

  if(pl->type!=PLAYER)
    return;
  if(p->freeze_inv)
    return;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
      LOG(llevError,"Warning, draw_inventory called in eric server mode.\n");
    return;
  }

  for(tmp=pl->inv,items=0;tmp!=NULL;tmp=tmp->below)
    if (show_what_object(tmp,p->show_what))
      items++;

  if(p->scroll_inv>(items - p->inv_size))
    p->scroll_inv=items-p->inv_size;
  if(p->scroll_inv<0)
    p->scroll_inv=0;
  if(p->last_weight!=pl->weight+pl->carrying) {
    p->last_weight=pl->weight+pl->carrying;
    sprintf(buf,"Inventory%s: (%s)",
            (p->show_what == show_applied ? " (applied) " :
            (p->show_what == show_unapplied ? " (unapplied) " : 
	    (p->show_what == show_unpaid ? " (unpaid) " :
	    (p->show_what == show_cursed ? " (cursed) ": 
	    (p->show_what == show_magical ? " (magical) " :
	    (p->show_what == show_nonmagical ? " (nonmagical) " : "")))))),
            query_weight(pl));
    XDrawImageString(p->gdisp,p->win_inv,
                     p->gc_inv_text,8,13,buf,strlen(buf));
  }
  for(tmp=pl->inv,i=0;tmp!=NULL;tmp=tmp->below,i++) {
    if (!show_what_object(tmp, p->show_what))
    {
      i--;
      continue;
    }
    if(i-p->scroll_inv>=p->inv_size||i<p->scroll_inv)
      continue;

    if(p->inv_face[i-p->scroll_inv]!=tmp->face->number) {
      p->inv_face[i-p->scroll_inv]=tmp->face->number;
      if (p->color_pixmaps) 
	XClearArea(p->gdisp, p->win_inv, 4, 16+24*(i-p->scroll_inv), 24,24,False);
      draw_face(p,p->win_inv,p->gc_inv_icon,4,40+24*(i-p->scroll_inv),tmp->face->number);
    }
    obj_name = query_name(tmp);

    /* Name buf is just the object name and weight.  Both need to be stored,
     * otherwise this would not always be updated (when dropping a short
     * sword with another below it, the weight of the next short sword would
     * be shown as the same as the original.
     */
    strcpy(name_buf,obj_name);
    strcat(name_buf,query_weight(tmp));

    if(p->inv_name[i-p->scroll_inv] &&
	 !strcmp(p->inv_name[i-p->scroll_inv],name_buf))
      continue;
    sprintf(buf,p->format_inv,obj_name,query_weight(tmp));

    if (p->show_inv_icon) {
      XClearArea(p->gdisp, p->win_inv, 32, 16+24*(i-p->scroll_inv), 24,24,False);
      if (QUERY_FLAG(tmp, FLAG_INV_LOCKED))
	draw_inv_face(p, p->win_inv, p->gc_inv_status_icon,32, 40+24*(i-p->scroll_inv),
	    inv_lock_face->number);
      if (QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)) {
        if (QUERY_FLAG(tmp, FLAG_DAMNED)) {
	  draw_inv_face(p, p->win_inv, p->gc_inv_status_icon,32, 40+24*(i-p->scroll_inv),
	    inv_damn_face->number);
        } else {
	  draw_inv_face(p, p->win_inv, p->gc_inv_status_icon,32, 40+24*(i-p->scroll_inv),
	      inv_curse_face->number);
	}
      }
      if (QUERY_FLAG(tmp,FLAG_KNOWN_MAGICAL) &&
       !QUERY_FLAG(tmp,FLAG_IDENTIFIED) && !QUERY_FLAG(tmp,FLAG_BEEN_APPLIED))
	  draw_inv_face(p, p->win_inv, p->gc_inv_status_icon,32, 40+24*(i-p->scroll_inv),
	    inv_magic_face->number);
      if (QUERY_FLAG(tmp, FLAG_APPLIED))
	draw_inv_face(p, p->win_inv, p->gc_inv_status_icon,32, 40+24*(i-p->scroll_inv),
	    inv_equip_face->number);
      if (QUERY_FLAG(tmp, FLAG_UNPAID))
	draw_inv_face(p, p->win_inv, p->gc_inv_status_icon,32, 40+24*(i-p->scroll_inv),
	    inv_unpaid_face->number);
    } /* if show_inv_icon */

    if (p->inv_name[i-p->scroll_inv])
	free_string(p->inv_name[i-p->scroll_inv]);
    p->inv_name[i-p->scroll_inv] = add_string(name_buf);

    if (p->show_inv_icon)
	XDrawImageString(p->gdisp,p->win_inv,
                     p->gc_inv_text,60,34+24*(i-p->scroll_inv)
                     ,buf,strlen(buf));
    else
	XDrawImageString(p->gdisp,p->win_inv,
                     p->gc_inv_text,32,34+24*(i-p->scroll_inv)
                     ,buf,strlen(buf));
  }
  if((i-p->scroll_inv)<=p->nrofdrawn_inv) {
    int start_height=16+24*(i-p->scroll_inv);
    int height;
    for(j=i;(j-p->scroll_inv)<=p->nrofdrawn_inv&&
        (j-p->scroll_inv)<p->inv_size;j++) {
      p->inv_face[j-p->scroll_inv]=blank_face->number;
      p->inv_name[j-p->scroll_inv]=NULL;
    }
    if(p->inv_size<p->nrofdrawn_inv)
      height=p->inv_size*24;
    else
      height=p->nrofdrawn_inv*24;
    XClearArea(p->gdisp,p->win_inv,0,start_height,
               p->invhint.width-23,height*24,0);
  }
  if(i<p->inv_size)
    i=p->inv_size;
  p->nrofdrawn_inv=i;
  j =((int)p->inv_size * (int)p->barlength_inv / i);
  if(p->scrollsize_inv!=j||
     p->last_scroll_inv!=p->scroll_inv)
  { /* Change the scrollbar if different */
    int offset = (int)p->scroll_inv * (int)p->barlength_inv / i;
    XClearArea(p->gdisp,p->win_inv,p->invhint.width-20,17+p->scrollstart_inv,
               16,p->scrollsize_inv,0);
    p->scrollsize_inv=j;
    p->scrollstart_inv=offset;
    p->last_scroll_inv=p->scroll_inv;
    XFillRectangle(p->gdisp,p->win_inv,p->gc_inv_text,
                   p->invhint.width-20,17+offset,16,p->scrollsize_inv);
  }
}

void draw_inventory_faces(object *pl) {
  int i,items;
  object *tmp, *inv=pl->inv;
  player *p=pl->contr;

  if(p->freeze_inv)
    return;
  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
/* This gets to be a bit too verbose */
      LOG(llevDebug,"Warning, draw_inventory_faces called in eric_server mode.\n");
    return;
  }

  for(tmp=inv,items=0;tmp!=NULL;tmp=tmp->below)
    if (show_what_object(tmp, p->show_what))
      items++;
  if(p->scroll_inv>items-p->inv_size)
    p->scroll_inv=items-p->inv_size;
  if(p->scroll_inv<0)
    p->scroll_inv=0;
  for(tmp=inv,i=0;tmp!=NULL;tmp=tmp->below) {
    if (show_what_object(tmp, p->show_what)) {
	if (!(i-p->scroll_inv>=p->inv_size || i<p->scroll_inv ||
	  p->inv_face[i-p->scroll_inv]==tmp->face->number)) {

	    p->inv_face[i-p->scroll_inv]=tmp->face->number;
	    if (p->color_pixmaps) 
	    XClearArea(p->gdisp, p->win_inv, 4, 16+24*(i-p->scroll_inv), 24,24,False);
	    draw_face(p,p->win_inv,p->gc_inv_icon,4,40+24*(i-p->scroll_inv),tmp->face->number);
	}
    i++;
    }
  }
  if((i-p->scroll_inv)<=p->nrofdrawn_inv) {
	int	start_height = 16 + 24*(i-p->scroll_inv),height;
	if (p->inv_size<p->nrofdrawn_inv)
		height = p->inv_size * 24;
	else
		height = p->nrofdrawn_inv * 24;
	XClearArea(p->gdisp, p->win_inv, 0, start_height, 28, height, False);

  }
}

void draw_all_inventory(object *pl) {
  int i;
  player *p=pl->contr;

  for(i=0;i<p->inv_size;i++) {
    p->inv_face[i]=blank_face->number;
    p->inv_name[i]=NULL;
  }

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
     LOG(llevDebug, "Warning, draw_all_inventory called in eric server mode\n");
    return;
  }

  XClearWindow(p->gdisp,p->win_inv);
  XDrawRectangle(p->gdisp,p->win_inv,
                 p->gc_inv_text,p->invhint.width-22,15,20,p->barlength_inv+4);
  p->scrollsize_inv=1,p->scrollstart_inv=1,
  p->nrofdrawn_inv=0;
  p->last_weight= (-1);
  p->freeze_inv=0;
  draw_inventory(pl);
}


void draw_look(object *pl) {
  signed short i,j,items;
  object *tmp,*top;
  char buf[MAX_BUF];
  player *p=pl->contr;

  if(p->freeze_look)
    return;

  if(QUERY_FLAG(pl, FLAG_REMOVED) || pl->map == NULL || pl->map->in_memory != MAP_IN_MEMORY)
    return;
  if(out_of_map(pl->map,pl->x,pl->y))
    return;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    esrv_draw_look(pl);
    return;
  }


  /* Eneq(@csd.uu.se): Display container or look. */

  if (pl->container==NULL)
    for(top=get_map_ob(pl->map,pl->x,pl->y);top!=NULL&&top->above!=NULL;
        top=top->above);
  else
    top = pl->container->inv;
  for(tmp=top,items=0;(tmp!=NULL)&&(!(tmp->above&&QUERY_FLAG(tmp->above,FLAG_IS_FLOOR))||QUERY_FLAG(tmp,FLAG_IS_FLOOR));tmp=tmp->below)
    if LOOK_OBJ(tmp)
      items++;

  if(p->scroll_look>items-p->look_size)
    p->scroll_look=items-p->look_size;
  if(p->scroll_look<0)
    p->scroll_look=0;
  XSetForeground(p->gdisp,p->gc_look_text,
                 p->gforeground);

  for(tmp=top,i=0;(tmp!=NULL)&&(!(tmp->above&&QUERY_FLAG(tmp->above,
	FLAG_IS_FLOOR))||QUERY_FLAG(tmp,FLAG_IS_FLOOR));tmp=tmp->below,i++)
  {
    if (!LOOK_OBJ(tmp)) {
      i--;
      continue;
    }
    /* If we have drawn all that is possible, break out of this loop */
    if ( i- p->scroll_look>=p->look_size) break;
    if (i<p->scroll_look)
      continue;

    if(p->look_face[i-p->scroll_look]!=tmp->face->number) {
	p->look_face[i-p->scroll_look]=tmp->face->number;
	if (p->color_pixmaps) 
	    XClearArea(p->gdisp, p->win_look, 4, 16+24*(i-p->scroll_look), 24,24,False);
	draw_face(p,p->win_look,p->gc_look_icon,4,40+24*(i-p->scroll_look),tmp->face->number);
    }
    if(QUERY_FLAG(tmp, FLAG_NO_PICK)) {
      char *cp;
      strncpy(buf,query_name(tmp),p->look_chars); /* Don't worry about screenwidth */
      buf[p->look_chars]='\0';
      cp=strchr(buf,'\0');

      /* We need to pad the string with spaces, so that it overwrites the
       * previous item name printed in the window.
       */

      if((sint16)strlen(buf)<p->look_chars)
        memset((void *)cp,' ',p->look_chars-strlen(buf));
      buf[p->look_chars]='\0';
    } else
      sprintf(buf,p->format_look,query_name(tmp),query_weight(tmp));
    if(!p->look_name[i-p->scroll_look] || strcmp(buf,p->look_name[i-p->scroll_look])) {
	if (p->look_name[i-p->scroll_look])
	    free_string(p->look_name[i-p->scroll_look]);
	p->look_name[i-p->scroll_look] = add_string(buf);
	XDrawImageString(p->gdisp,p->win_look,
                     p->gc_look_text,32,34+24*(i-p->scroll_look)
                     ,buf,strlen(buf));
    }
  }

  /* If there are not enough items to fill in the display area,
   * then set the unused ares to nothing.
   */

  if(items<p->nrofdrawn_look) {
    int start_height=16+24*(i-p->scroll_look);
    int height;

    for(j=i;(j-p->scroll_look)<=p->nrofdrawn_look&&
        (j-p->scroll_look)<p->look_size;j++) {
      p->look_name[j-p->scroll_look]=NULL;
      p->look_face[j-p->scroll_look]=blank_face->number;
    }
    if(p->look_size<p->nrofdrawn_look)
      height=p->look_size*24;
    else
      height=p->nrofdrawn_look*24;
    XClearArea(p->gdisp,p->win_look,0,start_height,
               p->lookhint.width-23,height,0);
  }
  p->nrofdrawn_look=items;
  if(items<p->look_size)
    items=p->look_size;
  j =((int)p->look_size * (int)p->barlength_look / items);

  /* draw the scrollbar now */
  if(p->scrollsize_look!=(unsigned short) j||
     p->last_scroll_look!=p->scroll_look) {
    int offset =(int)p->scroll_look * (int)p->barlength_look / items;

    XClearArea(p->gdisp,p->win_look,p->lookhint.width-20,
               17+p->scrollstart_look,16,p->scrollsize_look,0);
    p->scrollsize_look=j;
    p->scrollstart_look=offset;
    p->last_scroll_look=p->scroll_look;
    XFillRectangle(p->gdisp,p->win_look,p->gc_look_text,
                   p->lookhint.width-20,17+offset,16,p->scrollsize_look);
  }
}

/* This only draws the look faces.  Maybe used for animations?
 */

void draw_look_faces(object *pl) {
  int i,items;
  object *tmp,*top;
  player *p=pl->contr;


  if(p->freeze_look)
    return;
  if(QUERY_FLAG(pl, FLAG_REMOVED)||out_of_map(pl->map,pl->x,pl->y))
    return;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"\tdraw_look_faces called in eric_server mode.\n");
    return;
  }
  for(top=(pl->container?pl->container->inv:get_map_ob(pl->map,pl->x,pl->y));
      top!=NULL&&top->above!=NULL;
      top=top->above);

  for(tmp=top,items=0;(tmp!=NULL)&&(!(tmp->above&&
	QUERY_FLAG(tmp->above,FLAG_IS_FLOOR))||QUERY_FLAG(tmp,FLAG_IS_FLOOR)); tmp=tmp->below)
    if LOOK_OBJ(tmp)
      items++;

  /* If the number of objects is different than what was drawn
   * before, then we should also draw the names.  So just call
   * draw_look to do it.
   */
  if (p->nrofdrawn_look!=items) {
	draw_look(pl);
	return;
  }

  if(p->scroll_look>items-p->look_size)
    p->scroll_look=items-p->look_size;
  if(p->scroll_look<0)
    p->scroll_look=0;

  for(tmp=top,i=0;(tmp!=NULL)&&(!(tmp->above&&QUERY_FLAG(tmp->above,FLAG_IS_FLOOR))||QUERY_FLAG(tmp,FLAG_IS_FLOOR));tmp=tmp->below,i++) {
    if (!LOOK_OBJ(tmp)) {
      i--;
      continue;
    }
    if(i-p->scroll_look>=p->look_size) break;
    if (i<p->scroll_look) continue;

    if(p->look_face[i-p->scroll_look]!=tmp->face->number) {
	p->look_face[i-p->scroll_look]=tmp->face->number;
	if (p->color_pixmaps) 
	    XClearArea(p->gdisp, p->win_look, 4, 16+24*(i-p->scroll_look), 24,24,False);
	draw_face(p,p->win_look,p->gc_look_icon,4,40+24*(i-p->scroll_look),tmp->face->number);
    }
  }
  /* No need to do any erasing, because if the number of objects has
   * decreased, the draw_look function was called instead.
   */
}

void draw_all_look(object *pl) {
  int i;
  player *p=pl->contr;

  for(i=0;i<p->look_size;i++) {
    p->look_face[i]=blank_face->number;
    p->look_name[i]=NULL;
  }

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    esrv_draw_look(pl);
    return;
  }

  XClearWindow(p->gdisp,p->win_look);
  XDrawRectangle(p->gdisp,p->win_look,p->gc_look_text,
                 p->lookhint.width-22,15,20,p->barlength_look+4);
  p->scrollsize_look=1,p->scrollstart_look=1,
  p->nrofdrawn_look=0;
  if (!pl->container)
  XDrawImageString(p->gdisp,p->win_look,
                     p->gc_look_text,8,13,"You see:     ",13);
  else
    XDrawImageString(p->gdisp,p->win_look,
                     p->gc_look_text,8,13,"In container:",13);
  p->freeze_look=0;
  draw_look(pl);
}

#define MAX_BARS_MESSAGE 80

static void draw_stat_bar(object *pl, int bar_pos, 
			  int height, int old_height,
			  int is_alert, int old_is_alert)
{
  if(height==old_height && is_alert==old_is_alert) /* nothing changed */
    return;
  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"draw_stat_bar called in ericserver mode.\n");
    return;
  }

  if(height!=MAX_BARS_MESSAGE)	/* clear the top of the bar */
    XClearArea(pl->contr->gdisp,pl->contr->win_message,
	       bar_pos, 4,
	       10, MAX_BARS_MESSAGE-height,
	       0);
  if(height==0)			/* empty bar */
    return;
  if(is_alert && pl->contr->iscolor) /* this should have its own gc */
	 XSetForeground(pl->contr->gdisp,pl->contr->gc_look_text,
			pl->contr->discolor[3].pixel);
       XFillRectangle(pl->contr->gdisp,pl->contr->win_message,
		 pl->contr->gc_look_text,
		 bar_pos, 4+MAX_BARS_MESSAGE-height,
		 10, height);
  if(is_alert && pl->contr->iscolor)
	 XSetForeground(pl->contr->gdisp,pl->contr->gc_look_text,
			pl->contr->gforeground);
     }

void draw_message_window(object *pl) {
  int bar,is_alert;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"\tdraw_message_window called in eric_server mode.\n");
    return;
  }
  /* draw hp bar */
  if(pl->stats.maxhp>0)
    {
      bar=(pl->stats.hp*MAX_BARS_MESSAGE)/pl->stats.maxhp;
      if(bar<0)
	bar=0;
      is_alert=(pl->stats.hp <= pl->stats.maxhp/4);
    }
  else
    {
      bar=0;
      is_alert=0;
    }
  draw_stat_bar(pl,20,
		bar,pl->contr->scrollsize_hp,
		is_alert,pl->contr->scrollhp_alert);
  pl->contr->scrollsize_hp=bar;
  pl->contr->scrollhp_alert=is_alert;

  /* draw sp bar.  spellpoints can go above max
   * spellpoints via supercharging with the transferrance spell,
   * or taking off items that raise max spellpoints.
   */
  if (pl->stats.sp>pl->stats.maxsp)
	bar = MAX_BARS_MESSAGE;
  else
	bar=(pl->stats.sp*MAX_BARS_MESSAGE)/pl->stats.maxsp;
  if(bar<0) 
    bar=0;

  is_alert=(pl->stats.sp <= pl->stats.maxsp/4);

  draw_stat_bar(pl,60,
		bar,pl->contr->scrollsize_sp,
		is_alert,pl->contr->scrollsp_alert);
       pl->contr->scrollsize_sp=bar;
  pl->contr->scrollsp_alert=is_alert;
  
  /* draw grace bar.  grace can go above  max or below min
   */
  if (pl->stats.grace>pl->stats.maxgrace)
	bar = MAX_BARS_MESSAGE;
  else
	bar=(pl->stats.grace*MAX_BARS_MESSAGE)/pl->stats.maxgrace;
  if(bar<0) 
    bar=0;

  is_alert=(pl->stats.grace <= pl->stats.maxgrace/4);

  draw_stat_bar(pl,100,
		bar,pl->contr->scrollsize_grace,
		is_alert,pl->contr->scrollgrace_alert);
       pl->contr->scrollsize_grace=bar;
  pl->contr->scrollgrace_alert=is_alert;
  
  /* draw food bar */
  bar=(pl->stats.food*MAX_BARS_MESSAGE)/999;
  if(bar<0) 
    bar=0;
  is_alert=(pl->stats.food <= 999/4);
  draw_stat_bar(pl,140,
		bar,pl->contr->scrollsize_food,
		is_alert,pl->contr->scrollfood_alert);
       pl->contr->scrollsize_food=bar;
  pl->contr->scrollfood_alert=is_alert;
  if (!pl->contr->show_buttons) {
    if (pl->contr->draw_fire_on != pl->contr->fire_on) {
      if (pl->contr->fire_on)
	XDrawImageString(pl->contr->gdisp, pl->contr->win_message,
	    pl->contr->gc_look_text, 200, 40, "Fire On", 7);
      else
	XClearArea(pl->contr->gdisp, pl->contr->win_message,
		   200, 20, 60, 20, False);
      pl->contr->draw_fire_on = pl->contr->fire_on;
    }
    if (pl->contr->draw_run_on != pl->contr->run_on) {
     if (pl->contr->run_on)
	XDrawImageString(pl->contr->gdisp, pl->contr->win_message,
	    pl->contr->gc_look_text, 200, 70, "Run On", 6);
      else
	XClearArea(pl->contr->gdisp, pl->contr->win_message,
		   200, 50, 60, 20, False);
      pl->contr->draw_run_on = pl->contr->run_on;
    }
  }
}

void xwritedown(object *pl,char *txt,int x) {
  int y=13;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"xwritedown called in eric_server mode.\n");
    return;
  }
 
  for(;*txt!='\0';txt++,y+=13)
    XDrawImageString(pl->contr->gdisp,pl->contr->win_message,
                     pl->contr->gc_look_text,x,y,txt,1);
}

void draw_all_message(object *pl) {

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"draw_all_message called in eric_server mode");
    return;
  }

  XClearWindow(pl->contr->gdisp,pl->contr->win_message);
  pl->contr->scrollsize_hp=pl->contr->scrollsize_sp=
  pl->contr->scrollsize_food=pl->contr->scrollsize_grace=0;
  xwritedown(pl,"HP",06);
  XDrawRectangle(pl->contr->gdisp,pl->contr->win_message,
                 pl->contr->gc_look_text,18,2,14,MAX_BARS_MESSAGE+4);
  xwritedown(pl,"Mana",46);
  XDrawRectangle(pl->contr->gdisp,pl->contr->win_message,
                 pl->contr->gc_look_text,58,2,14,MAX_BARS_MESSAGE+4);
  xwritedown(pl,"Grace",86);
  XDrawRectangle(pl->contr->gdisp,pl->contr->win_message,
                 pl->contr->gc_look_text,98,2,14,MAX_BARS_MESSAGE+4);
  xwritedown(pl,"Food",126);
  XDrawRectangle(pl->contr->gdisp,pl->contr->win_message,
                 pl->contr->gc_look_text,138,2,14,MAX_BARS_MESSAGE+4);
/*#ifdef USE_BUTTONS*/
  if (pl->contr->show_buttons) {
    int i;
    for (i=0;i<NUMDIRBUTTS;i++) {
      XDrawRectangle(pl->contr->gdisp,pl->contr->win_message,
                     pl->contr->gc_look_text,dirX[i],dirY[i],
                     dirW,18);
      XDrawImageString(pl->contr->gdisp,pl->contr->win_message,
                       pl->contr->gc_look_text,dirX[i]+2,dirY[i]+14,
                       dirnames[i],2);
    }
    for (i=0;i<NUMOPBUTTS;i++) {
      XDrawImageString(pl->contr->gdisp,pl->contr->win_message,
                       pl->contr->gc_look_text,opX[i]+2,opY[i]+14,
                       opnames[i],8);
      XDrawRectangle(pl->contr->gdisp,pl->contr->win_message,
                     pl->contr->gc_look_text,opX[i],opY[i],
                     48,18);
    }
  } 
/*#endif*/
  draw_message_window(pl);
}


/****************************************************************************
 * Following group is a set of functions that deal with display information
 * to the player/screen.
 *
 * Cleaned up so that only a few functions are actually needed - just
 * pass different values to it.
 *
 ****************************************************************************/
static void print_message(int colr, object *pl,const char *tmp);
static void draw_info(object *pl,const char *str);

/* Following prints out the contents of one of the buffer structures,
 * and clears the string.
 */

void flush_output_element(object *pl, Output_Buf *outputs)
{
    char tbuf[MAX_BUF];

    if (outputs->buf==NULL) return;
    sprintf(tbuf,"%d times %s", outputs->count, outputs->buf);
    print_message(NDI_BLACK, pl, tbuf);
    free_string(outputs->buf);
    outputs->buf=NULL;
    outputs->first_update=0;	/* This way, it will be reused */
}

/* Following checks the various buffers in the player structure and
 * other things, and stores/prints/whatever's the data, as appropriate.
 */

void check_output_buffers(object *pl, char *buf)
{
    int i, oldest=0;

    if (pl->contr->outputs_count<2) {
	print_message(NDI_BLACK, pl, buf);
	return;
    }
    else {
	for (i=0; i<NUM_OUTPUT_BUFS; i++) {
	    if (pl->contr->outputs[i].buf && 
		!strcmp(buf, pl->contr->outputs[i].buf)) break;
	    else if (pl->contr->outputs[i].first_update <
		pl->contr->outputs[oldest].first_update)
			oldest=i;
	}
	/* We found a match */
	if (i<NUM_OUTPUT_BUFS) {
	    pl->contr->outputs[i].count++;
	    if (pl->contr->outputs[i].count>=pl->contr->outputs_count) {
		flush_output_element(pl, &pl->contr->outputs[i]);
	    }
	}
	/* No match - flush the oldest, and put the new one in */
	else {
	    flush_output_element(pl, &pl->contr->outputs[oldest]);

	    pl->contr->outputs[oldest].first_update = pticks;
	    pl->contr->outputs[oldest].count = 1;
	    if (pl->contr->outputs[oldest].buf!=NULL)
		free_string(pl->contr->outputs[oldest].buf);
	    pl->contr->outputs[oldest].buf = add_string(buf);
	}
    }
}
	    

/*
 * new_draw_info:
 *
 * flags is various flags - mostly color, plus a few specials.
 *
 * pri is priority.  It is a little odd - the lower the value, the more
 * important it is.  Thus, 0 gets sent no matter what.  Otherwise, the
 * value must be less than the listening level that the player has set.
 * Unfortunately, there is no clear guideline on what each level does what.
 *
 * pl can be passed as NULL - in fact, this will be done if NDI_ALL is set
 * in the flags.
 *
 */


void new_draw_info(int flags,int pri, object *pl, const char *buf)
{

    if (flags & NDI_ALL) {
	player	*tmppl;

	for (tmppl=first_player; tmppl!=NULL; tmppl=tmppl->next)
		new_draw_info((flags & ~NDI_ALL), pri, tmppl->ob, buf);

	if (pri<9) info_all_sockets((char*)buf);

	return;
    }
    if(!pl || (pl->type==PLAYER && pl->contr==NULL)) {
	/* Write to the socket? */
	print_message(0, NULL, buf);
	return;
    }
    if (pl->type!=PLAYER) return;
    if (pri>=pl->contr->listening) return;

    if ((flags&NDI_COLOR_MASK)==NDI_BLACK && !(flags &NDI_UNIQUE)) {
	/* following prints stuff out, as appropriate */
	check_output_buffers(pl, (char*)buf);
    }
    else {
	print_message(flags&NDI_COLOR_MASK, pl, buf);
    }
}

/* This is a pretty trivial function, but it allows us to use printf style
 * formatting, so instead of the calling function having to do it, we do
 * it here.  IT may also have advantages in the future for reduction of
 * client/server bandwidth (client could keep track of various strings
 */

void new_draw_info_format(int flags, int pri,object *pl, char *format, ...)
{
    char buf[HUGE_BUF];

    va_list ap;
    va_start(ap, format);

    vsprintf(buf, format, ap);

    va_end(ap);

    new_draw_info(flags, pri, pl, buf);
}

void new_info_map(int color, mapstruct *map, char *str) {
    player *pl;

    for(pl = first_player; pl != NULL; pl = pl->next)
	if(pl->ob != NULL && pl->ob->map == map) {
	    new_draw_info(color, 0, pl->ob, str);
	}
}

/****************************************************************************
print_message : This routine prints out the character string in tmp on the
                info window. If in color mode, then the text will show up in
                a color specified by the variable colr. If Black and white
                mode, then it prints a line of "="s before and after the
                messsage.

		This has been changed around - this is now a front end to
		draw_info.  draw_info should never be called directly, except
		by this functin.  Likewise, this function should only be
		called by a few functions above (new_draw_info, 
		check_output_buffers)
****************************************************************************/

static void print_message(int colr, object *pl,const char *tmp) {

  if(!pl || (pl->type==PLAYER && pl->contr==NULL)) {
#ifdef SERVER
    if(active_socket != (sockets *) NULL)
      draw_socket(active_socket->fd,tmp);
    else
#endif
      fprintf(logfile,"%s\n",tmp);
    return;
  }
  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    esrv_print_msg(pl->contr->eric_server,colr,(char*) tmp);
    return;
  }
  if(tmp == (char *) NULL) {
    tmp="[NULL]";
  }

  if (colr!=NDI_BLACK) {
    if (pl->contr->iscolor) {
	XSetForeground(pl->contr->gdisp,pl->contr->gc_info,
	    pl->contr->discolor[colr].pixel);
	draw_info(pl,tmp);
	XSetForeground(pl->contr->gdisp,pl->contr->gc_info,
	    pl->contr->discolor[0].pixel);
    }
    else {
	draw_info(pl,"==========================================");
	draw_info(pl,tmp);
	draw_info(pl,"==========================================");
    }
  }
  else
    draw_info(pl, tmp);
}

/* This entire function won't be needed once true client/server happens. */


static void draw_info(object *pl,const char *str) {
  static char blanks[]="                                                      ";
  char *cp;

  /* Following should be removed - all message handling to client should be
   * done in print_message, above
   */
  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"In draw_info, new client/server function will be called\n");
    esrv_print_msg(pl->contr->eric_server, NDI_BLACK, str);
    return;
  }

  if((cp=strchr(str,'\n'))!=NULL) {
    char *obuf = (char *) malloc(sizeof(char) * (strlen(str) + 2)), *buf = obuf;

    strcpy(buf,str);
    do {
      if ((cp = strchr(buf, '\n'))) {
        *cp='\0';
        draw_info(pl,buf);
        buf = cp +1;
      } else
        draw_info(pl, buf);
    } while (cp!=NULL);
    free(obuf);
    return;
  }
  /* Lets do the word wrap for messages - MSW (master@rahul.net) */
  if ((int)strlen(str) >= (int)pl->contr->infochars) {
	int i=pl->contr->infochars-1;

	/* i=last space (or ')' for armor.  Wrap armor, because
	otherwise, the two sets of ()() can be about half the line */
	while ((str[--i]!=' ') && (str[i]!=')') && (i!=0)) ;
	/* if i==0, string has no space.  Just let it be truncated */
	if (i!=0) {
	    char *buf = (char *)malloc(sizeof(char)*(i+2));
	    int j;

	    i++;	/* want to keep the ')'.  This also keeps
			the space, but that really doesn't matter */
	    strncpy(buf, str, i);
	    buf[i]='\0';
	    draw_info(pl, buf);
            free(buf);

	    for (j=i; j < (int)strlen(str); j++) /* if the wrap portion is */
		if (str[j]!=' ') break;		/* only space, don't wrap it*/
	    if ((((strlen(str)-i)!=1) || (str[i]!='.')) && (j!=strlen(str)))
		draw_info(pl, (str+i));
	    return;
	}
  }
  strncpy(pl->contr->info[pl->contr->infopos],str,pl->contr->infochars);
  pl->contr->info[pl->contr->infopos][pl->contr->infochars] = '\0';
  XDrawImageString(pl->contr->gdisp,pl->contr->win_info,
    pl->contr->gc_info,FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT,
    pl->contr->info[pl->contr->infopos],
    strlen(pl->contr->info[pl->contr->infopos]));
  pl->contr->infopos = (pl->contr->infopos+1)% pl->contr->infolines ;
  if(++(pl->contr->infoline)>=pl->contr->infolines){
    if (pl->contr->scroll) {
      XCopyArea(pl->contr->gdisp,pl->contr->win_info,pl->contr->win_info,
	        pl->contr->gc_info,0,FONTHEIGHT,pl->contr->infochars*FONTWIDTH,
                pl->contr->infolines*FONTHEIGHT,0,0);
      pl->contr->infoline--;
    }
    else
      pl->contr->infoline=0;
    pl->contr->infofull=1;
  }
  strncpy(pl->contr->info[pl->contr->infopos],blanks,pl->contr->infochars);
  pl->contr->info[pl->contr->infopos][pl->contr->infochars] = '\0';
  XDrawImageString(pl->contr->gdisp,pl->contr->win_info,
        pl->contr->gc_info,FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT,blanks,strlen(blanks));
  if(pl->contr->writing) {
    strncpy(pl->contr->info[pl->contr->infopos],
            pl->contr->write_buf,pl->contr->infochars);
    pl->contr->info[pl->contr->infopos][pl->contr->infochars] = '\0';
     
    XDrawImageString(pl->contr->gdisp,pl->contr->win_info,pl->contr->gc_info,
                     FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT,
                     pl->contr->write_buf,pl->contr->writing);
  }
  if ((int)strlen(str) > (int)pl->contr->infochars)
    draw_info(pl,str+pl->contr->infochars);
}


/****************************************************************************
 *
 * Following set of functions are used when entering commands, passwords,
 * or any other thing that takes several characters.
 *
 * This will not be needed for new client/server mode
 *
 ****************************************************************************/
void delete_ch(object *pl) {
    if(pl->type == PLAYER && pl->contr->eric_server > 0) {
	LOG(llevError,"delete_ch called in ericserver mode.\n");
	return;
    }

     /* Can't delete prompts for passwords and so on */
    if(pl->contr->no_echo&&pl->contr->writing==1)
	return;
    pl->contr->write_buf[--pl->contr->writing]='\0';

    /* Start of the line - we need to do some extra stuff here unless
     * we are erasing the original prompt. 
     */
    if (pl->contr->infox<3 && pl->contr->writing>1) {

	int line=pl->contr->infopos-1;
	if (line<0) line=pl->contr->infolines-1;
	strcpy(pl->contr->info[pl->contr->infopos], pl->contr->info[line]);
	pl->contr->infox = pl->contr->infochars-2;
	pl->contr->info[pl->contr->infopos][--pl->contr->infox]=0;
	XDrawImageString(pl->contr->gdisp,pl->contr->win_info,pl->contr->gc_info,
		FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT,
		pl->contr->info[pl->contr->infopos],pl->contr->infox);
    }
    else {
	pl->contr->info[pl->contr->infopos][--pl->contr->infox]='\0';
 
	XDrawImageString(pl->contr->gdisp,pl->contr->win_info,pl->contr->gc_info,
		(pl->contr->infox+1)*FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT," ",1);
    }
}

/* Writes a character to the screen, and also puts it into the players
 * input buffer.  This function is only called when we want a character
 * to go into the players input buffer.
 * Reworked for 0.93.5 to handle longer lines.
 */
void write_ch(object *pl,unsigned char key)
{
    char *c;

    if (pl->type != PLAYER || (pl->type == PLAYER && pl->contr->eric_server > 0)) {
	LOG(llevError,"write_ch called in eric_server or non player object mode.\n");
	return;
    }
    if ((key < 32 || key > 127) && key != 8)
	return;

    /* Need to initialize infox - if this is the first char, set infox */
    if (!pl->contr->writing) pl->contr->infox=0;

    /* Now use the write_buf size - we give a bit for margin of error, since at current
     * time, it is a general purpose buffer, and we want to make sure you can't bind stuff
     * longer than could be read in.
     */
    if(pl->contr->writing>=(MAX_BUF-15))
	return;

    if(key==8||key==127) {
	delete_ch(pl->contr->ob);
	return;
    }
    c= &pl->contr->write_buf[pl->contr->writing];
    pl->contr->write_buf[pl->contr->writing]=key;
    if(pl->contr->infox>=(pl->contr->infochars-2)) {
	int writing=pl->contr->writing;

	/* Draw the currently line and scroll down one */
	pl->contr->writing=0;
	new_draw_info(NDI_UNIQUE, 0, pl, pl->contr->info[pl->contr->infopos]);
	pl->contr->writing=writing;
	pl->contr->infox=2;
	pl->contr->info[pl->contr->infopos][0]=(pl->contr->writing/pl->contr->infochars)+49;
	pl->contr->info[pl->contr->infopos][1]='>';
	XDrawImageString(pl->contr->gdisp,pl->contr->win_info,pl->contr->gc_info,
			 FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT,
			 pl->contr->info[pl->contr->infopos],2);
    }

    pl->contr->write_buf[pl->contr->writing]=key;

    pl->contr->info[pl->contr->infopos][pl->contr->infox] =pl->contr->no_echo? '?': key;
    pl->contr->writing++;
    pl->contr->infox++;

    pl->contr->write_buf[pl->contr->writing]='\0';
    pl->contr->info[pl->contr->infopos][pl->contr->infox]='\0';
    if(pl->contr->no_echo)
	XDrawImageString(pl->contr->gdisp,pl->contr->win_info,pl->contr->gc_info,
			 pl->contr->infox*FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT,"?",1);
    else
	XDrawImageString(pl->contr->gdisp,pl->contr->win_info,pl->contr->gc_info,
			 pl->contr->infox*FONTWIDTH,(pl->contr->infoline+1)*FONTHEIGHT,c,1);
}

void refresh_win_info(object *op) {
  draw_all_info(op);
}

void clear_win_info(object *op) {
  int i;

	/* don't clear if in scroll mode MSW (master@rahul.net) */
  if((op->type==PLAYER)  &&  (op->contr->scroll)) return;

  if (op->type == PLAYER && op->contr->eric_server > 0) {
    /* In theory, sending a note on this to the client might not be a bad
     * thing (client could choose to ignore it if desired.
     */
    LOG(llevDebug,"clear_win_info called in eric_server mode.\n");
/*    esrv_foo(op->contr->eric_server,"clear_win_info");*/
    return;
  }

  for(i=0;i<op->contr->infolines;i++) {
    (void) memset((void *)op->contr->info[i],' ',op->contr->infochars);
    op->contr->info[i][op->contr->infochars]='\0';
  }
  XClearWindow(op->contr->gdisp,op->contr->win_info);
/*refresh_win_info(op); */
  op->contr->infoline=0;
  op->contr->infopos=0;
  op->contr->infofull=0;
}

void refresh(object *pl) {
  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"refresh called in eric_server mode.\n");
    return;
  }
  XClearWindow(pl->contr->gdisp,pl->contr->win_game);
  if(pl->contr->viewmap) {
    draw_map(pl);
    return;
  }
  refresh_map(pl->map);
  (void)memset((void *)pl->contr->drawn,'\0',
               sizeof(New_Face*)*(WINRIGHT-WINLEFT+1)*(WINLOWER-WINUPPER+1));
  draw(pl);
}

void draw_dots(object *op) {
  player *pl;

  if (op->type == PLAYER && op->contr->eric_server > 0) {
    LOG(llevError,"draw_dots called in eric_server mode.\n");
    return;
  }
  op->contr->viewmap^=2;
  if(op->contr->viewmap&2) {
    int i=0;
    XSetForeground(op->contr->gdisp,op->contr->gc_game,op->contr->gforeground);
    XSetBackground(op->contr->gdisp,op->contr->gc_game,op->contr->gbackground);
    for(pl=first_player;pl!=NULL;pl=pl->next) {
      if(pl->ob->map!=op->map)
        break;
      XFillRectangle(op->contr->gdisp,op->contr->win_game,op->contr->gc_game,
                     2+op->contr->mapres*(pl->ob->x - op->contr->mapxmin),
                     2+op->contr->mapres*(pl->ob->y - op->contr->mapymin),
                     op->contr->mapres,op->contr->mapres);
      op->contr->mapdelx[i]=pl->ob->x - op->contr->mapxmin;
      op->contr->mapdely[i]=pl->ob->y - op->contr->mapymin;
      i++;
    }
    op->contr->mapdelx[i]= -1;
  } else {
    int i;
    XSetForeground(op->contr->gdisp,op->contr->gc_game,op->contr->gbackground);
    XSetBackground(op->contr->gdisp,op->contr->gc_game,op->contr->gforeground);
    for(i=0;op->contr->mapdelx[i]!=-1;i++)
      XFillRectangle(op->contr->gdisp,op->contr->win_game,op->contr->gc_game,
                     2+op->contr->mapres*op->contr->mapdelx[i],
                     2+op->contr->mapres*op->contr->mapdely[i],
                     op->contr->mapres,op->contr->mapres);
    XSetForeground(op->contr->gdisp,op->contr->gc_game,op->contr->gforeground);
    XSetBackground(op->contr->gdisp,op->contr->gc_game,op->contr->gbackground);
  }
}
    

    

void magic_mapping_mark(object *pl, char *map_mark, int strength);
void magic_mapping_mark_recursive(object *pl, char *map_mark, int px, int py);

/* The following function is a lot messier than it really should be,
 * but there is no real easy solution.
 *
 * One of the main causes is uses the crossfire font to draw the stipple
 * pattern.  This then means that the excess needs to be erased.  As things
 * stand now, the excess is erased, and things look ok.
 *
 * Also, display on black and white system is still not as good (useful)
 * as on a color system.  However, things are not too bad.  At present, there
 * are 4 possible outputs:  White, meaning a wall, black, meaning
 * nothing (or only floor), grey (stippled pattern), for any other objects
 * that do not have a black foreground, and another stippled patern for
 * objects that do have a black foreground.
 *
 * Display of the stipples is not perfect.  One of the stipples is just
 * a checkerboard pattern.  IF the resolution is odd, and two of these
 * are placed together, little imperfections in the matching shows up.
 * However, it doesn't affect the usefulness of the display much, and
 * I don't want to add more code to deal with making the stipple perfect.
 * This is because the second stipple pattern used has a different
 * repeat rate
 *
 * Mark Wedel (master@rahul.net)
 */

void draw_map(object *pl) 
{
  int res,x,y;
  char *map_mark = (char *) malloc(pl->map->mapx * pl->map->mapy);
  int xmin = pl->map->mapx, xmax = 0, ymin = pl->map->mapy, ymax = 0;
  XWindowAttributes win_info;
  SockList sl;

  if (pl->type!=PLAYER) {
    LOG(llevError,"Non player objectg called draw_map.\n");
    return;
  }
  memset(map_mark, 0, pl->map->mapx * pl->map->mapy);
#if 0 /* Hmm, why did this fail completely?  I want to see all of it! */
  if (WIZPASS(pl)) /* Toggle WIZPASS with 'W' in DM-mode */
  {
    (void) memset((void *)map_mark, 1, pl->map->mapx * pl->map->mapy);
    xmin = 0;
    xmax = pl->map->mapx - 1;
    ymin = 0;
    ymax = pl->map->mapy - 1;
  }
  else
#endif
  {
    magic_mapping_mark(pl, map_mark, 3);
    for(x = 0; x < pl->map->mapx; x++) {
      for(y = 0; y < pl->map->mapy; y++) {
        if (map_mark[x + pl->map->mapx * y]==1) {
	  xmin = x < xmin ? x : xmin;
	  xmax = x > xmax ? x : xmax;
	  ymin = y < ymin ? y : ymin;
	  ymax = y > ymax ? y : ymax;
        }
      }
    }
    xmin--;
    xmin = xmin < 0 ? 0 : xmin;
    xmax++;
    xmax = xmax > pl->map->mapx - 1 ? pl->map->mapx - 1: xmax;
    ymin--;
    ymin = ymin < 0 ? 0 : ymin;
    ymax++;
    ymax = ymax > pl->map->mapy - 1? pl->map->mapy - 1: ymax;
  }

  /* New client/server uses different method */
  if (pl->contr->eric_server > 0) {
    sl.buf=malloc(MAXSOCKBUF);
    sprintf((char*)sl.buf,"magicmap %d %d %d %d ", (xmax-xmin+1), (ymax-ymin+1),
	    pl->x - xmin, pl->y - ymin);
    sl.len=strlen((char*)sl.buf);
  } else {
    pl->contr->viewmap=1;

    /* If the width and height of the game window is stored someplace,
     * that should be used instead.  I just didn't see this information
     * anywhere.  We could just use the standard width/height
     * values, but if the window has been resized larger, things wouldn't
     * look right. - Mark Wedel
     */
    XGetWindowAttributes(pl->contr->gdisp, pl->contr->win_game, &win_info);

    /* If we have color, set for a grey background. From what I have seen,
     * grey isn't used much, so with the background grey, it should be easier
     * to tell what details magic mapping added - Mark Wedel 
     * (master@rahul.net)
     */
    if (pl->contr->iscolor) {
	XSetForeground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->discolor[9].pixel);
	XSetBackground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gbackground);
	XFillRectangle(pl->contr->gdisp, pl->contr->win_game, pl->contr->gc_game,
	    0, 0, win_info.width, win_info.height);
    }
    else {
	  XSetBackground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gforeground);
	  XSetForeground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gbackground);
	  XClearWindow(pl->contr->gdisp,pl->contr->win_game);
    }

    res=(WINRIGHT-WINLEFT+1)*24/
	(xmax - xmin + 1 >  ymax - ymin + 1 ? xmax - xmin + 1 : ymax - ymin + 1); 

    /* The bitmap used for objects is only 24x24.  Too keep things simple,
     * so that we don't need to draw it several times, keep the maximum
     * resolution 24 pixels.  In general, only if magic mapping is mapping
     * a very small area will the resolution be greater than 24.
     * master@rahul.net
     */
    if (!pl->contr->iscolor && res>24) res=24;
    pl->contr->mapres=res;
    pl->contr->mapxmin = xmin;
    pl->contr->mapymin = ymin;
  } /* else non new client/server */
 
  /* Reversed ordering of X and Y in 0.93.2.  This way the order should
   * match up the way we said it would
   */
  for (y = ymin; y <= ymax; y++) {
    for (x = xmin; x <= xmax; x++) {
	if (pl->contr->eric_server>0) {
	    int mark;
	    if ((mark=map_mark[x+pl->map->mapx*y])==0)
		sl.buf[sl.len++]=0;
	    else {
		New_Face *f = get_map(pl->map, x, y)->face;
		if (mark==2)
		    sl.buf[sl.len++]=f->magicmap | FACE_WALL;
		else
		    sl.buf[sl.len++]=f->magicmap;
	    }
	    continue;
	}
	if (map_mark[x + pl->map->mapx * y]) { /* Saw into this space */

	    New_Face *f = get_map(pl->map, x, y)->face;
	    if (pl->contr->iscolor) {

		XSetForeground(pl->contr->gdisp,pl->contr->gc_game,
		    pl->contr->discolor[f->magicmap&FACE_COLOR_MASK].pixel);
		XFillRectangle(pl->contr->gdisp,pl->contr->win_game,
                       pl->contr->gc_game, 2 + res*(x - xmin), 
		       2 + res*(y - ymin), res, res);
	    } else { /* if on black/white system */
		/* we don't draw blank and floors.  If we are using the font,
		 * we do need to erase potential left over areas.
		 */
		if (f->magicmap & FACE_FLOOR) {
		    if (!pl->contr->use_pixmaps) {
			XSetForeground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gforeground);
			XFillRectangle(pl->contr->gdisp,pl->contr->win_game,
				       pl->contr->gc_game, 2 + res*(x - xmin), 
				       2 + res*(y - ymin), res, res);
			XSetForeground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gbackground);
		    }
		    continue;
		}
		/* Have a wall/blocked space.  It seems unclear what
		 * color we are drawing on this space.
		 */
		if (map_mark[x + pl->map->mapx * y]==2) {
		    XFillRectangle(pl->contr->gdisp,pl->contr->win_game,
			pl->contr->gc_game, 2 + res*(x - xmin), 
			2 + res*(y - ymin), res, res);
		} else { /* interesting object */
		    Fontindex use;
		    
		    if (f->fg==0)
			use = stipple2_face->number;
		    else
			use = stipple1_face->number;
		    if (pl->contr->use_pixmaps) {
			XCopyPlane(pl->contr->gdisp, pl->contr->pixmaps[use],
			       pl->contr->win_game, pl->contr->gc_game,
			       0, 0, res,res, 2 + res*(x-xmin), 2+res*(y-ymin),
			       1);
		    } else {
			XChar buf= FontindexToXChar((Fontindex) use);

			XDRAWIMAGESTRING(pl->contr->gdisp,pl->contr->win_game,
				     pl->contr->gc_game,2+res*(x-xmin),06+res*(y-ymin),&buf,1);
		    }
		} /* interesting object */
	    } /* black/white system */
	} /* Saw into this space */
	else if (!pl->contr->iscolor && !pl->contr->use_pixmaps) {
	    /* There is no easy way to draw only a portion of a font
	     * character (at least that I saw.)  So, if we used the stipple
	     * font to draw a character portion, we then need to erase the
	     * extra portions that were drawn, and this code does that as
	     * it encounters them.
	     */

	    XSetForeground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gforeground);
	    XFillRectangle(pl->contr->gdisp,pl->contr->win_game,
		pl->contr->gc_game, 2 + res*(x - xmin), 
		2 + res*(y - ymin), res, res);
	    XSetForeground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gbackground);
	} /* did not see into this space */
    } /* x loop */
  } /* y loop */
  if (pl->contr->eric_server > 0) {
    Send_With_Handling(getcnum(pl->contr->eric_server), &sl);
    free(sl.buf);
  } else {
    XSetForeground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gforeground);
    XSetBackground(pl->contr->gdisp,pl->contr->gc_game,pl->contr->gbackground);

    /* If black/white, and uses the font, erase the edges of the
     * window.  This is to remove any excess pattern that the font
     * might leave - master@rahul.net
     */
    if (!pl->contr->iscolor && !pl->contr->use_pixmaps) {
	XFillRectangle(pl->contr->gdisp, pl->contr->win_game, pl->contr->gc_game,
	    (xmax+1-xmin)*res+2, 0, win_info.width - 2 - res*(xmax+1-xmin),
	     win_info.height);
	XFillRectangle(pl->contr->gdisp, pl->contr->win_game, pl->contr->gc_game,
	    0, 2+res*(ymax+1-ymin), win_info.width, win_info.height-2 -
	    res*(ymax+1-ymin));
    }
  } /* non eric server mode */
  free(map_mark);
}

/* Note:  For improved magic mapping display, the space that blocks
 * the view is now marked with value 2.  Any dependencies of map_mark
 * being nonzero have been changed to check for 1.  Also, since
 * map_mark is a char value, putting 2 in should cause no problems.
 * Mark Wedel (master@rahul.net)
 * This function examines the map the player is on, and determines what
 * is visible.  2 is set for walls or objects that blocks view.  1
 * is for open spaces.  map_mark should already have been initialized
 * to zero before this is called.
 * strength is an initial strength*2 rectangular area that we automatically
 * see in/penetrate through.
 */

void magic_mapping_mark(object *pl, char *map_mark, int strength)
{
  int x, y;
  int xmin = pl->x - strength + 1 < 0 ? 0 : pl->x - strength + 1;
  int xmax = pl->x + strength - 1 > pl->map->mapx - 1 ? 
    pl->map->mapx - 1 : pl->x + strength - 1;
  int ymin = pl->y - strength + 1 < 0 ? 0 : pl->y - strength + 1;
  int ymax = pl->y + strength - 1 > pl->map->mapy - 1 ? 
    pl->map->mapy - 1 : pl->y + strength - 1;

  for (x = xmin; x <= xmax; x++) {
    for (y = ymin; y <= ymax; y++) {
      if (wall(pl->map, x, y) || blocks_view(pl->map, x, y))
	map_mark[x + pl->map->mapx * y] = 2;
      else {
	map_mark[x + pl->map->mapx * y] = 1;
	magic_mapping_mark_recursive(pl, map_mark, x, y);
      }
    }
  }
}

/* Takes a player, the map_mark array and an x and y starting position.
 * pl could be replaced by the map, since that is all taht pl is
 * used for.
 * This function examines all the adjacant spaces next to px, py.
 * If there is a wall or it otherwise blocks view on a space, we set
 * map_mark to 2.
 * If the space is otherwise open, we set it to 1, and this function
 * is called again to examine those spaces.
 */
void magic_mapping_mark_recursive(object *pl, char *map_mark, int px, int py)
{
  int x, y, dx, dy;

  for (dx = -1; dx <= 1; dx++) {
    for (dy = -1; dy <= 1; dy++) {
      x = px + dx;
      y = py + dy;
      if (x >= 0 && x < pl->map->mapx && y >= 0 && y < pl->map->mapy
	&& (map_mark[x + pl->map->mapx * y] ==0) ) {
            if (blocks_view(pl->map, x, y))
		map_mark[x + pl->map->mapx * y] = 2;
	    else {
		if (wall(pl->map, x, y))
		    map_mark[x + pl->map->mapx * y] = 2;
		else
		    map_mark[x + pl->map->mapx * y] = 1;
		magic_mapping_mark_recursive(pl, map_mark, x, y);
	    }
	}
    }
  }
}



void draw(object *pl) {
  int i,j,last_f= -1,last_b= -1,ax,ay; /* ax and ay goes from 0 to max-size of arrays */
  XChar buf;
  New_Face *f;

  if(pl->map == NULL || pl->map->in_memory != MAP_IN_MEMORY)
    return;

   if(pl->contr->viewmap) {
    draw_dots(pl);
    return;
  }
  if(pl->contr->do_los) {
    update_los(pl);
#ifdef USE_LIGHTING
    pl->map->do_los = 0;
#endif
    pl->contr->do_los = 0;
  }

  if (pl->contr->eric_server>0) {
	draw_client_map(pl);
	return;
  }
#ifdef Xpm_Pix
  if (pl->contr->color_pixmaps) {
      draw_color_pix(pl);
      return;
  }
#endif
  if(pl->contr->use_pixmaps) {
    draw_pix(pl);
    return;
  }
  if(!pl->contr->iscolor) {
    draw_bw(pl); /* This function optimizes better */
    return;
  }

  /* The following looks like it only applies to bw systems using
   * the font.
   */
  buf= FontindexToXChar((Fontindex) 0);
  for(j=pl->y+WINUPPER;j<=pl->y+WINLOWER;j++) {
    ay=j-pl->y-WINUPPER;
    for(i=pl->x+WINLEFT;i<=pl->x+WINRIGHT;i++) {
      ax=i-pl->x-WINLEFT;
      f = (out_of_map(pl->map,i,j)||
	   pl->contr->blocked_los[i+5-pl->x][j+5-pl->y])?
	       blocked_face : get_map(pl->map,i,j)->face;
      if(f!= pl->contr->drawn[ax][ay]) {
        buf=FontindexToXChar(f->number);
        pl->contr->drawn[ax][ay]=f;
        if(pl->contr->discolor[f->fg].pixel!=last_f)
          XSetForeground(pl->contr->gdisp,pl->contr->gc_game,
                         (last_f=pl->contr->discolor[f->fg].pixel));
        if(pl->contr->discolor[f->bg].pixel!=last_b)
          XSetBackground(pl->contr->gdisp,pl->contr->gc_game,
                           (last_b=pl->contr->discolor[f->bg].pixel));
        XDRAWIMAGESTRING(pl->contr->gdisp,pl->contr->win_game,
                         pl->contr->gc_game,2+24*ax,ay*24+26,&buf,1);
      }
    }
  }
  if(pl->invisible & (pl->invisible<50 ? 4 : 1)) {
    pl->contr->drawn[5][5]=pl->face;
    buf=FontindexToXChar(pl->face->number);
    XSetForeground(pl->contr->gdisp,pl->contr->gc_game,
      pl->contr->discolor[pl->face->fg].pixel);
    XSetBackground(pl->contr->gdisp,pl->contr->gc_game,
      pl->contr->discolor[pl->face->bg].pixel);
    XDRAWIMAGESTRING(pl->contr->gdisp,pl->contr->win_game,
                     pl->contr->gc_game,122,146,&buf,1);
    }
}

void draw_pix(object *pl) {
  int i,j,last_f= -1,last_b= -1,ax,ay; /* ax and ay goes from 0 to max-size of arrays */
  New_Face *f;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"draw_pix called in eric_server mode.\n");
    return;
  }
  if(pl->contr->viewmap) {
    draw_dots(pl);
    return;
  }
  for(j=pl->y+WINUPPER;j<=pl->y+WINLOWER;j++) {
    ay=j-pl->y-WINUPPER;
    for(i=pl->x+WINLEFT;i<=pl->x+WINRIGHT;i++) {
      ax=i-pl->x-WINLEFT;
      f=(out_of_map(pl->map,i,j)||
	 pl->contr->blocked_los[i+5-pl->x][j+5-pl->y]>3)?
	   blocked_face : get_map(pl->map,i,j)->face;
      if(f!=pl->contr->drawn[ax][ay]) {
        pl->contr->drawn[ax][ay]=f;
        if(pl->contr->iscolor) {
          if(pl->contr->discolor[f->fg].pixel!=last_f)
            XSetForeground(pl->contr->gdisp,pl->contr->gc_game,
                           (last_f=pl->contr->discolor[f->fg].pixel));
          if(pl->contr->discolor[f->bg].pixel!=last_b)
            XSetBackground(pl->contr->gdisp,pl->contr->gc_game,
                           (last_b=pl->contr->discolor[f->bg].pixel));
        }
        XCopyPlane(pl->contr->gdisp,pl->contr->pixmaps[f->number],
                   pl->contr->win_game,pl->contr->gc_game,
                   0,0,24,24,2+24*ax,2+24*ay,1);
      }
    }
  }
  if(pl->invisible & (pl->invisible < 50 ? 4 : 1)) {
    pl->contr->drawn[5][5] = pl->face;
    if(pl->contr->iscolor) {
      XSetForeground(pl->contr->gdisp,pl->contr->gc_game,
        pl->contr->discolor[pl->face->fg].pixel);
      XSetBackground(pl->contr->gdisp,pl->contr->gc_game,
        pl->contr->discolor[pl->face->bg].pixel);
    }
    XCopyPlane(pl->contr->gdisp,pl->contr->pixmaps[pl->face->number],
               pl->contr->win_game,pl->contr->gc_game,0,0,24,24,122,122,1);
  }
}

void draw_bw(object *pl) {
  int i,j,left,right,ax,ay; /* ax and ay goes from 0 to max-size of arrays */
  XChar buff[20];
  New_Face *tmp;

  if (pl->type == PLAYER && pl->contr->eric_server > 0) {
    LOG(llevError,"draw_bw called in eric_server mode.\n");
    return;
  }
  for(j=pl->y+WINUPPER;j<=pl->y+WINLOWER;j++) {
    left= -1,right=0,ay=j-pl->y-WINUPPER;
    for(i=pl->x+WINLEFT;i<=pl->x+WINRIGHT;i++) {
      ax=i-pl->x-WINLEFT;
      tmp = (out_of_map(pl->map,i,j)||
	     pl->contr->blocked_los[i+5-pl->x][j+5-pl->y]) ?
		 blocked_face : get_map(pl->map,i,j)->face;
      if(tmp!=pl->contr->drawn[ax][ay]) {
	  pl->contr->drawn[ax][ay]=tmp;
	  if(left== (-1)) left=ax;
	  right=ax;
      }
      buff[ax] = FontindexToXChar(tmp->number);
    }
    if(left!=-1)
      XDRAWIMAGESTRING(pl->contr->gdisp,pl->contr->win_game,
                       pl->contr->gc_game,2+24*left,ay*24+26,buff+left,
		       right-left+1);
  }
  if(pl->invisible & (pl->invisible < 50 ? 4 : 1)) {
    buff[0]=FontindexToXChar(pl->face->number);
    pl->contr->drawn[5][5] = pl->face;
    XDRAWIMAGESTRING(pl->contr->gdisp,pl->contr->win_game,
		     pl->contr->gc_game,122,122,buff,1);
  }
}

#ifdef Xpm_Pix
/* draw_color_pix is orignally from draw_pix.  I separated them for reasons
 * of speed.  The main differences are these:  Color pixmaps do not need
 * foreground and background set (it does nothing), but does need
 * to set the bitmask for objects.
 *
 * Because of shape masks, this function has become a little more complicated,
 * and probably slower.  The other option is to keep it like draw_pix and
 * the other draw functions, and only draw the top object.  However, I think
 * one of the advantages of the color pixmaps is that you do have a shape
 * mask, and it would be a shame not to use it.
 *
 * A re-draw is done only if the top object on a square is changed.
 *`As far as I know, floors can not really change unless someone moves
 * on top of them, so this should work fine.
 * An exception to this is the player, as he can move in the same direction.
 * His face remains the same, but the floor underneath him changes.
 *
 * One thought to perhaps make the function quicker and reduce flicker
 * would be to draw all the floors on the first pass, then come back and
 * draw the objects.  This should be quicker, because the floors should
 * not need any of the clipmasks.
 * The best way to reduce flicker would probably be to draw both the
 * floor and object into a new pixmap, and then draw that pixmap onto
 * the screen.  It might even be a little quicker, because the ClipOrigin
 * should only need to be set once.
 */

/* New version of draw_color_pix.  This should hopefully run faster and
 * with less jittering than the old routine.  It does cost a little more
 * memory (2 gc's and a 24x24 pixmap added to the player structure.)
 * The gc's have some values pre-set so that this routine does not need to
 * set them for each pixmap drawn.
 */
void draw_color_pix(object *pl) {
  int i,j,ax,ay,d; /* ax and ay goes from 0 to max-size of arrays */
  New_Face	*face,*floor;
#ifdef DOUBLE_FLOOR
  New_Face	*floor2;
#endif
  New_Face *darkmask;  /* the darkness level */
  static Pixmap	obj_mask = 0;

  if (pl->contr->eric_server>0) {
	 LOG(llevError,"draw_color_pix called in eric_server mode.\n");
	 return;
  }

  for(j=pl->y+WINUPPER;j<=pl->y+WINLOWER;j++) {
	 ay=j-pl->y-WINUPPER;
	 for(i=pl->x+WINLEFT;i<=pl->x+WINRIGHT;i++) {
		ax=i-pl->x-WINLEFT;
		if (out_of_map(pl->map,i,j) ||
			 pl->contr->blocked_los[i+5-pl->x][j+5-pl->y]>3) {
		  if (pl->contr->drawn[ax][ay]!=blocked_face) {

			 /* A XClearArea is probably faster than copying the pixmap.
			  * (it also works better)
			  * Also, this way we do not need to worry about any masking
			  * values.  
			  * What might be better is to detect which cooridnate (i or j)
			  * is out of bounds of the map.  Since all maps are square,
			  * then one XClearArea could be used to clear the entire
			  * row or column, and then break out.  This would
			  * skip several checks.
			  * Mark Wedel (master@rahul.net)
			  */
			 XClearArea(pl->contr->gdisp, pl->contr->win_game,
							2+24*ax, 2+24*ay, 24, 24, False);
			 pl->contr->drawn[ax][ay] = blocked_face;
			 pl->contr->darkmask[ax][ay] = 100;
		  }
		}
		else {
		  face = get_map(pl->map, i, j)->face;
		  floor = get_map_floor(pl->map, i,j)->face;
		  d=pl->contr->blocked_los[i+5-pl->x][j+5-pl->y];
#ifdef DOUBLE_FLOOR
		  floor2 = get_map_floor2(pl->map, i,j)->face;
		  if (face!=pl->contr->drawn[ax][ay] ||
				floor!=pl->contr->floor[ax][ay] ||
				d!=pl->contr->darkmask[ax][ay] || 
				floor2!=pl->contr->floor2[ax][ay]) {
#else
        if ( face!=pl->contr->drawn[ax][ay] ||
			  floor!=pl->contr->floor[ax][ay]|| 
			d!=pl->contr->darkmask[ax][ay] ) {
#endif
		/* First, copy the floor over */
		  XCopyArea(pl->contr->gdisp, pl->contr->pixmaps[floor->number],
						 pl->contr->xpm_pixmap, pl->contr->gc_xpm_floor,
						 0, 0, 24, 24, 0, 0);
#ifdef DOUBLE_FLOOR
		/* If there is a second FLOOR_TYPE item on top of the
		 * first, draw it with the right clipmask. This looks
		 * good, but may be expensive as 3 pixmaps are drawn
		 * instead of 2.
		 * Gregor Schmid (schmid@fb3-s7.math.tu-berlin.de)
		 */
		if (floor2 != blank_face) {
		  XSetClipMask(pl->contr->gdisp,
				pl->contr->gc_xpm_object,
				pl->contr->masks[floor2->number]);
		  XCopyArea(pl->contr->gdisp,
				pl->contr->pixmaps[floor2->number],
				pl->contr->xpm_pixmap,pl->contr->gc_xpm_object,
				0,0,24,24,0, 0);
		    obj_mask = pl->contr->masks[floor2->number];
		}
#endif
		if (obj_mask != pl->contr->masks[face->number]) {
		  XSetClipMask(pl->contr->gdisp, pl->contr->gc_xpm_object,
							pl->contr->masks[face->number]);
		  obj_mask = pl->contr->masks[face->number];
		}
		XCopyArea(pl->contr->gdisp,pl->contr->pixmaps[face->number],
					 pl->contr->xpm_pixmap,pl->contr->gc_xpm_object,
					 0,0,24,24,0, 0);

		/* do the darkness masking: cases > 3 handled already */
		if(d) {
		  darkmask = dark_faces[d-1];
		  XSetClipMask(pl->contr->gdisp,
					pl->contr->gc_xpm_object,
					pl->contr->masks[darkmask->number]);
		  XCopyArea(pl->contr->gdisp,pl->contr->pixmaps[darkmask->number],
				pl->contr->xpm_pixmap, pl->contr->gc_xpm_object,
				0,0,24,24,0,0);
		  obj_mask = pl->contr->masks[darkmask->number];
		}

		XCopyArea(pl->contr->gdisp,pl->contr->xpm_pixmap,
					 pl->contr->win_game,pl->contr->gc_game,
					 0,0,24,24,2+24*ax,2+24*ay);
		pl->contr->floor[ax][ay] = floor;
		pl->contr->drawn[ax][ay]= face;
		pl->contr->darkmask[ax][ay] = d;
		
#ifdef DOUBLE_FLOOR
		  pl->contr->floor2[ax][ay] = floor2;
#endif
		  }
		  }
		}
	 }
	 if(pl->invisible && (pl->invisible < 50 ? 4 : 1)) {
		pl->contr->drawn[5][5] = pl->face;
		XCopyArea(pl->contr->gdisp,pl->contr->pixmaps[pl->face->number],
					 pl->contr->win_game,pl->contr->gc_game,0,0,24,24,122,122);
	 }
  }
#endif

