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