ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.61
Committed: Sun May 18 19:53:07 2008 UTC (16 years ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_55, rel-2_56
Changes since 1.60: +6 -2 lines
Log Message:
*** empty log message ***

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