Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_spell_effect_c = 00003 * "$Id: spell_effect.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 00006 00007 /* 00008 CrossFire, A Multiplayer game for X-windows 00009 00010 Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team 00011 Copyright (C) 1992 Frank Tore Johansen 00012 00013 This program is free software; you can redistribute it and/or modify 00014 it under the terms of the GNU General Public License as published by 00015 the Free Software Foundation; either version 2 of the License, or 00016 (at your option) any later version. 00017 00018 This program is distributed in the hope that it will be useful, 00019 but WITHOUT ANY WARRANTY; without even the implied warranty of 00020 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00021 GNU General Public License for more details. 00022 00023 You should have received a copy of the GNU General Public License 00024 along with this program; if not, write to the Free Software 00025 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00026 00027 The authors can be reached via e-mail at crossfire-devel@real-time.com 00028 */ 00029 00036 #include <global.h> 00037 #include <object.h> 00038 #include <living.h> 00039 #ifndef __CEXTRACT__ 00040 #include <sproto.h> 00041 #endif 00042 #include <spells.h> 00043 #include <sounds.h> 00044 00055 void cast_magic_storm(object *op, object *tmp, int lvl) { 00056 if (!tmp) 00057 return; /* error */ 00058 tmp->level = op->level; 00059 tmp->x = op->x; 00060 tmp->y = op->y; 00061 tmp->range += lvl/5; /* increase the area of destruction */ 00062 tmp->duration += lvl/5; 00063 00064 /* Put a cap on duration for this - if the player fails in their 00065 * apartment, don't want it to go on so long that it kills them 00066 * multiple times. Also, damge already increases with level, 00067 * so don't really need to increase the duration as much either. 00068 */ 00069 if (tmp->duration >= 40) 00070 tmp->duration = 40; 00071 tmp->stats.dam = lvl; /* nasty recoils! */ 00072 tmp->stats.maxhp = tmp->count; /* tract single parent */ 00073 insert_ob_in_map(tmp, op->map, op, 0); 00074 } 00075 00090 int recharge(object *op, object *caster, object *spell_ob) { 00091 object *wand, *tmp; 00092 int ncharges; 00093 char name[MAX_BUF]; 00094 00095 wand = find_marked_object(op); 00096 if (wand == NULL || wand->type != WAND) { 00097 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 00098 "You need to mark the wand you want to recharge.", NULL); 00099 return 0; 00100 } 00101 if (!(random_roll(0, 3, op, PREFER_HIGH))) { 00102 query_name(wand, name, MAX_BUF); 00103 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00104 "The %s vibrates violently, then explodes!", 00105 "The %s vibrates violently, then explodes!", 00106 name); 00107 play_sound_map(SOUND_TYPE_ITEM, wand, 0, "explode"); 00108 remove_ob(wand); 00109 free_object(wand); 00110 tmp = create_archetype("fireball"); 00111 tmp->stats.dam = (spell_ob->stats.dam+SP_level_dam_adjust(caster, spell_ob))/10; 00112 if (!tmp->stats.dam) 00113 tmp->stats.dam = 1; 00114 tmp->stats.hp = tmp->stats.dam/2; 00115 if (tmp->stats.hp < 2) 00116 tmp->stats.hp = 2; 00117 tmp->x = op->x; 00118 tmp->y = op->y; 00119 insert_ob_in_map(tmp, op->map, NULL, 0); 00120 return 1; 00121 } 00122 00123 ncharges = (spell_ob->stats.dam+SP_level_dam_adjust(caster, spell_ob)); 00124 if (wand->inv && wand->inv->level) 00125 ncharges /= wand->inv->level; 00126 else { 00127 query_name(wand, name, MAX_BUF); 00128 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00129 "Your %s is broken.", 00130 "Your %s is broken.", 00131 name); 00132 return 0; 00133 } 00134 if (!ncharges) 00135 ncharges = 1; 00136 00137 wand->stats.food += ncharges; 00138 query_name(wand, name, MAX_BUF); 00139 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00140 "The %s glows with power.", 00141 "The %s glows with power.", 00142 name); 00143 00144 if (wand->arch && QUERY_FLAG(&wand->arch->clone, FLAG_ANIMATE)) { 00145 SET_FLAG(wand, FLAG_ANIMATE); 00146 wand->speed = wand->arch->clone.speed; 00147 update_ob_speed(wand); 00148 } 00149 return 1; 00150 } 00151 00152 /****************************************************************************** 00153 * Start of polymorph related functions. 00154 * 00155 * Changed around for 0.94.3 - it will now look through and use all the 00156 * possible choices for objects/monsters (before it was teh first 80 - 00157 * arbitrary hardcoded limit in this file.) Doing this will be a bit 00158 * slower however - while before, it traversed the archetypes once and 00159 * stored them into an array, it will now potentially traverse it 00160 * an average of 1.5 times. This is probably more costly on the polymorph 00161 * item function, since it is possible a couple lookups might be needed before 00162 * an item of proper value is generated. 00163 */ 00164 00173 static void polymorph_living(object *op, int level) { 00174 archetype *at; 00175 int x = op->x, y = op->y, numat = 0, choice, friendly; 00176 mapstruct *map = op->map; 00177 object *tmp, *next, *owner; 00178 00179 if (op->head) 00180 op = op->head; 00181 00182 /* High level creatures are immune, as are creatures immune to magic. Otherwise, 00183 * give the creature a saving throw. 00184 */ 00185 if (op->level >= level*2 00186 || did_make_save(op, op->level, op->resist[ATNR_MAGIC]/10) 00187 || (op->resist[ATNR_MAGIC] == 100)) 00188 return; 00189 00190 remove_ob(op); 00191 00192 /* First, count up the number of legal matches */ 00193 for (at = first_archetype; at != NULL; at = at->next) 00194 if ((QUERY_FLAG((&at->clone), FLAG_MONSTER) == QUERY_FLAG(op, FLAG_MONSTER)) 00195 && (find_free_spot(&at->clone, map, x, y, 0, SIZEOFFREE) != -1)) { 00196 numat++; 00197 } 00198 if (!numat) { 00199 insert_ob_in_map(op, map, NULL, 0); 00200 return; /* no valid matches? if so, return */ 00201 } 00202 00203 /* Next make a choice, and loop through until we get to it */ 00204 choice = rndm(0, numat-1); 00205 for (at = first_archetype; at != NULL; at = at->next) 00206 if ((QUERY_FLAG((&at->clone), FLAG_MONSTER) == QUERY_FLAG(op, FLAG_MONSTER)) && (find_free_spot(&at->clone, map, x, y, 0, SIZEOFFREE) != -1)) { 00207 if (!choice) 00208 break; 00209 else 00210 choice--; 00211 } 00212 00213 /* Look through the monster. Unapply anything they have applied, 00214 * and remove any spells. Note that if this is extended 00215 * to players, that would need to get fixed somehow. 00216 */ 00217 for (tmp = op->inv; tmp != NULL; tmp = next) { 00218 next = tmp->below; 00219 if (QUERY_FLAG(tmp, FLAG_APPLIED)) 00220 manual_apply(op, tmp, 0); 00221 if (tmp->type == SPELL) { 00222 remove_ob(tmp); 00223 free_object(tmp); 00224 } 00225 } 00226 00227 /* Preserve some values for the new object */ 00228 owner = get_owner(op); 00229 friendly = QUERY_FLAG(op, FLAG_FRIENDLY); 00230 if (friendly) 00231 remove_friendly_object(op); 00232 00233 copy_object(&(at->clone), op); 00234 if (owner != NULL) 00235 set_owner(op, owner); 00236 if (friendly) { 00237 SET_FLAG(op, FLAG_FRIENDLY); 00238 op->attack_movement = PETMOVE; 00239 add_friendly_object(op); 00240 } else 00241 CLEAR_FLAG(op, FLAG_FRIENDLY); 00242 00243 /* Put the new creature on the map */ 00244 op->x = x; 00245 op->y = y; 00246 if ((op = insert_ob_in_map(op, map, owner, 0)) == NULL) 00247 return; 00248 00249 if (HAS_RANDOM_ITEMS(op)) 00250 /* No GT_APPLY here because we'll do it manually. */ 00251 create_treasure(op->randomitems, op, GT_INVISIBLE, map->difficulty, 0); 00252 00253 /* Apply any objects. */ 00254 for (tmp = op->inv; tmp != NULL; tmp = next) { 00255 next = tmp->below; 00256 monster_check_apply(op, tmp); 00257 } 00258 } 00259 00260 00272 static void polymorph_melt(object *who, object *op) { 00273 /* Not unique */ 00274 char name[MAX_BUF]; 00275 00276 query_name(op, name, MAX_BUF); 00277 if (op->nrof > 1) 00278 draw_ext_info_format(NDI_GREY, 0, who, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00279 "The %s glow red, melt and evaporate!", 00280 "The %s glow red, melt and evaporate!", 00281 name); 00282 else 00283 draw_ext_info_format(NDI_GREY, 0, who, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00284 "The %s glows red, melts and evaporates!", 00285 "The %s glows red, melts and evaporates!", 00286 name); 00287 play_sound_map(SOUND_TYPE_ITEM, op, 0, "evaporate"); 00288 remove_ob(op); 00289 free_object(op); 00290 return; 00291 } 00292 00302 static void polymorph_item(object *who, object *op, int level) { 00303 archetype *at; 00304 int max_value, difficulty, tries = 0, choice, charges = op->stats.food, numat = 0; 00305 object *new_ob; 00306 00307 /* We try and limit the maximum value of the changed object. */ 00308 max_value = op->value*2; 00309 if (max_value > 2000*(level/10)) 00310 max_value = 2000*(level/10)+(max_value-2000*(level/10))/3; 00311 00312 /* Look through and try to find matching items. Can't turn into something 00313 * invisible. Also, if the value is too high now, it would almost 00314 * certainly be too high below. 00315 */ 00316 for (at = first_archetype; at != NULL; at = at->next) { 00317 if (at->clone.type == op->type 00318 && !at->clone.invisible 00319 && at->clone.value > 0 00320 && at->clone.value < max_value 00321 && !QUERY_FLAG(&at->clone, FLAG_NO_DROP) 00322 && !QUERY_FLAG(&at->clone, FLAG_STARTEQUIP)) 00323 numat++; 00324 } 00325 00326 if (!numat) 00327 return; 00328 00329 difficulty = op->magic*5; 00330 if (difficulty < 0) 00331 difficulty = 0; 00332 new_ob = get_object(); 00333 do { 00334 choice = rndm(0, numat-1); 00335 for (at = first_archetype; at != NULL; at = at->next) { 00336 if (at->clone.type == op->type 00337 && !at->clone.invisible 00338 && at->clone.value > 0 00339 && at->clone.value < max_value 00340 && !QUERY_FLAG(&at->clone, FLAG_NO_DROP) 00341 && !QUERY_FLAG(&at->clone, FLAG_STARTEQUIP)) { 00342 if (!choice) 00343 break; 00344 else 00345 choice--; 00346 } 00347 } 00348 copy_object(&(at->clone), new_ob); 00349 fix_generated_item(new_ob, op, difficulty, FABS(op->magic), GT_ENVIRONMENT); 00350 ++tries; 00351 } while (new_ob->value > max_value && tries < 10); 00352 if (new_ob->invisible) { 00353 LOG(llevError, "polymorph_item: fix_generated_object made %s invisible?!\n", new_ob->name); 00354 free_object(new_ob); 00355 return; 00356 } 00357 00358 /* Unable to generate an acceptable item? Melt it */ 00359 if (tries == 10) { 00360 polymorph_melt(who, op); 00361 free_object(new_ob); 00362 return; 00363 } 00364 00365 if (op->nrof && new_ob->nrof) { 00366 new_ob->nrof = op->nrof; 00367 /* decrease the number of items */ 00368 if (new_ob->nrof > 2) 00369 new_ob->nrof -= rndm(0, op->nrof/2-1); 00370 } 00371 00372 /* We don't want rings to keep sustenance/hungry status. There are probably 00373 * other cases too that should be checked. 00374 */ 00375 if (charges && op->type != RING && op->type != FOOD) 00376 op->stats.food = charges; 00377 00378 new_ob->x = op->x; 00379 new_ob->y = op->y; 00380 remove_ob(op); 00381 free_object(op); 00382 /* 00383 * Don't want objects merged or re-arranged, as it then messes up the 00384 * order 00385 */ 00386 insert_ob_in_map(new_ob, who->map, new_ob, INS_NO_MERGE|INS_NO_WALK_ON); 00387 } 00388 00399 void polymorph(object *op, object *who, int level) { 00400 int tmp; 00401 00402 /* Can't polymorph players right now */ 00403 /* polymorphing generators opens up all sorts of abuses */ 00404 if (op->type == PLAYER || QUERY_FLAG(op, FLAG_GENERATOR)) 00405 return; 00406 00407 if (QUERY_FLAG(op, FLAG_MONSTER)) { 00408 polymorph_living(op, level); 00409 return; 00410 } 00411 /* If it is a living object of some other type, don't handle 00412 * it now. 00413 */ 00414 if (QUERY_FLAG(op, FLAG_ALIVE)) 00415 return; 00416 00417 /* Don't want to morph flying arrows, etc... */ 00418 if (FABS(op->speed) > 0.001 && !QUERY_FLAG(op, FLAG_ANIMATE)) 00419 return; 00420 00421 /* Do some sanity checking here. type=0 is unknown, objects 00422 * without archetypes are not good. As are a few other 00423 * cases. 00424 */ 00425 if (op->type == 0 00426 || op->arch == NULL 00427 || QUERY_FLAG(op, FLAG_NO_PICK) 00428 || op->move_block 00429 || op->type == TREASURE) 00430 return; 00431 00432 tmp = rndm(0, 7); 00433 if (tmp) 00434 polymorph_item(who, op, level); 00435 else 00436 polymorph_melt(who, op); 00437 } 00438 00439 00453 int cast_polymorph(object *op, object *caster, object *spell_ob, int dir) { 00454 object *tmp, *next; 00455 int range, mflags, maxrange, level; 00456 mapstruct *m; 00457 00458 if (dir == 0) 00459 return 0; 00460 00461 maxrange = spell_ob->range+SP_level_range_adjust(caster, spell_ob); 00462 level = caster_level(caster, spell_ob); 00463 for (range = 1; range < maxrange; range++) { 00464 sint16 x = op->x+freearr_x[dir]*range, y = op->y+freearr_y[dir]*range; 00465 object *image; 00466 00467 m = op->map; 00468 mflags = get_map_flags(m, &m, x, y, &x, &y); 00469 00470 if (mflags&(P_NO_MAGIC|P_OUT_OF_MAP)) 00471 break; 00472 00473 if (GET_MAP_MOVE_BLOCK(m, x, y)&MOVE_FLY_LOW) 00474 break; 00475 00476 /* Get the top most object */ 00477 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL && tmp->above != NULL; tmp = tmp->above) 00478 ; 00479 00480 /* Now start polymorphing the objects, top down */ 00481 while (tmp != NULL) { 00482 /* Once we find the floor, no need to go further */ 00483 if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) 00484 break; 00485 next = tmp->below; 00486 polymorph(tmp, op, level); 00487 tmp = next; 00488 } 00489 image = arch_to_object(spell_ob->other_arch); 00490 image->x = x; 00491 image->y = y; 00492 image->stats.food = 5; 00493 image->speed_left = 0.1; 00494 insert_ob_in_map(image, m, op, 0); 00495 } 00496 return 1; 00497 } 00498 00525 int cast_create_missile(object *op, object *caster, object *spell, int dir, const char *stringarg) { 00526 int missile_plus = 0, bonus_plus = 0; 00527 const char *missile_name; 00528 object *tmp, *missile; 00529 tag_t tag; 00530 00531 missile_name = "arrow"; 00532 00533 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) 00534 if (tmp->type == BOW && QUERY_FLAG(tmp, FLAG_APPLIED)) 00535 missile_name = tmp->race; 00536 00537 missile_plus = spell->stats.dam+SP_level_dam_adjust(caster, spell); 00538 00539 if (!strcmp(missile_name, "arrows")) 00540 missile_name = "arrow"; 00541 else if (!strcmp(missile_name, "crossbow bolts")) 00542 missile_name = "bolt"; 00543 00544 if (find_archetype(missile_name) == NULL) { 00545 LOG(llevDebug, "Cast create_missile: could not find archetype %s\n", missile_name); 00546 return 0; 00547 } 00548 missile = create_archetype(missile_name); 00549 00550 if (stringarg) { 00551 /* If it starts with a letter, presume it is a description */ 00552 if (isalpha(*stringarg)) { 00553 artifact *al = find_artifactlist(missile->type)->items; 00554 00555 for (; al != NULL; al = al->next) 00556 if (!strcasecmp(al->item->name, stringarg)) 00557 break; 00558 00559 if (!al) { 00560 free_object(missile); 00561 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 00562 "No such object %ss of %s", 00563 "No such object %ss of %s", 00564 missile_name, stringarg); 00565 return 0; 00566 } 00567 if (al->item->slaying) { 00568 free_object(missile); 00569 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 00570 "You are not allowed to create %ss of %s", 00571 "You are not allowed to create %ss of %s", 00572 missile_name, stringarg); 00573 return 0; 00574 } 00575 give_artifact_abilities(missile, al->item); 00576 /* These special arrows cost something extra. Don't have them also be 00577 * magical - otherwise, in most cases, not enough will be created. 00578 * I don't want to get into the parsing of having to do both plus and 00579 * type. 00580 */ 00581 bonus_plus = 1+(al->item->value/5); 00582 missile_plus = 0; 00583 } else if (atoi(stringarg) < missile_plus) 00584 missile_plus = atoi(stringarg); 00585 } 00586 if (missile_plus > 4) 00587 missile_plus = 4; 00588 else if (missile_plus < -4) 00589 missile_plus = -4; 00590 00591 missile->nrof = spell->duration+SP_level_duration_adjust(caster, spell); 00592 if (missile->nrof <= 3*(missile_plus+bonus_plus)) { 00593 free_object(missile); 00594 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 00595 "This item is too powerful for you to create!", 00596 NULL); 00597 return 0; 00598 } 00599 missile->nrof -= 3*(missile_plus+bonus_plus); 00600 if (missile->nrof < 1) 00601 missile->nrof = 1; 00602 00603 missile->magic = missile_plus; 00604 /* Can't get any money for these objects */ 00605 missile->value = 0; 00606 00607 SET_FLAG(missile, FLAG_IDENTIFIED); 00608 tag = missile->count; 00609 00610 if (!cast_create_obj(op, missile, dir) 00611 && op->type == PLAYER 00612 && !was_destroyed(missile, tag)) { 00613 pick_up(op, missile); 00614 } 00615 return 1; 00616 } 00617 00618 00639 int cast_create_food(object *op, object *caster, object *spell_ob, int dir, const char *stringarg) { 00640 int food_value; 00641 archetype *at = NULL; 00642 object *new_op; 00643 00644 food_value = spell_ob->stats.food+50*SP_level_duration_adjust(caster, spell_ob); 00645 00646 if (stringarg) { 00647 at = find_archetype_by_object_type_name(FOOD, stringarg); 00648 if (at == NULL) 00649 at = find_archetype_by_object_type_name(DRINK, stringarg); 00650 if (at == NULL || at->clone.stats.food > food_value) 00651 stringarg = NULL; 00652 } 00653 00654 if (!stringarg) { 00655 archetype *at_tmp; 00656 00657 /* We try to find the archetype with the maximum food value. 00658 * This removes the dependancy of hard coded food values in this 00659 * function, and addition of new food types is automatically added. 00660 * We don't use flesh types because the weight values of those need 00661 * to be altered from the donor. 00662 */ 00663 00664 /* We assume the food items don't have multiple parts */ 00665 for (at_tmp = first_archetype; at_tmp != NULL; at_tmp = at_tmp->next) { 00666 if (at_tmp->clone.type == FOOD || at_tmp->clone.type == DRINK) { 00667 /* Basically, if the food value is something that is creatable 00668 * under the limits of the spell and it is higher than 00669 * the item we have now, take it instead. 00670 */ 00671 if (at_tmp->clone.stats.food <= food_value 00672 && (!at || at_tmp->clone.stats.food > at->clone.stats.food)) 00673 at = at_tmp; 00674 } 00675 } 00676 } 00677 /* Pretty unlikely (there are some very low food items), but you never 00678 * know 00679 */ 00680 if (!at) { 00681 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00682 "You don't have enough experience to create any food.", NULL); 00683 return 0; 00684 } 00685 00686 food_value /= at->clone.stats.food; 00687 new_op = get_object(); 00688 copy_object(&at->clone, new_op); 00689 new_op->nrof = food_value; 00690 00691 new_op->value = 0; 00692 if (new_op->nrof < 1) 00693 new_op->nrof = 1; 00694 00695 cast_create_obj(op, new_op, dir); 00696 return 1; 00697 } 00698 00715 int probe(object *op, object *caster, object *spell_ob, int dir) { 00716 int r, mflags, maxrange; 00717 object *tmp; 00718 mapstruct *m; 00719 00720 if (!dir) { 00721 examine_monster(op, op); 00722 return 1; 00723 } 00724 maxrange = spell_ob->range+SP_level_range_adjust(caster, spell_ob); 00725 for (r = 1; r < maxrange; r++) { 00726 sint16 x = op->x+r*freearr_x[dir], y = op->y+r*freearr_y[dir]; 00727 00728 m = op->map; 00729 mflags = get_map_flags(m, &m, x, y, &x, &y); 00730 00731 if (mflags&P_OUT_OF_MAP) 00732 break; 00733 00734 if (!QUERY_FLAG(op, FLAG_WIZCAST) && (mflags&P_NO_MAGIC)) { 00735 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00736 "Something blocks your magic.", NULL); 00737 return 0; 00738 } 00739 if (mflags&P_IS_ALIVE) { 00740 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) 00741 if (QUERY_FLAG(tmp, FLAG_ALIVE)&&(tmp->type == PLAYER || QUERY_FLAG(tmp, FLAG_MONSTER))) { 00742 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00743 "You detect something.", NULL); 00744 if (tmp->head != NULL) 00745 tmp = tmp->head; 00746 examine_monster(op, tmp); 00747 return 1; 00748 } 00749 } 00750 } 00751 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00752 "You detect nothing.", NULL); 00753 return 1; 00754 } 00755 00756 00774 int makes_invisible_to(object *pl, object *mon) { 00775 if (!pl->invisible) 00776 return 0; 00777 if (pl->type == PLAYER) { 00778 /* If race isn't set, then invisible unless it is undead */ 00779 if (!pl->contr->invis_race) { 00780 if (QUERY_FLAG(mon, FLAG_UNDEAD)) 00781 return 0; 00782 return 1; 00783 } 00784 /* invis_race is set if we get here */ 00785 if (!strcmp(pl->contr->invis_race, "undead") && is_true_undead(mon)) 00786 return 1; 00787 /* No race, can't be invisible to it */ 00788 if (!mon->race) 00789 return 0; 00790 if (strstr(mon->race, pl->contr->invis_race)) 00791 return 1; 00792 /* Nothing matched above, return 0 */ 00793 return 0; 00794 } else { 00795 /* monsters are invisible to everything */ 00796 return 1; 00797 } 00798 } 00799 00821 int cast_invisible(object *op, object *caster, object *spell_ob) { 00822 object *tmp; 00823 00824 if (op->invisible > 1000) { 00825 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00826 "You can not extend the duration of your invisibility any further", NULL); 00827 return 0; 00828 } 00829 00830 /* Remove the switch with 90% duplicate code - just handle the differences with 00831 * and if statement or two. 00832 */ 00833 op->invisible += spell_ob->duration+SP_level_duration_adjust(caster, spell_ob); 00834 /* max duration */ 00835 if (op->invisible > 1000) 00836 op->invisible = 1000; 00837 00838 if (op->type == PLAYER) { 00839 if (op->contr->invis_race) 00840 FREE_AND_CLEAR_STR(op->contr->invis_race); 00841 if (spell_ob->race) 00842 op->contr->invis_race = add_refcount(spell_ob->race); 00843 if (QUERY_FLAG(spell_ob, FLAG_MAKE_INVIS)) 00844 op->contr->tmp_invis = 0; 00845 else 00846 op->contr->tmp_invis = 1; 00847 00848 op->contr->hidden = 0; 00849 } 00850 if (makes_invisible_to(op, op)) 00851 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00852 "You can't see your hands!", NULL); 00853 else 00854 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00855 "You feel more transparent!", NULL); 00856 00857 update_object(op, UP_OBJ_FACE); 00858 00859 /* Only search the active objects - only these should actually do 00860 * harm to the player. 00861 */ 00862 for (tmp = active_objects; tmp != NULL; tmp = tmp->active_next) 00863 if (tmp->enemy == op) 00864 tmp->enemy = NULL; 00865 return 1; 00866 } 00867 00882 int cast_earth_to_dust(object *op, object *caster, object *spell_ob) { 00883 object *tmp, *next; 00884 int range, i, j, mflags; 00885 sint16 sx, sy; 00886 mapstruct *m; 00887 00888 if (op->type != PLAYER) 00889 return 0; 00890 00891 range = spell_ob->range+SP_level_range_adjust(caster, spell_ob); 00892 00893 for (i = -range; i < range; i++) 00894 for (j = -range; j < range; j++) { 00895 sx = op->x+i; 00896 sy = op->y+j; 00897 m = op->map; 00898 mflags = get_map_flags(m, &m, sx, sy, &sx, &sy); 00899 00900 if (mflags&P_OUT_OF_MAP) 00901 continue; 00902 00903 /* If the space doesn't block, no wall here to remove 00904 * Don't care too much what it blocks - this allows for 00905 * any sort of earthwall/airwall/waterwall, etc 00906 * type effects. 00907 */ 00908 if (GET_MAP_MOVE_BLOCK(m, sx, sy)) { 00909 for (tmp = GET_MAP_OB(m, sx, sy); tmp != NULL; tmp = next) { 00910 next = tmp->above; 00911 if (tmp && QUERY_FLAG(tmp, FLAG_TEAR_DOWN)) 00912 hit_player(tmp, 9998, op, AT_PHYSICAL, 0); 00913 } 00914 } 00915 } 00916 return 1; 00917 } 00918 00935 int cast_word_of_recall(object *op, object *caster, object *spell_ob) { 00936 object *dummy; 00937 int time; 00938 00939 if (op->type != PLAYER) 00940 return 0; 00941 00942 if (find_obj_by_type_subtype(op, SPELL_EFFECT, SP_WORD_OF_RECALL)) { 00943 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00944 "You feel a force starting to build up inside you.", NULL); 00945 return 1; 00946 } 00947 00948 dummy = create_archetype(FORCE_NAME); 00949 if (dummy == NULL) { 00950 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 00951 "Oops, program error!", NULL); 00952 LOG(llevError, "cast_word_of_recall: create_archetype(force) failed!\n"); 00953 return 0; 00954 } 00955 time = spell_ob->duration-SP_level_duration_adjust(caster, spell_ob); 00956 if (time < 1) 00957 time = 1; 00958 00959 /* value of speed really doesn't make much difference, as long as it is 00960 * positive. Lower value may be useful so that the problem doesn't 00961 * do anything really odd if it say a -1000 or something. 00962 */ 00963 dummy->speed = 0.002; 00964 update_ob_speed(dummy); 00965 dummy->speed_left = -dummy->speed*time; 00966 dummy->type = SPELL_EFFECT; 00967 dummy->subtype = SP_WORD_OF_RECALL; 00968 00969 /* If we could take advantage of enter_player_savebed() here, it would be 00970 * nice, but until the map load fails, we can't. 00971 */ 00972 EXIT_PATH(dummy) = add_string(op->contr->savebed_map); 00973 EXIT_X(dummy) = op->contr->bed_x; 00974 EXIT_Y(dummy) = op->contr->bed_y; 00975 00976 (void)insert_ob_in_ob(dummy, op); 00977 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00978 "You feel a force starting to build up inside you.", NULL); 00979 return 1; 00980 } 00981 00997 int cast_wonder(object *op, object *caster, int dir, object *spell_ob) { 00998 object *newspell; 00999 01000 if (!rndm(0, 3)) 01001 return cast_cone(op, caster, dir, spell_ob); 01002 01003 if (spell_ob->randomitems) { 01004 newspell = generate_treasure(spell_ob->randomitems, caster->level); 01005 if (!newspell) { 01006 LOG(llevError, "cast_wonder: Unable to get a spell!\n"); 01007 return 0; 01008 } 01009 if (newspell->type != SPELL) { 01010 LOG(llevError, "cast_wonder: spell returned is not a spell (%d, %s)!\n", newspell->type, newspell->name); 01011 return 0; 01012 } 01013 /* Prevent inifinit recursion */ 01014 if (newspell->subtype == SP_WONDER) { 01015 LOG(llevError, "cast_wonder: spell returned is another wonder spell!\n"); 01016 return 0; 01017 } 01018 return cast_spell(op, caster, dir, newspell, NULL); 01019 } 01020 return 1; 01021 } 01022 01031 int perceive_self(object *op) { 01032 char cp[VERY_BIG_BUF], buf[MAX_BUF]; 01033 archetype *at = find_archetype(ARCH_DEPLETION); 01034 object *tmp; 01035 const object *god; 01036 int i; 01037 01038 describe_item(op, op, cp, VERY_BIG_BUF); 01039 01040 god = find_god(determine_god(op)); 01041 if (god) 01042 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01043 "You worship %s", 01044 "You worship %s", 01045 god->name); 01046 else 01047 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01048 "You worship no god", NULL); 01049 01050 tmp = present_arch_in_ob(at, op); 01051 01052 if (*cp == '\0' && tmp == NULL) 01053 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01054 "You feel very mundane", NULL); 01055 else { 01056 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01057 "You have:", NULL); 01058 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01059 cp, cp); 01060 if (tmp != NULL) { 01061 for (i = 0; i < NUM_STATS; i++) { 01062 if (get_attr_value(&tmp->stats, i) < 0) { 01063 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01064 "Your %s is depleted by %d", 01065 "Your %s is depleted by %d", 01066 statname[i], -(get_attr_value(&tmp->stats, i))); 01067 } 01068 } 01069 } 01070 } 01071 if (op->glow_radius > 0) 01072 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01073 "You glow in the dark.", NULL); 01074 01075 if (is_dragon_pl(op)) { 01076 /* now grab the 'dragon_ability'-force from the player's inventory */ 01077 for (tmp = op->inv; tmp != NULL; tmp = tmp->below) { 01078 if (tmp->type == FORCE && !strcmp(tmp->arch->name, "dragon_ability_force")) { 01079 if (tmp->stats.exp == 0) { 01080 snprintf(buf, sizeof(buf), "Your metabolism isn't focused on anything."); 01081 } else { 01082 snprintf(buf, sizeof(buf), "Your metabolism is focused on %s.", change_resist_msg[tmp->stats.exp]); 01083 } 01084 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_PERCEIVE_SELF, 01085 buf, buf); 01086 break; 01087 } 01088 } 01089 } 01090 return 1; 01091 } 01092 01122 int cast_create_town_portal(object *op, object *caster, object *spell, int dir) { 01123 object *dummy, *force, *old_force, *tmp; 01124 archetype *perm_portal; 01125 char portal_name [1024], portal_message [1024]; 01126 sint16 exitx, exity; 01127 mapstruct *exitmap; 01128 int op_level; 01129 01130 /* Check to see if the map the player is currently on is a per player unique 01131 * map. This can be determined in that per player unique maps have the 01132 * full pathname listed. Ignore if settings.create_home_portals is true. 01133 */ 01134 if (!settings.create_home_portals) { 01135 if (!strncmp(op->map->path, settings.localdir, strlen(settings.localdir))) { 01136 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01137 "You can't cast that here.", NULL); 01138 return 0; 01139 } 01140 } 01141 01142 /* Check to see if the player is on a transport */ 01143 if (op->contr && op->contr->transport) { 01144 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01145 "You need to exit the transport to cast that.", NULL); 01146 return 0; 01147 } 01148 01149 /* The first thing to do is to check if we have a marked destination 01150 * dummy is used to make a check inventory for the force 01151 */ 01152 dummy = arch_to_object(spell->other_arch); 01153 if (dummy == NULL) { 01154 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01155 "Oops, program error!", NULL); 01156 LOG(llevError, "get_object failed (force in cast_create_town_portal for %s!\n", op->name); 01157 return 0; 01158 } 01159 force = check_inv_recursive(op, dummy); 01160 01161 if (force == NULL) { 01162 /* Here we know there is no destination marked up. 01163 * We have 2 things to do: 01164 * 1. Mark the destination in the player inventory. 01165 * 2. Let the player know it worked. 01166 */ 01167 free_string(dummy->name); 01168 dummy->name = add_string(op->map->path); 01169 EXIT_X(dummy) = op->x; 01170 EXIT_Y(dummy) = op->y; 01171 dummy->weapontype = op->map->last_reset_time.tv_sec; 01172 insert_ob_in_ob(dummy, op); 01173 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 01174 "You fix this place in your mind and feel that you" 01175 "can come here from anywhere.", 01176 NULL); 01177 return 1; 01178 } 01179 free_object(dummy); 01180 01181 /* Here we know where the town portal should go to 01182 * We should kill any existing portal associated with the player. 01183 * Than we should create the 2 portals. 01184 * For each of them, we need: 01185 * - To create the portal with the name of the player+destination map 01186 * - set the owner of the town portal 01187 * - To mark the position of the portal in the player's inventory 01188 * for easier destruction. 01189 * 01190 * The mark works has follow: 01191 * slaying: Existing town portal 01192 * hp, sp : x & y of the associated portal 01193 * name : name of the portal 01194 * race : map the portal is in 01195 */ 01196 01197 /* First step: killing existing town portals */ 01198 dummy = create_archetype(spell->race); 01199 if (dummy == NULL) { 01200 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01201 "Oops, program error!", NULL); 01202 LOG(llevError, "get_object failed (force) in cast_create_town_portal for %s!\n", op->name); 01203 return 0; 01204 } 01205 perm_portal = find_archetype(spell->slaying); 01206 01207 /* To kill a town portal, we go trough the player's inventory, 01208 * for each marked portal in player's inventory, 01209 * -We try load the associated map (if impossible, consider the portal destructed) 01210 * -We find any portal in the specified location. 01211 * If it has the good name, we destruct it. 01212 * -We destruct the force indicating that portal. 01213 */ 01214 while ((old_force = check_inv_recursive(op, dummy))) { 01215 exitx = EXIT_X(old_force); 01216 exity = EXIT_Y(old_force); 01217 LOG(llevDebug, "Trying to kill a portal in %s (%d,%d)\n", old_force->race, exitx, exity); 01218 01219 if (!strncmp(old_force->race, settings.localdir, strlen(settings.localdir))) 01220 exitmap = ready_map_name(old_force->race, MAP_PLAYER_UNIQUE); 01221 else 01222 exitmap = ready_map_name(old_force->race, 0); 01223 01224 if (exitmap) { 01225 tmp = present_arch(perm_portal, exitmap, exitx, exity); 01226 while (tmp) { 01227 if (tmp->name == old_force->name) { 01228 remove_ob(tmp); 01229 free_object(tmp); 01230 break; 01231 } else { 01232 tmp = tmp->above; 01233 } 01234 } 01235 } 01236 remove_ob(old_force); 01237 free_object(old_force); 01238 LOG(llevDebug, "\n"); 01239 } 01240 free_object(dummy); 01241 01242 /* Creating the portals. 01243 * The very first thing to do is to ensure 01244 * access to the destination map. 01245 * If we can't, don't fizzle. Simply warn player. 01246 * This ensure player pays his mana for the spell 01247 * because HE is responsible of forgotting. 01248 * 'force' is the destination of the town portal, which we got 01249 * from the players inventory above. 01250 */ 01251 01252 /* Ensure exit map is loaded*/ 01253 if (!strncmp(force->name, settings.localdir, strlen(settings.localdir))) 01254 exitmap = ready_map_name(force->name, MAP_PLAYER_UNIQUE); 01255 else 01256 exitmap = ready_map_name(force->name, 0); 01257 01258 /* If we were unable to load (ex. random map deleted), warn player*/ 01259 if (exitmap == NULL) { 01260 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01261 "Something strange happens. You can't remember where to go!?", 01262 NULL); 01263 remove_ob(force); 01264 free_object(force); 01265 return 1; 01266 } else if (exitmap->last_reset_time.tv_sec != force->weapontype) { 01267 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01268 "The spell effect has expired.", NULL); 01269 remove_ob(force); 01270 free_object(force); 01271 return 1; 01272 } 01273 01274 op_level = caster_level(caster, spell); 01275 if (op_level < 15) 01276 snprintf(portal_message, 1024, "\nThe air moves around you and\na huge smell of ammonia\nsurounds you as you pass\nthrough %s's tiny portal\nPouah!\n", op->name); 01277 else if (op_level < 30) 01278 snprintf(portal_message, 1024, "\n%s's portal smells of ozone.\nYou do a lot of movements and finally pass\nthrough the small hole in the air\n", op->name); 01279 else if (op_level < 60) 01280 snprintf(portal_message, 1024, "\nA shining door opens in the air in front of you,\nshowing you the path to another place.\n"); 01281 else 01282 snprintf(portal_message, 1024, "\nAs you walk through %s's portal, flowers come out\nfrom the ground around you.\nYou feel awed.\n", op->name); 01283 01284 /* Create a portal in front of player 01285 * dummy contain the portal and 01286 * force contain the track to kill it later 01287 */ 01288 01289 snprintf(portal_name, 1024, "%s's portal to %s", op->name, force->name); 01290 dummy = create_archetype(spell->slaying); /*The portal*/ 01291 if (dummy == NULL) { 01292 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01293 "Oops, program error!", NULL); 01294 LOG(llevError, "get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n", op->name); 01295 return 0; 01296 } 01297 EXIT_PATH(dummy) = add_string(force->name); 01298 EXIT_X(dummy) = EXIT_X(force); 01299 EXIT_Y(dummy) = EXIT_Y(force); 01300 FREE_AND_COPY(dummy->name, portal_name); 01301 FREE_AND_COPY(dummy->name_pl, portal_name); 01302 dummy->msg = add_string(portal_message); 01303 dummy->race = add_string(op->name); /*Save the owner of the portal*/ 01304 cast_create_obj(op, dummy, 0); 01305 01306 /* Now we need to to create a town portal marker inside the player 01307 * object, so on future castings, we can know that he has an active 01308 * town portal. 01309 */ 01310 tmp = create_archetype(spell->race); 01311 if (tmp == NULL) { 01312 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01313 "Oops, program error!", NULL); 01314 LOG(llevError, "get_object failed (force) in cast_create_town_portal for %s!\n", op->name); 01315 return 0; 01316 } 01317 tmp->race = add_string(op->map->path); 01318 FREE_AND_COPY(tmp->name, portal_name); 01319 EXIT_X(tmp) = dummy->x; 01320 EXIT_Y(tmp) = dummy->y; 01321 insert_ob_in_ob(tmp, op); 01322 01323 /* Create a portal in the destination map 01324 * dummy contain the portal and 01325 * force the track to kill it later 01326 * the 'force' variable still contains the 'reminder' of 01327 * where this portal goes to. 01328 */ 01329 snprintf(portal_name, 1024, "%s's portal to %s", op->name, op->map->path); 01330 dummy = create_archetype(spell->slaying); /*The portal*/ 01331 if (dummy == NULL) { 01332 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01333 "Oops, program error!", NULL); 01334 LOG(llevError, "get_object failed (perm_magic_portal) in cast_create_town_portal for %s!\n", op->name); 01335 return 0; 01336 } 01337 EXIT_PATH(dummy) = add_string(op->map->path); 01338 EXIT_X(dummy) = op->x; 01339 EXIT_Y(dummy) = op->y; 01340 FREE_AND_COPY(dummy->name, portal_name); 01341 FREE_AND_COPY(dummy->name_pl, portal_name); 01342 dummy->msg = add_string(portal_message); 01343 dummy->x = EXIT_X(force); 01344 dummy->y = EXIT_Y(force); 01345 dummy->race = add_string(op->name); /*Save the owner of the portal*/ 01346 insert_ob_in_map(dummy, exitmap, op, 0); 01347 01348 /* Now we create another town portal marker that 01349 * points back to the one we just made 01350 */ 01351 tmp = create_archetype(spell->race); 01352 if (tmp == NULL) { 01353 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01354 "Oops, program error!", NULL); 01355 LOG(llevError, "get_object failed (force) in cast_create_town_portal for %s!\n", op->name); 01356 return 0; 01357 } 01358 tmp->race = add_string(force->name); 01359 FREE_AND_COPY(tmp->name, portal_name); 01360 EXIT_X(tmp) = dummy->x; 01361 EXIT_Y(tmp) = dummy->y; 01362 insert_ob_in_ob(tmp, op); 01363 01364 /* Describe the player what happened 01365 */ 01366 draw_ext_info(NDI_UNIQUE|NDI_NAVY, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 01367 "You see air moving and showing you the way home.", NULL); 01368 remove_ob(force); /* Delete the force inside the player*/ 01369 free_object(force); 01370 return 1; 01371 } 01372 01373 01391 int magic_wall(object *op, object *caster, int dir, object *spell_ob) { 01392 object *tmp, *tmp2; 01393 int i, posblocked, negblocked, maxrange; 01394 sint16 x, y; 01395 mapstruct *m; 01396 const char *name; 01397 archetype *at; 01398 01399 if (!dir) { 01400 dir = op->facing; 01401 x = op->x; 01402 y = op->y; 01403 } else { 01404 x = op->x+freearr_x[dir]; 01405 y = op->y+freearr_y[dir]; 01406 } 01407 m = op->map; 01408 01409 if ((spell_ob->move_block || x != op->x || y != op->y) 01410 && (get_map_flags(m, &m, x, y, &x, &y)&(P_OUT_OF_MAP|P_IS_ALIVE) 01411 || ((spell_ob->move_block&GET_MAP_MOVE_BLOCK(m, x, y)) == spell_ob->move_block))) { 01412 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01413 "Something is in the way.", NULL); 01414 return 0; 01415 } 01416 if (spell_ob->other_arch) { 01417 tmp = arch_to_object(spell_ob->other_arch); 01418 } else if (spell_ob->race) { 01419 char buf1[MAX_BUF]; 01420 01421 snprintf(buf1, sizeof(buf1), spell_ob->race, dir); 01422 at = find_archetype(buf1); 01423 if (!at) { 01424 LOG(llevError, "summon_wall: Unable to find archetype %s\n", buf1); 01425 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01426 "This spell is broken.", NULL); 01427 return 0; 01428 } 01429 tmp = arch_to_object(at); 01430 } else { 01431 LOG(llevError, "magic_wall: spell %s lacks other_arch\n", spell_ob->name); 01432 return 0; 01433 } 01434 01435 if (tmp->type == SPELL_EFFECT) { 01436 tmp->attacktype = spell_ob->attacktype; 01437 tmp->duration = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob); 01438 tmp->stats.dam = spell_ob->stats.dam+SP_level_dam_adjust(caster, spell_ob); 01439 tmp->range = 0; 01440 } else if (QUERY_FLAG(tmp, FLAG_ALIVE)) { 01441 tmp->stats.hp = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob); 01442 tmp->stats.maxhp = tmp->stats.hp; 01443 set_owner(tmp, op); 01444 set_spell_skill(op, caster, spell_ob, tmp); 01445 } 01446 if (QUERY_FLAG(spell_ob, FLAG_IS_USED_UP) || QUERY_FLAG(tmp, FLAG_IS_USED_UP)) { 01447 tmp->stats.food = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob); 01448 SET_FLAG(tmp, FLAG_IS_USED_UP); 01449 } 01450 if (QUERY_FLAG(spell_ob, FLAG_TEAR_DOWN)) { 01451 tmp->stats.hp = spell_ob->stats.dam+SP_level_dam_adjust(caster, spell_ob); 01452 tmp->stats.maxhp = tmp->stats.hp; 01453 SET_FLAG(tmp, FLAG_TEAR_DOWN); 01454 SET_FLAG(tmp, FLAG_ALIVE); 01455 } 01456 01457 /* This can't really hurt - if the object doesn't kill anything, 01458 * these fields just won't be used. 01459 */ 01460 set_owner(tmp, op); 01461 set_spell_skill(op, caster, spell_ob, tmp); 01462 tmp->x = x; 01463 tmp->y = y; 01464 tmp->level = caster_level(caster, spell_ob)/2; 01465 01466 name = tmp->name; 01467 if ((tmp = insert_ob_in_map(tmp, m, op, 0)) == NULL) { 01468 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01469 "Something destroys your %s", 01470 "Something destroys your %s", 01471 name); 01472 return 0; 01473 } 01474 /* If this is a spellcasting wall, need to insert the spell object */ 01475 if (tmp->other_arch && tmp->other_arch->clone.type == SPELL) 01476 insert_ob_in_ob(arch_to_object(tmp->other_arch), tmp); 01477 01478 /* This code causes the wall to extend some distance in 01479 * each direction, or until an obstruction is encountered. 01480 * posblocked and negblocked help determine how far the 01481 * created wall can extend, it won't go extend through 01482 * blocked spaces. 01483 */ 01484 maxrange = spell_ob->range+SP_level_range_adjust(caster, spell_ob); 01485 posblocked = 0; 01486 negblocked = 0; 01487 01488 for (i = 1; i <= maxrange; i++) { 01489 int dir2; 01490 01491 dir2 = (dir < 4) ? (dir+2) : dir-2; 01492 01493 x = tmp->x+i*freearr_x[dir2]; 01494 y = tmp->y+i*freearr_y[dir2]; 01495 m = tmp->map; 01496 01497 if (!(get_map_flags(m, &m, x, y, &x, &y)&(P_OUT_OF_MAP|P_IS_ALIVE)) 01498 && ((spell_ob->move_block&GET_MAP_MOVE_BLOCK(m, x, y)) != spell_ob->move_block) 01499 && !posblocked) { 01500 tmp2 = get_object(); 01501 copy_object(tmp, tmp2); 01502 tmp2->x = x; 01503 tmp2->y = y; 01504 insert_ob_in_map(tmp2, m, op, 0); 01505 /* If this is a spellcasting wall, need to insert the spell object */ 01506 if (tmp2->other_arch && tmp2->other_arch->clone.type == SPELL) 01507 insert_ob_in_ob(arch_to_object(tmp2->other_arch), tmp2); 01508 } else 01509 posblocked = 1; 01510 01511 x = tmp->x-i*freearr_x[dir2]; 01512 y = tmp->y-i*freearr_y[dir2]; 01513 m = tmp->map; 01514 01515 if (!(get_map_flags(m, &m, x, y, &x, &y)&(P_OUT_OF_MAP|P_IS_ALIVE)) 01516 && ((spell_ob->move_block&GET_MAP_MOVE_BLOCK(m, x, y)) != spell_ob->move_block) 01517 && !negblocked) { 01518 tmp2 = get_object(); 01519 copy_object(tmp, tmp2); 01520 tmp2->x = x; 01521 tmp2->y = y; 01522 insert_ob_in_map(tmp2, m, op, 0); 01523 if (tmp2->other_arch && tmp2->other_arch->clone.type == SPELL) 01524 insert_ob_in_ob(arch_to_object(tmp2->other_arch), tmp2); 01525 } else 01526 negblocked = 1; 01527 } 01528 01529 if (QUERY_FLAG(tmp, FLAG_BLOCKSVIEW)) 01530 update_all_los(op->map, op->x, op->y); 01531 01532 return 1; 01533 } 01534 01551 int dimension_door(object *op, object *caster, object *spob, int dir) { 01552 uint32 dist, maxdist; 01553 int mflags; 01554 mapstruct *m; 01555 sint16 sx, sy; 01556 01557 if (op->type != PLAYER) 01558 return 0; 01559 01560 if (!dir) { 01561 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01562 "In what direction?", NULL); 01563 return 0; 01564 } 01565 01566 /* Given the new outdoor maps, can't let players dimension door for 01567 * ever, so put limits in. 01568 */ 01569 maxdist = spob->range+SP_level_range_adjust(caster, spob); 01570 01571 if (op->contr->count) { 01572 if (op->contr->count > maxdist) { 01573 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01574 "You can't dimension door that far!", NULL); 01575 return 0; 01576 } 01577 01578 for (dist = 0; dist < op->contr->count; dist++) { 01579 mflags = get_map_flags(op->map, &m, 01580 op->x+freearr_x[dir]*(dist+1), 01581 op->y+freearr_y[dir]*(dist+1), 01582 &sx, &sy); 01583 01584 if (mflags&(P_NO_MAGIC|P_OUT_OF_MAP)) 01585 break; 01586 01587 if ((mflags&P_BLOCKSVIEW) 01588 && OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) 01589 break; 01590 } 01591 01592 if (dist < op->contr->count) { 01593 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01594 "Something blocks the magic of the spell.", NULL); 01595 op->contr->count = 0; 01596 return 0; 01597 } 01598 op->contr->count = 0; 01599 01600 /* Remove code that puts player on random space on maps. IMO, 01601 * a lot of maps probably have areas the player should not get to, 01602 * but may not be marked as NO_MAGIC (as they may be bounded 01603 * by such squares). Also, there are probably treasure rooms and 01604 * lots of other maps that protect areas with no magic, but the 01605 * areas themselves don't contain no magic spaces. 01606 */ 01607 /* This call here is really just to normalize the coordinates */ 01608 mflags = get_map_flags(op->map, &m, op->x+freearr_x[dir]*dist, op->y+freearr_y[dir]*dist, 01609 &sx, &sy); 01610 if (mflags&P_IS_ALIVE || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) { 01611 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01612 "You cast your spell, but nothing happens.", NULL); 01613 return 1; /* Maybe the penalty should be more severe... */ 01614 } 01615 } else { 01616 /* Player didn't specify a distance, so lets see how far 01617 * we can move the player. Don't know why this stopped on 01618 * spaces that blocked the players view. 01619 */ 01620 01621 for (dist = 0; dist < maxdist; dist++) { 01622 mflags = get_map_flags(op->map, &m, 01623 op->x+freearr_x[dir]*(dist+1), 01624 op->y+freearr_y[dir]*(dist+1), 01625 &sx, &sy); 01626 01627 if (mflags&(P_NO_MAGIC|P_OUT_OF_MAP)) 01628 break; 01629 01630 if ((mflags&P_BLOCKSVIEW) 01631 && OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) 01632 break; 01633 01634 } 01635 01636 /* If the destination is blocked, keep backing up until we 01637 * find a place for the player. 01638 */ 01639 for (; dist > 0; dist--) { 01640 if (get_map_flags(op->map, &m, op->x+freearr_x[dir]*dist, op->y+freearr_y[dir]*dist, &sx, &sy)&(P_OUT_OF_MAP|P_IS_ALIVE)) 01641 continue; 01642 01643 if (!OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) 01644 break; 01645 } 01646 if (!dist) { 01647 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01648 "Your spell failed!", NULL); 01649 return 0; 01650 } 01651 } 01652 01653 /* Actually move the player now */ 01654 remove_ob(op); 01655 op->x += freearr_x[dir]*dist; 01656 op->y += freearr_y[dir]*dist; 01657 if ((op = insert_ob_in_map(op, op->map, op, 0)) == NULL) 01658 return 1; 01659 01660 if (op->type == PLAYER) 01661 map_newmap_cmd(&op->contr->socket); 01662 op->speed_left = -FABS(op->speed)*5; /* Freeze them for a short while */ 01663 return 1; 01664 } 01665 01666 01679 int cast_heal(object *op, object *caster, object *spell, int dir) { 01680 object *tmp; 01681 archetype *at; 01682 object *poison; 01683 int heal = 0, success = 0; 01684 01685 tmp = find_target_for_friendly_spell(op, dir); 01686 01687 if (tmp == NULL) 01688 return 0; 01689 01690 /* Figure out how many hp this spell might cure. 01691 * could be zero if this spell heals effects, not damage. 01692 */ 01693 heal = spell->stats.dam; 01694 if (spell->stats.hp) 01695 heal += random_roll(spell->stats.hp, 6, op, PREFER_HIGH)+spell->stats.hp; 01696 01697 if (heal) { 01698 if (tmp->stats.hp >= tmp->stats.maxhp) { 01699 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01700 "You are already fully healed.", NULL); 01701 } else { 01702 /* See how many points we actually heal. Instead of messages 01703 * based on type of spell, we instead do messages based 01704 * on amount of damage healed. 01705 */ 01706 if (heal > (tmp->stats.maxhp-tmp->stats.hp)) 01707 heal = tmp->stats.maxhp-tmp->stats.hp; 01708 tmp->stats.hp += heal; 01709 01710 if (tmp->stats.hp >= tmp->stats.maxhp) { 01711 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01712 "You feel just fine!", NULL); 01713 } else if (heal > 50) { 01714 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01715 "Your wounds close!", NULL); 01716 } else if (heal > 25) { 01717 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01718 "Your wounds mostly close.", NULL); 01719 } else if (heal > 10) { 01720 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01721 "Your wounds start to fade.", NULL); 01722 } else { 01723 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01724 "Your wounds start to close.", NULL); 01725 } 01726 success = 1; 01727 } 01728 } 01729 if (spell->attacktype&AT_DISEASE) 01730 if (cure_disease(tmp, op)) 01731 success = 1; 01732 01733 if (spell->attacktype&AT_POISON) { 01734 at = find_archetype("poisoning"); 01735 poison = present_arch_in_ob(at, tmp); 01736 if (poison) { 01737 success = 1; 01738 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01739 "Your body feels cleansed", NULL); 01740 poison->stats.food = 1; 01741 } 01742 } 01743 if (spell->attacktype&AT_CONFUSION) { 01744 poison = present_in_ob_by_name(FORCE, "confusion", tmp); 01745 if (poison) { 01746 success = 1; 01747 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01748 "Your mind feels clearer", NULL); 01749 poison->duration = 1; 01750 } 01751 } 01752 if (spell->attacktype&AT_BLIND) { 01753 at = find_archetype("blindness"); 01754 poison = present_arch_in_ob(at, tmp); 01755 if (poison) { 01756 success = 1; 01757 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01758 "Your vision begins to return.", NULL); 01759 poison->stats.food = 1; 01760 } 01761 } 01762 if (spell->last_sp && tmp->stats.sp < tmp->stats.maxsp) { 01763 tmp->stats.sp += spell->last_sp; 01764 if (tmp->stats.sp > tmp->stats.maxsp) 01765 tmp->stats.sp = tmp->stats.maxsp; 01766 success = 1; 01767 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01768 "Magical energies surge through your body!", NULL); 01769 } 01770 if (spell->last_grace && tmp->stats.grace < tmp->stats.maxgrace) { 01771 tmp->stats.grace += spell->last_grace; 01772 if (tmp->stats.grace > tmp->stats.maxgrace) 01773 tmp->stats.grace = tmp->stats.maxgrace; 01774 success = 1; 01775 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01776 "You feel redeemed with you god!", NULL); 01777 } 01778 if (spell->stats.food && tmp->stats.food < 999) { 01779 tmp->stats.food += spell->stats.food; 01780 if (tmp->stats.food > 999) 01781 tmp->stats.food = 999; 01782 success = 1; 01783 /* We could do something a bit better like the messages for healing above */ 01784 draw_ext_info(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_HEAL, 01785 "You feel your belly fill with food", NULL); 01786 } 01787 return success; 01788 } 01789 01790 01796 static const char *const no_gain_msgs[NUM_STATS] = { 01797 "You grow no stronger.", 01798 "You grow no more agile.", 01799 "You don't feel any healthier.", 01800 "no wis", 01801 "You are no easier to look at.", 01802 "no int", 01803 "no pow" 01804 }; 01805 01825 int cast_change_ability(object *op, object *caster, object *spell_ob, int dir, int silent) { 01826 object *tmp, *tmp2 = NULL; 01827 object *force = NULL; 01828 int i; 01829 01830 /* if dir = 99 op defaults to tmp, eat_special_food() requires this. */ 01831 if (dir != 0) { 01832 tmp = find_target_for_friendly_spell(op, dir); 01833 } else { 01834 tmp = op; 01835 } 01836 01837 if (tmp == NULL) 01838 return 0; 01839 01840 /* If we've already got a force of this type, don't add a new one. */ 01841 for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below) { 01842 if (tmp2->type == FORCE && tmp2->subtype == FORCE_CHANGE_ABILITY) { 01843 if (tmp2->name == spell_ob->name) { 01844 force = tmp2; /* the old effect will be "refreshed" */ 01845 break; 01846 } else if (spell_ob->race && spell_ob->race == tmp2->name) { 01847 if (!silent) 01848 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01849 "You can not cast %s while %s is in effect", 01850 "You can not cast %s while %s is in effect", 01851 spell_ob->name, tmp2->name_pl); 01852 return 0; 01853 } 01854 } 01855 } 01856 if (force == NULL) { 01857 force = create_archetype(FORCE_NAME); 01858 force->subtype = FORCE_CHANGE_ABILITY; 01859 free_string(force->name); 01860 if (spell_ob->race) 01861 force->name = add_refcount(spell_ob->race); 01862 else 01863 force->name = add_refcount(spell_ob->name); 01864 free_string(force->name_pl); 01865 force->name_pl = add_refcount(spell_ob->name); 01866 } else { 01867 int duration; 01868 01869 duration = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob)*50; 01870 if (duration > force->duration) { 01871 force->duration = duration; 01872 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 01873 "You recast the spell while in effect.", NULL); 01874 } else { 01875 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01876 "Recasting the spell had no effect.", NULL); 01877 } 01878 return 1; 01879 } 01880 force->duration = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob)*50; 01881 if (op->type == PLAYER) 01882 store_spell_expiry(force); 01883 force->speed = 1.0; 01884 force->speed_left = -1.0; 01885 SET_FLAG(force, FLAG_APPLIED); 01886 01887 /* Now start processing the effects. First, protections */ 01888 for (i = 0; i < NROFATTACKS; i++) { 01889 if (spell_ob->resist[i]) { 01890 force->resist[i] = spell_ob->resist[i]+SP_level_dam_adjust(caster, spell_ob); 01891 if (force->resist[i] > 100) 01892 force->resist[i] = 100; 01893 } 01894 } 01895 if (spell_ob->stats.hp) 01896 force->stats.hp = spell_ob->stats.hp+SP_level_dam_adjust(caster, spell_ob); 01897 01898 if (tmp->type == PLAYER) { 01899 /* Stat adjustment spells */ 01900 for (i = 0; i < NUM_STATS; i++) { 01901 sint8 stat = get_attr_value(&spell_ob->stats, i), k, sm; 01902 01903 if (stat) { 01904 sm = 0; 01905 for (k = 0; k < stat; k++) 01906 sm += rndm(1, 3); 01907 01908 if ((get_attr_value(&tmp->stats, i)+sm) > (15+5*stat)) { 01909 sm = (15+5*stat)-get_attr_value(&tmp->stats, i); 01910 if (sm < 0) 01911 sm = 0; 01912 } 01913 set_attr_value(&force->stats, i, sm); 01914 if (!sm) 01915 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 01916 no_gain_msgs[i], no_gain_msgs[i]); 01917 } 01918 } 01919 } 01920 01921 force->move_type = spell_ob->move_type; 01922 01923 if (QUERY_FLAG(spell_ob, FLAG_SEE_IN_DARK)) 01924 SET_FLAG(force, FLAG_SEE_IN_DARK); 01925 01926 if (QUERY_FLAG(spell_ob, FLAG_XRAYS)) 01927 SET_FLAG(force, FLAG_XRAYS); 01928 01929 /* Haste/bonus speed */ 01930 if (spell_ob->stats.exp) { 01931 if (op->speed > 0.5) 01932 force->stats.exp = (float)spell_ob->stats.exp/(op->speed+0.5); 01933 else 01934 force->stats.exp = spell_ob->stats.exp; 01935 } 01936 01937 force->stats.wc = spell_ob->stats.wc; 01938 force->stats.ac = spell_ob->stats.ac; 01939 force->attacktype = spell_ob->attacktype; 01940 01941 SET_FLAG(tmp, FLAG_NO_FIX_PLAYER); /* we don't want insert_ob_in_ob to call fix_object. */ 01942 insert_ob_in_ob(force, tmp); 01943 CLEAR_FLAG(tmp, FLAG_NO_FIX_PLAYER); 01944 change_abil(tmp, force); /* Display any relevant messages, and call fix_object to update the player */ 01945 01946 return 1; 01947 } 01948 01965 int cast_bless(object *op, object *caster, object *spell_ob, int dir) { 01966 int i; 01967 const object *god = find_god(determine_god(op)); 01968 object *tmp2, *force = NULL, *tmp; 01969 01970 /* if dir = 99 op defaults to tmp, eat_special_food() requires this. */ 01971 if (dir != 0) { 01972 tmp = find_target_for_friendly_spell(op, dir); 01973 } else { 01974 tmp = op; 01975 } 01976 01977 /* If we've already got a force of this type, don't add a new one. */ 01978 for (tmp2 = tmp->inv; tmp2 != NULL; tmp2 = tmp2->below) { 01979 if (tmp2->type == FORCE && tmp2->subtype == FORCE_CHANGE_ABILITY) { 01980 if (tmp2->name == spell_ob->name) { 01981 force = tmp2; /* the old effect will be "refreshed" */ 01982 break; 01983 } else if (spell_ob->race && spell_ob->race == tmp2->name) { 01984 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 01985 "You can not cast %s while %s is in effect", 01986 "You can not cast %s while %s is in effect", 01987 spell_ob->name, tmp2->name_pl); 01988 return 0; 01989 } 01990 } 01991 } 01992 if (force == NULL) { 01993 force = create_archetype(FORCE_NAME); 01994 force->subtype = FORCE_CHANGE_ABILITY; 01995 free_string(force->name); 01996 if (spell_ob->race) 01997 force->name = add_refcount(spell_ob->race); 01998 else 01999 force->name = add_refcount(spell_ob->name); 02000 free_string(force->name_pl); 02001 force->name_pl = add_refcount(spell_ob->name); 02002 } else { 02003 int duration; 02004 02005 duration = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob)*50; 02006 if (duration > force->duration) { 02007 force->duration = duration; 02008 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 02009 "You recast the spell while in effect.", NULL); 02010 } else { 02011 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02012 "Recasting the spell had no effect.", NULL); 02013 } 02014 return 0; 02015 } 02016 force->duration = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob)*50; 02017 force->speed = 1.0; 02018 force->speed_left = -1.0; 02019 SET_FLAG(force, FLAG_APPLIED); 02020 02021 if (!god) { 02022 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02023 "Your blessing seems empty.", NULL); 02024 } else { 02025 /* Only give out good benefits, and put a max on it */ 02026 for (i = 0; i < NROFATTACKS; i++) { 02027 if (god->resist[i] > 0) { 02028 force->resist[i] = MIN(god->resist[i], spell_ob->resist[ATNR_GODPOWER]); 02029 } 02030 } 02031 force->path_attuned |= god->path_attuned; 02032 if (spell_ob->attacktype) { 02033 force->attacktype |= god->attacktype|AT_PHYSICAL; 02034 if (god->slaying) 02035 force->slaying = add_string(god->slaying); 02036 } 02037 if (tmp != op) { 02038 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 02039 "You bless %s.", 02040 "You bless %s.", 02041 tmp->name); 02042 draw_ext_info_format(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_TARGET, 02043 "%s blessed you.", 02044 "%s blessed you.", 02045 op->name); 02046 } else { 02047 draw_ext_info_format(NDI_UNIQUE, 0, tmp, MSG_TYPE_SPELL, MSG_TYPE_SPELL_TARGET, 02048 "You are blessed by %s!", 02049 "You are blessed by %s!", 02050 god->name); 02051 } 02052 } 02053 force->stats.wc = spell_ob->stats.wc; 02054 force->stats.ac = spell_ob->stats.ac; 02055 02056 change_abil(tmp, force); /* Mostly to display any messages */ 02057 insert_ob_in_ob(force, tmp); 02058 fix_object(tmp); 02059 return 1; 02060 } 02061 02062 02063 02064 /* 02065 * Alchemy code by Mark Wedel 02066 * 02067 * This code adds a new spell, called alchemy. Alchemy will turn 02068 * objects to gold nuggets, the value of the gold nuggets being 02069 * from 5% to 40% of that of the item itself depending on casting level. 02070 * It uses the value of the object before charisma adjustments, because 02071 * the nuggets themselves will be will be adjusted by charisma when sold. 02072 * 02073 * Large nuggets are worth 25 gp each (base). You will always get 02074 * the maximum number of large nuggets you could get. 02075 * Small nuggets are worth 1 gp each (base). You will get from 0 02076 * to the max amount of small nuggets as you could get. 02077 * 02078 * For example, if an item is worth 110 gold, you will get 02079 * 4 large nuggets, and from 0-10 small nuggets. 02080 * 02081 * There is also a chance (1:30) that you will get nothing at all 02082 * for the object. There is also a maximum weight that will be 02083 * alchemied. 02084 */ 02085 02086 /* I didn't feel like passing these as arguements to the 02087 * two functions that need them. Real values are put in them 02088 * when the spell is cast, and these are freed when the spell 02089 * is finished. 02090 */ 02091 static object *small, *large; 02093 static uint64 small_value, large_value; 02109 static void alchemy_object(float value_adj, object *obj, int *small_nuggets, int *large_nuggets, int *weight) { 02110 uint64 value = query_cost(obj, NULL, F_TRUE); 02111 02112 /* Multiply the value of the object by value_adj, which should range 02113 * from 0.05 to 0.40. Set value to 0 instead if unpaid. 02114 */ 02115 if (QUERY_FLAG(obj, FLAG_UNPAID)) 02116 value = 0; 02117 else 02118 value *= value_adj; 02119 02120 02121 /* Give half of what value_adj says when we alchemy money (This should 02122 * hopefully make it so that it isn't worth it to alchemy money, sell 02123 * the nuggets, alchemy the gold from that, etc. 02124 */ 02125 if (value && (obj->type == MONEY || obj->type == GEM)) 02126 value /= 2; 02127 02128 if ((obj->value > 0) && rndm(0, 29)) { 02129 int count; 02130 02131 count = value/large_value; 02132 *large_nuggets += count; 02133 value -= (uint64)count*large_value; 02134 count = value/small_value; 02135 *small_nuggets += count; 02136 } 02137 02138 /* Turn 25 small nuggets into 1 large nugget. If the value 02139 * of large nuggets is not evenly divisable by the small nugget 02140 * value, take off an extra small_nugget (Assuming small_nuggets!=0) 02141 */ 02142 if (*small_nuggets*small_value >= large_value) { 02143 (*large_nuggets)++; 02144 *small_nuggets -= large_value/small_value; 02145 if (*small_nuggets && large_value%small_value) 02146 (*small_nuggets)--; 02147 } 02148 weight += obj->weight; 02149 remove_ob(obj); 02150 free_object(obj); 02151 } 02152 02166 static void place_alchemy_objects(object *op, mapstruct *m, int small_nuggets, int large_nuggets, int x, int y) { 02167 object *tmp; 02168 int flag = 0; 02169 02170 /* Put any nuggets below the player, but we can only pass this 02171 * flag if we are on the same space as the player 02172 */ 02173 if (x == op->x && y == op->y && op->map == m) 02174 flag = INS_BELOW_ORIGINATOR; 02175 02176 if (small_nuggets) { 02177 tmp = get_object(); 02178 copy_object(small, tmp); 02179 tmp-> nrof = small_nuggets; 02180 tmp->x = x; 02181 tmp->y = y; 02182 insert_ob_in_map(tmp, m, op, flag); 02183 } 02184 if (large_nuggets) { 02185 tmp = get_object(); 02186 copy_object(large, tmp); 02187 tmp-> nrof = large_nuggets; 02188 tmp->x = x; 02189 tmp->y = y; 02190 insert_ob_in_map(tmp, m, op, flag); 02191 } 02192 } 02193 02208 int alchemy(object *op, object *caster, object *spell_ob) { 02209 int x, y, weight = 0, weight_max, large_nuggets, small_nuggets, mflags; 02210 sint16 nx, ny; 02211 float value_adj; 02212 object *next, *tmp; 02213 mapstruct *mp; 02214 02215 if (op->type != PLAYER) 02216 return 0; 02217 02218 /* Put a maximum weight of items that can be alchemied. Limits the power 02219 * some, and also prevents people from alcheming every table/chair/clock 02220 * in sight 02221 */ 02222 weight_max = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob); 02223 weight_max *= 1000; 02224 small = create_archetype("smallnugget"), 02225 large = create_archetype("largenugget"); 02226 small_value = query_cost(small, NULL, F_TRUE); 02227 large_value = query_cost(large, NULL, F_TRUE); 02228 02229 /* Set value_adj to be a multiplier for how much of the original value 02230 * will be in the nuggets. Starts at 0.05, increasing by 0.01 per casting 02231 * level, maxing out at 0.40. 02232 */ 02233 value_adj = (SP_level_dam_adjust(caster, spell_ob)/100.00)+0.05; 02234 02235 if (value_adj > 0.40) 02236 value_adj = 0.40; 02237 02238 for (y = op->y-1; y <= op->y+1; y++) { 02239 for (x = op->x-1; x <= op->x+1; x++) { 02240 nx = x; 02241 ny = y; 02242 02243 mp = op->map; 02244 02245 mflags = get_map_flags(mp, &mp, nx, ny, &nx, &ny); 02246 02247 if (mflags&(P_OUT_OF_MAP|P_NO_MAGIC)) 02248 continue; 02249 02250 /* Treat alchemy a little differently - most spell effects 02251 * use fly as the movement type - for alchemy, consider it 02252 * ground level effect. 02253 */ 02254 if (GET_MAP_MOVE_BLOCK(mp, nx, ny)&MOVE_WALK) 02255 continue; 02256 02257 small_nuggets = 0; 02258 large_nuggets = 0; 02259 02260 for (tmp = GET_MAP_OB(mp, nx, ny); tmp != NULL; tmp = next) { 02261 next = tmp->above; 02262 if (tmp->weight > 0 && !QUERY_FLAG(tmp, FLAG_NO_PICK) 02263 && !QUERY_FLAG(tmp, FLAG_ALIVE) 02264 && !QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) { 02265 if (tmp->inv) { 02266 object *next1, *tmp1; 02267 for (tmp1 = tmp->inv; tmp1 != NULL; tmp1 = next1) { 02268 next1 = tmp1->below; 02269 if (tmp1->weight > 0 && !QUERY_FLAG(tmp1, FLAG_NO_PICK) 02270 && !QUERY_FLAG(tmp1, FLAG_ALIVE) 02271 && !QUERY_FLAG(tmp1, FLAG_IS_CAULDRON)) 02272 alchemy_object(value_adj, tmp1, &small_nuggets, &large_nuggets, &weight); 02273 } 02274 } 02275 alchemy_object(value_adj, tmp, &small_nuggets, &large_nuggets, &weight); 02276 02277 if (weight > weight_max) { 02278 place_alchemy_objects(op, mp, small_nuggets, large_nuggets, nx, ny); 02279 free_object(large); 02280 free_object(small); 02281 return 1; 02282 } 02283 } /* is alchemable object */ 02284 } /* process all objects on this space */ 02285 02286 /* Insert all the nuggets at one time. This probably saves time, but 02287 * it also prevents us from alcheming nuggets that were just created 02288 * with this spell. 02289 */ 02290 place_alchemy_objects(op, mp, small_nuggets, large_nuggets, nx, ny); 02291 } 02292 } 02293 free_object(large); 02294 free_object(small); 02295 /* reset this so that if player standing on a big pile of stuff, 02296 * it is redrawn properly. 02297 */ 02298 op->contr->socket.look_position = 0; 02299 return 1; 02300 } 02301 02302 02317 int remove_curse(object *op, object *caster, object *spell) { 02318 object *tmp; 02319 int success = 0, was_one = 0; 02320 02321 for (tmp = op->inv; tmp; tmp = tmp->below) 02322 if (QUERY_FLAG(tmp, FLAG_APPLIED) 02323 && ((QUERY_FLAG(tmp, FLAG_CURSED) && QUERY_FLAG(spell, FLAG_CURSED)) 02324 || (QUERY_FLAG(tmp, FLAG_DAMNED) && QUERY_FLAG(spell, FLAG_DAMNED)))) { 02325 was_one++; 02326 if (tmp->level <= caster_level(caster, spell)) { 02327 success++; 02328 if (QUERY_FLAG(spell, FLAG_DAMNED)) 02329 CLEAR_FLAG(tmp, FLAG_DAMNED); 02330 02331 CLEAR_FLAG(tmp, FLAG_CURSED); 02332 CLEAR_FLAG(tmp, FLAG_KNOWN_CURSED); 02333 tmp->value = 0; /* Still can't sell it */ 02334 if (op->type == PLAYER) 02335 esrv_update_item(UPD_FLAGS, op, tmp); 02336 } 02337 } 02338 02339 if (op->type == PLAYER) { 02340 if (success) { 02341 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 02342 "You feel like some of your items are looser now.", NULL); 02343 } else { 02344 if (was_one) 02345 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02346 "You failed to remove the curse.", NULL); 02347 else 02348 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02349 "You are not using any cursed items.", NULL); 02350 } 02351 } 02352 return success; 02353 } 02354 02367 int cast_item_curse_or_curse(object *op, object *caster, object *spell_ob) { 02368 object *marked = find_marked_object(op); 02369 char name[HUGE_BUF]; 02370 02371 if (!marked) { 02372 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02373 "You need to mark an item first!", NULL); 02374 return 0; 02375 } 02376 02377 if ((QUERY_FLAG(marked, FLAG_CURSED) && QUERY_FLAG(spell_ob, FLAG_CURSED)) 02378 || (QUERY_FLAG(marked, FLAG_BLESSED) && QUERY_FLAG(spell_ob, FLAG_BLESSED))) { 02379 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02380 "The spell has no effect", NULL); 02381 return 0; 02382 } 02383 02384 query_short_name(marked, name, HUGE_BUF); 02385 if (QUERY_FLAG(spell_ob, FLAG_CURSED)) { 02386 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 02387 "Your %s emits a dark light for a few seconds.", "Your %s emits a dark light for a few seconds.", name); 02388 SET_FLAG(marked, FLAG_CURSED); 02389 CLEAR_FLAG(marked, FLAG_KNOWN_CURSED); 02390 CLEAR_FLAG(marked, FLAG_IDENTIFIED); 02391 esrv_update_item(UPD_FLAGS, op, marked); 02392 return 1; 02393 02394 } 02395 02396 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 02397 "Your %s glows blue for a few seconds.", "Your %s glows blue for a few seconds.", name); 02398 SET_FLAG(marked, FLAG_BLESSED); 02399 SET_FLAG(marked, FLAG_KNOWN_BLESSED); 02400 SET_FLAG(marked, FLAG_STARTEQUIP); 02401 esrv_update_item(UPD_FLAGS, op, marked); 02402 return 1; 02403 } 02404 02419 int cast_identify(object *op, object *caster, object *spell) { 02420 object *tmp; 02421 int success = 0, num_ident; 02422 char desc[MAX_BUF]; 02423 02424 num_ident = spell->stats.dam+SP_level_dam_adjust(caster, spell); 02425 02426 if (num_ident < 1) 02427 num_ident = 1; 02428 02429 for (tmp = op->inv; tmp; tmp = tmp->below) { 02430 if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) && !tmp->invisible && need_identify(tmp)) { 02431 identify(tmp); 02432 if (op->type == PLAYER) { 02433 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO, 02434 "You have %s.", 02435 "You have %s.", 02436 ob_describe(tmp, op, desc, sizeof(desc))); 02437 if (tmp->msg) { 02438 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO, 02439 "The item has a story:\n%s", 02440 "The item has a story:\n%s", 02441 tmp->msg); 02442 } 02443 } 02444 num_ident--; 02445 success = 1; 02446 if (!num_ident) 02447 break; 02448 } 02449 } 02450 /* If all the power of the spell has been used up, don't go and identify 02451 * stuff on the floor. Only identify stuff on the floor if the spell 02452 * was not fully used. 02453 */ 02454 if (num_ident) { 02455 for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) 02456 if (!QUERY_FLAG(tmp, FLAG_IDENTIFIED) 02457 && !tmp->invisible 02458 && need_identify(tmp)) { 02459 identify(tmp); 02460 if (op->type == PLAYER) { 02461 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO, 02462 "On the ground is %s.", 02463 "On the ground is %s.", 02464 ob_describe(tmp, op, desc, sizeof(desc))); 02465 if (tmp->msg) { 02466 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_ITEM, MSG_TYPE_ITEM_INFO, 02467 "The item has a story:\n%s", 02468 "The item has a story:\n%s", 02469 tmp->msg); 02470 } 02471 esrv_update_item(UPD_FLAGS|UPD_NAME, op, tmp); 02472 } 02473 num_ident--; 02474 success = 1; 02475 if (!num_ident) 02476 break; 02477 } 02478 } 02479 if (!success) 02480 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02481 "You can't reach anything unidentified.", NULL); 02482 else { 02483 spell_effect(spell, op->x, op->y, op->map, op); 02484 } 02485 return success; 02486 } 02487 02500 int cast_detection(object *op, object *caster, object *spell) { 02501 object *tmp, *last, *detect; 02502 const object *god; 02503 int done_one, range, mflags, floor, level; 02504 sint16 x, y, nx, ny; 02505 mapstruct *m; 02506 02507 /* We precompute some values here so that we don't have to keep 02508 * doing it over and over again. 02509 */ 02510 god = find_god(determine_god(op)); 02511 level = caster_level(caster, spell); 02512 range = spell->range+SP_level_range_adjust(caster, spell); 02513 02514 for (x = op->x-range; x <= op->x+range; x++) 02515 for (y = op->y-range; y <= op->y+range; y++) { 02516 02517 m = op->map; 02518 mflags = get_map_flags(m, &m, x, y, &nx, &ny); 02519 if (mflags&P_OUT_OF_MAP) 02520 continue; 02521 02522 /* For most of the detections, we only detect objects above the 02523 * floor. But this is not true for show invisible. 02524 * Basically, we just go and find the top object and work 02525 * down - that is easier than working up. 02526 */ 02527 02528 for (last = NULL, tmp = GET_MAP_OB(m, nx, ny); tmp; tmp = tmp->above) 02529 last = tmp; 02530 /* Shouldn't happen, but if there are no objects on a space, this 02531 * would happen. 02532 */ 02533 if (!last) 02534 continue; 02535 02536 done_one = 0; 02537 floor = 0; 02538 detect = NULL; 02539 for (tmp = last; tmp; tmp = tmp->below) { 02540 /* show invisible */ 02541 if (QUERY_FLAG(spell, FLAG_MAKE_INVIS) 02542 /* Might there be other objects that we can make visibile? */ 02543 && (tmp->invisible && (QUERY_FLAG(tmp, FLAG_MONSTER) || 02544 (tmp->type == PLAYER && !QUERY_FLAG(tmp, FLAG_WIZ)) || 02545 tmp->type == CF_HANDLE || 02546 tmp->type == TRAPDOOR || tmp->type == EXIT || tmp->type == HOLE || 02547 tmp->type == BUTTON || tmp->type == TELEPORTER || 02548 tmp->type == GATE || tmp->type == LOCKED_DOOR || 02549 tmp->type == WEAPON || tmp->type == ALTAR || tmp->type == SIGN || 02550 tmp->type == TRIGGER_PEDESTAL || tmp->type == SPECIAL_KEY || 02551 tmp->type == TREASURE || tmp->type == BOOK || 02552 tmp->type == HOLY_ALTAR))) { 02553 if (random_roll(0, level-1, op, PREFER_HIGH) > tmp->level) { 02554 tmp->invisible = 0; 02555 done_one = 1; 02556 } 02557 } 02558 if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) 02559 floor = 1; 02560 02561 /* All detections below this point don't descend beneath the floor, 02562 * so just continue on. We could be clever and look at the type of 02563 * detection to completely break out if we don't care about objects beneath 02564 * the floor, but once we get to the floor, not likely a very big issue anyways. 02565 */ 02566 if (floor) 02567 continue; 02568 02569 /* I had thought about making detect magic and detect curse 02570 * show the flash the magic item like it does for detect monster. 02571 * however, if the object is within sight, this would then make it 02572 * difficult to see what object is magical/cursed, so the 02573 * effect wouldn't be as apparant. 02574 */ 02575 02576 /* detect magic */ 02577 if (QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL) 02578 && !QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL) 02579 && !QUERY_FLAG(tmp, FLAG_IDENTIFIED) 02580 && is_magical(tmp)) { 02581 SET_FLAG(tmp, FLAG_KNOWN_MAGICAL); 02582 /* make runes more visibile */ 02583 if (tmp->type == RUNE && tmp->attacktype&AT_MAGIC) 02584 tmp->stats.Cha /= 4; 02585 done_one = 1; 02586 } 02587 /* detect monster */ 02588 if (QUERY_FLAG(spell, FLAG_MONSTER) 02589 && (QUERY_FLAG(tmp, FLAG_MONSTER) || (tmp->type == PLAYER && !QUERY_FLAG(tmp, FLAG_WIZ)))) { 02590 done_one = 2; 02591 if (!detect) 02592 detect = tmp; 02593 } 02594 /* Basically, if race is set in the spell, then the creatures race must 02595 * match that. if the spell race is set to GOD, then the gods opposing 02596 * race must match. 02597 */ 02598 if (spell->race 02599 && QUERY_FLAG(tmp, FLAG_MONSTER) 02600 && tmp->race 02601 && ((!strcmp(spell->race, "GOD") && god && god->slaying && strstr(god->slaying, tmp->race)) || (strstr(spell->race, tmp->race)))) { 02602 done_one = 2; 02603 if (!detect) 02604 detect = tmp; 02605 } 02606 if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) 02607 && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) 02608 && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))) { 02609 SET_FLAG(tmp, FLAG_KNOWN_CURSED); 02610 done_one = 1; 02611 } 02612 } /* for stack of objects on this space */ 02613 02614 /* Code here puts an effect of the spell on the space, so you can see 02615 * where the magic is. 02616 */ 02617 if (done_one) { 02618 object *detect_ob = arch_to_object(spell->other_arch); 02619 02620 detect_ob->x = nx; 02621 detect_ob->y = ny; 02622 /* if this is set, we want to copy the face */ 02623 if (done_one == 2 && detect) { 02624 detect_ob->face = detect->face; 02625 detect_ob->animation_id = detect->animation_id; 02626 detect_ob->anim_speed = detect->anim_speed; 02627 detect_ob->last_anim = 0; 02628 /* by default, the detect_ob is already animated */ 02629 if (!QUERY_FLAG(detect, FLAG_ANIMATE)) 02630 CLEAR_FLAG(detect_ob, FLAG_ANIMATE); 02631 } 02632 insert_ob_in_map(detect_ob, m, op, 0); 02633 } 02634 } /* for processing the surrounding spaces */ 02635 02636 02637 /* Now process objects in the players inventory if detect curse or magic */ 02638 if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) || QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL)) { 02639 done_one = 0; 02640 for (tmp = op->inv; tmp; tmp = tmp->below) { 02641 if (!tmp->invisible && !QUERY_FLAG(tmp, FLAG_IDENTIFIED)) { 02642 if (QUERY_FLAG(spell, FLAG_KNOWN_MAGICAL) 02643 && is_magical(tmp) 02644 && !QUERY_FLAG(tmp, FLAG_KNOWN_MAGICAL)) { 02645 SET_FLAG(tmp, FLAG_KNOWN_MAGICAL); 02646 if (op->type == PLAYER) 02647 esrv_send_item(op, tmp); 02648 } 02649 if (QUERY_FLAG(spell, FLAG_KNOWN_CURSED) 02650 && !QUERY_FLAG(tmp, FLAG_KNOWN_CURSED) 02651 && (QUERY_FLAG(tmp, FLAG_CURSED) || QUERY_FLAG(tmp, FLAG_DAMNED))) { 02652 SET_FLAG(tmp, FLAG_KNOWN_CURSED); 02653 if (op->type == PLAYER) 02654 esrv_send_item(op, tmp); 02655 } 02656 } /* if item is not identified */ 02657 } /* for the players inventory */ 02658 } /* if detect magic/curse and object is a player */ 02659 return 1; 02660 } 02661 02669 static void charge_mana_effect(object *victim, int caster_level) { 02670 /* Prevent explosions for objects without mana. Without this check, doors 02671 * will explode, too. 02672 */ 02673 if (victim->stats.maxsp <= 0) 02674 return; 02675 02676 draw_ext_info(NDI_UNIQUE, 0, victim, MSG_TYPE_SPELL, MSG_TYPE_SPELL_TARGET, 02677 "You feel energy course through you.", NULL); 02678 02679 if (victim->stats.sp >= victim->stats.maxsp*2) { 02680 object *tmp; 02681 02682 draw_ext_info(NDI_UNIQUE, 0, victim, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_SPELL, 02683 "Your head explodes!", NULL); 02684 02685 /* Explodes a fireball centered at player */ 02686 tmp = create_archetype(EXPLODING_FIREBALL); 02687 tmp->dam_modifier = random_roll(1, caster_level, victim, PREFER_LOW)/5+1; 02688 tmp->stats.maxhp = random_roll(1, caster_level, victim, PREFER_LOW)/10+2; 02689 tmp->x = victim->x; 02690 tmp->y = victim->y; 02691 insert_ob_in_map(tmp, victim->map, NULL, 0); 02692 victim->stats.sp = 2*victim->stats.maxsp; 02693 } else if (victim->stats.sp >= victim->stats.maxsp*1.88) { 02694 draw_ext_info(NDI_UNIQUE, NDI_ORANGE, victim, MSG_TYPE_SPELL, MSG_TYPE_SPELL_TARGET, 02695 "You feel like your head is going to explode.", NULL); 02696 } else if (victim->stats.sp >= victim->stats.maxsp*1.66) { 02697 draw_ext_info(NDI_UNIQUE, 0, victim, MSG_TYPE_SPELL, MSG_TYPE_SPELL_TARGET, 02698 "You get a splitting headache!", NULL); 02699 } else if (victim->stats.sp >= victim->stats.maxsp*1.5) { 02700 draw_ext_info(NDI_UNIQUE, 0, victim, MSG_TYPE_SPELL, MSG_TYPE_SPELL_TARGET, 02701 "Chaos fills your world.", NULL); 02702 confuse_living(victim, victim, 99); 02703 } else if (victim->stats.sp >= victim->stats.maxsp*1.25) { 02704 draw_ext_info(NDI_UNIQUE, 0, victim, MSG_TYPE_SPELL, MSG_TYPE_SPELL_TARGET, 02705 "You start hearing voices.", NULL); 02706 } 02707 } 02708 02726 int cast_transfer(object *op, object *caster, object *spell, int dir) { 02727 object *plyr = NULL; 02728 sint16 x, y; 02729 mapstruct *m; 02730 int mflags; 02731 02732 m = op->map; 02733 x = op->x+freearr_x[dir]; 02734 y = op->y+freearr_y[dir]; 02735 02736 mflags = get_map_flags(m, &m, x, y, &x, &y); 02737 if (!(mflags&P_OUT_OF_MAP) && mflags&P_IS_ALIVE) { 02738 for (plyr = GET_MAP_OB(m, x, y); plyr != NULL; plyr = plyr->above) 02739 if (plyr != op && QUERY_FLAG(plyr, FLAG_ALIVE)) 02740 break; 02741 } 02742 02743 /* If we did not find a player in the specified direction, transfer 02744 * to anyone on top of us. This is used for the rune of transference mostly. 02745 */ 02746 if (plyr == NULL) 02747 for (plyr = GET_MAP_OB(op->map, op->x, op->y); plyr != NULL; plyr = plyr->above) 02748 if (plyr != op && QUERY_FLAG(plyr, FLAG_ALIVE)) 02749 break; 02750 02751 if (!plyr) { 02752 draw_ext_info(NDI_BLACK, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02753 "There is no one there.", NULL); 02754 return 0; 02755 } 02756 02757 /* give sp */ 02758 if (spell->stats.dam > 0) { 02759 plyr->stats.sp += spell->stats.dam+SP_level_dam_adjust(caster, spell); 02760 charge_mana_effect(plyr, caster_level(caster, spell)); 02761 return 1; 02762 /* suck sp away. Can't suck sp from yourself */ 02763 } else if (op != plyr) { 02764 /* old dragin magic used floats. easier to just use ints and divide by 100 */ 02765 02766 int rate = -spell->stats.dam+SP_level_dam_adjust(caster, spell), sucked; 02767 02768 if (rate > 95) 02769 rate = 95; 02770 02771 sucked = (plyr->stats.sp*rate)/100; 02772 plyr->stats.sp -= sucked; 02773 if (QUERY_FLAG(op, FLAG_ALIVE)) { 02774 /* Player doesn't get full credit */ 02775 sucked = (sucked*rate)/100; 02776 op->stats.sp += sucked; 02777 if (sucked > 0) { 02778 charge_mana_effect(op, caster_level(caster, spell)); 02779 } 02780 } 02781 return 1; 02782 } 02783 return 0; 02784 } 02785 02795 void counterspell(object *op, int dir) { 02796 object *tmp, *head, *next; 02797 int mflags; 02798 mapstruct *m; 02799 sint16 sx, sy; 02800 02801 sx = op->x+freearr_x[dir]; 02802 sy = op->y+freearr_y[dir]; 02803 m = op->map; 02804 mflags = get_map_flags(m, &m, sx, sy, &sx, &sy); 02805 if (mflags&P_OUT_OF_MAP) 02806 return; 02807 02808 for (tmp = GET_MAP_OB(m, sx, sy); tmp != NULL; tmp = next) { 02809 next = tmp->above; 02810 02811 /* Need to look at the head object - otherwise, if tmp 02812 * points to a monster, we don't have all the necessary 02813 * info for it. 02814 */ 02815 if (tmp->head) 02816 head = tmp->head; 02817 else 02818 head = tmp; 02819 02820 /* don't attack our own spells */ 02821 if (tmp->owner && tmp->owner == op->owner) 02822 continue; 02823 02824 /* Basically, if the object is magical and not counterspell, 02825 * we will more or less remove the object. Don't counterspell 02826 * monsters either. 02827 */ 02828 02829 if (head->attacktype&AT_MAGIC 02830 && !(head->attacktype&AT_COUNTERSPELL) 02831 && !QUERY_FLAG(head, FLAG_MONSTER) 02832 && (op->level > head->level)) { 02833 remove_ob(head); 02834 free_object(head); 02835 } else switch (head->type) { 02836 case SPELL_EFFECT: 02837 if ((op->level > head->level) && !op->stats.food && !op->speed_left) { 02838 remove_ob(head); 02839 free_object(head); 02840 } 02841 break; 02842 02843 /* I really don't get this rune code that much - that 02844 * random chance seems really low. 02845 */ 02846 case RUNE: 02847 if (rndm(0, 149) == 0) { 02848 head->stats.hp--; /* weaken the rune */ 02849 if (!head->stats.hp) { 02850 remove_ob(head); 02851 free_object(head); 02852 } 02853 } 02854 break; 02855 } 02856 } 02857 } 02858 02873 int cast_consecrate(object *op, object *caster, object *spell) { 02874 char buf[MAX_BUF]; 02875 object *tmp; 02876 const object *god = find_god(determine_god(op)); 02877 02878 if (!god) { 02879 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 02880 "You can't consecrate anything if you don't worship a god!", NULL); 02881 return 0; 02882 } 02883 02884 for (tmp = op->below; tmp; tmp = tmp->below) { 02885 if (QUERY_FLAG(tmp, FLAG_IS_FLOOR)) 02886 break; 02887 if (tmp->type == HOLY_ALTAR) { 02888 if (tmp->level > caster_level(caster, spell)) { 02889 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02890 "You are not powerful enough to reconsecrate the %s", 02891 "You are not powerful enough to reconsecrate the %s", 02892 tmp->name); 02893 return 0; 02894 } else { 02895 /* If we got here, we are consecrating an altar */ 02896 object *new_altar; 02897 int letter; 02898 archetype *altar_arch; 02899 02900 snprintf(buf, MAX_BUF, "altar_"); 02901 letter = strlen(buf); 02902 strncpy(buf+letter, god->name, MAX_BUF-letter); 02903 for (; letter < strlen(buf); letter++) 02904 buf[letter] = tolower(buf[letter]); 02905 altar_arch = find_archetype(buf); 02906 if (!altar_arch) { 02907 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02908 "You fail to consecrate the altar.", NULL); 02909 LOG(llevError, "cast_consecrate: can't find altar %s for god %s\n", buf, god->name); 02910 return 0; 02911 } 02912 new_altar = arch_to_object(altar_arch); 02913 new_altar->x = tmp->x; 02914 new_altar->y = tmp->y; 02915 new_altar->level = tmp->level; 02916 insert_ob_in_map(new_altar, tmp->map, tmp, INS_BELOW_ORIGINATOR); 02917 remove_ob(tmp); 02918 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 02919 "You consecrated the altar to %s!", 02920 "You consecrated the altar to %s!", 02921 god->name); 02922 return 1; 02923 } 02924 } 02925 } 02926 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 02927 "You are not standing over an altar!", NULL); 02928 return 0; 02929 } 02930 02954 int animate_weapon(object *op, object *caster, object *spell, int dir) { 02955 object *weapon, *tmp; 02956 char buf[MAX_BUF]; 02957 int a, i; 02958 sint16 x, y; 02959 mapstruct *m; 02960 materialtype_t *mt; 02961 02962 if (!spell->other_arch) { 02963 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 02964 "Oops, program error!", NULL); 02965 LOG(llevError, "animate_weapon failed: spell %s missing other_arch!\n", spell->name); 02966 return 0; 02967 } 02968 02969 /* exit if it's not a player using this spell. */ 02970 if (op->type != PLAYER) 02971 return 0; 02972 02973 /* if player already has a golem, abort */ 02974 if (op->contr->ranges[range_golem] != NULL && op->contr->golem_count == op->contr->ranges[range_golem]->count) { 02975 control_golem(op->contr->ranges[range_golem], dir); 02976 return 0; 02977 } 02978 02979 /* if no direction specified, pick one */ 02980 if (!dir) 02981 dir = find_free_spot(NULL, op->map, op->x, op->y, 1, 9); 02982 02983 m = op->map; 02984 x = op->x+freearr_x[dir]; 02985 y = op->y+freearr_y[dir]; 02986 02987 /* if there's no place to put the golem, abort */ 02988 if ((dir == -1) 02989 || (get_map_flags(m, &m, x, y, &x, &y)&P_OUT_OF_MAP) 02990 || ((spell->other_arch->clone.move_type&GET_MAP_MOVE_BLOCK(m, x, y)) == spell->other_arch->clone.move_type)) { 02991 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 02992 "There is something in the way.", NULL); 02993 return 0; 02994 } 02995 02996 /* Use the weapon marked by the player. */ 02997 weapon = find_marked_object(op); 02998 02999 if (!weapon) { 03000 draw_ext_info(NDI_BLACK, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 03001 "You must mark a weapon to use with this spell!", NULL); 03002 return 0; 03003 } 03004 if (spell->race && strcmp(weapon->arch->name, spell->race)) { 03005 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 03006 "The spell fails to transform your weapon.", NULL); 03007 return 0; 03008 } 03009 if (weapon->type != WEAPON) { 03010 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 03011 "You need to mark a weapon to animate it.", NULL); 03012 return 0; 03013 } 03014 if (QUERY_FLAG(weapon, FLAG_UNPAID)) { 03015 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 03016 "You need to pay for the weapon to animate it.", NULL); 03017 return 0; 03018 } 03019 if (QUERY_FLAG(weapon, FLAG_APPLIED)) { 03020 char wn[MAX_BUF]; 03021 03022 query_name(weapon, wn, MAX_BUF); 03023 draw_ext_info_format(NDI_BLACK, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 03024 "You need to unequip %s before using it in this spell", 03025 "You need to unequip %s before using it in this spell", 03026 wn); 03027 return 0; 03028 } 03029 03030 if (weapon->nrof > 1) { 03031 tmp = get_split_ob(weapon, 1, NULL, 0); 03032 esrv_update_item(UPD_NROF, op, weapon); 03033 weapon = tmp; 03034 } 03035 03036 /* create the golem object */ 03037 tmp = arch_to_object(spell->other_arch); 03038 03039 /* if animated by a player, give the player control of the golem */ 03040 CLEAR_FLAG(tmp, FLAG_MONSTER); 03041 SET_FLAG(tmp, FLAG_FRIENDLY); 03042 tmp->stats.exp = 0; 03043 add_friendly_object(tmp); 03044 tmp->type = GOLEM; 03045 set_owner(tmp, op); 03046 set_spell_skill(op, caster, spell, tmp); 03047 op->contr->ranges[range_golem] = tmp; 03048 op->contr->shoottype = range_golem; 03049 op->contr->golem_count = tmp->count; 03050 03051 /* Give the weapon to the golem now. A bit of a hack to check the 03052 * removed flag - it should only be set if get_split_object was 03053 * used above. 03054 */ 03055 if (!QUERY_FLAG(weapon, FLAG_REMOVED)) 03056 remove_ob(weapon); 03057 insert_ob_in_ob(weapon, tmp); 03058 03059 /* To do everything necessary to let a golem use the weapon is a pain, 03060 * so instead, just set it as equipped (otherwise, we need to update 03061 * body_info, skills, etc) 03062 */ 03063 SET_FLAG(tmp, FLAG_USE_WEAPON); 03064 SET_FLAG(weapon, FLAG_APPLIED); 03065 fix_object(tmp); 03066 03067 /* There used to be 'odd' code that basically seemed to take the absolute 03068 * value of the weapon->magic an use that. IMO, that doesn't make sense - 03069 * if you're using a crappy weapon, it shouldn't be as good. 03070 */ 03071 03072 /* modify weapon's animated wc */ 03073 tmp->stats.wc = tmp->stats.wc 03074 -SP_level_range_adjust(caster, spell) 03075 -5*weapon->stats.Dex 03076 -2*weapon->stats.Str 03077 -weapon->magic; 03078 if (tmp->stats.wc < -127) 03079 tmp->stats.wc = -127; 03080 03081 /* Modify hit points for weapon */ 03082 tmp->stats.maxhp = tmp->stats.maxhp 03083 +spell->duration 03084 +SP_level_duration_adjust(caster, spell) 03085 +8*weapon->magic 03086 +12*weapon->stats.Con; 03087 if (tmp->stats.maxhp < 0) 03088 tmp->stats.maxhp = 10; 03089 tmp->stats.hp = tmp->stats.maxhp; 03090 03091 /* Modify weapon's damage */ 03092 tmp->stats.dam = spell->stats.dam 03093 +SP_level_dam_adjust(caster, spell) 03094 +weapon->stats.dam 03095 +weapon->magic 03096 +5*weapon->stats.Str; 03097 if (tmp->stats.dam < 0) 03098 tmp->stats.dam = 127; 03099 03100 /* attacktype */ 03101 if (!tmp->attacktype) 03102 tmp->attacktype = AT_PHYSICAL; 03103 03104 mt = NULL; 03105 if (op->materialname != NULL) 03106 mt = name_to_material(op->materialname); 03107 if (mt != NULL) { 03108 for (i = 0; i < NROFATTACKS; i++) 03109 tmp->resist[i] = 50-(mt->save[i]*5); 03110 a = mt->save[0]; 03111 } else { 03112 for (i = 0; i < NROFATTACKS; i++) 03113 tmp->resist[i] = 5; 03114 a = 10; 03115 } 03116 /* Set weapon's immunity */ 03117 tmp->resist[ATNR_CONFUSION] = 100; 03118 tmp->resist[ATNR_POISON] = 100; 03119 tmp->resist[ATNR_SLOW] = 100; 03120 tmp->resist[ATNR_PARALYZE] = 100; 03121 tmp->resist[ATNR_TURN_UNDEAD] = 100; 03122 tmp->resist[ATNR_FEAR] = 100; 03123 tmp->resist[ATNR_DEPLETE] = 100; 03124 tmp->resist[ATNR_DEATH] = 100; 03125 tmp->resist[ATNR_BLIND] = 100; 03126 03127 /* Improve weapon's armour value according to best save vs. physical of its material */ 03128 03129 if (a > 14) 03130 a = 14; 03131 tmp->resist[ATNR_PHYSICAL] = 100-(int)((100.0-(float)tmp->resist[ATNR_PHYSICAL])/(30.0-2.0*a)); 03132 03133 /* Determine golem's speed */ 03134 tmp->speed = 0.4+0.1*SP_level_range_adjust(caster, spell); 03135 03136 if (tmp->speed > 3.33) 03137 tmp->speed = 3.33; 03138 03139 if (!spell->race) { 03140 snprintf(buf, sizeof(buf), "animated %s", weapon->name); 03141 if (tmp->name) 03142 free_string(tmp->name); 03143 tmp->name = add_string(buf); 03144 03145 tmp->face = weapon->face; 03146 tmp->animation_id = weapon->animation_id; 03147 tmp->anim_speed = weapon->anim_speed; 03148 tmp->last_anim = weapon->last_anim; 03149 tmp->state = weapon->state; 03150 if (QUERY_FLAG(weapon, FLAG_ANIMATE)) { 03151 SET_FLAG(tmp, FLAG_ANIMATE); 03152 } else { 03153 CLEAR_FLAG(tmp, FLAG_ANIMATE); 03154 } 03155 update_ob_speed(tmp); 03156 } 03157 03158 /* make experience increase in proportion to the strength of the summoned creature. */ 03159 tmp->stats.exp *= 1+(MAX(spell->stats.maxgrace, spell->stats.sp)/caster_level(caster, spell)); 03160 03161 tmp->speed_left = -1; 03162 tmp->x = x; 03163 tmp->y = y; 03164 tmp->direction = dir; 03165 insert_ob_in_map(tmp, m, op, 0); 03166 return 1; 03167 } 03168 03183 int cast_change_map_lightlevel(object *op, object *caster, object *spell) { 03184 int success; 03185 03186 if (!op->map) 03187 return 0; /* shouldnt happen */ 03188 03189 success = change_map_light(op->map, spell->stats.dam); 03190 if (!success) { 03191 if (spell->stats.dam < 0) 03192 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 03193 "It can be no brighter here.", NULL); 03194 else 03195 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 03196 "It can be no darker here.", NULL); 03197 } 03198 return success; 03199 } 03200 03213 int create_aura(object *op, object *caster, object *spell) { 03214 int refresh = 0; 03215 object *new_aura; 03216 03217 new_aura = present_arch_in_ob(spell->other_arch, op); 03218 if (new_aura) 03219 refresh = 1; 03220 else 03221 new_aura = arch_to_object(spell->other_arch); 03222 03223 new_aura->duration = spell->duration+10*SP_level_duration_adjust(caster, spell); 03224 if (op->type == PLAYER) 03225 store_spell_expiry(new_aura); 03226 03227 new_aura->stats.dam = spell->stats.dam+SP_level_dam_adjust(caster, spell); 03228 03229 set_owner(new_aura, op); 03230 set_spell_skill(op, caster, spell, new_aura); 03231 new_aura->attacktype = spell->attacktype; 03232 03233 new_aura->level = caster_level(caster, spell); 03234 if (refresh) 03235 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 03236 "You recast the spell while in effect.", NULL); 03237 else 03238 insert_ob_in_ob(new_aura, op); 03239 return 1; 03240 } 03241 03257 int write_mark(object *op, object *spell, const char *msg) { 03258 char rune[HUGE_BUF]; 03259 object *tmp; 03260 03261 if (!msg || msg[0] == 0) { 03262 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 03263 "Write what?", NULL); 03264 return 0; 03265 } 03266 03267 if (strcasestr_local(msg, "endmsg")) { 03268 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, 03269 "Trying to cheat are we?", NULL); 03270 LOG(llevInfo, "write_rune: player %s tried to write bogus rune %s\n", op->name, msg); 03271 return 0; 03272 } 03273 03274 if (!spell->other_arch) 03275 return 0; 03276 tmp = arch_to_object(spell->other_arch); 03277 strncpy(rune, msg, HUGE_BUF-2); 03278 rune[HUGE_BUF-2] = 0; 03279 strcat(rune, "\n"); 03280 tmp->race = add_string(op->name); /*Save the owner of the rune*/ 03281 tmp->msg = add_string(rune); 03282 tmp->x = op->x; 03283 tmp->y = op->y; 03284 insert_ob_in_map(tmp, op->map, op, INS_BELOW_ORIGINATOR); 03285 return 1; 03286 }