Crossfire Server, Branch 1.12  R12190
spell_attack.c
Go to the documentation of this file.
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 }