ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.C
Revision: 1.8
Committed: Thu Sep 7 10:01:58 2006 UTC (17 years, 9 months ago) by pippijn
Content type: text/plain
Branch: MAIN
Changes since 1.7: +5 -2 lines
Log Message:
Some cleanups.

File Contents

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