Crossfire Server, Branch 1.12  R12190
spell_effect.c
Go to the documentation of this file.
00001 /*
00002     CrossFire, A Multiplayer game for X-windows
00003 
00004     Copyright (C) 2007 Mark Wedel & Crossfire Development Team
00005     Copyright (C) 1992 Frank Tore Johansen
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00020 
00021     The authors can be reached via e-mail at crossfire-devel@real-time.com
00022 */
00023 
00028 #include <global.h>
00029 #include <ob_methods.h>
00030 #include <ob_types.h>
00031 #include <sounds.h>
00032 #include <sproto.h>
00033 
00034 static method_ret spell_effect_type_move_on(ob_methods *context, object *trap, object *victim, object *originator);
00035 static method_ret spell_effect_type_process(ob_methods *context, object *op);
00036 
00037 static void move_bolt(object *op);
00038 static void move_bullet(object *op);
00039 static void explosion(object *op);
00040 static void move_cone(object *op);
00041 static void animate_bomb(object *op);
00042 static void move_missile(object *op);
00043 static void execute_word_of_recall(object *op);
00044 static void move_ball_spell(object *op);
00045 static void move_swarm_spell(object *op);
00046 static void move_aura(object *aura);
00047 
00048 static void forklightning(object *op, object *tmp);
00049 static void check_spell_knockback(object *op);
00050 
00054 void init_type_spell_effect(void) {
00055     register_move_on(SPELL_EFFECT, spell_effect_type_move_on);
00056     register_process(SPELL_EFFECT, spell_effect_type_process);
00057 }
00058 
00067 static method_ret spell_effect_type_move_on(ob_methods *context, object *trap, object *victim, object *originator) {
00068     if (common_pre_ob_move_on(trap, victim, originator) == METHOD_ERROR)
00069         return METHOD_OK;
00070 
00071     switch (trap->subtype) {
00072     case SP_CONE:
00073         if (QUERY_FLAG(victim, FLAG_ALIVE)
00074         && trap->speed
00075         && trap->attacktype)
00076             hit_player(victim, trap->stats.dam, trap, trap->attacktype, 0);
00077         break;
00078 
00079     case SP_MAGIC_MISSILE:
00080         if (QUERY_FLAG(victim, FLAG_ALIVE)) {
00081             tag_t spell_tag = trap->count;
00082 
00083             hit_player(victim, trap->stats.dam, trap, trap->attacktype, 1);
00084             if (!was_destroyed(trap, spell_tag)) {
00085                 remove_ob(trap);
00086                 free_object(trap);
00087             }
00088         }
00089         break;
00090 
00091     case SP_MOVING_BALL:
00092         if (QUERY_FLAG(victim, FLAG_ALIVE))
00093             hit_player(victim, trap->stats.dam, trap, trap->attacktype, 1);
00094         else if (victim->material || victim->materialname)
00095             save_throw_object(victim, trap->attacktype, trap);
00096         break;
00097     }
00098     common_post_ob_move_on(trap, victim, originator);
00099     return METHOD_OK;
00100 }
00101 
00108 static method_ret spell_effect_type_process(ob_methods *context, object *op) {
00109     switch (op->subtype) {
00110     case SP_BOLT:
00111         move_bolt(op);
00112         break;
00113 
00114     case SP_BULLET:
00115         move_bullet(op);
00116         break;
00117 
00118     case SP_EXPLOSION:
00119         explosion(op);
00120         break;
00121 
00122     case SP_CONE:
00123         move_cone(op);
00124         break;
00125 
00126     case SP_BOMB:
00127         animate_bomb(op);
00128         break;
00129 
00130     case SP_MAGIC_MISSILE:
00131         move_missile(op);
00132         break;
00133 
00134     case SP_WORD_OF_RECALL:
00135         execute_word_of_recall(op);
00136         break;
00137 
00138     case SP_MOVING_BALL:
00139         move_ball_spell(op);
00140         break;
00141 
00142     case SP_SWARM:
00143         move_swarm_spell(op);
00144         break;
00145 
00146     case SP_AURA:
00147         move_aura(op);
00148         break;
00149     }
00150     return METHOD_OK;
00151 }
00152 
00158 static void move_bolt(object *op) {
00159     object *tmp;
00160     int mflags;
00161     sint16 x, y;
00162     mapstruct *m;
00163 
00164     if (--(op->duration) < 0) {
00165         remove_ob(op);
00166         free_object(op);
00167         return;
00168     }
00169     hit_map(op, 0, op->attacktype, 1);
00170 
00171     if (!op->direction)
00172         return;
00173 
00174     if (--op->range < 0) {
00175         op->range = 0;
00176     } else {
00177         x = op->x+DIRX(op);
00178         y = op->y+DIRY(op);
00179         m = op->map;
00180         mflags = get_map_flags(m, &m, x, y, &x, &y);
00181 
00182         if (mflags&P_OUT_OF_MAP)
00183             return;
00184 
00185         /* We are about to run into something - we may bounce */
00186         /* Calling reflwall is pretty costly, as it has to look at all the objects
00187          * on the space.  So only call reflwall if we think the data it returns
00188          * will be useful.
00189          */
00190         if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y))
00191         || ((mflags&P_IS_ALIVE) && reflwall(m, x, y, op))) {
00192             if (!QUERY_FLAG(op, FLAG_REFLECTING))
00193                 return;
00194 
00195             /* Since walls don't run diagonal, if the bolt is in
00196              * one of 4 main directions, it just reflects back in the
00197              * opposite direction.  However, if the bolt is travelling
00198              * on the diagonal, it is trickier - eg, a bolt travelling
00199              * northwest bounces different if it hits a north/south
00200              * wall (bounces to northeast) vs an east/west (bounces
00201              * to the southwest.
00202              */
00203             if (op->direction&1) {
00204                 op->direction = absdir(op->direction+4);
00205             } else {
00206                 int left, right;
00207                 int mflags;
00208 
00209                 /* Need to check for P_OUT_OF_MAP: if the bolt is tavelling
00210                  * over a corner in a tiled map, it is possible that
00211                  * op->direction is within an adjacent map but either
00212                  * op->direction-1 or op->direction+1 does not exist.
00213                  */
00214                 mflags = get_map_flags(op->map, &m, op->x+freearr_x[absdir(op->direction-1)], op->y+freearr_y[absdir(op->direction-1)], &x, &y);
00215 
00216                 left = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y));
00217 
00218                 mflags = get_map_flags(op->map, &m, op->x+freearr_x[absdir(op->direction+1)], op->y+freearr_y[absdir(op->direction+1)], &x, &y);
00219                 right = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y));
00220 
00221                 if (left == right)
00222                     op->direction = absdir(op->direction+4);
00223                 else if (left)
00224                     op->direction = absdir(op->direction+2);
00225                 else if (right)
00226                     op->direction = absdir(op->direction-2);
00227             }
00228             update_turn_face(op); /* A bolt *must *be IS_TURNABLE */
00229             return;
00230         } else { /* Create a copy of this object and put it ahead */
00231             tmp = get_object();
00232             copy_object(op, tmp);
00233             tmp->speed_left = -0.1;
00234             tmp->x += DIRX(tmp),
00235             tmp->y += DIRY(tmp);
00236             tmp = insert_ob_in_map(tmp, op->map, op, 0);
00237             /* To make up for the decrease at the top of the function */
00238             tmp->duration++;
00239 
00240             /* New forking code.  Possibly create forks of this object
00241              * going off in other directions.
00242              */
00243 
00244             if (rndm(0, 99) < tmp->stats.Dex) {  /* stats.Dex % of forking */
00245                 forklightning(op, tmp);
00246             }
00247             /* In this way, the object left behind sticks on the space, but
00248              * doesn't create any bolts that continue to move onward.
00249              */
00250             op->range = 0;
00251         } /* copy object and move it along */
00252     } /* if move bolt along */
00253 }
00254 
00261 static void move_bullet(object *op) {
00262     sint16 new_x, new_y;
00263     int mflags;
00264     mapstruct *m;
00265 
00266     /* Reached the end of its life - remove it */
00267     if (--op->range <= 0) {
00268         if (op->other_arch) {
00269             explode_bullet(op);
00270         } else {
00271             remove_ob(op);
00272             free_object(op);
00273         }
00274         return;
00275     }
00276 
00277     new_x = op->x+DIRX(op);
00278     new_y = op->y+DIRY(op);
00279     m = op->map;
00280     mflags = get_map_flags(m, &m, new_x, new_y, &new_x, &new_y);
00281 
00282     if (mflags&P_OUT_OF_MAP) {
00283         remove_ob(op);
00284         free_object(op);
00285         return;
00286     }
00287 
00288     if (!op->direction || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, new_x, new_y))) {
00289         if (op->other_arch) {
00290             explode_bullet(op);
00291         } else {
00292             remove_ob(op);
00293             free_object(op);
00294         }
00295         return;
00296     }
00297 
00298     remove_ob(op);
00299     op->x = new_x;
00300     op->y = new_y;
00301     if ((op = insert_ob_in_map(op, m, op, 0)) == NULL)
00302         return;
00303 
00304     if (reflwall(op->map, op->x, op->y, op)) {
00305         op->direction = absdir(op->direction+4);
00306         update_turn_face(op);
00307     } else {
00308         check_bullet(op);
00309     }
00310 }
00311 
00317 static void explosion(object *op) {
00318     object *tmp;
00319     mapstruct *m = op->map;
00320     int i;
00321 
00322     if (--(op->duration) < 0) {
00323         remove_ob(op);
00324         free_object(op);
00325         return;
00326     }
00327     hit_map(op, 0, op->attacktype, 0);
00328 
00329     if (op->range > 0) {
00330         for (i = 1; i < 9; i++) {
00331             sint16 dx, dy;
00332 
00333             dx = op->x+freearr_x[i];
00334             dy = op->y+freearr_y[i];
00335             /* ok_to_put_more already does things like checks for walls,
00336              * out of map, etc.
00337              */
00338             if (ok_to_put_more(op->map, dx, dy, op, op->attacktype)) {
00339                 tmp = get_object();
00340                 copy_object(op, tmp);
00341                 tmp->state = 0;
00342                 tmp->speed_left = -0.21;
00343                 tmp->range--;
00344                 tmp->value = 0;
00345                 tmp->x = dx;
00346                 tmp->y = dy;
00347                 insert_ob_in_map(tmp, m, op, 0);
00348             }
00349         }
00350         /* Reset range so we don't try to propogate anymore.
00351          * Call merge_spell to see if we can merge with another
00352          * spell on the space.
00353          */
00354         op->range = 0;
00355         merge_spell(op, op->x, op->y);
00356     }
00357 }
00358 
00363 static void move_cone(object *op) {
00364     int i;
00365     tag_t tag;
00366 
00367     /* if no map then hit_map will crash so just ignore object */
00368     if (!op->map) {
00369         LOG(llevError, "Tried to move_cone object %s without a map.\n", op->name ? op->name : "unknown");
00370         op->speed = 0;
00371         update_ob_speed(op);
00372         return;
00373     }
00374 
00375     /* lava saves it's life, but not yours  :) */
00376     if (QUERY_FLAG(op, FLAG_LIFESAVE)) {
00377         hit_map(op, 0, op->attacktype, 0);
00378         return;
00379     }
00380 
00381     tag = op->count;
00382     hit_map(op, 0, op->attacktype, 0);
00383 
00384     /* Check to see if we should push anything.
00385      * Spell objects with weight push whatever they encounter to some
00386      * degree.
00387      */
00388     if (op->weight)
00389         check_spell_knockback(op);
00390 
00391     if (was_destroyed(op, tag))
00392         return;
00393 
00394     if ((op->duration--) < 0) {
00395         remove_ob(op);
00396         free_object(op);
00397         return;
00398     }
00399     /* Object has hit maximum range, so don't have it move
00400      * any further.  When the duration above expires,
00401      * then the object will get removed.
00402      */
00403     if (--op->range < 0) {
00404         op->range = 0;    /* just so it doesn't wrap */
00405         return;
00406     }
00407 
00408     for (i = -1; i < 2; i++) {
00409         sint16 x = op->x+freearr_x[absdir(op->stats.sp+i)];
00410         sint16 y = op->y+freearr_y[absdir(op->stats.sp+i)];
00411 
00412         if (ok_to_put_more(op->map, x, y, op, op->attacktype)) {
00413             object *tmp = get_object();
00414 
00415             copy_object(op, tmp);
00416             tmp->x = x;
00417             tmp->y = y;
00418 
00419             tmp->duration = op->duration+1;
00420 
00421             /* Use for spell tracking - see ok_to_put_more() */
00422             tmp->stats.maxhp = op->stats.maxhp;
00423             insert_ob_in_map(tmp, op->map, op, 0);
00424             if (tmp->other_arch)
00425                 cone_drop(tmp);
00426         }
00427     }
00428 }
00429 
00434 static void animate_bomb(object *op) {
00435     int i;
00436     object *env, *tmp;
00437     archetype *at;
00438 
00439     if (op->state != NUM_ANIMATIONS(op)-1)
00440         return;
00441 
00442     env = object_get_env_recursive(op);
00443 
00444     if (op->env) {
00445         if (env->map == NULL)
00446             return;
00447 
00448         remove_ob(op);
00449         op->x = env->x;
00450         op->y = env->y;
00451         if ((op = insert_ob_in_map(op, env->map, op, 0)) == NULL)
00452             return;
00453     }
00454 
00455     /* This copies a lot of the code from the fire bullet,
00456      * but using the cast_bullet isn't really feasible,
00457      * so just set up the appropriate values.
00458      */
00459     at = find_archetype(SPLINT);
00460     if (at) {
00461         for (i = 1; i < 9; i++) {
00462             if (out_of_map(op->map, op->x+freearr_x[i], op->y+freearr_x[i]))
00463                 continue;
00464             tmp = arch_to_object(at);
00465             tmp->direction = i;
00466             tmp->range = op->range;
00467             tmp->stats.dam = op->stats.dam;
00468             tmp->duration = op->duration;
00469             tmp->attacktype = op->attacktype;
00470             copy_owner(tmp, op);
00471             if (op->skill && op->skill != tmp->skill) {
00472                 if (tmp->skill)
00473                     free_string(tmp->skill);
00474                 tmp->skill = add_refcount(op->skill);
00475             }
00476             if (QUERY_FLAG(tmp, FLAG_IS_TURNABLE))
00477                 SET_ANIMATION(tmp, i);
00478             tmp->x = op->x+freearr_x[i];
00479             tmp->y = op->y+freearr_x[i];
00480             insert_ob_in_map(tmp, op->map, op, 0);
00481             ob_process(tmp);
00482         }
00483     }
00484 
00485     explode_bullet(op);
00486 }
00487 
00492 static void move_missile(object *op) {
00493     int i, mflags;
00494     object *owner;
00495     sint16 new_x, new_y;
00496     mapstruct *m;
00497 
00498     if (op->range-- <= 0) {
00499         remove_ob(op);
00500         free_object(op);
00501         return;
00502     }
00503 
00504     owner = get_owner(op);
00505 
00506     new_x = op->x+DIRX(op);
00507     new_y = op->y+DIRY(op);
00508 
00509     mflags = get_map_flags(op->map, &m, new_x, new_y, &new_x, &new_y);
00510 
00511     if (!(mflags&P_OUT_OF_MAP)
00512     && ((mflags&P_IS_ALIVE) || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, new_x, new_y)))) {
00513         tag_t tag = op->count;
00514 
00515         hit_map(op, op->direction, AT_MAGIC, 1);
00516         /* Basically, missile only hits one thing then goes away.
00517          * we need to remove it if someone hasn't already done so.
00518          */
00519         if (!was_destroyed(op, tag)) {
00520             remove_ob(op);
00521             free_object(op);
00522         }
00523         return;
00524     }
00525 
00526     remove_ob(op);
00527     if (!op->direction || (mflags&P_OUT_OF_MAP)) {
00528         free_object(op);
00529         return;
00530     }
00531     op->x = new_x;
00532     op->y = new_y;
00533     op->map = m;
00534     i = spell_find_dir(op->map, op->x, op->y, get_owner(op));
00535     if (i > 0 && i != op->direction) {
00536         op->direction = i;
00537         SET_ANIMATION(op, op->direction);
00538     }
00539     insert_ob_in_map(op, op->map, op, 0);
00540 }
00541 
00546 static void execute_word_of_recall(object *op) {
00547     object *wor = op;
00548 
00549     while (op != NULL && op->type != PLAYER)
00550         op = op->env;
00551 
00552     if (op != NULL && op->map) {
00553         if ((get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL)&P_NO_CLERIC) && (!QUERY_FLAG(op, FLAG_WIZCAST)))
00554             draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
00555                 "You feel something fizzle inside you.", NULL);
00556         else
00557             enter_exit(op, wor);
00558     }
00559     remove_ob(wor);
00560     free_object(wor);
00561 }
00562 
00568 static void move_ball_spell(object *op) {
00569     int i, j, dam_save, dir, mflags;
00570     sint16 nx, ny, hx, hy;
00571     object *owner;
00572     mapstruct *m;
00573 
00574     owner = get_owner(op);
00575 
00576     /* the following logic makes sure that the ball doesn't move into a wall,
00577      * and makes sure that it will move along a wall to try and get at it's
00578      * victim.  The block immediately below more or less chooses a random
00579      * offset to move the ball, eg, keep it mostly on course, with some
00580      * deviations.
00581      */
00582 
00583     dir = 0;
00584     if (!(rndm(0, 3)))
00585         j = rndm(0, 1);
00586     else
00587         j = 0;
00588 
00589     for (i = 1; i < 9; i++) {
00590         /* i bit 0: alters sign of offset
00591          * other bits (i/2): absolute value of offset
00592          */
00593 
00594         int offset = ((i^j)&1) ? (i/2) : -(i/2);
00595         int tmpdir = absdir(op->direction+offset);
00596 
00597         nx = op->x+freearr_x[tmpdir];
00598         ny = op->y+freearr_y[tmpdir];
00599         if (!(get_map_flags(op->map, &m, nx, ny, &nx, &ny)&P_OUT_OF_MAP)
00600         && !(OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, nx, ny)))) {
00601             dir = tmpdir;
00602             break;
00603         }
00604     }
00605     if (dir == 0) {
00606         nx = op->x;
00607         ny = op->y;
00608         m = op->map;
00609     }
00610 
00611     remove_ob(op);
00612     op->y = ny;
00613     op->x = nx;
00614     insert_ob_in_map(op, m, op, 0);
00615 
00616     dam_save = op->stats.dam;  /* save the original dam: we do halfdam on
00617                                 surrounding squares */
00618 
00619     /* loop over current square and neighbors to hit.
00620      * if this has an other_arch field, we insert that in
00621      * the surround spaces.
00622      */
00623     for (j = 0; j < 9; j++) {
00624         object *new_ob;
00625 
00626         hx = nx+freearr_x[j];
00627         hy = ny+freearr_y[j];
00628 
00629         m = op->map;
00630         mflags = get_map_flags(m, &m, hx, hy, &hx, &hy);
00631 
00632         if (mflags&P_OUT_OF_MAP)
00633             continue;
00634 
00635         /* first, don't ever, ever hit the owner.  Don't hit out
00636          * of the map either.
00637          */
00638 
00639         if ((mflags&P_IS_ALIVE) && (!owner || owner->x != hx || owner->y != hy || !on_same_map(owner, op))) {
00640             if (j)
00641                 op->stats.dam = dam_save/2;
00642             hit_map(op, j, op->attacktype, 1);
00643 
00644         }
00645 
00646         /* insert the other arch */
00647         if (op->other_arch && !(OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, hx, hy)))) {
00648             new_ob = arch_to_object(op->other_arch);
00649             new_ob->x = hx;
00650             new_ob->y = hy;
00651             insert_ob_in_map(new_ob, m, op, 0);
00652         }
00653     }
00654 
00655     /* restore to the center location and damage*/
00656     op->stats.dam = dam_save;
00657 
00658     i = spell_find_dir(op->map, op->x, op->y, get_owner(op));
00659 
00660     if (i >= 0) { /* we have a preferred direction!  */
00661         /* pick another direction if the preferred dir is blocked. */
00662         if (get_map_flags(op->map, &m, nx+freearr_x[i], ny+freearr_y[i], &hx, &hy)&P_OUT_OF_MAP
00663         || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, hx, hy))) {
00664             i = absdir(i+rndm(0, 2)-1);  /* -1, 0, +1 */
00665         }
00666         op->direction = i;
00667     }
00668 }
00669 
00670 /*
00671  * This is an implementation of the swarm spell. It was written for meteor
00672  * swarm, but it could be used for any swarm. A swarm spell is a special type
00673  * of object that casts swarms of other types of spells. Which spell it casts
00674  * is flexible. It fires the spells from a set of squares surrounding the
00675  * caster, in a given direction.
00676  * @param op The spell effect.
00677  */
00678 static void move_swarm_spell(object *op) {
00679     static int cardinal_adjust[9] = { -3, -2, -1, 0, 0, 0, 1, 2, 3 };
00680     static int diagonal_adjust[10] = { -3, -2, -2, -1, 0, 0, 1, 2, 2, 3 };
00681     sint16 target_x, target_y, origin_x, origin_y;
00682     int basedir, adjustdir;
00683     mapstruct *m;
00684     object *owner;
00685 
00686     owner = get_owner(op);
00687     if (op->duration == 0 || owner == NULL || owner->x != op->x || owner->y != op->y) {
00688         remove_ob(op);
00689         free_object(op);
00690         return;
00691     }
00692     op->duration--;
00693 
00694     basedir = op->direction;
00695     if (basedir == 0) {
00696         /* spray in all directions! 8) */
00697         basedir = rndm(1, 8);
00698     }
00699 
00700     /* new offset calculation to make swarm element distribution
00701      * more uniform
00702      */
00703     if (op->duration) {
00704         if (basedir&1) {
00705             adjustdir = cardinal_adjust[rndm(0, 8)];
00706         } else {
00707             adjustdir = diagonal_adjust[rndm(0, 9)];
00708         }
00709     } else {
00710         adjustdir = 0;  /* fire the last one from forward. */
00711     }
00712 
00713     target_x = op->x+freearr_x[absdir(basedir+adjustdir)];
00714     target_y = op->y+freearr_y[absdir(basedir+adjustdir)];
00715 
00716     /* back up one space so we can hit point-blank targets, but this
00717      * necessitates extra out_of_map check below
00718      */
00719     origin_x = target_x-freearr_x[basedir];
00720     origin_y = target_y-freearr_y[basedir];
00721 
00722 
00723     /* spell pointer is set up for the spell this casts.  Since this
00724      * should just be a pointer to the spell in some inventory,
00725      * it is unlikely to disappear by the time we need it.  However,
00726      * do some sanity checking anyways.
00727      */
00728 
00729     if (op->spell && op->spell->type == SPELL && !(get_map_flags(op->map, &m, target_x, target_y, &target_x, &target_y)&P_OUT_OF_MAP)) {
00730         /* Bullet spells have a bunch more customization that needs to be done */
00731         if (op->spell->subtype == SP_BULLET)
00732             fire_bullet(owner, op, basedir, op->spell);
00733         else if (op->spell->subtype == SP_MAGIC_MISSILE)
00734             fire_arch_from_position(owner, op, origin_x, origin_y, basedir, op->spell);
00735     }
00736 }
00737 
00748 static void move_aura(object *aura) {
00749     int i, mflags;
00750     object *env;
00751     mapstruct *m;
00752 
00753     /* auras belong in inventories */
00754     env = aura->env;
00755 
00756     /* no matter what we've gotta remove the aura...
00757      * we'll put it back if its time isn't up.
00758      */
00759     remove_ob(aura);
00760 
00761     /* exit if we're out of gas */
00762     if (aura->duration-- < 0) {
00763         free_object(aura);
00764         return;
00765     }
00766 
00767     /* auras only exist in inventories */
00768     if (env == NULL || env->map == NULL) {
00769         free_object(aura);
00770         return;
00771     }
00772     aura->x = env->x;
00773     aura->y = env->y;
00774 
00775     /* we need to jump out of the inventory for a bit
00776      * in order to hit the map conveniently.
00777      */
00778     insert_ob_in_map(aura, env->map, aura, 0);
00779 
00780     for (i = 1; i < 9; i++) {
00781         sint16 nx, ny;
00782 
00783         nx = aura->x+freearr_x[i];
00784         ny = aura->y+freearr_y[i];
00785         mflags = get_map_flags(env->map, &m, nx, ny, &nx, &ny);
00786 
00787         /* Consider the movement type of the person with the aura as
00788          * movement type of the aura.  Eg, if the player is flying, the aura
00789          * is flying also, if player is walking, it is on the ground, etc.
00790          */
00791         if (!(mflags&P_OUT_OF_MAP) && !(OB_TYPE_MOVE_BLOCK(env, GET_MAP_MOVE_BLOCK(m, nx, ny)))) {
00792             hit_map(aura, i, aura->attacktype, 0);
00793 
00794             if (aura->other_arch) {
00795                 object *new_ob;
00796 
00797                 new_ob = arch_to_object(aura->other_arch);
00798                 new_ob->x = nx;
00799                 new_ob->y = ny;
00800                 insert_ob_in_map(new_ob, m, aura, 0);
00801             }
00802         }
00803     }
00804 
00805     /* put the aura back in the player's inventory */
00806     remove_ob(aura);
00807     insert_ob_in_ob(aura, env);
00808     check_spell_expiry(aura);
00809 }
00810 
00816 static void forklightning(object *op, object *tmp) {
00817     int new_dir = 1;  /* direction or -1 for left, +1 for right 0 if no new bolt */
00818     int t_dir; /* stores temporary dir calculation */
00819     mapstruct *m;
00820     sint16 sx, sy;
00821     object *new_bolt;
00822 
00823     /* pick a fork direction.  tmp->stats.Con is the left bias
00824      * i.e., the chance in 100 of forking LEFT
00825      * Should start out at 50, down to 25 for one already going left
00826      * down to 0 for one going 90 degrees left off original path
00827      */
00828 
00829     if (rndm(0, 99) < tmp->stats.Con)  /* fork left */
00830         new_dir = -1;
00831 
00832     /* check the new dir for a wall and in the map*/
00833     t_dir = absdir(tmp->direction+new_dir);
00834 
00835     if (get_map_flags(tmp->map, &m, tmp->x+freearr_x[t_dir], tmp->y+freearr_y[t_dir], &sx, &sy)&P_OUT_OF_MAP)
00836         return;
00837 
00838     if (OB_TYPE_MOVE_BLOCK(tmp, GET_MAP_MOVE_BLOCK(m, sx, sy)))
00839         return;
00840 
00841     /* OK, we made a fork */
00842     new_bolt = get_object();
00843 
00844     copy_object(tmp, new_bolt);
00845 
00846     /* reduce chances of subsequent forking */
00847     new_bolt->stats.Dex -= 10;
00848     tmp->stats.Dex -= 10;  /* less forks from main bolt too */
00849     new_bolt->stats.Con += 25*new_dir; /* adjust the left bias */
00850     new_bolt->speed_left = -0.1;
00851     new_bolt->direction = t_dir;
00852     new_bolt->duration++;
00853     new_bolt->x = sx;
00854     new_bolt->y = sy;
00855     new_bolt->stats.dam /= 2;  /* reduce daughter bolt damage */
00856     new_bolt->stats.dam++;
00857     tmp->stats.dam /= 2;  /* reduce father bolt damage */
00858     tmp->stats.dam++;
00859     new_bolt = insert_ob_in_map(new_bolt, m, op, 0);
00860     update_turn_face(new_bolt);
00861 }
00862 
00869 static void check_spell_knockback(object *op) {
00870     object *tmp, *tmp2; /* object on the map */
00871     int weight_move;
00872     int frictionmod = 2; /*poor man's physics - multipy targets weight by this amount */
00873 
00874     if (!op->weight) { /*shouldn't happen but if cone object has no weight drop out*/
00875         /*LOG(llevDebug, "DEBUG: arch weighs nothing\n");*/
00876         return;
00877     } else {
00878         weight_move = op->weight+(op->weight*op->level)/3;
00879         /*LOG(llevDebug, "DEBUG: arch weighs %d and masses %d (%s,level %d)\n", op->weight, weight_move, op->name, op->level);*/
00880     }
00881 
00882     for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) {
00883         int num_sections = 1;
00884 
00885         /* don't move DM */
00886         if (QUERY_FLAG(tmp, FLAG_WIZ))
00887             return;
00888 
00889         /* don't move parts of objects */
00890         if (tmp->head)
00891             continue;
00892 
00893         /* don't move floors or immobile objects */
00894         if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) || (!QUERY_FLAG(tmp, FLAG_ALIVE) && QUERY_FLAG(tmp, FLAG_NO_PICK)))
00895             continue;
00896 
00897         /* count the object's sections */
00898         for (tmp2 = tmp; tmp2 != NULL; tmp2 = tmp2->more)
00899             num_sections++;
00900 
00901         /* I'm not sure if it makes sense to divide by num_sections - bigger
00902          * objects should be harder to move, and we are moving the entire
00903          * object, not just the head, so the total weight should be relevant.
00904          */
00905 
00906          /* surface area? -tm */
00907 
00908         if (tmp->move_type&MOVE_FLYING)
00909             frictionmod = 1; /* flying objects loose the friction modifier */
00910 
00911         if (rndm(0, weight_move-1) > ((tmp->weight/num_sections)*frictionmod)) {  /* move it. */
00912             /* move_object is really for monsters, but looking at
00913              * the move_object function, it appears that it should
00914              * also be safe for objects.
00915              * This does return if successful or not, but
00916              * I don't see us doing anything useful with that information
00917              * right now.
00918              */
00919             move_object(tmp, absdir(op->stats.sp));
00920         }
00921     }
00922 }