ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.68
Committed: Mon Oct 6 08:46:33 2008 UTC (15 years, 7 months ago) by elmex
Content type: text/plain
Branch: MAIN
Changes since 1.67: +8 -3 lines
Log Message:
added some checks in move_cone.

File Contents

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