ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.74
Committed: Sun Dec 28 07:48:44 2008 UTC (15 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.73: +24 -47 lines
Log Message:
speed up destruction

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