ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.80
Committed: Thu Jan 1 20:49:48 2009 UTC (15 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_75
Changes since 1.79: +2 -2 lines
Log Message:
slim down perl interface

File Contents

# Content
1 /*
2 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 *
4 * Copyright (©) 2005,2006,2007,2008 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 * Copyright (©) 2002-2003,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Deliantra is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <support@deliantra.net>
22 */
23
24 /* This file contains all the spell attack code. Grouping this code
25 * together should hopefully make it easier to find the relevent bits
26 * of code
27 */
28
29 #include <global.h>
30 #include <object.h>
31 #include <living.h>
32 #include <sproto.h>
33 #include <spells.h>
34 #include <sounds.h>
35
36 /* this function checks to see if a spell pushes objects as well
37 * as flies over and damages them (only used for cones for now)
38 * but moved here so it could be applied to bolts too
39 * op is the spell object.
40 */
41 void
42 check_spell_knockback (object *op)
43 {
44 int weight_move;
45 int frictionmod = 2; /*poor man's physics - multipy targets weight by this amount */
46
47 if (!op->weight)
48 { /*shouldn't happen but if cone object has no weight drop out */
49 /*LOG (llevDebug, "DEBUG: arch weighs nothing\n"); */
50 return;
51 }
52 else
53 {
54 weight_move = op->weight + (op->weight * op->level) / 3;
55 /*LOG (llevDebug, "DEBUG: arch weighs %d and masses %d (%s,level %d)\n", op->weight,weight_move,op->name,op->level); */
56 }
57
58 for (object *tmp = op->ms ().bot; tmp; tmp = tmp->above)
59 {
60 int num_sections = 1;
61
62 /* don't move DM */
63 if (QUERY_FLAG (tmp, FLAG_WIZ))
64 return;
65
66 /* don't move parts of objects */
67 if (tmp->head)
68 continue;
69
70 /* don't move floors or immobile objects */
71 if (QUERY_FLAG (tmp, FLAG_IS_FLOOR) || (!QUERY_FLAG (tmp, FLAG_ALIVE) && QUERY_FLAG (tmp, FLAG_NO_PICK)))
72 continue;
73
74 /* count the object's sections */
75 for (object *tmp2 = tmp; tmp2; tmp2 = tmp2->more)
76 num_sections++;
77
78 /* I'm not sure if it makes sense to divide by num_sections - bigger
79 * objects should be harder to move, and we are moving the entire
80 * object, not just the head, so the total weight should be relevant.
81 */
82
83 /* surface area? -tm */
84
85 if (tmp->move_type & MOVE_FLYING)
86 frictionmod = 1; /* flying objects loose the friction modifier */
87
88 if (rndm (0, weight_move - 1) > ((tmp->weight / num_sections) * frictionmod))
89 { /* move it. */
90 /* move_object is really for monsters, but looking at
91 * the move_object function, it appears that it should
92 * also be safe for objects.
93 * This does return if successful or not, but
94 * I don't see us doing anything useful with that information
95 * right now.
96 */
97 move_object (tmp, absdir (op->stats.sp));
98 }
99
100 }
101 }
102
103 /***************************************************************************
104 *
105 * BOLT CODE
106 *
107 ***************************************************************************/
108
109 /* Causes op to fork. op is the original bolt, tmp
110 * is the first piece of the fork.
111 */
112 void
113 forklightning (object *op, object *tmp)
114 {
115 int new_dir = 1; /* direction or -1 for left, +1 for right 0 if no new bolt */
116 int t_dir; /* stores temporary dir calculation */
117 maptile *m;
118 sint16 sx, sy;
119 object *new_bolt;
120
121 /* pick a fork direction. tmp->stats.Con is the left bias
122 * i.e., the chance in 100 of forking LEFT
123 * Should start out at 50, down to 25 for one already going left
124 * down to 0 for one going 90 degrees left off original path
125 */
126
127 if (rndm (0, 99) < tmp->stats.Con) /* fork left */
128 new_dir = -1;
129
130 /* check the new dir for a wall and in the map */
131 t_dir = absdir (tmp->direction + new_dir);
132
133 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)
134 return;
135
136 if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (m, sx, sy)))
137 return;
138
139 /* OK, we made a fork */
140 new_bolt = tmp->clone ();
141
142 /* reduce chances of subsequent forking */
143 new_bolt->stats.Dex -= 10;
144 tmp->stats.Dex -= 10; /* less forks from main bolt too */
145 new_bolt->stats.Con += 25 * new_dir; /* adjust the left bias */
146 new_bolt->speed_left = -0.1f;
147 new_bolt->direction = t_dir;
148 new_bolt->duration++;
149 new_bolt->stats.dam /= 2; /* reduce daughter bolt damage */
150 new_bolt->stats.dam++;
151 tmp->stats.dam /= 2; /* reduce father bolt damage */
152 tmp->stats.dam++;
153
154 if ((new_bolt = m->insert (new_bolt, sx, sy, op)))
155 update_turn_face (new_bolt);
156 }
157
158 /* move_bolt: moves bolt 'op'. Basically, it just advances a space,
159 * and checks for various things that may stop it.
160 */
161 void
162 move_bolt (object *op)
163 {
164 int mflags;
165 sint16 x, y;
166 maptile *m;
167
168 if (--op->duration < 0)
169 {
170 op->drop_and_destroy ();
171 return;
172 }
173
174 hit_map (op, 0, op->attacktype, 1);
175
176 if (!op->direction)
177 return;
178
179 if (--op->range < 0)
180 op->range = 0;
181 else
182 {
183 x = op->x + DIRX (op);
184 y = op->y + DIRY (op);
185 m = op->map;
186 mflags = get_map_flags (m, &m, x, y, &x, &y);
187
188 if (mflags & P_OUT_OF_MAP)
189 return;
190
191 /* We are about to run into something - we may bounce */
192 /* Calling reflwall is pretty costly, as it has to look at all the objects
193 * on the space. So only call reflwall if we think the data it returns
194 * will be useful.
195 */
196 if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y)) || ((mflags & P_IS_ALIVE) && reflwall (m, x, y, op)))
197 {
198 if (!QUERY_FLAG (op, FLAG_REFLECTING))
199 return;
200
201 /* Since walls don't run diagonal, if the bolt is in
202 * one of 4 main directions, it just reflects back in the
203 * opposite direction. However, if the bolt is travelling
204 * on the diagonal, it is trickier - eg, a bolt travelling
205 * northwest bounces different if it hits a north/south
206 * wall (bounces to northeast) vs an east/west (bounces
207 * to the southwest.
208 */
209 if (op->direction & 1)
210 op->direction = absdir (op->direction + 4);
211 else
212 {
213 int left, right;
214 int mflags;
215
216 /* Need to check for P_OUT_OF_MAP: if the bolt is tavelling
217 * over a corner in a tiled map, it is possible that
218 * op->direction is within an adjacent map but either
219 * op->direction-1 or op->direction+1 does not exist.
220 */
221 mflags = get_map_flags (op->map, &m, op->x + freearr_x[absdir (op->direction - 1)],
222 op->y + freearr_y[absdir (op->direction - 1)], &x, &y);
223
224 left = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y));
225
226 mflags = get_map_flags (op->map, &m, op->x + freearr_x[absdir (op->direction + 1)],
227 op->y + freearr_y[absdir (op->direction + 1)], &x, &y);
228 right = (mflags & P_OUT_OF_MAP) ? 0 : OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y));
229
230 if (left == right)
231 op->direction = absdir (op->direction + 4);
232 else if (left)
233 op->direction = absdir (op->direction + 2);
234 else if (right)
235 op->direction = absdir (op->direction - 2);
236 }
237
238 update_turn_face (op); /* A bolt *must* be IS_TURNABLE */
239 return;
240 }
241 else
242 { /* Create a copy of this object and put it ahead */
243 object *tmp = op->clone ();
244
245 m->insert (tmp, x, y, op);
246 tmp->speed_left = -0.1f;
247 /* To make up for the decrease at the top of the function */
248 tmp->duration++;
249
250 /* New forking code. Possibly create forks of this object
251 * going off in other directions.
252 */
253 if (tmp->stats.Dex && rndm (0, 99) < tmp->stats.Dex)
254 forklightning (op, tmp); /* stats.Dex % of forking */
255
256 /* In this way, the object left behind sticks on the space, but
257 * doesn't create any bolts that continue to move onward.
258 */
259 op->range = 0;
260 } /* copy object and move it along */
261 } /* if move bolt along */
262 }
263
264 /* fire_bolt
265 * object op (cast from caster) files a bolt in dir.
266 * spob is the spell object for the bolt.
267 * we remove the magic flag - that can be derived from
268 * spob->attacktype.
269 * This function sets up the appropriate owner and skill
270 * pointers.
271 */
272 int
273 fire_bolt (object *op, object *caster, int dir, object *spob, object *skill)
274 {
275 object *tmp = NULL;
276 int mflags;
277
278 if (!spob->other_arch)
279 return 0;
280
281 tmp = arch_to_object (spob->other_arch);
282 if (tmp == NULL)
283 return 0;
284
285 /* peterm: level dependency for bolts */
286 tmp->stats.dam = spob->stats.dam + SP_level_dam_adjust (caster, spob);
287 tmp->attacktype = spob->attacktype;
288
289 if (spob->slaying)
290 tmp->slaying = spob->slaying;
291
292 tmp->range = spob->range + SP_level_range_adjust (caster, spob);
293 tmp->duration = spob->duration + SP_level_duration_adjust (caster, spob);
294 tmp->stats.Dex = spob->stats.Dex;
295 tmp->stats.Con = spob->stats.Con;
296
297 tmp->direction = dir;
298 if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE))
299 SET_ANIMATION (tmp, dir);
300
301 tmp->set_owner (op);
302 set_spell_skill (op, caster, spob, tmp);
303
304 tmp->x = op->x + DIRX (tmp);
305 tmp->y = op->y + DIRY (tmp);
306 tmp->map = op->map;
307
308 maptile *newmap;
309 mflags = get_map_flags (tmp->map, &newmap, tmp->x, tmp->y, &tmp->x, &tmp->y);
310 if (mflags & P_OUT_OF_MAP)
311 {
312 tmp->drop_and_destroy ();
313 return 0;
314 }
315
316 tmp->map = newmap;
317
318 if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (tmp->map, tmp->x, tmp->y)))
319 {
320 if (!QUERY_FLAG (tmp, FLAG_REFLECTING))
321 {
322 tmp->drop_and_destroy ();
323 return 0;
324 }
325
326 tmp->x = op->x;
327 tmp->y = op->y;
328 tmp->direction = absdir (tmp->direction + 4);
329 tmp->map = op->map;
330 }
331
332 if ((tmp = tmp->insert_at (tmp, op)))
333 move_bolt (tmp);
334
335 return 1;
336 }
337
338 /***************************************************************************
339 *
340 * BULLET/BALL CODE
341 *
342 ***************************************************************************/
343
344 /* expands an explosion. op is a piece of the
345 * explosion - this expands it in the different directions.
346 * At least that is what I think this does.
347 */
348 void
349 explosion (object *op)
350 {
351 maptile *m = op->map;
352 int i;
353
354 if (--op->duration < 0)
355 {
356 op->destroy ();
357 return;
358 }
359
360 hit_map (op, 0, op->attacktype, 0);
361
362 if (op->range > 0)
363 {
364 for (i = 1; i < 9; i++)
365 {
366 sint16 dx, dy;
367
368 dx = op->x + freearr_x[i];
369 dy = op->y + freearr_y[i];
370
371 /* ok_to_put_more already does things like checks for walls,
372 * out of map, etc.
373 */
374 if (ok_to_put_more (op->map, dx, dy, op, op->attacktype))
375 {
376 object *tmp = op->clone ();
377
378 tmp->state = 0;
379 tmp->speed_left = -0.21f;
380 tmp->range--;
381 tmp->value = 0;
382
383 m->insert (tmp, dx, dy, op);
384 }
385 }
386 }
387 }
388
389 /* Causes an object to explode, eg, a firebullet,
390 * poison cloud ball, etc. op is the object to
391 * explode.
392 */
393 void
394 explode_bullet (object *op)
395 {
396 object *tmp, *owner;
397
398 if (!op->other_arch)
399 {
400 LOG (llevError, "BUG: explode_bullet(): op without other_arch\n");
401 op->destroy ();
402 return;
403 }
404
405 if (op->env)
406 {
407 object *env = op->outer_env ();
408
409 if (!env->map || out_of_map (env->map, env->x, env->y))
410 {
411 LOG (llevError, "BUG: explode_bullet(): env out of map\n");
412 op->destroy ();
413 return;
414 }
415
416 op->insert_at (env, op, INS_NO_MERGE | INS_NO_WALK_ON);
417 }
418 else if (out_of_map (op->map, op->x, op->y))
419 {
420 LOG (llevError, "BUG: explode_bullet(): op out of map\n");
421 op->destroy ();
422 return;
423 }
424
425 // elmex Tue Aug 15 17:46:51 CEST 2006: Prevent explosions of any kind on safe maps
426 // NOTE: If this breaks something important: remove this. I can't think of anything
427 // bad at the moment that might happen from this.
428 if (get_map_flags (op->map, NULL, op->x, op->y, NULL, NULL) & P_SAFE)
429 {
430 op->destroy ();
431 return;
432 }
433
434 if (op->attacktype)
435 {
436 hit_map (op, 0, op->attacktype, 1);
437
438 if (op->destroyed ())
439 return;
440 }
441
442 /* other_arch contains what this explodes into */
443 tmp = arch_to_object (op->other_arch);
444
445 tmp->set_owner (op);
446 tmp->skill = op->skill;
447
448 owner = op->owner;
449
450 if ((tmp->attacktype & AT_HOLYWORD
451 || tmp->attacktype & AT_GODPOWER)
452 && owner
453 && !tailor_god_spell (tmp, owner))
454 {
455 op->destroy ();
456 return;
457 }
458
459 /* special for bombs - it actually has sane values for these */
460 if (op->type == SPELL_EFFECT && op->subtype == SP_BOMB)
461 {
462 tmp->attacktype = op->attacktype;
463 tmp->range = op->range;
464 tmp->stats.dam = op->stats.dam;
465 tmp->duration = op->duration;
466 }
467 else
468 {
469 if (op->attacktype & AT_MAGIC)
470 tmp->attacktype |= AT_MAGIC;
471
472 /* Spell doc describes what is going on here */
473 tmp->stats.dam = op->dam_modifier;
474 tmp->range = op->stats.maxhp;
475 tmp->duration = op->stats.hp;
476 /* Used for spell tracking - just need a unique val for this spell -
477 * the count of the parent should work fine.
478 */
479 tmp->stats.maxhp = op->count;
480 }
481
482 /* Set direction of cone explosion */
483 if (tmp->type == SPELL_EFFECT && tmp->subtype == SP_CONE)
484 tmp->stats.sp = op->direction;
485
486 /* Prevent recursion */
487 op->move_on = 0;
488
489 tmp->insert_at (op, op);
490 tmp->play_sound (tmp->sound);
491
492 /* remove the firebullet */
493 op->destroy ();
494 }
495
496 /* checks to see what op should do, given the space it is on
497 * (eg, explode, damage player, etc)
498 */
499 void
500 check_bullet (object *op)
501 {
502 object *tmp;
503 int dam, mflags;
504 maptile *m;
505 sint16 sx, sy;
506
507 mflags = get_map_flags (op->map, &m, op->x, op->y, &sx, &sy);
508
509 if (!(mflags & P_IS_ALIVE) && !OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, sx, sy)))
510 return;
511
512 if (op->other_arch)
513 {
514 /* explode object will also remove op */
515 explode_bullet (op);
516 return;
517 }
518
519 /* If nothing alive on this space, no reason to do anything further */
520 if (!(mflags & P_IS_ALIVE))
521 return;
522
523 for (tmp = op->ms ().bot; tmp; tmp = tmp->above)
524 {
525 if (QUERY_FLAG (tmp, FLAG_ALIVE))
526 {
527 dam = hit_player (tmp, op->stats.dam, op, op->attacktype, 1);
528
529 // TODO: can't understand the following if's
530 if (op->destroyed () || !tmp->destroyed () || (op->stats.dam -= dam) < 0)
531 {
532 if (!QUERY_FLAG (op, FLAG_REMOVED))
533 {
534 op->destroy ();
535 return;
536 }
537 }
538 }
539 }
540 }
541
542 /* Basically, we move 'op' one square, and if it hits something,
543 * call check_bullet.
544 * This function is only applicable to bullets, but not to all
545 * fired arches (eg, bolts).
546 */
547 void
548 move_bullet (object *op)
549 {
550 sint16 new_x, new_y;
551 int mflags;
552 maptile *m;
553
554 #if 0
555 /* We need a better general purpose way to do this */
556
557 /* peterm: added to make comet leave a trail of burnouts
558 it's an unadulterated hack, but the effect is cool. */
559 if (op->stats.sp == SP_METEOR)
560 {
561 replace_insert_ob_in_map ("fire_trail", op);
562 if (op->destroyed ())
563 return;
564 } /* end addition. */
565 #endif
566
567 /* Reached the end of its life - remove it */
568 if (--op->range <= 0)
569 {
570 if (op->other_arch)
571 explode_bullet (op);
572 else
573 op->destroy ();
574
575 return;
576 }
577
578 new_x = op->x + DIRX (op);
579 new_y = op->y + DIRY (op);
580 m = op->map;
581 mflags = get_map_flags (m, &m, new_x, new_y, &new_x, &new_y);
582
583 if (mflags & P_OUT_OF_MAP)
584 {
585 op->destroy ();
586 return;
587 }
588
589 if (!op->direction || OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, new_x, new_y)))
590 {
591 if (op->other_arch)
592 explode_bullet (op);
593 else
594 op->destroy ();
595
596 return;
597 }
598
599 if (!(op = m->insert (op, new_x, new_y, op)))
600 return;
601
602 if (reflwall (op->map, op->x, op->y, op))
603 {
604 op->direction = absdir (op->direction + 4);
605 update_turn_face (op);
606 }
607 else
608 check_bullet (op);
609 }
610
611 /* fire_bullet
612 * object op (cast from caster) files a bolt in dir.
613 * spob is the spell object for the bolt.
614 * we remove the magic flag - that can be derived from
615 * spob->attacktype.
616 * This function sets up the appropriate owner and skill
617 * pointers.
618 */
619 int
620 fire_bullet (object *op, object *caster, int dir, object *spob)
621 {
622 object *tmp = NULL;
623 int mflags;
624
625 if (!spob->other_arch)
626 return 0;
627
628 tmp = spob->other_arch->instance ();
629 if (!tmp)
630 return 0;
631
632 /* peterm: level dependency for bolts */
633 tmp->stats.dam = spob->stats.dam + SP_level_dam_adjust (caster, spob);
634 tmp->attacktype = spob->attacktype;
635 if (spob->slaying)
636 tmp->slaying = spob->slaying;
637
638 tmp->range = 50;
639
640 /* Need to store duration/range for the ball to use */
641 tmp->stats.hp = spob->duration + SP_level_duration_adjust (caster, spob);
642 tmp->stats.maxhp = spob->range + SP_level_range_adjust (caster, spob);
643 tmp->dam_modifier = spob->stats.food + SP_level_dam_adjust (caster, spob);
644
645 tmp->direction = dir;
646 if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE))
647 SET_ANIMATION (tmp, dir);
648
649 tmp->set_owner (op);
650 set_spell_skill (op, caster, spob, tmp);
651
652 tmp->x = op->x + freearr_x[dir];
653 tmp->y = op->y + freearr_y[dir];
654 tmp->map = op->map;
655
656 maptile *newmap;
657 mflags = get_map_flags (tmp->map, &newmap, tmp->x, tmp->y, &tmp->x, &tmp->y);
658 if (mflags & P_OUT_OF_MAP)
659 {
660 tmp->destroy ();
661 return 0;
662 }
663
664 tmp->map = newmap;
665
666 if (OB_TYPE_MOVE_BLOCK (tmp, GET_MAP_MOVE_BLOCK (tmp->map, tmp->x, tmp->y)))
667 {
668 if (!QUERY_FLAG (tmp, FLAG_REFLECTING))
669 {
670 tmp->destroy ();
671 return 0;
672 }
673
674 tmp->x = op->x;
675 tmp->y = op->y;
676 tmp->direction = absdir (tmp->direction + 4);
677 tmp->map = op->map;
678 }
679
680 if ((tmp = tmp->insert_at (tmp, op)))
681 check_bullet (tmp);
682
683 return 1;
684 }
685
686 /*****************************************************************************
687 *
688 * CONE RELATED FUNCTIONS
689 *
690 *****************************************************************************/
691
692 /* drops an object based on what is in the cone's "other_arch" */
693 void
694 cone_drop (object *op)
695 {
696 object *new_ob = arch_to_object (op->other_arch);
697
698 new_ob->level = op->level;
699 new_ob->set_owner (op->owner);
700
701 /* preserve skill ownership */
702 if (op->skill && op->skill != new_ob->skill)
703 new_ob->skill = op->skill;
704
705 new_ob->insert_at (op, op);
706 }
707
708 /* move_cone: causes cone object 'op' to move a space/hit creatures */
709
710 void
711 move_cone (object *op)
712 {
713 /* if no map then hit_map will crash so just ignore object */
714 if (!op->map)
715 {
716 LOG (llevError, "Tried to move_cone object %s without a map.\n", op->name ? &op->name : "unknown");
717 op->set_speed (0);
718 return;
719 }
720
721 /* lava saves it's life, but not yours :) */
722 if (QUERY_FLAG (op, FLAG_LIFESAVE))
723 {
724 hit_map (op, 0, op->attacktype, 0);
725 return;
726 }
727
728 #if 0
729 /* Disable this - enabling it makes monsters easier, as
730 * when their cone dies when they die.
731 */
732 /* If no owner left, the spell dies out. */
733 if (op->owner == NULL)
734 {
735 op->destroy ();
736 return;
737 }
738 #endif
739
740 hit_map (op, 0, op->attacktype, 0);
741
742 if (!op->is_on_map ())
743 return;
744
745 /* Check to see if we should push anything.
746 * Spell objects with weight push whatever they encounter to some
747 * degree.
748 */
749 if (op->weight)
750 {
751 check_spell_knockback (op);
752
753 if (!op->is_on_map ())
754 return;
755 }
756
757 if (op->duration-- < 0)
758 {
759 op->destroy ();
760 return;
761 }
762 /* Object has hit maximum range, so don't have it move
763 * any further. When the duration above expires,
764 * then the object will get removed.
765 */
766 if (--op->range < 0)
767 {
768 op->range = 0; /* just so it doesn't wrap */
769 return;
770 }
771
772 for (int i = -1; i <= 1; i++)
773 {
774 sint16 x = op->x + freearr_x[absdir (op->stats.sp + i)], y = op->y + freearr_y[absdir (op->stats.sp + i)];
775
776 if (ok_to_put_more (op->map, x, y, op, op->attacktype))
777 {
778 object *tmp = op->clone ();
779
780 tmp->duration = op->duration + 1;
781
782 /* Use for spell tracking - see ok_to_put_more() */
783 tmp->stats.maxhp = op->stats.maxhp;
784
785 op->map->insert (tmp, x, y, op);
786
787 if (tmp->other_arch)
788 cone_drop (tmp);
789 }
790 }
791 }
792
793 /* cast_cone: casts a cone spell.
794 * op: person firing the object.
795 * caster: object casting the spell.
796 * dir: direction to fire in.
797 * spell: spell that is being fired. It uses other_arch for the archetype
798 * to fire.
799 * returns 0 on failure, 1 on success.
800 */
801 int
802 cast_cone (object *op, object *caster, int dir, object *spell)
803 {
804 object *tmp;
805 int i, success = 0, range_min = -1, range_max = 1;
806 maptile *m;
807 sint16 sx, sy;
808 MoveType movetype;
809
810 if (!spell->other_arch)
811 return 0;
812
813 if (op->type == PLAYER && QUERY_FLAG (op, FLAG_UNDEAD) && op->attacktype & AT_TURN_UNDEAD)
814 {
815 new_draw_info (NDI_UNIQUE, 0, op, "Your undead nature prevents you from turning undead!");
816 return 0;
817 }
818
819 if (!dir)
820 {
821 range_min = 0;
822 range_max = 8;
823 }
824
825 /* Need to know what the movetype of the object we are about
826 * to create is, so we can know if the space we are about to
827 * insert it into is blocked.
828 */
829 movetype = spell->other_arch->move_type;
830
831 for (i = range_min; i <= range_max; i++)
832 {
833 sint16 x, y, d;
834
835 /* We can't use absdir here, because it never returns
836 * 0. If this is a rune, we want to hit the person on top
837 * of the trap (d==0). If it is not a rune, then we don't want
838 * to hit that person.
839 */
840 d = (dir + i) % 9;
841
842 /* If it's not a rune, we don't want to blast the caster.
843 * In that case, we have to see - if dir is specified,
844 * turn this into direction 8. If dir is not specified (all
845 * direction) skip - otherwise, one line would do more damage
846 * becase 0 direction will go through 9 directions - necessary
847 * for the rune code.
848 */
849 if (caster->type != RUNE && d == 0)
850 {
851 if (dir != 0)
852 d = 8;
853 else
854 continue;
855 }
856
857 x = op->x + freearr_x[d];
858 y = op->y + freearr_y[d];
859
860 if (get_map_flags (op->map, &m, x, y, &sx, &sy) & P_OUT_OF_MAP)
861 continue;
862
863 if ((movetype & GET_MAP_MOVE_BLOCK (m, sx, sy)) == movetype)
864 continue;
865
866 success = 1;
867 tmp = arch_to_object (spell->other_arch);
868 tmp->set_owner (op);
869 set_spell_skill (op, caster, spell, tmp);
870 tmp->level = casting_level (caster, spell);
871 tmp->attacktype = spell->attacktype;
872
873 /* holy word stuff */
874 if ((tmp->attacktype & AT_HOLYWORD) || (tmp->attacktype & AT_GODPOWER))
875 if (!tailor_god_spell (tmp, op))
876 return 0;
877
878 if (dir)
879 tmp->stats.sp = dir;
880 else
881 tmp->stats.sp = i;
882
883 tmp->range = spell->range + SP_level_range_adjust (caster, spell);
884
885 /* If casting it in all directions, it doesn't go as far */
886 if (dir == 0)
887 {
888 tmp->range /= 4;
889 if (tmp->range < 2 && spell->range >= 2)
890 tmp->range = 2;
891 }
892
893 tmp->stats.dam = spell->stats.dam + SP_level_dam_adjust (caster, spell);
894 tmp->duration = spell->duration + SP_level_duration_adjust (caster, spell);
895
896 /* Special bonus for fear attacks */
897 if (tmp->attacktype & AT_FEAR)
898 {
899 if (caster->type == PLAYER)
900 tmp->duration += fear_bonus[caster->stats.Cha];
901 else
902 tmp->duration += caster->level / 3;
903 }
904
905 if (tmp->attacktype & (AT_HOLYWORD | AT_TURN_UNDEAD))
906 {
907 if (caster->type == PLAYER)
908 tmp->duration += turn_bonus[caster->stats.Wis] / 5;
909 else
910 tmp->duration += caster->level / 3;
911 }
912
913 if (!(tmp->move_type & MOVE_FLY_LOW))
914 LOG (llevDebug, "cast_cone(): arch %s doesn't have flying 1\n", &spell->other_arch->archname);
915
916 if (!tmp->move_on && tmp->stats.dam)
917 LOG (llevDebug, "cast_cone(): arch %s doesn't have move_on set\n", &spell->other_arch->archname);
918
919 m->insert (tmp, sx, sy, op);
920
921 /* This is used for tracking spells so that one effect doesn't hit
922 * a single space too many times.
923 */
924 tmp->stats.maxhp = tmp->count;
925
926 if (tmp->other_arch)
927 cone_drop (tmp);
928 }
929
930 return success;
931 }
932
933 /****************************************************************************
934 *
935 * BOMB related code
936 *
937 ****************************************************************************/
938
939 /* This handles an exploding bomb.
940 * op is the original bomb object.
941 */
942 void
943 animate_bomb (object *op)
944 {
945 if (op->state != NUM_ANIMATIONS (op) - 1)
946 return;
947
948 object *env = op->outer_env ();
949
950 if (op->env)
951 {
952 if (env->map == NULL)
953 return;
954
955 if (!(op = op->insert_at (env, op)))
956 return;
957 }
958
959 // elmex Tue Aug 15 17:46:51 CEST 2006: Prevent bomb from exploding
960 // on a safe map. I don't like this special casing, but it seems to be neccessary
961 // as bombs can be carried.
962 if (get_map_flags (op->map, NULL, op->x, op->y, NULL, NULL) & P_SAFE)
963 {
964 op->destroy ();
965 return;
966 }
967
968 /* This copies a lot of the code from the fire bullet,
969 * but using the cast_bullet isn't really feasible,
970 * so just set up the appropriate values.
971 */
972 if (archetype *at = archetype::find (SPLINT))
973 {
974 for (int i = 1; i < 9; i++)
975 {
976 if (out_of_map (op->map, op->x + freearr_x[i], op->y + freearr_x[i]))
977 continue;
978
979 object *tmp = arch_to_object (at);
980 tmp->direction = i;
981 tmp->range = op->range;
982 tmp->stats.dam = op->stats.dam;
983 tmp->duration = op->duration;
984 tmp->attacktype = op->attacktype;
985 tmp->set_owner (op);
986 if (op->skill && op->skill != tmp->skill)
987 tmp->skill = op->skill;
988
989 if (QUERY_FLAG (tmp, FLAG_IS_TURNABLE))
990 SET_ANIMATION (tmp, i);
991
992 op->map->insert (tmp, op->x + freearr_x[i], op->y + freearr_x[i], op);
993 move_bullet (tmp);
994 }
995 }
996
997 explode_bullet (op);
998 }
999
1000 int
1001 create_bomb (object *op, object *caster, int dir, object *spell)
1002 {
1003 object *tmp;
1004 int mflags;
1005 sint16 dx = op->x + freearr_x[dir], dy = op->y + freearr_y[dir];
1006 maptile *m;
1007
1008 mflags = get_map_flags (op->map, &m, dx, dy, &dx, &dy);
1009
1010 // when creating a bomb below ourself it should always work, even
1011 // when movement is blocked (somehow we got here, somehow we are here,
1012 // so we should also be able to make a bomb here). (originally added
1013 // to fix create bomb traps in doors, which cast with dir=0).
1014 if (dir)
1015 {
1016 if ((mflags & P_OUT_OF_MAP) || (GET_MAP_MOVE_BLOCK (m, dx, dy) & MOVE_WALK))
1017 {
1018 new_draw_info (NDI_UNIQUE, 0, op, "There is something in the way.");
1019 return 0;
1020 }
1021 }
1022
1023 tmp = arch_to_object (spell->other_arch);
1024
1025 /* level dependencies for bomb */
1026 tmp->range = spell->range + SP_level_range_adjust (caster, spell);
1027 tmp->stats.dam = spell->stats.dam + SP_level_dam_adjust (caster, spell);
1028 tmp->duration = spell->duration + SP_level_duration_adjust (caster, spell);
1029 tmp->attacktype = spell->attacktype;
1030
1031 tmp->set_owner (op);
1032 set_spell_skill (op, caster, spell, tmp);
1033
1034 m->insert (tmp, dx, dy, op);
1035 return 1;
1036 }
1037
1038 /****************************************************************************
1039 *
1040 * smite related spell code.
1041 *
1042 ****************************************************************************/
1043
1044 /* get_pointed_target() - this is used by finger of death
1045 * and the 'smite' spells. Returns the pointer to the first
1046 * monster in the direction which is pointed to by op. b.t.
1047 * op is the caster - really only used for the source location.
1048 * dir is the direction to look in.
1049 * range is how far out to look.
1050 * type is the type of spell - either SPELL_MANA or SPELL_GRACE.
1051 * this info is used for blocked magic/unholy spaces.
1052 */
1053 object *
1054 get_pointed_target (object *op, int dir, int range, int type)
1055 {
1056 object *target;
1057 sint16 x, y;
1058 int dist, mflags;
1059 maptile *mp;
1060
1061 if (dir == 0)
1062 return NULL;
1063
1064 for (dist = 1; dist < range; dist++)
1065 {
1066 x = op->x + freearr_x[dir] * dist;
1067 y = op->y + freearr_y[dir] * dist;
1068 mp = op->map;
1069 mflags = get_map_flags (op->map, &mp, x, y, &x, &y);
1070
1071 if (mflags & P_OUT_OF_MAP)
1072 return NULL;
1073 if ((type & SPELL_MANA) && (mflags & P_NO_MAGIC))
1074 return NULL;
1075 if ((type & SPELL_GRACE) && (mflags & P_NO_CLERIC))
1076 return NULL;
1077 if (GET_MAP_MOVE_BLOCK (mp, x, y) & MOVE_FLY_LOW)
1078 return NULL;
1079
1080 if (mflags & P_IS_ALIVE)
1081 for (target = GET_MAP_OB (mp, x, y); target; target = target->above)
1082 if (QUERY_FLAG (target, FLAG_MONSTER))
1083 return target;
1084 }
1085
1086 return NULL;
1087 }
1088
1089 /* cast_smite_arch() - the priest points to a creature and causes
1090 * a 'godly curse' to decend.
1091 * usual params -
1092 * op = player
1093 * caster = object casting the spell.
1094 * dir = direction being cast
1095 * spell = spell object
1096 */
1097 int
1098 cast_smite_spell (object *op, object *caster, int dir, object *spell)
1099 {
1100 object *effect, *target;
1101 object *god = find_god (determine_god (op));
1102 int range;
1103
1104 range = spell->range + SP_level_range_adjust (caster, spell);
1105 target = get_pointed_target (op, dir, 50, spell->stats.grace ? SPELL_GRACE : SPELL_MANA);
1106
1107 /* Bunch of conditions for casting this spell. Note that only
1108 * require a god if this is a cleric spell (requires grace).
1109 * This makes this spell much more general purpose - it can be used
1110 * by wizards also, which is good, because I think this is a very
1111 * interesting spell.
1112 * if it is a cleric spell, you need a god, and the creature
1113 * can't be friendly to your god.
1114 */
1115
1116 if (!target
1117 || target->flag [FLAG_REFL_SPELL]
1118 || (!god && spell->stats.grace)
1119 || (god && target->title == god->name)
1120 || (god && target->race.contains (god->race)))
1121 {
1122 new_draw_info (NDI_UNIQUE, 0, op, "Your request is unheeded.");
1123 return 0;
1124 }
1125
1126 if (spell->other_arch)
1127 effect = arch_to_object (spell->other_arch);
1128 else
1129 return 0;
1130
1131 /* tailor the effect by priest level and worshipped God */
1132 effect->level = casting_level (caster, spell);
1133 effect->attacktype = spell->attacktype;
1134 if (effect->attacktype & (AT_HOLYWORD | AT_GODPOWER))
1135 {
1136 if (tailor_god_spell (effect, op))
1137 new_draw_info_format (NDI_UNIQUE, 0, op, "%s answers your call!", (const char *)determine_god (op));
1138 else
1139 {
1140 new_draw_info (NDI_UNIQUE, 0, op, "Your request is ignored.");
1141 return 0;
1142 }
1143 }
1144
1145 /* size of the area of destruction */
1146 effect->range = spell->range + SP_level_range_adjust (caster, spell);
1147 effect->duration = spell->duration + SP_level_range_adjust (caster, spell);
1148
1149 if (effect->attacktype & AT_DEATH)
1150 {
1151 effect->level = spell->stats.dam + SP_level_dam_adjust (caster, spell);
1152
1153 /* casting death spells at undead isn't a good thing */
1154 if (QUERY_FLAG (target, FLAG_UNDEAD))
1155 {
1156 if (random_roll (0, 2, op, PREFER_LOW))
1157 {
1158 new_draw_info (NDI_UNIQUE, 0, op, "Idiot! Your spell boomerangs!");
1159 effect->x = op->x;
1160 effect->y = op->y;
1161 }
1162 else
1163 {
1164 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s looks stronger!", query_name (target));
1165 target->stats.hp = target->stats.maxhp * 2;
1166 effect->destroy ();
1167 return 0;
1168 }
1169 }
1170 }
1171 else
1172 {
1173 /* how much woe to inflict :) */
1174 effect->stats.dam = spell->stats.dam + SP_level_dam_adjust (caster, spell);
1175 }
1176
1177 effect->set_owner (op);
1178 set_spell_skill (op, caster, spell, effect);
1179
1180 /* ok, tell it where to be, and insert! */
1181 effect->insert_at (target, op);
1182
1183 return 1;
1184 }
1185
1186 /****************************************************************************
1187 *
1188 * MAGIC MISSILE code.
1189 * note that the fire_bullet is used to fire the missile. The
1190 * code here is just to move the missile.
1191 ****************************************************************************/
1192
1193 /* op is a missile that needs to be moved */
1194 void
1195 move_missile (object *op)
1196 {
1197 if (op->range-- <= 0)
1198 {
1199 op->drop_and_destroy ();
1200 return;
1201 }
1202
1203 mapxy pos (op);
1204 pos.move (op->direction);
1205
1206 if (!pos.normalise ())
1207 {
1208 op->destroy ();
1209 return;
1210 }
1211
1212 mapspace &ms = pos.ms ();
1213
1214 if (ms.flags () & P_IS_ALIVE || ms.blocks (op))
1215 {
1216 hit_map (op, op->direction, AT_MAGIC, 1);
1217 /* Basically, missile only hits one thing then goes away.
1218 * we need to remove it if someone hasn't already done so.
1219 */
1220 op->destroy ();
1221 return;
1222 }
1223
1224 if (!op->direction)
1225 {
1226 op->destroy ();
1227 return;
1228 }
1229
1230 int i = spell_find_dir (pos.m, pos.x, pos.y, op->owner);
1231 if (i > 0 && i != op->direction)
1232 {
1233 op->direction = i;
1234 SET_ANIMATION (op, op->direction);
1235 }
1236
1237 pos.insert (op, op);
1238 }
1239
1240 /****************************************************************************
1241 * Destruction
1242 ****************************************************************************/
1243
1244 /* make_object_glow() - currently only makes living objects glow.
1245 * we do this by creating a force and inserting it in the
1246 * object. if time is 0, the object glows permanently. To truely
1247 * make this work for non-living objects, we would have to
1248 * give them the capability to have an inventory. b.t.
1249 */
1250 int
1251 make_object_glow (object *op, int radius, int time)
1252 {
1253 /* some things are unaffected... */
1254 if (op->path_denied & PATH_LIGHT)
1255 return 0;
1256
1257 object *tmp = get_archetype (FORCE_NAME);
1258 tmp->speed = 0.01;
1259 tmp->stats.food = time;
1260 SET_FLAG (tmp, FLAG_IS_USED_UP);
1261 tmp->glow_radius = min (MAX_LIGHT_RADIUS, radius);
1262 tmp = insert_ob_in_ob (tmp, op);
1263
1264 if (tmp->glow_radius > op->glow_radius)
1265 op->glow_radius = tmp->glow_radius;
1266
1267 return 1;
1268 }
1269
1270 int
1271 cast_destruction (object *op, object *caster, object *spell_ob)
1272 {
1273 int range = spell_ob->range + SP_level_range_adjust (caster, spell_ob);
1274 int dam = spell_ob->stats.dam + SP_level_dam_adjust (caster, spell_ob);
1275 int dur = spell_ob->duration + SP_level_duration_adjust (caster, spell_ob);
1276
1277 bool friendly = op->flag [FLAG_FRIENDLY] || op->is_player ();
1278
1279 /* destruction doesn't use another spell object, so we need
1280 * update op's skill pointer so that exp is properly awarded.
1281 */
1282 const shstr skill = op->skill;
1283
1284 if (caster == op)
1285 op->skill = spell_ob->skill;
1286 else if (caster->skill)
1287 op->skill = caster->skill;
1288 else
1289 op->skill = 0;
1290
1291 op->change_skill (find_skill_by_name (op, op->skill));
1292
1293 unordered_mapwalk (op, -range, -range, range, range)
1294 {
1295 mapspace &ms = m->at (nx, ny);
1296
1297 if (ms.flags () & P_IS_ALIVE)
1298 for (object *tmp = ms.bot; tmp; tmp = tmp->above)
1299 if (tmp->flag [FLAG_ALIVE] || tmp->is_player ())
1300 {
1301 tmp = tmp->head_ ();
1302
1303 if ((friendly && !tmp->flag [FLAG_FRIENDLY] && !tmp->is_player ())
1304 || (!friendly && (tmp->flag [FLAG_FRIENDLY] || tmp->is_player ())))
1305 {
1306 if (spell_ob->subtype == SP_DESTRUCTION)
1307 {
1308 hit_player (tmp, dam, op, spell_ob->attacktype, 0);
1309
1310 if (spell_ob->other_arch)
1311 m->insert (arch_to_object (spell_ob->other_arch), nx, ny, op);
1312 }
1313 else if (spell_ob->subtype == SP_FAERY_FIRE && tmp->resist [ATNR_MAGIC] != 100)
1314 {
1315 if (make_object_glow (tmp, 1, dur) && spell_ob->other_arch)
1316 m->insert (arch_to_object (spell_ob->other_arch), nx, ny, op);
1317 }
1318 }
1319 }
1320 }
1321
1322 op->skill = skill;
1323 return 1;
1324 }
1325
1326 /***************************************************************************
1327 *
1328 * CURSE
1329 *
1330 ***************************************************************************/
1331
1332 int
1333 cast_curse (object *op, object *caster, object *spell_ob, int dir)
1334 {
1335 object *god = find_god (determine_god (op));
1336 object *tmp, *force;
1337
1338 tmp = get_pointed_target (op, (dir == 0) ? op->direction : dir, spell_ob->range, SPELL_GRACE);
1339 if (!tmp)
1340 {
1341 new_draw_info (NDI_UNIQUE, 0, op, "There is no one in that direction to curse.");
1342 return 0;
1343 }
1344
1345 tmp = tmp->head_ ();
1346
1347 /* If we've already got a force of this type, don't add a new one. */
1348 for (force = tmp->inv; force; force = force->below)
1349 {
1350 if (force->type == FORCE && force->subtype == FORCE_CHANGE_ABILITY)
1351 {
1352 if (force->name == spell_ob->name)
1353 {
1354 break;
1355 }
1356 else if (spell_ob->race && spell_ob->race == force->name)
1357 {
1358 new_draw_info_format (NDI_UNIQUE, 0, op, "You can not cast %s while %s is in effect", &spell_ob->name, &force->name_pl);
1359 return 0;
1360 }
1361 }
1362 }
1363
1364 if (!force)
1365 {
1366 force = get_archetype (FORCE_NAME);
1367 force->subtype = FORCE_CHANGE_ABILITY;
1368
1369 if (spell_ob->race)
1370 force->name = spell_ob->race;
1371 else
1372 force->name = spell_ob->name;
1373
1374 force->name_pl = spell_ob->name;
1375
1376 }
1377 else
1378 {
1379 int duration;
1380
1381 duration = spell_ob->duration + SP_level_duration_adjust (caster, spell_ob) * 50;
1382 if (duration > force->duration)
1383 {
1384 force->duration = duration;
1385 new_draw_info (NDI_UNIQUE, 0, op, "You recast the spell while in effect.");
1386 }
1387 else
1388 new_draw_info (NDI_UNIQUE, 0, op, "Recasting the spell had no effect.");
1389
1390 return 1;
1391 }
1392
1393 force->duration = spell_ob->duration + SP_level_duration_adjust (caster, spell_ob) * 50;
1394 force->speed = 1.f;
1395 force->speed_left = -1.f;
1396 SET_FLAG (force, FLAG_APPLIED);
1397
1398 if (god)
1399 {
1400 if (spell_ob->last_grace)
1401 force->path_repelled = god->path_repelled;
1402 if (spell_ob->last_grace)
1403 force->path_denied = god->path_denied;
1404 new_draw_info_format (NDI_UNIQUE, 0, tmp, "You are a victim of %s's curse!", &god->name);
1405 }
1406 else
1407 new_draw_info (NDI_UNIQUE, 0, op, "Your curse seems empty.");
1408
1409
1410 if (tmp != op && op->type == PLAYER)
1411 new_draw_info_format (NDI_UNIQUE, 0, op, "You curse %s!", &tmp->name);
1412
1413 force->stats.ac = spell_ob->stats.ac;
1414 force->stats.wc = spell_ob->stats.wc;
1415
1416 change_abil (tmp, force); /* Mostly to display any messages */
1417 insert_ob_in_ob (force, tmp);
1418 tmp->update_stats ();
1419 return 1;
1420
1421 }
1422
1423 /**********************************************************************
1424 * mood change
1425 * Arguably, this may or may not be an attack spell. But since it
1426 * effects monsters, it seems best to put it into this file
1427 ***********************************************************************/
1428
1429 /* This covers the various spells that change the moods of monsters -
1430 * makes them angry, peacful, friendly, etc.
1431 */
1432 int
1433 mood_change (object *op, object *caster, object *spell)
1434 {
1435 object *tmp, *god, *head;
1436 int done_one, range, level, at, best_at;
1437 const char *race;
1438
1439 /* We precompute some values here so that we don't have to keep
1440 * doing it over and over again.
1441 */
1442 god = find_god (determine_god (op));
1443 level = casting_level (caster, spell);
1444 range = spell->range + SP_level_range_adjust (caster, spell);
1445
1446 /* On the bright side, no monster should ever have a race of GOD_...
1447 * so even if the player doesn't worship a god, if race=GOD_.., it
1448 * won't ever match anything.
1449 */
1450 if (!spell->race)
1451 race = NULL;
1452 else if (god && spell->race == shstr_GOD_SLAYING)
1453 race = god->slaying;
1454 else if (god && spell->race == shstr_GOD_FRIEND)
1455 race = god->race;
1456 else
1457 race = spell->race;
1458
1459 unordered_mapwalk (op, -range, -range, range, range)
1460 {
1461 mapspace &ms = m->at (nx, ny);
1462
1463 /* If there is nothing living on this space, no need to go further */
1464 if (!ms.flags () & P_IS_ALIVE)
1465 continue;
1466
1467 // players can only affect spaces that they can actually see
1468 if (caster
1469 && caster->contr
1470 && caster->contr->darkness_at (m, nx, ny) == LOS_BLOCKED)
1471 continue;
1472
1473 for (tmp = ms.top; tmp; tmp = tmp->below)
1474 if (tmp->flag [FLAG_MONSTER])
1475 break;
1476
1477 /* There can be living objects that are not monsters */
1478 if (!tmp)
1479 continue;
1480
1481 /* Only the head has meaningful data, so resolve to that */
1482 head = tmp->head_ ();
1483
1484 /* Make sure the race is OK. Likewise, only effect undead if spell specifically allows it */
1485 if (race && head->race && !strstr (race, head->race))
1486 continue;
1487
1488 if (QUERY_FLAG (head, FLAG_UNDEAD) && !QUERY_FLAG (spell, FLAG_UNDEAD))
1489 continue;
1490
1491 /* Now do a bunch of stuff related to saving throws */
1492 best_at = -1;
1493 if (spell->attacktype)
1494 {
1495 for (at = 0; at < NROFATTACKS; at++)
1496 if (spell->attacktype & (1 << at))
1497 if (best_at == -1 || head->resist[at] > head->resist[best_at])
1498 best_at = at;
1499
1500 if (best_at == -1)
1501 at = 0;
1502 else
1503 {
1504 if (head->resist[best_at] == 100)
1505 continue;
1506 else
1507 at = head->resist[best_at] / 5;
1508 }
1509
1510 at -= level / 5;
1511 if (did_make_save (head, head->level, at))
1512 continue;
1513 }
1514 else /* spell->attacktype */
1515 {
1516 /*
1517 Spell has no attacktype (charm & such), so we'll have a specific saving:
1518 * if spell level < monster level, no go
1519 * else, chance of effect = 20 + min( 50, 2 * ( spell level - monster level ) )
1520
1521 The chance will then be in the range [20-70] percent, not too bad.
1522
1523 This is required to fix the 'charm monster' abuse, where a player level 1 can
1524 charm a level 125 monster...
1525
1526 Ryo, august 14th
1527 */
1528 if (head->level > level)
1529 continue;
1530
1531 if (random_roll (0, 100, caster, PREFER_LOW) >= (20 + MIN (50, 2 * (level - head->level))))
1532 /* Failed, no effect */
1533 continue;
1534 }
1535
1536 /* Done with saving throw. Now start affecting the monster */
1537 done_one = 0;
1538
1539 /* aggravation */
1540 if (QUERY_FLAG (spell, FLAG_MONSTER))
1541 {
1542 CLEAR_FLAG (head, FLAG_SLEEP);
1543 remove_friendly_object (head);
1544 done_one = 1;
1545 head->enemy = op;
1546 }
1547
1548 /* calm monsters */
1549 if (QUERY_FLAG (spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (head, FLAG_UNAGGRESSIVE))
1550 {
1551 SET_FLAG (head, FLAG_UNAGGRESSIVE);
1552 head->enemy = NULL;
1553 done_one = 1;
1554 }
1555
1556 /* berserk monsters */
1557 if (QUERY_FLAG (spell, FLAG_BERSERK) && !QUERY_FLAG (head, FLAG_BERSERK))
1558 {
1559 SET_FLAG (head, FLAG_BERSERK);
1560 done_one = 1;
1561 }
1562
1563 /* charm */
1564 if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_FLAG (head, FLAG_FRIENDLY))
1565 {
1566 INVOKE_OBJECT (KILL, head, ARG_OBJECT (caster));
1567
1568 /* Prevent uncontrolled outbreaks of self replicating monsters.
1569 Typical use case is charm, go somwhere, use aggravation to make hostile.
1570 This could lead to fun stuff like mice outbreak in bigworld and server crawl. */
1571 CLEAR_FLAG (head, FLAG_GENERATOR);
1572 head->set_owner (op);
1573 set_spell_skill (op, caster, spell, head);
1574 add_friendly_object (head);
1575 head->attack_movement = PETMOVE;
1576 done_one = 1;
1577 change_exp (op, head->stats.exp / 2, head->skill, SK_EXP_ADD_SKILL);
1578 head->stats.exp = 0;
1579 }
1580
1581 /* If a monster was effected, put an effect in */
1582 if (done_one && spell->other_arch)
1583 m->insert (arch_to_object (spell->other_arch), nx, ny, op);
1584 }
1585
1586 return 1;
1587 }
1588
1589 /* Move_ball_spell: This handles ball type spells that just sort of wander
1590 * about. was called move_ball_lightning, but since more than the ball
1591 * lightning spell used it, that seemed misnamed.
1592 * op is the spell effect.
1593 * note that duration is handled by process_object() in time.c
1594 */
1595 void
1596 move_ball_spell (object *op)
1597 {
1598 int i, j, dam_save, dir, mflags;
1599 sint16 nx, ny, hx, hy;
1600 object *owner;
1601 maptile *m;
1602
1603 owner = op->owner;
1604
1605 /* the following logic makes sure that the ball doesn't move into a wall,
1606 * and makes sure that it will move along a wall to try and get at it's
1607 * victim. The block immediately below more or less chooses a random
1608 * offset to move the ball, eg, keep it mostly on course, with some
1609 * deviations.
1610 */
1611
1612 dir = 0;
1613 if (!(rndm (0, 3)))
1614 j = rndm (0, 1);
1615 else
1616 j = 0;
1617
1618 for (i = 1; i < 9; i++)
1619 {
1620 /* i bit 0: alters sign of offset
1621 * other bits (i / 2): absolute value of offset
1622 */
1623 int offset = ((i ^ j) & 1) ? (i / 2) : -(i / 2);
1624 int tmpdir = absdir (op->direction + offset);
1625
1626 nx = op->x + freearr_x[tmpdir];
1627 ny = op->y + freearr_y[tmpdir];
1628 if (!(get_map_flags (op->map, &m, nx, ny, &nx, &ny) & P_OUT_OF_MAP) && !(OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, nx, ny))))
1629 {
1630 dir = tmpdir;
1631 break;
1632 }
1633 }
1634
1635 if (dir == 0)
1636 {
1637 nx = op->x;
1638 ny = op->y;
1639 m = op->map;
1640 }
1641
1642 m->insert (op, nx, ny, op);
1643
1644 dam_save = op->stats.dam; /* save the original dam: we do halfdam on
1645 surrounding squares */
1646
1647 /* loop over current square and neighbors to hit.
1648 * if this has an other_arch field, we insert that in
1649 * the surround spaces.
1650 */
1651 for (j = 0; j < 9; j++)
1652 {
1653 hx = nx + freearr_x[j];
1654 hy = ny + freearr_y[j];
1655
1656 m = op->map;
1657 mflags = get_map_flags (m, &m, hx, hy, &hx, &hy);
1658
1659 if (mflags & P_OUT_OF_MAP)
1660 continue;
1661
1662 /* first, don't ever, ever hit the owner. Don't hit out
1663 * of the map either.
1664 */
1665
1666 if ((mflags & P_IS_ALIVE) && (!owner || owner->x != hx || owner->y != hy || !on_same_map (owner, op)))
1667 {
1668 if (j)
1669 op->stats.dam = dam_save / 2;
1670
1671 hit_map (op, j, op->attacktype, 1);
1672 }
1673
1674 /* insert the other arch */
1675 if (op->other_arch && !(OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, hx, hy))))
1676 m->insert (arch_to_object (op->other_arch), hx, hy, op);
1677 }
1678
1679 /* restore to the center location and damage */
1680 op->stats.dam = dam_save;
1681
1682 i = spell_find_dir (op->map, op->x, op->y, op->owner);
1683
1684 if (i >= 0)
1685 { /* we have a preferred direction! */
1686 /* pick another direction if the preferred dir is blocked. */
1687 if (get_map_flags (op->map, &m, nx + freearr_x[i], ny + freearr_y[i], &hx, &hy) & P_OUT_OF_MAP ||
1688 OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, hx, hy)))
1689 i = absdir (i + rndm (0, 2) - 1); /* -1, 0, +1 */
1690
1691 op->direction = i;
1692 }
1693 }
1694
1695 /* move_swarm_spell: peterm
1696 * This is an implementation of the swarm spell. It was written for
1697 * meteor swarm, but it could be used for any swarm. A swarm spell
1698 * is a special type of object that casts swarms of other types
1699 * of spells. Which spell it casts is flexible. It fires the spells
1700 * from a set of squares surrounding the caster, in a given direction.
1701 */
1702 void
1703 move_swarm_spell (object *op)
1704 {
1705 #if 0
1706 static int cardinal_adjust[9] = { -3, -2, -1, 0, 0, 0, 1, 2, 3 };
1707 static int diagonal_adjust[10] = { -3, -2, -2, -1, 0, 0, 1, 2, 2, 3 };
1708 sint16 target_x, target_y, origin_x, origin_y;
1709 int adjustdir;
1710 maptile *m;
1711 #endif
1712 object *owner = op->env;
1713
1714 if (!owner) // MUST not happen, remove when true TODO
1715 {
1716 LOG (llevError, "swarm spell found outside inventory: %s\n", op->debug_desc ());
1717 op->destroy ();
1718 return;
1719 }
1720
1721 if (!op->duration || !owner->is_on_map ())
1722 {
1723 op->drop_and_destroy ();
1724 return;
1725 }
1726
1727 op->duration--;
1728
1729 int basedir = op->direction;
1730 if (!basedir)
1731 {
1732 /* spray in all directions! 8) */
1733 op->facing = (op->facing + op->state) & 7;
1734 basedir = op->facing + 1;
1735 }
1736
1737 #if 0
1738 // this is bogus: it causes wrong places to be checked below
1739 // (a wall 2 cells away will block the effect...) and
1740 // doesn't work for SP_BULLET anyhow, so again tests the wrong
1741 // space.
1742 // should be fixed later, but correctness before features...
1743 // (schmorp)
1744
1745 /* new offset calculation to make swarm element distribution
1746 * more uniform
1747 */
1748 if (op->duration)
1749 {
1750 if (basedir & 1)
1751 {
1752 adjustdir = cardinal_adjust[rndm (0, 8)];
1753 }
1754 else
1755 {
1756 adjustdir = diagonal_adjust[rndm (0, 9)];
1757 }
1758 }
1759 else
1760 {
1761 adjustdir = 0; /* fire the last one from forward. */
1762 }
1763
1764 target_x = op->x + freearr_x[absdir (basedir + adjustdir)];
1765 target_y = op->y + freearr_y[absdir (basedir + adjustdir)];
1766
1767 /* back up one space so we can hit point-blank targets, but this
1768 * necessitates extra out_of_map check below
1769 */
1770 origin_x = target_x - freearr_x[basedir];
1771 origin_y = target_y - freearr_y[basedir];
1772
1773
1774 /* spell pointer is set up for the spell this casts. Since this
1775 * should just be a pointer to the spell in some inventory,
1776 * it is unlikely to disappear by the time we need it. However,
1777 * do some sanity checking anyways.
1778 */
1779
1780 if (op->spell && op->spell->type == SPELL &&
1781 !(get_map_flags (op->map, &m, target_x, target_y, &target_x, &target_y) & P_OUT_OF_MAP) &&
1782 !(OB_TYPE_MOVE_BLOCK (op->spell, GET_MAP_MOVE_BLOCK (m, target_x, target_y))))
1783 {
1784
1785 /* Bullet spells have a bunch more customization that needs to be done */
1786 if (op->spell->subtype == SP_BULLET)
1787 fire_bullet (owner, op, basedir, op->spell);
1788 else if (op->spell->subtype == SP_MAGIC_MISSILE)
1789 fire_arch_from_position (owner, op, origin_x, origin_y, basedir, op->spell);
1790 }
1791 #endif
1792
1793 /* spell pointer is set up for the spell this casts. Since this
1794 * should just be a pointer to the spell in some inventory,
1795 * it is unlikely to disappear by the time we need it. However,
1796 * do some sanity checking anyways.
1797 */
1798
1799 if (op->spell && op->spell->type == SPELL)
1800 {
1801 /* Bullet spells have a bunch more customization that needs to be done */
1802 if (op->spell->subtype == SP_BULLET)
1803 fire_bullet (owner, op, basedir, op->spell);
1804 else if (op->spell->subtype == SP_MAGIC_MISSILE)
1805 fire_arch_from_position (owner, op, owner->x, owner->y, basedir, op->spell);
1806 }
1807 }
1808
1809 /* fire_swarm:
1810 * The following routine creates a swarm of objects. It actually
1811 * sets up a specific swarm object, which then fires off all
1812 * the parts of the swarm.
1813 *
1814 * op: the owner
1815 * caster: the caster (owner, wand, rod, scroll)
1816 * dir: the direction everything will be fired in
1817 * spell - the spell that is this spell.
1818 * n: the number to be fired.
1819 */
1820 int
1821 fire_swarm (object *op, object *caster, object *spell, int dir)
1822 {
1823 if (!spell->other_arch)
1824 return 0;
1825
1826 object *tmp = archetype::get (SWARM_SPELL);
1827
1828 set_spell_skill (op, caster, spell, tmp);
1829 tmp->level = casting_level (caster, spell); /* needed later, to get level dep. right. */
1830 tmp->spell = spell->other_arch->instance ();
1831 tmp->attacktype = tmp->spell->attacktype;
1832
1833 if (tmp->attacktype & AT_HOLYWORD || tmp->attacktype & AT_GODPOWER)
1834 if (!tailor_god_spell (tmp, op))
1835 return 1;
1836
1837 tmp->duration = SP_level_duration_adjust (caster, spell);
1838 for (int i = 0; i < spell->duration; i++)
1839 tmp->duration += die_roll (1, 3, op, PREFER_HIGH);
1840
1841 tmp->invisible = 1;
1842 tmp->flag [FLAG_NO_DROP] = 1; // make sure it stays in inv, or else
1843 tmp->direction = dir;
1844 tmp->facing = rndm (1, 8); // initial firing direction
1845 tmp->state = rndm (4) * 2 + 1; // direction increment
1846
1847 op->insert (tmp);
1848
1849 return 1;
1850 }
1851
1852 /* See the spells documentation file for why this is its own
1853 * function.
1854 */
1855 int
1856 cast_light (object *op, object *caster, object *spell, int dir)
1857 {
1858 object *target = NULL, *tmp = NULL;
1859 sint16 x, y;
1860 int dam, mflags;
1861 maptile *m;
1862
1863 dam = spell->stats.dam + SP_level_dam_adjust (caster, spell);
1864
1865 if (dir)
1866 {
1867 x = op->x + freearr_x[dir];
1868 y = op->y + freearr_y[dir];
1869 m = op->map;
1870
1871 mflags = get_map_flags (m, &m, x, y, &x, &y);
1872
1873 if (mflags & P_OUT_OF_MAP)
1874 {
1875 new_draw_info (NDI_UNIQUE, 0, op, "Nothing is there.");
1876 return 0;
1877 }
1878
1879 if (mflags & P_IS_ALIVE && spell->attacktype)
1880 {
1881 for (target = GET_MAP_OB (m, x, y); target; target = target->above)
1882 if (QUERY_FLAG (target, FLAG_MONSTER))
1883 {
1884 /* oky doky. got a target monster. Lets make a blinding attack */
1885 if (target->head)
1886 target = target->head;
1887
1888 hit_player (target, dam, op, spell->attacktype, 1);
1889 return 1; /* one success only! */
1890 }
1891 }
1892
1893 /* no live target, perhaps a wall is in the way? */
1894 if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y)))
1895 {
1896 new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way.");
1897 return 0;
1898 }
1899 }
1900
1901 /* ok, looks groovy to just insert a new light on the map */
1902 tmp = arch_to_object (spell->other_arch);
1903 if (!tmp)
1904 {
1905 LOG (llevError, "Error: spell arch for cast_light() missing.\n");
1906 return 0;
1907 }
1908
1909 tmp->stats.food = spell->duration + SP_level_duration_adjust (caster, spell);
1910
1911 if (tmp->glow_radius)
1912 tmp->glow_radius = min (MAX_LIGHT_RADIUS, spell->range + SP_level_range_adjust (caster, spell));
1913
1914 if (dir)
1915 m->insert (tmp, x, y, op);
1916 else
1917 caster->outer_env ()->insert (tmp);
1918
1919 return 1;
1920 }
1921
1922 /* cast_cause_disease: this spell looks along <dir> from the
1923 * player and infects someone.
1924 * op is the player/monster, caster is the object, dir is the direction
1925 * to cast, disease_arch is the specific disease, and type is the spell number
1926 * perhaps this should actually be in disease.c?
1927 */
1928 int
1929 cast_cause_disease (object *op, object *caster, object *spell, int dir)
1930 {
1931 sint16 x, y;
1932 int i, mflags, range, dam_mod, dur_mod;
1933 object *walk;
1934 maptile *m;
1935
1936 x = op->x;
1937 y = op->y;
1938
1939 /* If casting from a scroll, no direction will be available, so refer to the
1940 * direction the player is pointing.
1941 */
1942 if (!dir)
1943 dir = op->facing;
1944
1945 if (!dir)
1946 return 0; /* won't find anything if casting on ourself, so just return */
1947
1948 /* Calculate these once here */
1949 range = spell->range + SP_level_range_adjust (caster, spell);
1950 dam_mod = SP_level_dam_adjust (caster, spell);
1951 dur_mod = SP_level_duration_adjust (caster, spell);
1952
1953 /* search in a line for a victim */
1954 for (i = 1; i < range; i++)
1955 {
1956 x = op->x + i * freearr_x[dir];
1957 y = op->y + i * freearr_y[dir];
1958 m = op->map;
1959
1960 mflags = get_map_flags (m, &m, x, y, &x, &y);
1961
1962 if (mflags & P_OUT_OF_MAP)
1963 return 0;
1964
1965 /* don't go through walls - presume diseases are airborne */
1966 if (GET_MAP_MOVE_BLOCK (m, x, y) & MOVE_FLY_LOW)
1967 return 0;
1968
1969 /* Only bother looking on this space if there is something living here */
1970 if (mflags & P_IS_ALIVE)
1971 {
1972 /* search this square for a victim */
1973 for (walk = GET_MAP_OB (m, x, y); walk; walk = walk->above)
1974 if (QUERY_FLAG (walk, FLAG_MONSTER) || (walk->type == PLAYER))
1975 { /* found a victim */
1976 object *disease = arch_to_object (spell->other_arch);
1977
1978 disease->set_owner (op);
1979 set_spell_skill (op, caster, spell, disease);
1980 disease->stats.exp = 0;
1981 disease->level = casting_level (caster, spell);
1982
1983 /* do level adjustments */
1984 if (disease->stats.wc)
1985 disease->stats.wc += dur_mod / 2;
1986
1987 if (disease->magic > 0)
1988 disease->magic += dur_mod / 8;
1989
1990 if (disease->stats.maxhp > 0)
1991 disease->stats.maxhp += dur_mod;
1992
1993 if (disease->stats.maxgrace > 0)
1994 disease->stats.maxgrace += dur_mod;
1995
1996 if (disease->stats.dam)
1997 {
1998 if (disease->stats.dam > 0)
1999 disease->stats.dam += dam_mod;
2000 else
2001 disease->stats.dam -= dam_mod;
2002 }
2003
2004 if (disease->last_sp)
2005 {
2006 disease->last_sp -= 2 * dam_mod;
2007 if (disease->last_sp < 1)
2008 disease->last_sp = 1;
2009 }
2010
2011 if (disease->stats.maxsp)
2012 {
2013 if (disease->stats.maxsp > 0)
2014 disease->stats.maxsp += dam_mod;
2015 else
2016 disease->stats.maxsp -= dam_mod;
2017 }
2018
2019 if (disease->stats.ac)
2020 disease->stats.ac += dam_mod;
2021
2022 if (disease->last_eat)
2023 disease->last_eat -= dam_mod;
2024
2025 if (disease->stats.hp)
2026 disease->stats.hp -= dam_mod;
2027
2028 if (disease->stats.sp)
2029 disease->stats.sp -= dam_mod;
2030
2031 if (infect_object (walk, disease, 1))
2032 {
2033 new_draw_info_format (NDI_UNIQUE, 0, op, "You inflict %s on %s!", &disease->name, &walk->name);
2034
2035 disease->destroy (); /* don't need this one anymore */
2036 walk->map->insert (get_archetype ("detect_magic"), x, y, op);
2037 return 1;
2038 }
2039
2040 disease->destroy ();
2041 }
2042 } /* if living creature */
2043 } /* for range of spaces */
2044
2045 new_draw_info (NDI_UNIQUE, 0, op, "No one caught anything!");
2046 return 1;
2047 }