Crossfire Mailing List Archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug in player.c: fix




Yipe, I didn't mean to send that last yet....
here's the fix....

replace player.c: (the entire file)


<cut here>
----------------------------------------------------------------
/*
 * static char *rcsid_player_c =
 *   "$Id: player.c,v 1.24 1994/03/03 06:17:22 master Exp master $";
 */

/*
    CrossFire, A Multiplayer game for X-windows

    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 frankj@ifi.uio.no.
*/

#include <pwd.h>
#include <graphics.h>
#include <global.h>
#ifndef __CEXTRACT__
#include <sproto.h>
#endif
#include <player.h>
#include <sounds.h>
#include <living.h>

extern spell spells[NROFREALSPELLS];

void display_motd(object *op) {
#ifdef MOTD
  char buf[MAX_BUF];
  FILE *fp;
  int comp;

  sprintf(buf,"%s/%s",LibDir,MOTD);
  if((fp=open_and_uncompress(buf,0,&comp))==NULL) {
    return;
  }
  while(fgets(buf,MAX_BUF,fp)!=NULL) {
    char *cp;
    if(*buf=='#')
      continue;
    cp=strchr(buf,'\n');
    if(cp!=NULL)
      *cp='\0';
    draw_info(op,buf);
  }
  close_and_delete(fp, comp);
  draw_info(op," ");
#endif
}

int playername_ok(char *cp) {
  for(;*cp!='\0';cp++)
    if(!((*cp>='a'&&*cp<='z')||(*cp>='A'&&*cp<='Z'))&&*cp!='-'&&*cp!='_')
      return 0;
  return 1;
}

int add_player(char *disp, char *username, mapstruct *m) {
  player *p;
  char buf[MAX_BUF],*cp,*defname = "nobody";
  struct passwd *pwent;

  if (username == NULL) {
    pwent = getpwuid(getuid());
    if (pwent)
      username = pwent->pw_name;
    else
      username = defname;
  }

  init_beforeplay(); /* Make sure everything is ready */
  if(m == NULL)
    m = ready_map_name(first_map_path,0);
  if(m == NULL)
    fatal(MAP_ERROR);
  m->timeout = MAP_TIMEOUT; /* Just in case we fail to add the player */
  m->players++;
  p=get_player_ob();
  p->name=add_string(disp);
  p->username = strdup_local(username);
  p->writing=0,
  p->weapon_sp=0,p->last_weapon_sp= -1;
  p->last_armour= (-1);
  p->has_hit=0;
  p->scroll_inv=0,p->scroll_look=0,
  strcpy(p->format_inv,"%-24.24s%-6s"); /* This can be changed by resize */
  strcpy(p->format_look,p->format_inv);
  p->inv_size=INV_SIZE;
  p->look_size=LOOK_SIZE;
  p->inv_chars=30;
  p->look_chars=30;
  p->barlength_inv=BARLENGTH_INV;
  p->barlength_look=BARLENGTH_LOOK;
  p->last_scroll_inv=p->last_scroll_look=1,
  p->scrollsize_inv= p->scrollsize_look= p->scrollsize_hp=p->scrollsize_sp=
  p->scrollsize_food=p->scrollstart_inv= p->scrollstart_look=0,
  p->nrofdrawn_inv=0,p->nrofdrawn_look=0;
  p->peaceful=1;			/* default peaceful */
  p->do_los=1;
  p->scroll=0;				/* default without scroll */
  p->split_window=default_split_window;	/* See -w flag */
  p->last_weight= -1;
  p->freeze_inv=p->freeze_look=0;
  p->viewmap=0;
  p->mapres=1;
  p->mapdelx[0]= -1;
#ifdef EXPLORE_MODE
  p->explore=0;
#endif  
#ifdef SHOP_LISTINGS
  p->menu = NULL;
#endif
  if(use_pixmaps)
    p->use_pixmaps = 1;
  if (color_pix) {
	p->use_pixmaps = 1;
	p->color_pixmaps = 1;
  }
  strncpy(buf,disp,MAX_BUF);
  if(strchr(buf,':')==NULL)
    strcat(buf,":0");
  if(get_root_display(p,buf)  || get_game_display(p,buf) ||
     get_stats_display(p,buf) || get_info_display(p,buf) ||
     get_inv_display(p,buf)   || get_look_display(p,buf) ||
     get_message_display(p,buf)) {
    m->players--;
    free_player(p);
    return 1;
  }

  /* Is there any reason that the values checked for in the if statements
   * need to be re-set below them?  MSW (master@rahul.net)
   */
  if (p->color_pixmaps) {
	p->color_pixmaps = 1;
	p->use_pixmaps = 1;

	/* We pass the player to ReadPixmaps because both the pixmaps
	 * and the mask value for it needs to be set.  Could only
	 * return one, and since the mask is created at the same time
	 * as the pixmap....
 	 */
	ReadPixmaps(p);
  }
  else if(p->use_pixmaps) {
    p->use_pixmaps = 1;
    p->pixmaps = ReadBitmaps (p->gdisp);
  }

  load_default_keys(p);

  setuperrors();
  Protocol_atom = XInternAtom(p->gdisp, "WM_PROTOCOLS", False);
  Kill_atom = XInternAtom(p->gdisp, "WM_DELETE_WINDOW", False);
  if(!p->split_window)
    XSetWMProtocols(p->gdisp, p->win_root, &Kill_atom, 1);
  else { /* I hope this is correct... */
    XSetWMProtocols(p->gdisp, p->win_game, &Kill_atom, 1);
    XSetWMProtocols(p->gdisp, p->win_stats, &Kill_atom, 1);
    XSetWMProtocols(p->gdisp, p->win_info, &Kill_atom, 1);
    XSetWMProtocols(p->gdisp, p->win_inv, &Kill_atom, 1);
    XSetWMProtocols(p->gdisp, p->win_look, &Kill_atom, 1);
    XSetWMProtocols(p->gdisp, p->win_message, &Kill_atom, 1);
  }
  free_string(p->ob->name);
  p->ob->name = NULL;
  free_object(p->ob);
  p->ob=get_player(p,m);
  add_friendly_object(p->ob);
  if ((cp = XGetDefault(p->gdisp, name, "wimpy")) != NULL)
    p->ob->run_away = atoi(cp);
#ifdef SOUND_EFFECTS
  if ((cp = XGetDefault(p->gdisp, name, "sounds")) != NULL)
    {
      if (!strcmp("on", cp) || !strcmp("yes", cp))
	p->rplay_fd = init_disp_sound(disp);
      else if (!strcmp("off", cp) || !strcmp("no", cp))
	p->rplay_fd = 0;
    }
  else
    p->rplay_fd = init_disp_sound(disp);
  LOG(llevDebug, "Rplay fd: %d\n", p->rplay_fd);
  play_sound_player_only(p, SOUND_NEW_PLAYER);
#endif
#ifdef MOTD
  display_motd(p->ob);
#endif
#ifndef SAVE_PLAYER
  draw_info(p->ob,"Welcome, Brave Warrior!");
  draw_info(p->ob," ");
  Roll_Again(p->ob);
#endif
  if((cp=XGetDefault(p->gdisp,name,"name"))!=NULL&&check_name(p,cp)) {
    if(p->ob->name!=NULL)
      free_string(p->ob->name);
    p->ob->name=add_string(cp);
#ifdef SAVE_PLAYER
    remove_lock(p);
    get_password(p->ob);
#endif
    p->name_changed=1;
  } else {
#ifdef SAVE_PLAYER
    get_name(p->ob);
#endif
    cp=strchr(buf,'.');
    if(cp!=NULL) *cp='\0';
    cp=strchr(buf,':');
    if(cp!=NULL) *cp='\0';
    p->ob->name=add_string(buf);
    p->name_changed=0;
  }
  if((cp=XGetDefault(p->gdisp,name,"peaceful"))!=NULL)
    if(!strcmp("on",cp)||!strcmp("yes",cp))
      p->peaceful=1;
    else if(!strcmp("off",cp)||!strcmp("no",cp))
      p->peaceful=0;
  if((cp=XGetDefault(p->gdisp,name,"berzerk"))!=NULL)
    if(!strcmp("on",cp)||!strcmp("yes",cp))
      p->berzerk=1;
    else if(!strcmp("off",cp)||!strcmp("no",cp))
      p->berzerk=0;
  if((cp=XGetDefault(p->gdisp,name,"scroll"))!=NULL)
    if(!strcmp("on",cp)||!strcmp("yes",cp))
      p->scroll=1;
    else if(!strcmp("off",cp)||!strcmp("no",cp))
      p->scroll=0;
  if((cp=XGetDefault(p->gdisp,name,"title"))!=NULL && strlen(cp)<MAX_NAME)
      strcpy (p->own_title, cp);
  else
      p->own_title[0]='\0';
  return 0;
}

/*
 * get_player_archetype() return next player archetype from archetype
 * list. Not very efficient routine, but used only creating new players.
 * Note: there MUST be at least one player archetype!
 */
archetype *get_player_archetype(archetype* at)
{
    archetype *start = at;
    for (;;) {
	if (at==NULL || at->next==NULL)
	    at=first_archetype;
	else
	    at=at->next;
	if(at->clone.type==PLAYER)
	    return at;
	if (at == start) {
	    LOG (llevError, "No Player achetypes\n");
	    exit (-1);
	}
    }
}


