Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_spell_attack_c = 00003 * "$Id: spell_attack.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 00040 #include <global.h> 00041 #include <object.h> 00042 #include <living.h> 00043 #ifndef __CEXTRACT__ 00044 #include <sproto.h> 00045 #endif 00046 #include <spells.h> 00047 #include <sounds.h> 00048 00049 /*************************************************************************** 00050 * 00051 * BOLT CODE 00052 * 00053 ***************************************************************************/ 00054 00078 int fire_bolt(object *op, object *caster, int dir, object *spob, object *skill) { 00079 object *tmp = NULL; 00080 int mflags; 00081 00082 if (!spob->other_arch) 00083 return 0; 00084 00085 tmp = arch_to_object(spob->other_arch); 00086 if (tmp == NULL) 00087 return 0; 00088 00089 /* peterm: level dependency for bolts */ 00090 tmp->stats.dam = spob->stats.dam+SP_level_dam_adjust(caster, spob); 00091 tmp->attacktype = spob->attacktype; 00092 if (spob->slaying) 00093 tmp->slaying = add_refcount(spob->slaying); 00094 tmp->range = spob->range+SP_level_range_adjust(caster, spob); 00095 tmp->duration = spob->duration+SP_level_duration_adjust(caster, spob); 00096 tmp->stats.Dex = spob->stats.Dex; 00097 tmp->stats.Con = spob->stats.Con; 00098 00099 tmp->direction = dir; 00100 if (QUERY_FLAG(tmp, FLAG_IS_TURNABLE)) 00101 SET_ANIMATION(tmp, dir); 00102 00103 set_owner(tmp, op); 00104 set_spell_skill(op, caster, spob, tmp); 00105 00106 tmp->x = op->x+DIRX(tmp); 00107 tmp->y = op->y+DIRY(tmp); 00108 tmp->map = op->map; 00109 00110 mflags = get_map_flags(tmp->map, &tmp->map, tmp->x, tmp->y, &tmp->x, &tmp->y); 00111 if (mflags&P_OUT_OF_MAP) { 00112 free_object(tmp); 00113 return 0; 00114 } 00115 if (OB_TYPE_MOVE_BLOCK(tmp, GET_MAP_MOVE_BLOCK(tmp->map, tmp->x, tmp->y))) { 00116 if (!QUERY_FLAG(tmp, FLAG_REFLECTING)) { 00117 free_object(tmp); 00118 return 0; 00119 } 00120 tmp->x = op->x; 00121 tmp->y = op->y; 00122 tmp->direction = absdir(tmp->direction+4); 00123 tmp->map = op->map; 00124 } 00125 if ((tmp = insert_ob_in_map(tmp, tmp->map, op, 0)) != NULL) 00126 ob_process(tmp); 00127 return 1; 00128 } 00129 00130 00131 00132 /*************************************************************************** 00133 * 00134 * BULLET/BALL CODE 00135 * 00136 ***************************************************************************/ 00137 00144 void explode_bullet(object *op) { 00145 tag_t op_tag = op->count; 00146 object *tmp, *owner; 00147 00148 if (op->other_arch == NULL) { 00149 LOG(llevError, "BUG: explode_bullet(): op without other_arch\n"); 00150 remove_ob(op); 00151 free_object(op); 00152 return; 00153 } 00154 00155 if (op->env) { 00156 object *env; 00157 00158 env = object_get_env_recursive(op); 00159 if (env->map == NULL || out_of_map(env->map, env->x, env->y)) { 00160 LOG(llevError, "BUG: explode_bullet(): env out of map\n"); 00161 remove_ob(op); 00162 free_object(op); 00163 return; 00164 } 00165 remove_ob(op); 00166 op->x = env->x; 00167 op->y = env->y; 00168 insert_ob_in_map(op, env->map, op, INS_NO_MERGE|INS_NO_WALK_ON); 00169 } else if (out_of_map(op->map, op->x, op->y)) { 00170 LOG(llevError, "BUG: explode_bullet(): op out of map\n"); 00171 remove_ob(op); 00172 free_object(op); 00173 return; 00174 } 00175 00176 if (op->attacktype) { 00177 hit_map(op, 0, op->attacktype, 1); 00178 if (was_destroyed(op, op_tag)) 00179 return; 00180 } 00181 00182 /* other_arch contains what this explodes into */ 00183 tmp = arch_to_object(op->other_arch); 00184 00185 copy_owner(tmp, op); 00186 if (tmp->skill) 00187 FREE_AND_CLEAR_STR(tmp->skill); 00188 if (op->skill) 00189 tmp->skill = add_refcount(op->skill); 00190 00191 owner = get_owner(op); 00192 if ((tmp->attacktype&AT_HOLYWORD || tmp->attacktype&AT_GODPOWER) && owner && !tailor_god_spell(tmp, owner)) { 00193 remove_ob(op); 00194 free_object(op); 00195 return; 00196 } 00197 tmp->x = op->x; 00198 tmp->y = op->y; 00199 00200 /* special for bombs - it actually has sane values for these */ 00201 if (op->type == SPELL_EFFECT && op->subtype == SP_BOMB) { 00202 tmp->attacktype = op->attacktype; 00203 tmp->range = op->range; 00204 tmp->stats.dam = op->stats.dam; 00205 tmp->duration = op->duration; 00206 } else { 00207 if (op->attacktype&AT_MAGIC) 00208 tmp->attacktype |= AT_MAGIC; 00209 /* Spell doc describes what is going on here */ 00210 tmp->stats.dam = op->dam_modifier; 00211 tmp->range = op->stats.maxhp; 00212 tmp->duration = op->stats.hp; 00213 } 00214 00215 /* Used for spell tracking - just need a unique val for this spell - 00216 * the count of the parent should work fine. 00217 */ 00218 tmp->stats.maxhp = op->count; 00219 00220 /* Set direction of cone explosion */ 00221 if (tmp->type == SPELL_EFFECT && tmp->subtype == SP_CONE) 00222 tmp->stats.sp = op->direction; 00223 00224 /* Prevent recursion */ 00225 op->move_on = 0; 00226 00227 insert_ob_in_map(tmp, op->map, op, 0); 00228 /* remove the firebullet */ 00229 if (!was_destroyed(op, op_tag)) { 00230 remove_ob(op); 00231 free_object(op); 00232 } 00233 } 00234 00242 void check_bullet(object *op) { 00243 tag_t op_tag = op->count, tmp_tag; 00244 object *tmp; 00245 int dam, mflags; 00246 mapstruct *m; 00247 sint16 sx, sy; 00248 00249 mflags = get_map_flags(op->map, &m, op->x, op->y, &sx, &sy); 00250 00251 if (!(mflags&P_IS_ALIVE) && !OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy))) 00252 return; 00253 00254 if (op->other_arch) { 00255 /* explode object will also remove op */ 00256 explode_bullet(op); 00257 return; 00258 } 00259 00260 /* If nothing alive on this space, no reason to do anything further */ 00261 if (!(mflags&P_IS_ALIVE)) 00262 return; 00263 00264 for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) { 00265 if (QUERY_FLAG(tmp, FLAG_ALIVE)) { 00266 tmp_tag = tmp->count; 00267 dam = hit_player(tmp, op->stats.dam, op, op->attacktype, 1); 00268 if (was_destroyed(op, op_tag) || !was_destroyed(tmp, tmp_tag) || (op->stats.dam -= dam) < 0) { 00269 if (!QUERY_FLAG(op, FLAG_REMOVED)) { 00270 remove_ob(op); 00271 free_object(op); 00272 return; 00273 } 00274 } 00275 } 00276 } 00277 } 00278 00279 00301 int fire_bullet(object *op, object *caster, int dir, object *spob) { 00302 object *tmp = NULL; 00303 int mflags; 00304 00305 if (!spob->other_arch) 00306 return 0; 00307 00308 tmp = arch_to_object(spob->other_arch); 00309 if (tmp == NULL) 00310 return 0; 00311 00312 /* peterm: level dependency for bolts */ 00313 tmp->stats.dam = spob->stats.dam+SP_level_dam_adjust(caster, spob); 00314 tmp->attacktype = spob->attacktype; 00315 if (spob->slaying) 00316 tmp->slaying = add_refcount(spob->slaying); 00317 00318 tmp->range = 50; 00319 00320 /* Need to store duration/range for the ball to use */ 00321 tmp->stats.hp = spob->duration+SP_level_duration_adjust(caster, spob); 00322 tmp->stats.maxhp = spob->range+SP_level_range_adjust(caster, spob); 00323 tmp->dam_modifier = spob->stats.food+SP_level_dam_adjust(caster, spob); 00324 00325 tmp->direction = dir; 00326 if (QUERY_FLAG(tmp, FLAG_IS_TURNABLE)) 00327 SET_ANIMATION(tmp, dir); 00328 00329 set_owner(tmp, op); 00330 set_spell_skill(op, caster, spob, tmp); 00331 00332 tmp->x = op->x+freearr_x[dir]; 00333 tmp->y = op->y+freearr_y[dir]; 00334 tmp->map = op->map; 00335 00336 mflags = get_map_flags(tmp->map, &tmp->map, tmp->x, tmp->y, &tmp->x, &tmp->y); 00337 if (mflags&P_OUT_OF_MAP) { 00338 free_object(tmp); 00339 return 0; 00340 } 00341 if (OB_TYPE_MOVE_BLOCK(tmp, GET_MAP_MOVE_BLOCK(tmp->map, tmp->x, tmp->y))) { 00342 if (!QUERY_FLAG(tmp, FLAG_REFLECTING)) { 00343 free_object(tmp); 00344 return 0; 00345 } 00346 tmp->x = op->x; 00347 tmp->y = op->y; 00348 tmp->direction = absdir(tmp->direction+4); 00349 tmp->map = op->map; 00350 } 00351 if ((tmp = insert_ob_in_map(tmp, tmp->map, op, 0)) != NULL) { 00352 check_bullet(tmp); 00353 } 00354 return 1; 00355 } 00356 00357 /***************************************************************************** 00358 * 00359 * CONE RELATED FUNCTIONS 00360 * 00361 *****************************************************************************/ 00362 00369 void cone_drop(object *op) { 00370 object *new_ob = arch_to_object(op->other_arch); 00371 00372 new_ob->x = op->x; 00373 new_ob->y = op->y; 00374 new_ob->level = op->level; 00375 set_owner(new_ob, op->owner); 00376 00377 /* preserve skill ownership */ 00378 if (op->skill && op->skill != new_ob->skill) { 00379 if (new_ob->skill) 00380 free_string(new_ob->skill); 00381 new_ob->skill = add_refcount(op->skill); 00382 } 00383 insert_ob_in_map(new_ob, op->map, op, 0); 00384 } 00385 00403 int cast_cone(object *op, object *caster, int dir, object *spell) { 00404 object *tmp; 00405 int i, success = 0, range_min = -1, range_max = 1; 00406 mapstruct *m; 00407 sint16 sx, sy; 00408 MoveType movetype; 00409 00410 if (!spell->other_arch) 00411 return 0; 00412 00413 if (op->type == PLAYER && QUERY_FLAG(op, FLAG_UNDEAD) && op->attacktype&AT_TURN_UNDEAD) { 00414 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, "Your undead nature prevents you from turning undead!", NULL); 00415 return 0; 00416 } 00417 00418 if (!dir) { 00419 range_min = 0; 00420 range_max = 8; 00421 } 00422 00423 /* Need to know what the movetype of the object we are about 00424 * to create is, so we can know if the space we are about to 00425 * insert it into is blocked. 00426 */ 00427 movetype = spell->other_arch->clone.move_type; 00428 00429 for (i = range_min; i <= range_max; i++) { 00430 sint16 x, y, d; 00431 00432 /* We can't use absdir here, because it never returns 00433 * 0. If this is a rune, we want to hit the person on top 00434 * of the trap (d==0). If it is not a rune, then we don't want 00435 * to hit that person. 00436 */ 00437 d = dir+i; 00438 while (d < 0) 00439 d += 8; 00440 while (d > 8) 00441 d -= 8; 00442 00443 /* If it's not a rune, we don't want to blast the caster. 00444 * In that case, we have to see - if dir is specified, 00445 * turn this into direction 8. If dir is not specified (all 00446 * direction) skip - otherwise, one line would do more damage 00447 * becase 0 direction will go through 9 directions - necessary 00448 * for the rune code. 00449 */ 00450 if (caster->type != RUNE && d == 0) { 00451 if (dir != 0) 00452 d = 8; 00453 else 00454 continue; 00455 } 00456 00457 x = op->x+freearr_x[d]; 00458 y = op->y+freearr_y[d]; 00459 00460 if (get_map_flags(op->map, &m, x, y, &sx, &sy)&P_OUT_OF_MAP) 00461 continue; 00462 00463 if ((movetype&GET_MAP_MOVE_BLOCK(m, sx, sy)) == movetype) 00464 continue; 00465 00466 success = 1; 00467 tmp = arch_to_object(spell->other_arch); 00468 set_owner(tmp, op); 00469 set_spell_skill(op, caster, spell, tmp); 00470 tmp->level = caster_level(caster, spell); 00471 tmp->x = sx; 00472 tmp->y = sy; 00473 tmp->attacktype = spell->attacktype; 00474 00475 /* holy word stuff */ 00476 if ((tmp->attacktype&AT_HOLYWORD) || (tmp->attacktype&AT_GODPOWER)) { 00477 if (!tailor_god_spell(tmp, op)) 00478 return 0; 00479 } 00480 00481 if (dir) 00482 tmp->stats.sp = dir; 00483 else 00484 tmp->stats.sp = i; 00485 00486 tmp->range = spell->range+SP_level_range_adjust(caster, spell); 00487 00488 /* If casting it in all directions, it doesn't go as far */ 00489 if (dir == 0) { 00490 tmp->range /= 4; 00491 if (tmp->range < 2 && spell->range >= 2) 00492 tmp->range = 2; 00493 } 00494 tmp->stats.dam = spell->stats.dam+SP_level_dam_adjust(caster, spell); 00495 tmp->duration = spell->duration+SP_level_duration_adjust(caster, spell); 00496 00497 /* Special bonus for fear attacks */ 00498 if (tmp->attacktype&AT_FEAR) { 00499 if (caster->type == PLAYER) 00500 tmp->duration += fear_bonus[caster->stats.Cha]; 00501 else 00502 tmp->duration += caster->level/3; 00503 } 00504 if (tmp->attacktype&(AT_HOLYWORD|AT_TURN_UNDEAD)) { 00505 if (caster->type == PLAYER) 00506 tmp->duration += turn_bonus[caster->stats.Wis]/5; 00507 else 00508 tmp->duration += caster->level/3; 00509 } 00510 00511 if (!(tmp->move_type&MOVE_FLY_LOW)) 00512 LOG(llevDebug, "cast_cone(): arch %s doesn't have flying 1\n", spell->other_arch->name); 00513 00514 if (!tmp->move_on && tmp->stats.dam) { 00515 LOG(llevDebug, "cast_cone(): arch %s doesn't have move_on set\n", spell->other_arch->name); 00516 } 00517 insert_ob_in_map(tmp, m, op, 0); 00518 00519 /* This is used for tracking spells so that one effect doesn't hit 00520 * a single space too many times. 00521 */ 00522 tmp->stats.maxhp = tmp->count; 00523 00524 if (tmp->other_arch) 00525 cone_drop(tmp); 00526 } 00527 return success; 00528 } 00529 00530 /**************************************************************************** 00531 * 00532 * BOMB related code 00533 * 00534 ****************************************************************************/ 00535 00552 int create_bomb(object *op, object *caster, int dir, object *spell) { 00553 object *tmp; 00554 int mflags; 00555 sint16 dx = op->x+freearr_x[dir], dy = op->y+freearr_y[dir]; 00556 mapstruct *m; 00557 00558 mflags = get_map_flags(op->map, &m, dx, dy, &dx, &dy); 00559 if ((mflags&P_OUT_OF_MAP) || (GET_MAP_MOVE_BLOCK(m, dx, dy)&MOVE_WALK)) { 00560 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, "There is something in the way.", NULL); 00561 return 0; 00562 } 00563 tmp = arch_to_object(spell->other_arch); 00564 00565 /* level dependencies for bomb */ 00566 tmp->range = spell->range+SP_level_range_adjust(caster, spell); 00567 tmp->stats.dam = spell->stats.dam+SP_level_dam_adjust(caster, spell); 00568 tmp->duration = spell->duration+SP_level_duration_adjust(caster, spell); 00569 tmp->attacktype = spell->attacktype; 00570 00571 set_owner(tmp, op); 00572 set_spell_skill(op, caster, spell, tmp); 00573 tmp->x = dx; 00574 tmp->y = dy; 00575 insert_ob_in_map(tmp, m, op, 0); 00576 return 1; 00577 } 00578 00579 /**************************************************************************** 00580 * 00581 * smite related spell code. 00582 * 00583 ****************************************************************************/ 00584 00603 static object *get_pointed_target(object *op, int dir, int range, int type) { 00604 object *target; 00605 sint16 x, y; 00606 int dist, mflags; 00607 mapstruct *mp; 00608 00609 if (dir == 0) 00610 return NULL; 00611 00612 for (dist = 1; dist < range; dist++) { 00613 x = op->x+freearr_x[dir]*dist; 00614 y = op->y+freearr_y[dir]*dist; 00615 mp = op->map; 00616 mflags = get_map_flags(op->map, &mp, x, y, &x, &y); 00617 00618 if (mflags&P_OUT_OF_MAP) 00619 return NULL; 00620 if ((type&SPELL_MANA) && (mflags&P_NO_MAGIC)) 00621 return NULL; 00622 if ((type&SPELL_GRACE) && (mflags&P_NO_CLERIC)) 00623 return NULL; 00624 if (GET_MAP_MOVE_BLOCK(mp, x, y)&MOVE_FLY_LOW) 00625 return NULL; 00626 00627 if (mflags&P_IS_ALIVE) { 00628 for (target = GET_MAP_OB(mp, x, y); target; target = target->above) { 00629 if (QUERY_FLAG(target->head ? target->head : target, FLAG_MONSTER)) { 00630 return target; 00631 } 00632 } 00633 } 00634 } 00635 return NULL; 00636 } 00637 00638 00655 int cast_smite_spell(object *op, object *caster, int dir, object *spell) { 00656 object *effect, *target; 00657 const object *god = find_god(determine_god(op)); 00658 int range; 00659 00660 /* BUG? The value in range doesn't seem to be used. */ 00661 range = spell->range+SP_level_range_adjust(caster, spell); 00662 target = get_pointed_target(op, dir, 50, spell->stats.grace ? SPELL_GRACE : SPELL_MANA); 00663 00664 /* Bunch of conditions for casting this spell. Note that only 00665 * require a god if this is a cleric spell (requires grace). 00666 * This makes this spell much more general purpose - it can be used 00667 * by wizards also, which is good, because I think this is a very 00668 * interesting spell. 00669 * if it is a cleric spell, you need a god, and the creature 00670 * can't be friendly to your god. 00671 */ 00672 00673 if (!target 00674 || QUERY_FLAG(target, FLAG_REFL_SPELL) 00675 || (!god && spell->stats.grace) 00676 || (target->title && god && !strcmp(target->title, god->name)) 00677 || (target->race && god && strstr(target->race, god->race))) { 00678 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "Your request is unheeded.", NULL); 00679 return 0; 00680 } 00681 00682 if (spell->other_arch) 00683 effect = arch_to_object(spell->other_arch); 00684 else 00685 return 0; 00686 00687 /* tailor the effect by priest level and worshipped God */ 00688 effect->level = caster_level(caster, spell); 00689 effect->attacktype = spell->attacktype; 00690 if (effect->attacktype&(AT_HOLYWORD|AT_GODPOWER)) { 00691 if (tailor_god_spell(effect, op)) 00692 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00693 "%s answers your call!", 00694 "%s answers your call!", 00695 determine_god(op)); 00696 else { 00697 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "Your request is ignored.", NULL); 00698 return 0; 00699 } 00700 } 00701 00702 /* size of the area of destruction */ 00703 effect->range = spell->range+SP_level_range_adjust(caster, spell); 00704 effect->duration = spell->duration+SP_level_range_adjust(caster, spell); 00705 00706 if (effect->attacktype&AT_DEATH) { 00707 effect->level = spell->stats.dam+SP_level_dam_adjust(caster, spell); 00708 00709 /* casting death spells at undead isn't a good thing */ 00710 if QUERY_FLAG(target, FLAG_UNDEAD) { 00711 if (random_roll(0, 2, op, PREFER_LOW)) { 00712 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "Idiot! Your spell boomerangs!", NULL); 00713 effect->x = op->x; 00714 effect->y = op->y; 00715 } else { 00716 char target_name[HUGE_BUF]; 00717 00718 query_name(target, target_name, HUGE_BUF); 00719 draw_ext_info_format(NDI_UNIQUE, 0, op, 00720 MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00721 "The %s looks stronger!", 00722 "The %s looks stronger!", 00723 target_name); 00724 target->stats.hp = target->stats.maxhp*2; 00725 free_object(effect); 00726 return 0; 00727 } 00728 } 00729 } else { 00730 /* how much woe to inflict :) */ 00731 effect->stats.dam = spell->stats.dam+SP_level_dam_adjust(caster, spell); 00732 } 00733 00734 if (effect->type == SPELL_EFFECT && effect->subtype == SP_EXPLOSION) { 00735 /* Used for spell tracking - just need a unique val for this spell - 00736 * the count of the parent should work fine. 00737 * 00738 * Without this the server can easily get overloaded at high level 00739 * spells. 00740 */ 00741 effect->stats.maxhp = spell->count; 00742 } 00743 00744 set_owner(effect, op); 00745 set_spell_skill(op, caster, spell, effect); 00746 00747 /* ok, tell it where to be, and insert! */ 00748 effect->x = target->x; 00749 effect->y = target->y; 00750 insert_ob_in_map(effect, target->map, op, 0); 00751 00752 return 1; 00753 } 00754 00755 /**************************************************************************** 00756 * Destruction 00757 ****************************************************************************/ 00758 00777 static int make_object_glow(object *op, int radius, int time) { 00778 object *tmp; 00779 00780 /* some things are unaffected... */ 00781 if (op->path_denied&PATH_LIGHT) 00782 return 0; 00783 00784 tmp = create_archetype(FORCE_NAME); 00785 tmp->speed = 0.01; 00786 tmp->stats.food = time; 00787 SET_FLAG(tmp, FLAG_IS_USED_UP); 00788 tmp->glow_radius = radius; 00789 if (tmp->glow_radius > MAX_LIGHT_RADII) 00790 tmp->glow_radius = MAX_LIGHT_RADII; 00791 00792 tmp->x = op->x; 00793 tmp->y = op->y; 00794 if (tmp->speed < MIN_ACTIVE_SPEED) 00795 tmp->speed = MIN_ACTIVE_SPEED; /* safety */ 00796 tmp = insert_ob_in_ob(tmp, op); 00797 if (tmp->glow_radius > op->glow_radius) 00798 op->glow_radius = tmp->glow_radius; 00799 00800 if (!tmp->env || op != tmp->env) { 00801 LOG(llevError, "make_object_glow() failed to insert glowing force in %s\n", op->name); 00802 return 0; 00803 } 00804 return 1; 00805 } 00806 00819 int cast_destruction(object *op, object *caster, object *spell_ob) { 00820 int i, j, range, mflags, friendly = 0, dam, dur; 00821 sint16 sx, sy; 00822 mapstruct *m; 00823 object *tmp; 00824 const char *skill; 00825 00826 range = spell_ob->range+SP_level_range_adjust(caster, spell_ob); 00827 dam = spell_ob->stats.dam+SP_level_dam_adjust(caster, spell_ob); 00828 dur = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob); 00829 if (QUERY_FLAG(op, FLAG_FRIENDLY) || op->type == PLAYER) 00830 friendly = 1; 00831 00832 /* destruction doesn't use another spell object, so we need 00833 * update op's skill pointer so that exp is properly awarded. 00834 * We do some shortcuts here - since this is just temporary 00835 * and we'll reset the values back, we don't need to go through 00836 * the full share string/free_string route. 00837 */ 00838 skill = op->skill; 00839 if (caster == op) 00840 op->skill = spell_ob->skill; 00841 else if (caster->skill) 00842 op->skill = caster->skill; 00843 else 00844 op->skill = NULL; 00845 00846 change_skill(op, find_skill_by_name(op, op->skill), 1); 00847 00848 for (i = -range; i < range; i++) { 00849 for (j = -range; j < range; j++) { 00850 m = op->map; 00851 sx = op->x+i; 00852 sy = op->y+j; 00853 mflags = get_map_flags(m, &m, sx, sy, &sx, &sy); 00854 if (mflags&P_OUT_OF_MAP) 00855 continue; 00856 if (mflags&P_IS_ALIVE) { 00857 for (tmp = GET_MAP_OB(m, sx, sy); tmp; tmp = tmp->above) { 00858 if (QUERY_FLAG(tmp, FLAG_ALIVE) || tmp->type == PLAYER) 00859 break; 00860 } 00861 if (tmp) { 00862 if (tmp->head) 00863 tmp = tmp->head; 00864 00865 if ((friendly && !QUERY_FLAG(tmp, FLAG_FRIENDLY) && tmp->type != PLAYER) 00866 || (!friendly && (QUERY_FLAG(tmp, FLAG_FRIENDLY) || tmp->type == PLAYER))) { 00867 if (spell_ob->subtype == SP_DESTRUCTION) { 00868 hit_player(tmp, dam, op, spell_ob->attacktype, 0); 00869 if (spell_ob->other_arch) { 00870 tmp = arch_to_object(spell_ob->other_arch); 00871 tmp->x = sx; 00872 tmp->y = sy; 00873 insert_ob_in_map(tmp, m, op, 0); 00874 } 00875 } else if (spell_ob->subtype == SP_FAERY_FIRE && tmp->resist[ATNR_MAGIC] != 100) { 00876 if (make_object_glow(tmp, 1, dur) && spell_ob->other_arch) { 00877 object *effect = arch_to_object(spell_ob->other_arch); 00878 effect->x = sx; 00879 effect->y = sy; 00880 insert_ob_in_map(effect, m, op, 0); 00881 } 00882 } 00883 } 00884 } 00885 } 00886 } 00887 } 00888 op->skill = skill; 00889 return 1; 00890 } 00891 00892 /*************************************************************************** 00893 * 00894 * CURSE 00895 * 00896 ***************************************************************************/ 00897 00914 int cast_curse(object *op, object *caster, object *spell_ob, int dir) { 00915 const object *god = find_god(determine_god(op)); 00916 object *tmp, *force; 00917 00918 tmp = get_pointed_target(op, (dir == 0) ? op->direction : dir, spell_ob->range, SPELL_GRACE); 00919 if (!tmp) { 00920 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "There is no one in that direction to curse.", NULL); 00921 return 0; 00922 } 00923 00924 /* If we've already got a force of this type, don't add a new one. */ 00925 for (force = tmp->inv; force != NULL; force = force->below) { 00926 if (force->type == FORCE && force->subtype == FORCE_CHANGE_ABILITY) { 00927 if (force->name == spell_ob->name) { 00928 break; 00929 } else if (spell_ob->race && spell_ob->race == force->name) { 00930 draw_ext_info_format(NDI_UNIQUE, 0, op, 00931 MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, 00932 "You can not cast %s while %s is in effect", 00933 "You can not cast %s while %s is in effect", 00934 spell_ob->name, force->name_pl); 00935 return 0; 00936 } 00937 } 00938 } 00939 00940 if (force == NULL) { 00941 force = create_archetype(FORCE_NAME); 00942 force->subtype = FORCE_CHANGE_ABILITY; 00943 free_string(force->name); 00944 if (spell_ob->race) 00945 force->name = add_refcount(spell_ob->race); 00946 else 00947 force->name = add_refcount(spell_ob->name); 00948 free_string(force->name_pl); 00949 force->name_pl = add_refcount(spell_ob->name); 00950 } else { 00951 int duration; 00952 00953 duration = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob)*50; 00954 if (duration > force->duration) { 00955 force->duration = duration; 00956 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, "You recast the spell while in effect.", NULL); 00957 } else { 00958 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "Recasting the spell had no effect.", NULL); 00959 } 00960 return 1; 00961 } 00962 force->duration = spell_ob->duration+SP_level_duration_adjust(caster, spell_ob)*50; 00963 force->speed = 1.0; 00964 force->speed_left = -1.0; 00965 SET_FLAG(force, FLAG_APPLIED); 00966 00967 if (god) { 00968 if (spell_ob->last_grace) 00969 force->path_repelled = god->path_repelled; 00970 if (spell_ob->last_grace) 00971 force->path_denied = god->path_denied; 00972 draw_ext_info_format(NDI_UNIQUE, 0, tmp, MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_SPELL, 00973 "You are a victim of %s's curse!", 00974 "You are a victim of %s's curse!", 00975 god->name); 00976 } else 00977 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "Your curse seems empty.", NULL); 00978 00979 00980 if (tmp != op && op->type == PLAYER) 00981 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, 00982 "You curse %s!", 00983 "You curse %s!", 00984 tmp->name); 00985 00986 force->stats.ac = spell_ob->stats.ac; 00987 force->stats.wc = spell_ob->stats.wc; 00988 00989 change_abil(tmp, force); /* Mostly to display any messages */ 00990 insert_ob_in_ob(force, tmp); 00991 fix_object(tmp); 00992 return 1; 00993 } 00994 00995 /********************************************************************** 00996 * mood change 00997 * Arguably, this may or may not be an attack spell. But since it 00998 * effects monsters, it seems best to put it into this file 00999 ***********************************************************************/ 01000 01014 int mood_change(object *op, object *caster, object *spell) { 01015 object *tmp, *head; 01016 const object *god; 01017 int done_one, range, mflags, level, at, best_at; 01018 sint16 x, y, nx, ny; 01019 mapstruct *m; 01020 const char *race; 01021 01022 /* We precompute some values here so that we don't have to keep 01023 * doing it over and over again. 01024 */ 01025 god = find_god(determine_god(op)); 01026 level = caster_level(caster, spell); 01027 range = spell->range+SP_level_range_adjust(caster, spell); 01028 01029 /* On the bright side, no monster should ever have a race of GOD_... 01030 * so even if the player doesn't worship a god, if race=GOD_.., it 01031 * won't ever match anything. 01032 */ 01033 if (!spell->race) 01034 race = NULL; 01035 else if (god && !strcmp(spell->race, "GOD_SLAYING")) 01036 race = god->slaying; 01037 else if (god && !strcmp(spell->race, "GOD_FRIEND")) 01038 race = god->race; 01039 else 01040 race = spell->race; 01041 01042 for (x = op->x-range; x <= op->x+range; x++) 01043 for (y = op->y-range; y <= op->y+range; y++) { 01044 done_one = 0; 01045 m = op->map; 01046 nx = x; 01047 ny = y; 01048 mflags = get_map_flags(m, &m, x, y, &nx, &ny); 01049 if (mflags&P_OUT_OF_MAP) 01050 continue; 01051 01052 /* If there is nothing living on this space, no need to go further */ 01053 if (!(mflags&P_IS_ALIVE)) 01054 continue; 01055 01056 for (tmp = GET_MAP_OB(m, nx, ny); tmp; tmp = tmp->above) 01057 if (QUERY_FLAG(tmp, FLAG_MONSTER)) 01058 break; 01059 01060 /* There can be living objects that are not monsters */ 01061 if (!tmp || tmp->type == PLAYER) 01062 continue; 01063 01064 /* Only the head has meaningful data, so resolve to that */ 01065 if (tmp->head) 01066 head = tmp->head; 01067 else 01068 head = tmp; 01069 01070 /* Make sure the race is OK. Likewise, only effect undead if spell specifically allows it */ 01071 if (race && head->race && !strstr(race, head->race)) 01072 continue; 01073 if (QUERY_FLAG(head, FLAG_UNDEAD) && !QUERY_FLAG(spell, FLAG_UNDEAD)) 01074 continue; 01075 01076 /* Now do a bunch of stuff related to saving throws */ 01077 best_at = -1; 01078 if (spell->attacktype) { 01079 for (at = 0; at < NROFATTACKS; at++) 01080 if (spell->attacktype&(1<<at)) 01081 if (best_at == -1 || head->resist[at] > head->resist[best_at]) 01082 best_at = at; 01083 01084 if (best_at == -1) 01085 at = 0; 01086 else { 01087 if (head->resist[best_at] == 100) 01088 continue; 01089 else 01090 at = head->resist[best_at]/5; 01091 } 01092 at -= level/5; 01093 if (did_make_save(head, head->level, at)) 01094 continue; 01095 } else { /* spell->attacktype */ 01096 /* 01097 * Spell has no attacktype (charm&such), so we'll have a specific saving: 01098 * if spell level < monster level, no go 01099 * else, chance of effect = 20+min(50, 2*(spell level-monster level)) 01100 * 01101 * The chance will then be in the range [20-70] percent, not too bad. 01102 * 01103 * This is required to fix the 'charm monster' abuse, where a player level 1 can 01104 * charm a level 125 monster... 01105 * 01106 * Ryo, august 14th 01107 */ 01108 if (head->level > level) 01109 continue; 01110 if (random_roll(0, 100, caster, PREFER_LOW) >= (20+MIN(50, 2*(level-head->level)))) 01111 /* Failed, no effect */ 01112 continue; 01113 } 01114 01115 /* Done with saving throw. Now start effecting the monster */ 01116 01117 /* aggravation */ 01118 if (QUERY_FLAG(spell, FLAG_MONSTER)) { 01119 CLEAR_FLAG(head, FLAG_SLEEP); 01120 if (QUERY_FLAG(head, FLAG_FRIENDLY)) 01121 remove_friendly_object(head); 01122 01123 done_one = 1; 01124 head->enemy = op; 01125 } 01126 01127 /* calm monsters */ 01128 if (QUERY_FLAG(spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG(head, FLAG_UNAGGRESSIVE)) { 01129 SET_FLAG(head, FLAG_UNAGGRESSIVE); 01130 head->enemy = NULL; 01131 done_one = 1; 01132 } 01133 01134 /* berserk monsters */ 01135 if (QUERY_FLAG(spell, FLAG_BERSERK) && !QUERY_FLAG(head, FLAG_BERSERK)) { 01136 SET_FLAG(head, FLAG_BERSERK); 01137 done_one = 1; 01138 } 01139 /* charm */ 01140 if (QUERY_FLAG(spell, FLAG_NO_ATTACK) && !QUERY_FLAG(head, FLAG_FRIENDLY)) { 01141 SET_FLAG(head, FLAG_FRIENDLY); 01142 /* Prevent uncontolled outbreaks of self replicating monsters. 01143 Typical use case is charm, go somwhere, use aggravation to make hostile. 01144 This could lead to fun stuff like mice outbreak in bigworld and server crawl. */ 01145 CLEAR_FLAG(head, FLAG_GENERATOR); 01146 set_owner(head, op); 01147 set_spell_skill(op, caster, spell, head); 01148 add_friendly_object(head); 01149 head->attack_movement = PETMOVE; 01150 done_one = 1; 01151 share_exp(op, head->stats.exp/2, head->skill, SK_EXP_ADD_SKILL); 01152 head->stats.exp = 0; 01153 } 01154 01155 /* If a monster was effected, put an effect in */ 01156 if (done_one && spell->other_arch) { 01157 tmp = arch_to_object(spell->other_arch); 01158 tmp->x = nx; 01159 tmp->y = ny; 01160 insert_ob_in_map(tmp, m, op, 0); 01161 } 01162 } /* for y */ 01163 01164 return 1; 01165 } 01166 01167 01185 int fire_swarm(object *op, object *caster, object *spell, int dir) { 01186 object *tmp; 01187 int i; 01188 01189 if (!spell->other_arch) 01190 return 0; 01191 01192 tmp = create_archetype(SWARM_SPELL); 01193 tmp->x = op->x; 01194 tmp->y = op->y; 01195 set_owner(tmp, op); /* needed so that if swarm elements kill, caster gets xp.*/ 01196 set_spell_skill(op, caster, spell, tmp); 01197 01198 tmp->level = caster_level(caster, spell); /*needed later, to get level dep. right.*/ 01199 tmp->spell = arch_to_object(spell->other_arch); 01200 01201 tmp->attacktype = tmp->spell->attacktype; 01202 01203 if (tmp->attacktype&AT_HOLYWORD || tmp->attacktype&AT_GODPOWER) { 01204 if (!tailor_god_spell(tmp, op)) 01205 return 1; 01206 } 01207 tmp->duration = SP_level_duration_adjust(caster, spell); 01208 for (i = 0; i < spell->duration; i++) 01209 tmp->duration += die_roll(1, 3, op, PREFER_HIGH); 01210 01211 tmp->direction = dir; 01212 tmp->invisible = 1; 01213 insert_ob_in_map(tmp, op->map, op, 0); 01214 return 1; 01215 } 01216 01235 int cast_light(object *op, object *caster, object *spell, int dir) { 01236 object *target = NULL, *tmp = NULL; 01237 sint16 x, y; 01238 int dam, mflags; 01239 mapstruct *m; 01240 01241 dam = spell->stats.dam+SP_level_dam_adjust(caster, spell); 01242 01243 if (!dir) { 01244 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, "In what direction?", NULL); 01245 return 0; 01246 } 01247 01248 x = op->x+freearr_x[dir]; 01249 y = op->y+freearr_y[dir]; 01250 m = op->map; 01251 01252 mflags = get_map_flags(m, &m, x, y, &x, &y); 01253 01254 if (mflags&P_OUT_OF_MAP) { 01255 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "Nothing is there.", NULL); 01256 return 0; 01257 } 01258 01259 if (mflags&P_IS_ALIVE && spell->attacktype) { 01260 for (target = GET_MAP_OB(m, x, y); target; target = target->above) 01261 if (QUERY_FLAG(target, FLAG_MONSTER)) { 01262 /* oky doky. got a target monster. Lets make a blinding attack */ 01263 if (target->head) 01264 target = target->head; 01265 (void)hit_player(target, dam, op, spell->attacktype, 1); 01266 return 1; /* one success only! */ 01267 } 01268 } 01269 01270 /* no live target, perhaps a wall is in the way? */ 01271 if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y))) { 01272 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR, "Something is in the way.", NULL); 01273 return 0; 01274 } 01275 01276 /* ok, looks groovy to just insert a new light on the map */ 01277 tmp = arch_to_object(spell->other_arch); 01278 if (!tmp) { 01279 LOG(llevError, "Error: spell arch for cast_light() missing.\n"); 01280 return 0; 01281 } 01282 tmp->stats.food = spell->duration+SP_level_duration_adjust(caster, spell); 01283 if (tmp->glow_radius) { 01284 tmp->glow_radius = spell->range+SP_level_range_adjust(caster, spell); 01285 if (tmp->glow_radius > MAX_LIGHT_RADII) 01286 tmp->glow_radius = MAX_LIGHT_RADII; 01287 } 01288 tmp->x = x; 01289 tmp->y = y; 01290 insert_ob_in_map(tmp, m, op, 0); 01291 return 1; 01292 } 01293 01310 int cast_cause_disease(object *op, object *caster, object *spell, int dir) { 01311 sint16 x, y; 01312 int i, mflags, range, dam_mod, dur_mod; 01313 object *walk, *target_head; 01314 mapstruct *m; 01315 01316 x = op->x; 01317 y = op->y; 01318 01319 /* If casting from a scroll, no direction will be available, so refer to the 01320 * direction the player is pointing. 01321 */ 01322 if (!dir) 01323 dir = op->facing; 01324 if (!dir) 01325 return 0; /* won't find anything if casting on ourself, so just return */ 01326 01327 /* Calculate these once here */ 01328 range = spell->range+SP_level_range_adjust(caster, spell); 01329 dam_mod = SP_level_dam_adjust(caster, spell); 01330 dur_mod = SP_level_duration_adjust(caster, spell); 01331 01332 /* search in a line for a victim */ 01333 for (i = 1; i < range; i++) { 01334 x = op->x+i*freearr_x[dir]; 01335 y = op->y+i*freearr_y[dir]; 01336 m = op->map; 01337 01338 mflags = get_map_flags(m, &m, x, y, &x, &y); 01339 01340 if (mflags&P_OUT_OF_MAP) 01341 return 0; 01342 01343 /* don't go through walls - presume diseases are airborne */ 01344 if (GET_MAP_MOVE_BLOCK(m, x, y)&MOVE_FLY_LOW) 01345 return 0; 01346 01347 /* Only bother looking on this space if there is something living here */ 01348 if (mflags&P_IS_ALIVE) { 01349 /* search this square for a victim */ 01350 for (walk = GET_MAP_OB(m, x, y); walk; walk = walk->above) { 01351 /* Flags for monster is set on head only, so get it now */ 01352 target_head = walk; 01353 while (target_head->head) 01354 target_head = target_head->head; 01355 if (QUERY_FLAG(target_head, FLAG_MONSTER) || (target_head->type == PLAYER)) { /* found a victim */ 01356 object *disease = arch_to_object(spell->other_arch); 01357 01358 set_owner(disease, op); 01359 set_spell_skill(op, caster, spell, disease); 01360 disease->stats.exp = 0; 01361 disease->level = caster_level(caster, spell); 01362 01363 /* do level adjustments */ 01364 if (disease->stats.wc) 01365 disease->stats.wc += dur_mod/2; 01366 01367 if (disease->magic > 0) 01368 disease->magic += dur_mod/4; 01369 01370 if (disease->stats.maxhp > 0) 01371 disease->stats.maxhp += dur_mod; 01372 01373 if (disease->stats.maxgrace > 0) 01374 disease->stats.maxgrace += dur_mod; 01375 01376 if (disease->stats.dam) { 01377 if (disease->stats.dam > 0) 01378 disease->stats.dam += dam_mod; 01379 else 01380 disease->stats.dam -= dam_mod; 01381 } 01382 01383 if (disease->last_sp) { 01384 disease->last_sp -= 2*dam_mod; 01385 if (disease->last_sp < 1) 01386 disease->last_sp = 1; 01387 } 01388 01389 if (disease->stats.maxsp) { 01390 if (disease->stats.maxsp > 0) 01391 disease->stats.maxsp += dam_mod; 01392 else 01393 disease->stats.maxsp -= dam_mod; 01394 } 01395 01396 if (disease->stats.ac) 01397 disease->stats.ac += dam_mod; 01398 01399 if (disease->last_eat) 01400 disease->last_eat -= dam_mod; 01401 01402 if (disease->stats.hp) 01403 disease->stats.hp -= dam_mod; 01404 01405 if (disease->stats.sp) 01406 disease->stats.sp -= dam_mod; 01407 01408 if (infect_object(target_head, disease, 1)) { 01409 object *flash; /* visual effect for inflicting disease */ 01410 01411 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_SUCCESS, "You inflict %s on %s!", NULL, disease->name, target_head->name); 01412 01413 free_object(disease); /* don't need this one anymore */ 01414 flash = create_archetype(ARCH_DETECT_MAGIC); 01415 flash->x = x; 01416 flash->y = y; 01417 flash->map = walk->map; 01418 insert_ob_in_map(flash, walk->map, op, 0); 01419 return 1; 01420 } 01421 free_object(disease); 01422 } /* Found a victim */ 01423 } /* Search squares for living creature */ 01424 } /* if living creature on square */ 01425 } /* for range of spaces */ 01426 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE, "No one caught anything!", NULL); 01427 return 1; 01428 }