ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.24
Committed: Tue Dec 26 08:55:00 2006 UTC (17 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.23: +2 -5 lines
Log Message:
replace update_ob_speed by ->set_speed

File Contents

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