comparison urogue/sticks.c @ 256:c495a4f288c6

Import UltraRogue from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 31 Jan 2017 19:56:04 -0500
parents
children e52a8a7ad4c5
comparison
equal deleted inserted replaced
253:d9badb9c0179 256:c495a4f288c6
1 /*
2 sticks.c - Functions to implement the various sticks one might find
3
4 UltraRogue: The Ultimate Adventure in the Dungeons of Doom
5 Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
6 All rights reserved.
7
8 Based on "Advanced Rogue"
9 Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
10 All rights reserved.
11
12 Based on "Rogue: Exploring the Dungeons of Doom"
13 Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
14 All rights reserved.
15
16 See the file LICENSE.TXT for full copyright and licensing information.
17 */
18
19 #include <limits.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include "rogue.h"
23
24 /* for WS_HIT, WS_WEB, etc */
25
26 static struct object null_stick =
27 {
28 {0,0},NULL,NULL,"",0,"0d0",0,0,'X',0,0,0,0,0,0,0,0,0,{0}
29 };
30
31 /*
32 * Mask for cancelling special abilities The flags listed here will be the
33 * ones left on after the cancellation takes place
34 */
35
36 #define CANC0MASK ( ISBLIND | ISINWALL | ISRUN | \
37 ISFLEE | ISMEAN | ISGREED | \
38 CANSHOOT | ISHELD | ISHUH | \
39 ISSLOW | ISHASTE | ISCLEAR | \
40 ISUNIQUE)
41
42 #define CANC1MASK ( HASDISEASE | DIDSUFFOCATE | CARRYGOLD | \
43 HASITCH | CANSELL | CANBBURN | \
44 CANSPEAK | CANFLY | ISFRIENDLY)
45
46 #define CANC2MASK ( HASINFEST | NOMOVE | ISSCAVENGE | \
47 DOROT | HASSTINK | DIDHOLD)
48
49 #define CANC3MASK ( ISUNDEAD | CANBREATHE | CANCAST | \
50 HASOXYGEN)
51
52 #define CANC4MASK ( CANTRAMPLE | CANSWIM | CANWIELD | \
53 ISFAST | CANBARGAIN | CANSPORE | \
54 ISLARGE | ISSMALL | ISFLOCK | \
55 ISSWARM | CANSTICK | CANTANGLE | \
56 SHOOTNEEDLE | CANZAP | HASARMOR | \
57 CANTELEPORT | ISBERSERK | ISFAMILIAR | \
58 HASFAMILIAR | SUMMONING)
59
60 #define CANC5MASK ( CANREFLECT | MAGICATTRACT | HASSHIELD | HASMSHIELD)
61
62 #define CANC6MASK ( 0 )
63 #define CANC7MASK ( 0 )
64 #define CANC8MASK ( 0 )
65 #define CANC9MASK ( 0 )
66 #define CANCAMASK ( 0 )
67 #define CANCBMASK ( 0 )
68 #define CANCCMASK ( 0 )
69 #define CANCDMASK ( 0 )
70 #define CANCEMASK ( 0 )
71 #define CANCFMASK ( 0 )
72
73 void
74 fix_stick(struct object *cur)
75 {
76 if (strcmp(ws_type[cur->o_which], "staff") == 0)
77 {
78 cur->o_weight = 100;
79 cur->o_charges = 5 + rnd(10);
80 cur->o_damage = "2d3";
81
82 switch (cur->o_which)
83 {
84 case WS_HIT:
85 cur->o_hplus = 3;
86 cur->o_dplus = 3;
87 cur->o_damage = "2d8";
88 break;
89
90 case WS_LIGHT:
91 cur->o_charges = 20 + rnd(10);
92 break;
93 }
94 }
95 else /* A wand */
96 {
97 cur->o_damage = "1d3";
98 cur->o_weight = 60;
99 cur->o_charges = 3 + rnd(5);
100
101 switch (cur->o_which)
102 {
103 case WS_HIT:
104 cur->o_hplus = 3;
105 cur->o_dplus = 3;
106 cur->o_damage = "1d8";
107 break;
108
109 case WS_LIGHT:
110 cur->o_charges = 10 + rnd(10);
111 break;
112 }
113 }
114
115 cur->o_hurldmg = "1d1";
116 }
117
118 /*
119 do_zap()
120 zap a stick (or effect a stick-like spell)
121 zapper: who does it
122 got_dir: need to ask for direction?
123 which: which WS_STICK (-1 means ask from pack)
124 flags: ISBLESSED, ISCURSED
125 */
126
127 void
128 do_zap(struct thing *zapper, int which, unsigned long flags)
129 {
130 struct linked_list *item = NULL, *nitem;
131 struct object *obj, *nobj;
132 struct room *rp;
133 struct thing *tp = NULL;
134 char *mname = NULL;
135 int y, x;
136 int blessed = flags & ISBLESSED;
137 int cursed = flags & ISCURSED;
138 int is_stick = (which < 0 ? TRUE : FALSE);
139 int got_one = TRUE;
140
141 if (zapper != &player)
142 {
143 monster_do_zap(zapper, which, flags);
144 return;
145 }
146
147 if (is_stick)
148 {
149 if ((obj = get_object(pack, "zap with", 0, bff_zappable)) == NULL)
150 return;
151
152 if (obj->o_type != STICK && !(obj->o_flags & ISZAPPED))
153 {
154 msg("You can't zap with that!");
155 return;
156 }
157
158 if (obj->o_type != STICK) /* an electrified weapon */
159 which = WS_ELECT;
160 else
161 which = obj->o_which;
162
163 if (obj->o_charges < 1)
164 {
165 nothing_message(flags);
166 return;
167 }
168
169 obj->o_charges--;
170
171 if (delta.y == 0 && delta.x == 0)
172 do
173 {
174 delta.y = rnd(3) - 1;
175 delta.x = rnd(3) - 1;
176 }
177 while (delta.y == 0 && delta.x == 0);
178
179 flags = obj->o_flags;
180 cursed = obj->o_flags & ISCURSED;
181 blessed = obj->o_flags & ISBLESSED;
182 }
183 else
184 obj = &null_stick;
185
186 /* Find out who the target is */
187
188 y = hero.y;
189 x = hero.x;
190
191 while(shoot_ok(CCHAR(winat(y, x))))
192 {
193 y += delta.y;
194 x += delta.x;
195 }
196
197 if (x >= 0 && x < COLS && y >= 1 && y < LINES - 2 &&
198 isalpha(mvwinch(mw, y, x)))
199 {
200 item = find_mons(y, x);
201 tp = THINGPTR(item);
202 mname = on(player, ISBLIND) ? "monster" :
203 monsters[tp->t_index].m_name;
204
205 if (on(*tp, CANSELL))
206 {
207 luck++;
208 aggravate();
209 }
210 }
211 else
212 got_one = FALSE;
213
214 debug("Zapping with %d", which);
215
216 switch(which)
217 {
218 case WS_LIGHT:
219 /* Reddy Kilowat wand. Light up the room */
220 if (blue_light(flags) && is_stick)
221 if (is_stick)
222 know_items[TYP_STICK][WS_LIGHT] = TRUE;
223 break;
224
225 case WS_DRAIN:
226
227 /*
228 * Take away 1/2 of hero's hit points, then take it away
229 * evenly from the monsters in the room or next to hero if he
230 * is in a passage. Leave the monsters alone if the stick is
231 * cursed. Drain 1/3rd of hero's hit points if blessed.
232 */
233
234 if (pstats.s_hpt < 2)
235 {
236 death(D_DRAINLIFE);
237 return;
238 }
239
240 if (cursed)
241 pstats.s_hpt /= 2;
242 else if ((rp = roomin(hero)) == NULL)
243 drain(hero.y - 1, hero.y + 1, hero.x - 1, hero.x + 1);
244 else
245 drain(rp->r_pos.y, rp->r_pos.y + rp->r_max.y,
246 rp->r_pos.x, rp->r_pos.x + rp->r_max.x);
247
248 if (blessed)
249 pstats.s_hpt = (int) (pstats.s_hpt * 2.0 / 3.0);
250
251 break;
252
253 case WS_POLYMORPH:
254 case WS_MONSTELEP:
255 case WS_CANCEL:
256 {
257 char oldch;
258 int rm;
259 int save_adj = 0;
260
261 if (got_one)
262 {
263 /* if the monster gets the saving throw, leave the case */
264
265 if (blessed)
266 save_adj = -5;
267
268 if (cursed)
269 if (which == WS_POLYMORPH)
270 save_adj = -5; /* not save vs becoming tougher */
271 else
272 save_adj = 5;
273
274 if (save_throw(VS_MAGIC - save_adj, tp))
275 {
276 nothing_message(flags);
277 break;
278 }
279 else if (is_stick)
280 know_items[TYP_STICK][which] = TRUE;
281
282 /* Unhold player */
283
284 if (on(*tp, DIDHOLD))
285 {
286 turn_off(*tp, DIDHOLD);
287
288 if (--hold_count == 0)
289 turn_off(player, ISHELD);
290 }
291
292 /* unsuffocate player */
293
294 if (on(*tp, DIDSUFFOCATE))
295 {
296 turn_off(*tp, DIDSUFFOCATE);
297 extinguish_fuse(FUSE_SUFFOCATE);
298 }
299
300 if (which == WS_POLYMORPH)
301 {
302 int which_new;
303 int charmed;
304
305 detach(mlist, item);
306 charmed = on(*tp, ISCHARMED);
307 oldch = tp->t_oldch;
308 delta.y = y;
309 delta.x = x;
310
311 if (!blessed && !cursed)
312 which_new = randmonster(WANDER, GRAB);
313 else
314 {
315 /* duplicate randmonster() for now */
316 /* Eventually fix to take level */
317
318 int cur_level = 0, range, i;
319
320 if (blessed)
321 cur_level = level / 2;
322
323 if (cursed)
324 cur_level = level * 2;
325
326 range = 4 * NLEVMONS;
327 i = 0;
328
329 do
330 {
331 if (i++ > range * 10) /* just in case all have */
332 { /* been genocided */
333 i = 0;
334
335 if (--cur_level <= 0)
336 fatal("Rogue could not find a monster to make");
337 }
338
339 which_new = NLEVMONS * (cur_level - 1) + (rnd(range) - (range - 1 - NLEVMONS));
340
341 if (which_new < 1)
342 which_new = rnd(NLEVMONS) + 1;
343
344 if (which_new > nummonst - NUMSUMMON - 1)
345 {
346 if (blessed)
347 which_new = rnd(range) + (nummonst - NUMSUMMON - 1) - (range - 1);
348 else if (which_new > nummonst - 1)
349 which_new = rnd(range + NUMSUMMON) + (nummonst - 1) - (range + NUMSUMMON - 1);
350 }
351 }
352 while (!monsters[which_new].m_normal);
353 }
354
355 new_monster(item, which_new, &delta, NOMAXSTATS);
356 mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name;
357
358 if (!cursed && charmed)
359 turn_on(*tp, ISCHARMED);
360
361 if (off(*tp, ISRUN))
362 chase_it(&delta, &player);
363
364 if (isalpha(mvwinch(cw, y, x)))
365 mvwaddch(cw, y, x, tp->t_type);
366
367 tp->t_oldch = oldch;
368 seemsg("You have created a new %s!", mname);
369 }
370 else if (which == WS_CANCEL)
371 {
372 tp->t_flags[0] &= CANC0MASK;
373 tp->t_flags[1] &= CANC1MASK;
374 tp->t_flags[2] &= CANC2MASK;
375 tp->t_flags[3] &= CANC3MASK;
376 tp->t_flags[4] &= CANC4MASK;
377 tp->t_flags[5] &= CANC5MASK;
378 tp->t_flags[6] &= CANC5MASK;
379 tp->t_flags[7] &= CANC7MASK;
380 tp->t_flags[8] &= CANC8MASK;
381 tp->t_flags[9] &= CANC9MASK;
382 tp->t_flags[10] &= CANCAMASK;
383 tp->t_flags[11] &= CANCBMASK;
384 tp->t_flags[12] &= CANCCMASK;
385 tp->t_flags[13] &= CANCDMASK;
386 tp->t_flags[14] &= CANCEMASK;
387 tp->t_flags[15] &= CANCFMASK;
388 }
389 else /* A teleport stick */
390 {
391 if (cursed) /* Teleport monster to */
392 { /* player */
393 if ((y == (hero.y + delta.y)) &&
394 (x == (hero.x + delta.x)))
395 nothing_message(flags);
396 else
397 {
398 tp->t_pos.y = hero.y + delta.y;
399 tp->t_pos.x = hero.x + delta.x;
400 }
401 }
402 else if (blessed) /* Get rid of monster */
403 {
404 killed(NULL, item, NOMESSAGE, NOPOINTS);
405 return;
406 }
407 else
408 {
409 int i = 0;
410
411 do /* Move monster to */
412 { /* another room */
413 rm = rnd_room();
414 rnd_pos(&rooms[rm], &tp->t_pos);
415 }
416 while (winat(tp->t_pos.y, tp->t_pos.x) !=
417 FLOOR && i++ < 500);
418 }
419
420 /* Now move the monster */
421
422 if (isalpha(mvwinch(cw, y, x)))
423 mvwaddch(cw, y, x, tp->t_oldch);
424
425 chase_it(&tp->t_pos, &player);
426 mvwaddch(mw, y, x, ' ');
427 mvwaddch(mw, tp->t_pos.y, tp->t_pos.x, tp->t_oldch);
428
429 if (tp->t_pos.y != y || tp->t_pos.x != x)
430 tp->t_oldch = CCHAR(mvwinch(cw, tp->t_pos.y, tp->t_pos.x));
431 }
432 }
433 }
434 break;
435
436 case WS_MISSILE:
437 {
438 int damage;
439 int nsides = 4;
440 int ch;
441 coord pos;
442
443 if (is_stick)
444 know_items[TYP_STICK][which] = TRUE;
445
446 /* Magic Missiles *always* hit, no saving throw */
447
448 pos = do_motion('*', delta.y, delta.x, &player);
449 ch = winat(pos.y, pos.x);
450
451 if (cursed)
452 nsides /= 2;
453 else if (blessed)
454 nsides *= 2;
455
456 damage = roll(pstats.s_lvl, nsides);
457
458 if (isalpha(ch))
459 {
460 debug("Missiled %s for %d (%d)",
461 mname, damage, tp->t_stats.s_hpt - damage);
462
463 if ((tp->t_stats.s_hpt -= damage) <= 0)
464 {
465 seemsg("The missile kills the %s.", mname);
466 killed(&player, item, NOMESSAGE, POINTS);
467 }
468 else
469 {
470 seemsg("The missile hits the %s", mname);
471 chase_it(&pos, &player);
472 summon_help(tp, NOFORCE);
473 }
474 }
475
476 if (pos.y >= 0 && pos.x >= 0 &&
477 pos.y < LINES && pos.x < COLS)
478 mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x));
479 }
480 break;
481
482 case WS_HIT:
483 {
484 coord pos;
485 int ch;
486 struct object strike; /* don't want to change sticks attributes */
487
488 if (is_stick)
489 know_items[TYP_STICK][which] = TRUE;
490
491 pos = do_motion('@', delta.y, delta.x, &player);
492 ch = winat(pos.y, pos.x);
493
494 if (isalpha(ch))
495 {
496 static char buf[20];
497 int nsides, ndice;
498
499 strike = *obj;
500
501 if (blessed)
502 strike.o_hplus = 12;
503 else if (cursed)
504 strike.o_hplus = 3;
505 else
506 strike.o_hplus = 6;
507
508 if (!is_stick || strcmp(ws_type[which], "staff") == 0)
509 ndice = 3;
510 else
511 ndice = 2;
512
513 if (blessed)
514 nsides = 16;
515 else if (cursed)
516 nsides = 4;
517 else
518 nsides = 8;
519
520 sprintf(buf, "%dd%d", ndice, nsides);
521
522 strike.o_damage = buf;
523 fight(&tp->t_pos, &strike, NOTHROWN);
524 }
525 }
526 break;
527
528 case WS_SLOW_M:
529 if (got_one)
530 {
531 if (cursed)
532 {
533 if (off(*tp, ISSLOW))
534 turn_on(*tp, ISHASTE);
535 else
536 turn_off(*tp, ISSLOW);
537 }
538 else if (blessed)
539 {
540 if (is_stick)
541 know_items[TYP_STICK][which] = TRUE;
542
543 turn_off(*tp, ISRUN);
544 turn_on(*tp, ISHELD);
545 return;
546 }
547 else
548 {
549 if (is_stick)
550 know_items[TYP_STICK][which] = TRUE;
551
552 if (off(*tp, ISHASTE))
553 turn_on(*tp, ISSLOW);
554 else
555 turn_off(*tp, ISHASTE);
556
557 tp->t_turn = TRUE;
558 }
559
560 delta.y = y;
561 delta.x = x;
562 chase_it(&delta, &player);
563 }
564 break;
565
566 case WS_CHARGE:
567 if (know_items[TYP_STICK][which] != TRUE && is_stick)
568 {
569 msg("This is a wand of charging.");
570 know_items[TYP_STICK][which] = TRUE;
571 }
572
573 if ((nitem = get_item("charge", STICK)) != NULL)
574 {
575 nobj = OBJPTR(nitem);
576
577 if ((nobj->o_charges == 0) && (nobj->o_which != WS_CHARGE))
578 {
579 fix_stick(nobj);
580
581 if (blessed)
582 nobj->o_charges *= 2;
583 else if (cursed)
584 nobj->o_charges /= 2;
585 }
586 else
587 {
588 if (blessed)
589 nobj->o_charges += 2;
590 else if (cursed)
591 nobj->o_charges -= 1;
592 else
593 nobj->o_charges += 1;
594 }
595 }
596 break;
597
598 case WS_ELECT:
599 case WS_FIRE:
600 case WS_COLD:
601 {
602 char *name = NULL;
603 int damage;
604 int ndice = 3 + pstats.s_lvl;
605 int nsides;
606
607 if (is_stick)
608 {
609 know_items[TYP_STICK][which] = TRUE;
610
611 if (strcmp(ws_type[which], "staff") == 0)
612 nsides = 8;
613 else
614 nsides = 4;
615 }
616 else
617 nsides = 6;
618
619 if (cursed)
620 nsides /= 2;
621 else if (blessed)
622 nsides *= 2;
623
624 switch (which)
625 {
626 case WS_ELECT:
627 name = "lightning bolt";
628
629 if (rnd(2))
630 ndice += rnd(obj->o_charges / 10);
631 else
632 nsides += rnd(obj->o_charges / 10);
633
634 break;
635
636 case WS_FIRE:
637 name = "flame";
638 break;
639
640 case WS_COLD:
641 name = "ice";
642 break;
643 }
644
645 damage = roll(ndice, nsides);
646 shoot_bolt(&player, hero, delta, POINTS, D_BOLT, name, damage);
647 }
648 break;
649
650 case WS_ANTIMATTER:
651 {
652 int m1, m2, x1, y1;
653 char ch;
654 struct linked_list *ll;
655 struct thing *lt;
656
657 if (is_stick)
658 know_items[TYP_STICK][which] = TRUE;
659
660 y1 = hero.y;
661 x1 = hero.x;
662
663 do
664 {
665 y1 += delta.y;
666 x1 += delta.x;
667 ch = winat(y1, x1);
668 }
669 while (ch == PASSAGE || ch == FLOOR);
670
671 for (m1 = x1 - 1; m1 <= x1 + 1; m1++)
672 {
673 for (m2 = y1 - 1; m2 <= y1 + 1; m2++)
674 {
675 ch = winat(m2, m1);
676
677 if (m1 == hero.x && m2 == hero.y)
678 continue;
679
680 if (ch != ' ')
681 {
682 ll = find_obj(m2, m1);
683
684 while (ll != NULL)
685 {
686 rem_obj(ll, TRUE);
687 ll = find_obj(m2, m1);
688 }
689
690 ll = find_mons(m2, m1);
691
692 if (ll != NULL)
693 {
694 lt = THINGPTR(ll);
695 if (on(*lt, CANSELL))
696 {
697 luck++;
698 aggravate();
699 }
700
701 if (off(*lt, CANINWALL))
702 {
703 check_residue(lt);
704 detach(mlist, ll);
705 discard(ll);
706 mvwaddch(mw, m2, m1, ' ');
707 }
708 }
709
710 mvaddch(m2, m1, ' ');
711 mvwaddch(cw, m2, m1, ' ');
712 }
713 }
714 }
715
716 touchwin(cw);
717 touchwin(mw);
718 }
719 break;
720
721 case WS_CONFMON:
722 case WS_PARALYZE:
723 if (got_one)
724 {
725 if (save_throw(VS_MAGIC - (blessed ? 5 : (cursed ? -5 : 0)), tp))
726 nothing_message(flags);
727 else
728 {
729 if (is_stick)
730 know_items[TYP_STICK][which] = TRUE;
731
732 switch(which)
733 {
734 case WS_CONFMON:
735 seemsg("The %s looks bewildered.", mname);
736 turn_on(*tp, ISHUH);
737 break;
738
739 case WS_PARALYZE:
740 seemsg("The %s looks stunned.", mname);
741 tp->t_no_move = FREEZETIME;
742 break;
743 }
744 }
745
746 delta.y = y;
747 delta.x = x;
748 chase_it(&delta, &player);
749 }
750 else
751 nothing_message(flags);
752
753 break;
754
755 case WS_XENOHEALING:
756 {
757 int hpt_gain = roll(zapper->t_stats.s_lvl, (blessed ? 8 : 4));
758 int mons_hpt = tp->t_stats.s_hpt;
759
760 if (got_one)
761 {
762 if (cursed)
763 {
764 if (!save_throw(VS_MAGIC, tp))
765 {
766 if ((mons_hpt -= hpt_gain) <= 0) {
767 seemsg("The %s shrivels up and dies!", mname);
768 killed(&player, item, NOMESSAGE, POINTS);
769 }
770 else
771 seemsg("Wounds appear all over the %s.", mname);
772 }
773 else
774 nothing_message(flags);
775
776 delta.y = y;
777 delta.x = x;
778 chase_it(&delta, &player);
779 }
780 else
781 {
782 if (is_stick)
783 know_items[TYP_STICK][which] = TRUE;
784
785 mons_hpt = min(mons_hpt + hpt_gain, tp->maxstats.s_hpt);
786 seemsg("The %s appears less wounded!", mname);
787 }
788 }
789 else
790 nothing_message(flags);
791 }
792 break;
793
794 case WS_DISINTEGRATE:
795 if (got_one)
796 {
797 if (cursed)
798 {
799 if (on(*tp, ISUNIQUE))
800 {
801 if (on(*tp, CANSUMMON))
802 summon_help(tp, FORCE);
803 else
804 msg("The %s is very upset.", mname);
805
806 turn_on(*tp, ISHASTE);
807 }
808 else
809 {
810 int m1, m2;
811 coord mp;
812 struct linked_list *titem;
813 int ch;
814 struct thing *th;
815
816 for (m1 = tp->t_pos.x - 1; m1 <= tp->t_pos.x + 1; m1++)
817 {
818 for (m2 = tp->t_pos.y - 1; m2 <= tp->t_pos.y + 1; m2++)
819 {
820 ch = winat(m2, m1);
821
822 if (shoot_ok(ch) && ch != PLAYER)
823 {
824 mp.x = m1; /* create it */
825 mp.y = m2;
826 titem = new_item(sizeof(struct thing));
827 new_monster(titem, (short) tp->t_index, &mp, NOMAXSTATS);
828 th = THINGPTR(titem);
829 turn_on(*th, ISMEAN);
830 chase_it(&mp, &player);
831 }
832 }
833 }
834 }
835
836 delta.y = y;
837 delta.x = x;
838 turn_on(*tp, ISMEAN);
839 chase_it(&delta, &player);
840 }
841 else /* if its a UNIQUE it might still live */
842 {
843 if (is_stick)
844 know_items[TYP_STICK][which] = TRUE;
845
846 if (on(*tp, ISUNIQUE) &&
847 save_throw(VS_MAGIC - (blessed ? -5 : 0), tp))
848 {
849 tp->t_stats.s_hpt = tp->t_stats.s_hpt / 2 - 1;
850
851 if (tp->t_stats.s_hpt < 1)
852 {
853 killed(&player, item, NOMESSAGE, POINTS);
854 seemsg("The %s fades away.", mname);
855 }
856 else
857 {
858 delta.y = y;
859 delta.x = x;
860
861 chase_it(&delta, &player);
862
863 if (on(*tp, CANSUMMON))
864 summon_help(tp, FORCE);
865 else
866 seemsg("The %s is very upset.", mname);
867 }
868 }
869 else
870 {
871 if (on(*tp, CANSELL))
872 {
873 luck++;
874 aggravate();
875 }
876 seemsg("The %s turns to dust and blows away.", mname);
877 killed(&player, item, NOMESSAGE, POINTS);
878 }
879 }
880 }
881 break;
882
883 case WS_NOTHING:
884 nothing_message(flags);
885 break;
886
887 case WS_INVIS:
888 {
889 if (cursed)
890 {
891 int x1, y1, x2, y2;
892 int zapped = FALSE;
893
894 if ((rp = roomin(hero)) == NULL)
895 {
896 x1 = max(hero.x - 1, 0);
897 y1 = max(hero.y - 1, 0);
898 x2 = min(hero.x + 1, COLS - 1);
899 y2 = min(hero.y + 1, LINES - 3);
900 }
901 else
902 {
903 x1 = rp->r_pos.x;
904 y1 = rp->r_pos.y;
905 x2 = rp->r_pos.x + rp->r_max.x;
906 y2 = rp->r_pos.y + rp->r_max.y;
907 }
908
909 for (item = mlist; item != NULL; item = next(item))
910 {
911 tp = THINGPTR(item);
912
913 if (tp->t_pos.x >= x1 && tp->t_pos.x <= x2 &&
914 tp->t_pos.y >= y1 && tp->t_pos.y <= y2)
915 {
916 turn_on(*tp, ISINVIS);
917 turn_on(*tp, ISRUN);
918 turn_off(*tp, ISDISGUISE);
919 chase_it(&tp->t_pos, &player);
920 zapped = TRUE;
921 }
922 }
923
924 if (zapped)
925 seemsg("The monsters seem to have all disappeared.");
926 else
927 nothing_message(flags);
928 }
929 else
930 {
931 if (got_one)
932 {
933 if (is_stick)
934 know_items[TYP_STICK][which] = TRUE;
935
936 if (blessed)
937 {
938 turn_off(*tp, ISINVIS);
939 turn_off(*tp, ISSHADOW);
940 seemsg("The %s appears.", mname);
941 }
942 else
943 {
944 turn_on(*tp, ISINVIS);
945 seemsg("The %s disappears.", mname);
946 }
947 }
948 else
949 nothing_message(flags);
950 }
951 light(&hero);
952 }
953 break;
954
955 case WS_BLAST:
956 {
957 int ch;
958 struct linked_list *itm, *ip;
959 struct object *ob;
960 struct trap *trp;
961
962 if (is_stick)
963 know_items[TYP_STICK][which] = TRUE;
964
965 itm = spec_item(WEAPON, GRENADE, 0, 0);
966 ob = OBJPTR(itm);
967 ob->o_count = 1;
968 ob->o_flags |= ISKNOW;
969 hearmsg("BOOOM!");
970 aggravate();
971 ob->o_pos.x = hero.x;
972 ob->o_pos.y = hero.y;
973
974 for (;;)
975 {
976 ob->o_pos.y += delta.y;
977 ob->o_pos.x += delta.x;
978
979 if (!ce(ob->o_pos, hero) &&
980 cansee(ob->o_pos.y, ob->o_pos.x) &&
981 mvwinch(cw, ob->o_pos.y, ob->o_pos.x) != ' ')
982 {
983 mvwaddch(cw, ob->o_pos.y, ob->o_pos.x,
984 show(ob->o_pos.y, ob->o_pos.x));
985 }
986
987 if (shoot_ok(ch = winat(ob->o_pos.y, ob->o_pos.x))
988 && ch != DOOR && !ce(ob->o_pos, hero))
989 {
990 if (cansee(ob->o_pos.y, ob->o_pos.x) &&
991 ntraps + 1 < 2 * MAXTRAPS &&
992 mvwinch(cw, ob->o_pos.y, ob->o_pos.x) != ' ')
993 {
994 mvwaddch(cw, ob->o_pos.y, ob->o_pos.x,TRAPDOOR);
995 wrefresh(cw);
996 }
997
998 if (isatrap(ch))
999 {
1000 trp = trap_at(ob->o_pos.y, ob->o_pos.x);
1001
1002 if (trp != NULL)
1003 {
1004 trp->tr_type = TRAPDOOR;
1005 trp->tr_flags |= ISFOUND;
1006 trp->tr_show = TRAPDOOR;
1007 }
1008 }
1009 else if (isalpha(ch))
1010 hit_monster(ob->o_pos.y, ob->o_pos.x, ob, &player);
1011 else if ((ch == FLOOR || ch == PASSAGE)
1012 && ntraps + 1 < 2 * MAXTRAPS)
1013 {
1014 mvaddch(ob->o_pos.y, ob->o_pos.x, TRAPDOOR);
1015 traps[ntraps].tr_type = TRAPDOOR;
1016 traps[ntraps].tr_flags = ISFOUND;
1017 traps[ntraps].tr_show = TRAPDOOR;
1018 traps[ntraps].tr_pos.y = ob->o_pos.y;
1019 traps[ntraps++].tr_pos.x = ob->o_pos.x;
1020 }
1021 else if (ch == POTION || ch == SCROLL || ch == FOOD
1022 || ch == WEAPON || ch == RING || ch == ARMOR
1023 || ch == STICK || ch == ARTIFACT || ch == GOLD)
1024 {
1025 ip = find_obj(ob->o_pos.y, ob->o_pos.x);
1026
1027 while (ip) /* BOOM destroys all stacked objects */
1028 {
1029 rem_obj(ip, TRUE);
1030 ip = find_obj(ob->o_pos.y, ob->o_pos.x);
1031 }
1032
1033 mvaddch(ob->o_pos.y, ob->o_pos.x,
1034 (roomin(ob->o_pos) == NULL ? PASSAGE : FLOOR) );
1035 }
1036 continue;
1037 }
1038 break;
1039 }
1040 discard(itm);
1041 }
1042 break;
1043
1044 case WS_WEB:
1045 {
1046 int ch;
1047 coord pos;
1048
1049 if (is_stick)
1050 know_items[TYP_STICK][which] = TRUE;
1051
1052 pos = do_motion('#', delta.y, delta.x, &player);
1053 ch = winat(pos.y, pos.x);
1054
1055 if (isalpha(ch))
1056 {
1057 if (save_throw(VS_MAGIC, tp))
1058 seemsg("The %s evades your web.", mname);
1059 else
1060 {
1061 seemsg("The %s is webbed.", mname);
1062 turn_off(*tp, ISRUN);
1063 turn_on(*tp, ISHELD);
1064 }
1065 }
1066
1067 if (pos.y >= 0 && pos.x >= 0 &&
1068 pos.y < LINES && pos.x < COLS)
1069 mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x));
1070 }
1071 break;
1072
1073 case WS_KNOCK:
1074 case WS_CLOSE:
1075 if (blessed) /* Do all doors in room */
1076 {
1077 char new_door = (which == WS_KNOCK ? DOOR : SECRETDOOR);
1078 struct room *rmp = roomin(hero);
1079
1080 if (rmp != NULL)
1081 for (x = 0; x < rmp->r_nexits; x++)
1082 {
1083 move(rmp->r_exit[x].y, rmp->r_exit[x].x);
1084 addch(new_door);
1085 }
1086 else /* Do all adjacent doors */
1087 for (x = hero.x - 1; x <= hero.x + 1; x++)
1088 for (y = hero.y - 1; y <= hero.y + 1; y++)
1089 switch(CCHAR( winat(y, x) ))
1090 {
1091 case DOOR:
1092 case SECRETDOOR:
1093 addch(new_door);
1094 break;
1095 }
1096 light(&hero);
1097 }
1098 else if (cursed)/* do opposite spell */
1099 do_zap(zapper, (which == WS_KNOCK ? WS_CLOSE : WS_KNOCK), ISBLESSED);
1100 else
1101 {
1102 coord zap_loc;
1103 char loc;
1104
1105 zap_loc.y = hero.y + delta.y;
1106 zap_loc.x = hero.x + delta.x;
1107 loc = winat(zap_loc.y, zap_loc.x);
1108
1109 switch (loc)
1110 {
1111 case SECRETDOOR:
1112 if (which == WS_KNOCK)
1113 {
1114 mvaddch(zap_loc.y, zap_loc.x, DOOR);
1115 seemsg("A secret door stands revealed before you!");
1116
1117 if (is_stick)
1118 know_items[TYP_STICK][which] = TRUE;
1119 }
1120 else
1121 nothing_message(flags);
1122 break;
1123
1124 case DOOR:
1125
1126 if (which == WS_CLOSE)
1127 {
1128 mvaddch(zap_loc.y, zap_loc.x, SECRETDOOR);
1129 msg("You sucessfully block off the door.")
1130 ;
1131 if (is_stick)
1132 know_items[TYP_STICK][which] = TRUE;
1133 }
1134 else
1135 nothing_message(flags);
1136
1137 break;
1138
1139 default:
1140 nothing_message(flags);
1141 }
1142 }
1143
1144 break;
1145 default:
1146 msg("What a bizarre schtick!");
1147 }
1148 }
1149
1150 /*
1151 drain()
1152 Do drain hit points from player shtick
1153 */
1154
1155 void
1156 drain(int ymin, int ymax, int xmin, int xmax)
1157 {
1158 int i, j, cnt;
1159 struct thing *ick;
1160 struct linked_list *item;
1161
1162 /* First count how many things we need to spread the hit points among */
1163
1164 for (cnt = 0, i = ymin; i <= ymax; i++)
1165 for (j = xmin; j <= xmax; j++)
1166 if (isalpha(mvwinch(mw, i, j)))
1167 cnt++;
1168
1169 if (cnt == 0)
1170 {
1171 msg("You have a tingling feeling.");
1172 return;
1173 }
1174 else
1175 msg("You feel weaker.");
1176
1177 cnt = pstats.s_hpt / cnt;
1178 pstats.s_hpt /= 2;
1179
1180 /* Now zot all of the monsters */
1181
1182 for (i = ymin; i <= ymax; i++)
1183 for (j = xmin; j <= xmax; j++)
1184 if (isalpha(mvwinch(mw, i, j)) && ((item = find_mons(i, j)) != NULL))
1185 {
1186 ick = THINGPTR(item);
1187
1188 if (on(*ick, CANSELL))
1189 {
1190 luck++;
1191 aggravate();
1192 }
1193
1194 if ((ick->t_stats.s_hpt -= cnt) < 1)
1195 killed(&player, item,
1196 cansee(i, j) && !on(*ick, ISINVIS), POINTS);
1197 }
1198 }
1199
1200 /*
1201 charge_str()
1202 charge a wand for wizards.
1203 */
1204
1205 char *
1206 charge_str(struct object *obj, char *buf)
1207 {
1208 if (buf == NULL)
1209 return( "[UltraRogue Bug #103]" );
1210
1211 if (!(obj->o_flags & ISKNOW))
1212 buf[0] = '\0';
1213 else if (obj->o_charges == 1)
1214 sprintf(buf, " [%d charge]", obj->o_charges);
1215 else
1216 sprintf(buf, " [%d charges]", obj->o_charges);
1217
1218 return(buf);
1219 }
1220
1221
1222 /*
1223 shoot_bolt()
1224 fires a bolt from the given starting point in the given direction
1225 struct thing *shooter;
1226 coord start, dir;
1227 int get_points; should player get exp points?
1228 short reason; reason for dying
1229 char *name; fire, nerve, cold, etc
1230 int damage; make zapee suffer
1231 */
1232
1233 void
1234 shoot_bolt(struct thing *shooter, coord start, coord dir, int get_points, int reason, char *name, int damage)
1235 {
1236 int ch;
1237 char dirch;
1238 int change;
1239 short y, x;
1240 coord pos;
1241 coord spotpos[BOLT_LENGTH + 1];
1242 int ret_val = FALSE;/* True if breathing monster gets killed */
1243 struct linked_list *item;
1244 struct thing *tp;
1245 char *mname;
1246 int bounced; /* where along BOLT_LENGTH it hit a wall */
1247 int player_damage; /* damage if player saved */
1248 int no_effect; /* zap does not effect zappee */
1249 int take_that[BOLT_LENGTH + 1]; /* damage to each monster */
1250
1251 /* last spot for player */
1252
1253 debug("%s does %d damage", name, damage);
1254
1255 switch (dir.y + dir.x)
1256 {
1257 case 0:
1258 dirch = '/';
1259 break;
1260
1261 case 1:
1262 case -1:
1263 dirch = (dir.y == 0 ? '-' : '|');
1264 break;
1265
1266 case 2:
1267 case -2:
1268 dirch = '\\';
1269 break;
1270 }
1271
1272 pos.y = start.y + dir.y;
1273 pos.x = start.x + dir.x;
1274 change = FALSE;
1275
1276 for (y = 0; y < BOLT_LENGTH + 1; y++)
1277 take_that[y] = 0;
1278
1279 bounced = 0;
1280
1281 for (y = 0; y < BOLT_LENGTH; y++)
1282 {
1283 no_effect = FALSE;
1284 ch = winat(pos.y, pos.x);
1285 spotpos[y] = pos;
1286
1287 /* Bolt damage dimishes over space */
1288
1289 damage = max(1, damage - (y / 3));
1290
1291 /* Are we at hero? */
1292
1293 if (ce(pos, hero))
1294 goto at_hero;
1295
1296 switch(ch)
1297 {
1298 case SECRETDOOR:
1299 case '|':
1300 case '-':
1301 case ' ':
1302 bounced = y;
1303 if (dirch == '-' || dirch == '|')
1304 {
1305 dir.y = -dir.y;
1306 dir.x = -dir.x;
1307 }
1308 else
1309 switch (ch)
1310 {
1311 case '|':
1312 case '-':
1313 case SECRETDOOR:
1314 {
1315 struct room *rp;
1316
1317 rp = roomin(pos);
1318
1319 if (pos.y == rp->r_pos.y ||
1320 pos.y == rp->r_pos.y + rp->r_max.y - 1)
1321 {
1322 dir.y = -dir.y;
1323 change ^= TRUE;
1324 }
1325
1326 if (pos.x == rp->r_pos.x ||
1327 pos.x == rp->r_pos.x + rp->r_max.x - 1)
1328 {
1329 dir.x = -dir.x;
1330 change ^= TRUE;
1331 }
1332 }
1333 default: /* A wall */
1334 {
1335 char chy = CCHAR( mvinch(pos.y - dir.y, pos.x + dir.x)), chx = CCHAR( mvinch(pos.y + dir.y, pos.x - dir.x) );
1336
1337 if (chy != WALL && chy != SECRETDOOR &&
1338 chy != '-' && chy != '|')
1339 {
1340 dir.y = -dir.y;
1341 change = TRUE;
1342 }
1343 else if (chx != WALL && chx != SECRETDOOR &&
1344 chx != '-' && chx != '|')
1345 {
1346 dir.x = -dir.x;
1347 change = TRUE;
1348 }
1349 else
1350 {
1351 dir.y = -dir.y;
1352 dir.x = -dir.x;
1353 }
1354 }
1355 }
1356
1357 /* Do we change how the bolt looks? */
1358
1359 if (change)
1360 {
1361 change = FALSE;
1362
1363 if (dirch == '\\')
1364 dirch = '/';
1365 else if (dirch == '/')
1366 dirch = '\\';
1367 }
1368
1369 break;
1370
1371 default:
1372 if (isalpha(ch)) /* hit a monster */
1373 {
1374 item = find_mons(pos.y, pos.x);
1375
1376 if (item == NULL)
1377 {
1378 debug("Can't find monster %c @ %d %d.",
1379 ch, pos.y, pos.x);
1380
1381 continue;
1382 }
1383
1384 tp = THINGPTR(item);
1385 mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name;
1386
1387 /* Disguised monsters stay hidden if they save */
1388
1389 if (on(*tp, ISDISGUISE) && save_throw(VS_MAGIC, tp) &&
1390 (tp->t_type != tp->t_disguise) && off(player, ISBLIND))
1391 {
1392 msg("Wait! That's a %s!", mname);
1393 turn_off(*tp, ISDISGUISE);
1394 }
1395
1396 tp->t_wasshot = TRUE;
1397
1398 if (on(*tp, CANSELL))
1399 {
1400 luck++;
1401 aggravate();
1402 }
1403
1404 /* Hit the monster -- does it do damage? */
1405
1406 if (strcmp(name, "ice") == 0)
1407 {
1408 if (on(*tp, NOCOLD) || on(*tp, ISUNDEAD))
1409 no_effect = TRUE;
1410 }
1411 else if (strcmp(name, "flame") == 0)
1412 {
1413 if (on(*tp, NOFIRE))
1414 no_effect = TRUE;
1415
1416 if (on(*tp, CANBBURN))
1417 {
1418 seemsg("The %s is burned to death by the flame.",
1419 mname);
1420
1421 take_that[y] += tp->t_stats.s_hpt + 1;
1422 ret_val = TRUE;
1423 }
1424 }
1425 else if (strcmp(name, "lightning bolt") == 0)
1426 {
1427 if (on(*tp, NOBOLT))
1428 no_effect = TRUE;
1429
1430 if (on(*tp, BOLTDIVIDE))
1431 {
1432 no_effect = TRUE;
1433
1434 if (creat_mons(tp, tp->t_index, NOMESSAGE))
1435 seemsg("The %s divides the %s.", name, mname);
1436 }
1437 }
1438
1439 if (no_effect == TRUE)
1440 {
1441 msg("The %s has no effect on the %s.", name,
1442 on(player, ISBLIND) ? "monster" : mname);
1443 }
1444 else
1445 {
1446 take_that[(bounced ? bounced-- : y)] +=
1447 save_throw(VS_MAGIC, tp) ? damage / 2 : damage;
1448 }
1449 }
1450 else if (pos.y == hero.y && pos.x == hero.x)
1451 {
1452 at_hero:
1453 player_damage = damage;
1454 running = fighting = FALSE;
1455 bounced = 0;
1456
1457 if (cur_armor != NULL &&
1458 cur_armor->o_which == CRYSTAL_ARMOR &&
1459 (strcmp(name, "acid") == 0))
1460 {
1461 player_damage = 0;
1462 msg("The acid splashes harmlessly against your armor!");
1463 }
1464 else if (((cur_armor != NULL &&
1465 cur_armor->o_which == CRYSTAL_ARMOR) ||
1466 (on(player, ISELECTRIC)) ||
1467 is_wearing(R_ELECTRESIST)) &&
1468 (strcmp(name, "lightning bolt") == 0))
1469 {
1470 player_damage = 0;
1471
1472 if (rnd(100) < 75
1473 && cur_weapon != NULL
1474 && shooter != &player)
1475 {
1476 cur_weapon->o_flags |= ISZAPPED;
1477 cur_weapon->o_charges += (10 + rnd(15));
1478 }
1479
1480 if (cur_weapon != NULL && cur_armor != NULL)
1481 msg("Your armor and %s are covered with dancing blue lights!", weaps[cur_weapon->o_which].w_name);
1482 else if (cur_armor != NULL)
1483 msg("Your armor is covered with dancing blue lights!");
1484 else if (cur_weapon != NULL)
1485 msg("Your %s is covered with dancing blue lights!", weaps[cur_weapon->o_which].w_name);
1486 else
1487 msg("You are momentarily covered with dancing blue lights.");
1488 }
1489 else if ((is_wearing(R_FIRERESIST) || on(player, NOFIRE)) &&
1490 (strcmp(name, "flame") == 0))
1491 {
1492 player_damage = 0;
1493 msg("The flame bathes you harmlessly.");
1494 }
1495 else if ((is_wearing(R_COLDRESIST) || on(player, NOCOLD)) &&
1496 (strcmp(name, "ice") == 0))
1497 {
1498 player_damage = 0;
1499 msg("The ice cracks and quickly melts around you.");
1500 }
1501 else if (save(VS_MAGIC))
1502 {
1503 take_that[BOLT_LENGTH] += player_damage / 2;
1504 debug("Player dodges %s for %d.", name, player_damage / 2);
1505 }
1506 else
1507 {
1508 debug("Player zapped by %s for %d.", name, player_damage);
1509 take_that[BOLT_LENGTH] += player_damage;
1510 }
1511
1512 /* Check for gas with special effects */
1513
1514 if (off(player, HASOXYGEN) && !is_wearing(R_BREATHE) && !save(VS_BREATH))
1515 {
1516 if (strcmp(name, "nerve gas") == 0)
1517 {
1518 if (no_command == 0)
1519 {
1520 msg("The nerve gas paralyzes you.");
1521 no_command = FREEZETIME;
1522 }
1523 }
1524 else if (strcmp(name, "sleeping gas") == 0)
1525 {
1526 if (no_command == 0)
1527 {
1528 msg("The sleeping gas puts you to sleep.");
1529 no_command = SLEEPTIME;
1530 }
1531 }
1532 else if (strcmp(name, "slow gas") == 0
1533 && !is_wearing(R_FREEDOM))
1534 {
1535 msg("You feel yourself moving %sslower.",
1536 on(player, ISSLOW) ? "even " : "");
1537
1538 if (on(player, ISSLOW))
1539 lengthen_fuse(FUSE_NOSLOW, rnd(10) + 4);
1540 else
1541 {
1542 turn_on(player, ISSLOW);
1543 player.t_turn = TRUE;
1544 light_fuse(FUSE_NOSLOW, 0, rnd(10) + 4, AFTER);
1545 }
1546 }
1547 else if (strcmp(name, "fear gas") == 0)
1548 {
1549 if (!(on(player, ISFLEE) &&
1550 (player.t_chasee==shooter)) &&
1551 (shooter != &player))
1552 {
1553 if (off(player, SUPERHERO)
1554 && player.t_ctype != C_PALADIN)
1555 {
1556 turn_on(player, ISFLEE);
1557 player.t_chasee = shooter;
1558 player.t_ischasing = FALSE;
1559 msg("The fear gas terrifies you.");
1560 }
1561 else
1562 msg("The fear gas has no effect.");
1563 }
1564 }
1565 }
1566 }
1567 mvwaddch(cw, pos.y, pos.x, dirch);
1568 wrefresh(cw);
1569 }
1570 pos.y += dir.y;
1571 pos.x += dir.x;
1572 }
1573
1574 /*
1575 * Now that we have determined the damage for the length of the bolt,
1576 * apply it to each monster (and then the player) in turn
1577 */
1578
1579 for (x = 0; x < BOLT_LENGTH; x++)
1580 {
1581 ch = winat(spotpos[x].y, spotpos[x].x);
1582
1583 if (take_that[x] != 0 && isalpha(ch))
1584 {
1585 if ((item = find_mons(spotpos[x].y, spotpos[x].x)) == NULL)
1586 {
1587 debug("Can't find monster %c @ %d %d.",
1588 ch, spotpos[x].y, spotpos[x].x);
1589
1590 continue;
1591 }
1592 else
1593 tp = THINGPTR(item);
1594
1595 mname = on(player, ISBLIND) ? "monster" : monsters[tp->t_index].m_name;
1596
1597 debug("Zapped %s for %d (%d)",
1598 mname, take_that[x], tp->t_stats.s_hpt - take_that[x]);
1599
1600 if ((tp->t_stats.s_hpt -= take_that[x]) <= 0)
1601 {
1602 if (cansee(tp->t_pos.y, tp->t_pos.x))
1603 msg("The %s kills the %s.", name,
1604 on(player, ISBLIND) ? "monster" : mname);
1605
1606 killed(shooter, item, NOMESSAGE, get_points);
1607 ret_val = TRUE;
1608 }
1609 else if (get_points)
1610 {
1611 chase_it(&spotpos[x], &player);
1612 summon_help(tp, NOFORCE);
1613 }
1614 }
1615
1616 if (spotpos[x].y >= 0 && spotpos[x].x >= 0 &&
1617 spotpos[x].y < LINES && spotpos[x].x < COLS)
1618 mvwaddch(cw, spotpos[x].y, spotpos[x].x,
1619 show(spotpos[x].y, spotpos[x].x));
1620 }
1621
1622 if (take_that[BOLT_LENGTH] != 0)
1623 {
1624 if (tp != THINGPTR(fam_ptr))
1625 {
1626 msg("You are hit by the %s.", name);
1627
1628 if ((pstats.s_hpt -= take_that[BOLT_LENGTH]) <= 0)
1629 {
1630 death(reason);
1631 return;
1632 }
1633 }
1634 }
1635
1636 return;
1637 }
1638
1639 /*
1640 monster_do_zap()
1641 monster gets the effect
1642 */
1643
1644 void
1645 monster_do_zap(struct thing *zapper, int which, int flags)
1646 {
1647 struct stats *curp = &(zapper->t_stats);
1648 int blessed = flags & ISBLESSED;
1649 int cursed = flags & ISCURSED;
1650 int shoot = FALSE;
1651 int damage;
1652 int ndice, nsides;
1653 int ch;
1654 char *bolt_name = NULL;
1655 coord pos;
1656
1657 switch(which)
1658 {
1659 case WS_MISSILE:
1660 bolt_name = "magic missile";
1661 pos = do_motion('*', delta.y, delta.x, zapper);
1662 ch = winat(pos.y, pos.x);
1663 ndice = curp->s_lvl;
1664 nsides = 4;
1665
1666 if (cursed)
1667 nsides /= 2;
1668 else if (blessed)
1669 nsides *= 2;
1670
1671 damage = roll(ndice, nsides);
1672
1673 if (ch == PLAYER)
1674 {
1675 if (save(VS_MAGIC)) /* help the player */
1676 msg("You evade the %s.", bolt_name);
1677 else
1678 {
1679 msg("You are hit by the %s.", bolt_name);
1680
1681 if ((pstats.s_hpt -= damage) <= 0)
1682 death(D_BOLT);
1683 }
1684 }
1685 else if (isalpha(ch))
1686 {
1687 struct linked_list *item = find_mons(pos.y, pos.x);
1688
1689 if (item != NULL)
1690 {
1691 struct thing *tp = THINGPTR(item);
1692 int see_it = cansee(pos.y, pos.x);
1693
1694 if ((tp->t_stats.s_hpt -= damage) <= 0)
1695 killed(zapper, item, see_it, (zapper == THINGPTR(fam_ptr)));
1696 else if (see_it)
1697 msg("The %s hits the monster.", bolt_name);
1698 }
1699 }
1700
1701 if (pos.y >= 0 && pos.x >= 0 &&
1702 pos.y < LINES && pos.x < COLS)
1703 mvwaddch(cw, pos.y, pos.x, show(pos.y, pos.x));
1704
1705 break;
1706
1707 case WS_CANCEL:
1708 cancel_player(off(*zapper, ISUNIQUE));
1709 break;
1710
1711 case WS_COLD:
1712 shoot = TRUE;
1713 bolt_name = "cold";
1714 break;
1715
1716 case WS_FIRE:
1717 shoot = TRUE;
1718 bolt_name = "fire";
1719 break;
1720
1721 case WS_ELECT:
1722 shoot = TRUE;
1723 bolt_name = "lightning";
1724 break;
1725
1726 default:
1727 debug("'%s' is a strange stick for a monster to zap!",
1728 ws_magic[which].mi_name);
1729 break;
1730 }
1731
1732 if (shoot)
1733 {
1734 ndice = curp->s_lvl / 2 + 1;
1735 nsides = 6;
1736
1737 if (cursed)
1738 nsides /= 2;
1739 else if (blessed)
1740 nsides *= 2;
1741
1742 damage = roll(ndice, nsides);
1743 shoot_bolt(zapper, zapper->t_pos, delta,
1744 (zapper == THINGPTR(fam_ptr)), D_BOLT, bolt_name, damage);
1745 }
1746 }
1747
1748 struct powers
1749 {
1750 unsigned long p_flag;
1751 int fuse_id;
1752 };
1753
1754 /* Order in which powers will attempt to be cancelled */
1755
1756 struct powers player_powers[] =
1757 {
1758 { ISHASTE, FUSE_NOHASTE },
1759 { ISELECTRIC, FUSE_UNELECTRIFY },
1760 { CANINWALL, FUSE_UNPHASE },
1761 { CANFLY, FUSE_UNFLY },
1762 { ISINVIS, FUSE_APPEAR },
1763 { CANREFLECT, FUSE_UNGAZE },
1764 { ISDISGUISE, FUSE_UNDISGUISE },
1765 { HASMSHIELD, FUSE_UNMSHIELD },
1766 { ISREGEN, FUSE_UNREGEN },
1767 { CANSEE, FUSE_UNSEE },
1768 { NOCOLD, FUSE_UNCOLD },
1769 { NOFIRE, FUSE_UNHOT },
1770 { HASOXYGEN, FUSE_UNBREATHE },
1771 { HASSHIELD, FUSE_UNSHIELD },
1772 { ULONG_MAX, FUSE_NULL }
1773 };
1774
1775 void
1776 cancel_player(int not_unique)
1777 {
1778 struct powers *pp;
1779 int no_effect = TRUE;
1780
1781 for(pp = player_powers; pp->p_flag != ULONG_MAX; pp++)
1782 {
1783 if (on(player, pp->p_flag) && !save(VS_MAGIC))
1784 {
1785 fuses[pp->fuse_id].func(NULL);
1786 extinguish_fuse(pp->fuse_id);
1787 no_effect = FALSE;
1788
1789 if (not_unique) /* Gods might cancel everything */
1790 break;
1791 }
1792 }
1793
1794 if (no_effect)
1795 nothing_message(ISNORMAL);
1796 }