Crossfire Server, Branch 1.12
R12190
|
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 }