object *get_player(player *p, mapstruct *m) {
  object *op=arch_to_object(get_player_archetype(NULL));
  int i;

  p->loading = NULL;
  op->map=m;
  if(m->in_memory != MAP_IN_MEMORY) {
    p->loading = m;
    p->new_x = 0;
    p->new_y = 0;
    p->removed = 0;
    op->x=0;
    op->y=0;
  } else {
    i=find_free_spot(NULL,m,EXIT_X(m->map_object),EXIT_Y(m->map_object),
	0,SIZEOFFREE);
    op->x=EXIT_X(m->map_object)+freearr_x[i];
    op->y=EXIT_Y(m->map_object)+freearr_y[i];
  }
  p->fire_on=0,p->run_on=0;
/*  p->face=0;
  op->face=classarch[p->face]->faces[0]; */
  p->count=0;
  p->count_left=0;
  p->prev_cmd=' ';
  p->prev_fire_on=0;
  p->mode=0;
  p->berzerk=1;
  p->idle=0;
  *p->maplevel=0;
#if 0
  op->type=PLAYER;
  SET_ALIVE(op);
  op->speed=0.5;
  op->stats.exp=0;
  op->stats.food=500;
  op->weight=70000; /* 70 Kg. */
  op->attacktype=AT_PHYSICAL;
#endif
  op->run_away = 25; /* Then we panick... */
  op->contr=p; /* this aren't yet in archetype */
  op->speed_left=0.5;
  op->direction=0;
  op->stats.wc=2;
  op->run_away = 25; /* Then we panick... */

  roll_stats(op);
  p->state=ST_ROLL_STAT;
  clear_los(op);
  p->last_stats.Str=0,  p->last_stats.Dex=0,
  p->last_stats.Int=0,  p->last_stats.Con=0,
  p->last_stats.Wis=0,  p->last_stats.Cha=0;
  p->last_stats.hp=0,   p->last_stats.maxhp=0;
  p->last_stats.wc=0,   p->last_stats.ac=0;
  p->last_stats.sp=0,   p->last_stats.maxsp=0;
  p->last_stats.exp= -1,p->last_stats.food=0;
  p->digestion=0,p->gen_hp=0,p->gen_sp=0;
  p->last_spell= -1;
  p->last_value= -1;
  p->last_speed= -1;
  p->shoottype=0,p->shootstrength=5;
  p->last_shoot= -1;
  p->listening=9;
  p->golem=NULL;
  p->last_used=NULL;
  p->last_used_id=0;
#if 0
  strncpy(p->title,classname[0],MAX_NAME);
  op->race = add_string (classname[0]);
#else
  strncpy(p->title,op->arch->clone.name,MAX_NAME);
  op->race = add_string (op->arch->clone.race);
#endif

  (void)memset((void *)op->contr->drawn,'\0',
               sizeof(Fontindex)*(WINRIGHT-WINLEFT+1)*(WINLOWER-WINUPPER+1));
  for(i=0;i<NROFREALSPELLS;i++)
    p->known_spells[i]= -1;
  p->nrofknownspells=0;
  p->chosen_spell = -1;
  draw_all_inventory(op);
  draw_all_look(op);
  return op;
}

object *get_nearest_player(object *mon) {
  object *op = NULL;
  objectlink *ol;
  int lastdist,tmp;

  for(ol=first_friendly_object,lastdist=1000;ol!=NULL;ol=ol->next) {
    if((ol->ob->type==PLAYER && ol->ob->contr->state)
       ||((ol->ob->invisible&&QUERY_FLAG(ol->ob,FLAG_UNDEAD)==QUERY_FLAG(mon,FLAG_UNDEAD))
          &&!QUERY_FLAG(mon,FLAG_SEE_INVISIBLE))||ol->ob->map!=mon->map)
      continue;
    tmp=distance(ol->ob,mon);
    if(lastdist>tmp) {
      op=ol->ob;
      lastdist=tmp;
    }
  }
  return op;
}

int path_to_player(object *mon, object *pl,int mindiff) {
  int dir,x,y,diff;
  dir=find_dir_2(mon->x-pl->x,mon->y-pl->y);
  x=mon->x+freearr_x[dir],y=mon->y+freearr_y[dir];
  if(mon->value) x++;
  diff=isqrt((x-pl->x)*(x-pl->x)+(y-pl->y)*(y-pl->y));
  if(diff<mindiff) return 0;
  while(diff-- > 0) {
    if(blocked(mon->map,x,y))
      return 0;
    x+=freearr_x[dir],y+=freearr_y[dir];
  }
  return dir;
}

