ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.35
Committed: Thu May 17 20:27:02 2007 UTC (17 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.34: +1 -1 lines
Log Message:
- more rewrite:
  change_skill now only changes the chosen_skill, nothing else.
  new flag to suppress skill tool behaviour of readying the skill
  when applying the tool, for use in find_skill.

File Contents

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