ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.50
Committed: Sun Apr 20 23:25:09 2008 UTC (16 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.49: +6 -8 lines
Log Message:
minor refactoring

File Contents

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