void give_initial_items(object *pl) {
  object *op;
  char start_spells[] = {0, 1, 4, 5, 7, 19};
  if(pl->arch->randomitems!=NULL)
     create_treasure(pl->arch->randomitems,pl,GT_INVENTORY,1);
  for(op=pl->inv;op;op=op->below) {
    if(op->nrof<2 && op->type!=CONTAINER)
      SET_FLAG(op,FLAG_STARTEQUIP);
    if(op->type==SPELLBOOK) /* fix spells for first level spells */
      op->stats.sp=start_spells[RANDOM()%sizeof(start_spells)];
    /* Give starting characters identified, uncursed, and undamned
     * items.  Just don't identify gold or silver, or it won't be
     * merged properly.
     */
    if (op->type!=GOLDCOIN && op->type!=SILVERCOIN) {
	SET_FLAG(op, FLAG_IDENTIFIED);
	CLEAR_FLAG(op, FLAG_CURSED);
	CLEAR_FLAG(op, FLAG_DAMNED);
    }
  }
#if 0
  int i,nrof;
  object *op;
  char buf[BIG_NAME],buf2[BIG_NAME];

  pl->contr->freeze_inv=1;
  for(i=0;i<4;i++) {
    nrof=0;
    strcpy(buf,class_items[pl->contr->face][i]);
    if(!strcmp(buf,"nothing"))
      continue;
    if(sscanf(buf,"%d %s",&nrof,buf2)==2)
      strcpy(buf,buf2);
    if((op=get_archetype(buf))==NULL) {
      LOG(llevError,"Error in class_items[%d][%d]\n",pl->contr->face,i);
      continue;
    }
    if(nrof)
      op->nrof=nrof;
    if(op->type==SPELLBOOK)
      switch(RANDOM()%6) {
      case 0:
        op->stats.sp=0;
        break;
      case 1:
        op->stats.sp=1;
        break;
      case 2:
        op->stats.sp=4;
        break;
      case 3:
        op->stats.sp=5;
        break;
      case 4:
        op->stats.sp=7;
        break;
      case 5:
        op->stats.sp=19;
        break;
      }
    if(op->nrof<2 && op->type!=CONTAINER)
      SET_STARTEQUIP(op);
    SET_IDENTIFIED(op);
    insert_ob_in_ob(op,pl);
  }
  for (
#endif	
  pl->contr->freeze_inv=0;
  draw_all_inventory(pl);
}

void get_name(object *op) {
  op->contr->write_buf[0]='\0';
  op->contr->writing=0;
  op->contr->state=ST_GET_NAME;
  draw_info(op,"What is your name?");
  write_ch(op,':');
}

void get_password(object *op) {
  op->contr->write_buf[0]='\0';
  op->contr->writing=0;
  op->contr->state=ST_GET_PASSWORD;
  draw_info(op,"What is your password?");
  write_ch(op,':');
  op->contr->no_echo=1;
}

void confirm_password(object *op) {
  op->contr->write_buf[0]='\0';
  op->contr->writing=0;
  op->contr->state=ST_CONFIRM_PASSWORD;
  draw_info(op,"Please type your password again.");
  write_ch(op,':');
  op->contr->no_echo=1;
}

int roll_stat() {
  int a[4],i,j,k;
  for(i=0;i<4;i++)
    a[i]=(int)RANDOM()%6+1;
  for(i=0,j=0,k=7;i<4;i++)
    if(a[i]<k)
      k=a[i],j=i;
  for(i=0,k=0;i<4;i++) {
    if(i!=j)
      k+=a[i];
  }
  return k;
}

void roll_stats(object *op) {
  int sum=0;
  do {
    op->stats.Str=roll_stat();
    op->stats.Dex=roll_stat();
    op->stats.Int=roll_stat();
    op->stats.Con=roll_stat();
    op->stats.Wis=roll_stat();
    op->stats.Cha=roll_stat();
    sum=op->stats.Str+op->stats.Dex+op->stats.Int+
        op->stats.Con+op->stats.Wis+op->stats.Cha;
  } while(sum<70||sum>99);
#if defined( USE_SWAP_STATS) && defined(SORT_ROLLED_STATS)
        /* Sort the stats so that rerolling is easier... */
        {
                int             i = 0, j = 0;
                int             statsort[6];

                statsort[0] = op->stats.Str;
                statsort[1] = op->stats.Dex;
                statsort[2] = op->stats.Int;
                statsort[3] = op->stats.Con;
                statsort[4] = op->stats.Wis;
                statsort[5] = op->stats.Cha;

                /* a quick and dirty bubblesort? */
                do {
                        if (statsort[i] < statsort[i + 1]) {
                                j = statsort[i];
                                statsort[i] = statsort[i + 1];
                                statsort[i + 1] = j;
                                i = 0;
                      } else {
                                i++;
                      }
              } while (i < 5);

                op->stats.Str = statsort[0];
                op->stats.Dex = statsort[1];
                op->stats.Con = statsort[2];
                op->stats.Int = statsort[3];
                op->stats.Wis = statsort[4];
                op->stats.Cha = statsort[5];
      }
#endif /* SWAP_STATS */

#if 1
  op->contr->orig_stats.Str=op->stats.Str;
  op->contr->orig_stats.Dex=op->stats.Dex;
  op->contr->orig_stats.Int=op->stats.Int;
  op->contr->orig_stats.Con=op->stats.Con;
  op->contr->orig_stats.Wis=op->stats.Wis;
  op->contr->orig_stats.Cha=op->stats.Cha;
#endif
  op->stats.hp= -10000;
  op->level=0;
  op->stats.exp=0;
  op->stats.sp=0;
  op->stats.ac=0;
  add_exp(op,0);
  op->stats.sp=op->stats.maxsp;
  op->stats.hp=op->stats.maxhp;
  op->contr->orig_stats=op->stats;
}

void Roll_Again(object *op)
{
#ifndef USE_SWAP_STATS
  draw_info(op,"Roll again (y/n)? ");
#else
  draw_info(op,"[y] to roll new stats [n] to use stats");
  draw_info(op,"[1-6] [1-6] to swap stats.\n");
  draw_info(op,"Roll again (y/n/1-6)? ");
#endif /* USE_SWAP_STATS */
}

void Swap_Stat(object *op,int Swap_Second)
{
#ifdef USE_SWAP_STATS
  signed char tmp;
  char *from=NULL,*to=NULL;

  if ( op->contr->Swap_First == 0 ) {
    draw_info(op,"How the hell did you get here?!?!!!");
    draw_info(op,"Error in Swap_Stat code,");
    draw_info(op,"mail korg@rdt.monash.edu.au");
    return;
    }
  switch( op->contr->Swap_First ) {
    case 1: from= (char *)&op->contr->orig_stats.Str; break;
    case 2: from= (char *)&op->contr->orig_stats.Dex; break;
    case 3: from= (char *)&op->contr->orig_stats.Con; break;
    case 4: from= (char *)&op->contr->orig_stats.Int; break;
    case 5: from= (char *)&op->contr->orig_stats.Wis; break;
    case 6: from= (char *)&op->contr->orig_stats.Cha; break;
    default: break;
    };
  switch(Swap_Second) {
  case 1:
    to= (char *)&op->contr->orig_stats.Str;
    draw_info(op,"Str done\n");
    break;
  case 2:
    to= (char *)&op->contr->orig_stats.Dex;
    draw_info(op,"Dex done\n");
    break;
  case 3:
    to= (char *)&op->contr->orig_stats.Con;
    draw_info(op,"Con done\n");
    break;
  case 4:
    to= (char *)&op->contr->orig_stats.Int;
    draw_info(op,"Int done\n");
    break;
  case 5:
    to= (char *)&op->contr->orig_stats.Wis;
    draw_info(op,"Wis done\n");
    break;
  case 6:
    to= (char *)&op->contr->orig_stats.Cha;
    draw_info(op,"Cha done\n");
    break;
  default:
    break;
  }
  if (from!=NULL&&to!=NULL) {
  tmp= (signed char) *from;
  *from= (signed char) *to;
  *to=tmp;
  op->stats.Str = op->contr->orig_stats.Str;
  op->stats.Dex = op->contr->orig_stats.Dex;
  op->stats.Con = op->contr->orig_stats.Con;
  op->stats.Int = op->contr->orig_stats.Int;
  op->stats.Wis = op->contr->orig_stats.Wis;
  op->stats.Cha = op->contr->orig_stats.Cha;
  }
    op->stats.hp= -10000;
    op->level=0;
    op->stats.exp=0;
    op->stats.sp=0;
    op->stats.ac=0;
    add_exp(op,0);
    op->stats.sp=op->stats.maxsp;
    op->stats.hp=op->stats.maxhp;
    add_exp(op,0);
  op->contr->Swap_First=0;
#endif /* USE_SWAP_STATS */
}

void flee_player(object *op) {
  int dir,diff;
  if(op->stats.hp < 0) {
    LOG(llevDebug, "Fleeing player is dead.\n");
    CLEAR_FLAG(op, FLAG_SCARED);
    return;
  }
  if(op->enemy==NULL) {
    LOG(llevDebug,"Fleeing player had no enemy.\n");
    CLEAR_FLAG(op, FLAG_SCARED);
    return;
  }
  if(!(RANDOM()%5)&&RANDOM()%20+1>=savethrow[op->level]) {
    op->enemy=NULL;
    CLEAR_FLAG(op, FLAG_SCARED);
    return;
  }
  dir=absdir(4+find_dir_2(op->x-op->enemy->x,op->y-op->enemy->y));
  for(diff=0;diff<3;diff++) {
    int m=1-(RANDOM()&2);
    if(move_ob(op,absdir(dir+diff*m))||
       (diff==0&&move_ob(op,absdir(dir-diff*m)))) {
      draw(op);
      return;
    }
  }
  /* Cornered, get rid of scared */
  CLEAR_FLAG(op, FLAG_SCARED);
  op->enemy=NULL;
}

int check_pick(object *op) {
  if(QUERY_FLAG(op,FLAG_FLYING) || op->below==NULL || !can_pick(op,op->below))
    return 1;
#ifdef SEARCH_ITEMS
  if(op->contr->search_str[0]!='\0')
    {
      object *next,*tmp;
      tmp=op->below;
      while(tmp!=NULL&&can_pick(op,tmp))
	{
	  next=tmp->below;
	  if(strstr(long_desc(tmp),op->contr->search_str)!=NULL)
	    pick_up(op,tmp);
	  tmp=next;
	}
    }
#endif /* SEARCH_ITEMS */
  if(op->contr->mode)
    {
  if(op->contr->mode==3) return 0;
  pick_up(op,op->below);
  while(op->below!=NULL&&can_pick(op,op->below)&&op->contr->mode>3)
    pick_up(op,op->below);
  return op->contr->mode==1||op->contr->mode==4;
    }
  return 1;
}

void fire(object *op,int dir) {
  object *tmp,*weap;

  if (op->contr->tmp_invis) {         /* tmp invis goes away now */
    op->invisible = 0;
    op->contr->tmp_invis = 0;
    update_object(op);
  }
  switch(op->contr->shoottype) {
  case range_none:
    return;
  case range_bow:
    for(weap=op->inv;weap!=NULL;weap=weap->below)
      if(weap->type==BOW&&QUERY_FLAG(weap, FLAG_APPLIED))
        break;
    if(weap==NULL) {
      draw_info(op,"You have no bow readied.");
      op->contr->count_left=0;
      return;
    }
    if(wall(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir])) {
      draw_info(op,"Something is in the way.");
      op->contr->count_left=0;
      return;
    }
    if((tmp=present_in_ob(weap->stats.maxsp,op))==NULL)
    { /* maxsp = which arrowtype to use */
      sprintf(errmsg,"You have no %s left.",get_archename(weap->stats.maxsp));
      draw_info(op,errmsg);
      op->contr->count_left=0;
      return;
    }
    if((tmp=get_split_ob(tmp,1))==NULL) { /* Shouldn't happen */
      draw_info(op,errmsg);
      return;
    }
    op->speed_left=0.01-(float)FABS(op->speed)*(float)weap->stats.sp;
    fix_player(op);
    set_owner(tmp,op);
    tmp->direction=dir;
    tmp->x=op->x,tmp->y=op->y;
    tmp->speed=1, tmp->speed_left=0;
    tmp->face.number=tmp->arch->faces[dir];
    tmp->stats.dam=(QUERY_FLAG(weap, FLAG_NO_STRENGTH)?0:dam_bonus[op->stats.Str])
                   +weap->stats.dam+weap->magic;
    tmp->stats.wc= 20-weap->magic-op->level-dex_bonus[op->stats.Dex]
                   -thaco_bonus[op->stats.Str];
    tmp->map=op->map;
    SET_FLAG(tmp, FLAG_FLYING);
    SET_FLAG(tmp, FLAG_FLY_ON);
    SET_FLAG(tmp, FLAG_WALK_ON);
    D_LOCK(op);
#ifdef SOUND_EFFECTS
    play_sound_map(op->map, op->x, op->y, SOUND_FIRE_ARROW);
#endif
    insert_ob_in_map(tmp,op->map);
    move_arrow(tmp);
    D_UNLOCK(op);
    return;
  case range_magic: /* Casting spells */
    op->stats.sp-=cast_spell(op,dir,op->contr->chosen_spell,0,spellNormal);
    draw_stats(op);
    return;
  case range_wand:
    for(weap=op->inv;weap!=NULL;weap=weap->below)
      if(weap->type==WAND&&QUERY_FLAG(weap, FLAG_APPLIED))
        break;
    if(weap==NULL) {
      draw_info(op,"You have no wand readied.");
      op->contr->count_left=0;
      return;
    }
    if(weap->stats.food<=0) {
#ifdef SOUND_EFFECTS
      play_sound_player_only(op->contr, SOUND_WAND_POOF);
#endif
      draw_info(op,"The wand says poof.");
      return;
    }
    if(cast_spell(op,dir,op->contr->chosen_spell,0,spellWand)) {
      SET_FLAG(op, FLAG_BEEN_APPLIED); /* You now know something about it */
      if (!(--weap->stats.food))
      {
        object *tmp;
        if (weap->arch) {
	  CLEAR_FLAG(weap, FLAG_ANIMATE);
          weap->face = weap->arch->clone.face;
          weap->speed = 0;
        }
        if ((tmp=is_player_inv(weap)))
          draw_inventory_faces(tmp);
      }
    }
    return;
  case range_rod:
  case range_horn:
    for(weap=op->inv;weap!=NULL;weap=weap->below)
      if(QUERY_FLAG(weap, FLAG_APPLIED)&&
         weap->type==(op->contr->shoottype==range_rod?ROD:HORN))
        break;
    if(weap==NULL) {
      char buf[MAX_BUF];
      sprintf(buf, "You have no %s readied.",
        op->contr->shoottype == range_rod ? "rod" : "horn");
      draw_info(op, buf);
      op->contr->count_left=0;
      return;
    }
    if(weap->stats.hp<spells[weap->stats.sp].sp) {
      LOG(llevDebug,"Horn/rod: %d < %d (%d)\n", weap->stats.hp, spells[weap->stats.sp].sp, weap->stats.sp);
#ifdef SOUND_EFFECTS
      play_sound_player_only(op->contr, SOUND_WAND_POOF);
#endif
      if (op->contr->shoottype == range_rod)
        draw_info(op,"The rod whines for a while, but nothing happens.");
      else
        draw_info(op,
                  "No matter how hard you try you can't get another note out.");
      return;
    }
    if(cast_spell(op,dir,op->contr->chosen_spell,0,
       op->contr->shoottype == range_rod ? spellRod : spellHorn)) {
      SET_FLAG(op, FLAG_BEEN_APPLIED); /* You now know something about it */
      drain_rod_charge(weap);
    }
    return;
  case range_scroll: /* Control summoned monsters from scrolls */
    if(op->contr->golem==NULL) {
      op->contr->shoottype=range_none;
      op->contr->chosen_spell= -1;
      draw_stats(op);
    }
    cast_spell(op,dir,op->contr->chosen_spell,0, spellScroll);
    return;
#if 0
  case 10: /* Thrown items */
    draw_info(op,"Throw is unimplemented as of now.");
    return;
    tmp=op->inv;
    if(tmp==NULL) {
      draw_info(op,"You have nothing to throw.");
      op->contr->count_left=0;
      return;
    }
    if((tmp->weight/1000)>max_carry[op->stats.Str]/2) {
      sprintf(buf,"You're not strong enough to throw %s.",tmp->name);
      draw_info(op,buf);
      op->contr->count_left=0;
      return;
    }
    if(tmp->type!=BOMB&&(tmp->speed||IS_ALIVE(tmp))) {
      sprintf(buf,"You can only throw dead objects, not the %s.",tmp->name);
      draw_info(op,buf);
      op->contr->count_left=0;
      return;
    }
    if(tmp->type!=BOMB)
      tmp->speed=0.5;
    tmp->direction=dir;
    tmp->thrown=max_carry[op->stats.Str]/(tmp->weight/500);
    if(tmp->thrown<1) tmp->thrown=1;
    if(tmp->thrown>10) tmp->thrown=10;
    tmp->thrownthaco=
      20-op->level-dex_bonus[op->stats.Dex]-thaco_bonus[op->stats.Str];
    set_owner(tmp,op);
    move_thrown(tmp);
    return;
#endif
  default:
    draw_info(op,"Illegal shoot type.");
    op->contr->count_left=0;
    return;
  }
}

