ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.6
Committed: Tue Aug 29 08:01:38 2006 UTC (17 years, 9 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +762 -762 lines
Log Message:
expand initial tabs to spaces

File Contents

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