Crossfire Server, Branches 1.12  R18729
spell_effect.c
Go to the documentation of this file.
1 /*
2  CrossFire, A Multiplayer game for X-windows
3 
4  Copyright (C) 2007 Mark Wedel & Crossfire Development Team
5  Copyright (C) 1992 Frank Tore Johansen
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21  The authors can be reached via e-mail at crossfire-devel@real-time.com
22 */
23 
28 #include <global.h>
29 #include <ob_methods.h>
30 #include <ob_types.h>
31 #include <sounds.h>
32 #include <sproto.h>
33 
34 static method_ret spell_effect_type_move_on(ob_methods *context, object *trap, object *victim, object *originator);
35 static method_ret spell_effect_type_process(ob_methods *context, object *op);
36 
37 static void move_bolt(object *op);
38 static void move_bullet(object *op);
39 static void explosion(object *op);
40 static void move_cone(object *op);
41 static void animate_bomb(object *op);
42 static void move_missile(object *op);
43 static void execute_word_of_recall(object *op);
44 static void move_ball_spell(object *op);
45 static void move_swarm_spell(object *op);
46 static void move_aura(object *aura);
47 
48 static void forklightning(object *op, object *tmp);
49 static void check_spell_knockback(object *op);
50 
57 }
58 
67 static method_ret spell_effect_type_move_on(ob_methods *context, object *trap, object *victim, object *originator) {
68  if (common_pre_ob_move_on(trap, victim, originator) == METHOD_ERROR)
69  return METHOD_OK;
70 
71  switch (trap->subtype) {
72  case SP_CONE:
73  if (QUERY_FLAG(victim, FLAG_ALIVE)
74  && trap->speed
75  && trap->attacktype)
76  hit_player(victim, trap->stats.dam, trap, trap->attacktype, 0);
77  break;
78 
79  case SP_MAGIC_MISSILE:
80  if (QUERY_FLAG(victim, FLAG_ALIVE)) {
81  tag_t spell_tag = trap->count;
82 
83  hit_player(victim, trap->stats.dam, trap, trap->attacktype, 1);
84  if (!was_destroyed(trap, spell_tag)) {
85  remove_ob(trap);
86  free_object(trap);
87  }
88  }
89  break;
90 
91  case SP_MOVING_BALL:
92  if (QUERY_FLAG(victim, FLAG_ALIVE))
93  hit_player(victim, trap->stats.dam, trap, trap->attacktype, 1);
94  else if (victim->material || victim->materialname)
95  save_throw_object(victim, trap->attacktype, trap);
96  break;
97  }
98  common_post_ob_move_on(trap, victim, originator);
99  return METHOD_OK;
100 }
101 
108 static method_ret spell_effect_type_process(ob_methods *context, object *op) {
109  switch (op->subtype) {
110  case SP_BOLT:
111  move_bolt(op);
112  break;
113 
114  case SP_BULLET:
115  move_bullet(op);
116  break;
117 
118  case SP_EXPLOSION:
119  explosion(op);
120  break;
121 
122  case SP_CONE:
123  move_cone(op);
124  break;
125 
126  case SP_BOMB:
127  animate_bomb(op);
128  break;
129 
130  case SP_MAGIC_MISSILE:
131  move_missile(op);
132  break;
133 
134  case SP_WORD_OF_RECALL:
136  break;
137 
138  case SP_MOVING_BALL:
139  move_ball_spell(op);
140  break;
141 
142  case SP_SWARM:
143  move_swarm_spell(op);
144  break;
145 
146  case SP_AURA:
147  move_aura(op);
148  break;
149  }
150  return METHOD_OK;
151 }
152 
158 static void move_bolt(object *op) {
159  object *tmp;
160  int mflags;
161  sint16 x, y;
162  mapstruct *m;
163 
164  if (--(op->duration) < 0) {
165  remove_ob(op);
166  free_object(op);
167  return;
168  }
169  hit_map(op, 0, op->attacktype, 1);
170 
171  if (!op->direction)
172  return;
173 
174  if (--op->range < 0) {
175  op->range = 0;
176  } else {
177  x = op->x+DIRX(op);
178  y = op->y+DIRY(op);
179  m = op->map;
180  mflags = get_map_flags(m, &m, x, y, &x, &y);
181 
182  if (mflags&P_OUT_OF_MAP)
183  return;
184 
185  /* We are about to run into something - we may bounce */
186  /* Calling reflwall is pretty costly, as it has to look at all the objects
187  * on the space. So only call reflwall if we think the data it returns
188  * will be useful.
189  */
190  if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y))
191  || ((mflags&P_IS_ALIVE) && reflwall(m, x, y, op))) {
192  if (!QUERY_FLAG(op, FLAG_REFLECTING))
193  return;
194 
195  /* Since walls don't run diagonal, if the bolt is in
196  * one of 4 main directions, it just reflects back in the
197  * opposite direction. However, if the bolt is travelling
198  * on the diagonal, it is trickier - eg, a bolt travelling
199  * northwest bounces different if it hits a north/south
200  * wall (bounces to northeast) vs an east/west (bounces
201  * to the southwest.
202  */
203  if (op->direction&1) {
204  op->direction = absdir(op->direction+4);
205  } else {
206  int left, right;
207  int mflags;
208 
209  /* Need to check for P_OUT_OF_MAP: if the bolt is tavelling
210  * over a corner in a tiled map, it is possible that
211  * op->direction is within an adjacent map but either
212  * op->direction-1 or op->direction+1 does not exist.
213  */
214  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);
215 
216  left = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y));
217 
218  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);
219  right = (mflags&P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, x, y));
220 
221  if (left == right)
222  op->direction = absdir(op->direction+4);
223  else if (left)
224  op->direction = absdir(op->direction+2);
225  else if (right)
226  op->direction = absdir(op->direction-2);
227  }
228  update_turn_face(op); /* A bolt *must *be IS_TURNABLE */
229  return;
230  } else { /* Create a copy of this object and put it ahead */
231  tmp = get_object();
232  copy_object(op, tmp);
233  tmp->speed_left = -0.1;
234  tmp->x += DIRX(tmp),
235  tmp->y += DIRY(tmp);
236  tmp = insert_ob_in_map(tmp, op->map, op, 0);
237  /* To make up for the decrease at the top of the function */
238  tmp->duration++;
239 
240  /* New forking code. Possibly create forks of this object
241  * going off in other directions.
242  */
243 
244  if (rndm(0, 99) < tmp->stats.Dex) { /* stats.Dex % of forking */
245  forklightning(op, tmp);
246  }
247  /* In this way, the object left behind sticks on the space, but
248  * doesn't create any bolts that continue to move onward.
249  */
250  op->range = 0;
251  } /* copy object and move it along */
252  } /* if move bolt along */
253 }
254 
261 static void move_bullet(object *op) {
262  sint16 new_x, new_y;
263  int mflags;
264  mapstruct *m;
265 
266  /* Reached the end of its life - remove it */
267  if (--op->range <= 0) {
268  if (op->other_arch) {
269  explode_bullet(op);
270  } else {
271  remove_ob(op);
272  free_object(op);
273  }
274  return;
275  }
276 
277  new_x = op->x+DIRX(op);
278  new_y = op->y+DIRY(op);
279  m = op->map;
280  mflags = get_map_flags(m, &m, new_x, new_y, &new_x, &new_y);
281 
282  if (mflags&P_OUT_OF_MAP) {
283  remove_ob(op);
284  free_object(op);
285  return;
286  }
287 
288  if (!op->direction || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, new_x, new_y))) {
289  if (op->other_arch) {
290  explode_bullet(op);
291  } else {
292  remove_ob(op);
293  free_object(op);
294  }
295  return;
296  }
297 
298  remove_ob(op);
299  op->x = new_x;
300  op->y = new_y;
301  if ((op = insert_ob_in_map(op, m, op, 0)) == NULL)
302  return;
303 
304  if (reflwall(op->map, op->x, op->y, op)) {
305  op->direction = absdir(op->direction+4);
306  update_turn_face(op);
307  } else {
308  check_bullet(op);
309  }
310 }
311 
317 static void explosion(object *op) {
318  object *tmp;
319  mapstruct *m = op->map;
320  int i;
321 
322  if (--(op->duration) < 0) {
323  remove_ob(op);
324  free_object(op);
325  return;
326  }
327  hit_map(op, 0, op->attacktype, 0);
328 
329  if (op->range > 0) {
330  for (i = 1; i < 9; i++) {
331  sint16 dx, dy;
332 
333  dx = op->x+freearr_x[i];
334  dy = op->y+freearr_y[i];
335  /* ok_to_put_more already does things like checks for walls,
336  * out of map, etc.
337  */
338  if (ok_to_put_more(op->map, dx, dy, op, op->attacktype)) {
339  tmp = get_object();
340  copy_object(op, tmp);
341  tmp->state = 0;
342  tmp->speed_left = -0.21;
343  tmp->range--;
344  tmp->value = 0;
345  tmp->x = dx;
346  tmp->y = dy;
347  insert_ob_in_map(tmp, m, op, 0);
348  }
349  }
350  /* Reset range so we don't try to propogate anymore.
351  * Call merge_spell to see if we can merge with another
352  * spell on the space.
353  */
354  op->range = 0;
355  merge_spell(op, op->x, op->y);
356  }
357 }
358 
363 static void move_cone(object *op) {
364  int i;
365  tag_t tag;
366 
367  /* if no map then hit_map will crash so just ignore object */
368  if (!op->map) {
369  LOG(llevError, "Tried to move_cone object %s without a map.\n", op->name ? op->name : "unknown");
370  op->speed = 0;
371  update_ob_speed(op);
372  return;
373  }
374 
375  /* lava saves it's life, but not yours :) */
376  if (QUERY_FLAG(op, FLAG_LIFESAVE)) {
377  hit_map(op, 0, op->attacktype, 0);
378  return;
379  }
380 
381  tag = op->count;
382  hit_map(op, 0, op->attacktype, 0);
383 
384  /* Check to see if we should push anything.
385  * Spell objects with weight push whatever they encounter to some
386  * degree.
387  */
388  if (op->weight)
390 
391  if (was_destroyed(op, tag))
392  return;
393 
394  if ((op->duration--) < 0) {
395  remove_ob(op);
396  free_object(op);
397  return;
398  }
399  /* Object has hit maximum range, so don't have it move
400  * any further. When the duration above expires,
401  * then the object will get removed.
402  */
403  if (--op->range < 0) {
404  op->range = 0; /* just so it doesn't wrap */
405  return;
406  }
407 
408  for (i = -1; i < 2; i++) {
409  sint16 x = op->x+freearr_x[absdir(op->stats.sp+i)];
410  sint16 y = op->y+freearr_y[absdir(op->stats.sp+i)];
411 
412  if (ok_to_put_more(op->map, x, y, op, op->attacktype)) {
413  object *tmp = get_object();
414 
415  copy_object(op, tmp);
416  tmp->x = x;
417  tmp->y = y;
418 
419  tmp->duration = op->duration+1;
420 
421  /* Use for spell tracking - see ok_to_put_more() */
422  tmp->stats.maxhp = op->stats.maxhp;
423  insert_ob_in_map(tmp, op->map, op, 0);
424  if (tmp->other_arch)
425  cone_drop(tmp);
426  }
427  }
428 }
429 
434 static void animate_bomb(object *op) {
435  int i;
436  object *env, *tmp;
437  archetype *at;
438 
439  if (op->state != NUM_ANIMATIONS(op)-1)
440  return;
441 
442  env = object_get_env_recursive(op);
443 
444  if (op->env) {
445  if (env->map == NULL)
446  return;
447 
448  remove_ob(op);
449  op->x = env->x;
450  op->y = env->y;
451  if ((op = insert_ob_in_map(op, env->map, op, 0)) == NULL)
452  return;
453  }
454 
455  /* This copies a lot of the code from the fire bullet,
456  * but using the cast_bullet isn't really feasible,
457  * so just set up the appropriate values.
458  */
459  at = find_archetype(SPLINT);
460  if (at) {
461  for (i = 1; i < 9; i++) {
462  if (out_of_map(op->map, op->x+freearr_x[i], op->y+freearr_x[i]))
463  continue;
464  tmp = arch_to_object(at);
465  tmp->direction = i;
466  tmp->range = op->range;
467  tmp->stats.dam = op->stats.dam;
468  tmp->duration = op->duration;
469  tmp->attacktype = op->attacktype;
470  copy_owner(tmp, op);
471  if (op->skill && op->skill != tmp->skill) {
472  if (tmp->skill)
473  free_string(tmp->skill);
474  tmp->skill = add_refcount(op->skill);
475  }
476  if (QUERY_FLAG(tmp, FLAG_IS_TURNABLE))
477  SET_ANIMATION(tmp, i);
478  tmp->x = op->x+freearr_x[i];
479  tmp->y = op->y+freearr_x[i];
480  insert_ob_in_map(tmp, op->map, op, 0);
481  ob_process(tmp);
482  }
483  }
484 
485  explode_bullet(op);
486 }
487 
492 static void move_missile(object *op) {
493  int i, mflags;
494  object *owner;
495  sint16 new_x, new_y;
496  mapstruct *m;
497 
498  if (op->range-- <= 0) {
499  remove_ob(op);
500  free_object(op);
501  return;
502  }
503 
504  owner = get_owner(op);
505 
506  new_x = op->x+DIRX(op);
507  new_y = op->y+DIRY(op);
508 
509  mflags = get_map_flags(op->map, &m, new_x, new_y, &new_x, &new_y);
510 
511  if (!(mflags&P_OUT_OF_MAP)
512  && ((mflags&P_IS_ALIVE) || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, new_x, new_y)))) {
513  tag_t tag = op->count;
514 
515  hit_map(op, op->direction, AT_MAGIC, 1);
516  /* Basically, missile only hits one thing then goes away.
517  * we need to remove it if someone hasn't already done so.
518  */
519  if (!was_destroyed(op, tag)) {
520  remove_ob(op);
521  free_object(op);
522  }
523  return;
524  }
525 
526  remove_ob(op);
527  if (!op->direction || (mflags&P_OUT_OF_MAP)) {
528  free_object(op);
529  return;
530  }
531  op->x = new_x;
532  op->y = new_y;
533  op->map = m;
534  i = spell_find_dir(op->map, op->x, op->y, get_owner(op));
535  if (i > 0 && i != op->direction) {
536  op->direction = i;
537  SET_ANIMATION(op, op->direction);
538  }
539  insert_ob_in_map(op, op->map, op, 0);
540 }
541 
546 static void execute_word_of_recall(object *op) {
547  object *wor = op;
548 
549  while (op != NULL && op->type != PLAYER)
550  op = op->env;
551 
552  if (op != NULL && op->map) {
553  if ((get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL)&P_NO_CLERIC) && (!QUERY_FLAG(op, FLAG_WIZCAST)))
555  "You feel something fizzle inside you.", NULL);
556  else
557  enter_exit(op, wor);
558  }
559  remove_ob(wor);
560  free_object(wor);
561 }
562 
568 static void move_ball_spell(object *op) {
569  int i, j, dam_save, dir, mflags;
570  sint16 nx, ny, hx, hy;
571  object *owner;
572  mapstruct *m;
573 
574  owner = get_owner(op);
575 
576  /* the following logic makes sure that the ball doesn't move into a wall,
577  * and makes sure that it will move along a wall to try and get at it's
578  * victim. The block immediately below more or less chooses a random
579  * offset to move the ball, eg, keep it mostly on course, with some
580  * deviations.
581  */
582 
583  dir = 0;
584  if (!(rndm(0, 3)))
585  j = rndm(0, 1);
586  else
587  j = 0;
588 
589  for (i = 1; i < 9; i++) {
590  /* i bit 0: alters sign of offset
591  * other bits (i/2): absolute value of offset
592  */
593 
594  int offset = ((i^j)&1) ? (i/2) : -(i/2);
595  int tmpdir = absdir(op->direction+offset);
596 
597  nx = op->x+freearr_x[tmpdir];
598  ny = op->y+freearr_y[tmpdir];
599  if (!(get_map_flags(op->map, &m, nx, ny, &nx, &ny)&P_OUT_OF_MAP)
600  && !(OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, nx, ny)))) {
601  dir = tmpdir;
602  break;
603  }
604  }
605  if (dir == 0) {
606  nx = op->x;
607  ny = op->y;
608  m = op->map;
609  }
610 
611  remove_ob(op);
612  op->y = ny;
613  op->x = nx;
614  insert_ob_in_map(op, m, op, 0);
615 
616  dam_save = op->stats.dam; /* save the original dam: we do halfdam on
617  surrounding squares */
618 
619  /* loop over current square and neighbors to hit.
620  * if this has an other_arch field, we insert that in
621  * the surround spaces.
622  */
623  for (j = 0; j < 9; j++) {
624  object *new_ob;
625 
626  hx = nx+freearr_x[j];
627  hy = ny+freearr_y[j];
628 
629  m = op->map;
630  mflags = get_map_flags(m, &m, hx, hy, &hx, &hy);
631 
632  if (mflags&P_OUT_OF_MAP)
633  continue;
634 
635  /* first, don't ever, ever hit the owner. Don't hit out
636  * of the map either.
637  */
638 
639  if ((mflags&P_IS_ALIVE) && (!owner || owner->x != hx || owner->y != hy || !on_same_map(owner, op))) {
640  if (j)
641  op->stats.dam = dam_save/2;
642  hit_map(op, j, op->attacktype, 1);
643 
644  }
645 
646  /* insert the other arch */
647  if (op->other_arch && !(OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, hx, hy)))) {
648  new_ob = arch_to_object(op->other_arch);
649  new_ob->x = hx;
650  new_ob->y = hy;
651  insert_ob_in_map(new_ob, m, op, 0);
652  }
653  }
654 
655  /* restore to the center location and damage*/
656  op->stats.dam = dam_save;
657 
658  i = spell_find_dir(op->map, op->x, op->y, get_owner(op));
659 
660  if (i >= 0) { /* we have a preferred direction! */
661  /* pick another direction if the preferred dir is blocked. */
662  if (get_map_flags(op->map, &m, nx+freearr_x[i], ny+freearr_y[i], &hx, &hy)&P_OUT_OF_MAP
663  || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, hx, hy))) {
664  i = absdir(i+rndm(0, 2)-1); /* -1, 0, +1 */
665  }
666  op->direction = i;
667  }
668 }
669 
670 /*
671  * This is an implementation of the swarm spell. It was written for meteor
672  * swarm, but it could be used for any swarm. A swarm spell is a special type
673  * of object that casts swarms of other types of spells. Which spell it casts
674  * is flexible. It fires the spells from a set of squares surrounding the
675  * caster, in a given direction.
676  * @param op The spell effect.
677  */
678 static void move_swarm_spell(object *op) {
679  static int cardinal_adjust[9] = { -3, -2, -1, 0, 0, 0, 1, 2, 3 };
680  static int diagonal_adjust[10] = { -3, -2, -2, -1, 0, 0, 1, 2, 2, 3 };
681  sint16 target_x, target_y, origin_x, origin_y;
682  int basedir, adjustdir;
683  mapstruct *m;
684  object *owner;
685 
686  owner = get_owner(op);
687  if (op->duration == 0 || owner == NULL || owner->x != op->x || owner->y != op->y) {
688  remove_ob(op);
689  free_object(op);
690  return;
691  }
692  op->duration--;
693 
694  basedir = op->direction;
695  if (basedir == 0) {
696  /* spray in all directions! 8) */
697  basedir = rndm(1, 8);
698  }
699 
700  /* new offset calculation to make swarm element distribution
701  * more uniform
702  */
703  if (op->duration) {
704  if (basedir&1) {
705  adjustdir = cardinal_adjust[rndm(0, 8)];
706  } else {
707  adjustdir = diagonal_adjust[rndm(0, 9)];
708  }
709  } else {
710  adjustdir = 0; /* fire the last one from forward. */
711  }
712 
713  target_x = op->x+freearr_x[absdir(basedir+adjustdir)];
714  target_y = op->y+freearr_y[absdir(basedir+adjustdir)];
715 
716  /* back up one space so we can hit point-blank targets, but this
717  * necessitates extra out_of_map check below
718  */
719  origin_x = target_x-freearr_x[basedir];
720  origin_y = target_y-freearr_y[basedir];
721 
722 
723  /* spell pointer is set up for the spell this casts. Since this
724  * should just be a pointer to the spell in some inventory,
725  * it is unlikely to disappear by the time we need it. However,
726  * do some sanity checking anyways.
727  */
728 
729  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)) {
730  /* Bullet spells have a bunch more customization that needs to be done */
731  if (op->spell->subtype == SP_BULLET)
732  fire_bullet(owner, op, basedir, op->spell);
733  else if (op->spell->subtype == SP_MAGIC_MISSILE)
734  fire_arch_from_position(owner, op, origin_x, origin_y, basedir, op->spell);
735  }
736 }
737 
748 static void move_aura(object *aura) {
749  int i, mflags;
750  object *env;
751  mapstruct *m;
752 
753  /* auras belong in inventories */
754  env = aura->env;
755 
756  /* no matter what we've gotta remove the aura...
757  * we'll put it back if its time isn't up.
758  */
759  remove_ob(aura);
760 
761  /* exit if we're out of gas */
762  if (aura->duration-- < 0) {
763  free_object(aura);
764  return;
765  }
766 
767  /* auras only exist in inventories */
768  if (env == NULL || env->map == NULL) {
769  free_object(aura);
770  return;
771  }
772  aura->x = env->x;
773  aura->y = env->y;
774 
775  /* we need to jump out of the inventory for a bit
776  * in order to hit the map conveniently.
777  */
778  insert_ob_in_map(aura, env->map, aura, 0);
779 
780  for (i = 1; i < 9; i++) {
781  sint16 nx, ny;
782 
783  nx = aura->x+freearr_x[i];
784  ny = aura->y+freearr_y[i];
785  mflags = get_map_flags(env->map, &m, nx, ny, &nx, &ny);
786 
787  /* Consider the movement type of the person with the aura as
788  * movement type of the aura. Eg, if the player is flying, the aura
789  * is flying also, if player is walking, it is on the ground, etc.
790  */
791  if (!(mflags&P_OUT_OF_MAP) && !(OB_TYPE_MOVE_BLOCK(env, GET_MAP_MOVE_BLOCK(m, nx, ny)))) {
792  hit_map(aura, i, aura->attacktype, 0);
793 
794  if (aura->other_arch) {
795  object *new_ob;
796 
797  new_ob = arch_to_object(aura->other_arch);
798  new_ob->x = nx;
799  new_ob->y = ny;
800  insert_ob_in_map(new_ob, m, aura, 0);
801  }
802  }
803  }
804 
805  /* put the aura back in the player's inventory */
806  remove_ob(aura);
807  insert_ob_in_ob(aura, env);
808  check_spell_expiry(aura);
809 }
810 
816 static void forklightning(object *op, object *tmp) {
817  int new_dir = 1; /* direction or -1 for left, +1 for right 0 if no new bolt */
818  int t_dir; /* stores temporary dir calculation */
819  mapstruct *m;
820  sint16 sx, sy;
821  object *new_bolt;
822 
823  /* pick a fork direction. tmp->stats.Con is the left bias
824  * i.e., the chance in 100 of forking LEFT
825  * Should start out at 50, down to 25 for one already going left
826  * down to 0 for one going 90 degrees left off original path
827  */
828 
829  if (rndm(0, 99) < tmp->stats.Con) /* fork left */
830  new_dir = -1;
831 
832  /* check the new dir for a wall and in the map*/
833  t_dir = absdir(tmp->direction+new_dir);
834 
835  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)
836  return;
837 
838  if (OB_TYPE_MOVE_BLOCK(tmp, GET_MAP_MOVE_BLOCK(m, sx, sy)))
839  return;
840 
841  /* OK, we made a fork */
842  new_bolt = get_object();
843 
844  copy_object(tmp, new_bolt);
845 
846  /* reduce chances of subsequent forking */
847  new_bolt->stats.Dex -= 10;
848  tmp->stats.Dex -= 10; /* less forks from main bolt too */
849  new_bolt->stats.Con += 25*new_dir; /* adjust the left bias */
850  new_bolt->speed_left = -0.1;
851  new_bolt->direction = t_dir;
852  new_bolt->duration++;
853  new_bolt->x = sx;
854  new_bolt->y = sy;
855  new_bolt->stats.dam /= 2; /* reduce daughter bolt damage */
856  new_bolt->stats.dam++;
857  tmp->stats.dam /= 2; /* reduce father bolt damage */
858  tmp->stats.dam++;
859  new_bolt = insert_ob_in_map(new_bolt, m, op, 0);
860  update_turn_face(new_bolt);
861 }
862 
869 static void check_spell_knockback(object *op) {
870  object *tmp, *tmp2; /* object on the map */
871  int weight_move;
872  int frictionmod = 2; /*poor man's physics - multipy targets weight by this amount */
873 
874  if (!op->weight) { /*shouldn't happen but if cone object has no weight drop out*/
875  /*LOG(llevDebug, "DEBUG: arch weighs nothing\n");*/
876  return;
877  } else {
878  weight_move = op->weight+(op->weight*op->level)/3;
879  /*LOG(llevDebug, "DEBUG: arch weighs %d and masses %d (%s,level %d)\n", op->weight, weight_move, op->name, op->level);*/
880  }
881 
882  for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) {
883  int num_sections = 1;
884 
885  /* don't move DM */
886  if (QUERY_FLAG(tmp, FLAG_WIZ))
887  return;
888 
889  /* don't move parts of objects */
890  if (tmp->head)
891  continue;
892 
893  /* don't move floors or immobile objects */
894  if (QUERY_FLAG(tmp, FLAG_IS_FLOOR) || (!QUERY_FLAG(tmp, FLAG_ALIVE) && QUERY_FLAG(tmp, FLAG_NO_PICK)))
895  continue;
896 
897  /* count the object's sections */
898  for (tmp2 = tmp; tmp2 != NULL; tmp2 = tmp2->more)
899  num_sections++;
900 
901  /* I'm not sure if it makes sense to divide by num_sections - bigger
902  * objects should be harder to move, and we are moving the entire
903  * object, not just the head, so the total weight should be relevant.
904  */
905 
906  /* surface area? -tm */
907 
908  if (tmp->move_type&MOVE_FLYING)
909  frictionmod = 1; /* flying objects loose the friction modifier */
910 
911  if (rndm(0, weight_move-1) > ((tmp->weight/num_sections)*frictionmod)) { /* move it. */
912  /* move_object is really for monsters, but looking at
913  * the move_object function, it appears that it should
914  * also be safe for objects.
915  * This does return if successful or not, but
916  * I don't see us doing anything useful with that information
917  * right now.
918  */
919  move_object(tmp, absdir(op->stats.sp));
920  }
921  }
922 }
int get_map_flags(mapstruct *oldmap, mapstruct **newmap, sint16 x, sint16 y, sint16 *nx, sint16 *ny)
Definition: map.c:330
archetype * find_archetype(const char *name)
Definition: arch.c:700
#define FLAG_IS_FLOOR
Definition: define.h:599
MoveType move_type
Definition: object.h:277
#define SP_BOLT
Definition: spells.h:106
signed short sint16
Definition: global.h:72
void enter_exit(object *op, object *exit_ob)
Definition: server.c:740
void save_throw_object(object *op, uint32 type, object *originator)
Definition: attack.c:171
void merge_spell(object *op, sint16 x, sint16 y)
Definition: object.c:1792
sstring add_refcount(sstring str)
Definition: shstr.c:202
#define SPLINT
Definition: spells.h:192
#define SP_CONE
Definition: spells.h:109
method_ret common_pre_ob_move_on(object *trap, object *victim, object *originator)
Definition: common_apply.c:51
sint8 range
Definition: object.h:256
uint16 material
Definition: object.h:198
int fire_arch_from_position(object *op, object *caster, sint16 x, sint16 y, int dir, object *spell)
Definition: spell_util.c:653
object * object_get_env_recursive(object *op)
Definition: object.c:339
static void move_bolt(object *op)
Definition: spell_effect.c:158
#define METHOD_ERROR
Definition: ob_methods.h:44
int fire_bullet(object *op, object *caster, int dir, object *spob)
Definition: spell_attack.c:301
#define SP_MOVING_BALL
Definition: spells.h:137
#define SP_AURA
Definition: spells.h:148
void free_string(sstring str)
Definition: shstr.c:272
#define MSG_TYPE_SPELL_FAILURE
Definition: newclient.h:548
#define SET_ANIMATION(ob, newanim)
Definition: global.h:247
sint16 duration
Definition: object.h:254
short freearr_x[SIZEOFFREE]
Definition: object.c:75
#define DIRX(xyz)
Definition: define.h:785
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: standalone.c:171
static void animate_bomb(object *op)
Definition: spell_effect.c:434
uint8 subtype
Definition: object.h:190
#define MSG_TYPE_SPELL
Definition: newclient.h:333
void check_spell_expiry(object *spell)
Definition: spell_util.c:1854
struct obj * above
Definition: object.h:146
method_ret ob_process(object *op)
Definition: ob_methods.c:79
static void explosion(object *op)
Definition: spell_effect.c:317
sint16 x
Definition: object.h:179
sint16 sp
Definition: living.h:83
void cone_drop(object *op)
Definition: spell_attack.c:369
#define FLAG_REFLECTING
Definition: define.h:558
int reflwall(mapstruct *m, int x, int y, object *sp_op)
Definition: spell_util.c:490
struct archt * other_arch
Definition: object.h:264
int absdir(int d)
Definition: object.c:3417
Definition: object.h:321
int ok_to_put_more(mapstruct *m, sint16 x, sint16 y, object *op, uint32 immune_stop)
Definition: spell_util.c:555
#define SP_WORD_OF_RECALL
Definition: spells.h:120
#define PLAYER
Definition: define.h:113
sint8 Con
Definition: living.h:78
#define DIRY(xyz)
Definition: define.h:786
short freearr_y[SIZEOFFREE]
Definition: object.c:81
void check_bullet(object *op)
Definition: spell_attack.c:242
char method_ret
Definition: ob_methods.h:41
int rndm(int min, int max)
Definition: utils.c:174
static void move_aura(object *aura)
Definition: spell_effect.c:748
uint32 tag_t
Definition: object.h:40
void remove_ob(object *op)
Definition: object.c:1515
sint16 maxhp
Definition: living.h:82
#define SP_BULLET
Definition: spells.h:107
static void check_spell_knockback(object *op)
Definition: spell_effect.c:869
#define FLAG_ALIVE
Definition: define.h:526
struct obj * spell
Definition: object.h:259
float speed_left
Definition: object.h:182
const char * materialname
Definition: object.h:197
void register_move_on(int ob_type, move_on_func method)
Definition: ob_types.c:106
sint32 weight
Definition: object.h:216
#define METHOD_OK
Definition: ob_methods.h:42
#define SP_BOMB
Definition: spells.h:110
struct mapdef * map
Definition: object.h:155
#define MOVE_FLYING
Definition: define.h:703
static void move_swarm_spell(object *op)
Definition: spell_effect.c:678
sint16 dam
Definition: living.h:87
const char * name
Definition: object.h:167
struct obj * env
Definition: object.h:151
#define SPELL
Definition: define.h:283
int hit_map(object *op, int dir, uint32 type, int full_hit)
Definition: attack.c:292
object * get_owner(object *op)
Definition: object.c:524
static method_ret spell_effect_type_move_on(ob_methods *context, object *trap, object *victim, object *originator)
Definition: spell_effect.c:67
#define OB_TYPE_MOVE_BLOCK(ob1, type)
Definition: define.h:740
#define P_OUT_OF_MAP
Definition: map.h:272
#define GET_MAP_MOVE_BLOCK(M, X, Y)
Definition: map.h:213
sint16 y
Definition: object.h:179
float speed
Definition: object.h:181
int on_same_map(const object *op1, const object *op2)
Definition: map.c:2609
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
uint8 state
Definition: object.h:200
#define FLAG_WIZ
Definition: define.h:527
object * insert_ob_in_ob(object *op, object *where)
Definition: object.c:2510
#define SP_SWARM
Definition: spells.h:138
object * get_object(void)
Definition: object.c:921
void explode_bullet(object *op)
Definition: spell_attack.c:144
object * insert_ob_in_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1992
const char * skill
Definition: object.h:174
void copy_owner(object *op, object *clone)
Definition: object.c:614
static void move_cone(object *op)
Definition: spell_effect.c:363
static void move_bullet(object *op)
Definition: spell_effect.c:261
void common_post_ob_move_on(object *trap, object *victim, object *originator)
Definition: common_apply.c:88
static method_ret spell_effect_type_process(ob_methods *context, object *op)
Definition: spell_effect.c:108
uint32 attacktype
Definition: object.h:193
sint8 direction
Definition: object.h:185
static void forklightning(object *op, object *tmp)
Definition: spell_effect.c:816
#define NUM_ANIMATIONS(ob)
Definition: global.h:255
void update_turn_face(object *op)
Definition: object.c:990
tag_t count
Definition: object.h:157
living stats
Definition: object.h:219
#define FLAG_WIZCAST
Definition: define.h:586
sint8 Dex
Definition: living.h:78
#define SPELL_EFFECT
Definition: define.h:284
#define SP_MAGIC_MISSILE
Definition: spells.h:113
int move_object(object *op, int dir)
Definition: move.c:53
int out_of_map(mapstruct *m, int x, int y)
Definition: map.c:2300
#define FLAG_LIFESAVE
Definition: define.h:602
void register_process(int ob_type, process_func method)
Definition: ob_types.c:88
static void move_missile(object *op)
Definition: spell_effect.c:492
void init_type_spell_effect(void)
Definition: spell_effect.c:54
void update_ob_speed(object *op)
Definition: object.c:1008
#define AT_MAGIC
Definition: attack.h:105
#define GET_MAP_OB(M, X, Y)
Definition: map.h:193
#define NDI_UNIQUE
Definition: newclient.h:219
struct obj * head
Definition: object.h:154
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
#define was_destroyed(op, old_tag)
Definition: object.h:94
void copy_object(object *op2, object *op)
Definition: object.c:758
void free_object(object *ob)
Definition: object.c:1238
Definition: map.h:346
#define P_IS_ALIVE
Definition: map.h:258
#define FLAG_NO_PICK
Definition: define.h:535
int spell_find_dir(mapstruct *m, int x, int y, object *exclude)
Definition: spell_util.c:852
sint16 level
Definition: object.h:202
#define SP_EXPLOSION
Definition: spells.h:108
struct obj * more
Definition: object.h:153
object * arch_to_object(archetype *at)
Definition: arch.c:576
sint32 value
Definition: object.h:201
static void move_ball_spell(object *op)
Definition: spell_effect.c:568
int hit_player(object *op, int dam, object *hitter, uint32 type, int full_hit)
Definition: attack.c:1868
#define P_NO_CLERIC
Definition: map.h:259
uint8 type
Definition: object.h:189
static void execute_word_of_recall(object *op)
Definition: spell_effect.c:546
#define FLAG_IS_TURNABLE
Definition: define.h:552