int move_player(object *op,int dir) {
  object *tmp,*tmp2;
  object *stack;
  int dx, dy;
  int face = dir ? (dir - 1) / 2 : -1;

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

  /* peterm:  added following line */
  op->facing = dir;
  if(QUERY_FLAG(op,FLAG_CONFUSED) && dir)
    dir = absdir(dir + RANDOM()%3 + RANDOM()%3 - 2);

  dx = freearr_x[dir];
  dy = freearr_y[dir];

  if(op->contr->fire_on)
    fire(op,dir);
  else
    if((op->contr->braced||!move_ob(op,dir))
       &&!out_of_map(op->map,op->x+dx,op->y+dy)) {
      op->contr->has_hit = 1; /* The last action was to hit, so use weapon_sp */
      stack=tmp=get_map_ob(op->map,op->x+freearr_x[dir],op->y+freearr_y[dir]);
      while(stack) {
        if ((QUERY_FLAG(stack,FLAG_ALIVE) || QUERY_FLAG(stack,FLAG_CAN_ROLL)
	    || stack->type ==LOCKED_DOOR) && stack!=op)
          tmp = stack;
        stack = stack->above;
      }
      if(tmp!=NULL) {
        if(tmp->head != NULL)
          tmp = tmp->head;
        if(tmp->type==DOOR&&tmp->stats.hp>=0) {
          tmp2=op->inv;
          while(tmp2!=NULL&&tmp2->type!=KEY) /* Find a key */
            tmp2=tmp2->below;
          if(tmp2!=NULL) {
#ifdef SOUND_EFFECTS
	    play_sound_map(op->map, op->x, op->y, SOUND_OPEN_DOOR);
#endif
            decrease_ob(tmp2); /* Use up one of the keys */
            hit_player(tmp,9999,op,AT_PHYSICAL); /* Break through the door */
          }
        }
        if(tmp->type==LOCKED_DOOR) {
          tmp2=op->inv;
          while(tmp2 && (tmp2->type != SPECIAL_KEY ||
		tmp2->slaying != tmp->slaying)) /* Find the key */
	     tmp2=tmp2->below;
          if(tmp2) {
            decrease_ob_nr(tmp2, 1); /* Use the key */
            remove_door2(tmp); /* remove door without violence ;-) */
#ifdef SOUND_EFFECTS
	    play_sound_map(op->map, op->x, op->y, SOUND_OPEN_DOOR);
#endif
          } else if (tmp->msg) /* show door's message if present */
	    print_message(op, tmp->msg, 2);
        }
        if (tmp->enemy != op &&
            (tmp->type==PLAYER || QUERY_FLAG(tmp,FLAG_UNAGGRESSIVE)
	    || QUERY_FLAG(tmp, FLAG_FRIENDLY)) &&
            op->contr->peaceful && (!op->contr->braced)) {
#ifdef SOUND_EFFECTS
	  play_sound_map(op->map, op->x, op->y, SOUND_PUSH_PLAYER);
#endif
	  (void) push_ob(tmp,dir,op);
	}
        else if(QUERY_FLAG(tmp,FLAG_CAN_ROLL)&&(!op->contr->braced))
          recursive_roll(tmp,dir,op);
        else if(tmp->stats.hp>=0&&QUERY_FLAG(tmp, FLAG_ALIVE)) {
          (void) attack_ob(tmp,op);
          if (tmp->type == PLAYER && tmp->stats.hp >= 0 && !tmp->contr->has_hit)
          {
            short luck = tmp->stats.luck;
            tmp->contr->has_hit = 1;
            (void) attack_ob(op, tmp); /* Just hit back.  We are adjacent. */
            tmp->stats.luck = luck; /* Don't loose luck defending yourself */
          }
/*        op->speed_left-=op->contr->weapon_sp-1; */
          if(op->contr->tmp_invis) {
            op->contr->tmp_invis=0;
            op->invisible=0;
            update_object(op);
          }
        }
#if 0
        else
          op->contr->run_on=0,op->direction=0;
#endif
      }
    }
  if((check_pick(op)&&op->contr->run_on)||
     (op->contr->berzerk&&op->contr->run_on))
    op->direction=dir;
  else
    op->direction=0;
  if(face != -1)
    /*op->face = classarch[op->contr->face]->faces[face];*/
    op->face.number = op->arch->faces[face];
  update_object(op);
  return 0;
}

int has_cleared_window(Window w,Window *win_clr,int max) {
  for(max--;max>=0;max--)
    if(win_clr[max]==w)
      return 1;
  return 0;
}

