Crossfire Server, Branch 1.12  R12190
player.c
Go to the documentation of this file.
00001 /*
00002  * static char *rcsid_player_c =
00003  *   "$Id: player.c 11578 2009-02-23 22:02:27Z lalo $";
00004  */
00005 
00006 /*
00007     CrossFire, A Multiplayer game for X-windows
00008 
00009     Copyright (C) 2002,2006-2007 Mark Wedel & Crossfire Development Team
00010     Copyright (C) 1992 Frank Tore Johansen
00011 
00012     This program is free software; you can redistribute it and/or modify
00013     it under the terms of the GNU General Public License as published by
00014     the Free Software Foundation; either version 2 of the License, or
00015     (at your option) any later version.
00016 
00017     This program is distributed in the hope that it will be useful,
00018     but WITHOUT ANY WARRANTY; without even the implied warranty of
00019     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020     GNU General Public License for more details.
00021 
00022     You should have received a copy of the GNU General Public License
00023     along with this program; if not, write to the Free Software
00024     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00025 
00026     The author can be reached via e-mail to crossfire-devel@real-time.com
00027 */
00028 
00035 #include <global.h>
00036 #include <assert.h>
00037 #ifndef WIN32 /* ---win32 remove headers */
00038 #include <pwd.h>
00039 #endif
00040 #ifndef __CEXTRACT__
00041 #include <sproto.h>
00042 #endif
00043 #include <sounds.h>
00044 #include <living.h>
00045 #include <object.h>
00046 #include <spells.h>
00047 #include <skills.h>
00048 #include <newclient.h>
00049 
00050 static archetype *get_player_archetype(archetype *at);
00051 
00052 static int action_makes_visible(object *op);
00053 
00062 player *find_player(const char *plname) {
00063     player *pl;
00064     char name[MAX_BUF];
00065 
00066     for (pl = first_player; pl != NULL; pl = pl->next) {
00067         if (pl->ob != NULL) {
00068             query_name(pl->ob, name, MAX_BUF);
00069             if (!strcmp(name, plname))
00070                 return pl;
00071         }
00072     }
00073     return NULL;
00074 }
00075 
00084 player *find_player_partial_name(const char *plname) {
00085     player *pl;
00086     player *found = NULL;
00087     size_t namelen = strlen(plname);
00088 
00089     for (pl = first_player; pl != NULL; pl = pl->next) {
00090         if (strlen(pl->ob->name) < namelen)
00091             continue;
00092 
00093         if (!strcmp(pl->ob->name, plname))
00094             return pl;
00095 
00096         if (!strncasecmp(pl->ob->name, plname, namelen)) {
00097             if (found)
00098                 return NULL;
00099 
00100             found = pl;
00101         }
00102     }
00103     return found;
00104 }
00105 
00112 void display_motd(const object *op) {
00113     char buf[MAX_BUF];
00114     char motd[HUGE_BUF];
00115     FILE *fp;
00116     int comp;
00117     int size;
00118 
00119     snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.motd);
00120     if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
00121         return;
00122     }
00123     motd[0] = '\0';
00124     size = 0;
00125     while (fgets(buf, MAX_BUF, fp) != NULL) {
00126         if (*buf == '#')
00127             continue;
00128         strncat(motd+size, buf, HUGE_BUF-size);
00129         size += strlen(buf);
00130     }
00131     draw_ext_info(NDI_UNIQUE|NDI_GREEN, 0, op, MSG_TYPE_MOTD, MSG_SUBTYPE_NONE,
00132                   motd, NULL);
00133     close_and_delete(fp, comp);
00134 }
00135 
00142 void send_rules(const object *op) {
00143     char buf[MAX_BUF];
00144     char rules[HUGE_BUF];
00145     FILE *fp;
00146     int comp;
00147     int size;
00148 
00149     snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.rules);
00150     if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL) {
00151         return;
00152     }
00153     rules[0] = '\0';
00154     size = 0;
00155     while (fgets(buf, MAX_BUF, fp) != NULL) {
00156         if (*buf == '#')
00157             continue;
00158         if (size+strlen(buf) >= HUGE_BUF) {
00159             LOG(llevDebug, "Warning, rules size is > %d bytes.\n", HUGE_BUF);
00160             break;
00161         }
00162         strncat(rules+size, buf, HUGE_BUF-size);
00163         size += strlen(buf);
00164     }
00165     draw_ext_info(NDI_UNIQUE|NDI_GREEN, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_RULES,
00166                   rules, NULL);
00167     close_and_delete(fp, comp);
00168 }
00169 
00176 void send_news(const object *op) {
00177     char buf[MAX_BUF];
00178     char news[HUGE_BUF];
00179     char subject[MAX_BUF];
00180     FILE *fp;
00181     int comp;
00182     int size;
00183 
00184     snprintf(buf, sizeof(buf), "%s/%s", settings.confdir, settings.news);
00185     if ((fp = open_and_uncompress(buf, 0, &comp)) == NULL)
00186         return;
00187     news[0] = '\0';
00188     subject[0] = '\0';
00189     size = 0;
00190     while (fgets(buf, MAX_BUF, fp) != NULL) {
00191         if (*buf == '#')
00192             continue;
00193         if (*buf == '%') { /* send one news */
00194             if (size > 0)
00195                 draw_ext_info_format(NDI_UNIQUE|NDI_GREEN, 0, op,
00196                                      MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS,
00197                                      "%s:\n%s",
00198                                      "%s:\n%s",
00199                                      subject, news); /*send previously read news*/
00200             strcpy(subject, buf+1);
00201             strip_endline(subject);
00202             size = 0;
00203             news[0] = '\0';
00204         } else {
00205             if (size+strlen(buf) >= HUGE_BUF) {
00206                 LOG(llevDebug, "Warning, one news item has size > %d bytes.\n", HUGE_BUF);
00207                 break;
00208             }
00209             strncat(news+size, buf, HUGE_BUF-size);
00210             size += strlen(buf);
00211         }
00212     }
00213 
00214     draw_ext_info_format(NDI_UNIQUE|NDI_GREEN, 0, op,
00215                          MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_NEWS,
00216                          "%s:\n%s",
00217                          "%s:\n%s",
00218                          subject, news);
00219     close_and_delete(fp, comp);
00220 }
00221 
00230 int playername_ok(const char *cp) {
00231     /* Don't allow - or _ as first character in the name */
00232     if (*cp == '-' || *cp == '_')
00233         return 0;
00234 
00235     for (; *cp != '\0'; cp++)
00236         if (!((*cp >= 'a' && *cp <= 'z') || (*cp >= 'A' && *cp <= 'Z'))
00237         && *cp != '-'
00238         && *cp != '_')
00239             return 0;
00240     return 1;
00241 }
00242 
00258 static player *get_player(player *p) {
00259     object *op = arch_to_object(get_player_archetype(NULL));
00260     int i;
00261 
00262     if (!p) {
00263         player *tmp;
00264 
00265         p = (player *)malloc(sizeof(player));
00266         if (p == NULL)
00267             fatal(OUT_OF_MEMORY);
00268 
00269         /* This adds the player in the linked list.  There is extra
00270          * complexity here because we want to add the new player at the
00271          * end of the list - there is in fact no compelling reason that
00272          * that needs to be done except for things like output of
00273          * 'who'.
00274          */
00275         tmp = first_player;
00276         while (tmp != NULL && tmp->next != NULL)
00277             tmp = tmp->next;
00278         if (tmp != NULL)
00279             tmp->next = p;
00280         else
00281             first_player = p;
00282 
00283         p->next = NULL;
00284     } else {
00285         /* Only needed when reusing existing player. */
00286         clear_player(p);
00287     }
00288 
00289     /* Clears basically the entire player structure except
00290      * for next and socket.
00291      */
00292     memset((void *)((char *)p+offsetof(player, maplevel)), 0, sizeof(player)-offsetof(player, maplevel));
00293 
00294     /* There are some elements we want initialized to non zero value -
00295      * we deal with that below this point.
00296      */
00297     p->party = NULL;
00298     p->rejoin_party = party_rejoin_if_exists;
00299     p->outputs_sync = 16;  /* Every 2 seconds */
00300     p->outputs_count = 1;  /* Keeps present behaviour */
00301     p->unapply = unapply_nochoice;
00302     p->Swap_First = -1;
00303 
00304 #ifdef AUTOSAVE
00305     p->last_save_tick = 9999999;
00306 #endif
00307 
00308     strcpy(p->savebed_map, first_map_path);  /* Init. respawn position */
00309 
00310     op->contr = p; /* this aren't yet in archetype */
00311     p->ob = op;
00312     op->speed_left = 0.5;
00313     op->speed = 1.0;
00314     op->direction = 5;     /* So player faces south */
00315     op->stats.wc = 2;
00316     op->run_away = 25; /* Then we panick... */
00317     p->socket.monitor_spells = 0; /* this needs to be set before roll_stats() as it calls fix_object() that sends the spells. */
00318 
00319     roll_stats(op);
00320     p->state = ST_ROLL_STAT;
00321     clear_los(op);
00322 
00323     p->gen_sp_armour = 10;
00324     p->last_speed = -1;
00325     p->shoottype = range_none;
00326     p->bowtype = bow_normal;
00327     p->petmode = pet_normal;
00328     p->listening = 10;
00329     p->last_weapon_sp = -1;
00330     p->peaceful = 1;   /* default peaceful */
00331     p->do_los = 1;
00332     p->explore = 0;
00333     p->no_shout = 0;   /* default can shout */
00334     p->language = 0;
00335 
00336     strncpy(p->title, op->arch->clone.name, sizeof(p->title)-1);
00337     p->title[sizeof(p->title)-1] = '\0';
00338     op->race = add_string(op->arch->clone.race);
00339 
00340     CLEAR_FLAG(op, FLAG_READY_SKILL);
00341 
00342     /* we need to clear these to -1 and not zero - otherwise,
00343      * if a player quits and starts a new character, we wont
00344      * send new values to the client, as things like exp start
00345      * at zero.
00346      */
00347     for (i = 0; i < NUM_SKILLS; i++) {
00348         p->last_skill_exp[i] = -1;
00349         p->last_skill_ob[i] = NULL;
00350     }
00351     for (i = 0; i < NROFATTACKS; i++) {
00352         p->last_resist[i] = -1;
00353     }
00354     p->last_stats.exp = -1;
00355     p->last_weight = (uint32)-1;
00356 
00357     p->socket.update_look = 0;
00358     p->socket.look_position = 0;
00359     return p;
00360 }
00361 
00368 static void set_first_map(object *op) {
00369     strcpy(op->contr->maplevel, first_map_path);
00370     op->x = -1;
00371     op->y = -1;
00372     enter_exit(op, NULL);
00373 }
00374 
00383 void add_player(socket_struct *ns) {
00384     player *p;
00385 
00386     p = get_player(NULL);
00387     memcpy(&p->socket, ns, sizeof(socket_struct));
00388 
00389     /* The memcpy above copies the reference to faces sent.  So we need to clear
00390      * that pointer in ns, otherwise we get a double free.
00391      */
00392     ns->faces_sent = NULL;
00393 
00394     if (p->socket.faces_sent == NULL)
00395         fatal(OUT_OF_MEMORY);
00396 
00397     /* Needed because the socket we just copied over needs to be cleared.
00398      * Note that this can result in a client reset if there is partial data
00399      * on the uncoming socket.
00400      */
00401     SockList_ResetRead(&p->socket.inbuf);
00402     set_first_map(p->ob);
00403 
00404     CLEAR_FLAG(p->ob, FLAG_FRIENDLY);
00405     add_friendly_object(p->ob);
00406     send_rules(p->ob);
00407     send_news(p->ob);
00408     display_motd(p->ob);
00409     get_name(p->ob);
00410 }
00411 
00422 static archetype *get_player_archetype(archetype *at) {
00423     archetype *start = at;
00424 
00425     for (;;) {
00426         if (at == NULL || at->next == NULL)
00427             at = first_archetype;
00428         else
00429             at = at->next;
00430         if (at->clone.type == PLAYER)
00431             return at;
00432         if (at == start) {
00433             LOG(llevError, "No Player archetypes\n");
00434             exit(-1);
00435         }
00436     }
00437 }
00438 
00447 object *get_nearest_player(object *mon) {
00448     object *op = NULL;
00449     player *pl = NULL;
00450     objectlink *ol;
00451     unsigned lastdist;
00452     rv_vector rv;
00453 
00454     for (ol = first_friendly_object, lastdist = 1000; ol != NULL; ol = ol->next) {
00455         /* We should not find free objects on this friendly list, but it
00456          * does periodically happen.  Given that, lets deal with it.
00457          * While unlikely, it is possible the next object on the friendly
00458          * list is also free, so encapsulate this in a while loop.
00459          */
00460         while (QUERY_FLAG(ol->ob, FLAG_FREED) || !QUERY_FLAG(ol->ob, FLAG_FRIENDLY)) {
00461             object *tmp = ol->ob;
00462 
00463             /* Can't do much more other than log the fact, because the object
00464              * itself will have been cleared.
00465              */
00466             LOG(llevDebug, "get_nearest_player: Found free/non friendly object on friendly list\n");
00467             ol = ol->next;
00468             remove_friendly_object(tmp);
00469             if (!ol)
00470                 return op;
00471         }
00472 
00473         /* Remove special check for player from this.  First, it looks to cause
00474          * some crashes (ol->ob->contr not set properly?), but secondly, a more
00475          * complicated method of state checking would be needed in any case -
00476          * as it was, a clever player could type quit, and the function would
00477          * skip them over while waiting for confirmation.  Remove
00478          * on_same_map check, as can_detect_enemy also does this
00479          */
00480         if (!can_detect_enemy(mon, ol->ob, &rv))
00481             continue;
00482 
00483         if (lastdist > rv.distance) {
00484             op = ol->ob;
00485             lastdist = rv.distance;
00486         }
00487     }
00488     for (pl = first_player; pl != NULL; pl = pl->next) {
00489         if (can_detect_enemy(mon, pl->ob, &rv)) {
00490             if (lastdist > rv.distance) {
00491                 op = pl->ob;
00492                 lastdist = rv.distance;
00493             }
00494         }
00495     }
00496     return op;
00497 }
00498 
00508 #define DETOUR_AMOUNT 2
00509 
00523 #define MAX_SPACES 50
00524 
00556 int path_to_player(object *mon, object *pl, unsigned mindiff) {
00557     rv_vector rv;
00558     sint16  x, y;
00559     int lastx, lasty, dir, i, diff, firstdir = 0, lastdir, max = MAX_SPACES, mflags, blocked;
00560     mapstruct *m, *lastmap;
00561 
00562     get_rangevector(mon, pl, &rv, 0);
00563 
00564     if (rv.distance < mindiff)
00565             return 0;
00566 
00567     x = mon->x;
00568     y = mon->y;
00569     m = mon->map;
00570     dir = rv.direction;
00571     lastdir = firstdir = rv.direction; /* perhaps we stand next to pl, init firstdir too */
00572     diff = MAX(FABS(rv.distance_x), FABS(rv.distance_y));
00573     /* If we can't solve it within the search distance, return now. */
00574     if (diff > max)
00575         return 0;
00576     while (diff > 1 && max > 0) {
00577         lastx = x;
00578         lasty = y;
00579         lastmap = m;
00580         x = lastx+freearr_x[dir];
00581         y = lasty+freearr_y[dir];
00582 
00583         mflags = get_map_flags(m, &m, x, y, &x, &y);
00584         blocked = (mflags&P_OUT_OF_MAP) ? MOVE_ALL : GET_MAP_MOVE_BLOCK(m, x, y);
00585 
00586         /* Space is blocked - try changing direction a little */
00587         if ((mflags&P_OUT_OF_MAP)
00588         || ((OB_TYPE_MOVE_BLOCK(mon, blocked) || (mflags&P_IS_ALIVE))
00589         && (m == mon->map && blocked_link(mon, m, x, y)))) {
00590             /* recalculate direction from last good location.  Possible
00591              * we were not traversing ideal location before.
00592              */
00593             get_rangevector_from_mapcoord(lastmap, lastx, lasty, pl, &rv, 0);
00594             if (rv.direction != dir) {
00595                 /* OK - says direction should be different - lets reset the
00596                  * the values so it will try again.
00597                  */
00598                 x = lastx;
00599                 y = lasty;
00600                 m = lastmap;
00601                 dir = firstdir = rv.direction;
00602             } else {
00603                 /* direct path is blocked - try taking a side step to
00604                  * either the left or right.
00605                  * Note increase the values in the loop below to be
00606                  * more than -1/1 respectively will mean the monster takes
00607                  * bigger detour.  Have to be careful about these values getting
00608                  * too big (3 or maybe 4 or higher) as the monster may just try
00609                  * stepping back and forth
00610                  */
00611                 for (i = -DETOUR_AMOUNT; i <= DETOUR_AMOUNT; i++) {
00612                     if (i == 0)
00613                         continue; /* already did this, so skip it */
00614                     /* Use lastdir here - otherwise,
00615                      * since the direction that the creature should move in
00616                      * may change, you could get infinite loops.
00617                      * ie, player is northwest, but monster can only
00618                      * move west, so it does that.  It goes some distance,
00619                      * gets blocked, finds that it should move north,
00620                      * can't do that, but now finds it can move east, and
00621                      * gets back to its original point.  lastdir contains
00622                      * the last direction the creature has successfully
00623                      * moved.
00624                      */
00625 
00626                     x = lastx+freearr_x[absdir(lastdir+i)];
00627                     y = lasty+freearr_y[absdir(lastdir+i)];
00628                     m = lastmap;
00629                     mflags = get_map_flags(m, &m, x, y, &x, &y);
00630                     if (mflags&P_OUT_OF_MAP)
00631                         continue;
00632                     blocked = GET_MAP_MOVE_BLOCK(m, x, y);
00633                     if (OB_TYPE_MOVE_BLOCK(mon, blocked))
00634                         continue;
00635                     if (mflags&P_IS_ALIVE)
00636                         continue;
00637 
00638                     if (m == mon->map && blocked_link(mon, m, x, y))
00639                         break;
00640                 }
00641                 /* go through entire loop without finding a valid
00642                  * sidestep to take - thus, no valid path.
00643                  */
00644                 if (i == (DETOUR_AMOUNT+1))
00645                     return 0;
00646                 diff--;
00647                 lastdir = dir;
00648                 max--;
00649                 if (!firstdir)
00650                     firstdir = dir+i;
00651             } /* else check alternate directions */
00652         } /* if blocked */
00653         else {
00654             /* we moved towards creature, so diff is less */
00655             diff--;
00656             max--;
00657             lastdir = dir;
00658             if (!firstdir)
00659                 firstdir = dir;
00660         }
00661         if (diff <= 1) {
00662             /* Recalculate diff (distance) because we may not have actually
00663              * headed toward player for entire distance.
00664              */
00665             get_rangevector_from_mapcoord(m, x, y, pl, &rv, 0);
00666             diff = MAX(FABS(rv.distance_x), FABS(rv.distance_y));
00667         }
00668         if (diff > max)
00669             return 0;
00670     }
00671     /* If we reached the max, didn't find a direction in time */
00672     if (!max)
00673         return 0;
00674 
00675     return firstdir;
00676 }
00677 
00688 void give_initial_items(object *pl, treasurelist *items) {
00689     object *op, *next = NULL;
00690 
00691     if (pl->randomitems != NULL)
00692         create_treasure(items, pl, GT_STARTEQUIP|GT_ONLY_GOOD, 1, 0);
00693 
00694     for (op = pl->inv; op; op = next) {
00695         next = op->below;
00696 
00697         /* Forces get applied per default, unless they have the
00698          * flag "neutral" set. Sorry but I can't think of a better way
00699          */
00700         if (op->type == FORCE && !QUERY_FLAG(op, FLAG_NEUTRAL))
00701             SET_FLAG(op, FLAG_APPLIED);
00702 
00703         /* we never give weapons/armour if these cannot be used
00704          * by this player due to race restrictions
00705          */
00706         if (pl->type == PLAYER) {
00707             if ((!QUERY_FLAG(pl, FLAG_USE_ARMOUR) && IS_ARMOR(op))
00708             || (!QUERY_FLAG(pl, FLAG_USE_WEAPON) && IS_WEAPON(op))
00709             || (!QUERY_FLAG(pl, FLAG_USE_SHIELD) && IS_SHIELD(op))) {
00710                 remove_ob(op);
00711                 free_object(op);
00712                 continue;
00713             }
00714         }
00715 
00716         /* This really needs to be better - we should really give
00717          * a substitute spellbook.  The problem is that we don't really
00718          * have a good idea what to replace it with (need something like
00719          * a first level treasurelist for each skill.)
00720          * remove duplicate skills also
00721          */
00722         if (op->type == SPELLBOOK || op->type == SKILL) {
00723             object *tmp;
00724 
00725             for (tmp = op->below; tmp; tmp = tmp->below)
00726                 if (tmp->type == op->type && tmp->name == op->name)
00727                     break;
00728 
00729             if (tmp) {
00730                 remove_ob(op);
00731                 free_object(op);
00732                 LOG(llevError, "give_initial_items: Removing duplicate object %s\n", tmp->name);
00733                 continue;
00734             }
00735             if (op->nrof > 1)
00736                 op->nrof = 1;
00737         }
00738 
00739         if (op->type == SPELLBOOK && op->inv) {
00740             CLEAR_FLAG(op->inv, FLAG_STARTEQUIP);
00741         }
00742 
00743         /* Give starting characters identified, uncursed, and undamned
00744          * items.  Just don't identify gold or silver, or it won't be
00745          * merged properly.
00746          */
00747         if (need_identify(op)) {
00748             SET_FLAG(op, FLAG_IDENTIFIED);
00749             CLEAR_FLAG(op, FLAG_CURSED);
00750             CLEAR_FLAG(op, FLAG_DAMNED);
00751         }
00752         if (op->type == SPELL)  {
00753             remove_ob(op);
00754             free_object(op);
00755             continue;
00756         } else if (op->type == SKILL)  {
00757             SET_FLAG(op, FLAG_CAN_USE_SKILL);
00758             op->stats.exp = 0;
00759             op->level = 1;
00760         }
00761         /* lock all 'normal items by default */
00762         else
00763             SET_FLAG(op, FLAG_INV_LOCKED);
00764     } /* for loop of objects in player inv */
00765 
00766     /* Need to set up the skill pointers */
00767     link_player_skills(pl);
00768 
00773     for (op = pl->inv; op; op = next) {
00774         next = op->below;
00775         if ((IS_ARMOR(op) || IS_WEAPON(op) || IS_SHIELD(op)) && !QUERY_FLAG(op, FLAG_APPLIED))
00776             manual_apply(pl, op, AP_NOPRINT);
00777     }
00778 }
00779 
00786 void get_name(object *op) {
00787     op->contr->write_buf[0] = '\0';
00788     op->contr->state = ST_GET_NAME;
00789     send_query(&op->contr->socket, 0, "What is your name?\n:");
00790 }
00791 
00798 void get_password(object *op) {
00799     op->contr->write_buf[0] = '\0';
00800     op->contr->state = ST_GET_PASSWORD;
00801     send_query(&op->contr->socket, CS_QUERY_HIDEINPUT, "What is your password?\n:");
00802 }
00803 
00810 void play_again(object *op) {
00811     op->contr->state = ST_PLAY_AGAIN;
00812     op->chosen_skill = NULL;
00813     send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Do you want to play again (a/q)?");
00814     /* a bit of a hack, but there are various places early in th
00815      * player creation process that a user can quit (eg, roll
00816      * stats) that isn't removing the player.  Taking a quick
00817      * look, there are many places that call play_again without
00818      * removing the player - it probably makes more sense
00819      * to leave it to play_again to remove the object in all
00820      * cases.
00821      */
00822     if (!QUERY_FLAG(op, FLAG_REMOVED))
00823         remove_ob(op);
00824     /* Need to set this to null - otherwise, it could point to garbage,
00825      * and draw() doesn't check to see if the player is removed, only if
00826      * the map is null or not swapped out.
00827      */
00828     op->map = NULL;
00829 }
00830 
00839 void receive_play_again(object *op, char key) {
00840     if (key == 'q' || key == 'Q') {
00841         remove_friendly_object(op);
00842         leave(op->contr, 0); /* ericserver will draw the message */
00843         return;
00844     } else if (key == 'a' || key == 'A') {
00845         player *pl = op->contr;
00846         const char *name = op->name;
00847 
00848         add_refcount(name);
00849         remove_friendly_object(op);
00850         free_object(op);
00851         pl = get_player(pl);
00852         op = pl->ob;
00853         add_friendly_object(op);
00854         op->contr->password[0] = '~';
00855         FREE_AND_CLEAR_STR(op->name);
00856         FREE_AND_CLEAR_STR(op->name_pl);
00857 
00858         /* Lets put a space in here */
00859         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN,
00860                       "\n", "\n");
00861         get_name(op);
00862         op->name = name;  /* Already added a refcount above */
00863         op->name_pl = add_string(name);
00864         set_first_map(op);
00865     } else {
00866         /* user pressed something else so just ask again... */
00867         play_again(op);
00868     }
00869 }
00870 
00877 void confirm_password(object *op) {
00878     op->contr->write_buf[0] = '\0';
00879     op->contr->state = ST_CONFIRM_PASSWORD;
00880     send_query(&op->contr->socket, CS_QUERY_HIDEINPUT, "Please type your password again.\n:");
00881 }
00882 
00891 void get_party_password(object *op, partylist *party) {
00892     if (party == NULL) {
00893         LOG(llevError, "get_party_password(): tried to make player %s join a NULL party\n", op->name);
00894         return;
00895     }
00896     op->contr->write_buf[0] = '\0';
00897     op->contr->state = ST_GET_PARTY_PASSWORD;
00898     op->contr->party_to_join = party;
00899     send_query(&op->contr->socket, CS_QUERY_HIDEINPUT, "What is the password?\n:");
00900 }
00901 
00908 int roll_stat(void) {
00909     int a[4], i, j, k;
00910 
00911     for (i = 0; i < 4; i++)
00912         a[i] = (int)RANDOM()%6+1;
00913 
00914     for (i = 0, j = 0, k = 7; i < 4; i++)
00915         if (a[i] < k)
00916             k = a[i],
00917             j = i;
00918 
00919     for (i = 0, k = 0; i < 4; i++) {
00920         if (i != j)
00921             k += a[i];
00922     }
00923     return k;
00924 }
00925 
00932 void roll_stats(object *op) {
00933     int sum = 0;
00934     int i = 0, j = 0;
00935     int statsort[7];
00936 
00937     do {
00938         op->stats.Str = roll_stat();
00939         op->stats.Dex = roll_stat();
00940         op->stats.Int = roll_stat();
00941         op->stats.Con = roll_stat();
00942         op->stats.Wis = roll_stat();
00943         op->stats.Pow = roll_stat();
00944         op->stats.Cha = roll_stat();
00945         sum = op->stats.Str+op->stats.Dex+op->stats.Int+op->stats.Con+op->stats.Wis+op->stats.Pow+op->stats.Cha;
00946     } while (sum != 105); /* 116 used to be best possible character */
00947 
00948     /* Sort the stats so that rerolling is easier... */
00949     statsort[0] = op->stats.Str;
00950     statsort[1] = op->stats.Dex;
00951     statsort[2] = op->stats.Int;
00952     statsort[3] = op->stats.Con;
00953     statsort[4] = op->stats.Wis;
00954     statsort[5] = op->stats.Pow;
00955     statsort[6] = op->stats.Cha;
00956 
00957     /* a quick and dirty bubblesort? */
00958     do {
00959         if (statsort[i] < statsort[i+1]) {
00960             j = statsort[i];
00961             statsort[i] = statsort[i+1];
00962             statsort[i+1] = j;
00963             i = 0;
00964         } else {
00965             i++;
00966         }
00967     } while (i < 6);
00968 
00969     op->stats.Str = statsort[0];
00970     op->stats.Dex = statsort[1];
00971     op->stats.Con = statsort[2];
00972     op->stats.Int = statsort[3];
00973     op->stats.Wis = statsort[4];
00974     op->stats.Pow = statsort[5];
00975     op->stats.Cha = statsort[6];
00976 
00977     op->contr->orig_stats.Str = op->stats.Str;
00978     op->contr->orig_stats.Dex = op->stats.Dex;
00979     op->contr->orig_stats.Int = op->stats.Int;
00980     op->contr->orig_stats.Con = op->stats.Con;
00981     op->contr->orig_stats.Wis = op->stats.Wis;
00982     op->contr->orig_stats.Pow = op->stats.Pow;
00983     op->contr->orig_stats.Cha = op->stats.Cha;
00984 
00985     op->level = 1;
00986     op->stats.exp = 0;
00987     op->stats.ac = 0;
00988 
00989     op->contr->levhp[1] = 9;
00990     op->contr->levsp[1] = 6;
00991     op->contr->levgrace[1] = 3;
00992 
00993     fix_object(op);
00994     op->stats.hp = op->stats.maxhp;
00995     op->stats.sp = op->stats.maxsp;
00996     op->stats.grace = op->stats.maxgrace;
00997     op->contr->orig_stats = op->stats;
00998 }
00999 
01006 void roll_again(object *op) {
01007     esrv_new_player(op->contr, 0);
01008     send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "[y] to roll new stats [n] to use stats\n[1-7] [1-7] to swap stats.\nRoll again (y/n/1-7)?  ");
01009 }
01010 
01020 static void swap_stat(object *op, int Swap_Second) {
01021     signed char tmp;
01022 
01023     if (op->contr->Swap_First == -1) {
01024         LOG(llevError, "player.c:swap_stat() - Swap_First is -1\n");
01025         return;
01026     }
01027 
01028     tmp = get_attr_value(&op->contr->orig_stats, op->contr->Swap_First);
01029 
01030     set_attr_value(&op->contr->orig_stats, op->contr->Swap_First, get_attr_value(&op->contr->orig_stats, Swap_Second));
01031 
01032     set_attr_value(&op->contr->orig_stats, Swap_Second, tmp);
01033 
01034     draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01035                          "%s done\n",
01036                          "%s done\n",
01037                          short_stat_name[Swap_Second]);
01038 
01039     op->stats.Str = op->contr->orig_stats.Str;
01040     op->stats.Dex = op->contr->orig_stats.Dex;
01041     op->stats.Con = op->contr->orig_stats.Con;
01042     op->stats.Int = op->contr->orig_stats.Int;
01043     op->stats.Wis = op->contr->orig_stats.Wis;
01044     op->stats.Pow = op->contr->orig_stats.Pow;
01045     op->stats.Cha = op->contr->orig_stats.Cha;
01046     op->stats.ac = 0;
01047 
01048     op->level = 1;
01049     op->stats.exp = 0;
01050     op->stats.ac = 0;
01051 
01052     op->contr->levhp[1] = 9;
01053     op->contr->levsp[1] = 6;
01054     op->contr->levgrace[1] = 3;
01055 
01056     fix_object(op);
01057     op->stats.hp = op->stats.maxhp;
01058     op->stats.sp = op->stats.maxsp;
01059     op->stats.grace = op->stats.maxgrace;
01060     op->contr->orig_stats = op->stats;
01061     op->contr->Swap_First = -1;
01062 }
01063 
01079 void key_roll_stat(object *op, char key) {
01080     int keynum = key-'0';
01081     static const sint8 stat_trans[] = {
01082         -1,
01083         STR,
01084         DEX,
01085         CON,
01086         INT,
01087         WIS,
01088         POW,
01089         CHA
01090     };
01091 
01092     if (keynum > 0 && keynum <= 7) {
01093         if (op->contr->Swap_First == -1) {
01094             op->contr->Swap_First = stat_trans[keynum];
01095             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01096                                  "%s ->",
01097                                  "%s ->",
01098                                  short_stat_name[stat_trans[keynum]]);
01099         } else
01100             swap_stat(op, stat_trans[keynum]);
01101 
01102         send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "");
01103         return;
01104     }
01105     switch (key) {
01106     case 'n':
01107     case 'N': {
01108         SET_FLAG(op, FLAG_WIZ);
01109         if (op->map == NULL) {
01110             LOG(llevError, "Map == NULL in state 2\n");
01111             break;
01112         }
01113 
01114         SET_ANIMATION(op, 2);     /* So player faces south */
01115         /* Enter exit adds a player otherwise */
01116         add_statbonus(op);
01117         send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Now choose a character.\nPress any key to change outlook.\nPress `d' when you're pleased.\n");
01118         op->contr->state = ST_CHANGE_CLASS;
01119         if (op->msg)
01120             draw_ext_info(NDI_BLUE, 0, op,
01121                           MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01122                           op->msg, op->msg);
01123         return;
01124     }
01125     case 'y':
01126     case 'Y':
01127         roll_stats(op);
01128         send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "");
01129         return;
01130 
01131     case 'q':
01132     case 'Q':
01133         play_again(op);
01134         return;
01135 
01136     default:
01137         send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Yes, No, Quit or 1-6.  Roll again?");
01138         return;
01139     }
01140     return;
01141 }
01142 
01156 void key_change_class(object *op, char key) {
01157     int tmp_loop;
01158 
01159     if (key == 'q' || key == 'Q') {
01160         remove_ob(op);
01161         play_again(op);
01162         return;
01163     }
01164     if (key == 'd' || key == 'D') {
01165         char buf[MAX_BUF];
01166 
01167         /* this must before then initial items are given */
01168         esrv_new_player(op->contr, op->weight+op->carrying);
01169         create_treasure(find_treasurelist("starting_wealth"), op, 0, 0, 0);
01170 
01171         /* Lauwenmark : Here we handle the BORN global event */
01172         execute_global_event(EVENT_BORN, op);
01173 
01174         /* Lauwenmark : We then generate a LOGIN event */
01175         execute_global_event(EVENT_LOGIN, op->contr, op->contr->socket.host);
01176         op->contr->state = ST_PLAYING;
01177 
01178         if (op->msg) {
01179             free_string(op->msg);
01180             op->msg = NULL;
01181         }
01182 
01183         /* We create this now because some of the unique maps will need it
01184          * to save here.
01185          */
01186         snprintf(buf, sizeof(buf), "%s/%s/%s", settings.localdir, settings.playerdir, op->name);
01187         make_path_to_file(buf);
01188 
01189 #ifdef AUTOSAVE
01190         op->contr->last_save_tick = pticks;
01191 #endif
01192         start_info(op);
01193         CLEAR_FLAG(op, FLAG_WIZ);
01194         give_initial_items(op, op->randomitems);
01195         link_player_skills(op);
01196         esrv_send_inventory(op, op);
01197         fix_object(op);
01198 
01199         /* This moves the player to a different start map, if there
01200          * is one for this race
01201          */
01202         if (*first_map_ext_path) {
01203             object *tmp;
01204             char mapname[MAX_BUF];
01205             mapstruct *oldmap;
01206 
01207             oldmap = op->map;
01208 
01209             snprintf(mapname, MAX_BUF-1, "%s/%s", first_map_ext_path, op->arch->name);
01210             /*printf("%s\n", mapname);*/
01211             tmp = get_object();
01212             EXIT_PATH(tmp) = add_string(mapname);
01213             EXIT_X(tmp) = op->x;
01214             EXIT_Y(tmp) = op->y;
01215             enter_exit(op, tmp);
01216 
01217             if (oldmap != op->map) {
01218                 /* map exists, update bed of reality location, in case player dies */
01219                 op->contr->bed_x = op->x;
01220                 op->contr->bed_y = op->y;
01221                 snprintf(op->contr->savebed_map, sizeof(op->contr->savebed_map), "%s", mapname);
01222             }
01223 
01224             free_object(tmp);
01225         } else {
01226             LOG(llevDebug, "first_map_ext_path not set\n");
01227         }
01228         return;
01229     }
01230 
01231     /* Following actually changes the race - this is the default command
01232      * if we don't match with one of the options above.
01233      */
01234 
01235     tmp_loop = 0;
01236     while (!tmp_loop) {
01237         const char *name = add_string(op->name);
01238         int x = op->x, y = op->y;
01239 
01240         remove_statbonus(op);
01241         remove_ob(op);
01242         op->arch = get_player_archetype(op->arch);
01243         copy_object(&op->arch->clone, op);
01244         op->stats = op->contr->orig_stats;
01245         free_string(op->name);
01246         op->name = name;
01247         free_string(op->name_pl);
01248         op->name_pl = add_string(name);
01249         op->x = x;
01250         op->y = y;
01251         SET_ANIMATION(op, 2);    /* So player faces south */
01252         insert_ob_in_map(op, op->map, op, 0);
01253         strncpy(op->contr->title, op->arch->clone.name, sizeof(op->contr->title)-1);
01254         op->contr->title[sizeof(op->contr->title)-1] = '\0';
01255         add_statbonus(op);
01256         tmp_loop = allowed_class(op);
01257     }
01258     update_object(op, UP_OBJ_FACE);
01259     esrv_update_item(UPD_FACE, op, op);
01260     fix_object(op);
01261     op->stats.hp = op->stats.maxhp;
01262     op->stats.sp = op->stats.maxsp;
01263     op->stats.grace = 0;
01264     if (op->msg)
01265         draw_ext_info(NDI_BLUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_NEWPLAYER,
01266                       op->msg, op->msg);
01267     send_query(&op->contr->socket, CS_QUERY_SINGLECHAR, "Press any key for the next race.\nPress `d' to play this race.\n");
01268 }
01269 
01278 void key_confirm_quit(object *op, char key) {
01279     char buf[MAX_BUF];
01280 
01281     if (key != 'y' && key != 'Y' && key != 'q' && key != 'Q') {
01282         op->contr->state = ST_PLAYING;
01283         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_LOGIN,
01284                       "OK, continuing to play.", NULL);
01285         return;
01286     }
01287 
01288     /* Lauwenmark : Here we handle the REMOVE global event */
01289     execute_global_event(EVENT_REMOVE, op);
01290     terminate_all_pets(op);
01291     remove_ob(op);
01292     op->direction = 0;
01293     draw_ext_info_format(NDI_UNIQUE|NDI_ALL|NDI_DK_ORANGE, 5, NULL, MSG_TYPE_ADMIN, MSG_TYPE_ADMIN_PLAYER,
01294                          "%s quits the game.",
01295                          "%s quits the game.",
01296                          op->name);
01297 
01298     strcpy(op->contr->killer, "quit");
01299     check_score(op, 0);
01300     op->contr->party = NULL;
01301     if (settings.set_title == TRUE)
01302         op->contr->own_title[0] = '\0';
01303 
01304     if (!QUERY_FLAG(op, FLAG_WAS_WIZ)) {
01305         mapstruct *mp, *next;
01306 
01307         /* We need to hunt for any per player unique maps in memory and
01308          * get rid of them.  The trailing slash in the path is intentional,
01309          * so that players named 'Ab' won't match against players 'Abe' pathname
01310          */
01311         snprintf(buf, sizeof(buf), "%s/%s/%s/", settings.localdir, settings.playerdir, op->name);
01312         for (mp = first_map; mp != NULL; mp = next) {
01313             next = mp->next;
01314             if (!strncmp(mp->path, buf, strlen(buf)))
01315                 delete_map(mp);
01316         }
01317 
01318         delete_character(op->name);
01319     }
01320     play_again(op);
01321 }
01322 
01329 static void flee_player(object *op) {
01330     int dir, diff;
01331     rv_vector rv;
01332 
01333     if (op->stats.hp < 0) {
01334         LOG(llevDebug, "Fleeing player is dead.\n");
01335         CLEAR_FLAG(op, FLAG_SCARED);
01336         return;
01337     }
01338 
01339     if (op->enemy == NULL) {
01340         LOG(llevDebug, "Fleeing player had no enemy.\n");
01341         CLEAR_FLAG(op, FLAG_SCARED);
01342         return;
01343     }
01344 
01345     /* Seen some crashes here.  Since we don't store an
01346      * op->enemy_count, it is possible that something destroys the
01347      * actual enemy, and the object is recycled.
01348      */
01349     if (op->enemy->map == NULL) {
01350         CLEAR_FLAG(op, FLAG_SCARED);
01351         op->enemy = NULL;
01352         return;
01353     }
01354 
01355     if (!(random_roll(0, 4, op, PREFER_LOW)) && did_make_save(op, op->level, 0)) {
01356         op->enemy = NULL;
01357         CLEAR_FLAG(op, FLAG_SCARED);
01358         return;
01359     }
01360     get_rangevector(op, op->enemy, &rv, 0);
01361 
01362     dir = absdir(4+rv.direction);
01363     for (diff = 0; diff < 3; diff++) {
01364         int m = 1-(RANDOM()&2);
01365         if (move_ob(op, absdir(dir+diff*m), op)
01366         || (diff == 0 && move_ob(op, absdir(dir-diff*m), op))) {
01367             return;
01368         }
01369     }
01370     /* Cornered, get rid of scared */
01371     CLEAR_FLAG(op, FLAG_SCARED);
01372     op->enemy = NULL;
01373 }
01374 
01384 int check_pick(object *op) {
01385     object *tmp, *next;
01386     tag_t next_tag = 0, op_tag;
01387     int stop = 0;
01388     int j, k, wvratio;
01389     char putstring[128], tmpstr[16];
01390 
01391     /* if you're flying, you can't pick up anything */
01392     if (op->move_type&MOVE_FLYING)
01393         return 1;
01394 
01395     op_tag = op->count;
01396 
01397     next = op->below;
01398     if (next)
01399         next_tag = next->count;
01400 
01401     /* loop while there are items on the floor that are not marked as
01402      * destroyed */
01403     while (next && !was_destroyed(next, next_tag)) {
01404         tmp = next;
01405         next = tmp->below;
01406         if (next)
01407             next_tag = next->count;
01408 
01409         if (was_destroyed(op, op_tag))
01410             return 0;
01411 
01412         if (!can_pick(op, tmp))
01413             continue;
01414 
01415         if (op->contr->search_str[0] != '\0' && settings.search_items == TRUE) {
01416             if (item_matched_string(op, tmp, op->contr->search_str))
01417                 pick_up(op, tmp);
01418             continue;
01419         }
01420 
01421         /* high not bit set?  We're using the old autopickup model */
01422         if (!(op->contr->mode&PU_NEWMODE)) {
01423             switch (op->contr->mode) {
01424             case 0:
01425                 return 1; /* don't pick up */
01426 
01427             case 1:
01428                 pick_up(op, tmp);
01429                 return 1;
01430 
01431             case 2:
01432                 pick_up(op, tmp);
01433                 return 0;
01434 
01435             case 3:
01436                 return 0; /* stop before pickup */
01437 
01438             case 4:
01439                 pick_up(op, tmp);
01440                 break;
01441 
01442             case 5:
01443                 pick_up(op, tmp);
01444                 stop = 1;
01445                 break;
01446 
01447             case 6:
01448                 if (QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL)
01449                 && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED))
01450                     pick_up(op, tmp);
01451                 break;
01452 
01453             case 7:
01454                 if (tmp->type == MONEY || tmp->type == GEM)
01455                     pick_up(op, tmp);
01456                 break;
01457 
01458             default:
01459                 /* use value density */
01460                 if (!QUERY_FLAG(tmp, FLAG_UNPAID)
01461                 && (query_cost(tmp, op, F_TRUE)*100/(tmp->weight*MAX(tmp->nrof, 1))) >= op->contr->mode)
01462                     pick_up(op, tmp);
01463             }
01464         } else { /* old model */
01465             /* NEW pickup handling */
01466             if (op->contr->mode&PU_DEBUG) {
01467                 /* some debugging code to figure out item information */
01468                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DEBUG,
01469                                      "item name: %s    item type: %d    weight/value: %d",
01470                                      "item name: %s    item type: %d    weight/value: %d",
01471                                      tmp->name ? tmp->name : tmp->arch->name, tmp->type,
01472                                      (int)(query_cost(tmp, op, F_TRUE)*100/(tmp->weight*MAX(tmp->nrof, 1))));
01473 
01474 
01475                 snprintf(putstring, sizeof(putstring), "...flags: ");
01476                 for (k = 0; k < 4; k++) {
01477                     for (j = 0; j < 32; j++) {
01478                         if ((tmp->flags[k]>>j)&0x01) {
01479                             snprintf(tmpstr, sizeof(tmpstr), "%d ", k*32+j);
01480                             strcat(putstring, tmpstr);
01481                         }
01482                     }
01483                 }
01484                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_SUCCESS,
01485                               putstring, putstring);
01486             }
01487             /* philosophy:
01488              * It's easy to grab an item type from a pile, as long as it's
01489              * generic.  This takes no game-time.  For more detailed pickups
01490              * and selections, select-items should be used.  This is a
01491              * grab-as-you-run type mode that's really useful for arrows for
01492              * example.
01493              * The drawback: right now it has no frontend, so you need to
01494              * stick the bits you want into a calculator in hex mode and then
01495              * convert to decimal and then 'pickup <#>
01496              */
01497 
01498             /* the first two modes are exclusive: if NOTHING we return, if
01499              * STOP then we stop.  All the rest are applied sequentially,
01500              * meaning if any test passes, the item gets picked up. */
01501 
01502             /* if mode is set to pick nothing up, return */
01503 
01504             if (op->contr->mode&PU_NOTHING)
01505                 return 1;
01506 
01507             /* if mode is set to stop when encountering objects, return.
01508              * Take STOP before INHIBIT since it doesn't actually pick
01509              * anything up */
01510 
01511             if (op->contr->mode&PU_STOP)
01512                 return 0;
01513 
01514             /* useful for going into stores and not losing your settings... */
01515             /* and for battles wher you don't want to get loaded down while
01516              * fighting */
01517             if (op->contr->mode&PU_INHIBIT)
01518                 return 1;
01519 
01520             /* prevent us from turning into auto-thieves :) */
01521             if (QUERY_FLAG(tmp, FLAG_UNPAID))
01522                 continue;
01523 
01524             /* ignore known cursed objects */
01525             if (QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) && op->contr->mode&PU_NOT_CURSED)
01526                 continue;
01527 
01528             /* all food and drink if desired */
01529             /* question: don't pick up known-poisonous stuff? */
01530             if (op->contr->mode&PU_FOOD)
01531                 if (tmp->type == FOOD) {
01532                     pick_up(op, tmp);
01533                     if (0)
01534                         fprintf(stderr, "FOOD\n");
01535                     continue;
01536                 }
01537             if (op->contr->mode&PU_DRINK)
01538                 if (tmp->type == DRINK || (tmp->type == POISON && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED))) {
01539                     pick_up(op, tmp);
01540                     if (0)
01541                         fprintf(stderr, "DRINK\n");
01542                     continue;
01543                 }
01544             /* we don't forget dragon food */
01545             if (op->contr->mode&PU_FLESH)
01546                 if (tmp->type == FLESH) {
01547                     pick_up(op, tmp);
01548                     if (0)
01549                         fprintf(stderr, "FLESH\n");
01550                     continue;
01551                 }
01552             if (op->contr->mode&PU_POTION)
01553                 if (tmp->type == POTION) {
01554                     pick_up(op, tmp);
01555                     if (0)
01556                         fprintf(stderr, "POTION\n");
01557                     continue;
01558                 }
01559 
01560             /* spellbooks, skillscrolls and normal books/scrolls */
01561             if (op->contr->mode&PU_SPELLBOOK)
01562                 if (tmp->type == SPELLBOOK) {
01563                     pick_up(op, tmp);
01564                     if (0)
01565                         fprintf(stderr, "SPELLBOOK\n");
01566                     continue;
01567                 }
01568             if (op->contr->mode&PU_SKILLSCROLL)
01569                 if (tmp->type == SKILLSCROLL) {
01570                     pick_up(op, tmp);
01571                     if (0)
01572                         fprintf(stderr, "SKILLSCROLL\n");
01573                     continue;
01574                 }
01575             if (op->contr->mode&PU_READABLES)
01576                 if (tmp->type == BOOK || tmp->type == SCROLL) {
01577                     pick_up(op, tmp);
01578                     if (0)
01579                         fprintf(stderr, "READABLES\n");
01580                     continue;
01581                 }
01582 
01583             /* wands/staves/rods/horns */
01584             if (op->contr->mode&PU_MAGIC_DEVICE)
01585                 if (tmp->type == WAND || tmp->type == ROD || tmp->type == HORN) {
01586                     pick_up(op, tmp);
01587                     if (0)
01588                         fprintf(stderr, "MAGIC_DEVICE\n");
01589                     continue;
01590                 }
01591 
01592             /* pick up all magical items */
01593             if (op->contr->mode&PU_MAGICAL)
01594                 if (QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL) && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED)) {
01595                     pick_up(op, tmp);
01596                     if (0)
01597                         fprintf(stderr, "MAGICAL\n");
01598                     continue;
01599                 }
01600 
01601             if (op->contr->mode&PU_VALUABLES) {
01602                 if (tmp->type == MONEY || tmp->type == GEM) {
01603                     pick_up(op, tmp);
01604                     if (0)
01605                         fprintf(stderr, "MONEY/GEM\n");
01606                     continue;
01607                 }
01608             }
01609 
01610             /* rings & amulets - talismans seems to be typed AMULET */
01611             if (op->contr->mode&PU_JEWELS)
01612                 if (tmp->type == RING || tmp->type == AMULET) {
01613                     pick_up(op, tmp);
01614                     if (0)
01615                         fprintf(stderr, "JEWELS\n");
01616                     continue;
01617                 }
01618 
01619             /* bows and arrows. Bows are good for selling! */
01620             if (op->contr->mode&PU_BOW)
01621                 if (tmp->type == BOW) {
01622                     pick_up(op, tmp);
01623                     if (0)
01624                         fprintf(stderr, "BOW\n");
01625                     continue;
01626                 }
01627             if (op->contr->mode&PU_ARROW)
01628                 if (tmp->type == ARROW) {
01629                     pick_up(op, tmp);
01630                     if (0)
01631                         fprintf(stderr, "ARROW\n");
01632                     continue;
01633                 }
01634 
01635             /* all kinds of armor etc. */
01636             if (op->contr->mode&PU_ARMOUR)
01637                 if (tmp->type == ARMOUR) {
01638                     pick_up(op, tmp);
01639                     if (0)
01640                         fprintf(stderr, "ARMOUR\n");
01641                     continue;
01642                 }
01643             if (op->contr->mode&PU_HELMET)
01644                 if (tmp->type == HELMET) {
01645                     pick_up(op, tmp);
01646                     if (0)
01647                         fprintf(stderr, "HELMET\n");
01648                     continue;
01649                 }
01650             if (op->contr->mode&PU_SHIELD)
01651                 if (tmp->type == SHIELD) {
01652                     pick_up(op, tmp);
01653                     if (0)
01654                         fprintf(stderr, "SHIELD\n");
01655                     continue;
01656                 }
01657             if (op->contr->mode&PU_BOOTS)
01658                 if (tmp->type == BOOTS) {
01659                     pick_up(op, tmp);
01660                     if (0)
01661                         fprintf(stderr, "BOOTS\n");
01662                     continue;
01663                 }
01664             if (op->contr->mode&PU_GLOVES)
01665                 if (tmp->type == GLOVES) {
01666                     pick_up(op, tmp);
01667                     if (0)
01668                         fprintf(stderr, "GLOVES\n");
01669                     continue;
01670                 }
01671             if (op->contr->mode&PU_CLOAK)
01672                 if (tmp->type == CLOAK) {
01673                     pick_up(op, tmp);
01674                     if (0)
01675                         fprintf(stderr, "GLOVES\n");
01676                     continue;
01677                 }
01678 
01679             /* hoping to catch throwing daggers here */
01680             if (op->contr->mode&PU_MISSILEWEAPON)
01681                 if (tmp->type == WEAPON && QUERY_FLAG(tmp, FLAG_IS_THROWN)) {
01682                     pick_up(op, tmp);
01683                     if (0)
01684                         fprintf(stderr, "MISSILEWEAPON\n");
01685                     continue;
01686                 }
01687 
01688             /* careful: chairs and tables are weapons! */
01689             if (op->contr->mode&PU_ALLWEAPON) {
01690                 if (tmp->type == WEAPON && tmp->name != NULL) {
01691                     if (strstr(tmp->name, "table") == NULL
01692                     && strstr(tmp->arch->name, "table") == NULL
01693                     && strstr(tmp->name, "chair")
01694                     && strstr(tmp->arch->name, "chair") == NULL) {
01695                         pick_up(op, tmp);
01696                         if (0)
01697                             fprintf(stderr, "WEAPON\n");
01698                         continue;
01699                     }
01700                 }
01701                 if (tmp->type == WEAPON && tmp->name == NULL) {
01702                     if (strstr(tmp->arch->name, "table") == NULL
01703                     && strstr(tmp->arch->name, "chair") == NULL) {
01704                         pick_up(op, tmp);
01705                         if (0)
01706                             fprintf(stderr, "WEAPON\n");
01707                         continue;
01708                     }
01709                 }
01710             }
01711 
01712             /* misc stuff that's useful */
01713             if (op->contr->mode&PU_KEY)
01714                 if (tmp->type == KEY || tmp->type == SPECIAL_KEY) {
01715                     pick_up(op, tmp);
01716                     if (0)
01717                         fprintf(stderr, "KEY\n");
01718                     continue;
01719                 }
01720 
01721             /* any of the last 4 bits set means we use the ratio for value
01722              * pickups */
01723             if (op->contr->mode&PU_RATIO) {
01724                 /* use value density to decide what else to grab.
01725                  * >=7 was >= op->contr->mode
01726                  * >=7 is the old standard setting.  Now we take the last 4 bits
01727                  * and multiply them by 5, giving 0..15*5== 5..75 */
01728                 wvratio = (op->contr->mode&PU_RATIO)*5;
01729                 if ((query_cost(tmp, op, F_TRUE)*100/(tmp->weight*MAX(tmp->nrof, 1))) >= wvratio) {
01730                     pick_up(op, tmp);
01731                     continue;
01732                 }
01733             }
01734         } /* the new pickup model */
01735     }
01736     return !stop;
01737 }
01738 
01751 static object *find_arrow(object *op, const char *type) {
01752     object *tmp = NULL;
01753 
01754     for (op = op->inv; op; op = op->below)
01755         if (!tmp
01756         && op->type == CONTAINER
01757         && op->race == type
01758         && QUERY_FLAG(op, FLAG_APPLIED))
01759             tmp = find_arrow(op, type);
01760         else if (op->type == ARROW && op->race == type)
01761             return op;
01762     return tmp;
01763 }
01764 
01782 static object *find_better_arrow(object *op, object *target, const char *type, int *better) {
01783     object *tmp = NULL, *arrow, *ntmp;
01784     int attacknum, attacktype, betterby = 0, i;
01785 
01786     if (!type)
01787         return NULL;
01788 
01789     for (arrow = op->inv; arrow; arrow = arrow->below) {
01790         if (arrow->type == CONTAINER
01791         && arrow->race == type
01792         && QUERY_FLAG(arrow, FLAG_APPLIED)) {
01793             i = 0;
01794             ntmp = find_better_arrow(arrow, target, type, &i);
01795             if (i > betterby) {
01796                 tmp = ntmp;
01797                 betterby = i;
01798             }
01799         } else if (arrow->type == ARROW && arrow->race == type) {
01800             /* allways prefer assasination/slaying */
01801             if (target->race != NULL
01802             && arrow->slaying != NULL
01803             && strstr(arrow->slaying, target->race)) {
01804                 if (arrow->attacktype&AT_DEATH) {
01805                     if (better)
01806                         *better = 100;
01807                     return arrow;
01808                 } else {
01809                     tmp = arrow;
01810                     betterby = (arrow->magic+arrow->stats.dam)*2;
01811                 }
01812             } else {
01813                 for (attacknum = 0; attacknum < NROFATTACKS; attacknum++) {
01814                     attacktype = 1<<attacknum;
01815                     if ((arrow->attacktype&attacktype) && (target->arch->clone.resist[attacknum]) < 0)
01816                         if (((arrow->magic+arrow->stats.dam)*(100-target->arch->clone.resist[attacknum])/100) > betterby) {
01817                             tmp = arrow;
01818                             betterby = (arrow->magic+arrow->stats.dam)*(100-target->arch->clone.resist[attacknum])/100;
01819                         }
01820                 }
01821                 if ((2+arrow->magic+arrow->stats.dam) > betterby) {
01822                     tmp = arrow;
01823                     betterby = 2+arrow->magic+arrow->stats.dam;
01824                 }
01825                 if (arrow->title && (1+arrow->magic+arrow->stats.dam) > betterby) {
01826                     tmp = arrow;
01827                     betterby = 1+arrow->magic+arrow->stats.dam;
01828                 }
01829             }
01830         }
01831     }
01832     if (tmp == NULL && arrow == NULL)
01833         return find_arrow(op, type);
01834 
01835     if (better)
01836         *better = betterby;
01837     return tmp;
01838 }
01839 
01852 static object *pick_arrow_target(object *op, const char *type, int dir) {
01853     object *tmp = NULL;
01854     mapstruct *m;
01855     int i, mflags, found, number;
01856     sint16 x, y;
01857 
01858     if (op->map == NULL)
01859         return find_arrow(op, type);
01860 
01861     /* do a dex check */
01862     number = (die_roll(2, 40, op, PREFER_LOW)-2)/2;
01863     if (number > (op->stats.Dex+(op->chosen_skill ? op->chosen_skill->level : op->level)))
01864         return find_arrow(op, type);
01865 
01866     m = op->map;
01867     x = op->x;
01868     y = op->y;
01869 
01870     /* find the first target */
01871     for (i = 0, found = 0; i < 20; i++) {
01872         x += freearr_x[dir];
01873         y += freearr_y[dir];
01874         mflags = get_map_flags(m, &m, x, y, &x, &y);
01875         if (mflags&P_OUT_OF_MAP || mflags&P_BLOCKSVIEW) {
01876             tmp = NULL;
01877             break;
01878         } else if (GET_MAP_MOVE_BLOCK(m, x, y) == MOVE_FLY_LOW) {
01879             /* This block presumes arrows and the like are MOVE_FLY_SLOW -
01880              * perhaps a bad assumption.
01881              */
01882             tmp = NULL;
01883             break;
01884         }
01885         if (mflags&P_IS_ALIVE) {
01886             for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above)
01887                 if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
01888                     found++;
01889                     break;
01890                 }
01891             if (found)
01892                 break;
01893         }
01894     }
01895     if (tmp == NULL)
01896         return find_arrow(op, type);
01897 
01898     if (tmp->head)
01899         tmp = tmp->head;
01900 
01901     return find_better_arrow(op, tmp, type, NULL);
01902 }
01903 
01922 int fire_bow(object *op, object *arrow, int dir, int wc_mod, sint16 sx, sint16 sy) {
01923     object *left, *bow;
01924     tag_t left_tag, tag;
01925     int bowspeed, mflags;
01926     mapstruct *m;
01927 
01928     if (!dir) {
01929         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01930                       "You can't shoot yourself!", NULL);
01931         return 0;
01932     }
01933     if (op->type == PLAYER)
01934         bow = op->contr->ranges[range_bow];
01935     else {
01936         for (bow = op->inv; bow; bow = bow->below)
01937             /* Don't check for applied - monsters don't apply bows - in that way, they
01938              * don't need to switch back and forth between bows and weapons.
01939              */
01940             if (bow->type == BOW)
01941                 break;
01942 
01943         if (!bow) {
01944             LOG(llevError, "Range: bow without activated bow (%s).\n", op->name);
01945             return 0;
01946         }
01947     }
01948     if (!bow->race || !bow->skill) {
01949         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01950                              "Your %s is broken.",
01951                              "Your %s is broken.",
01952                              bow->name);
01953         return 0;
01954     }
01955 
01956     bowspeed = bow->stats.sp+dex_bonus[op->stats.Dex];
01957 
01958     /* penalize ROF for bestarrow */
01959     if (op->type == PLAYER && op->contr->bowtype == bow_bestarrow)
01960         bowspeed -= dex_bonus[op->stats.Dex]+5;
01961     if (bowspeed < 1)
01962         bowspeed = 1;
01963 
01964     if (arrow == NULL) {
01965         if ((arrow = find_arrow(op, bow->race)) == NULL) {
01966             if (op->type == PLAYER)
01967                 draw_ext_info_format(NDI_UNIQUE, 0, op,
01968                                      MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01969                                      "You have no %s left.",
01970                                      "You have no %s left.",
01971                                      bow->race);
01972             /* FLAG_READY_BOW will get reset if the monsters picks up some arrows */
01973             else
01974                 CLEAR_FLAG(op, FLAG_READY_BOW);
01975             return 0;
01976         }
01977     }
01978     mflags = get_map_flags(op->map, &m, sx, sy, &sx, &sy);
01979     if (mflags&P_OUT_OF_MAP) {
01980         return 0;
01981     }
01982     if (GET_MAP_MOVE_BLOCK(m, sx, sy)&MOVE_FLY_LOW) {
01983         return 0;
01984     }
01985 
01986     /* this should not happen, but sometimes does */
01987     if (arrow->nrof == 0) {
01988         remove_ob(arrow);
01989         free_object(arrow);
01990         return 0;
01991     }
01992 
01993     left = arrow; /* these are arrows left to the player */
01994     /* BUG? The value in left_tag doesn't seem to be used. */
01995     left_tag = left->count;
01996     arrow = get_split_ob(arrow, 1, NULL, 0);
01997     if (arrow == NULL) {
01998         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
01999                              "You have no %s left.",
02000                              "You have no %s left.",
02001                              bow->race);
02002         return 0;
02003     }
02004     set_owner(arrow, op);
02005     if (arrow->skill)
02006         free_string(arrow->skill);
02007     arrow->skill = add_refcount(bow->skill);
02008 
02009     arrow->direction = dir;
02010     arrow->x = sx;
02011     arrow->y = sy;
02012 
02013     if (op->type == PLAYER) {
02014         op->speed_left = 0.01-(float)FABS(op->speed)*100/bowspeed;
02015         fix_object(op);
02016     }
02017 
02018     SET_ANIMATION(arrow, arrow->direction);
02019     arrow->stats.sp = arrow->stats.wc; /* save original wc and dam */
02020     arrow->stats.hp = arrow->stats.dam;
02021     arrow->stats.grace = arrow->attacktype;
02022     if (arrow->slaying != NULL)
02023         arrow->spellarg = strdup_local(arrow->slaying);
02024 
02025     /* Note that this was different for monsters - they got their level
02026      * added to the damage.  I think the strength bonus is more proper.
02027      */
02028 
02029     arrow->stats.dam += (QUERY_FLAG(bow, FLAG_NO_STRENGTH) ? 0 : dam_bonus[op->stats.Str])
02030         +bow->stats.dam
02031         +bow->magic
02032         +arrow->magic;
02033 
02034     /* update the speed */
02035     arrow->speed = (float)((QUERY_FLAG(bow, FLAG_NO_STRENGTH) ? 0 : dam_bonus[op->stats.Str])+bow->magic+arrow->magic)/5.0
02036         +(float)bow->stats.dam/7.0;
02037 
02038     if (arrow->speed < 1.0)
02039         arrow->speed = 1.0;
02040     update_ob_speed(arrow);
02041     arrow->speed_left = 0;
02042 
02043     if (op->type == PLAYER) {
02044         /* we don't want overflows of wc (sint), so cap the value - mod and pl should be substracted */
02045         int mod = bow->magic
02046             +arrow->magic
02047             +dex_bonus[op->stats.Dex]
02048             +thaco_bonus[op->stats.Str]
02049             +arrow->stats.wc
02050             +bow->stats.wc
02051             -wc_mod;
02052         int plmod = (op->chosen_skill ? op->chosen_skill->level : op->level);
02053         if (plmod+mod > 140)
02054             plmod = 140-mod;
02055         else if (plmod+mod < -100)
02056             plmod = -100-mod;
02057         arrow->stats.wc = 20-(sint8)plmod-(sint8)mod;
02058 
02059         arrow->level = op->chosen_skill ? op->chosen_skill->level : op->level;
02060     } else {
02061         arrow->stats.wc = op->stats.wc
02062             -bow->magic
02063             -arrow->magic
02064             -arrow->stats.wc
02065             +wc_mod;
02066 
02067         arrow->level = op->level;
02068     }
02069     if (arrow->attacktype == AT_PHYSICAL)
02070         arrow->attacktype |= bow->attacktype;
02071     if (bow->slaying != NULL)
02072         arrow->slaying = add_string(bow->slaying);
02073 
02074     arrow->map = m;
02075     /* If move_type is ever changed, monster.c:monster_use_bow() needs to be changed too. */
02076     arrow->move_type = MOVE_FLY_LOW;
02077     arrow->move_on = MOVE_FLY_LOW|MOVE_WALK;
02078 
02079     tag = arrow->count;
02080     insert_ob_in_map(arrow, m, op, 0);
02081 
02082     if (!was_destroyed(arrow, tag)) {
02083         play_sound_map(SOUND_TYPE_ITEM, arrow, arrow->direction, "fire");
02084         ob_process(arrow);
02085     }
02086 
02087     return 1;
02088 }
02089 
02106 static int player_fire_bow(object *op, int dir) {
02107     int ret = 0, wcmod = 0;
02108 
02109     if (op->contr->bowtype == bow_bestarrow) {
02110         ret = fire_bow(op, pick_arrow_target(op, op->contr->ranges[range_bow]->race, dir), dir, 0, op->x, op->y);
02111     } else if (op->contr->bowtype >= bow_n && op->contr->bowtype <= bow_nw) {
02112         if (!similar_direction(dir, op->contr->bowtype-bow_n+1))
02113             wcmod = -1;
02114         ret = fire_bow(op, NULL, op->contr->bowtype-bow_n+1, wcmod, op->x, op->y);
02115     } else if (op->contr->bowtype == bow_threewide) {
02116         ret = fire_bow(op, NULL, dir, 0, op->x, op->y);
02117         ret |= fire_bow(op, NULL, dir, -5, op->x+freearr_x[absdir(dir+2)], op->y+freearr_y[absdir(dir+2)]);
02118         ret |= fire_bow(op, NULL, dir, -5, op->x+freearr_x[absdir(dir-2)], op->y+freearr_y[absdir(dir-2)]);
02119     } else if (op->contr->bowtype == bow_spreadshot) {
02120         ret |= fire_bow(op, NULL, dir, 0, op->x, op->y);
02121         ret |= fire_bow(op, NULL, absdir(dir-1), -5, op->x, op->y);
02122         ret |= fire_bow(op, NULL, absdir(dir+1), -5, op->x, op->y);
02123 
02124     } else {
02125         /* Simple case */
02126         ret = fire_bow(op, NULL, dir, 0, op->x, op->y);
02127     }
02128     return ret;
02129 }
02130 
02143 static void fire_misc_object(object *op, int dir) {
02144     object *item;
02145     char name[MAX_BUF];
02146 
02147     if (!op->contr->ranges[range_misc]) {
02148         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02149                       "You have no range item readied.", NULL);
02150         return;
02151     }
02152 
02153     item = op->contr->ranges[range_misc];
02154     if (!item->inv) {
02155         LOG(llevError, "Object %s lacks a spell\n", item->name);
02156         return;
02157     }
02158     if (item->type == WAND) {
02159         if (item->stats.food <= 0) {
02160             play_sound_player_only(op->contr, SOUND_TYPE_ITEM, item, 0, "poof");
02161             query_base_name(item, 0, name, MAX_BUF);
02162             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
02163                                  "The %s goes poof.",
02164                                  "The %s goes poof.",
02165                                  name);
02166             return;
02167         }
02168     } else if (item->type == ROD || item->type == HORN) {
02169         if (item->stats.hp < SP_level_spellpoint_cost(item, item->inv, SPELL_HIGHEST)) {
02170             play_sound_player_only(op->contr, SOUND_TYPE_ITEM, item, 0, "poof");
02171             query_base_name(item, 0, name, MAX_BUF);
02172             if (item->type == ROD)
02173                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
02174                                      "The %s whines for a while, but nothing happens.",
02175                                      "The %s whines for a while, but nothing happens.",
02176                                      name);
02177             else
02178                 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_FAILURE,
02179                                      "The %s needs more time to charge.",
02180                                      "The %s needs more time to charge.",
02181                                      name);
02182             return;
02183         }
02184     }
02185 
02186     if (cast_spell(op, item, dir, item->inv, NULL)) {
02187         SET_FLAG(op, FLAG_BEEN_APPLIED); /* You now know something about it */
02188         if (item->type == WAND) {
02189             drain_wand_charge(item);
02190         } else if (item->type == ROD || item->type == HORN) {
02191             drain_rod_charge(item);
02192         }
02193     }
02194 }
02195 
02204 void fire(object *op, int dir) {
02205     int spellcost = 0;
02206 
02207     /* check for loss of invisiblity/hide */
02208     if (action_makes_visible(op))
02209         make_visible(op);
02210 
02211     switch (op->contr->shoottype) {
02212     case range_none:
02213         return;
02214 
02215     case range_bow:
02216         player_fire_bow(op, dir);
02217         return;
02218 
02219     case range_magic: /* Casting spells */
02220         /* BUG? The value in spellcost is never used again it seems. */
02221         spellcost = (cast_spell(op, op, dir, op->contr->ranges[range_magic], op->contr->spellparam[0] ? op->contr->spellparam : NULL));
02222         return;
02223 
02224     case range_misc:
02225         fire_misc_object(op, dir);
02226         return;
02227 
02228     case range_golem: /* Control summoned monsters from scrolls */
02229         if (op->contr->ranges[range_golem] == NULL
02230         || op->contr->golem_count != op->contr->ranges[range_golem]->count) {
02231             op->contr->ranges[range_golem] = NULL;
02232             op->contr->shoottype = range_none;
02233             op->contr->golem_count = 0;
02234         } else
02235             control_golem(op->contr->ranges[range_golem], dir);
02236         return;
02237 
02238     case range_skill:
02239         if (!op->chosen_skill) {
02240             if (op->type == PLAYER)
02241                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02242                               "You have no applicable skill to use.", NULL);
02243             return;
02244         }
02245         (void)do_skill(op, op, op->chosen_skill, dir, NULL);
02246         return;
02247 
02248     case range_builder:
02249         apply_map_builder(op, dir);
02250         return;
02251 
02252     default:
02253         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_ERROR,
02254                       "Illegal shoot type.", NULL);
02255         return;
02256     }
02257 }
02258 
02278 object *find_key(object *pl, object *container, object *door) {
02279     object *tmp, *key;
02280 
02281     /* Should not happen, but sanity checking is never bad */
02282     if (container->inv == NULL)
02283         return NULL;
02284 
02285     /* First, lets try to find a key in the top level inventory */
02286     for (tmp = container->inv; tmp != NULL; tmp = tmp->below) {
02287         if (door->type == DOOR && tmp->type == KEY)
02288             break;
02289         /* For sanity, we should really check door type, but other stuff
02290          * (like containers) can be locked with special keys
02291          */
02292         if (tmp->slaying
02293         && tmp->type == SPECIAL_KEY
02294         && tmp->slaying == door->slaying)
02295             break;
02296     }
02297     /* No key found - lets search inventories now */
02298     /* If we find and use a key in an inventory, return at that time.
02299      * otherwise, if we search all the inventories and still don't find
02300      * a key, return
02301      */
02302     if (!tmp) {
02303         for (tmp = container->inv; tmp != NULL; tmp = tmp->below) {
02304             /* No reason to search empty containers */
02305             if (tmp->type == CONTAINER && tmp->inv) {
02306                 if ((key = find_key(pl, tmp, door)) != NULL)
02307                     return key;
02308             }
02309         }
02310         if (!tmp)
02311             return NULL;
02312     }
02313     /* We get down here if we have found a key.  Now if its in a container,
02314      * see if we actually want to use it
02315      */
02316     if (pl != container) {
02317         /* Only let players use keys in containers */
02318         if (!pl->contr)
02319             return NULL;
02320         /* cases where this fails:
02321          * If we only search the player inventory, return now since we
02322          * are not in the players inventory.
02323          * If the container is not active, return now since only active
02324          * containers can be used.
02325          * If we only search keyrings and the container does not have
02326          * a race/isn't a keyring.
02327          * No checking for all containers - to fall through past here,
02328          * inv must have been an container and must have been active.
02329          *
02330          * Change the color so that the message doesn't disappear with
02331          * all the others.
02332          */
02333         if (pl->contr->usekeys == key_inventory
02334         || !QUERY_FLAG(container, FLAG_APPLIED)
02335         || (pl->contr->usekeys == keyrings && (!container->race || strcmp(container->race, "keys")))) {
02336             char name_tmp[MAX_BUF], name_cont[MAX_BUF];
02337 
02338             query_name(tmp, name_tmp, MAX_BUF);
02339             query_name(container, name_cont, MAX_BUF);
02340             draw_ext_info_format(NDI_UNIQUE|NDI_BROWN, 0, pl,
02341                                  MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO,
02342                                  "The %s in your %s vibrates as you approach the door",
02343                                  "The %s in your %s vibrates as you approach the door",
02344                                  name_tmp, name_cont);
02345             return NULL;
02346         }
02347     }
02348     return tmp;
02349 }
02350 
02361 static int player_attack_door(object *op, object *door) {
02362 
02363     /* If its a door, try to find a use a key.  If we do destroy the door,
02364      * might as well return immediately as there is nothing more to do -
02365      * otherwise, we fall through to the rest of the code.
02366      */
02367     object *key = find_key(op, op, door);
02368 
02369     /* IF we found a key, do some extra work */
02370     if (key) {
02371         object *container = key->env;
02372 
02373         play_sound_map(SOUND_TYPE_GROUND, door, 0, "open");
02374         if (action_makes_visible(op))
02375             make_visible(op);
02376         if (door->inv && (door->inv->type == RUNE || door->inv->type == TRAP))
02377             spring_trap(door->inv, op);
02378         if (door->type == DOOR) {
02379             hit_player(door, 9998, op, AT_PHYSICAL, 1); /* Break through the door */
02380         } else if (door->type == LOCKED_DOOR) {
02381             char name[HUGE_BUF];
02382 
02383             query_short_name(key, name, HUGE_BUF);
02384             draw_ext_info_format(NDI_UNIQUE, NDI_BROWN, op,
02385                                  MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
02386                                  "You open the door with the %s",
02387                                  "You open the door with the %s",
02388                                  name);
02389             remove_locked_door(door); /* remove door without violence ;-) */
02390         }
02391         /* Do this after we print the message */
02392         decrease_ob(key); /* Use up one of the keys */
02393         /* Need to update the weight the container the key was in */
02394         if (container != op)
02395             esrv_update_item(UPD_WEIGHT, op, container);
02396         return 1; /* Nothing more to do below */
02397     } else if (door->type == LOCKED_DOOR) {
02398         /* Might as well return now - no other way to open this */
02399         draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_NOKEY,
02400                       door->msg, door->msg);
02401         return 1;
02402     }
02403     return 0;
02404 }
02405 
02419 void move_player_attack(object *op, int dir) {
02420     object *tmp, *mon, *tpl, *mon_owner;
02421     sint16 nx, ny;
02422     int on_battleground;
02423     mapstruct *m;
02424 
02425     if (op->contr->transport)
02426         tpl = op->contr->transport;
02427     else
02428         tpl = op;
02429     nx = freearr_x[dir]+tpl->x;
02430     ny = freearr_y[dir]+tpl->y;
02431 
02432     on_battleground = op_on_battleground(tpl, NULL, NULL, NULL);
02433 
02434     /* If braced, or can't move to the square, and it is not out of the
02435      * map, attack it.  Note order of if statement is important - don't
02436      * want to be calling move_ob if braced, because move_ob will move the
02437      * player.  This is a pretty nasty hack, because if we could
02438      * move to some space, it then means that if we are braced, we should
02439      * do nothing at all.  As it is, if we are braced, we go through
02440      * quite a bit of processing.  However, it probably is less than what
02441      * move_ob uses.
02442      */
02443     if ((op->contr->braced || !move_ob(tpl, dir, tpl)) && !out_of_map(tpl->map, nx, ny)) {
02444         if (OUT_OF_REAL_MAP(tpl->map, nx, ny)) {
02445             m = get_map_from_coord(tpl->map, &nx, &ny);
02446             if (!m)
02447                 return; /* Don't think this should happen */
02448         } else
02449             m = tpl->map;
02450 
02451         if ((tmp = GET_MAP_OB(m, nx, ny)) == NULL) {
02452             /* LOG(llevError, "player_move_attack: GET_MAP_OB returns NULL, but player can not more there.\n");*/
02453             return;
02454         }
02455 
02456         mon = NULL;
02457         /* Go through all the objects, and find ones of interest. Only stop if
02458          * we find a monster - that is something we know we want to attack.
02459          * if its a door or barrel (can roll) see if there may be monsters
02460          * on the space
02461          */
02462         while (tmp != NULL) {
02463             if (tmp == op) {
02464                 tmp = tmp->above;
02465                 continue;
02466             }
02467             if (QUERY_FLAG(tmp, FLAG_ALIVE)) {
02468                 mon = tmp;
02469                 /* Gros: Objects like (pass-through) doors are alive, but haven't
02470                  * their monster flag set - so this is a good way attack real
02471                  * monsters in priority.
02472                  */
02473                 if (QUERY_FLAG(tmp, FLAG_MONSTER))
02474                     break;
02475             }
02476             if (tmp->type == LOCKED_DOOR || QUERY_FLAG(tmp, FLAG_CAN_ROLL))
02477                 mon = tmp;
02478             tmp = tmp->above;
02479         }
02480 
02481         if (mon == NULL)  /* This happens anytime the player tries to move */
02482             return;  /* into a wall */
02483 
02484         if (mon->head != NULL)
02485             mon = mon->head;
02486 
02487         if ((mon->type == DOOR && mon->stats.hp >= 0) || (mon->type == LOCKED_DOOR))
02488             if (player_attack_door(op, mon))
02489                 return;
02490 
02491         /* The following deals with possibly attacking peaceful
02492          * or frienddly creatures.  Basically, all players are considered
02493          * unaggressive.  If the moving player has peaceful set, then the
02494          * object should be pushed instead of attacked.  It is assumed that
02495          * if you are braced, you will not attack friends accidently,
02496          * and thus will not push them.
02497          */
02498 
02499         /* If the creature is a pet, push it even if the player is not
02500          * peaceful.  Our assumption is the creature is a pet if the
02501          * player owns it and it is either friendly or unagressive.
02502          */
02503         mon_owner = get_owner(mon);
02504         if ((op->type == PLAYER)
02505         && (mon_owner == op || (mon_owner != NULL && mon_owner->type == PLAYER && mon_owner->contr->party != NULL && mon_owner->contr->party == op->contr->party))
02506         && (QUERY_FLAG(mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG(mon, FLAG_FRIENDLY))) {
02507             /* If we're braced, we don't want to switch places with it */
02508             if (op->contr->braced)
02509                 return;
02510             play_sound_map(SOUND_TYPE_LIVING, mon, dir, "push");
02511             (void)push_ob(mon, dir, op);
02512             if (op->contr->tmp_invis || op->hide)
02513                 make_visible(op);
02514             return;
02515         }
02516 
02517         /* in certain circumstances, you shouldn't attack friendly
02518          * creatures.  Note that if you are braced, you can't push
02519          * someone, but put it inside this loop so that you won't
02520          * attack them either.
02521          */
02522         if ((mon->type == PLAYER || mon->enemy != op)
02523         && (mon->type == PLAYER || QUERY_FLAG(mon, FLAG_UNAGGRESSIVE) || QUERY_FLAG(mon, FLAG_FRIENDLY))
02524         && (op->contr->peaceful && !on_battleground)) {
02525             if (!op->contr->braced) {
02526                 play_sound_map(SOUND_TYPE_LIVING, mon, dir, "push");
02527                 (void)push_ob(mon, dir, op);
02528             } else {
02529                 draw_ext_info(0, 0, op, MSG_TYPE_ATTACK, MSG_TYPE_ATTACK_NOATTACK,
02530                               "You withhold your attack", NULL);
02531             }
02532             if (op->contr->tmp_invis || op->hide)
02533                 make_visible(op);
02534         }
02535 
02536         /* If the object is a boulder or other rollable object, then
02537          * roll it if not braced.  You can't roll it if you are braced.
02538          */
02539         else if (QUERY_FLAG(mon, FLAG_CAN_ROLL) && (!op->contr->braced)) {
02540             recursive_roll(mon, dir, op);
02541             if (action_makes_visible(op))
02542                 make_visible(op);
02543 
02544         /* Any generic living creature.  Including things like doors.
02545          * Way it works is like this:  First, it must have some hit points
02546          * and be living.  Then, it must be one of the following:
02547          * 1) Not a player, 2) A player, but of a different party.  Note
02548          * that party_number -1 is no party, so attacks can still happen.
02549          */
02550 
02551         } else if ((mon->stats.hp >= 0)
02552         && QUERY_FLAG(mon, FLAG_ALIVE)
02553         && ((mon->type != PLAYER || op->contr->party == NULL || op->contr->party != mon->contr->party))) {
02554             /* If the player hasn't hit something this tick, and does
02555              * so, give them speed boost based on weapon speed.  Doing
02556              * it here is better than process_players2, which basically
02557              * incurred a 1 tick offset.
02558              */
02559             if (!op->contr->has_hit) {
02560                 op->speed_left += op->speed / op->contr->weapon_sp;
02561 
02562                 op->contr->has_hit = 1; /* The last action was to hit, so use weapon_sp */
02563             }
02564 
02565             skill_attack(mon, op, 0, NULL, NULL);
02566 
02567             /* If attacking another player, that player gets automatic
02568              * hitback, and doesn't loose luck either.
02569              * Disable hitback on the battleground or if the target is
02570              * the wiz.
02571              */
02572             if (mon->type == PLAYER
02573             && mon->stats.hp >= 0
02574             && !mon->contr->has_hit
02575             && !on_battleground
02576             && !QUERY_FLAG(mon, FLAG_WIZ)) {
02577                 short luck = mon->stats.luck;
02578                 mon->contr->has_hit = 1;
02579                 skill_attack(op, mon, 0, NULL, NULL);
02580                 mon->stats.luck = luck;
02581             }
02582             if (action_makes_visible(op))
02583                 make_visible(op);
02584         }
02585     } /* if player should attack something */
02586 }
02587 
02594 static void update_transport_block(object *transport, int dir) {
02595     object *part;
02596     int sx, sy, x, y;
02597 
02598     get_multi_size(transport, &sx, &sy, NULL, NULL);
02599     assert(sx == sy);
02600 
02601     if (dir == 1 || dir == 5) {
02602         part = transport;
02603         for (y = 0; y <= sy; y++) {
02604             for (x = 0; x < sx; x++) {
02605                 part->move_type = transport->move_type;
02606                 part = part->more;
02607             }
02608             part->move_type = 0;
02609             part = part->more;
02610         }
02611     } else if (dir == 3 || dir == 7) {
02612         part = transport;
02613         for (y = 0; y < sy; y++) {
02614             for (x = 0; x <= sx; x++) {
02615                 part->move_type = transport->move_type;
02616                 part = part->more;
02617             }
02618         }
02619         while (part) {
02620             part->move_type = 0;
02621             part = part->more;
02622         }
02623     } else {
02624         for (part = transport; part; part = part->more) {
02625             part->move_type = transport->move_type;
02626         }
02627     }
02628 }
02629 
02639 static int turn_one_transport(object *transport, object *captain, int dir) {
02640     int x, y, scroll_dir = 0;
02641 
02642     assert(transport->type == TRANSPORT);
02643 
02644     x = transport->x;
02645     y = transport->y;
02646 
02647     if (transport->direction == 1 && dir == 8) {
02648         x--;
02649     } else if (transport->direction == 2 && dir == 3) {
02650         y++;
02651     } else if (transport->direction == 3 && dir == 2) {
02652         y--;
02653     } else if (transport->direction == 5 && dir == 6) {
02654         x--;
02655     } else if (transport->direction == 6 && dir == 5) {
02656         x++;
02657     } else if (transport->direction == 7 && dir == 8) {
02658         y--;
02659     } else if (transport->direction == 8 && dir == 7) {
02660         y++;
02661     } else if (transport->direction == 8 && dir == 1) {
02662         x++;
02663     }
02664 
02665     update_transport_block(transport, dir);
02666     remove_ob(transport);
02667     if (ob_blocked(transport, transport->map, x, y)) {
02668         update_transport_block(transport, transport->direction);
02669         insert_ob_in_map(transport, transport->map, NULL, 0);
02670         return 2;
02671     }
02672 
02673     if (x != transport->x || y != transport->y) {
02674         object *pl;
02675 
02676 /*        assert(scroll_dir != 0);*/
02677 
02678         for (pl = transport->inv; pl; pl = pl->below) {
02679             if (pl->type == PLAYER) {
02680                 pl->contr->do_los = 1;
02681                 pl->map = transport->map;
02682                 pl->x = x;
02683                 pl->y = y;
02684                 esrv_map_scroll(&pl->contr->socket, freearr_x[scroll_dir], freearr_y[scroll_dir]);
02685                 pl->contr->socket.update_look = 1;
02686                 pl->contr->socket.look_position = 0;
02687             }
02688         }
02689     }
02690 
02691     insert_ob_in_map_at(transport, transport->map, NULL, 0, x, y);
02692     transport->direction = dir;
02693     transport->facing = dir;
02694     animate_object(transport, dir);
02695     captain->direction = dir;
02696     return 1;
02697 }
02698 
02711 static int turn_transport(object *transport, object *captain, int dir) {
02712     assert(transport->type == TRANSPORT);
02713 
02714     if (get_ob_key_value(transport, "turnable_transport") == NULL) {
02715         transport->direction = dir;
02716         transport->facing = dir;
02717         animate_object(transport, dir);
02718         captain->direction = dir;
02719         return 0;
02720     }
02721 
02722     if (transport->direction == dir)
02723         return 0;
02724 
02725     if (absdir(transport->direction-dir) > 2)
02726         return turn_one_transport(transport, captain, absdir(transport->direction+1));
02727     else
02728         return turn_one_transport(transport, captain, absdir(transport->direction-1));
02729 }
02730 
02741 int move_player(object *op, int dir) {
02742     int pick;
02743     object *transport = op->contr->transport;
02744 
02745     if (!transport && (op->map == NULL || op->map->in_memory != MAP_IN_MEMORY))
02746         return 0;
02747 
02748     /* Sanity check: make sure dir is valid */
02749     if ((dir < 0) || (dir >= 9)) {
02750         LOG(llevError, "move_player: invalid direction %d\n", dir);
02751         return 0;
02752     }
02753 
02754     /* peterm:  added following line */
02755     if (QUERY_FLAG(op, FLAG_CONFUSED) && dir)
02756         dir = absdir(dir+RANDOM()%3+RANDOM()%3-2);
02757 
02758     op->facing = dir;
02759 
02760     if (!transport && op->hide)
02761         do_hidden_move(op);
02762 
02763     if (transport) {
02764         int turn;
02765 
02766         /* transport->contr is set up for the person in charge of the boat.
02767          * if that isn't this person, he can't steer it, etc
02768          */
02769         if (transport->contr != op->contr)
02770             return 0;
02771 
02772         /* Transport can't move.  But update dir so it at least
02773          * will point in the same direction if player is running.
02774          */
02775         if (transport->speed_left < 0.0) {
02776             return 0;
02777         }
02778         /* Remove transport speed.  Give player just a little speed -
02779          * enough so that they will get an action again quickly.
02780          */
02781         transport->speed_left -= 1.0;
02782         if (op->speed_left < 0.0)
02783             op->speed_left = -0.01;
02784 
02785         turn = turn_transport(transport, op, dir);
02786         if (turn != 0)
02787             return 0;
02788     } else {
02789         /* it is important to change the animation now, as fire or move_player_attack can start a compound animation,
02790          * and leave us with state = 0, which we don't want to change again. */
02791         op->state++; /* player moved, so change animation. */
02792         animate_object(op, op->facing);
02793     }
02794 
02795     if (op->contr->fire_on) {
02796         fire(op, dir);
02797     } else
02798         move_player_attack(op, dir);
02799 
02800     pick = check_pick(op);
02801 
02802 
02803     /* Add special check for newcs players and fire on - this way, the
02804      * server can handle repeat firing.
02805      */
02806     if (op->contr->fire_on || (op->contr->run_on && pick != 0)) {
02807         op->direction = dir;
02808     } else {
02809         op->direction = 0;
02810     }
02811     return 0;
02812 }
02813 
02826 int handle_newcs_player(object *op) {
02827     if (op->contr->hidden) {
02828         op->invisible = 1000;
02829         /* the socket code flasehs the player visible/invisible
02830          * depending on the value if invisible, so we need to
02831          * alternate it here for it to work correctly.
02832          */
02833         if (pticks&2)
02834             op->invisible--;
02835     } else if (op->invisible && !(QUERY_FLAG(op, FLAG_MAKE_INVIS))) {
02836         op->invisible--;
02837         if (!op->invisible) {
02838             make_visible(op);
02839             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_END,
02840                           "Your invisibility spell runs out.", NULL);
02841         }
02842     }
02843 
02844     if (QUERY_FLAG(op, FLAG_SCARED)) {
02845         flee_player(op);
02846         /* If player is still scared, that is his action for this tick */
02847         if (QUERY_FLAG(op, FLAG_SCARED)) {
02848             op->speed_left--;
02849             return 0;
02850         }
02851     }
02852 
02853     /* I've been seeing crashes where the golem has been destroyed, but
02854      * the player object still points to the defunct golem.  The code that
02855      * destroys the golem looks correct, and it doesn't always happen, so
02856      * put this in a a workaround to clean up the golem pointer.
02857      */
02858     if (op->contr->ranges[range_golem]
02859     && ((op->contr->golem_count != op->contr->ranges[range_golem]->count) || QUERY_FLAG(op->contr->ranges[range_golem], FLAG_REMOVED))) {
02860         op->contr->ranges[range_golem] = NULL;
02861         op->contr->golem_count = 0;
02862     }
02863 
02864     /* call this here - we also will call this in do_ericserver, but
02865      * the players time has been increased when doericserver has been
02866      * called, so we recheck it here.
02867      */
02868     handle_client(&op->contr->socket, op->contr);
02869     if (op->speed_left < 0)
02870         return 0;
02871 
02872     if (op->direction && (op->contr->run_on || op->contr->fire_on)) {
02873         /* All move commands take 1 tick, at least for now */
02874         op->speed_left--;
02875 
02876         /* Instead of all the stuff below, let move_player take care
02877          * of it.  Also, some of the skill stuff is only put in
02878          * there, as well as the confusion stuff.
02879          */
02880         move_player(op, op->direction);
02881         if (op->speed_left > 0)
02882             return 1;
02883         else
02884             return 0;
02885     }
02886     return 0;
02887 }
02888 
02899 static int save_life(object *op) {
02900     object *tmp;
02901 
02902     if (!QUERY_FLAG(op, FLAG_LIFESAVE))
02903         return 0;
02904 
02905     for (tmp = op->inv; tmp != NULL; tmp = tmp->below)
02906         if (QUERY_FLAG(tmp, FLAG_APPLIED) && QUERY_FLAG(tmp, FLAG_LIFESAVE)) {
02907             char name[MAX_BUF];
02908 
02909             query_name(tmp, name, MAX_BUF);
02910             play_sound_map(SOUND_TYPE_ITEM, tmp, 0, "evaporate");
02911             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
02912                                  "Your %s vibrates violently, then evaporates.",
02913                                  "Your %s vibrates violently, then evaporates.",
02914                                  name);
02915             remove_ob(tmp);
02916             free_object(tmp);
02917             CLEAR_FLAG(op, FLAG_LIFESAVE);
02918             if (op->stats.hp < 0)
02919                 op->stats.hp = op->stats.maxhp;
02920             if (op->stats.food < 0)
02921                 op->stats.food = 999;
02922             fix_object(op);
02923             return 1;
02924         }
02925     LOG(llevError, "Error: LIFESAVE set without applied object.\n");
02926     CLEAR_FLAG(op, FLAG_LIFESAVE);
02927     enter_player_savebed(op); /* bring him home. */
02928     return 0;
02929 }
02930 
02943 void remove_unpaid_objects(object *op, object *env, int free_items) {
02944     object *next;
02945 
02946     while (op) {
02947         next = op->below; /* Make sure we have a good value, in case
02948                            * we remove object 'op'
02949                            */
02950         if (QUERY_FLAG(op, FLAG_UNPAID)) {
02951             remove_ob(op);
02952             if (free_items)
02953                 free_object(op);
02954             else {
02955                 op->x = env->x;
02956                 op->y = env->y;
02957                 insert_ob_in_map(op, env->map, NULL, 0);
02958             }
02959         } else if (op->inv)
02960             remove_unpaid_objects(op->inv, env, free_items);
02961         op = next;
02962     }
02963 }
02964 
02982 static const char *gravestone_text(object *op, char *buf2, int len) {
02983     char buf[MAX_BUF];
02984     time_t now = time(NULL);
02985 
02986     strncpy(buf2, "                 R.I.P.\n\n", len);
02987     if (op->type == PLAYER)
02988         snprintf(buf, sizeof(buf), "%s the %s\n", op->name, op->contr->title);
02989     else
02990         snprintf(buf, sizeof(buf), "%s\n", op->name);
02991     strncat(buf2, "                    ",  20-strlen(buf)/2);
02992     strncat(buf2, buf, len-strlen(buf2)-1);
02993     if (op->type == PLAYER)
02994         snprintf(buf, sizeof(buf), "who was in level %d when killed\n", op->level);
02995     else
02996         snprintf(buf, sizeof(buf), "who was in level %d when died.\n\n", op->level);
02997     strncat(buf2, "                    ", 20-strlen(buf)/2);
02998     strncat(buf2, buf, len-strlen(buf2)-1);
02999     if (op->type == PLAYER) {
03000         snprintf(buf, sizeof(buf), "by %s.\n\n", op->contr->killer);
03001         strncat(buf2, "                    ",  21-strlen(buf)/2);
03002         strncat(buf2, buf, len-strlen(buf2)-1);
03003     }
03004     strftime(buf, MAX_BUF, "%b %d %Y\n", localtime(&now));
03005     strncat(buf2, "                    ",  20-strlen(buf)/2);
03006     strncat(buf2, buf, len-strlen(buf2)-1);
03007     return buf2;
03008 }
03009 
03017 void do_some_living(object *op) {
03018     int last_food = op->stats.food;
03019     int gen_hp, gen_sp, gen_grace;
03020     int over_hp, over_sp, over_grace;
03021     int i;
03022     int rate_hp = 1200;
03023     int rate_sp = 2500;
03024     int rate_grace = 2000;
03025     const int max_hp = 1;
03026     const int max_sp = 1;
03027     const int max_grace = 1;
03028 
03029     if (op->contr->outputs_sync) {
03030         for (i = 0; i < NUM_OUTPUT_BUFS; i++)
03031             if (op->contr->outputs[i].buf != NULL
03032             && (op->contr->outputs[i].first_update+op->contr->outputs_sync) < pticks)
03033                 flush_output_element(op, &op->contr->outputs[i]);
03034     }
03035 
03036     if (op->contr->state == ST_PLAYING) {
03037         /* these next three if clauses make it possible to SLOW DOWN
03038            hp/grace/spellpoint regeneration. */
03039         if (op->contr->gen_hp >= 0)
03040             gen_hp = (op->contr->gen_hp+1)*op->stats.maxhp;
03041         else {
03042             gen_hp = op->stats.maxhp;
03043             rate_hp -= rate_hp/2*op->contr->gen_hp;
03044         }
03045         if (op->contr->gen_sp >= 0)
03046             gen_sp = (op->contr->gen_sp+1)*op->stats.maxsp;
03047         else {
03048             gen_sp = op->stats.maxsp;
03049             rate_sp -= rate_sp/2*op->contr->gen_sp;
03050         }
03051         if (op->contr->gen_grace >= 0)
03052             gen_grace = (op->contr->gen_grace+1)*op->stats.maxgrace;
03053         else {
03054             gen_grace = op->stats.maxgrace;
03055             rate_grace -= rate_grace/2*op->contr->gen_grace;
03056         }
03057 
03058         /* Regenerate Spell Points */
03059         if (op->contr->ranges[range_golem] == NULL && --op->last_sp < 0) {
03060             gen_sp = gen_sp*10/MAX(op->contr->gen_sp_armour, 10);
03061             if (op->stats.sp < op->stats.maxsp) {
03062                 op->stats.sp++;
03063                 /* dms do not consume food */
03064                 if (!QUERY_FLAG(op, FLAG_WIZ)) {
03065                     op->stats.food--;
03066                     if (op->contr->digestion < 0)
03067                         op->stats.food += op->contr->digestion;
03068                     else if (op->contr->digestion > 0
03069                     && random_roll(0, op->contr->digestion, op, PREFER_HIGH))
03070                         op->stats.food = last_food;
03071                 }
03072             }
03073             if (max_sp > 1) {
03074                 over_sp = (gen_sp+10)/rate_sp;
03075                 if (over_sp > 0) {
03076                     if (op->stats.sp < op->stats.maxsp) {
03077                         op->stats.sp += MIN(over_sp, max_sp);
03078                         if (random_roll(0, rate_sp-1, op, PREFER_LOW) > ((gen_sp+10)%rate_sp))
03079                             op->stats.sp--;
03080                         if (op->stats.sp > op->stats.maxsp)
03081                             op->stats.sp = op->stats.maxsp;
03082                     }
03083                     op->last_sp = 0;
03084                 } else {
03085                     op->last_sp = rate_sp/(MAX(gen_sp, 20)+10);
03086                 }
03087             } else {
03088                 op->last_sp = rate_sp/(MAX(gen_sp, 20)+10);
03089             }
03090         }
03091 
03092         /* Regenerate Grace */
03093         /* I altered this a little - maximum grace is ony achieved through prayer -b.t.*/
03094         if (--op->last_grace < 0) {
03095             if (op->stats.grace < op->stats.maxgrace/2)
03096                 op->stats.grace++; /* no penalty in food for regaining grace */
03097             if (max_grace > 1) {
03098                 over_grace = (MAX(gen_grace, 20)+10)/rate_grace;
03099                 if (over_grace > 0) {
03100                     op->stats.sp += over_grace+(random_roll(0, rate_grace-1, op, PREFER_HIGH) > ((MAX(gen_grace, 20)+10)%rate_grace)) ? -1 : 0;
03101                     op->last_grace = 0;
03102                 } else {
03103                     op->last_grace = rate_grace/(MAX(gen_grace, 20)+10);
03104                 }
03105             } else {
03106                 op->last_grace = rate_grace/(MAX(gen_grace, 20)+10);
03107             }
03108             /* wearing stuff doesn't detract from grace generation. */
03109         }
03110 
03111         /* Regenerate Hit Points (unless you are a wraith player) */
03112         if (--op->last_heal < 0 && !is_wraith_pl(op)) {
03113             if (op->stats.hp < op->stats.maxhp) {
03114                 op->stats.hp++;
03115                 /* dms do not consume food */
03116                 if (!QUERY_FLAG(op, FLAG_WIZ)) {
03117                     op->stats.food--;
03118                     if (op->contr->digestion < 0)
03119                         op->stats.food += op->contr->digestion;
03120                     else if (op->contr->digestion > 0
03121                     && random_roll(0, op->contr->digestion, op, PREFER_HIGH))
03122                         op->stats.food = last_food;
03123                 }
03124             }
03125             if (max_hp > 1 && !is_wraith_pl(op)) {
03126                 over_hp = (MAX(gen_hp, 20)+10)/rate_hp;
03127                 if (over_hp > 0) {
03128                     op->stats.sp += over_hp+(RANDOM()%rate_hp > ((MAX(gen_hp, 20)+10)%rate_hp)) ? -1 : 0;
03129                     op->last_heal = 0;
03130                 } else {
03131                     op->last_heal = rate_hp/(MAX(gen_hp, 20)+10);
03132                 }
03133             } else {
03134                 op->last_heal = rate_hp/(MAX(gen_hp, 20)+10);
03135             }
03136         }
03137 
03138         /* Digestion */
03139         if (--op->last_eat < 0) {
03140             int bonus = MAX(op->contr->digestion, 0);
03141             int penalty = MAX(-op->contr->digestion, 0);
03142             if (op->contr->gen_hp > 0)
03143                 op->last_eat = 25*(1+bonus)/(op->contr->gen_hp+penalty+1);
03144             else
03145                 op->last_eat = 25*(1+bonus)/(penalty+1);
03146             /* dms do not consume food */
03147             if (!QUERY_FLAG(op, FLAG_WIZ))
03148                 op->stats.food--;
03149         }
03150     }
03151 
03152     if (op->contr->state == ST_PLAYING && op->stats.food < 0 && op->stats.hp >= 0) {
03153         if (is_wraith_pl(op))
03154             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE, "You feel a hunger for living flesh.", NULL);
03155         else {
03156             object *tmp, *flesh = NULL;
03157 
03158             for (tmp = op->inv; tmp != NULL; tmp = tmp->below) {
03159                 if (!QUERY_FLAG(tmp, FLAG_UNPAID)) {
03160                     if (tmp->type == FOOD || tmp->type == DRINK || tmp->type == POISON) {
03161                         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
03162                                       "You blindly grab for a bite of food.", NULL);
03163                         manual_apply(op, tmp, 0);
03164                         if (op->stats.food >= 0 || op->stats.hp < 0)
03165                             break;
03166                     } else if (tmp->type == FLESH)
03167                         flesh = tmp;
03168                 } /* End if paid for object */
03169             } /* end of for loop */
03170             /* If player is still starving, it means they don't have any food, so
03171              * eat flesh instead.
03172              */
03173             if (op->stats.food < 0 && op->stats.hp >= 0 && flesh) {
03174                 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_REMOVE,
03175                               "You blindly grab for a bite of food.", NULL);
03176                 manual_apply(op, flesh, 0);
03177             }
03178         } /* end not wraith */
03179     } /* end if player is starving */
03180 
03181     while (op->stats.food < 0 && op->stats.hp > 0)
03182         op->stats.food++,
03183         op->stats.hp--;
03184 
03185     if (!op->contr->state && !QUERY_FLAG(op, FLAG_WIZ) && (op->stats.hp < 0 || op->stats.food < 0))
03186         kill_player(op);
03187 }
03188 
03195 static void loot_object(object *op) {
03196     object *tmp, *tmp2, *next;
03197 
03198     if (op->container) { /* close open sack first */
03199         apply_container(op, op->container);
03200     }
03201 
03202     for (tmp = op->inv; tmp != NULL; tmp = next) {
03203         next = tmp->below;
03204         if (tmp->type == EXPERIENCE || tmp->invisible)
03205             continue;
03206         remove_ob(tmp);
03207         tmp->x = op->x,
03208         tmp->y = op->y;
03209         if (tmp->type == CONTAINER) { /* empty container to ground */
03210             loot_object(tmp);
03211         }
03212         if (!QUERY_FLAG(tmp, FLAG_UNIQUE)
03213         && (QUERY_FLAG(tmp, FLAG_STARTEQUIP) || QUERY_FLAG(tmp, FLAG_NO_DROP) || !(RANDOM()%3))) {
03214             if (tmp->nrof > 1) {
03215                 tmp2 = get_split_ob(tmp, 1+RANDOM()%(tmp->nrof-1), NULL, 0);
03216                 free_object(tmp2);
03217                 insert_ob_in_map(tmp, op->map, NULL, 0);
03218             } else
03219                 free_object(tmp);
03220         } else
03221             insert_ob_in_map(tmp, op->map, NULL, 0);
03222     }
03223 }
03224 
03238 void kill_player(object *op) {
03239     char buf[MAX_BUF];
03240     int x, y, i;
03241     mapstruct *map;  /*  this is for resurrection */
03242     int z;
03243     int num_stats_lose;
03244     int lost_a_stat;
03245     int lose_this_stat;
03246     int this_stat;
03247     int will_kill_again;
03248     archetype *at;
03249     object *tmp;
03250     archetype *trophy;
03251 
03252     if (save_life(op))
03253         return;
03254 
03255     /* If player dies on BATTLEGROUND, no stat/exp loss! For Combat-Arenas
03256      * in cities ONLY!!! It is very important that this doesn't get abused.
03257      * Look at op_on_battleground() for more info       --AndreasV
03258      */
03259     if (op_on_battleground(op, &x, &y, &trophy)) {
03260         draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_VICTIM,  MSG_TYPE_VICTIM_DIED,
03261                       "You have been defeated in combat!\n"
03262                       "Local medics have saved your life...",
03263                       NULL);
03264 
03265         /* restore player */
03266         at = find_archetype("poisoning");
03267         tmp = present_arch_in_ob(at, op);
03268         if (tmp) {
03269             remove_ob(tmp);
03270             free_object(tmp);
03271             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03272                           "Your body feels cleansed", NULL);
03273         }
03274 
03275         at = find_archetype("confusion");
03276         tmp = present_arch_in_ob(at, op);
03277         if (tmp) {
03278             remove_ob(tmp);
03279             free_object(tmp);
03280             draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03281                           "Your mind feels clearer", NULL);
03282         }
03283 
03284         cure_disease(op, NULL);  /* remove any disease */
03285         op->stats.hp = op->stats.maxhp;
03286         if (op->stats.food <= 0)
03287           op->stats.food = 999;
03288 
03289         /* create a bodypart-trophy to make the winner happy */
03290         tmp = arch_to_object(trophy);
03291         if (tmp != NULL) {
03292             snprintf(buf, sizeof(buf), "%s's %s", op->name, tmp->name);
03293             tmp->name = add_string(buf);
03294             if (tmp->type == FLESH)
03295                 snprintf(buf, sizeof(buf), "  This %s has been cut off %s\n"
03296                          "  the %s, when he was defeated at\n"
03297                          "  level %d by %s.\n",
03298                          tmp->name, op->name, op->contr->title,
03299                          (int)(op->level), op->contr->killer);
03300             else
03301                 snprintf(buf, sizeof(buf), "  This %s has been taken from %s\n"
03302                          "  the %s, when he was defeated at\n"
03303                          "  level %d by %s.\n",
03304                          tmp->name, op->name, op->contr->title,
03305                          (int)(op->level), op->contr->killer);
03306             tmp->msg = add_string(buf);
03307             tmp->type = 0;
03308             tmp->value = 0;
03309             tmp->material = 0;
03310             tmp->materialname = NULL;
03311             tmp->x = op->x,
03312             tmp->y = op->y;
03313             insert_ob_in_map(tmp, op->map, op, 0);
03314         }
03315 
03316         /* teleport defeated player to new destination*/
03317         transfer_ob(op, x, y, 0, NULL);
03318         op->contr->braced = 0;
03319         return;
03320     }
03321 
03322     /* Lauwenmark: Handle for plugin death event */
03323     if (execute_event(op, EVENT_DEATH, NULL, NULL, NULL, SCRIPT_FIX_ALL) != 0)
03324         return;
03325 
03326     /* Lauwenmark: Handle for the global death event */
03327     execute_global_event(EVENT_PLAYER_DEATH, op);
03328     if (op->stats.food < 0) {
03329         if (op->contr->explore) {
03330             draw_ext_info(NDI_UNIQUE, 0, op,
03331                           MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03332                           "You would have starved, but you are "
03333                           "in explore mode, so...", NULL);
03334             op->stats.food = 999;
03335             return;
03336         }
03337         snprintf(buf, sizeof(buf), "%s starved to death.", op->name);
03338         strcpy(op->contr->killer, "starvation");
03339     } else {
03340         if (op->contr->explore) {
03341             draw_ext_info(NDI_UNIQUE, 0, op,
03342                           MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03343                           "You would have died, but you are "
03344                           "in explore mode, so...", NULL);
03345             op->stats.hp = op->stats.maxhp;
03346             return;
03347         }
03348         snprintf(buf, sizeof(buf), "%s died.", op->name);
03349     }
03350     play_sound_player_only(op->contr, SOUND_TYPE_LIVING, op, 0, "death");
03351 
03352     /*  save the map location for corpse, gravestone*/
03353     x = op->x;
03354     y = op->y;
03355     map = op->map;
03356 
03357     if (settings.not_permadeth == TRUE) {
03358         /* NOT_PERMADEATH code.  This basically brings the character back to
03359          * life if they are dead - it takes some exp and a random stat.
03360          * See the config.h file for a little more in depth detail about this.
03361          */
03362 
03363         /* Basically two ways to go - remove a stat permanently, or just
03364          * make it depletion.  This bunch of code deals with that aspect
03365          * of death.
03366          */
03367 
03368         if (settings.balanced_stat_loss) {
03369             /* If stat loss is permanent, lose one stat only. */
03370             /* Lower level chars don't lose as many stats because they suffer
03371                more if they do. */
03372             /* Higher level characters can afford things such as potions of
03373                restoration, or better, stat potions. So we slug them that
03374                little bit harder. */
03375             /* GD */
03376             if (settings.stat_loss_on_death)
03377                 num_stats_lose = 1;
03378             else
03379                 num_stats_lose = 1+op->level/BALSL_NUMBER_LOSSES_RATIO;
03380         } else {
03381             num_stats_lose = 1;
03382         }
03383         lost_a_stat = 0;
03384 
03385         for (z = 0; z < num_stats_lose; z++) {
03386             if (settings.stat_loss_on_death) {
03387                 /* Pick a random stat and take a point off it.  Tell the player
03388                  * what he lost.
03389                  */
03390                 i = RANDOM()%7;
03391                 change_attr_value(&(op->stats), i, -1);
03392                 check_stat_bounds(&(op->stats));
03393                 change_attr_value(&(op->contr->orig_stats), i, -1);
03394                 check_stat_bounds(&(op->contr->orig_stats));
03395                 draw_ext_info(NDI_UNIQUE, 0, op,
03396                               MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_STAT_LOSS,
03397                               lose_msg[i], lose_msg[i]);
03398                 lost_a_stat = 1;
03399             } else {
03400                 /* deplete a stat */
03401                 archetype *deparch = find_archetype("depletion");
03402                 object *dep;
03403 
03404                 i = RANDOM()%7;
03405                 dep = present_arch_in_ob(deparch, op);
03406                 if (!dep) {
03407                     dep = arch_to_object(deparch);
03408                     insert_ob_in_ob(dep, op);
03409                 }
03410                 lose_this_stat = 1;
03411                 if (settings.balanced_stat_loss) {
03412                     /* GD */
03413                     /* Get the stat that we're about to deplete. */
03414                     this_stat = get_attr_value(&(dep->stats), i);
03415                     if (this_stat < 0) {
03416                         int loss_chance = 1+op->level/BALSL_LOSS_CHANCE_RATIO;
03417                         int keep_chance = this_stat*this_stat;
03418                         /* Yes, I am paranoid. Sue me. */
03419                         if (keep_chance < 1)
03420                             keep_chance = 1;
03421 
03422                         /* There is a maximum depletion total per level. */
03423                         if (this_stat < -1-op->level/BALSL_MAX_LOSS_RATIO) {
03424                             lose_this_stat = 0;
03425                             /* Take loss chance vs keep chance to see if we
03426                                retain the stat. */
03427                         } else {
03428                             if (random_roll(0, loss_chance+keep_chance-1, op, PREFER_LOW) < keep_chance)
03429                                 lose_this_stat = 0;
03430                             /* LOG(llevDebug, "Determining stat loss. Stat: %d Keep: %d Lose: %d Result: %s.\n", this_stat, keep_chance, loss_chance, lose_this_stat ? "LOSE" : "KEEP"); */
03431                         }
03432                     }
03433                 }
03434 
03435                 if (lose_this_stat) {
03436                     this_stat = get_attr_value(&(dep->stats), i);
03437                     /* We could try to do something clever like find another
03438                      * stat to reduce if this fails.  But chances are, if
03439                      * stats have been depleted to -50, all are pretty low
03440                      * and should be roughly the same, so it shouldn't make a
03441                      * difference.
03442                      */
03443                     if (this_stat >= -50) {
03444                         change_attr_value(&(dep->stats), i, -1);
03445                         SET_FLAG(dep, FLAG_APPLIED);
03446                         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_STAT_LOSS,
03447                                       lose_msg[i], lose_msg[i]);
03448                         fix_object(op);
03449                         lost_a_stat = 1;
03450                     }
03451                 }
03452             }
03453         }
03454         /* If no stat lost, tell the player. */
03455         if (!lost_a_stat) {
03456             /* determine_god() seems to not work sometimes... why is this? Should I be using something else? GD */
03457             const char *god = determine_god(op);
03458 
03459             if (god && (strcmp(god, "none")))
03460                 draw_ext_info_format(NDI_UNIQUE, 0, op,
03461                                      MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD,
03462                                      "For a brief moment you feel the holy presence of %s protecting you",
03463                                      "For a brief moment you feel the holy presence of %s protecting you",
03464                                      god);
03465             else
03466                 draw_ext_info(NDI_UNIQUE, 0, op,
03467                               MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_GOD,
03468                               "For a brief moment you feel a holy presence protecting you.",
03469                               NULL);
03470         }
03471 
03472         /* Put a gravestone up where the character 'almost' died.  List the
03473          * exp loss on the stone.
03474          */
03475         tmp = arch_to_object(find_archetype("gravestone"));
03476         snprintf(buf, sizeof(buf), "%s's gravestone", op->name);
03477         FREE_AND_COPY(tmp->name, buf);
03478         snprintf(buf, sizeof(buf), "%s's gravestones", op->name);
03479         FREE_AND_COPY(tmp->name_pl, buf);
03480         snprintf(buf, sizeof(buf), "RIP\nHere rests the hero %s the %s,\n"
03481                  "who was killed\n"
03482                  "by %s.\n",
03483                  op->name, op->contr->title,
03484                  op->contr->killer);
03485         tmp->msg = add_string(buf);
03486         tmp->x = op->x,
03487         tmp->y = op->y;
03488         insert_ob_in_map(tmp, op->map, NULL, 0);
03489 
03490         /* restore player: remove any poisoning, disease and confusion the
03491          * character may be suffering.*/
03492         at = find_archetype("poisoning");
03493         tmp = present_arch_in_ob(at, op);
03494         if (tmp) {
03495             remove_ob(tmp);
03496             free_object(tmp);
03497             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03498                           "Your body feels cleansed", NULL);
03499         }
03500 
03501         at = find_archetype("confusion");
03502         tmp = present_arch_in_ob(at, op);
03503         if (tmp) {
03504             remove_ob(tmp);
03505             free_object(tmp);
03506             draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_BAD_EFFECT_END,
03507                           "Your mind feels clearer", NULL);
03508         }
03509         cure_disease(op, NULL);  /* remove any disease */
03510 
03511         /* Subtract the experience points, if we died cause of food, give
03512          * us food, and reset HP's...
03513          */
03514         apply_death_exp_penalty(op);
03515         if (op->stats.food < 100)
03516             op->stats.food = 900;
03517         op->stats.hp = op->stats.maxhp;
03518         op->stats.sp = MAX(op->stats.sp, op->stats.maxsp);
03519         op->stats.grace = MAX(op->stats.grace, op->stats.maxgrace);
03520 
03521         /* Check to see if the player is in a shop. IF so, then check to see if
03522          * the player has any unpaid items.  If so, remove them and put them back
03523          * in the map.
03524          *
03525          * If they are not in a shop, just free the unpaid items instead of
03526          * putting them back on map.
03527          */
03528         if (is_in_shop(op))
03529             remove_unpaid_objects(op->inv, op, 0);
03530         else
03531             remove_unpaid_objects(op->inv, op, 1);
03532 
03533         /* Move player to his current respawn-position (usually last savebed) */
03534         enter_player_savebed(op);
03535 
03536         /* Save the player before inserting the force to reduce chance of abuse. */
03537         op->contr->braced = 0;
03538         save_player(op, 1);
03539 
03540         /* it is possible that the player has blown something up
03541          * at his savebed location, and that can have long lasting
03542          * spell effects.  So first see if there is a spell effect
03543          * on the space that might harm the player.
03544          */
03545         will_kill_again = 0;
03546         for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp; tmp = tmp->above) {
03547             if (tmp->type == SPELL_EFFECT)
03548                 will_kill_again |= tmp->attacktype;
03549         }
03550         if (will_kill_again) {
03551             object *force;
03552             int at;
03553 
03554             force = create_archetype(FORCE_NAME);
03555             /* 50 ticks should be enough time for the spell to abate */
03556             force->speed = 0.1;
03557             force->speed_left = -5.0;
03558             SET_FLAG(force, FLAG_APPLIED);
03559             for (at = 0; at < NROFATTACKS; at++) {
03560                 if (will_kill_again&(1<<at))
03561                     force->resist[at] = 100;
03562             }
03563             insert_ob_in_ob(force, op);
03564             fix_object(op);
03565         }
03566 
03567         /* Tell the player they have died */
03568         draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03569                       "YOU HAVE DIED.", NULL);
03570         return;
03571     } /* NOT_PERMADETH */
03572     else {
03573         /* If NOT_PERMADETH is set, then the rest of this is not reachable.  This
03574          * should probably be embedded in an else statement.
03575          */
03576 
03577         op->contr->party = NULL;
03578         if (settings.set_title == TRUE)
03579             op->contr->own_title[0] = '\0';
03580 
03581         /* buf should be the kill message */
03582         draw_ext_info(NDI_UNIQUE|NDI_ALL, 0, NULL, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_DIED,
03583                       buf, buf);
03584         check_score(op, 0);
03585         if (op->contr->ranges[range_golem] != NULL) {
03586             remove_friendly_object(op->contr->ranges[range_golem]);
03587             remove_ob(op->contr->ranges[range_golem]);
03588             free_object(op->contr->ranges[range_golem]);
03589             op->contr->ranges[range_golem] = NULL;
03590             op->contr->golem_count = 0;
03591         }
03592         loot_object(op); /* Remove some of the items for good */
03593         remove_ob(op);
03594         op->direction = 0;
03595 
03596         if (!QUERY_FLAG(op, FLAG_WAS_WIZ) && op->stats.exp) {
03597             if (settings.resurrection == TRUE) {
03598                 /* save playerfile sans equipment when player dies
03599                  * -then save it as player.pl.dead so that future resurrection
03600                  * -type spells will work on them nicely
03601                  */
03602                 op->stats.hp = op->stats.maxhp;
03603                 op->stats.food = 999;
03604 
03605                 /* set the location of where the person will reappear when  */
03606                 /* maybe resurrection code should fix map also */
03607                 strcpy(op->contr->maplevel, settings.emergency_mapname);
03608                 if (op->map != NULL)
03609                     op->map = NULL;
03610                 op->x = settings.emergency_x;
03611                 op->y = settings.emergency_y;
03612                 save_player(op, 0);
03613                 op->map = map;
03614                 /* please see resurrection.c: peterm */
03615                 dead_player(op);
03616             } else {
03617                 delete_character(op->name);
03618             }
03619         }
03620         play_again(op);
03621 
03622         /*  peterm:  added to create a corpse at deathsite.  */
03623         tmp = arch_to_object(find_archetype("corpse_pl"));
03624         snprintf(buf, sizeof(buf), "%s", op->name);
03625         FREE_AND_COPY(tmp->name, buf);
03626         FREE_AND_COPY(tmp->name_pl, buf);
03627         tmp->level = op->level;
03628         tmp->x = x;
03629         tmp->y = y;
03630         if (tmp->msg)
03631             free_string(tmp->msg);
03632         tmp->msg = add_string(gravestone_text(op, buf, sizeof(buf)));
03633         SET_FLAG(tmp, FLAG_UNIQUE);
03634         insert_ob_in_map(tmp, map, NULL, 0);
03635     }
03636 }
03637 
03645 void fix_weight(void) {
03646     player *pl;
03647 
03648     for (pl = first_player; pl != NULL; pl = pl->next) {
03649         int old = pl->ob->carrying, sum = sum_weight(pl->ob);
03650 
03651         if (old == sum)
03652             continue;
03653         fix_object(pl->ob);
03654         LOG(llevDebug, "Fixed inventory in %s (%d -> %d)\n", pl->ob->name, old, sum);
03655     }
03656 }
03657 
03661 void fix_luck(void) {
03662     player *pl;
03663 
03664     for (pl = first_player; pl != NULL; pl = pl->next)
03665         if (!pl->ob->contr->state)
03666             change_luck(pl->ob, 0);
03667 }
03668 
03669 
03682 void cast_dust(object *op, object *throw_ob, int dir) {
03683     object *skop, *spob;
03684 
03685     skop = find_skill_by_name(op, throw_ob->skill);
03686 
03687     /* casting POTION 'dusts' is really a use_magic_item skill */
03688     if (op->type == PLAYER && throw_ob->type == POTION && !skop) {
03689         LOG(llevError, "Player %s lacks critical skill use_magic_item!\n", op->name);
03690         return;
03691     }
03692     spob = throw_ob->inv;
03693     if (op->type == PLAYER && spob)
03694         draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS,
03695                              "You cast %s.",
03696                              "You cast %s.",
03697                              spob->name);
03698 
03699     cast_spell(op, throw_ob, dir, spob, NULL);
03700 
03701     if (!QUERY_FLAG(throw_ob, FLAG_REMOVED))
03702         remove_ob(throw_ob);
03703     free_object(throw_ob);
03704 }
03705 
03712 void make_visible(object *op) {
03713     op->hide = 0;
03714     op->invisible = 0;
03715     if (op->type == PLAYER) {
03716         op->contr->tmp_invis = 0;
03717         if (op->contr->invis_race)
03718             FREE_AND_CLEAR_STR(op->contr->invis_race);
03719     }
03720     update_object(op, UP_OBJ_FACE);
03721 }
03722 
03731 int is_true_undead(object *op) {
03732     object *tmp = NULL;
03733 
03734     if (QUERY_FLAG(&op->arch->clone, FLAG_UNDEAD))
03735         return 1;
03736 
03737     if (op->type == PLAYER)
03738         for (tmp = op->inv; tmp; tmp = tmp->below)
03739             if (tmp->type == EXPERIENCE && tmp->stats.Wis)
03740                 if (QUERY_FLAG(tmp, FLAG_UNDEAD))
03741                     return 1;
03742     return 0;
03743 }
03744 
03755 int hideability(object *ob) {
03756     int i, level = 0, mflag;
03757     sint16 x, y;
03758 
03759     if (!ob || !ob->map)
03760         return 0;
03761 
03762     /* so, on normal lighted maps, its hard to hide */
03763     level = ob->map->darkness-2;
03764 
03765     /* this also picks up whether the object is glowing.
03766      * If you carry a light on a non-dark map, its not
03767      * as bad as carrying a light on a pitch dark map
03768      */
03769     if (has_carried_lights(ob))
03770         level = -(10+(2*ob->map->darkness));
03771 
03772     /* scan through all nearby squares for terrain to hide in */
03773     for (i = 0, x = ob->x, y = ob->y; i < 9; i++, x = ob->x+freearr_x[i], y = ob->y+freearr_y[i]) {
03774         mflag = get_map_flags(ob->map, NULL, x, y, NULL, NULL);
03775         if (mflag&P_OUT_OF_MAP) {
03776             continue;
03777         }
03778         if (mflag&P_BLOCKSVIEW) /* something to hide near! */
03779             level += 2;
03780         else /* open terrain! */
03781             level -= 1;
03782     }
03783 
03784     return level;
03785 }
03786 
03796 void do_hidden_move(object *op) {
03797     int hide = 0, num = random_roll(0, 19, op, PREFER_LOW);
03798     object *skop;
03799 
03800     if (!op || !op->map)
03801         return;
03802 
03803     skop = find_obj_by_type_subtype(op, SKILL, SK_HIDING);
03804 
03805     /* its *extremely *hard to run and sneak/hide at the same time! */
03806     if (op->type == PLAYER && op->contr->run_on) {
03807         if (!skop || num >= skop->level) {
03808             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
03809                           "You ran too much! You are no longer hidden!", NULL);
03810             make_visible(op);
03811             return;
03812         } else
03813             num += 20;
03814     }
03815     num += op->map->difficulty;
03816     hide = hideability(op); /* modify by terrain hidden level */
03817     num -= hide;
03818     if ((op->type == PLAYER && hide < -10)
03819     || ((op->invisible -= num) <= 0)) {
03820         make_visible(op);
03821         if (op->type == PLAYER)
03822             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
03823                           "You moved out of hiding! You are visible!", NULL);
03824     } else if (op->type == PLAYER && skop) {
03825         change_exp(op, calc_skill_exp(op, NULL, skop), skop->skill, 0);
03826     }
03827 }
03828 
03837 int stand_near_hostile(object *who) {
03838     object *tmp = NULL;
03839     int i, friendly = 0, player = 0, mflags;
03840     mapstruct *m;
03841     sint16  x, y;
03842 
03843     if (!who)
03844         return 0;
03845 
03846     if (who->type == PLAYER)
03847         player = 1;
03848     else
03849         friendly = QUERY_FLAG(who, FLAG_FRIENDLY);
03850 
03851     /* search adjacent squares */
03852     for (i = 1; i < 9; i++) {
03853         x = who->x+freearr_x[i];
03854         y = who->y+freearr_y[i];
03855         m = who->map;
03856         mflags = get_map_flags(m, &m, x, y, &x, &y);
03857         /* space must be blocked if there is a monster.  If not
03858          * blocked, don't need to check this space.
03859          */
03860         if (mflags&P_OUT_OF_MAP)
03861             continue;
03862         if (OB_TYPE_MOVE_BLOCK(who, GET_MAP_MOVE_BLOCK(m, x, y)))
03863             continue;
03864 
03865         for (tmp = GET_MAP_OB(m, x, y); tmp; tmp = tmp->above) {
03866             if ((player || friendly)
03867             && QUERY_FLAG(tmp, FLAG_MONSTER)
03868             && !QUERY_FLAG(tmp, FLAG_UNAGGRESSIVE))
03869                 return 1;
03870             else if (tmp->type == PLAYER) {
03871                 /*don't let a hidden DM prevent you from hiding*/
03872                 if (!QUERY_FLAG(tmp, FLAG_WIZ) || tmp->contr->hidden == 0)
03873                     return 1;
03874             }
03875         }
03876     }
03877     return 0;
03878 }
03879 
03906 int player_can_view(object *pl, object *op) {
03907     rv_vector rv;
03908     int dx, dy;
03909 
03910     if (pl->type != PLAYER) {
03911         LOG(llevError, "player_can_view() called for non-player object\n");
03912         return -1;
03913     }
03914     if (!pl || !op)
03915         return 0;
03916 
03917     if (op->head) {
03918         op = op->head;
03919     }
03920     get_rangevector(pl, op, &rv, 0x1);
03921 
03922     /* starting with the 'head' part, lets loop
03923      * through the object and find if it has any
03924      * part that is in the los array but isnt on
03925      * a blocked los square.
03926      * we use the archetype to figure out offsets.
03927      */
03928     while (op) {
03929         dx = rv.distance_x+op->arch->clone.x;
03930         dy = rv.distance_y+op->arch->clone.y;
03931 
03932         /* only the viewable area the player sees is updated by LOS
03933          * code, so we need to restrict ourselves to that range of values
03934          * for any meaningful values.
03935          */
03936         if (FABS(dx) <= (pl->contr->socket.mapx/2)
03937         && FABS(dy) <= (pl->contr->socket.mapy/2)
03938         && !pl->contr->blocked_los[dx+(pl->contr->socket.mapx/2)][dy+(pl->contr->socket.mapy/2)])
03939             return 1;
03940         op = op->more;
03941     }
03942     return 0;
03943 }
03944 
03958 static int action_makes_visible(object *op) {
03959     if (op->invisible && QUERY_FLAG(op, FLAG_ALIVE)) {
03960         if (QUERY_FLAG(op, FLAG_MAKE_INVIS))
03961             return 0;
03962 
03963         if (op->contr && op->contr->tmp_invis == 0)
03964             return 0;
03965 
03966         /* If monsters, they should become visible */
03967         if (op->hide || !op->contr || (op->contr && op->contr->tmp_invis)) {
03968             draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_MISC, MSG_SUBTYPE_NONE,
03969                                  "You become %s!",
03970                                  "You become %s!",
03971                                  op->hide ? "unhidden" : "visible");
03972             return 1;
03973         }
03974     }
03975     return 0;
03976 }
03977 
04002 int op_on_battleground(object *op, int *x, int *y, archetype **trophy) {
04003     object *tmp;
04004 
04005     /* A battleground-tile needs the following attributes to be valid:
04006      * is_floor 1 (has to be the FIRST floor beneath the player's feet),
04007      * name="battleground", no_pick 1, type=58 (type BATTLEGROUND)
04008      * and the exit-coordinates sp/hp must both be > 0.
04009      * => The intention here is to prevent abuse of the battleground-
04010      * feature (like pickable or hidden battleground tiles). */
04011     for (tmp = op->below; tmp != NULL; tmp = tmp->below) {
04012         if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) {
04013             if (QUERY_FLAG(tmp, FLAG_NO_PICK)
04014             && strcmp(tmp->name, "battleground") == 0
04015             && tmp->type == BATTLEGROUND
04016             && EXIT_X(tmp)
04017             && EXIT_Y(tmp)) {
04018                 /*before we assign the exit, check if this is a teambattle*/
04019                 if (EXIT_ALT_X(tmp) && EXIT_ALT_Y(tmp) && EXIT_PATH(tmp)) {
04020                     object *invtmp;
04021 
04022                     for (invtmp = op->inv; invtmp != NULL; invtmp = invtmp->below) {
04023                         if (invtmp->type == FORCE
04024                         && invtmp->slaying
04025                         && !strcmp(EXIT_PATH(tmp), invtmp->slaying)) {
04026                             if (x != NULL && y != NULL)
04027                                 *x = EXIT_ALT_X(tmp),
04028                                 *y = EXIT_ALT_Y(tmp);
04029                             return 1;
04030                         }
04031                     }
04032                 }
04033                 if (x != NULL && y != NULL)
04034                     *x = EXIT_X(tmp),
04035                     *y = EXIT_Y(tmp);
04036                 if (trophy != NULL) {
04037                     if (tmp->other_arch)
04038                       *trophy = tmp->other_arch;
04039                     else
04040                       *trophy = find_archetype("finger");
04041                 }
04042                 return 1;
04043             }
04044         }
04045     }
04046     /* If we got here, did not find a battleground */
04047     return 0;
04048 }
04049 
04060 void dragon_ability_gain(object *who, int atnr, int level) {
04061     treasurelist *trlist = NULL;    /* treasurelist */
04062     treasure *tr;      /* treasure */
04063     object *tmp, *skop;      /* tmp. object */
04064     object *item;      /* treasure object */
04065     char buf[MAX_BUF];      /* tmp. string buffer */
04066     int i = 0, j = 0;
04067 
04068     /* get the appropriate treasurelist */
04069     if (atnr == ATNR_FIRE)
04070         trlist = find_treasurelist("dragon_ability_fire");
04071     else if (atnr == ATNR_COLD)
04072         trlist = find_treasurelist("dragon_ability_cold");
04073     else if (atnr == ATNR_ELECTRICITY)
04074         trlist = find_treasurelist("dragon_ability_elec");
04075     else if (atnr == ATNR_POISON)
04076         trlist = find_treasurelist("dragon_ability_poison");
04077 
04078     if (trlist == NULL || who->type != PLAYER)
04079         return;
04080 
04081     for (i = 0, tr = trlist->items; tr != NULL && i < level-1; tr = tr->next, i++)
04082         ;
04083     if (tr == NULL || tr->item == NULL) {
04084         /* LOG(llevDebug, "-> no more treasure for %s\n", change_resist_msg[atnr]); */
04085         return;
04086     }
04087 
04088     /* everything seems okay - now bring on the gift: */
04089     item = &(tr->item->clone);
04090 
04091     if (item->type == SPELL) {
04092         if (check_spell_known(who, item->name))
04093             return;
04094 
04095         draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, who,
04096                              MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04097                              "You gained the ability of %s",
04098                              "You gained the ability of %s",
04099                              item->name);
04100         do_learn_spell(who, item, 0);
04101         return;
04102     }
04103 
04104     /* grant direct spell */
04105     if (item->type == SPELLBOOK) {
04106         if (!item->inv) {
04107             LOG(llevDebug, "dragon_ability_gain: Broken spellbook %s\n", item->name);
04108             return;
04109         }
04110         if (check_spell_known(who, item->inv->name))
04111             return;
04112         if (item->invisible) {
04113             draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, who,
04114                                  MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04115                                  "You gained the ability of %s",
04116                                  "You gained the ability of %s",
04117                                  item->inv->name);
04118             do_learn_spell(who, item->inv, 0);
04119             return;
04120         }
04121     } else if (item->type == SKILL_TOOL && item->invisible) {
04122         if (item->subtype == SK_CLAWING && (skop = find_skill_by_name(who, item->skill)) != NULL) {
04123             /* should this perhaps be (skop->attackyp&item->attacktype) != item->attacktype ...
04124              * in this way, if the player is missing any of the attacktypes, he gets
04125              * them.  As it is now, if the player has any that match the granted skill,
04126              * but not all of them, he gets nothing.
04127              */
04128             if (!(skop->attacktype&item->attacktype)) {
04129                 /* Give new attacktype */
04130                 skop->attacktype |= item->attacktype;
04131 
04132                 /* always add physical if there's none */
04133                 skop->attacktype |= AT_PHYSICAL;
04134 
04135                 if (item->msg != NULL)
04136                     draw_ext_info(NDI_UNIQUE|NDI_BLUE, 0, who,
04137                                   MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04138                                   item->msg, item->msg);
04139 
04140                 /* Give player new face */
04141                 if (item->animation_id) {
04142                     who->face = skop->face;
04143                     who->animation_id = item->animation_id;
04144                     who->anim_speed = item->anim_speed;
04145                     who->last_anim = 0;
04146                     who->state = 0;
04147                     animate_object(who, who->direction);
04148                 }
04149             }
04150         }
04151     } else if (item->type == FORCE) {
04152         /* forces in the treasurelist can alter the player's stats */
04153         object *skin;
04154 
04155         /* first get the dragon skin force */
04156         for (skin = who->inv; skin != NULL && strcmp(skin->arch->name, "dragon_skin_force") != 0; skin = skin->below)
04157             ;
04158         if (skin == NULL)
04159             return;
04160 
04161         /* adding new spellpath attunements */
04162         if (item->path_attuned > 0 && !(skin->path_attuned&item->path_attuned)) {
04163             skin->path_attuned |= item->path_attuned; /* add attunement to skin */
04164 
04165             /* print message */
04166             snprintf(buf, sizeof(buf), "You feel attuned to ");
04167             for (i = 0, j = 0; i < NRSPELLPATHS; i++) {
04168                 if (item->path_attuned&(1<<i)) {
04169                     if (j)
04170                         strcat(buf, " and ");
04171                     else
04172                         j = 1;
04173                     strcat(buf, spellpathnames[i]);
04174                 }
04175             }
04176             strcat(buf, ".");
04177             draw_ext_info(NDI_UNIQUE|NDI_BLUE, 0, who,
04178                           MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04179                           buf, buf);
04180         }
04181 
04182         /* evtl. adding flags: */
04183         if (QUERY_FLAG(item, FLAG_XRAYS))
04184             SET_FLAG(skin, FLAG_XRAYS);
04185         if (QUERY_FLAG(item, FLAG_STEALTH))
04186             SET_FLAG(skin, FLAG_STEALTH);
04187         if (QUERY_FLAG(item, FLAG_SEE_IN_DARK))
04188             SET_FLAG(skin, FLAG_SEE_IN_DARK);
04189 
04190         /* print message if there is one */
04191         if (item->msg != NULL)
04192             draw_ext_info(NDI_UNIQUE|NDI_BLUE, 0, who,
04193                           MSG_TYPE_ATTRIBUTE, MSG_TYPE_ATTRIBUTE_RACE,
04194                           item->msg, item->msg);
04195     } else {
04196         /* generate misc. treasure */
04197         char name[HUGE_BUF];
04198 
04199         tmp = arch_to_object(tr->item);
04200         query_short_name(tmp, name, HUGE_BUF);
04201         draw_ext_info_format(NDI_UNIQUE|NDI_BLUE, 0, who,
04202                              MSG_TYPE_ITEM, MSG_TYPE_ITEM_ADD,
04203                              "You gained %s",
04204                              "You gained %s",
04205                              name);
04206         tmp = insert_ob_in_ob(tmp, who);
04207         if (who->type == PLAYER)
04208             esrv_send_item(who, tmp);
04209     }
04210 }
04211 
04221 void player_unready_range_ob(player *pl, object *ob) {
04222     rangetype i;
04223 
04224     for (i = 0; i < range_size; i++) {
04225         if (pl->ranges[i] == ob) {
04226             pl->ranges[i] = NULL;
04227             if (pl->shoottype == i) {
04228                 pl->shoottype = range_none;
04229             }
04230         }
04231     }
04232 }