Crossfire Mailing List Archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug in player.c: fix
- To: crossfire (at) ifi.uio.no
- Subject: bug in player.c: fix
- From: Peter Mardahl <>
- Date: Tue, 8 Mar 1994 13:56:15 -0800
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 .
*/
#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 ()
*/
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 ");
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: 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);
}