void handle_player(object *op) {
  int repeat,windex=0;
  char text[10];
  char buf[MAX_BUF];
  Window win_clr[10];

  if(op->contr->state == ST_PLAYING && op->contr->loading != NULL) {
    op->speed_left++;
    if(op->contr->loading->in_memory == MAP_IN_MEMORY)
      enter_map(op);
    else
      return;
  }
  if(op->invisible) {
    op->invisible--;
    if(!op->invisible) {
      CLEAR_FLAG(op, FLAG_UNDEAD);
      update_object(op);
    }
  }
  op->contr->has_hit=0;
  draw_stats(op);
  repeat=1;
  while(repeat) {
    repeat=0;
    if (QUERY_FLAG(op,FLAG_SCARED)) {
      flee_player(op);
      return;
    }
    if (XEventsQueued(op->contr->gdisp, QueuedAfterReading)==0) {
      if(op->contr->count_left>0) {
        int tmp_fire_on=op->contr->fire_on;
        op->contr->fire_on=op->contr->prev_fire_on;
        op->contr->count_left--;
        parse_key(op, op->contr->prev_cmd,
		  op->contr->prev_keycode, op->contr->prev_keysym);
        op->contr->fire_on=tmp_fire_on;
      } else if(op->direction)
        if(op->contr->berzerk&&!op->contr->fire_on)
          move_player(op,op->direction);
        else if(!move_ob(op,op->direction)||!check_pick(op))
          op->direction=0;
#if 0
      if(synchronize && op->contr->idle++>1) {
        op->contr->idle=1;
        XSync(op->contr->gdisp, False);
      }
      else XFlush(op->contr->gdisp);
#endif
      return;
    }
    op->contr->idle=0;
    XNextEvent(op->contr->gdisp,&op->contr->gevent);
    switch(op->contr->gevent.type) {
    case ConfigureNotify:
      if(op->contr->gevent.xconfigure.window==op->contr->win_info)
        resize_win_info(op->contr,&op->contr->gevent);
      else if(op->contr->gevent.xconfigure.window==op->contr->win_inv)
        resize_win_inv(op->contr,&op->contr->gevent);
      else if(op->contr->gevent.xconfigure.window==op->contr->win_look)
        resize_win_look(op->contr,&op->contr->gevent);
      break;
    case Expose:
      repeat=1;
      if(has_cleared_window(op->contr->gevent.xexpose.window,win_clr,windex))
        break;
      win_clr[windex++]=op->contr->gevent.xexpose.window;
      if(op->contr->gevent.xexpose.window==op->contr->win_stats) {
        XClearWindow(op->contr->gdisp,op->contr->win_stats);
        op->contr->last_value= -1;
        draw_stats(op);
      } else if(op->contr->gevent.xexpose.window==op->contr->win_info)
        draw_all_info(op);
      else if(op->contr->gevent.xexpose.window==op->contr->win_inv)
        draw_all_inventory(op);
      else if(op->contr->gevent.xexpose.window==op->contr->win_look)
        draw_all_look(op);
      else if(op->contr->gevent.xexpose.window==op->contr->win_message)
        draw_all_message(op);
      else if(op->contr->gevent.xexpose.window==op->contr->win_game)
        refresh(op);
      else if(!op->contr->split_window&&
              op->contr->gevent.xexpose.window==op->contr->win_root) {
        XClearWindow(op->contr->gdisp,op->contr->win_root);
      }
#if 0 /* Hopefully made obsolete by win_clr.  Didn't work well anyway... */
      while(repeat&&XEventsQueued(op->contr->gdisp, QueuedAfterReading))
      {
        XEvent tmp;
        XPeekEvent(op->contr->gdisp,&tmp);
        repeat=tmp.type==Expose&&
               tmp.xexpose.window==op->contr->gevent.xexpose.window;
        if(repeat) XNextEvent(op->contr->gdisp,&tmp);
      }
#endif
      op->speed_left++;
      break;
    case MappingNotify:
      op->speed_left++;
      XRefreshKeyboardMapping(&op->contr->gevent.xmapping);
      repeat=1;
      break;
    case ClientMessage:
      cmev = (XClientMessageEvent *) & op->contr->gevent;
      if (cmev->message_type == Protocol_atom && cmev->data.l[0] == Kill_atom) {
        LOG(llevDebug,"Got WM_DELETE_WINDOW from %s.\n",op->name);
        if (op->map->in_memory == MAP_IN_MEMORY)
        {
          op->map->players--;
          if (!QUERY_FLAG(op,FLAG_REMOVED))
            remove_ob(op);
        }
        op->contr->state = 1;
        op->direction = 0;
        op->contr->count_left = 0;
        sprintf(buf, "%s lost connection.", op->name);
        info_all(buf, 5);
        strcpy(op->contr->killer, "lost connection");
        check_score(op);
        (void) save_player(op, 0);
        remove_lock(op->contr);
        info_flush();
        free_player(op->contr);
        if(first_player==NULL) {
	   if (server_mode != SERVER_ENABLED)
             exit(0);
	   XCloseDisplay(op->contr->gdisp);
	   LOG(llevDebug, "In server mode: continuing action.\n");
	 }
#if 0 /* This should crash if it doesn't return! -Frank */
	if (first_player==NULL)
#endif /* (Also, free_player() must be before the NULL test!) */
        return;
      }
      break;
    case ButtonPress:
      repeat=1;
      { /* Need some more local variables */
        int i=0,y=op->contr->gevent.xbutton.y,x=op->contr->gevent.xbutton.x,
            button=op->contr->gevent.xbutton.button;
        int dx=(x-2)/24-5,dy=(y-2)/24-5;
        object *inv;

        inv = op->inv;
        if(op->contr->state||QUERY_FLAG(op,FLAG_REMOVED))
          break;
        if(op->above) {
          remove_ob(op);
          insert_ob_in_map(op,op->map);
        }
        if(op->contr->gevent.xbutton.window==op->contr->win_game) {
          switch (button) {
          case 1:
            {
              if(dx<WINLEFT||dx>WINRIGHT||dy<WINUPPER||dy>WINLOWER)
                break;
              if(op->contr->blocked_los[dx+5][dy+5])
                break;
              look_at(op,dx,dy);
              op->contr->last_cmd=(dx||dy)?0:2;
            }
            break;
          case 2:
          case 3:
            if (button == 2)
              op->contr->fire_on=1;
            if (x<115)
               i = 0;
            else if (x>149)
               i = 6;
            else i =3;

            if (y>152)
              i += 2;
            else if (y>115)
              i++;

            switch (i) {
              case 0: move_player (op,8);break;
              case 1: move_player (op,7);break;
              case 2: move_player (op,6);break;
              case 3: move_player (op,1);break;
              case 5: move_player (op,5);break;
              case 6: move_player (op,2);break;
              case 7: move_player (op,3);break;
              case 8: move_player (op,4);break;
            }
            op->contr->fire_on=0;
            break;
          }
          break;
        } else if(op->contr->gevent.xbutton.window == op->contr->win_message) {
#ifdef USE_BUTTONS
          if (y>62) {
            for (i=0; i< NUMOPBUTTS+1; i++) {
              if (x>opX[i]&&x<opX[i]+64&&y>opY[i]&&y<opY[i]+18) {
                if  (i == CHANGE_button) {
                  if (button == 1)
                    change_spell (op,'+');
                  else if (button == 2)
                    change_spell (op,'-');
                } else if (i == APPLY_button) {
                  if (button == 1)
                    apply_below (op);
                  else if (button ==2) {
                    apply_inventory(op);
                    if (op->contr->show_what != show_all)
                      draw_all_inventory(op);
                  }
                } else if (i == TALK_button)
                  ;
                else if (i == PEACE_button)
                   parse_string (op,"peaceful");
              }
            }
          } else {
            for (i=0;i<NUMDIRBUTTS+1;i++)
              if(x>dirX[i]&&x<dirX[i]+dirW&&y>dirY[i]&&y<dirY[i]+18) {
                if (button == 2)
                  op->contr->fire_on=1;
                else if (button == 3)
                  op->contr->run_on=1;
                switch (i) {
                case NW_button: move_player (op,8);break;
                case NO_button: move_player (op,1);break;
                case NE_button: move_player (op,2);break;
                case WE_button: move_player (op,7);break;
                case BR_button: break;
                case EA_button: move_player (op,3);break;
                case SW_button: move_player (op,6);break;
                case SO_button: move_player (op,5);break;
                case SE_button: move_player (op,4);break;
                default:break;
                }
              op->contr->fire_on=0;
              op->contr->run_on=0;
            }
          }
#endif
        } else if(op->contr->gevent.xbutton.window==op->contr->win_inv) {
          int dy=(y-16)/24+op->contr->scroll_inv;
          object *tmp;
 
          if(x>270) {
            dy-=op->contr->scroll_inv;
            switch(button) {
            case 1:
              op->contr->scroll_inv-=dy>0?dy:1;
              draw_inventory(op);
              break;
            case 2:
              {
                int objects,scroll;
                object *tmp;

                for(tmp=inv,objects=0;tmp!=NULL;tmp=tmp->below)
                  if(!tmp->invisible && (op->contr->show_what == show_all ||
                     (op->contr->show_what == show_applied && QUERY_FLAG(tmp, FLAG_APPLIED)) ||
                     (op->contr->show_what == show_unapplied && !QUERY_FLAG(tmp, FLAG_APPLIED))))
                    objects++;
                scroll=(y-17)*objects/op->contr->barlength_inv;
                if(op->contr->scroll_inv==scroll)
                  break;
                op->contr->scroll_inv=scroll;
                draw_inventory(op);
                break;
              }
            case 3:
              op->contr->scroll_inv+=dy>0?dy:1;
              draw_inventory(op);
              break;
            }
            break;
          }
          if(dy<0)
            break;
          for(tmp=inv,i=0;tmp!=NULL;tmp=tmp->below) {
            while(tmp != NULL && (tmp->invisible ||
               (op->contr->show_what == show_applied && !QUERY_FLAG(tmp, FLAG_APPLIED)) ||
               (op->contr->show_what == show_unapplied && QUERY_FLAG(tmp, FLAG_APPLIED))))
              tmp = tmp->below;
            if (tmp == NULL)
              break;
            if (i++ == dy) {
              switch(button) {
              case 1:
                examine(op,tmp);
                break;
              case 2:
                apply(op,tmp);
                if (op->contr->show_what != show_all)
                  draw_all_inventory(op);
                break;
              case 3:
                drop(op,tmp);
                break;
              }
              break;
            }
          }
          break;
        } else if(op->contr->gevent.xbutton.window==op->contr->win_look) {
          int dy=(y-16)/24+op->contr->scroll_look;
          object *top, *tmp;
 
          if(x>270) {
            dy-=op->contr->scroll_look;
            switch(button) {
            case 1:
              op->contr->scroll_look-=dy>0?dy:1;
              draw_look(op);
              break;
            case 2:
              {
                int objects,scroll;

                /* Eneq(@csd.uu.se): ... */    

                for(tmp=(op->container?op->container->inv:op->below),objects=0;tmp!=NULL;tmp=tmp->below)
                  if(!(tmp->invisible))
                    objects++;
                scroll=(y-17)*objects/op->contr->barlength_look;
                if(op->contr->scroll_look==scroll)
                  break;
                op->contr->scroll_look=scroll;
                draw_look(op);
                break;
              }
            case 3:
              op->contr->scroll_look+=dy>0?dy:1;
              draw_look(op);
              break;
            }
            break;
          }
          if(dy<0)
            break;
          if(wall(op->map,op->x,op->y))
            break;
          
          /* Eneq(@csd.uu.se): Altered container-treatment, displays
             contents in look-window. */
          if (op->container)
            top=op->container->inv;
          else
            for(top=get_map_ob(op->map,op->x,op->y);
                top!=NULL&&top->above!=NULL&&top->above!=op;top=top->above);

          for(tmp=top,i=0;(tmp!=NULL)&&!(tmp->above&&
		QUERY_FLAG(tmp->above,FLAG_IS_FLOOR))&&
		i<dy;tmp=tmp->below)
            if(!tmp->invisible && tmp!=op && tmp->name!=NULL)
              i++;
          if(tmp==NULL||tmp==op)
            break;
          switch(button) {
          case 1:
            examine(op,tmp);
            break;
          case 2:
            apply(op,tmp);
	    draw_look(op);
            break;
          case 3:
            pick_up(op,tmp);
            break;
          }
          break;
        } else if(op->contr->gevent.xbutton.window==op->contr->win_info) {
          object *tmp;
          switch(button) {
          case 1:
          case 2:
            if(op->contr->last_cmd!=1) {
              inventory(op,NULL);
              op->contr->last_cmd=1;
              break;
            }
            for(tmp=inv,i=0;tmp!=NULL&&i<(y-13)/13;tmp=tmp->below,i++);
            if(tmp!=NULL) {
              if(button==1)
                apply(op,tmp);
              else
                drop(op,tmp);
              inventory(op,NULL);
              op->contr->last_cmd=1;
            }
            break;
          case 3:
            if(op->contr->last_cmd!=2) {
              look_at(op,0,0);
              op->contr->last_cmd=2;
              break;
            }
            if(wall(op->map,op->x,op->y))
              break;
            for(tmp=op->below,i=0;tmp!=NULL&&
                (QUERY_FLAG(op,FLAG_WIZ)||(tmp->type!=EARTHWALL&&tmp!=op))&&i<(y-13)/13;
                tmp=tmp->below,i++);
            if(tmp!=NULL) {
              pick_up(op,tmp);
              op->contr->last_cmd=0;
            }
            break;
          default:
            draw_info(op,"Undefined button.");
          }
        }
      }
      break;
    case KeyRelease:
      repeat=1;
      XLookupString(&op->contr->gevent.xkey,text,10,&op->contr->gkey,NULL);
      parse_key_release(op);
      break;
    case KeyPress:
      op->contr->count_left=0;
      if(!XLookupString(&op->contr->gevent.xkey,text,10,
         &op->contr->gkey,NULL)) {
        text[0]='\0';
      }
      switch(op->contr->state) {
      case ST_PLAYING:
        repeat=parse_key(op,text[0],op->contr->gevent.xkey.keycode,
			 op->contr->gkey);
        break;
      case ST_PLAY_AGAIN:
        repeat=0;
        if(text[0]=='q'||text[0]=='Q') {
          remove_friendly_object(op);
          leave(op->contr);
          return;
        }
        else if(text[0]=='a'||text[0]=='A') {
          object *tmp;
          remove_friendly_object(op);
          op->contr->ob=get_player(op->contr,op->map);
          tmp=op->contr->ob;
          add_friendly_object(tmp);
#ifdef SAVE_PLAYER
          tmp->contr->state=ST_GET_NAME;
          tmp->contr->writing=0;
          tmp->contr->password[0]='~';
          draw_info(tmp,"What is your name?");
          write_ch(tmp,':');
#else
          tmp->contr->state=ST_ROLL_STAT;
          draw_info(tmp,"Welcome back!");
          Roll_Again(tmp);
#endif
          if(tmp->name!=NULL)
            free_string(tmp->name);
          add_refcount(tmp->name = op->name);
          op->type=DEAD_OBJECT;
          free_object(op);
          op=tmp;
        }
        break;
      case ST_ROLL_STAT:
        switch(text[0]) {
#ifdef USE_SWAP_STATS
        case '1':
          if ( ! op->contr->Swap_First ) {
           op->contr->Swap_First=1;
           draw_info(op,"Str ->");
          } else
            Swap_Stat(op,1);
          break;
        case '2':
          if ( ! op->contr->Swap_First ) {
            op->contr->Swap_First=2;
            draw_info(op,"Dex ->");
          } else 
            Swap_Stat(op,2);
          break;
        case '3':
          if ( ! op->contr->Swap_First ) {
            op->contr->Swap_First=3;
            draw_info(op,"Con ->");
          } else
            Swap_Stat(op,3);
          break;
        case '4':
          if ( ! op->contr->Swap_First ) {
            op->contr->Swap_First=4;
            draw_info(op,"Int ->");
          } else
            Swap_Stat(op,4);
          break;
        case '5':
          if ( ! op->contr->Swap_First ) {
            op->contr->Swap_First=5;
            draw_info(op,"Wis ->");
          } else
            Swap_Stat(op,5);
          break;
        case '6':
          if ( ! op->contr->Swap_First ) {
            op->contr->Swap_First=6;
            draw_info(op,"Cha ->");
          } else
            Swap_Stat(op,6);
          break;
#endif /* USE_SWAP_STATS */
        case 'n':
        case 'N':
          op->contr->state = ST_CHANGE_CLASS;
	  SET_FLAG(op, FLAG_WIZ);
          if(op->map==NULL) {
            LOG(llevError,"Map == NULL in state 2\n");
            break;
          }
          op->x= -1; /* So that enter_exit will put us at startx/starty */
          enter_exit(op,NULL);
          if(op->contr->loading == NULL) {
            insert_ob_in_map(op,op->map);
            op->map->players--; /* Since add_player() already increased it */
          } else {
            op->contr->loading->players--;
            op->contr->removed = 0; /* Will insert pl. when map is loaded */
          }
          add_statbonus(op);
          draw_info(op,"Now choose a character.");
          draw_info(op,"Press any key to change outlook.");
          draw_info(op,"Press `d' when you're pleased.");
          repeat=0;
          break;
        case 'y':
        case 'Y':
          roll_stats(op);
          repeat=1;
          break;
        case 'q':
        case 'Q':
          draw_info(op,"Do you want to play again (a/q)?");
          op->contr->state=ST_PLAY_AGAIN;
#ifdef SAVE_PLAYER
          remove_lock(op->contr);
#endif
          repeat=1;
          break;
        default:
#ifndef USE_SWAP_STATS
          draw_info(op,"Yes, No or Quit. Roll again?");
#else
          draw_info(op,"Yes, No, Quit or 1-6.  Roll again?");
#endif /* USE_SWAP_STATS */
          repeat=0;
        }
        break;
      case ST_CHANGE_CLASS:
        repeat=0;
        if(text[0]=='q'||text[0]=='Q') {
          remove_ob(op);
          draw_info(op,"Do you want to play again (a/q)?");
          op->contr->state=ST_PLAY_AGAIN;
          break;
        }
        if(text[0]=='d'||text[0]=='D') {
          object *tmp=generate_treasure(7,0);

          if(tmp!=NULL)
            insert_ob_in_ob(tmp,op);
          op->contr->state=ST_PLAYING;
          start_info(op);
	  CLEAR_FLAG(op, FLAG_WIZ);
          give_initial_items(op);
          break;
        }
        while(!repeat) {
	  char *name = add_string (op->name);
	  int x = op->x, y = op->y;
          remove_statbonus(op);
	  remove_ob (op);
	  op->arch = get_player_archetype(op->arch);
	  copy_object (&op->arch->clone, op);
	  op->stats = op->contr->orig_stats;
	  free_string (op->name);
	  op->name = name;
	  op->x = x;
	  op->y = y;
	  insert_ob_in_map (op, op->map);

/*	  op->stats.Str = op->contr->orig_stats.Str;
	  op->stats.Dex = op->contr->orig_stats.Dex;
	  op->stats.Con = op->contr->orig_stats.Con;
	  op->stats.Wis = op->contr->orig_stats.Wis;
	  op->stats.Cha = op->contr->orig_stats.Cha;
	  op->stats.Int = op->contr->orig_stats.Int;
	  op->stats.hp = op->contr->orig_stats.hp;*/
#if 0
	  op->contr->face = (op->contr->face + 1) % NROFPFACES;
          op->face = classarch[op->contr->face]->faces[0];
          strncpy(op->contr->title,classname[op->contr->face],MAX_NAME);
	  if (op->race)
	    free_string (op->race);
	  op->race = add_string (classname[op->contr->face]);
          add_statbonus(op);

	  op->face = op->arch->clone.face;
	  if (op->race)
	    free_string (op->race);
	  op->race = add_string (op->arch->clone.race);
#endif
          strncpy(op->contr->title,op->arch->clone.name,MAX_NAME);
          add_statbonus(op);
          repeat=allowed_class(op);
        }
        repeat=0;
        update_object(op);
        fix_player(op);
        op->stats.hp=op->stats.maxhp;
        op->stats.sp=op->stats.maxsp;
        op->contr->last_value= -1;
        draw_stats(op);
        break;
      case ST_CONFIRM_QUIT:
        if(text[0]!='y'&&text[0]!='Y'&&text[0]!='q'&&text[0]!='Q') {
          op->contr->state=ST_PLAYING;
          draw_info(op,"OK, continuing to play.");
          break;
        }
        terminate_all_pets(op);
        remove_ob(op);
        op->contr->state=ST_PLAY_AGAIN;
        op->direction=0;
        op->contr->count_left=0;
        sprintf(buf,"%s quits the game.",op->name);
        info_all(buf,5);
        info_flush();
        strcpy(op->contr->killer,"quit");
        check_score(op);
#ifdef SIMPLE_PARTY_SYSTEM
    op->contr->party_number=(-1);
#endif /* SIMPLE_PARTY_SYSTEM */
#ifdef SET_TITLE
    op->contr->own_title[0]='\0';
#endif /* SET_TITLE */
	load_default_keys(op->contr);
#ifdef SAVE_PLAYER
        if(!QUERY_FLAG(op,FLAG_WAS_WIZ)) {
          sprintf(buf,"%s/%s/%s.pl",LibDir,PlayerDir,op->name);
          if(unlink(buf)== -1 && debug)
            perror("crossfire (delete character)");
        }
        remove_lock(op->contr);
#endif
        draw_info(op,"Do you want to play again(a/q)?");
        break;
      case ST_CONFIGURE:
        repeat=1;
        configure_keys(op, op->contr->gevent.xkey.keycode, op->contr->gkey);
        break;
#ifdef SAVE_PLAYER
      case ST_GET_NAME: /* Waiting for player name */
        receive_player_name(op,text[0]);
        repeat=1;
        break;
      case ST_GET_PASSWORD: /* Waiting for player password */
        receive_player_password(op,text[0]);
        repeat=1;
        break;
      case ST_CONFIRM_PASSWORD: /* Confirm new password */
        receive_player_password(op,text[0]);
        repeat=1;
        break;
#endif
#ifdef SHOP_LISTINGS
	case ST_MENU_MORE:	/* more items to be listed */
		shop_listing_more(op);
		break;
#endif

      default:
        LOG(llevError,"Illegal state: %d\n",op->contr->state);
      }
      break;
    default:
      repeat=1;
    }
  }
  /* The following lines deletes any keypresses left in the queue: */
/*
  while(XEventsQueued(op->contr->gdisp, QueuedAfterReading))
    XNextEvent(op->contr->gdisp,&op->contr->gevent);
*/
  while(XEventsQueued(op->contr->gdisp, QueuedAfterReading)) {
    XPeekEvent(op->contr->gdisp,&op->contr->gevent);
    switch(op->contr->gevent.type) {
    case KeyPress:
      XNextEvent(op->contr->gdisp,&op->contr->gevent);
      XLookupString(&op->contr->gevent.xkey,text,10,&op->contr->gkey,NULL);
    /* Some keypresses we can't just throw away...: */
      if((op->contr->gevent.xkey.keycode==op->contr->firekey[0] &&
	  op->contr->gkey==op->contr->firekeysym[0]) ||
         (op->contr->gevent.xkey.keycode==op->contr->firekey[1] &&
	  op->contr->gkey==op->contr->firekeysym[1]))
        op->contr->fire_on=1;
      if((op->contr->gevent.xkey.keycode==op->contr->runkey[0] &&
	  op->contr->gkey==op->contr->runkeysym[0]) ||
         (op->contr->gevent.xkey.keycode==op->contr->runkey[1] &&
	  op->contr->gkey==op->contr->runkeysym[1]))
        op->contr->run_on=1;
      continue;
    case KeyRelease:
      XNextEvent(op->contr->gdisp,&op->contr->gevent);
      XLookupString(&op->contr->gevent.xkey,text,10,&op->contr->gkey,NULL);
      parse_key_release(op);
      continue;
    }
    break;
  }
}

