-
Notifications
You must be signed in to change notification settings - Fork 454
/
muse.c
2588 lines (2441 loc) · 89 KB
/
muse.c
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
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* NetHack 3.6 muse.c $NHDT-Date: 1505181522 2017/09/12 01:58:42 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.80 $ */
/* Copyright (C) 1990 by Ken Arromdee */
/* NetHack may be freely redistributed. See license for details. */
/*
* Monster item usage routines.
*/
#include "hack.h"
extern const int monstr[];
boolean m_using = FALSE;
/* Let monsters use magic items. Arbitrary assumptions: Monsters only use
* scrolls when they can see, monsters know when wands have 0 charges,
* monsters cannot recognize if items are cursed are not, monsters which
* are confused don't know not to read scrolls, etc....
*/
STATIC_DCL struct permonst *FDECL(muse_newcham_mon, (struct monst *));
STATIC_DCL int FDECL(precheck, (struct monst *, struct obj *));
STATIC_DCL void FDECL(mzapmsg, (struct monst *, struct obj *, BOOLEAN_P));
STATIC_DCL void FDECL(mreadmsg, (struct monst *, struct obj *));
STATIC_DCL void FDECL(mquaffmsg, (struct monst *, struct obj *));
STATIC_DCL boolean FDECL(m_use_healing, (struct monst *));
STATIC_PTR int FDECL(mbhitm, (struct monst *, struct obj *));
STATIC_DCL void FDECL(mbhit, (struct monst *, int,
int FDECL((*), (MONST_P, OBJ_P)),
int FDECL((*), (OBJ_P, OBJ_P)), struct obj *));
STATIC_DCL void FDECL(you_aggravate, (struct monst *));
STATIC_DCL void FDECL(mon_consume_unstone, (struct monst *, struct obj *,
BOOLEAN_P, BOOLEAN_P));
STATIC_DCL boolean FDECL(cures_stoning, (struct monst *, struct obj *,
BOOLEAN_P));
STATIC_DCL boolean FDECL(mcould_eat_tin, (struct monst *));
STATIC_DCL boolean FDECL(muse_unslime, (struct monst *, struct obj *,
struct trap *, BOOLEAN_P));
STATIC_DCL int FDECL(cures_sliming, (struct monst *, struct obj *));
STATIC_DCL boolean FDECL(green_mon, (struct monst *));
static struct musable {
struct obj *offensive;
struct obj *defensive;
struct obj *misc;
int has_offense, has_defense, has_misc;
/* =0, no capability; otherwise, different numbers.
* If it's an object, the object is also set (it's 0 otherwise).
*/
} m;
static int trapx, trapy;
static boolean zap_oseen; /* for wands which use mbhitm and are zapped at
* players. We usually want an oseen local to
* the function, but this is impossible since the
* function mbhitm has to be compatible with the
* normal zap routines, and those routines don't
* remember who zapped the wand. */
/* Any preliminary checks which may result in the monster being unable to use
* the item. Returns 0 if nothing happened, 2 if the monster can't do
* anything (i.e. it teleported) and 1 if it's dead.
*/
STATIC_OVL int
precheck(mon, obj)
struct monst *mon;
struct obj *obj;
{
boolean vis;
if (!obj)
return 0;
vis = cansee(mon->mx, mon->my);
if (obj->oclass == POTION_CLASS) {
coord cc;
static const char *empty = "The potion turns out to be empty.";
const char *potion_descr;
struct monst *mtmp;
potion_descr = OBJ_DESCR(objects[obj->otyp]);
if (potion_descr && !strcmp(potion_descr, "milky")) {
if (!(mvitals[PM_GHOST].mvflags & G_GONE)
&& !rn2(POTION_OCCUPANT_CHANCE(mvitals[PM_GHOST].born))) {
if (!enexto(&cc, mon->mx, mon->my, &mons[PM_GHOST]))
return 0;
mquaffmsg(mon, obj);
m_useup(mon, obj);
mtmp = makemon(&mons[PM_GHOST], cc.x, cc.y, NO_MM_FLAGS);
if (!mtmp) {
if (vis)
pline1(empty);
} else {
if (vis) {
pline(
"As %s opens the bottle, an enormous %s emerges!",
mon_nam(mon),
Hallucination ? rndmonnam(NULL)
: (const char *) "ghost");
pline("%s is frightened to death, and unable to move.",
Monnam(mon));
}
paralyze_monst(mon, 3);
}
return 2;
}
}
if (potion_descr && !strcmp(potion_descr, "smoky")
&& !(mvitals[PM_DJINNI].mvflags & G_GONE)
&& !rn2(POTION_OCCUPANT_CHANCE(mvitals[PM_DJINNI].born))) {
if (!enexto(&cc, mon->mx, mon->my, &mons[PM_DJINNI]))
return 0;
mquaffmsg(mon, obj);
m_useup(mon, obj);
mtmp = makemon(&mons[PM_DJINNI], cc.x, cc.y, NO_MM_FLAGS);
if (!mtmp) {
if (vis)
pline1(empty);
} else {
if (vis)
pline("In a cloud of smoke, %s emerges!", a_monnam(mtmp));
pline("%s speaks.", vis ? Monnam(mtmp) : Something);
/* I suspect few players will be upset that monsters */
/* can't wish for wands of death here.... */
if (rn2(2)) {
verbalize("You freed me!");
mtmp->mpeaceful = 1;
set_malign(mtmp);
} else {
verbalize("It is about time.");
if (vis)
pline("%s vanishes.", Monnam(mtmp));
mongone(mtmp);
}
}
return 2;
}
}
if (obj->oclass == WAND_CLASS && obj->cursed
&& !rn2(WAND_BACKFIRE_CHANCE)) {
int dam = d(obj->spe + 2, 6);
/* 3.6.1: no Deaf filter; 'if' message doesn't warrant it, 'else'
message doesn't need it since You_hear() has one of its own */
if (vis) {
pline("%s zaps %s, which suddenly explodes!", Monnam(mon),
an(xname(obj)));
} else {
/* same near/far threshold as mzapmsg() */
int range = couldsee(mon->mx, mon->my) /* 9 or 5 */
? (BOLT_LIM + 1) : (BOLT_LIM - 3);
You_hear("a zap and an explosion %s.",
(distu(mon->mx, mon->my) <= range * range)
? "nearby" : "in the distance");
}
m_useup(mon, obj);
mon->mhp -= dam;
if (mon->mhp <= 0) {
monkilled(mon, "", AD_RBRE);
return 1;
}
m.has_defense = m.has_offense = m.has_misc = 0;
/* Only one needed to be set to 0 but the others are harmless */
}
return 0;
}
STATIC_OVL void
mzapmsg(mtmp, otmp, self)
struct monst *mtmp;
struct obj *otmp;
boolean self;
{
if (!canseemon(mtmp)) {
int range = couldsee(mtmp->mx, mtmp->my) /* 9 or 5 */
? (BOLT_LIM + 1) : (BOLT_LIM - 3);
You_hear("a %s zap.", (distu(mtmp->mx, mtmp->my) <= range * range)
? "nearby" : "distant");
} else if (self) {
pline("%s zaps %sself with %s!", Monnam(mtmp), mhim(mtmp),
doname(otmp));
} else {
pline("%s zaps %s!", Monnam(mtmp), an(xname(otmp)));
stop_occupation();
}
}
STATIC_OVL void
mreadmsg(mtmp, otmp)
struct monst *mtmp;
struct obj *otmp;
{
boolean vismon = canseemon(mtmp);
char onambuf[BUFSZ];
short saverole;
unsigned savebknown;
if (!vismon && Deaf)
return; /* no feedback */
otmp->dknown = 1; /* seeing or hearing it read reveals its label */
/* shouldn't be able to hear curse/bless status of unseen scrolls;
for priest characters, bknown will always be set during naming */
savebknown = otmp->bknown;
saverole = Role_switch;
if (!vismon) {
otmp->bknown = 0;
if (Role_if(PM_PRIEST))
Role_switch = 0;
}
Strcpy(onambuf, singular(otmp, doname));
Role_switch = saverole;
otmp->bknown = savebknown;
if (vismon)
pline("%s reads %s!", Monnam(mtmp), onambuf);
else
You_hear("%s reading %s.",
x_monnam(mtmp, ARTICLE_A, (char *) 0,
(SUPPRESS_IT | SUPPRESS_INVISIBLE | SUPPRESS_SADDLE),
FALSE),
onambuf);
if (mtmp->mconf)
pline("Being confused, %s mispronounces the magic words...",
vismon ? mon_nam(mtmp) : mhe(mtmp));
}
STATIC_OVL void
mquaffmsg(mtmp, otmp)
struct monst *mtmp;
struct obj *otmp;
{
if (canseemon(mtmp)) {
otmp->dknown = 1;
pline("%s drinks %s!", Monnam(mtmp), singular(otmp, doname));
} else if (!Deaf)
You_hear("a chugging sound.");
}
/* Defines for various types of stuff. The order in which monsters prefer
* to use them is determined by the order of the code logic, not the
* numerical order in which they are defined.
*/
#define MUSE_SCR_TELEPORTATION 1
#define MUSE_WAN_TELEPORTATION_SELF 2
#define MUSE_POT_HEALING 3
#define MUSE_POT_EXTRA_HEALING 4
#define MUSE_WAN_DIGGING 5
#define MUSE_TRAPDOOR 6
#define MUSE_TELEPORT_TRAP 7
#define MUSE_UPSTAIRS 8
#define MUSE_DOWNSTAIRS 9
#define MUSE_WAN_CREATE_MONSTER 10
#define MUSE_SCR_CREATE_MONSTER 11
#define MUSE_UP_LADDER 12
#define MUSE_DN_LADDER 13
#define MUSE_SSTAIRS 14
#define MUSE_WAN_TELEPORTATION 15
#define MUSE_BUGLE 16
#define MUSE_UNICORN_HORN 17
#define MUSE_POT_FULL_HEALING 18
#define MUSE_LIZARD_CORPSE 19
/*
#define MUSE_INNATE_TPT 9999
* We cannot use this. Since monsters get unlimited teleportation, if they
* were allowed to teleport at will you could never catch them. Instead,
* assume they only teleport at random times, despite the inconsistency
* that if you polymorph into one you teleport at will.
*/
STATIC_OVL boolean
m_use_healing(mtmp)
struct monst *mtmp;
{
struct obj *obj = 0;
if ((obj = m_carrying(mtmp, POT_FULL_HEALING)) != 0) {
m.defensive = obj;
m.has_defense = MUSE_POT_FULL_HEALING;
return TRUE;
}
if ((obj = m_carrying(mtmp, POT_EXTRA_HEALING)) != 0) {
m.defensive = obj;
m.has_defense = MUSE_POT_EXTRA_HEALING;
return TRUE;
}
if ((obj = m_carrying(mtmp, POT_HEALING)) != 0) {
m.defensive = obj;
m.has_defense = MUSE_POT_HEALING;
return TRUE;
}
return FALSE;
}
/* Select a defensive item/action for a monster. Returns TRUE iff one is
found. */
boolean
find_defensive(mtmp)
struct monst *mtmp;
{
register struct obj *obj = 0;
struct trap *t;
int x = mtmp->mx, y = mtmp->my;
boolean stuck = (mtmp == u.ustuck);
boolean immobile = (mtmp->data->mmove == 0);
int fraction;
if (is_animal(mtmp->data) || mindless(mtmp->data))
return FALSE;
if (dist2(x, y, mtmp->mux, mtmp->muy) > 25)
return FALSE;
if (u.uswallow && stuck)
return FALSE;
m.defensive = (struct obj *) 0;
m.has_defense = 0;
/* since unicorn horns don't get used up, the monster would look
* silly trying to use the same cursed horn round after round
*/
if (mtmp->mconf || mtmp->mstun || !mtmp->mcansee) {
if (!is_unicorn(mtmp->data) && !nohands(mtmp->data)) {
for (obj = mtmp->minvent; obj; obj = obj->nobj)
if (obj->otyp == UNICORN_HORN && !obj->cursed)
break;
}
if (obj || is_unicorn(mtmp->data)) {
m.defensive = obj;
m.has_defense = MUSE_UNICORN_HORN;
return TRUE;
}
}
if (mtmp->mconf || mtmp->mstun) {
struct obj *liztin = 0;
for (obj = mtmp->minvent; obj; obj = obj->nobj) {
if (obj->otyp == CORPSE && obj->corpsenm == PM_LIZARD) {
m.defensive = obj;
m.has_defense = MUSE_LIZARD_CORPSE;
return TRUE;
} else if (obj->otyp == TIN && obj->corpsenm == PM_LIZARD) {
liztin = obj;
}
}
/* confused or stunned monster might not be able to open tin */
if (liztin && mcould_eat_tin(mtmp) && rn2(3)) {
m.defensive = liztin;
/* tin and corpse ultimately end up being handled the same */
m.has_defense = MUSE_LIZARD_CORPSE;
return TRUE;
}
}
/* It so happens there are two unrelated cases when we might want to
* check specifically for healing alone. The first is when the monster
* is blind (healing cures blindness). The second is when the monster
* is peaceful; then we don't want to flee the player, and by
* coincidence healing is all there is that doesn't involve fleeing.
* These would be hard to combine because of the control flow.
* Pestilence won't use healing even when blind.
*/
if (!mtmp->mcansee && !nohands(mtmp->data)
&& mtmp->data != &mons[PM_PESTILENCE]) {
if (m_use_healing(mtmp))
return TRUE;
}
fraction = u.ulevel < 10 ? 5 : u.ulevel < 14 ? 4 : 3;
if (mtmp->mhp >= mtmp->mhpmax
|| (mtmp->mhp >= 10 && mtmp->mhp * fraction >= mtmp->mhpmax))
return FALSE;
if (mtmp->mpeaceful) {
if (!nohands(mtmp->data)) {
if (m_use_healing(mtmp))
return TRUE;
}
return FALSE;
}
if (stuck || immobile) {
; /* fleeing by stairs or traps is not possible */
} else if (levl[x][y].typ == STAIRS) {
if (x == xdnstair && y == ydnstair) {
if (!is_floater(mtmp->data))
m.has_defense = MUSE_DOWNSTAIRS;
} else if (x == xupstair && y == yupstair) {
m.has_defense = MUSE_UPSTAIRS;
} else if (sstairs.sx && x == sstairs.sx && y == sstairs.sy) {
if (sstairs.up || !is_floater(mtmp->data))
m.has_defense = MUSE_SSTAIRS;
}
} else if (levl[x][y].typ == LADDER) {
if (x == xupladder && y == yupladder) {
m.has_defense = MUSE_UP_LADDER;
} else if (x == xdnladder && y == ydnladder) {
if (!is_floater(mtmp->data))
m.has_defense = MUSE_DN_LADDER;
} else if (sstairs.sx && x == sstairs.sx && y == sstairs.sy) {
if (sstairs.up || !is_floater(mtmp->data))
m.has_defense = MUSE_SSTAIRS;
}
} else {
/* Note: trap doors take precedence over teleport traps. */
int xx, yy, i, locs[10][2];
boolean ignore_boulders = (verysmall(mtmp->data)
|| throws_rocks(mtmp->data)
|| passes_walls(mtmp->data)),
diag_ok = !NODIAG(monsndx(mtmp->data));
for (i = 0; i < 10; ++i) /* 10: 9 spots plus sentinel */
locs[i][0] = locs[i][1] = 0;
/* collect viable spots; monster's <mx,my> comes first */
locs[0][0] = x, locs[0][1] = y;
i = 1;
for (xx = x - 1; xx <= x + 1; xx++)
for (yy = y - 1; yy <= y + 1; yy++)
if (isok(xx, yy) && (xx != x || yy != y)) {
locs[i][0] = xx, locs[i][1] = yy;
++i;
}
/* look for a suitable trap among the viable spots */
for (i = 0; i < 10; ++i) {
xx = locs[i][0], yy = locs[i][1];
if (!xx)
break; /* we've run out of spots */
/* skip if it's hero's location
or a diagonal spot and monster can't move diagonally
or some other monster is there */
if ((xx == u.ux && yy == u.uy)
|| (xx != x && yy != y && !diag_ok)
|| (level.monsters[xx][yy] && !(xx == x && yy == y)))
continue;
/* skip if there's no trap or can't/won't move onto trap */
if ((t = t_at(xx, yy)) == 0
|| (!ignore_boulders && sobj_at(BOULDER, xx, yy))
|| onscary(xx, yy, mtmp))
continue;
/* use trap if it's the correct type */
if ((t->ttyp == TRAPDOOR || t->ttyp == HOLE)
&& !is_floater(mtmp->data)
&& !mtmp->isshk && !mtmp->isgd && !mtmp->ispriest
&& Can_fall_thru(&u.uz)) {
trapx = xx;
trapy = yy;
m.has_defense = MUSE_TRAPDOOR;
break; /* no need to look at any other spots */
} else if (t->ttyp == TELEP_TRAP) {
trapx = xx;
trapy = yy;
m.has_defense = MUSE_TELEPORT_TRAP;
}
}
}
if (nohands(mtmp->data)) /* can't use objects */
goto botm;
if (is_mercenary(mtmp->data) && (obj = m_carrying(mtmp, BUGLE)) != 0) {
int xx, yy;
struct monst *mon;
/* Distance is arbitrary. What we really want to do is
* have the soldier play the bugle when it sees or
* remembers soldiers nearby...
*/
for (xx = x - 3; xx <= x + 3; xx++) {
for (yy = y - 3; yy <= y + 3; yy++) {
if (!isok(xx, yy) || (xx == x && yy == y))
continue;
if ((mon = m_at(xx, yy)) != 0 && is_mercenary(mon->data)
&& mon->data != &mons[PM_GUARD]
&& (mon->msleeping || !mon->mcanmove)) {
m.defensive = obj;
m.has_defense = MUSE_BUGLE;
goto toot; /* double break */
}
}
}
toot:
;
}
/* use immediate physical escape prior to attempting magic */
if (m.has_defense) /* stairs, trap door or tele-trap, bugle alert */
goto botm;
/* kludge to cut down on trap destruction (particularly portals) */
t = t_at(x, y);
if (t && (t->ttyp == PIT || t->ttyp == SPIKED_PIT || t->ttyp == WEB
|| t->ttyp == BEAR_TRAP))
t = 0; /* ok for monster to dig here */
#define nomore(x) if (m.has_defense == x) continue;
/* selection could be improved by collecting all possibilities
into an array and then picking one at random */
for (obj = mtmp->minvent; obj; obj = obj->nobj) {
/* don't always use the same selection pattern */
if (m.has_defense && !rn2(3))
break;
/* nomore(MUSE_WAN_DIGGING); */
if (m.has_defense == MUSE_WAN_DIGGING)
break;
if (obj->otyp == WAN_DIGGING && obj->spe > 0 && !stuck && !t
&& !mtmp->isshk && !mtmp->isgd && !mtmp->ispriest
&& !is_floater(mtmp->data)
/* monsters digging in Sokoban can ruin things */
&& !Sokoban
/* digging wouldn't be effective; assume they know that */
&& !(levl[x][y].wall_info & W_NONDIGGABLE)
&& !(Is_botlevel(&u.uz) || In_endgame(&u.uz))
&& !(is_ice(x, y) || is_pool(x, y) || is_lava(x, y))
&& !(mtmp->data == &mons[PM_VLAD_THE_IMPALER]
&& In_V_tower(&u.uz))) {
m.defensive = obj;
m.has_defense = MUSE_WAN_DIGGING;
}
nomore(MUSE_WAN_TELEPORTATION_SELF);
nomore(MUSE_WAN_TELEPORTATION);
if (obj->otyp == WAN_TELEPORTATION && obj->spe > 0) {
/* use the TELEP_TRAP bit to determine if they know
* about noteleport on this level or not. Avoids
* ineffective re-use of teleportation. This does
* mean if the monster leaves the level, they'll know
* about teleport traps.
*/
if (!level.flags.noteleport
|| !(mtmp->mtrapseen & (1 << (TELEP_TRAP - 1)))) {
m.defensive = obj;
m.has_defense = (mon_has_amulet(mtmp))
? MUSE_WAN_TELEPORTATION
: MUSE_WAN_TELEPORTATION_SELF;
}
}
nomore(MUSE_SCR_TELEPORTATION);
if (obj->otyp == SCR_TELEPORTATION && mtmp->mcansee
&& haseyes(mtmp->data)
&& (!obj->cursed || (!(mtmp->isshk && inhishop(mtmp))
&& !mtmp->isgd && !mtmp->ispriest))) {
/* see WAN_TELEPORTATION case above */
if (!level.flags.noteleport
|| !(mtmp->mtrapseen & (1 << (TELEP_TRAP - 1)))) {
m.defensive = obj;
m.has_defense = MUSE_SCR_TELEPORTATION;
}
}
if (mtmp->data != &mons[PM_PESTILENCE]) {
nomore(MUSE_POT_FULL_HEALING);
if (obj->otyp == POT_FULL_HEALING) {
m.defensive = obj;
m.has_defense = MUSE_POT_FULL_HEALING;
}
nomore(MUSE_POT_EXTRA_HEALING);
if (obj->otyp == POT_EXTRA_HEALING) {
m.defensive = obj;
m.has_defense = MUSE_POT_EXTRA_HEALING;
}
nomore(MUSE_WAN_CREATE_MONSTER);
if (obj->otyp == WAN_CREATE_MONSTER && obj->spe > 0) {
m.defensive = obj;
m.has_defense = MUSE_WAN_CREATE_MONSTER;
}
nomore(MUSE_POT_HEALING);
if (obj->otyp == POT_HEALING) {
m.defensive = obj;
m.has_defense = MUSE_POT_HEALING;
}
} else { /* Pestilence */
nomore(MUSE_POT_FULL_HEALING);
if (obj->otyp == POT_SICKNESS) {
m.defensive = obj;
m.has_defense = MUSE_POT_FULL_HEALING;
}
nomore(MUSE_WAN_CREATE_MONSTER);
if (obj->otyp == WAN_CREATE_MONSTER && obj->spe > 0) {
m.defensive = obj;
m.has_defense = MUSE_WAN_CREATE_MONSTER;
}
}
nomore(MUSE_SCR_CREATE_MONSTER);
if (obj->otyp == SCR_CREATE_MONSTER) {
m.defensive = obj;
m.has_defense = MUSE_SCR_CREATE_MONSTER;
}
}
botm:
return (boolean) !!m.has_defense;
#undef nomore
}
/* Perform a defensive action for a monster. Must be called immediately
* after find_defensive(). Return values are 0: did something, 1: died,
* 2: did something and can't attack again (i.e. teleported).
*/
int
use_defensive(mtmp)
struct monst *mtmp;
{
int i, fleetim, how = 0;
struct obj *otmp = m.defensive;
boolean vis, vismon, oseen;
const char *Mnam;
if ((i = precheck(mtmp, otmp)) != 0)
return i;
vis = cansee(mtmp->mx, mtmp->my);
vismon = canseemon(mtmp);
oseen = otmp && vismon;
/* when using defensive choice to run away, we want monster to avoid
rushing right straight back; don't override if already scared */
fleetim = !mtmp->mflee ? (33 - (30 * mtmp->mhp / mtmp->mhpmax)) : 0;
#define m_flee(m) \
if (fleetim && !m->iswiz) { \
monflee(m, fleetim, FALSE, FALSE); \
}
switch (m.has_defense) {
case MUSE_UNICORN_HORN:
if (vismon) {
if (otmp)
pline("%s uses a unicorn horn!", Monnam(mtmp));
else
pline_The("tip of %s's horn glows!", mon_nam(mtmp));
}
if (!mtmp->mcansee) {
mcureblindness(mtmp, vismon);
} else if (mtmp->mconf || mtmp->mstun) {
mtmp->mconf = mtmp->mstun = 0;
if (vismon)
pline("%s seems steadier now.", Monnam(mtmp));
} else
impossible("No need for unicorn horn?");
return 2;
case MUSE_BUGLE:
if (vismon)
pline("%s plays %s!", Monnam(mtmp), doname(otmp));
else if (!Deaf)
You_hear("a bugle playing reveille!");
awaken_soldiers(mtmp);
return 2;
case MUSE_WAN_TELEPORTATION_SELF:
if ((mtmp->isshk && inhishop(mtmp)) || mtmp->isgd || mtmp->ispriest)
return 2;
m_flee(mtmp);
mzapmsg(mtmp, otmp, TRUE);
otmp->spe--;
how = WAN_TELEPORTATION;
mon_tele:
if (tele_restrict(mtmp)) { /* mysterious force... */
if (vismon && how) /* mentions 'teleport' */
makeknown(how);
/* monster learns that teleportation isn't useful here */
if (level.flags.noteleport)
mtmp->mtrapseen |= (1 << (TELEP_TRAP - 1));
return 2;
}
if ((mon_has_amulet(mtmp) || On_W_tower_level(&u.uz)) && !rn2(3)) {
if (vismon)
pline("%s seems disoriented for a moment.", Monnam(mtmp));
return 2;
}
if (oseen && how)
makeknown(how);
(void) rloc(mtmp, TRUE);
return 2;
case MUSE_WAN_TELEPORTATION:
zap_oseen = oseen;
mzapmsg(mtmp, otmp, FALSE);
otmp->spe--;
m_using = TRUE;
mbhit(mtmp, rn1(8, 6), mbhitm, bhito, otmp);
/* monster learns that teleportation isn't useful here */
if (level.flags.noteleport)
mtmp->mtrapseen |= (1 << (TELEP_TRAP - 1));
m_using = FALSE;
return 2;
case MUSE_SCR_TELEPORTATION: {
int obj_is_cursed = otmp->cursed;
if (mtmp->isshk || mtmp->isgd || mtmp->ispriest)
return 2;
m_flee(mtmp);
mreadmsg(mtmp, otmp);
m_useup(mtmp, otmp); /* otmp might be free'ed */
how = SCR_TELEPORTATION;
if (obj_is_cursed || mtmp->mconf) {
int nlev;
d_level flev;
if (mon_has_amulet(mtmp) || In_endgame(&u.uz)) {
if (vismon)
pline("%s seems very disoriented for a moment.",
Monnam(mtmp));
return 2;
}
nlev = random_teleport_level();
if (nlev == depth(&u.uz)) {
if (vismon)
pline("%s shudders for a moment.", Monnam(mtmp));
return 2;
}
get_level(&flev, nlev);
migrate_to_level(mtmp, ledger_no(&flev), MIGR_RANDOM,
(coord *) 0);
if (oseen)
makeknown(SCR_TELEPORTATION);
} else
goto mon_tele;
return 2;
}
case MUSE_WAN_DIGGING: {
struct trap *ttmp;
m_flee(mtmp);
mzapmsg(mtmp, otmp, FALSE);
otmp->spe--;
if (oseen)
makeknown(WAN_DIGGING);
if (IS_FURNITURE(levl[mtmp->mx][mtmp->my].typ)
|| IS_DRAWBRIDGE(levl[mtmp->mx][mtmp->my].typ)
|| (is_drawbridge_wall(mtmp->mx, mtmp->my) >= 0)
|| (sstairs.sx && sstairs.sx == mtmp->mx
&& sstairs.sy == mtmp->my)) {
pline_The("digging ray is ineffective.");
return 2;
}
if (!Can_dig_down(&u.uz) && !levl[mtmp->mx][mtmp->my].candig) {
if (canseemon(mtmp))
pline_The("%s here is too hard to dig in.",
surface(mtmp->mx, mtmp->my));
return 2;
}
ttmp = maketrap(mtmp->mx, mtmp->my, HOLE);
if (!ttmp)
return 2;
seetrap(ttmp);
if (vis) {
pline("%s has made a hole in the %s.", Monnam(mtmp),
surface(mtmp->mx, mtmp->my));
pline("%s %s through...", Monnam(mtmp),
is_flyer(mtmp->data) ? "dives" : "falls");
} else if (!Deaf)
You_hear("%s crash through the %s.", something,
surface(mtmp->mx, mtmp->my));
/* we made sure that there is a level for mtmp to go to */
migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_RANDOM,
(coord *) 0);
return 2;
}
case MUSE_WAN_CREATE_MONSTER: {
coord cc;
/* pm: 0 => random, eel => aquatic, croc => amphibious */
struct permonst *pm =
!is_pool(mtmp->mx, mtmp->my)
? 0
: &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
struct monst *mon;
if (!enexto(&cc, mtmp->mx, mtmp->my, pm))
return 0;
mzapmsg(mtmp, otmp, FALSE);
otmp->spe--;
mon = makemon((struct permonst *) 0, cc.x, cc.y, NO_MM_FLAGS);
if (mon && canspotmon(mon) && oseen)
makeknown(WAN_CREATE_MONSTER);
return 2;
}
case MUSE_SCR_CREATE_MONSTER: {
coord cc;
struct permonst *pm = 0, *fish = 0;
int cnt = 1;
struct monst *mon;
boolean known = FALSE;
if (!rn2(73))
cnt += rnd(4);
if (mtmp->mconf || otmp->cursed)
cnt += 12;
if (mtmp->mconf)
pm = fish = &mons[PM_ACID_BLOB];
else if (is_pool(mtmp->mx, mtmp->my))
fish = &mons[u.uinwater ? PM_GIANT_EEL : PM_CROCODILE];
mreadmsg(mtmp, otmp);
while (cnt--) {
/* `fish' potentially gives bias towards water locations;
`pm' is what to actually create (0 => random) */
if (!enexto(&cc, mtmp->mx, mtmp->my, fish))
break;
mon = makemon(pm, cc.x, cc.y, NO_MM_FLAGS);
if (mon && canspotmon(mon))
known = TRUE;
}
/* The only case where we don't use oseen. For wands, you
* have to be able to see the monster zap the wand to know
* what type it is. For teleport scrolls, you have to see
* the monster to know it teleported.
*/
if (known)
makeknown(SCR_CREATE_MONSTER);
else if (!objects[SCR_CREATE_MONSTER].oc_name_known
&& !objects[SCR_CREATE_MONSTER].oc_uname)
docall(otmp);
m_useup(mtmp, otmp);
return 2;
}
case MUSE_TRAPDOOR:
/* trap doors on "bottom" levels of dungeons are rock-drop
* trap doors, not holes in the floor. We check here for
* safety.
*/
if (Is_botlevel(&u.uz))
return 0;
m_flee(mtmp);
if (vis) {
struct trap *t = t_at(trapx, trapy);
Mnam = Monnam(mtmp);
pline("%s %s into a %s!", Mnam,
vtense(Mnam, locomotion(mtmp->data, "jump")),
(t->ttyp == TRAPDOOR) ? "trap door" : "hole");
if (levl[trapx][trapy].typ == SCORR) {
levl[trapx][trapy].typ = CORR;
unblock_point(trapx, trapy);
}
seetrap(t_at(trapx, trapy));
}
/* don't use rloc_to() because worm tails must "move" */
remove_monster(mtmp->mx, mtmp->my);
newsym(mtmp->mx, mtmp->my); /* update old location */
place_monster(mtmp, trapx, trapy);
if (mtmp->wormno)
worm_move(mtmp);
newsym(trapx, trapy);
migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_RANDOM,
(coord *) 0);
return 2;
case MUSE_UPSTAIRS:
m_flee(mtmp);
if (ledger_no(&u.uz) == 1)
goto escape; /* impossible; level 1 upstairs are SSTAIRS */
if (Inhell && mon_has_amulet(mtmp) && !rn2(4)
&& (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz) - 3)) {
if (vismon)
pline(
"As %s climbs the stairs, a mysterious force momentarily surrounds %s...",
mon_nam(mtmp), mhim(mtmp));
/* simpler than for the player; this will usually be
the Wizard and he'll immediately go right to the
upstairs, so there's not much point in having any
chance for a random position on the current level */
migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_RANDOM,
(coord *) 0);
} else {
if (vismon)
pline("%s escapes upstairs!", Monnam(mtmp));
migrate_to_level(mtmp, ledger_no(&u.uz) - 1, MIGR_STAIRS_DOWN,
(coord *) 0);
}
return 2;
case MUSE_DOWNSTAIRS:
m_flee(mtmp);
if (vismon)
pline("%s escapes downstairs!", Monnam(mtmp));
migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_STAIRS_UP,
(coord *) 0);
return 2;
case MUSE_UP_LADDER:
m_flee(mtmp);
if (vismon)
pline("%s escapes up the ladder!", Monnam(mtmp));
migrate_to_level(mtmp, ledger_no(&u.uz) - 1, MIGR_LADDER_DOWN,
(coord *) 0);
return 2;
case MUSE_DN_LADDER:
m_flee(mtmp);
if (vismon)
pline("%s escapes down the ladder!", Monnam(mtmp));
migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_LADDER_UP,
(coord *) 0);
return 2;
case MUSE_SSTAIRS:
m_flee(mtmp);
if (ledger_no(&u.uz) == 1) {
escape:
/* Monsters without the Amulet escape the dungeon and
* are gone for good when they leave up the up stairs.
* A monster with the Amulet would leave it behind
* (mongone -> mdrop_special_objs) but we force any
* monster who manages to acquire it or the invocation
* tools to stick around instead of letting it escape.
*/
if (mon_has_special(mtmp))
return 0;
if (vismon)
pline("%s escapes the dungeon!", Monnam(mtmp));
mongone(mtmp);
return 2;
}
if (vismon)
pline("%s escapes %sstairs!", Monnam(mtmp),
sstairs.up ? "up" : "down");
/* going from the Valley to Castle (Stronghold) has no sstairs
to target, but having sstairs.<sx,sy> == <0,0> will work the
same as specifying MIGR_RANDOM when mon_arrive() eventually
places the monster, so we can use MIGR_SSTAIRS unconditionally */
migrate_to_level(mtmp, ledger_no(&sstairs.tolev), MIGR_SSTAIRS,
(coord *) 0);
return 2;
case MUSE_TELEPORT_TRAP:
m_flee(mtmp);
if (vis) {
Mnam = Monnam(mtmp);
pline("%s %s onto a teleport trap!", Mnam,
vtense(Mnam, locomotion(mtmp->data, "jump")));
seetrap(t_at(trapx, trapy));
}
/* don't use rloc_to() because worm tails must "move" */
remove_monster(mtmp->mx, mtmp->my);
newsym(mtmp->mx, mtmp->my); /* update old location */
place_monster(mtmp, trapx, trapy);
if (mtmp->wormno)
worm_move(mtmp);
newsym(trapx, trapy);
goto mon_tele;
case MUSE_POT_HEALING:
mquaffmsg(mtmp, otmp);
i = d(6 + 2 * bcsign(otmp), 4);
mtmp->mhp += i;
if (mtmp->mhp > mtmp->mhpmax)
mtmp->mhp = ++mtmp->mhpmax;
if (!otmp->cursed && !mtmp->mcansee)
mcureblindness(mtmp, vismon);
if (vismon)
pline("%s looks better.", Monnam(mtmp));
if (oseen)
makeknown(POT_HEALING);
m_useup(mtmp, otmp);
return 2;
case MUSE_POT_EXTRA_HEALING:
mquaffmsg(mtmp, otmp);
i = d(6 + 2 * bcsign(otmp), 8);
mtmp->mhp += i;
if (mtmp->mhp > mtmp->mhpmax)
mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 5 : 2));
if (!mtmp->mcansee)
mcureblindness(mtmp, vismon);
if (vismon)
pline("%s looks much better.", Monnam(mtmp));
if (oseen)
makeknown(POT_EXTRA_HEALING);
m_useup(mtmp, otmp);
return 2;
case MUSE_POT_FULL_HEALING:
mquaffmsg(mtmp, otmp);
if (otmp->otyp == POT_SICKNESS)
unbless(otmp); /* Pestilence */
mtmp->mhp = (mtmp->mhpmax += (otmp->blessed ? 8 : 4));
if (!mtmp->mcansee && otmp->otyp != POT_SICKNESS)
mcureblindness(mtmp, vismon);
if (vismon)
pline("%s looks completely healed.", Monnam(mtmp));
if (oseen)
makeknown(otmp->otyp);
m_useup(mtmp, otmp);
return 2;
case MUSE_LIZARD_CORPSE:
/* not actually called for its unstoning effect */
mon_consume_unstone(mtmp, otmp, FALSE, FALSE);
return 2;
case 0:
return 0; /* i.e. an exploded wand */
default:
impossible("%s wanted to perform action %d?", Monnam(mtmp),
m.has_defense);
break;
}
return 0;
#undef m_flee
}
int
rnd_defensive_item(mtmp)
struct monst *mtmp;
{
struct permonst *pm = mtmp->data;
int difficulty = monstr[(monsndx(pm))];
int trycnt = 0;
if (is_animal(pm) || attacktype(pm, AT_EXPL) || mindless(mtmp->data)
|| pm->mlet == S_GHOST || pm->mlet == S_KOP)
return 0;
try_again: