ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.31
Committed: Fri Mar 30 21:53:09 2007 UTC (17 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.30: +2 -9 lines
Log Message:
*** empty log message ***

File Contents

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