int save_life(object *op) {
  object *tmp;
  char buf[MAX_BUF];
  if(!QUERY_FLAG(op,FLAG_LIFESAVE))
    return 0;
  for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
    if(QUERY_FLAG(tmp, FLAG_APPLIED)&&QUERY_FLAG(tmp,FLAG_LIFESAVE)) {
#ifdef SOUND_EFFECTS
      play_sound_map(op->map, op->x, op->y, SOUND_OB_EVAPORATE);
#endif
      sprintf(buf,"Your %s vibrates violently, then evaporates.",
              query_name(tmp));
      draw_info(op,buf);
      remove_ob(tmp);
      free_object(tmp);
      CLEAR_FLAG(op, FLAG_LIFESAVE);
      if(op->stats.hp<0)
        op->stats.hp = op->stats.maxhp;
      if(op->stats.food<0)
        op->stats.food = 999;
      return 1;
    }
  LOG(llevError,"Error: LIFESAVE set without applied object.\n");
  CLEAR_FLAG(op, FLAG_LIFESAVE);
  return 0;
}

void do_some_living(object *op) {
  char buf[MAX_BUF];
  object *tmp;
  int last_food=op->stats.food;
  int gen_hp, gen_sp;
  int x,y;  /*  these are for resurrection */
  mapstruct *map;  /*  this is for resurrection */

  if(!op->contr->state) {
    gen_hp=(op->contr->gen_hp+1)*op->stats.maxhp;
    gen_sp=(op->contr->gen_sp+1)*op->stats.maxsp;
    if(op->contr->golem==NULL&&--op->last_sp<0) {
      if(op->stats.sp<op->stats.maxsp)
        op->stats.sp++,op->stats.food--;
      op->last_sp=2000/(gen_sp<20 ? 30 : gen_sp+10);
      for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
        if((tmp->type==ARMOUR||tmp->type==HELMET||tmp->type==BOOTS||
            tmp->type==SHIELD||tmp->type==GLOVES||tmp->type==GIRDLE||
            tmp->type==AMULET)
           &&QUERY_FLAG(tmp, FLAG_APPLIED))
          op->last_sp+=ARMOUR_SPELLS(tmp);
    }
    if(--op->last_heal<0) {
      if(op->stats.hp<op->stats.maxhp)
        op->stats.hp++,op->stats.food--;
      op->last_heal=1200/(gen_hp<20 ? 30 : gen_hp+10);
      if(op->contr->digestion<0)
        op->stats.food+=op->contr->digestion;
      else if(op->contr->digestion>0&&RANDOM()%(1+op->contr->digestion))
        op->stats.food=last_food;
    }
    if(--op->last_eat<0) {
      int bonus=op->contr->digestion>0?op->contr->digestion:0,
          penalty=op->contr->digestion<0?-op->contr->digestion:0;
      op->last_eat=25*(1+bonus)/(op->contr->gen_hp+penalty+1);
      op->stats.food--;
    }
  }
  if(!op->contr->state&&op->stats.food<0&&op->stats.hp>=0) {
    object *tmp;
    for(tmp=op->inv;tmp!=NULL;tmp=tmp->below)
      if(!QUERY_FLAG(tmp, FLAG_UNPAID)&&
         (tmp->type==FOOD||tmp->type==DRINK||tmp->type==POISON))
      {
        draw_info(op,"You blindly grab for a bite of food.");
        apply(op,tmp);
        if (op->contr->show_what != show_all)
          draw_all_inventory(op);
        if(op->stats.food>=0||op->stats.hp<0)
          break;
      }
  }
  while(op->stats.food<0&&op->stats.hp>0)
    op->stats.food++,op->stats.hp--;

  if (!op->contr->state&&!QUERY_FLAG(op,FLAG_WIZ)&&(op->stats.hp<0||op->stats.food<0)) {
    if(save_life(op))
      return;
    if(op->stats.food<0) {
#ifdef EXPLORE_MODE
      if (op->contr->explore) {
      draw_info(op,"You would have starved, but you are");
      draw_info(op,"in explore mode, so...");
      op->stats.food=999;
      return;
      }
#endif /* EXPLORE_MODE */
      sprintf(buf,"%s starved to death.",op->name);
      strcpy(op->contr->killer,"starvation");
    }
    else
#ifdef EXPLORE_MODE
      if (op->contr->explore) {
      draw_info(op,"You would have died, but you are");
      draw_info(op,"in explore mode, so...");
      op->stats.hp=op->stats.maxhp;
      return;
      }
#endif /* EXPLORE_MODE */
      sprintf(buf,"%s died.",op->name);
#ifdef SOUND_EFFECTS
      play_sound_player_only(op->contr, SOUND_PLAYER_DIES);
#endif
    /*  save the map location for corpse, gravestone*/
    x=op->x;y=op->y;map=op->map;


#ifdef NOT_PERMADEATH
/****************************************************************************/
/* Patch: NOT_PERMADEATH                Author: Charles Henrich             */
/* Email: henrich@crh.cl.msu.edu        Date  : April 9, 1993               */
/*                                                                          */
/* Purpose: This patch changes death from being a permanent, very painful   */
/*          thing, to a painful, but survivable thing.  More mudlike in     */
/*          nature.  With this patch defined, when a player dies, they will */
/*          permanently lose one point off of a random stat, as well as     */
/*          losing 20% of their experience points.  Then they are whisked   */
/*          to the start map.  Although this sounds very nice here, it is   */
/*          still REAL painful to die, 20% of a million is alot!            */
/*                                                                          */
/****************************************************************************/

 /**************************************/
 /*                                    */
 /* Pick a stat, and steal on pt from  */
 /* it...                              */
 /*                                    */
 /**************************************/

    switch(RANDOM()%6)
        {
        case 0:
            {
            if(op->stats.Dex > 1) op->stats.Dex--;
            op->contr->orig_stats.Dex=op->stats.Dex;
            break;
            }

        case 1:
            {
            if(op->stats.Con > 1) op->stats.Con--;
            op->contr->orig_stats.Con=op->stats.Con;
            break;
            }

        case 2:
            {
            if(op->stats.Int > 1) op->stats.Int--;
            op->contr->orig_stats.Int=op->stats.Int;
            break;
            }

        case 3:
            {
            if(op->stats.Cha > 1) op->stats.Cha--;
            op->contr->orig_stats.Cha=op->stats.Cha;
            break;
            }

        case 4:
            {
            if(op->stats.Str > 1) op->stats.Str--;
            op->contr->orig_stats.Str=op->stats.Str;
            break;
            }
        case 5:
            {
            if(op->stats.Wis > 1) op->stats.Wis--;
            op->contr->orig_stats.Wis=op->stats.Wis;
            break;
            }
        }

    tmp=arch_to_object(find_archetype("gravestone"));

 /**************************************/
 /*                                    */
 /* Lets make up a gravestone to put   */
 /* here... We put experience lost on  */
 /* it for kicks....                   */
 /*                                    */
 /**************************************/

    sprintf(buf,"%s's gravestone",op->name);
    tmp->name=add_string(buf);
    sprintf(buf,"RIP\nHere rests the hero %s the %s,\n"
                "who lost %d experience when killed\n"
                "by %s.\n",
                op->name, op->contr->title, (int)(op->stats.exp * 0.20),
                op->contr->killer);
    tmp->msg = add_string(buf);
    tmp->x=op->x,tmp->y=op->y;
    insert_ob_in_map(tmp,op->map);

 /**************************************/
 /*                                    */
 /* Move the player to the beginning   */
 /* map....                            */
 /*                                    */
 /**************************************/

    tmp=get_object();
    tmp->map = ready_map_name(first_map_path,0);  
    EXIT_PATH(tmp) = first_map_path;
    EXIT_X(tmp) = -1;
    EXIT_Y(tmp) = -1;
    enter_exit(op,tmp);

/* commenting this out seems to fix core dumps on some systems. */
/*    free_object(tmp);*/

 /**************************************/
 /*                                    */
 /* Subtract the experience points,    */
 /* if we died cause of food, give us  */
 /* food, and reset HP's...            */
 /*                                    */
 /**************************************/

    /* remove any poisoning the character may be suffering. */
    tmp = present_arch_in_ob(find_archetype("poisoning"), op);
    if (tmp!=NULL)
	tmp->stats.food =1;
	
    add_exp(op, (op->stats.exp * -0.20));
    if(op->stats.food < 0) op->stats.food = 500;
    op->stats.hp = op->stats.maxhp;


 /**************************************/
 /*                                    */
 /* Repaint the characters inv, and    */
 /* stats, and show a nasty message ;) */
 /*                                    */
 /**************************************/

    draw_stats(op);
    draw_all_inventory(op);
    draw_info(op,"YOU HAVE DIED.");
    return;
#endif

#ifdef SIMPLE_PARTY_SYSTEM
    op->contr->party_number=(-1);
#endif /* SIMPLE_PARTY_SYSTEM */
#ifdef SET_TITLE
    op->contr->own_title[0]='\0';
#endif /* SET_TITLE */
    op->contr->count_left=0;
    load_default_keys(op->contr);
    info_all(buf,1);
    info_flush();
    check_score(op);
    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;
    }
    op->contr->freeze_inv=1;
    op->contr->freeze_look=1;
    loot_object(op); /* Remove some of the items for good */
    op->contr->freeze_inv=0;
    op->contr->freeze_look=0;
    remove_ob(op);
    op->direction=0;
#ifdef SAVE_PLAYER
    if(!QUERY_FLAG(op,FLAG_WAS_WIZ)&&op->stats.exp) {
      delete_character(op->name);
#ifndef NOT_PERMADEATH
        /* save playerfile sans equipment when player dies
        ** then save it as player.pl.dead so that future resurrection
        ** type spells will work on them nicely
        */
      op->stats.hp = op->stats.maxhp;
      op->stats.food = 999;
      

      /*  set the location of where the person will reappear when  */
      if(op->map!=NULL)
	strcpy(op->map->path, EMERGENCY_MAPPATH);
      op->x = EMERGENCY_X;
      op->y = EMERGENCY_Y;
      save_player(op,0);
      /* please see resurrection.c: peterm */
      dead_player(op);
#endif
    }
    remove_lock(op->contr);
#endif
    op->contr->state=ST_PLAY_AGAIN;
    draw_info(op,"Do you want to play again (a/q)?");
#ifdef NOT_PERMADEATH 
    tmp=arch_to_object(find_archetype("gravestone"));
    sprintf(buf,"%s's gravestone",op->name);
    tmp->name=add_string(buf);
    sprintf(buf,"RIP\nHere rests the hero %s the %s,\nwho was killed by %s.\n",
            op->name, op->contr->title, op->contr->killer);
    tmp->msg = add_string(buf);
    tmp->x=x,tmp->y=y;
    insert_ob_in_map(tmp,map);
#endif
#ifndef NOT_PERMADEATH
    /*  peterm:  added to create a corpse at deathsite.  */
    tmp=arch_to_object(find_archetype("corpse"));
    sprintf(buf,"%s",op->name);
    tmp->name=add_string(buf);
    tmp->immune=262143;  /*  immune to everything, indestructible player */
                                /*      corpse.*/
    tmp->level=op->level;
    tmp->x=x;tmp->y=y;
    insert_ob_in_map(tmp,map);
#endif
  }
}

