ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/spell_attack.c
Revision: 1.2
Committed: Tue Feb 21 11:00:07 2006 UTC (18 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +21 -22 lines
Log Message:
*** empty log message ***

File Contents

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