Crossfire Server, Branch 1.12
R12190
|
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 }