void loot_object(object *op) { /* Grab and destroy some treasure */
  object *tmp,*tmp2,*next;
  if (op->container) { /* close open sack first */
      tmp = op->container;
      if (tmp->below!=NULL)
          tmp->below->above=NULL;
      op->container->inv=tmp->below;
      tmp->below=op->container=NULL;
      free_object(tmp);
  }
  for(tmp=op->inv;tmp!=NULL;tmp=next) {
    next=tmp->below;
    remove_ob(tmp);
    tmp->x=op->x,tmp->y=op->y;
    if (QUERY_FLAG(tmp, FLAG_CONTAINER)) { /* empty container to ground */
        loot_object(tmp);
    }
    if(QUERY_FLAG(tmp, FLAG_STARTEQUIP)||QUERY_FLAG(tmp,FLAG_NO_DROP)
    ||!(RANDOM()%3)) {
      if(tmp->nrof>1) {
        tmp2=get_split_ob(tmp,1+RANDOM()%(tmp->nrof-1));
        free_object(tmp2);
        insert_ob_in_map(tmp,op->map);
      } else
        free_object(tmp);
    } else
      insert_ob_in_map(tmp,op->map);
  }
}

/*
 * fix_weight(): Check recursively the weight of all players, and fix
 * what needs to be fixed.  Refresh windows and fix speed if anything
 * was changed.
 * sum_weight() is a recursive function which calculates this.
 */

signed long sum_weight(object *op) {
  signed long sum;
  object *inv;
  for(sum = 0, inv = op->inv; inv != NULL; inv = inv->below)
    sum += sum_weight(inv) + inv->nrof ? inv->weight * inv->nrof : inv->weight;
  if (op->bonus)
    sum = (sum * (100 - op->bonus))/100;
  if(op->carrying != sum)
    op->carrying = sum;
  return sum;
}

void fix_weight() {
  player *pl;
  for (pl = first_player; pl != NULL; pl = pl->next) {
    int old = pl->ob->carrying, sum = sum_weight(pl->ob);
    if(old == sum)
      continue;
    fix_player(pl->ob);
    draw_all_inventory(pl->ob);
    LOG(llevDebug,"Fixed inventory in %s (%d -> %d)\n",
        pl->ob->name, old, sum);
  }
}

void fix_luck() {
  player *pl;
  for (pl = first_player; pl != NULL; pl = pl->next)
    if (!pl->ob->contr->state)
      change_luck(pl->ob, 0);
}