Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_apply_c = 00003 * "$Id: apply.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 /* 00006 CrossFire, A Multiplayer game for X-windows 00007 00008 Copyright (C) 2006 Mark Wedel & Crossfire Development Team 00009 Copyright (C) 1992 Frank Tore Johansen 00010 00011 This program is free software; you can redistribute it and/or modify 00012 it under the terms of the GNU General Public License as published by 00013 the Free Software Foundation; either version 2 of the License, or 00014 (at your option) any later version. 00015 00016 This program is distributed in the hope that it will be useful, 00017 but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 GNU General Public License for more details. 00020 00021 You should have received a copy of the GNU General Public License 00022 along with this program; if not, write to the Free Software 00023 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00024 00025 The authors can be reached via e-mail to crossfire-devel@real-time.com 00026 */ 00027 00033 #include <global.h> 00034 #include <living.h> 00035 #include <spells.h> 00036 #include <skills.h> 00037 #include <tod.h> 00038 00039 #ifndef __CEXTRACT__ 00040 #include <sproto.h> 00041 #endif 00042 00043 /* Want this regardless of rplay. */ 00044 #include <sounds.h> 00045 00046 /* need math lib for double-precision and pow() in dragon_eat_flesh() */ 00047 #include <math.h> 00048 00064 int transport_can_hold(const object *transport, const object *op, int nrof) { 00065 if ((op->weight*nrof+transport->carrying) > transport->weight_limit) 00066 return 0; 00067 else 00068 return 1; 00069 } 00070 00081 int should_director_abort(object *op, object *victim) { 00082 int arch_flag, name_flag, race_flag; 00083 /* Get flags to determine what of arch, name, and race should be 00084 * checked. This is stored in subtype, and is a bitmask, the LSB 00085 * is the arch flag, the next is the name flag, and the last is 00086 * the race flag. Also note, if subtype is set to zero, that also 00087 * goes to defaults of all affecting it. Examples: 00088 * subtype 1: only arch 00089 * subtype 3: arch or name 00090 * subtype 5: arch or race 00091 * subtype 7: all three 00092 */ 00093 if (op->subtype) { 00094 arch_flag = (op->subtype&1); 00095 name_flag = (op->subtype&2); 00096 race_flag = (op->subtype&4); 00097 } else { 00098 arch_flag = 1; 00099 name_flag = 1; 00100 race_flag = 1; 00101 } 00102 /* If the director has race set, only affect objects with a arch, 00103 * name or race that matches. 00104 */ 00105 if ((op->race) 00106 && ((!(victim->arch && arch_flag && victim->arch->name) || strcmp(op->race, victim->arch->name))) 00107 && ((!(victim->name && name_flag) || strcmp(op->race, victim->name))) 00108 && ((!(victim->race && race_flag) || strcmp(op->race, victim->race)))) { 00109 return 1; 00110 } 00111 00112 /* If the director has slaying set, only affect objects where none 00113 * of arch, name, or race match. 00114 */ 00115 if ((op->slaying) 00116 && (((victim->arch && arch_flag && victim->arch->name && !strcmp(op->slaying, victim->arch->name))) 00117 || ((victim->name && name_flag && !strcmp(op->slaying, victim->name))) 00118 || ((victim->race && race_flag && !strcmp(op->slaying, victim->race))))) { 00119 return 1; 00120 } 00121 return 0; 00122 } 00123 00131 void handle_apply_yield(object *tmp) { 00132 const char *yield; 00133 00134 yield = get_ob_key_value(tmp, "on_use_yield"); 00135 if (yield != NULL) { 00136 object *drop = create_archetype(yield); 00137 if (tmp->env) { 00138 drop = insert_ob_in_ob(drop, tmp->env); 00139 } else { 00140 drop->x = tmp->x; 00141 drop->y = tmp->y; 00142 insert_ob_in_map(drop, tmp->map, tmp, INS_BELOW_ORIGINATOR); 00143 } 00144 } 00145 } 00146 00147 int check_weapon_power(const object *who, int improvs); 00148 00160 int set_object_face_main(object *op) { 00161 int newface = op->arch->clone.face->number; 00162 sstring saved = get_ob_key_value(op, "face_closed"); 00163 00164 if (saved) { 00165 newface = find_face(saved, newface); 00166 } 00167 if (newface && op->face != &new_faces[newface]) { 00168 op->face = &new_faces[newface]; 00169 return TRUE; 00170 } 00171 return FALSE; 00172 } 00173 00185 static int set_object_face_other(object *op) { 00186 sstring custom; 00187 int newface = 0; 00188 00189 if (op->face && op->other_arch && op->other_arch->clone.face) 00190 newface = op->other_arch->clone.face->number; 00191 00192 if (op->face != op->arch->clone.face) { 00193 /* object has a custom face, save it so it gets correctly restored later. */ 00194 set_ob_key_value(op, "face_closed", op->face->name, 1); 00195 } 00196 00197 custom = get_ob_key_value(op, "face_opened"); 00198 if (custom) { 00199 newface = find_face(custom, newface); 00200 } 00201 00202 if (newface && op->face->number != newface) { 00203 op->face = &new_faces[newface]; 00204 return TRUE; 00205 } 00206 return FALSE; 00207 } 00208 00230 int apply_container(object *op, object *sack) { 00231 char name_sack[MAX_BUF], name_tmp[MAX_BUF]; 00232 object *tmp = op->container; 00233 00234 if (op->type != PLAYER) 00235 return 0; /* This might change */ 00236 00237 if (sack == NULL || sack->type != CONTAINER) { 00238 LOG(llevError, "apply_container: %s is not container!\n", sack ? sack->name : "NULL"); 00239 return 0; 00240 } 00241 00242 /* If we have a currently open container, then it needs 00243 * to be closed in all cases if we are opening this one up. 00244 * We then fall through if appropriate for openening the new 00245 * container. 00246 */ 00247 if (op->container && QUERY_FLAG(sack, FLAG_APPLIED)) { 00248 if (op->container->env != op) { /* if container is on the ground */ 00249 op->container->move_off = 0; 00250 } 00251 /* Lauwenmark: Handle for plugin close event */ 00252 if (execute_event(tmp, EVENT_CLOSE, op, NULL, NULL, SCRIPT_FIX_ALL) != 0) 00253 return 1; 00254 00255 query_name(op->container, name_tmp, MAX_BUF); 00256 draw_ext_info_format(NDI_UNIQUE, 0, op, 00257 MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00258 "You close %s.", 00259 "You close %s.", 00260 name_tmp); 00261 CLEAR_FLAG(op->container, FLAG_APPLIED); 00262 op->container = NULL; 00263 if (set_object_face_main(tmp)) { 00264 esrv_update_item(UPD_FLAGS|UPD_FACE, op, tmp); 00265 } else { 00266 esrv_update_item(UPD_FLAGS, op, tmp); 00267 } 00268 if (tmp == sack) 00269 return 1; 00270 } 00271 00272 query_name(sack, name_sack, MAX_BUF); 00273 00274 /* If the player is trying to open it (which he must be doing 00275 * if we got here), and it is locked, check to see if player 00276 * has the equipment to open it. 00277 */ 00278 00279 if (sack->slaying) { /* it's locked */ 00280 tmp = find_key(op, op, sack); 00281 if (tmp) { 00282 query_name(tmp, name_tmp, MAX_BUF); 00283 draw_ext_info_format(NDI_UNIQUE, 0, op, 00284 MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 00285 "You unlock %s with %s.", 00286 "You unlock %s with %s.", 00287 name_sack, name_tmp); 00288 } else { 00289 draw_ext_info_format(NDI_UNIQUE, 0, op, 00290 MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00291 "You don't have the key to unlock %s.", 00292 "You don't have the key to unlock %s.", 00293 name_sack); 00294 return 0; 00295 } 00296 } 00297 00298 /* By the time we get here, we have made sure any other container 00299 * has been closed and if this is a locked container, the player 00300 * has the key to open it. 00301 */ 00302 00303 /* There are really two cases - the sack is either on the ground, 00304 * or the sack is part of the player's inventory. If on the ground, 00305 * we assume that the player is opening it, since if it was being 00306 * closed, that would have been taken care of above. 00307 */ 00308 00309 00310 if (sack->env != op) { 00311 /* Hypothetical case - the player is trying to open a sack 00312 * that belongs to someone else. This normally should not 00313 * happen, but a misbehaving client/player could 00314 * try to do it, so let's handle it gracefully. 00315 */ 00316 if (sack->env) { 00317 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00318 "You can't open %s", 00319 "You can't open %s", 00320 name_sack); 00321 return 0; 00322 } 00323 00324 if (sack->nrof > 1) { 00325 object *left = get_split_ob(sack, sack->nrof-1, NULL, 0); 00326 00327 insert_ob_in_map_at(left, sack->map, NULL, INS_NO_MERGE, sack->x, sack->y); 00328 /* recompute the name so it's nice */ 00329 query_name(sack, name_sack, MAX_BUF); 00330 } 00331 00332 /* set it so when the player walks off, we can unapply the sack */ 00333 sack->move_off = MOVE_ALL; /* trying force closing it */ 00334 00335 CLEAR_FLAG(sack, FLAG_APPLIED); 00336 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 00337 "You open %s.", 00338 "You open %s.", 00339 name_sack); 00340 SET_FLAG(sack, FLAG_APPLIED); 00341 op->container = sack; 00342 if (set_object_face_other(sack)) { 00343 esrv_update_item(UPD_FLAGS|UPD_FACE, op, sack); 00344 } else { 00345 esrv_update_item(UPD_FLAGS, op, sack); 00346 } 00347 esrv_send_inventory(op, sack); 00348 } else { /* sack is in players inventory */ 00349 if (QUERY_FLAG(sack, FLAG_APPLIED)) { /* readied sack becoming open */ 00350 CLEAR_FLAG(sack, FLAG_APPLIED); 00351 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 00352 "You open %s.", 00353 "You open %s.", 00354 name_sack); 00355 SET_FLAG(sack, FLAG_APPLIED); 00356 op->container = sack; 00357 if (set_object_face_other(sack)) { 00358 esrv_update_item(UPD_FLAGS|UPD_FACE, op, sack); 00359 } else { 00360 esrv_update_item(UPD_FLAGS, op, sack); 00361 } 00362 esrv_send_inventory(op, sack); 00363 } else { 00364 object *left = NULL; 00365 00366 if (sack->nrof > 1) { 00367 left = get_split_ob(sack, sack->nrof-1, NULL, 1); 00368 } 00369 00370 CLEAR_FLAG(sack, FLAG_APPLIED); 00371 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 00372 "You readied %s.", 00373 "You readied %s.", 00374 name_sack); 00375 SET_FLAG(sack, FLAG_APPLIED); 00376 esrv_update_item(UPD_FLAGS, op, sack); 00377 00378 if (left) { 00379 insert_ob_in_ob(left, sack->env); 00380 esrv_send_item(op, left); 00381 } 00382 } 00383 } 00384 return 1; 00385 } 00386 00398 void do_learn_spell(object *op, object *spell, int special_prayer) { 00399 object *tmp; 00400 00401 if (op->type != PLAYER) { 00402 LOG(llevError, "BUG: do_learn_spell(): not a player\n"); 00403 return; 00404 } 00405 00406 /* Upgrade special prayers to normal prayers */ 00407 if ((tmp = check_spell_known(op, spell->name)) != NULL) { 00408 if (special_prayer && !QUERY_FLAG(tmp, FLAG_STARTEQUIP)) { 00409 LOG(llevError, "BUG: do_learn_spell(): spell already known, but not marked as startequip\n"); 00410 return; 00411 } 00412 return; 00413 } 00414 00415 play_sound_player_only(op->contr, SOUND_TYPE_SPELL, spell, 0, "learn"); 00416 tmp = get_object(); 00417 copy_object(spell, tmp); 00418 insert_ob_in_ob(tmp, op); 00419 00420 if (special_prayer) { 00421 SET_FLAG(tmp, FLAG_STARTEQUIP); 00422 } 00423 00424 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 00425 "Type 'bind cast %s to store the spell in a key.", 00426 "Type 'bind cast %s to store the spell in a key.", 00427 spell->name); 00428 00429 esrv_add_spells(op->contr, tmp); 00430 } 00431 00440 void do_forget_spell(object *op, const char *spell) { 00441 object *spob; 00442 00443 if (op->type != PLAYER) { 00444 LOG(llevError, "BUG: do_forget_spell(): not a player\n"); 00445 return; 00446 } 00447 if ((spob = check_spell_known(op, spell)) == NULL) { 00448 LOG(llevError, "BUG: do_forget_spell(): spell not known\n"); 00449 return; 00450 } 00451 00452 draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED, 00453 "You lose knowledge of %s.", 00454 "You lose knowledge of %s.", 00455 spell); 00456 player_unready_range_ob(op->contr, spob); 00457 esrv_remove_spell(op->contr, spob); 00458 remove_ob(spob); 00459 free_object(spob); 00460 } 00461 00472 static int check_race_restrictions(object *who, object *item) { 00473 char buf[MAX_BUF]; 00474 sstring restriction; 00475 00476 if (who->type != PLAYER || QUERY_FLAG(who, FLAG_WIZ)) 00477 return 1; 00478 00479 restriction = get_ob_key_value(item, "race_restriction"); 00480 if (!restriction) 00481 return 1; 00482 00483 snprintf(buf, sizeof(buf), ":%s:", who->race); 00484 buf[sizeof(buf)-1] = '\0'; 00485 00486 if (strstr(restriction, buf) != NULL) 00487 return 1; 00488 00489 query_name(item, buf, sizeof(buf)); 00490 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_PROHIBITION, "Somehow you can't seem to use the %s.", NULL, buf); 00491 00492 return 0; 00493 } 00494 00512 int manual_apply(object *op, object *tmp, int aflag) { 00513 if (tmp->head) 00514 tmp = tmp->head; 00515 00516 if (QUERY_FLAG(tmp, FLAG_UNPAID) && !QUERY_FLAG(tmp, FLAG_APPLIED)) { 00517 if (op->type == PLAYER) { 00518 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00519 "You should pay for it first.", NULL); 00520 return METHOD_SILENT_ERROR; 00521 } 00522 return 0; /* monsters just skip unpaid items */ 00523 } 00524 00525 if (!check_race_restrictions(op, tmp)) 00526 return METHOD_SILENT_ERROR; 00527 00528 /* Lauwenmark: Handle for plugin apply event */ 00529 if (execute_event(tmp, EVENT_APPLY, op, NULL, NULL, SCRIPT_FIX_ALL) != 0) 00530 return METHOD_OK; 00531 00532 if (op->contr) 00533 play_sound_player_only(op->contr, SOUND_TYPE_ITEM, tmp, 0, "apply"); 00534 00535 return ob_apply(tmp, op, aflag); 00536 } 00537 00557 int player_apply(object *pl, object *op, int aflag, int quiet) { 00558 int tmp; 00559 00560 if (op->env == NULL && (pl->move_type&MOVE_FLYING)) { 00561 /* player is flying and applying object not in inventory */ 00562 if (!QUERY_FLAG(pl, FLAG_WIZ) && !(op->move_type&MOVE_FLYING)) { 00563 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00564 "But you are floating high above the ground!", NULL); 00565 return 0; 00566 } 00567 } 00568 00569 /* Check for PLAYER to avoid a DM to disappear in a puff of smoke if 00570 * applied. 00571 */ 00572 if (op->type != PLAYER 00573 && QUERY_FLAG(op, FLAG_WAS_WIZ) 00574 && !QUERY_FLAG(pl, FLAG_WAS_WIZ)) { 00575 play_sound_map(SOUND_TYPE_ITEM, op, 0, "evaporate"); 00576 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00577 "The object disappears in a puff of smoke!", NULL); 00578 draw_ext_info(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00579 "It must have been an illusion.", NULL); 00580 remove_ob(op); 00581 free_object(op); 00582 return 1; 00583 } 00584 00585 pl->contr->last_used = op; 00586 pl->contr->last_used_id = op->count; 00587 00588 tmp = manual_apply(pl, op, aflag); 00589 if (!quiet) { 00590 if (tmp == METHOD_UNHANDLED) { 00591 char name[MAX_BUF]; 00592 00593 query_name(op, name, MAX_BUF); 00594 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00595 "I don't know how to apply the %s.", 00596 "I don't know how to apply the %s.", 00597 name); 00598 } else if (tmp == METHOD_ERROR) 00599 draw_ext_info_format(NDI_UNIQUE, 0, pl, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 00600 "You must get it first!\n", NULL); 00601 else if (tmp == METHOD_SILENT_ERROR) 00602 return tmp; 00603 } 00604 if (tmp == METHOD_OK) { 00605 if (op->anim_suffix != NULL) 00606 apply_anim_suffix(pl, op->anim_suffix); 00607 } 00608 return tmp; 00609 } 00610 00619 void player_apply_below(object *pl) { 00620 object *tmp, *next; 00621 int floors; 00622 00623 if (pl->contr->transport && pl->contr->transport->type == TRANSPORT) { 00624 ob_apply(pl->contr->transport, pl, 0); 00625 return; 00626 } 00627 00628 /* If using a container, set the starting item to be the top 00629 * item in the container. Otherwise, use the map. 00630 */ 00631 tmp = (pl->container != NULL) ? pl->container->inv : pl->below; 00632 00633 /* This is perhaps more complicated. However, I want to make sure that 00634 * we don't use a corrupt pointer for the next object, so we get the 00635 * next object in the stack before applying. This is can only be a 00636 * problem if player_apply() has a bug in that it uses the object but 00637 * does not return a proper value. 00638 */ 00639 for (floors = 0; tmp != NULL; tmp = next) { 00640 next = tmp->below; 00641 if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) 00642 floors++; 00643 else if (floors > 0) 00644 return; /* process only floor objects after first floor object */ 00645 00646 /* If it is visible, player can apply it. If it is applied by 00647 * person moving on it, also activate. Added code to make it 00648 * so that at least one of players movement types be that which 00649 * the item needs. 00650 */ 00651 if (!tmp->invisible || (tmp->move_on&pl->move_type)) { 00652 if (player_apply(pl, tmp, 0, 1) == METHOD_OK) 00653 return; 00654 } 00655 if (floors >= 2) 00656 return; /* process at most two floor objects */ 00657 } 00658 } 00659 00675 static int unapply_special(object *who, object *op, int aflags) { 00676 char name[MAX_BUF]; 00677 00678 if (op->type != LAMP) 00679 CLEAR_FLAG(op, FLAG_APPLIED); 00680 query_name(op, name, MAX_BUF); 00681 switch (op->type) { 00682 case WEAPON: 00683 if (!(aflags&AP_NOPRINT)) 00684 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00685 "You unwield %s.", 00686 "You unwield %s.", 00687 name); 00688 (void)change_abil(who, op); 00689 if (QUERY_FLAG(who, FLAG_READY_WEAPON)) 00690 CLEAR_FLAG(who, FLAG_READY_WEAPON); 00691 who->current_weapon = NULL; 00692 clear_skill(who); 00693 break; 00694 00695 case SKILL: /* allows objects to impart skills */ 00696 case SKILL_TOOL: 00697 if (op != who->chosen_skill) { 00698 LOG(llevError, "BUG: apply_special(): applied skill is not a chosen skill\n"); 00699 } 00700 if (who->type == PLAYER) { 00701 if (who->contr->shoottype == range_skill) 00702 who->contr->shoottype = range_none; 00703 if (!op->invisible) { 00704 if (!(aflags&AP_NOPRINT)) 00705 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00706 "You stop using the %s.", 00707 "You stop using the %s.", 00708 name); 00709 } else { 00710 if (!(aflags&AP_NOPRINT)) 00711 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00712 "You can no longer use the skill: %s.", 00713 "You can no longer use the skill: %s.", 00714 op->skill); 00715 } 00716 } 00717 (void)change_abil(who, op); 00718 who->chosen_skill = NULL; 00719 CLEAR_FLAG(who, FLAG_READY_SKILL); 00720 break; 00721 00722 case ARMOUR: 00723 case HELMET: 00724 case SHIELD: 00725 case RING: 00726 case BOOTS: 00727 case GLOVES: 00728 case AMULET: 00729 case GIRDLE: 00730 case BRACERS: 00731 case CLOAK: 00732 if (!(aflags&AP_NOPRINT)) 00733 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00734 "You unwear %s.", 00735 "You unwear %s.", 00736 name); 00737 (void)change_abil(who, op); 00738 break; 00739 00740 case BOW: 00741 case WAND: 00742 case ROD: 00743 case HORN: 00744 clear_skill(who); 00745 if (!(aflags&AP_NOPRINT)) 00746 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00747 "You unready %s.", 00748 "You unready %s.", 00749 name); 00750 if (who->type == PLAYER) { 00751 who->contr->shoottype = range_none; 00752 } else { 00753 if (op->type == BOW) 00754 CLEAR_FLAG(who, FLAG_READY_BOW); 00755 else 00756 CLEAR_FLAG(who, FLAG_READY_RANGE); 00757 } 00758 break; 00759 00760 case BUILDER: 00761 if (!(aflags&AP_NOPRINT)) 00762 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00763 "You unready %s.", 00764 "You unready %s.", 00765 name); 00766 who->contr->shoottype = range_none; 00767 who->contr->ranges[range_builder] = NULL; 00768 break; 00769 00770 default: 00771 if (!(aflags&AP_NOPRINT)) 00772 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00773 "You unapply %s.", 00774 "You unapply %s.", 00775 name); 00776 break; 00777 } 00778 00779 fix_object(who); 00780 00781 if (!(aflags&AP_NO_MERGE)) { 00782 object *tmp; 00783 00784 tmp = merge_ob(op, NULL); 00785 if (who->type == PLAYER) { 00786 if (tmp) { /* it was merged */ 00787 op = tmp; 00788 } 00789 esrv_update_item(UPD_FLAGS, who, op); 00790 } 00791 } 00792 return 0; 00793 } 00794 00814 static object *get_item_from_body_location(object *start, int loc) { 00815 object *tmp; 00816 00817 if (!start) 00818 return NULL; 00819 00820 for (tmp = start; tmp; tmp = tmp->below) 00821 if (QUERY_FLAG(tmp, FLAG_APPLIED) 00822 && tmp->body_info[loc] 00823 && (!tmp->invisible || tmp->type == SKILL)) 00824 return tmp; 00825 00826 return NULL; 00827 } 00828 00850 static int unapply_for_ob(object *who, object *op, int aflags) { 00851 int i; 00852 object *tmp = NULL, *last; 00853 char name[MAX_BUF]; 00854 00855 /* If we are applying a shield or weapon, unapply any equipped shield 00856 * or weapons first - only allowed to use one weapon/shield at a time. 00857 */ 00858 if (op->type == WEAPON || op->type == SHIELD) { 00859 for (tmp = who->inv; tmp; tmp = tmp->below) { 00860 if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == op->type) { 00861 if ((aflags&AP_IGNORE_CURSE) 00862 || (aflags&AP_PRINT) 00863 || (!QUERY_FLAG(tmp, FLAG_CURSED) && !QUERY_FLAG(tmp, FLAG_DAMNED))) { 00864 if (aflags&AP_PRINT) { 00865 query_name(tmp, name, MAX_BUF); 00866 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00867 name, NULL); 00868 } else 00869 unapply_special(who, tmp, aflags); 00870 } else { 00871 /* In this case, we want to try and remove a 00872 * cursed item. While we know it won't work, we 00873 * want unapply_special to at least generate the 00874 * message. 00875 */ 00876 if (!(aflags&AP_NOPRINT)) { 00877 query_name(tmp, name, MAX_BUF); 00878 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00879 "No matter how hard you try, you just can't remove %s.", 00880 "No matter how hard you try, you just can't remove %s.", 00881 name); 00882 } 00883 return 1; 00884 } 00885 00886 } 00887 } 00888 } 00889 00890 for (i = 0; i < NUM_BODY_LOCATIONS; i++) { 00891 /* this used up a slot that we need to free */ 00892 if (op->body_info[i]) { 00893 last = who->inv; 00894 00895 /* We do a while loop - may need to remove several items 00896 * in order to free up enough slots. 00897 */ 00898 while ((who->body_used[i]+op->body_info[i]) < 0) { 00899 tmp = get_item_from_body_location(last, i); 00900 if (!tmp) { 00901 return 1; 00902 } 00903 00904 /* If just printing, we don't care about cursed status */ 00905 if ((aflags&AP_IGNORE_CURSE) 00906 || (aflags&AP_PRINT) 00907 || (!(QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)))) { 00908 if (aflags&AP_PRINT) { 00909 query_name(tmp, name, MAX_BUF); 00910 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00911 name, NULL); 00912 } else 00913 unapply_special(who, tmp, aflags); 00914 } else { 00915 /* Cursed item that we can't unequip - tell the player. 00916 * Note this could be annoying if this is just one of a 00917 * few, so it may not be critical (eg, putting on a 00918 * ring and you have one cursed ring.) 00919 */ 00920 if (!(aflags&AP_NOPRINT)) { 00921 query_name(tmp, name, MAX_BUF); 00922 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 00923 "The %s just won't come off", 00924 "The %s just won't come off", 00925 name); 00926 } 00927 } 00928 last = tmp->below; 00929 } 00930 /* if we got here, this slot is freed up - otherwise, if it 00931 * wasn't freed up, the return in the !tmp would have 00932 * kicked in. 00933 */ 00934 } /* if op is using this body location */ 00935 } /* for body lcoations */ 00936 return 0; 00937 } 00938 00955 int can_apply_object(object *who, object *op) { 00956 int i, retval = 0; 00957 object *tmp = NULL, *ws = NULL; 00958 00959 /* Players have 2 'arm's, so they could in theory equip 2 shields or 00960 * 2 weapons, but we don't want to let them do that. So if they are 00961 * trying to equip a weapon or shield, see if they already have one 00962 * in place and store that way. 00963 */ 00964 if (op->type == WEAPON || op->type == SHIELD) { 00965 for (tmp = who->inv; tmp && !ws; tmp = tmp->below) { 00966 if (QUERY_FLAG(tmp, FLAG_APPLIED) && tmp->type == op->type) { 00967 retval = CAN_APPLY_UNAPPLY; 00968 ws = tmp; 00969 } 00970 } 00971 } 00972 00973 for (i = 0; i < NUM_BODY_LOCATIONS; i++) { 00974 if (op->body_info[i]) { 00975 /* Item uses more slots than we have */ 00976 if (FABS(op->body_info[i]) > who->body_info[i]) { 00977 /* Could return now for efficiently - rest of info 00978 * below isn't really needed. 00979 */ 00980 retval |= CAN_APPLY_NEVER; 00981 } else if ((who->body_used[i]+op->body_info[i]) < 0) { 00982 /* in this case, equipping this would use more free 00983 * spots than we have. 00984 */ 00985 object *tmp1; 00986 00987 /* if we have an applied weapon/shield, and unapply 00988 * it would free enough slots to equip the new item, 00989 * then just set this can continue. We don't care 00990 * about the logic below - if you have shield equipped 00991 * and try to equip another shield, there is only one 00992 * choice. However, the check for the number of body 00993 * locations does take into the account cases where what 00994 * is being applied may be two handed for example. 00995 */ 00996 if (ws) { 00997 if ((who->body_used[i]-ws->body_info[i]+op->body_info[i]) >= 0) { 00998 retval |= CAN_APPLY_UNAPPLY; 00999 continue; 01000 } 01001 } 01002 01003 tmp1 = get_item_from_body_location(who->inv, i); 01004 if (!tmp1) { 01005 retval |= CAN_APPLY_NEVER; 01006 } else { 01007 /* need to unapply something. However, if this 01008 * something is different than we had found before, 01009 * it means they need to apply multiple objects 01010 */ 01011 retval |= CAN_APPLY_UNAPPLY; 01012 if (!tmp) 01013 tmp = tmp1; 01014 else if (tmp != tmp1) { 01015 retval |= CAN_APPLY_UNAPPLY_MULT; 01016 } 01017 /* This object isn't using up all the slots, so 01018 * there must be another. If so, and if the new 01019 * item doesn't need all the slots, the player 01020 * then has a choice. 01021 */ 01022 if (((who->body_used[i]-tmp1->body_info[i]) != who->body_info[i]) 01023 && (FABS(op->body_info[i]) < who->body_info[i])) 01024 retval |= CAN_APPLY_UNAPPLY_CHOICE; 01025 01026 /* Does unequipping 'tmp1' free up enough slots 01027 * for this to be equipped? If not, there must 01028 * be something else to unapply. 01029 */ 01030 if ((who->body_used[i]+op->body_info[i]-tmp1->body_info[i]) < 0) 01031 retval |= CAN_APPLY_UNAPPLY_MULT; 01032 } 01033 } /* if not enough free slots */ 01034 } /* if this object uses location i */ 01035 } /* for i -> num_body_locations loop */ 01036 01037 /* Do checks for can_use_weapon/shield/armour. */ 01038 if (IS_WEAPON(op) && !QUERY_FLAG(who, FLAG_USE_WEAPON)) 01039 retval |= CAN_APPLY_RESTRICTION; 01040 if (IS_SHIELD(op) && !QUERY_FLAG(who, FLAG_USE_SHIELD)) 01041 retval |= CAN_APPLY_RESTRICTION; 01042 if (IS_ARMOR(op) && !QUERY_FLAG(who, FLAG_USE_ARMOUR)) 01043 retval |= CAN_APPLY_RESTRICTION; 01044 01045 if (who->type != PLAYER) { 01046 if ((op->type == WAND || op->type == HORN || op->type == ROD) 01047 && !QUERY_FLAG(who, FLAG_USE_RANGE)) 01048 retval |= CAN_APPLY_RESTRICTION; 01049 if (op->type == BOW && !QUERY_FLAG(who, FLAG_USE_BOW)) 01050 retval |= CAN_APPLY_RESTRICTION; 01051 if (op->type == RING && !QUERY_FLAG(who, FLAG_USE_RING)) 01052 retval |= CAN_APPLY_RESTRICTION; 01053 if (op->type == BOW && !QUERY_FLAG(who, FLAG_USE_BOW)) 01054 retval |= CAN_APPLY_RESTRICTION; 01055 } 01056 return retval; 01057 } 01058 01076 int check_weapon_power(const object *who, int improvs) { 01077 /* Old code is below (commented out). Basically, since weapons 01078 * are the only object players really have any control to improve, 01079 * it's a bit harsh to require high level in some combat skill, 01080 * so we just use overall level. 01081 */ 01082 #if 1 01083 if (((who->level/5)+5) >= improvs) 01084 return 1; 01085 else 01086 return 0; 01087 01088 #else 01089 int level = 0; 01090 01091 /* The skill system hands out wc and dam bonuses to fighters 01092 * more generously than the old system (see fix_object). Thus 01093 * we need to curtail the power of player enchanted weapons. 01094 * I changed this to 1 improvement per "fighter" level/5 -b.t. 01095 * Note: Nothing should break by allowing this ratio to be 01096 * different or using normal level - it is just a matter of play 01097 * balance. 01098 */ 01099 if (who->type == PLAYER) { 01100 object *wc_obj = NULL; 01101 01102 for (wc_obj = who->inv; wc_obj; wc_obj = wc_obj->below) 01103 if (wc_obj->type == SKILL && IS_COMBAT_SKILL(wc_obj->subtype) 01104 && wc_obj->level > level) 01105 level = wc_obj->level; 01106 01107 if (!level) { 01108 LOG(llevError, "Error: Player: %s lacks wc experience object\n", who->name); 01109 level = who->level; 01110 } 01111 } else 01112 level = who->level; 01113 01114 return (improvs <= ((level/5)+5)); 01115 #endif 01116 } 01117 01139 int apply_special(object *who, object *op, int aflags) { 01140 int basic_flag = aflags&AP_BASIC_FLAGS; 01141 object *tmp, *skop = NULL; 01142 int i; 01143 char name_op[MAX_BUF]; 01144 01145 if (who == NULL) { 01146 LOG(llevError, "apply_special() from object without environment.\n"); 01147 return 1; 01148 } 01149 01150 query_name(op, name_op, MAX_BUF); 01151 01152 if (op->env != who) 01153 return 1; /* op is not in inventory */ 01154 01155 /* trying to unequip op */ 01156 if (QUERY_FLAG(op, FLAG_APPLIED)) { 01157 /* always apply, so no reason to unapply */ 01158 if (basic_flag == AP_APPLY) 01159 return 0; 01160 01161 if (!(aflags&AP_IGNORE_CURSE) 01162 && (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED))) { 01163 if (!(aflags&AP_NOPRINT)) 01164 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 01165 "No matter how hard you try, you just can't remove %s.", 01166 "No matter how hard you try, you just can't remove %s.", 01167 name_op); 01168 return 1; 01169 } 01170 return unapply_special(who, op, aflags); 01171 } 01172 01173 if (basic_flag == AP_UNAPPLY) 01174 return 0; 01175 01176 i = can_apply_object(who, op); 01177 01178 /* Can't just apply this object. Lets see what not and what to do */ 01179 if (i) { 01180 if (i&CAN_APPLY_NEVER) { 01181 if (!(aflags&AP_NOPRINT)) 01182 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_BADBODY, 01183 "You don't have the body to use a %s", 01184 "You don't have the body to use a %s", 01185 name_op); 01186 return 1; 01187 } else if (i&CAN_APPLY_RESTRICTION) { 01188 if (!(aflags&AP_NOPRINT)) 01189 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_PROHIBITION, 01190 "You have a prohibition against using a %s", 01191 "You have a prohibition against using a %s", 01192 name_op); 01193 return 1; 01194 } 01195 if (who->type != PLAYER) { 01196 /* Some error, so don't try to equip something more */ 01197 if (unapply_for_ob(who, op, aflags)) 01198 return 1; 01199 } else { 01200 if (who->contr->unapply == unapply_never 01201 || (i&CAN_APPLY_UNAPPLY_CHOICE && who->contr->unapply == unapply_nochoice)) { 01202 if (!(aflags&AP_NOPRINT)) 01203 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_UNAPPLY, 01204 "You need to unapply some item(s):", NULL); 01205 unapply_for_ob(who, op, AP_PRINT); 01206 return 1; 01207 } else if (who->contr->unapply == unapply_always 01208 || !(i&CAN_APPLY_UNAPPLY_CHOICE)) { 01209 i = unapply_for_ob(who, op, aflags); 01210 if (i) 01211 return 1; 01212 } 01213 } 01214 } 01215 if (op->skill && op->type != SKILL && op->type != SKILL_TOOL) { 01216 skop = find_skill_by_name(who, op->skill); 01217 if (!skop) { 01218 if (!(aflags&AP_NOPRINT)) 01219 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 01220 "You need the %s skill to use this item!", 01221 "You need the %s skill to use this item!", 01222 op->skill); 01223 return 1; 01224 } else { 01225 /* While experience will be credited properly, we want to 01226 * change the skill so that the dam and wc get updated 01227 */ 01228 change_skill(who, skop, (aflags&AP_NOPRINT)); 01229 } 01230 } 01231 01232 if (who->type == PLAYER 01233 && op->item_power 01234 && (op->item_power+who->contr->item_power) > (settings.item_power_factor*who->level)) { 01235 if (!(aflags&AP_NOPRINT)) 01236 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 01237 "Equipping that combined with other items would consume your soul!", NULL); 01238 return 1; 01239 } 01240 01241 /* If personalized blessings are activated, the weapon can bite 01242 * the wielder if he/she is not the one who initially blessed it. 01243 * Chances of being hurt depend on the experience amount 01244 * ("willpower") the object has, compared to the experience 01245 * amount of the wielder. 01246 */ 01247 if (settings.personalized_blessings) { 01248 const char *owner = get_ob_key_value(op, "item_owner"); 01249 if ((owner != NULL) && (strcmp(owner, who->name))) { 01250 const char *will = get_ob_key_value(op, "item_willpower"); 01251 long item_will = 0; 01252 long margin = 0; 01253 const char *msg = NULL; 01254 int random_effect = 0; 01255 int damage_percentile = 0; 01256 01257 if (will != NULL) 01258 item_will = atol(will); 01259 if (item_will > who->stats.exp) { 01260 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 01261 "This %s refuses to serve you - it keeps evading your hand !", 01262 "This %s refuses to serve you - it keeps evading your hand !", 01263 op->name); 01264 return 1; 01265 } 01266 if (item_will != 0) 01267 margin = who->stats.exp/item_will; 01268 else 01269 margin = who->stats.exp; 01270 random_effect = (random_roll(0, 100, who, 1)-(margin*20)); 01271 if (random_effect > 80) { 01272 msg = "You don't know why, but you have the feeling that the %s is angry at you !"; 01273 damage_percentile = 60; 01274 } else if (random_effect > 60) { 01275 msg = "The %s seems to look at you nastily !"; 01276 damage_percentile = 45; 01277 } else if (random_effect > 40) { 01278 msg = "You have the strange feeling that the %s is annoyed..."; 01279 damage_percentile = 30; 01280 } else if (random_effect > 20) { 01281 msg = "The %s seems tired, or bored, in a way. Very strange !"; 01282 damage_percentile = 15; 01283 } else if (random_effect > 0) { 01284 msg = "You hear the %s sighing !"; 01285 damage_percentile = 0; 01286 } 01287 if (msg != NULL) 01288 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01289 msg, msg, op->name); 01290 if (damage_percentile > 0) { 01291 int weapon_bite = (who->stats.hp*damage_percentile)/100; 01292 if (weapon_bite < 1) 01293 weapon_bite = 1; 01294 who->stats.hp -= weapon_bite; 01295 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_WAS_HIT, 01296 "You get a nasty bite in the hand !", 01297 "You get a nasty bite in the hand !"); 01298 } 01299 } 01300 } 01301 01302 /* Ok. We are now at the state where we can apply the new object. 01303 * Note that we don't have the checks for can_use_... 01304 * below - that is already taken care of by can_apply_object. 01305 */ 01306 01307 if (op->nrof > 1) 01308 tmp = get_split_ob(op, op->nrof-1, NULL, 0); 01309 else 01310 tmp = NULL; 01311 01312 switch (op->type) { 01313 case WEAPON: { 01314 int ownerlen = 0; 01315 char *quotepos = NULL; 01316 01317 if (!check_weapon_power(who, op->last_eat)) { 01318 if (!(aflags&AP_NOPRINT)) 01319 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, 01320 MSG_TYPE_APPLY_ERROR, 01321 "That weapon is too powerful for you to use. It would consume your soul!", 01322 NULL); 01323 01324 if (tmp != NULL) 01325 (void)insert_ob_in_ob(tmp, who); 01326 return 1; 01327 } 01328 /* BUG? It seems the value of quotepos is never used. */ 01329 if ((quotepos = strstr(op->name, "'")) != NULL) { 01330 ownerlen = (strstr(op->name, "'")-op->name); 01331 if (op->level && (strncmp(op->name, who->name, ownerlen))) { 01332 /* if the weapon does not have the name as the 01333 * character, can't use it. (Ragnarok's sword 01334 * attempted to be used by Foo: won't work) */ 01335 if (!(aflags&AP_NOPRINT)) 01336 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 01337 "The weapon does not recognize you as its owner.", NULL); 01338 if (tmp != NULL) 01339 (void)insert_ob_in_ob(tmp, who); 01340 return 1; 01341 } 01342 } 01343 SET_FLAG(op, FLAG_APPLIED); 01344 01345 if (skop) 01346 change_skill(who, skop, 1); 01347 if (!QUERY_FLAG(who, FLAG_READY_WEAPON)) 01348 SET_FLAG(who, FLAG_READY_WEAPON); 01349 01350 if (!(aflags&AP_NOPRINT)) 01351 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01352 "You wield %s.", "You wield %s.", 01353 name_op); 01354 01355 (void)change_abil(who, op); 01356 break; 01357 } 01358 01359 case ARMOUR: 01360 case HELMET: 01361 case SHIELD: 01362 case BOOTS: 01363 case GLOVES: 01364 case GIRDLE: 01365 case BRACERS: 01366 case CLOAK: 01367 case RING: 01368 case AMULET: 01369 SET_FLAG(op, FLAG_APPLIED); 01370 if (!(aflags&AP_NOPRINT)) 01371 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01372 "You wear %s.", 01373 "You wear %s.", 01374 name_op); 01375 (void)change_abil(who, op); 01376 break; 01377 01378 /* this part is needed for skill-tools */ 01379 case SKILL: 01380 case SKILL_TOOL: 01381 if (who->chosen_skill) { 01382 LOG(llevError, "BUG: apply_special(): can't apply two skills\n"); 01383 return 1; 01384 } 01385 01386 if (who->type == PLAYER) { 01387 who->contr->shoottype = range_skill; 01388 who->contr->ranges[range_skill] = op; 01389 if (!op->invisible) { 01390 if (!(aflags&AP_NOPRINT)) { 01391 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01392 "You ready %s.", 01393 "You ready %s.", 01394 name_op); 01395 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01396 "You can now use the skill: %s.", 01397 "You can now use the skill: %s.", 01398 op->skill); 01399 } 01400 } else { 01401 if (!(aflags&AP_NOPRINT)) 01402 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01403 "Readied skill: %s.", 01404 "Readied skill: %s.", 01405 op->skill ? op->skill : op->name); 01406 } 01407 } 01408 SET_FLAG(op, FLAG_APPLIED); 01409 (void)change_abil(who, op); 01410 who->chosen_skill = op; 01411 SET_FLAG(who, FLAG_READY_SKILL); 01412 break; 01413 01414 case BOW: 01415 if (!check_weapon_power(who, op->last_eat)) { 01416 if (!(aflags&AP_NOPRINT)) { 01417 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 01418 "That item is too powerful for you to use.", 01419 NULL); 01420 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 01421 "It would consume your soul!.", NULL); 01422 } 01423 if (tmp != NULL) 01424 (void)insert_ob_in_ob(tmp, who); 01425 return 1; 01426 } 01427 if (op->level && (strncmp(op->name, who->name, strlen(who->name)))) { 01428 if (!(aflags&AP_NOPRINT)) { 01429 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR, 01430 "The weapon does not recognize you as its owner.", NULL); 01431 } 01432 if (tmp != NULL) 01433 (void)insert_ob_in_ob(tmp, who); 01434 return 1; 01435 } 01436 /*FALLTHROUGH*/ 01437 case WAND: 01438 case ROD: 01439 case HORN: 01440 /* check for skill, alter player status */ 01441 SET_FLAG(op, FLAG_APPLIED); 01442 if (skop) 01443 change_skill(who, skop, 0); 01444 if (!(aflags&AP_NOPRINT)) 01445 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01446 "You ready %s.", 01447 "You ready %s.", 01448 name_op); 01449 if (who->type == PLAYER) { 01450 if (op->type == BOW) { 01451 (void)change_abil(who, op); 01452 if (!(aflags&AP_NOPRINT)) 01453 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01454 "You will now fire %s with %s.", 01455 "You will now fire %s with %s.", 01456 op->race ? op->race : "nothing", 01457 name_op); 01458 who->contr->shoottype = range_bow; 01459 } else { 01460 who->contr->shoottype = range_misc; 01461 } 01462 } else { 01463 if (op->type == BOW) 01464 SET_FLAG(who, FLAG_READY_BOW); 01465 else 01466 SET_FLAG(who, FLAG_READY_RANGE); 01467 } 01468 break; 01469 01470 case BUILDER: 01471 if (who->contr->ranges[range_builder]) 01472 unapply_special(who, who->contr->ranges[range_builder], 0); 01473 who->contr->shoottype = range_builder; 01474 who->contr->ranges[range_builder] = op; 01475 if (!(aflags&AP_NOPRINT)) 01476 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01477 "You ready your %s.", 01478 "You ready your %s.", 01479 name_op); 01480 break; 01481 01482 default: 01483 draw_ext_info_format(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_SUCCESS, 01484 "You apply %s.", 01485 "You apply %s.", 01486 name_op); 01487 } /* end of switch op->type */ 01488 01489 SET_FLAG(op, FLAG_APPLIED); 01490 01491 if (tmp != NULL) 01492 tmp = insert_ob_in_ob(tmp, who); 01493 01494 fix_object(who); 01495 01496 /* We exclude spell casting objects. The fire code will set the 01497 * been applied flag when they are used - until that point, 01498 * you don't know anything about them. 01499 */ 01500 if (who->type == PLAYER && op->type != WAND && op->type != HORN && op->type != ROD) 01501 SET_FLAG(op, FLAG_BEEN_APPLIED); 01502 01503 if (QUERY_FLAG(op, FLAG_CURSED) || QUERY_FLAG(op, FLAG_DAMNED)) { 01504 if (who->type == PLAYER) { 01505 draw_ext_info(NDI_UNIQUE, 0, who, MSG_TYPE_APPLY, MSG_TYPE_APPLY_CURSED, 01506 "Oops, it feels deadly cold!", NULL); 01507 SET_FLAG(op, FLAG_KNOWN_CURSED); 01508 } 01509 } 01510 if (who->type == PLAYER) { 01511 esrv_update_item(UPD_NROF|UPD_FLAGS|UPD_WEIGHT, who, op); 01512 } 01513 return 0; 01514 } 01515 01526 int auto_apply(object *op) { 01527 object *tmp = NULL, *tmp2; 01528 int i; 01529 01530 switch (op->type) { 01531 case SHOP_FLOOR: 01532 if (!HAS_RANDOM_ITEMS(op)) 01533 return 0; 01534 do { 01535 i = 10; /* let's give it 10 tries */ 01536 while ((tmp = generate_treasure(op->randomitems, op->stats.exp ? (int)op->stats.exp : MAX(op->map->difficulty, 5))) == NULL 01537 && --i) 01538 ; 01539 if (tmp == NULL) 01540 return 0; 01541 if (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED)) { 01542 free_object(tmp); 01543 tmp = NULL; 01544 } 01545 } while (!tmp); 01546 tmp->x = op->x; 01547 tmp->y = op->y; 01548 SET_FLAG(tmp, FLAG_UNPAID); 01549 insert_ob_in_map(tmp, op->map, NULL, 0); 01550 CLEAR_FLAG(op, FLAG_AUTO_APPLY); 01551 identify(tmp); 01552 break; 01553 01554 case TREASURE: 01555 if (QUERY_FLAG(op, FLAG_IS_A_TEMPLATE)) 01556 return 0; 01557 while ((op->stats.hp--) > 0) 01558 create_treasure(op->randomitems, op, op->map ? GT_ENVIRONMENT : 0, op->stats.exp ? (int)op->stats.exp : op->map == NULL ? 14 : op->map->difficulty, 0); 01559 01560 /* If we generated an object and put it in this object's 01561 * inventory, move it to the parent object as the current 01562 * object is about to disappear. An example of this item 01563 * is the random_ *stuff that is put inside other objects. 01564 */ 01565 for (tmp = op->inv; tmp; tmp = tmp2) { 01566 tmp2 = tmp->below; 01567 remove_ob(tmp); 01568 if (op->env) 01569 insert_ob_in_ob(tmp, op->env); 01570 else 01571 free_object(tmp); 01572 } 01573 remove_ob(op); 01574 free_object(op); 01575 break; 01576 } 01577 return tmp ? 1 : 0; 01578 } 01579 01590 void fix_auto_apply(mapstruct *m) { 01591 object *tmp, *above = NULL; 01592 int x, y; 01593 01594 if (m == NULL) 01595 return; 01596 01597 for (x = 0; x < MAP_WIDTH(m); x++) 01598 for (y = 0; y < MAP_HEIGHT(m); y++) 01599 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = above) { 01600 above = tmp->above; 01601 01602 if (tmp->inv) { 01603 object *invtmp, *invnext; 01604 01605 for (invtmp = tmp->inv; invtmp != NULL; invtmp = invnext) { 01606 invnext = invtmp->below; 01607 01608 if (QUERY_FLAG(invtmp, FLAG_AUTO_APPLY)) 01609 auto_apply(invtmp); 01610 else if (invtmp->type == TREASURE && HAS_RANDOM_ITEMS(invtmp)) { 01611 while ((invtmp->stats.hp--) > 0) 01612 create_treasure(invtmp->randomitems, invtmp, 0, m->difficulty, 0); 01613 invtmp->randomitems = NULL; 01614 } else if (invtmp && invtmp->arch 01615 && invtmp->type != TREASURE 01616 && invtmp->type != SPELL 01617 && invtmp->type != CLASS 01618 && HAS_RANDOM_ITEMS(invtmp)) { 01619 create_treasure(invtmp->randomitems, invtmp, 0, m->difficulty, 0); 01620 /* Need to clear this so that we never try to 01621 * create treasure again for this object 01622 */ 01623 invtmp->randomitems = NULL; 01624 } 01625 } 01626 /* This is really temporary - the code at the 01627 * bottom will also set randomitems to null. 01628 * The problem is there are bunches of maps/players 01629 * already out there with items that have spells 01630 * which haven't had the randomitems set 01631 * to null yet. 01632 * MSW 2004-05-13 01633 * 01634 * And if it's a spellbook, it's better to set 01635 * randomitems to NULL too, else you get two spells 01636 * in the book ^_- 01637 * Ryo 2004-08-16 01638 */ 01639 if (tmp->type == WAND 01640 || tmp->type == ROD 01641 || tmp->type == SCROLL 01642 || tmp->type == HORN 01643 || tmp->type == FIREWALL 01644 || tmp->type == POTION 01645 || tmp->type == ALTAR 01646 || tmp->type == SPELLBOOK) 01647 tmp->randomitems = NULL; 01648 } 01649 01650 if (QUERY_FLAG(tmp, FLAG_AUTO_APPLY)) 01651 auto_apply(tmp); 01652 else if ((tmp->type == TREASURE || (tmp->type == CONTAINER)) 01653 && HAS_RANDOM_ITEMS(tmp)) { 01654 while ((tmp->stats.hp--) > 0) 01655 create_treasure(tmp->randomitems, tmp, 0, m->difficulty, 0); 01656 tmp->randomitems = NULL; 01657 } else if (tmp->type == TIMED_GATE) { 01658 object *head = tmp->head != NULL ? tmp->head : tmp; 01659 01660 if (QUERY_FLAG(head, FLAG_IS_LINKED)) { 01661 tmp->speed = 0; 01662 update_ob_speed(tmp); 01663 } 01664 } 01665 /* This function can be called everytime a map is loaded, 01666 * even when swapping back in. As such, we don't want to 01667 * create the treasure over and ove again, so after we 01668 * generate the treasure, blank out randomitems so if it 01669 * is swapped in again, it won't make anything. This is a 01670 * problem for the above objects, because they have 01671 * counters which say how many times to make the treasure. 01672 */ 01673 else if (tmp 01674 && tmp->arch 01675 && tmp->type != PLAYER 01676 && tmp->type != TREASURE 01677 && tmp->type != SPELL 01678 && tmp->type != PLAYER_CHANGER 01679 && tmp->type != CLASS 01680 && HAS_RANDOM_ITEMS(tmp)) { 01681 create_treasure(tmp->randomitems, tmp, GT_APPLY, m->difficulty, 0); 01682 tmp->randomitems = NULL; 01683 } 01684 } 01685 01686 for (x = 0; x < MAP_WIDTH(m); x++) 01687 for (y = 0; y < MAP_HEIGHT(m); y++) 01688 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) 01689 if (tmp->above 01690 && (tmp->type == TRIGGER_BUTTON || tmp->type == TRIGGER_PEDESTAL)) 01691 check_trigger(tmp, tmp->above); 01692 } 01693 01708 void scroll_failure(object *op, int failure, int power) { 01709 if (abs(failure/4) > power) 01710 power = abs(failure/4); /* set minimum effect */ 01711 01712 if (failure <= -1 && failure > -15) {/* wonder */ 01713 object *tmp; 01714 01715 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, 01716 "Your spell warps!", NULL); 01717 tmp = create_archetype(SPELL_WONDER); 01718 cast_wonder(op, op, 0, tmp); 01719 if (op->stats.sp < 0) 01720 /* For some reason the sp can become negative here. */ 01721 op->stats.sp = 0; 01722 free_object(tmp); 01723 return; 01724 } 01725 01726 if (settings.spell_failure_effects == TRUE) { 01727 if (failure <= -35 && failure > -60) { /* confusion */ 01728 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, 01729 "The magic recoils on you!", NULL); 01730 confuse_living(op, op, power); 01731 return; 01732 } 01733 01734 if (failure <= -60 && failure > -70) {/* paralysis */ 01735 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, 01736 "The magic recoils and paralyzes you!", NULL); 01737 paralyze_living(op, op, power); 01738 return; 01739 } 01740 01741 if (failure <= -70 && failure > -80) {/* blind */ 01742 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, 01743 "The magic recoils on you!", NULL); 01744 blind_living(op, op, power); 01745 return; 01746 } 01747 01748 if (failure <= -80) {/* blast the immediate area */ 01749 object *tmp; 01750 01751 tmp = create_archetype(LOOSE_MANA); 01752 cast_magic_storm(op, tmp, power); 01753 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, 01754 "You unlease uncontrolled mana!", NULL); 01755 free_object(tmp); 01756 return; 01757 } 01758 } 01759 /* Either no spell failure on this server, or wrong values, 01760 * in any case let's punish. 01761 */ 01762 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_FAILURE, 01763 "Your mana is drained!", NULL); 01764 op->stats.sp -= random_roll(0, power-1, op, PREFER_LOW); 01765 if (op->stats.sp < 0) 01766 op->stats.sp = 0; 01767 } 01768 01776 void apply_changes_to_player(object *pl, object *change) { 01777 int excess_stat = 0; /* if the stat goes over the maximum 01778 * for the race, put the excess stat some 01779 * where else. 01780 */ 01781 01782 switch (change->type) { 01783 case CLASS: { 01784 living *stats = &(pl->contr->orig_stats); 01785 living *ns = &(change->stats); 01786 object *walk; 01787 int flag_change_face = 1; 01788 01789 /* the following code assigns stats up to the stat max 01790 * for the race, and if the stat max is exceeded, 01791 * tries to randomly reassign the excess stat 01792 */ 01793 int i, j; 01794 for (i = 0; i < NUM_STATS; i++) { 01795 sint8 stat = get_attr_value(stats, i); 01796 int race_bonus = get_attr_value(&(pl->arch->clone.stats), i); 01797 01798 stat += get_attr_value(ns, i); 01799 if (stat > 20+race_bonus) { 01800 excess_stat++; 01801 stat = 20+race_bonus; 01802 } 01803 set_attr_value(stats, i, stat); 01804 } 01805 01806 for (j = 0; excess_stat > 0 && j < 100; j++) { 01807 /* try 100 times to assign excess stats */ 01808 int i = rndm(0, 6); 01809 int stat = get_attr_value(stats, i); 01810 int race_bonus = get_attr_value(&(pl->arch->clone.stats), i); 01811 01812 if (i == CHA) 01813 continue; /* exclude cha from this */ 01814 if (stat < 20+race_bonus) { 01815 change_attr_value(stats, i, 1); 01816 excess_stat--; 01817 } 01818 } 01819 01820 /* insert the randomitems from the change's treasurelist into 01821 * the player ref: player.c 01822 */ 01823 if (change->randomitems != NULL) 01824 give_initial_items(pl, change->randomitems); 01825 01826 01827 /* set up the face, for some races. */ 01828 01829 /* first, look for the force object banning changing the 01830 * face. Certain races never change face with class. 01831 */ 01832 for (walk = pl->inv; walk != NULL; walk = walk->below) 01833 if (!strcmp(walk->name, "NOCLASSFACECHANGE")) 01834 flag_change_face = 0; 01835 01836 if (flag_change_face) { 01837 pl->animation_id = GET_ANIM_ID(change); 01838 pl->face = change->face; 01839 01840 if (QUERY_FLAG(change, FLAG_ANIMATE)) 01841 SET_FLAG(pl, FLAG_ANIMATE); 01842 else 01843 CLEAR_FLAG(pl, FLAG_ANIMATE); 01844 } 01845 01846 if (change->anim_suffix) { 01847 char buf[MAX_BUF]; 01848 int anim; 01849 01850 snprintf(buf, MAX_BUF, "%s_%s", animations[pl->animation_id].name, change->anim_suffix); 01851 anim = try_find_animation(buf); 01852 if (anim) { 01853 pl->animation_id = anim; 01854 pl->anim_speed = -1; 01855 CLEAR_FLAG(pl, FLAG_ANIMATE); 01856 animate_object(pl, pl->facing); 01857 } 01858 } 01859 01860 /* check the special case of can't use weapons */ 01861 /*if(QUERY_FLAG(change, FLAG_USE_WEAPON)) 01862 * CLEAR_FLAG(pl, FLAG_USE_WEAPON); 01863 */ 01864 if (!strcmp(change->name, "monk")) 01865 CLEAR_FLAG(pl, FLAG_USE_WEAPON); 01866 01867 break; 01868 } 01869 } 01870 } 01871 01872 void legacy_apply_container(object *op, object *sack) { 01873 apply_container(op, sack); 01874 }