forked from NetHack/NetHack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmcastu.c
More file actions
865 lines (820 loc) · 27.6 KB
/
Copy pathmcastu.c
File metadata and controls
865 lines (820 loc) · 27.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
/* NetHack 3.6 mcastu.c $NHDT-Date: 1436753517 2015/07/13 02:11:57 $ $NHDT-Branch: master $:$NHDT-Revision: 1.44 $ */
/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
/*-Copyright (c) Robert Patrick Rankin, 2011. */
/* NetHack may be freely redistributed. See license for details. */
#include "hack.h"
/* monster mage spells */
enum mcast_mage_spells {
MGC_PSI_BOLT = 0,
MGC_CURE_SELF,
MGC_HASTE_SELF,
MGC_STUN_YOU,
MGC_DISAPPEAR,
MGC_WEAKEN_YOU,
MGC_DESTRY_ARMR,
MGC_CURSE_ITEMS,
MGC_AGGRAVATION,
MGC_SUMMON_MONS,
MGC_CLONE_WIZ,
MGC_DEATH_TOUCH
};
/* monster cleric spells */
enum mcast_cleric_spells {
CLC_OPEN_WOUNDS = 0,
CLC_CURE_SELF,
CLC_CONFUSE_YOU,
CLC_PARALYZE,
CLC_BLIND_YOU,
CLC_INSECTS,
CLC_CURSE_ITEMS,
CLC_LIGHTNING,
CLC_FIRE_PILLAR,
CLC_GEYSER
};
STATIC_DCL void FDECL(cursetxt, (struct monst *, BOOLEAN_P));
STATIC_DCL int FDECL(choose_magic_spell, (int));
STATIC_DCL int FDECL(choose_clerical_spell, (int));
STATIC_DCL void FDECL(cast_wizard_spell, (struct monst *, int, int));
STATIC_DCL void FDECL(cast_cleric_spell, (struct monst *, int, int));
STATIC_DCL boolean FDECL(is_undirected_spell, (unsigned int, int));
STATIC_DCL boolean
FDECL(spell_would_be_useless, (struct monst *, unsigned int, int));
extern const char *const flash_types[]; /* from zap.c */
/* feedback when frustrated monster couldn't cast a spell */
STATIC_OVL
void
cursetxt(mtmp, undirected)
struct monst *mtmp;
boolean undirected;
{
if (canseemon(mtmp) && couldsee(mtmp->mx, mtmp->my)) {
const char *point_msg; /* spellcasting monsters are impolite */
if (undirected)
point_msg = "all around, then curses";
else if ((Invis && !perceives(mtmp->data)
&& (mtmp->mux != u.ux || mtmp->muy != u.uy))
|| is_obj_mappear(&youmonst, STRANGE_OBJECT)
|| u.uundetected)
point_msg = "and curses in your general direction";
else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
point_msg = "and curses at your displaced image";
else
point_msg = "at you, then curses";
pline("%s points %s.", Monnam(mtmp), point_msg);
} else if ((!(moves % 4) || !rn2(4))) {
if (!Deaf)
Norep("You hear a mumbled curse.");
}
}
/* convert a level based random selection into a specific mage spell;
inappropriate choices will be screened out by spell_would_be_useless() */
STATIC_OVL int
choose_magic_spell(spellval)
int spellval;
{
/* for 3.4.3 and earlier, val greater than 22 selected the default spell
*/
while (spellval > 24 && rn2(25))
spellval = rn2(spellval);
switch (spellval) {
case 24:
case 23:
if (Antimagic || Hallucination)
return MGC_PSI_BOLT;
/*FALLTHRU*/
case 22:
case 21:
case 20:
return MGC_DEATH_TOUCH;
case 19:
case 18:
return MGC_CLONE_WIZ;
case 17:
case 16:
case 15:
return MGC_SUMMON_MONS;
case 14:
case 13:
return MGC_AGGRAVATION;
case 12:
case 11:
case 10:
return MGC_CURSE_ITEMS;
case 9:
case 8:
return MGC_DESTRY_ARMR;
case 7:
case 6:
return MGC_WEAKEN_YOU;
case 5:
case 4:
return MGC_DISAPPEAR;
case 3:
return MGC_STUN_YOU;
case 2:
return MGC_HASTE_SELF;
case 1:
return MGC_CURE_SELF;
case 0:
default:
return MGC_PSI_BOLT;
}
}
/* convert a level based random selection into a specific cleric spell */
STATIC_OVL int
choose_clerical_spell(spellnum)
int spellnum;
{
/* for 3.4.3 and earlier, num greater than 13 selected the default spell
*/
while (spellnum > 15 && rn2(16))
spellnum = rn2(spellnum);
switch (spellnum) {
case 15:
case 14:
if (rn2(3))
return CLC_OPEN_WOUNDS;
/*FALLTHRU*/
case 13:
return CLC_GEYSER;
case 12:
return CLC_FIRE_PILLAR;
case 11:
return CLC_LIGHTNING;
case 10:
case 9:
return CLC_CURSE_ITEMS;
case 8:
return CLC_INSECTS;
case 7:
case 6:
return CLC_BLIND_YOU;
case 5:
case 4:
return CLC_PARALYZE;
case 3:
case 2:
return CLC_CONFUSE_YOU;
case 1:
return CLC_CURE_SELF;
case 0:
default:
return CLC_OPEN_WOUNDS;
}
}
/* return values:
* 1: successful spell
* 0: unsuccessful spell
*/
int
castmu(mtmp, mattk, thinks_it_foundyou, foundyou)
register struct monst *mtmp;
register struct attack *mattk;
boolean thinks_it_foundyou;
boolean foundyou;
{
int dmg, ml = mtmp->m_lev;
int ret;
int spellnum = 0;
/* Three cases:
* -- monster is attacking you. Search for a useful spell.
* -- monster thinks it's attacking you. Search for a useful spell,
* without checking for undirected. If the spell found is directed,
* it fails with cursetxt() and loss of mspec_used.
* -- monster isn't trying to attack. Select a spell once. Don't keep
* searching; if that spell is not useful (or if it's directed),
* return and do something else.
* Since most spells are directed, this means that a monster that isn't
* attacking casts spells only a small portion of the time that an
* attacking monster does.
*/
if ((mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) && ml) {
int cnt = 40;
do {
spellnum = rn2(ml);
if (mattk->adtyp == AD_SPEL)
spellnum = choose_magic_spell(spellnum);
else
spellnum = choose_clerical_spell(spellnum);
/* not trying to attack? don't allow directed spells */
if (!thinks_it_foundyou) {
if (!is_undirected_spell(mattk->adtyp, spellnum)
|| spell_would_be_useless(mtmp, mattk->adtyp, spellnum)) {
if (foundyou)
impossible(
"spellcasting monster found you and doesn't know it?");
return 0;
}
break;
}
} while (--cnt > 0
&& spell_would_be_useless(mtmp, mattk->adtyp, spellnum));
if (cnt == 0)
return 0;
}
/* monster unable to cast spells? */
if (mtmp->mcan || mtmp->mspec_used || !ml) {
cursetxt(mtmp, is_undirected_spell(mattk->adtyp, spellnum));
return (0);
}
if (mattk->adtyp == AD_SPEL || mattk->adtyp == AD_CLRC) {
mtmp->mspec_used = 10 - mtmp->m_lev;
if (mtmp->mspec_used < 2)
mtmp->mspec_used = 2;
}
/* monster can cast spells, but is casting a directed spell at the
wrong place? If so, give a message, and return. Do this *after*
penalizing mspec_used. */
if (!foundyou && thinks_it_foundyou
&& !is_undirected_spell(mattk->adtyp, spellnum)) {
pline("%s casts a spell at %s!",
canseemon(mtmp) ? Monnam(mtmp) : "Something",
levl[mtmp->mux][mtmp->muy].typ == WATER ? "empty water"
: "thin air");
return (0);
}
nomul(0);
if (rn2(ml * 10) < (mtmp->mconf ? 100 : 20)) { /* fumbled attack */
if (canseemon(mtmp) && !Deaf)
pline_The("air crackles around %s.", mon_nam(mtmp));
return (0);
}
if (canspotmon(mtmp) || !is_undirected_spell(mattk->adtyp, spellnum)) {
pline("%s casts a spell%s!",
canspotmon(mtmp) ? Monnam(mtmp) : "Something",
is_undirected_spell(mattk->adtyp, spellnum)
? ""
: (Invisible && !perceives(mtmp->data)
&& (mtmp->mux != u.ux || mtmp->muy != u.uy))
? " at a spot near you"
: (Displaced
&& (mtmp->mux != u.ux || mtmp->muy != u.uy))
? " at your displaced image"
: " at you");
}
/*
* As these are spells, the damage is related to the level
* of the monster casting the spell.
*/
if (!foundyou) {
dmg = 0;
if (mattk->adtyp != AD_SPEL && mattk->adtyp != AD_CLRC) {
impossible(
"%s casting non-hand-to-hand version of hand-to-hand spell %d?",
Monnam(mtmp), mattk->adtyp);
return (0);
}
} else if (mattk->damd)
dmg = d((int) ((ml / 2) + mattk->damn), (int) mattk->damd);
else
dmg = d((int) ((ml / 2) + 1), 6);
if (Half_spell_damage)
dmg = (dmg + 1) / 2;
ret = 1;
switch (mattk->adtyp) {
case AD_FIRE:
pline("You're enveloped in flames.");
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
pline("But you resist the effects.");
dmg = 0;
}
burn_away_slime();
break;
case AD_COLD:
pline("You're covered in frost.");
if (Cold_resistance) {
shieldeff(u.ux, u.uy);
pline("But you resist the effects.");
dmg = 0;
}
break;
case AD_MAGM:
You("are hit by a shower of missiles!");
if (Antimagic) {
shieldeff(u.ux, u.uy);
pline_The("missiles bounce off!");
dmg = 0;
} else
dmg = d((int) mtmp->m_lev / 2 + 1, 6);
break;
case AD_SPEL: /* wizard spell */
case AD_CLRC: /* clerical spell */
{
if (mattk->adtyp == AD_SPEL)
cast_wizard_spell(mtmp, dmg, spellnum);
else
cast_cleric_spell(mtmp, dmg, spellnum);
dmg = 0; /* done by the spell casting functions */
break;
}
}
if (dmg)
mdamageu(mtmp, dmg);
return (ret);
}
/* monster wizard and cleric spellcasting functions */
/*
If dmg is zero, then the monster is not casting at you.
If the monster is intentionally not casting at you, we have previously
called spell_would_be_useless() and spellnum should always be a valid
undirected spell.
If you modify either of these, be sure to change is_undirected_spell()
and spell_would_be_useless().
*/
STATIC_OVL
void
cast_wizard_spell(mtmp, dmg, spellnum)
struct monst *mtmp;
int dmg;
int spellnum;
{
if (dmg == 0 && !is_undirected_spell(AD_SPEL, spellnum)) {
impossible("cast directed wizard spell (%d) with dmg=0?", spellnum);
return;
}
switch (spellnum) {
case MGC_DEATH_TOUCH:
pline("Oh no, %s's using the touch of death!", mhe(mtmp));
if (nonliving(youmonst.data) || is_demon(youmonst.data)) {
You("seem no deader than before.");
} else if (!Antimagic && rn2(mtmp->m_lev) > 12) {
if (Hallucination) {
You("have an out of body experience.");
} else {
killer.format = KILLED_BY_AN;
Strcpy(killer.name, "touch of death");
done(DIED);
}
} else {
if (Antimagic)
shieldeff(u.ux, u.uy);
pline("Lucky for you, it didn't work!");
}
dmg = 0;
break;
case MGC_CLONE_WIZ:
if (mtmp->iswiz && context.no_of_wizards == 1) {
pline("Double Trouble...");
clonewiz();
dmg = 0;
} else
impossible("bad wizard cloning?");
break;
case MGC_SUMMON_MONS: {
int count;
count = nasty(mtmp); /* summon something nasty */
if (mtmp->iswiz)
verbalize("Destroy the thief, my pet%s!", plur(count));
else {
const char *mappear =
(count == 1) ? "A monster appears" : "Monsters appear";
/* messages not quite right if plural monsters created but
only a single monster is seen */
if (Invisible && !perceives(mtmp->data)
&& (mtmp->mux != u.ux || mtmp->muy != u.uy))
pline("%s around a spot near you!", mappear);
else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
pline("%s around your displaced image!", mappear);
else
pline("%s from nowhere!", mappear);
}
dmg = 0;
break;
}
case MGC_AGGRAVATION:
You_feel("that monsters are aware of your presence.");
aggravate();
dmg = 0;
break;
case MGC_CURSE_ITEMS:
You_feel("as if you need some help.");
rndcurse();
dmg = 0;
break;
case MGC_DESTRY_ARMR:
if (Antimagic) {
shieldeff(u.ux, u.uy);
pline("A field of force surrounds you!");
} else if (!destroy_arm(some_armor(&youmonst))) {
Your("skin itches.");
}
dmg = 0;
break;
case MGC_WEAKEN_YOU: /* drain strength */
if (Antimagic) {
shieldeff(u.ux, u.uy);
You_feel("momentarily weakened.");
} else {
You("suddenly feel weaker!");
dmg = mtmp->m_lev - 6;
if (Half_spell_damage)
dmg = (dmg + 1) / 2;
losestr(rnd(dmg));
if (u.uhp < 1)
done_in_by(mtmp, DIED);
}
dmg = 0;
break;
case MGC_DISAPPEAR: /* makes self invisible */
if (!mtmp->minvis && !mtmp->invis_blkd) {
if (canseemon(mtmp))
pline("%s suddenly %s!", Monnam(mtmp),
!See_invisible ? "disappears" : "becomes transparent");
mon_set_minvis(mtmp);
if (cansee(mtmp->mx, mtmp->my) && !canspotmon(mtmp))
map_invisible(mtmp->mx, mtmp->my);
dmg = 0;
} else
impossible("no reason for monster to cast disappear spell?");
break;
case MGC_STUN_YOU:
if (Antimagic || Free_action) {
shieldeff(u.ux, u.uy);
if (!Stunned)
You_feel("momentarily disoriented.");
make_stunned(1L, FALSE);
} else {
You(Stunned ? "struggle to keep your balance." : "reel...");
dmg = d(ACURR(A_DEX) < 12 ? 6 : 4, 4);
if (Half_spell_damage)
dmg = (dmg + 1) / 2;
make_stunned((HStun & TIMEOUT) + (long) dmg, FALSE);
}
dmg = 0;
break;
case MGC_HASTE_SELF:
mon_adjust_speed(mtmp, 1, (struct obj *) 0);
dmg = 0;
break;
case MGC_CURE_SELF:
if (mtmp->mhp < mtmp->mhpmax) {
if (canseemon(mtmp))
pline("%s looks better.", Monnam(mtmp));
/* note: player healing does 6d4; this used to do 1d8 */
if ((mtmp->mhp += d(3, 6)) > mtmp->mhpmax)
mtmp->mhp = mtmp->mhpmax;
dmg = 0;
}
break;
case MGC_PSI_BOLT:
/* prior to 3.4.0 Antimagic was setting the damage to 1--this
made the spell virtually harmless to players with magic res. */
if (Antimagic) {
shieldeff(u.ux, u.uy);
dmg = (dmg + 1) / 2;
}
if (dmg <= 5)
You("get a slight %sache.", body_part(HEAD));
else if (dmg <= 10)
Your("brain is on fire!");
else if (dmg <= 20)
Your("%s suddenly aches painfully!", body_part(HEAD));
else
Your("%s suddenly aches very painfully!", body_part(HEAD));
break;
default:
impossible("mcastu: invalid magic spell (%d)", spellnum);
dmg = 0;
break;
}
if (dmg)
mdamageu(mtmp, dmg);
}
STATIC_OVL
void
cast_cleric_spell(mtmp, dmg, spellnum)
struct monst *mtmp;
int dmg;
int spellnum;
{
if (dmg == 0 && !is_undirected_spell(AD_CLRC, spellnum)) {
impossible("cast directed cleric spell (%d) with dmg=0?", spellnum);
return;
}
switch (spellnum) {
case CLC_GEYSER:
/* this is physical damage (force not heat),
* not magical damage or fire damage
*/
pline("A sudden geyser slams into you from nowhere!");
dmg = d(8, 6);
if (Half_physical_damage)
dmg = (dmg + 1) / 2;
break;
case CLC_FIRE_PILLAR:
pline("A pillar of fire strikes all around you!");
if (Fire_resistance) {
shieldeff(u.ux, u.uy);
dmg = 0;
} else
dmg = d(8, 6);
if (Half_spell_damage)
dmg = (dmg + 1) / 2;
burn_away_slime();
(void) burnarmor(&youmonst);
destroy_item(SCROLL_CLASS, AD_FIRE);
destroy_item(POTION_CLASS, AD_FIRE);
destroy_item(SPBOOK_CLASS, AD_FIRE);
(void) burn_floor_objects(u.ux, u.uy, TRUE, FALSE);
break;
case CLC_LIGHTNING: {
boolean reflects;
pline("A bolt of lightning strikes down at you from above!");
reflects = ureflects("It bounces off your %s%s.", "");
if (reflects || Shock_resistance) {
shieldeff(u.ux, u.uy);
dmg = 0;
if (reflects)
break;
} else
dmg = d(8, 6);
if (Half_spell_damage)
dmg = (dmg + 1) / 2;
destroy_item(WAND_CLASS, AD_ELEC);
destroy_item(RING_CLASS, AD_ELEC);
(void) flashburn((long) rnd(100));
break;
}
case CLC_CURSE_ITEMS:
You_feel("as if you need some help.");
rndcurse();
dmg = 0;
break;
case CLC_INSECTS: {
/* Try for insects, and if there are none
left, go for (sticks to) snakes. -3. */
struct permonst *pm = mkclass(S_ANT, 0);
struct monst *mtmp2 = (struct monst *) 0;
char let = (pm ? S_ANT : S_SNAKE);
boolean success = FALSE, seecaster;
int i, quan, oldseen, newseen;
coord bypos;
const char *fmt;
oldseen = monster_census(TRUE);
quan = (mtmp->m_lev < 2) ? 1 : rnd((int) mtmp->m_lev / 2);
if (quan < 3)
quan = 3;
for (i = 0; i <= quan; i++) {
if (!enexto(&bypos, mtmp->mux, mtmp->muy, mtmp->data))
break;
if ((pm = mkclass(let, 0)) != 0
&& (mtmp2 = makemon(pm, bypos.x, bypos.y, MM_ANGRY)) != 0) {
success = TRUE;
mtmp2->msleeping = mtmp2->mpeaceful = mtmp2->mtame = 0;
set_malign(mtmp2);
}
}
newseen = monster_census(TRUE);
/* not canspotmon(), which includes unseen things sensed via warning
*/
seecaster = canseemon(mtmp) || tp_sensemon(mtmp) || Detect_monsters;
fmt = 0;
if (!seecaster) {
char *arg; /* [not const: upstart(N==1 ? an() : makeplural())] */
const char *what = (let == S_SNAKE) ? "snake" : "insect";
if (newseen <= oldseen || Unaware) {
/* unseen caster fails or summons unseen critters,
or unconscious hero ("You dream that you hear...") */
You_hear("someone summoning %s.", makeplural(what));
} else {
/* unseen caster summoned seen critter(s) */
arg = (newseen == oldseen + 1) ? an(what) : makeplural(what);
if (!Deaf)
You_hear("someone summoning something, and %s %s.", arg,
vtense(arg, "appear"));
else
pline("%s %s.", upstart(arg), vtense(arg, "appear"));
}
/* seen caster, possibly producing unseen--or just one--critters;
hero is told what the caster is doing and doesn't necessarily
observe complete accuracy of that caster's results (in other
words, no need to fuss with visibility or singularization;
player is told what's happening even if hero is unconscious) */
} else if (!success)
fmt = "%s casts at a clump of sticks, but nothing happens.";
else if (let == S_SNAKE)
fmt = "%s transforms a clump of sticks into snakes!";
else if (Invisible && !perceives(mtmp->data)
&& (mtmp->mux != u.ux || mtmp->muy != u.uy))
fmt = "%s summons insects around a spot near you!";
else if (Displaced && (mtmp->mux != u.ux || mtmp->muy != u.uy))
fmt = "%s summons insects around your displaced image!";
else
fmt = "%s summons insects!";
if (fmt)
pline(fmt, Monnam(mtmp));
dmg = 0;
break;
}
case CLC_BLIND_YOU:
/* note: resists_blnd() doesn't apply here */
if (!Blinded) {
int num_eyes = eyecount(youmonst.data);
pline("Scales cover your %s!", (num_eyes == 1)
? body_part(EYE)
: makeplural(body_part(EYE)));
make_blinded(Half_spell_damage ? 100L : 200L, FALSE);
if (!Blind)
Your1(vision_clears);
dmg = 0;
} else
impossible("no reason for monster to cast blindness spell?");
break;
case CLC_PARALYZE:
if (Antimagic || Free_action) {
shieldeff(u.ux, u.uy);
if (multi >= 0)
You("stiffen briefly.");
nomul(-1);
multi_reason = "paralyzed by a monster";
} else {
if (multi >= 0)
You("are frozen in place!");
dmg = 4 + (int) mtmp->m_lev;
if (Half_spell_damage)
dmg = (dmg + 1) / 2;
nomul(-dmg);
multi_reason = "paralyzed by a monster";
}
nomovemsg = 0;
dmg = 0;
break;
case CLC_CONFUSE_YOU:
if (Antimagic) {
shieldeff(u.ux, u.uy);
You_feel("momentarily dizzy.");
} else {
boolean oldprop = !!Confusion;
dmg = (int) mtmp->m_lev;
if (Half_spell_damage)
dmg = (dmg + 1) / 2;
make_confused(HConfusion + dmg, TRUE);
if (Hallucination)
You_feel("%s!", oldprop ? "trippier" : "trippy");
else
You_feel("%sconfused!", oldprop ? "more " : "");
}
dmg = 0;
break;
case CLC_CURE_SELF:
if (mtmp->mhp < mtmp->mhpmax) {
if (canseemon(mtmp))
pline("%s looks better.", Monnam(mtmp));
/* note: player healing does 6d4; this used to do 1d8 */
if ((mtmp->mhp += d(3, 6)) > mtmp->mhpmax)
mtmp->mhp = mtmp->mhpmax;
dmg = 0;
}
break;
case CLC_OPEN_WOUNDS:
if (Antimagic) {
shieldeff(u.ux, u.uy);
dmg = (dmg + 1) / 2;
}
if (dmg <= 5)
Your("skin itches badly for a moment.");
else if (dmg <= 10)
pline("Wounds appear on your body!");
else if (dmg <= 20)
pline("Severe wounds appear on your body!");
else
Your("body is covered with painful wounds!");
break;
default:
impossible("mcastu: invalid clerical spell (%d)", spellnum);
dmg = 0;
break;
}
if (dmg)
mdamageu(mtmp, dmg);
}
STATIC_DCL
boolean
is_undirected_spell(adtyp, spellnum)
unsigned int adtyp;
int spellnum;
{
if (adtyp == AD_SPEL) {
switch (spellnum) {
case MGC_CLONE_WIZ:
case MGC_SUMMON_MONS:
case MGC_AGGRAVATION:
case MGC_DISAPPEAR:
case MGC_HASTE_SELF:
case MGC_CURE_SELF:
return TRUE;
default:
break;
}
} else if (adtyp == AD_CLRC) {
switch (spellnum) {
case CLC_INSECTS:
case CLC_CURE_SELF:
return TRUE;
default:
break;
}
}
return FALSE;
}
/* Some spells are useless under some circumstances. */
STATIC_DCL
boolean
spell_would_be_useless(mtmp, adtyp, spellnum)
struct monst *mtmp;
unsigned int adtyp;
int spellnum;
{
/* Some spells don't require the player to really be there and can be cast
* by the monster when you're invisible, yet still shouldn't be cast when
* the monster doesn't even think you're there.
* This check isn't quite right because it always uses your real position.
* We really want something like "if the monster could see mux, muy".
*/
boolean mcouldseeu = couldsee(mtmp->mx, mtmp->my);
if (adtyp == AD_SPEL) {
/* aggravate monsters, etc. won't be cast by peaceful monsters */
if (mtmp->mpeaceful
&& (spellnum == MGC_AGGRAVATION || spellnum == MGC_SUMMON_MONS
|| spellnum == MGC_CLONE_WIZ))
return TRUE;
/* haste self when already fast */
if (mtmp->permspeed == MFAST && spellnum == MGC_HASTE_SELF)
return TRUE;
/* invisibility when already invisible */
if ((mtmp->minvis || mtmp->invis_blkd) && spellnum == MGC_DISAPPEAR)
return TRUE;
/* peaceful monster won't cast invisibility if you can't see
invisible,
same as when monsters drink potions of invisibility. This doesn't
really make a lot of sense, but lets the player avoid hitting
peaceful monsters by mistake */
if (mtmp->mpeaceful && !See_invisible && spellnum == MGC_DISAPPEAR)
return TRUE;
/* healing when already healed */
if (mtmp->mhp == mtmp->mhpmax && spellnum == MGC_CURE_SELF)
return TRUE;
/* don't summon monsters if it doesn't think you're around */
if (!mcouldseeu && (spellnum == MGC_SUMMON_MONS
|| (!mtmp->iswiz && spellnum == MGC_CLONE_WIZ)))
return TRUE;
if ((!mtmp->iswiz || context.no_of_wizards > 1)
&& spellnum == MGC_CLONE_WIZ)
return TRUE;
/* aggravation (global wakeup) when everyone is already active */
if (spellnum == MGC_AGGRAVATION) {
/* if nothing needs to be awakened then this spell is useless
but caster might not realize that [chance to pick it then
must be very small otherwise caller's many retry attempts
will eventually end up picking it too often] */
if (!has_aggravatables(mtmp))
return rn2(100) ? TRUE : FALSE;
}
} else if (adtyp == AD_CLRC) {
/* summon insects/sticks to snakes won't be cast by peaceful monsters
*/
if (mtmp->mpeaceful && spellnum == CLC_INSECTS)
return TRUE;
/* healing when already healed */
if (mtmp->mhp == mtmp->mhpmax && spellnum == CLC_CURE_SELF)
return TRUE;
/* don't summon insects if it doesn't think you're around */
if (!mcouldseeu && spellnum == CLC_INSECTS)
return TRUE;
/* blindness spell on blinded player */
if (Blinded && spellnum == CLC_BLIND_YOU)
return TRUE;
}
return FALSE;
}
/* convert 1..10 to 0..9; add 10 for second group (spell casting) */
#define ad_to_typ(k) (10 + (int) k - 1)
/* monster uses spell (ranged) */
int
buzzmu(mtmp, mattk)
register struct monst *mtmp;
register struct attack *mattk;
{
/* don't print constant stream of curse messages for 'normal'
spellcasting monsters at range */
if (mattk->adtyp > AD_SPC2)
return (0);
if (mtmp->mcan) {
cursetxt(mtmp, FALSE);
return (0);
}
if (lined_up(mtmp) && rn2(3)) {
nomul(0);
if (mattk->adtyp && (mattk->adtyp < 11)) { /* no cf unsigned >0 */
if (canseemon(mtmp))
pline("%s zaps you with a %s!", Monnam(mtmp),
flash_types[ad_to_typ(mattk->adtyp)]);
buzz(-ad_to_typ(mattk->adtyp), (int) mattk->damn, mtmp->mx,
mtmp->my, sgn(tbx), sgn(tby));
} else
impossible("Monster spell %d cast", mattk->adtyp - 1);
}
return (1);
}
/*mcastu.c*/