ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.75
Committed: Sun Dec 28 08:08:25 2008 UTC (15 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.74: +127 -137 lines
Log Message:
speed up charm/mood change

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 root 1.75 int done_one, range, level, at, best_at;
1439 root 1.9 const char *race;
1440    
1441     /* We precompute some values here so that we don't have to keep
1442     * doing it over and over again.
1443     */
1444     god = find_god (determine_god (op));
1445 root 1.64 level = casting_level (caster, spell);
1446 root 1.9 range = spell->range + SP_level_range_adjust (caster, spell);
1447    
1448     /* On the bright side, no monster should ever have a race of GOD_...
1449     * so even if the player doesn't worship a god, if race=GOD_.., it
1450     * won't ever match anything.
1451     */
1452     if (!spell->race)
1453     race = NULL;
1454     else if (god && !strcmp (spell->race, "GOD_SLAYING"))
1455     race = god->slaying;
1456     else if (god && !strcmp (spell->race, "GOD_FRIEND"))
1457     race = god->race;
1458     else
1459     race = spell->race;
1460 elmex 1.1
1461 root 1.75 unordered_mapwalk (op, -range, -range, range, range)
1462     {
1463     mapspace &ms = m->at (nx, ny);
1464    
1465     /* If there is nothing living on this space, no need to go further */
1466     if (!ms.flags () & P_IS_ALIVE)
1467     continue;
1468    
1469     // players can only affect spaces that they can actually see
1470     if (caster
1471     && caster->contr
1472     && caster->contr->darkness_at (m, nx, ny) == LOS_BLOCKED)
1473     continue;
1474    
1475     for (tmp = ms.top; tmp; tmp = tmp->below)
1476     if (tmp->flag [FLAG_MONSTER])
1477     break;
1478    
1479     /* There can be living objects that are not monsters */
1480     if (!tmp)
1481     continue;
1482    
1483     /* Only the head has meaningful data, so resolve to that */
1484     head = tmp->head_ ();
1485    
1486     /* Make sure the race is OK. Likewise, only effect undead if spell specifically allows it */
1487     if (race && head->race && !strstr (race, head->race))
1488     continue;
1489    
1490     if (QUERY_FLAG (head, FLAG_UNDEAD) && !QUERY_FLAG (spell, FLAG_UNDEAD))
1491     continue;
1492    
1493     /* Now do a bunch of stuff related to saving throws */
1494     best_at = -1;
1495     if (spell->attacktype)
1496     {
1497     for (at = 0; at < NROFATTACKS; at++)
1498     if (spell->attacktype & (1 << at))
1499     if (best_at == -1 || head->resist[at] > head->resist[best_at])
1500     best_at = at;
1501    
1502     if (best_at == -1)
1503     at = 0;
1504     else
1505     {
1506     if (head->resist[best_at] == 100)
1507     continue;
1508     else
1509     at = head->resist[best_at] / 5;
1510     }
1511    
1512     at -= level / 5;
1513     if (did_make_save (head, head->level, at))
1514     continue;
1515     }
1516     else /* spell->attacktype */
1517     {
1518     /*
1519     Spell has no attacktype (charm & such), so we'll have a specific saving:
1520     * if spell level < monster level, no go
1521     * else, chance of effect = 20 + min( 50, 2 * ( spell level - monster level ) )
1522    
1523     The chance will then be in the range [20-70] percent, not too bad.
1524    
1525     This is required to fix the 'charm monster' abuse, where a player level 1 can
1526     charm a level 125 monster...
1527    
1528     Ryo, august 14th
1529     */
1530     if (head->level > level)
1531     continue;
1532    
1533     if (random_roll (0, 100, caster, PREFER_LOW) >= (20 + MIN (50, 2 * (level - head->level))))
1534     /* Failed, no effect */
1535     continue;
1536     }
1537    
1538     /* Done with saving throw. Now start affecting the monster */
1539     done_one = 0;
1540    
1541     /* aggravation */
1542     if (QUERY_FLAG (spell, FLAG_MONSTER))
1543     {
1544     CLEAR_FLAG (head, FLAG_SLEEP);
1545     remove_friendly_object (head);
1546     done_one = 1;
1547     head->enemy = op;
1548     }
1549    
1550     /* calm monsters */
1551     if (QUERY_FLAG (spell, FLAG_UNAGGRESSIVE) && !QUERY_FLAG (head, FLAG_UNAGGRESSIVE))
1552     {
1553     SET_FLAG (head, FLAG_UNAGGRESSIVE);
1554     head->enemy = NULL;
1555     done_one = 1;
1556     }
1557    
1558     /* berserk monsters */
1559     if (QUERY_FLAG (spell, FLAG_BERSERK) && !QUERY_FLAG (head, FLAG_BERSERK))
1560     {
1561     SET_FLAG (head, FLAG_BERSERK);
1562     done_one = 1;
1563     }
1564    
1565     /* charm */
1566     if (QUERY_FLAG (spell, FLAG_NO_ATTACK) && !QUERY_FLAG (head, FLAG_FRIENDLY))
1567     {
1568     INVOKE_OBJECT (KILL, head, ARG_OBJECT (caster));
1569    
1570     /* Prevent uncontrolled outbreaks of self replicating monsters.
1571     Typical use case is charm, go somwhere, use aggravation to make hostile.
1572     This could lead to fun stuff like mice outbreak in bigworld and server crawl. */
1573     CLEAR_FLAG (head, FLAG_GENERATOR);
1574     head->set_owner (op);
1575     set_spell_skill (op, caster, spell, head);
1576     add_friendly_object (head);
1577     head->attack_movement = PETMOVE;
1578     done_one = 1;
1579     change_exp (op, head->stats.exp / 2, head->skill, SK_EXP_ADD_SKILL);
1580     head->stats.exp = 0;
1581     }
1582    
1583     /* If a monster was effected, put an effect in */
1584     if (done_one && spell->other_arch)
1585     m->insert (arch_to_object (spell->other_arch), nx, ny, op);
1586     }
1587 elmex 1.1
1588 root 1.9 return 1;
1589 elmex 1.1 }
1590    
1591     /* Move_ball_spell: This handles ball type spells that just sort of wander
1592     * about. was called move_ball_lightning, but since more than the ball
1593     * lightning spell used it, that seemed misnamed.
1594     * op is the spell effect.
1595     * note that duration is handled by process_object() in time.c
1596     */
1597 root 1.9 void
1598     move_ball_spell (object *op)
1599     {
1600     int i, j, dam_save, dir, mflags;
1601     sint16 nx, ny, hx, hy;
1602     object *owner;
1603 root 1.15 maptile *m;
1604 root 1.9
1605 root 1.19 owner = op->owner;
1606 root 1.9
1607     /* the following logic makes sure that the ball doesn't move into a wall,
1608     * and makes sure that it will move along a wall to try and get at it's
1609     * victim. The block immediately below more or less chooses a random
1610     * offset to move the ball, eg, keep it mostly on course, with some
1611     * deviations.
1612     */
1613    
1614     dir = 0;
1615     if (!(rndm (0, 3)))
1616     j = rndm (0, 1);
1617     else
1618     j = 0;
1619    
1620     for (i = 1; i < 9; i++)
1621     {
1622     /* i bit 0: alters sign of offset
1623     * other bits (i / 2): absolute value of offset
1624     */
1625     int offset = ((i ^ j) & 1) ? (i / 2) : -(i / 2);
1626     int tmpdir = absdir (op->direction + offset);
1627    
1628     nx = op->x + freearr_x[tmpdir];
1629     ny = op->y + freearr_y[tmpdir];
1630     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))))
1631     {
1632     dir = tmpdir;
1633     break;
1634 root 1.6 }
1635 elmex 1.1 }
1636 root 1.71
1637 root 1.9 if (dir == 0)
1638     {
1639     nx = op->x;
1640     ny = op->y;
1641     m = op->map;
1642     }
1643    
1644 root 1.25 m->insert (op, nx, ny, op);
1645 root 1.9
1646     dam_save = op->stats.dam; /* save the original dam: we do halfdam on
1647     surrounding squares */
1648    
1649     /* loop over current square and neighbors to hit.
1650     * if this has an other_arch field, we insert that in
1651     * the surround spaces.
1652     */
1653     for (j = 0; j < 9; j++)
1654     {
1655     hx = nx + freearr_x[j];
1656     hy = ny + freearr_y[j];
1657 elmex 1.1
1658 root 1.9 m = op->map;
1659     mflags = get_map_flags (m, &m, hx, hy, &hx, &hy);
1660 elmex 1.1
1661 root 1.9 if (mflags & P_OUT_OF_MAP)
1662     continue;
1663 elmex 1.1
1664 root 1.9 /* first, don't ever, ever hit the owner. Don't hit out
1665     * of the map either.
1666     */
1667 elmex 1.1
1668 root 1.9 if ((mflags & P_IS_ALIVE) && (!owner || owner->x != hx || owner->y != hy || !on_same_map (owner, op)))
1669     {
1670     if (j)
1671     op->stats.dam = dam_save / 2;
1672 root 1.58
1673 root 1.9 hit_map (op, j, op->attacktype, 1);
1674 root 1.6 }
1675 elmex 1.1
1676 root 1.9 /* insert the other arch */
1677     if (op->other_arch && !(OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, hx, hy))))
1678 root 1.25 m->insert (arch_to_object (op->other_arch), hx, hy, op);
1679 elmex 1.1 }
1680    
1681 root 1.9 /* restore to the center location and damage */
1682     op->stats.dam = dam_save;
1683 elmex 1.1
1684 root 1.19 i = spell_find_dir (op->map, op->x, op->y, op->owner);
1685 elmex 1.1
1686 root 1.9 if (i >= 0)
1687     { /* we have a preferred direction! */
1688     /* pick another direction if the preferred dir is blocked. */
1689     if (get_map_flags (op->map, &m, nx + freearr_x[i], ny + freearr_y[i], &hx, &hy) & P_OUT_OF_MAP ||
1690     OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, hx, hy)))
1691 root 1.25 i = absdir (i + rndm (0, 2) - 1); /* -1, 0, +1 */
1692    
1693 root 1.9 op->direction = i;
1694 elmex 1.1 }
1695     }
1696    
1697 root 1.55 /* move_swarm_spell: peterm
1698 elmex 1.1 * This is an implementation of the swarm spell. It was written for
1699 root 1.55 * meteor swarm, but it could be used for any swarm. A swarm spell
1700 elmex 1.1 * is a special type of object that casts swarms of other types
1701 root 1.55 * of spells. Which spell it casts is flexible. It fires the spells
1702 elmex 1.1 * from a set of squares surrounding the caster, in a given direction.
1703     */
1704 root 1.9 void
1705     move_swarm_spell (object *op)
1706 elmex 1.1 {
1707 pippijn 1.8 #if 0
1708 root 1.9 static int cardinal_adjust[9] = { -3, -2, -1, 0, 0, 0, 1, 2, 3 };
1709     static int diagonal_adjust[10] = { -3, -2, -2, -1, 0, 0, 1, 2, 2, 3 };
1710     sint16 target_x, target_y, origin_x, origin_y;
1711     int adjustdir;
1712 root 1.15 maptile *m;
1713 pippijn 1.8 #endif
1714 root 1.55 object *owner = op->env;
1715 elmex 1.1
1716 root 1.59 if (!owner) // MUST not happen, remove when true TODO
1717     {
1718     LOG (llevError, "swarm spell found outside inventory: %s\n", op->debug_desc ());
1719 root 1.67 op->destroy ();
1720 root 1.59 return;
1721     }
1722    
1723 root 1.55 if (!op->duration || !owner->is_on_map ())
1724 root 1.9 {
1725 root 1.66 op->drop_and_destroy ();
1726 root 1.9 return;
1727 elmex 1.1 }
1728 root 1.17
1729 root 1.9 op->duration--;
1730 elmex 1.1
1731 root 1.55 int basedir = op->direction;
1732     if (!basedir)
1733 root 1.61 {
1734     /* spray in all directions! 8) */
1735     op->facing = (op->facing + op->state) & 7;
1736     basedir = op->facing + 1;
1737     }
1738 elmex 1.1
1739     #if 0
1740 root 1.9 // this is bogus: it causes wrong places to be checked below
1741     // (a wall 2 cells away will block the effect...) and
1742     // doesn't work for SP_BULLET anyhow, so again tests the wrong
1743     // space.
1744 root 1.31 // should be fixed later, but correctness before features...
1745 root 1.9 // (schmorp)
1746    
1747     /* new offset calculation to make swarm element distribution
1748     * more uniform
1749     */
1750     if (op->duration)
1751     {
1752     if (basedir & 1)
1753     {
1754     adjustdir = cardinal_adjust[rndm (0, 8)];
1755     }
1756     else
1757     {
1758     adjustdir = diagonal_adjust[rndm (0, 9)];
1759     }
1760     }
1761     else
1762     {
1763     adjustdir = 0; /* fire the last one from forward. */
1764     }
1765    
1766     target_x = op->x + freearr_x[absdir (basedir + adjustdir)];
1767     target_y = op->y + freearr_y[absdir (basedir + adjustdir)];
1768    
1769     /* back up one space so we can hit point-blank targets, but this
1770     * necessitates extra out_of_map check below
1771     */
1772     origin_x = target_x - freearr_x[basedir];
1773     origin_y = target_y - freearr_y[basedir];
1774    
1775    
1776     /* spell pointer is set up for the spell this casts. Since this
1777     * should just be a pointer to the spell in some inventory,
1778     * it is unlikely to disappear by the time we need it. However,
1779     * do some sanity checking anyways.
1780     */
1781    
1782     if (op->spell && op->spell->type == SPELL &&
1783     !(get_map_flags (op->map, &m, target_x, target_y, &target_x, &target_y) & P_OUT_OF_MAP) &&
1784     !(OB_TYPE_MOVE_BLOCK (op->spell, GET_MAP_MOVE_BLOCK (m, target_x, target_y))))
1785     {
1786    
1787     /* Bullet spells have a bunch more customization that needs to be done */
1788     if (op->spell->subtype == SP_BULLET)
1789     fire_bullet (owner, op, basedir, op->spell);
1790     else if (op->spell->subtype == SP_MAGIC_MISSILE)
1791     fire_arch_from_position (owner, op, origin_x, origin_y, basedir, op->spell);
1792 elmex 1.1 }
1793     #endif
1794    
1795 root 1.9 /* spell pointer is set up for the spell this casts. Since this
1796     * should just be a pointer to the spell in some inventory,
1797     * it is unlikely to disappear by the time we need it. However,
1798     * do some sanity checking anyways.
1799     */
1800    
1801     if (op->spell && op->spell->type == SPELL)
1802     {
1803     /* Bullet spells have a bunch more customization that needs to be done */
1804     if (op->spell->subtype == SP_BULLET)
1805     fire_bullet (owner, op, basedir, op->spell);
1806     else if (op->spell->subtype == SP_MAGIC_MISSILE)
1807 root 1.57 fire_arch_from_position (owner, op, owner->x, owner->y, basedir, op->spell);
1808 root 1.9 }
1809 elmex 1.1 }
1810    
1811     /* fire_swarm:
1812     * The following routine creates a swarm of objects. It actually
1813     * sets up a specific swarm object, which then fires off all
1814     * the parts of the swarm.
1815     *
1816     * op: the owner
1817     * caster: the caster (owner, wand, rod, scroll)
1818     * dir: the direction everything will be fired in
1819     * spell - the spell that is this spell.
1820     * n: the number to be fired.
1821     */
1822 root 1.9 int
1823     fire_swarm (object *op, object *caster, object *spell, int dir)
1824 elmex 1.1 {
1825 root 1.9 if (!spell->other_arch)
1826     return 0;
1827 elmex 1.1
1828 root 1.55 object *tmp = archetype::get (SWARM_SPELL);
1829 root 1.61
1830 root 1.9 set_spell_skill (op, caster, spell, tmp);
1831 root 1.64 tmp->level = casting_level (caster, spell); /* needed later, to get level dep. right. */
1832 root 1.55 tmp->spell = spell->other_arch->instance ();
1833 root 1.9 tmp->attacktype = tmp->spell->attacktype;
1834 elmex 1.1
1835 root 1.9 if (tmp->attacktype & AT_HOLYWORD || tmp->attacktype & AT_GODPOWER)
1836 root 1.25 if (!tailor_god_spell (tmp, op))
1837     return 1;
1838    
1839 root 1.9 tmp->duration = SP_level_duration_adjust (caster, spell);
1840 root 1.55 for (int i = 0; i < spell->duration; i++)
1841 root 1.9 tmp->duration += die_roll (1, 3, op, PREFER_HIGH);
1842    
1843 root 1.59 tmp->invisible = 1;
1844     tmp->flag [FLAG_NO_DROP] = 1; // make sure it stays in inv, or else
1845 root 1.9 tmp->direction = dir;
1846 root 1.57 tmp->facing = rndm (1, 8); // initial firing direction
1847 root 1.56 tmp->state = rndm (4) * 2 + 1; // direction increment
1848 root 1.25
1849 root 1.55 op->insert (tmp);
1850    
1851 root 1.9 return 1;
1852 elmex 1.1 }
1853    
1854     /* See the spells documentation file for why this is its own
1855     * function.
1856     */
1857 root 1.9 int
1858     cast_light (object *op, object *caster, object *spell, int dir)
1859     {
1860     object *target = NULL, *tmp = NULL;
1861     sint16 x, y;
1862     int dam, mflags;
1863 root 1.15 maptile *m;
1864 elmex 1.1
1865 root 1.9 dam = spell->stats.dam + SP_level_dam_adjust (caster, spell);
1866 elmex 1.1
1867 root 1.69 if (dir)
1868 root 1.9 {
1869 root 1.69 x = op->x + freearr_x[dir];
1870     y = op->y + freearr_y[dir];
1871     m = op->map;
1872 elmex 1.1
1873 root 1.69 mflags = get_map_flags (m, &m, x, y, &x, &y);
1874 elmex 1.1
1875 root 1.69 if (mflags & P_OUT_OF_MAP)
1876     {
1877     new_draw_info (NDI_UNIQUE, 0, op, "Nothing is there.");
1878     return 0;
1879     }
1880 elmex 1.1
1881 root 1.69 if (mflags & P_IS_ALIVE && spell->attacktype)
1882     {
1883     for (target = GET_MAP_OB (m, x, y); target; target = target->above)
1884     if (QUERY_FLAG (target, FLAG_MONSTER))
1885     {
1886     /* oky doky. got a target monster. Lets make a blinding attack */
1887     if (target->head)
1888     target = target->head;
1889 elmex 1.1
1890 root 1.69 hit_player (target, dam, op, spell->attacktype, 1);
1891     return 1; /* one success only! */
1892     }
1893     }
1894 root 1.58
1895 root 1.69 /* no live target, perhaps a wall is in the way? */
1896     if (OB_TYPE_MOVE_BLOCK (op, GET_MAP_MOVE_BLOCK (m, x, y)))
1897     {
1898     new_draw_info (NDI_UNIQUE, 0, op, "Something is in the way.");
1899     return 0;
1900     }
1901 elmex 1.1 }
1902    
1903 root 1.9 /* ok, looks groovy to just insert a new light on the map */
1904     tmp = arch_to_object (spell->other_arch);
1905     if (!tmp)
1906     {
1907     LOG (llevError, "Error: spell arch for cast_light() missing.\n");
1908     return 0;
1909     }
1910 root 1.69
1911 root 1.9 tmp->stats.food = spell->duration + SP_level_duration_adjust (caster, spell);
1912 root 1.69
1913 root 1.9 if (tmp->glow_radius)
1914 root 1.69 tmp->glow_radius = min (MAX_LIGHT_RADIUS, spell->range + SP_level_range_adjust (caster, spell));
1915    
1916     if (dir)
1917     m->insert (tmp, x, y, op);
1918     else
1919     caster->outer_env ()->insert (tmp);
1920 root 1.25
1921 root 1.9 return 1;
1922 elmex 1.1 }
1923    
1924     /* cast_cause_disease: this spell looks along <dir> from the
1925     * player and infects someone.
1926     * op is the player/monster, caster is the object, dir is the direction
1927     * to cast, disease_arch is the specific disease, and type is the spell number
1928     * perhaps this should actually be in disease.c?
1929     */
1930 root 1.9 int
1931     cast_cause_disease (object *op, object *caster, object *spell, int dir)
1932     {
1933     sint16 x, y;
1934     int i, mflags, range, dam_mod, dur_mod;
1935     object *walk;
1936 root 1.15 maptile *m;
1937 root 1.9
1938     x = op->x;
1939     y = op->y;
1940    
1941     /* If casting from a scroll, no direction will be available, so refer to the
1942     * direction the player is pointing.
1943     */
1944     if (!dir)
1945     dir = op->facing;
1946 root 1.48
1947 root 1.9 if (!dir)
1948     return 0; /* won't find anything if casting on ourself, so just return */
1949    
1950     /* Calculate these once here */
1951     range = spell->range + SP_level_range_adjust (caster, spell);
1952     dam_mod = SP_level_dam_adjust (caster, spell);
1953     dur_mod = SP_level_duration_adjust (caster, spell);
1954    
1955     /* search in a line for a victim */
1956     for (i = 1; i < range; i++)
1957     {
1958     x = op->x + i * freearr_x[dir];
1959     y = op->y + i * freearr_y[dir];
1960     m = op->map;
1961    
1962     mflags = get_map_flags (m, &m, x, y, &x, &y);
1963    
1964     if (mflags & P_OUT_OF_MAP)
1965     return 0;
1966    
1967     /* don't go through walls - presume diseases are airborne */
1968     if (GET_MAP_MOVE_BLOCK (m, x, y) & MOVE_FLY_LOW)
1969     return 0;
1970    
1971     /* Only bother looking on this space if there is something living here */
1972     if (mflags & P_IS_ALIVE)
1973     {
1974     /* search this square for a victim */
1975 root 1.21 for (walk = GET_MAP_OB (m, x, y); walk; walk = walk->above)
1976 root 1.9 if (QUERY_FLAG (walk, FLAG_MONSTER) || (walk->type == PLAYER))
1977     { /* found a victim */
1978     object *disease = arch_to_object (spell->other_arch);
1979    
1980 root 1.19 disease->set_owner (op);
1981 root 1.9 set_spell_skill (op, caster, spell, disease);
1982     disease->stats.exp = 0;
1983 root 1.64 disease->level = casting_level (caster, spell);
1984 root 1.9
1985     /* do level adjustments */
1986     if (disease->stats.wc)
1987     disease->stats.wc += dur_mod / 2;
1988    
1989     if (disease->magic > 0)
1990 root 1.48 disease->magic += dur_mod / 8;
1991 root 1.9
1992     if (disease->stats.maxhp > 0)
1993     disease->stats.maxhp += dur_mod;
1994    
1995     if (disease->stats.maxgrace > 0)
1996     disease->stats.maxgrace += dur_mod;
1997    
1998     if (disease->stats.dam)
1999     {
2000     if (disease->stats.dam > 0)
2001     disease->stats.dam += dam_mod;
2002     else
2003     disease->stats.dam -= dam_mod;
2004     }
2005 root 1.6
2006 root 1.9 if (disease->last_sp)
2007     {
2008     disease->last_sp -= 2 * dam_mod;
2009     if (disease->last_sp < 1)
2010     disease->last_sp = 1;
2011     }
2012 root 1.6
2013 root 1.9 if (disease->stats.maxsp)
2014     {
2015     if (disease->stats.maxsp > 0)
2016     disease->stats.maxsp += dam_mod;
2017     else
2018     disease->stats.maxsp -= dam_mod;
2019     }
2020 root 1.6
2021 root 1.9 if (disease->stats.ac)
2022     disease->stats.ac += dam_mod;
2023 root 1.6
2024 root 1.9 if (disease->last_eat)
2025     disease->last_eat -= dam_mod;
2026 root 1.6
2027 root 1.9 if (disease->stats.hp)
2028     disease->stats.hp -= dam_mod;
2029 root 1.6
2030 root 1.9 if (disease->stats.sp)
2031     disease->stats.sp -= dam_mod;
2032    
2033     if (infect_object (walk, disease, 1))
2034     {
2035     new_draw_info_format (NDI_UNIQUE, 0, op, "You inflict %s on %s!", &disease->name, &walk->name);
2036    
2037 root 1.67 disease->destroy (); /* don't need this one anymore */
2038 root 1.40 walk->map->insert (get_archetype ("detect_magic"), x, y, op);
2039 root 1.9 return 1;
2040 root 1.6 }
2041 root 1.17
2042 root 1.67 disease->destroy ();
2043 root 1.9 }
2044     } /* if living creature */
2045     } /* for range of spaces */
2046 root 1.25
2047 root 1.9 new_draw_info (NDI_UNIQUE, 0, op, "No one caught anything!");
2048     return 1;
2049 elmex 1.1 }