1 | /* SCCS Id: @(#)wizard.c 3.3 99/03/29 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | /* wizard code - inspired by rogue code from Merlyn Leroy (digi-g!brian) */
6 | /* - heavily modified to give the wiz balls. (genat!mike) */
7 | /* - dewimped and given some maledictions. -3. */
8 | /* - generalized for 3.1 (mike@bullns.on01.bull.ca) */
9 |
10 | #include "hack.h"
11 | #include "qtext.h"
12 |
13 | #ifdef OVLB
14 |
15 | STATIC_DCL short FDECL(which_arti, (int));
16 | STATIC_DCL boolean FDECL(mon_has_arti, (struct monst *,SHORT_P));
17 | STATIC_DCL struct monst *FDECL(other_mon_has_arti, (struct monst *,SHORT_P));
18 | STATIC_DCL struct obj *FDECL(on_ground, (SHORT_P));
19 | STATIC_DCL boolean FDECL(you_have, (int));
20 | STATIC_DCL long FDECL(target_on, (int,struct monst *));
21 | STATIC_DCL long FDECL(strategy, (struct monst *));
22 |
23 | static NEARDATA const int nasties[] = {
24 | PM_COCKATRICE, PM_ETTIN, PM_STALKER, PM_MINOTAUR, PM_RED_DRAGON,
25 | PM_BLACK_DRAGON, PM_GREEN_DRAGON, PM_OWLBEAR, PM_PURPLE_WORM,
26 | PM_ROCK_TROLL, PM_XAN, PM_GREMLIN, PM_UMBER_HULK, PM_VAMPIRE_LORD,
27 | PM_XORN, PM_ZRUTY, PM_ELF_LORD, PM_ELVENKING, PM_YELLOW_DRAGON,
28 | PM_LEOCROTTA, PM_BALUCHITHERIUM, PM_CARNIVOROUS_APE, PM_FIRE_GIANT,
29 | PM_COUATL, PM_CAPTAIN, PM_WINGED_GARGOYLE, PM_MASTER_MIND_FLAYER,
30 | PM_FIRE_ELEMENTAL, PM_JABBERWOCK, PM_ARCH_LICH, PM_OGRE_KING,
31 | PM_OLOG_HAI, PM_IRON_GOLEM, PM_OCHRE_JELLY, PM_GREEN_SLIME,
32 | PM_DISENCHANTER
33 | };
34 |
35 | static NEARDATA const unsigned wizapp[] = {
36 | PM_HUMAN, PM_WATER_DEMON, PM_VAMPIRE,
37 | PM_RED_DRAGON, PM_TROLL, PM_UMBER_HULK,
38 | PM_XORN, PM_XAN, PM_COCKATRICE,
39 | PM_FLOATING_EYE,
40 | PM_GUARDIAN_NAGA,
41 | PM_TRAPPER
42 | };
43 |
44 | #endif /* OVLB */
45 | #ifdef OVL0
46 |
47 | /* If you've found the Amulet, make the Wizard appear after some time */
48 | /* Also, give hints about portal locations, if amulet is worn/wielded -dlc */
49 | void
50 | amulet()
51 | {
52 | struct monst *mtmp;
53 | struct trap *ttmp;
54 | struct obj *amu;
55 |
56 | #if 0 /* caller takes care of this check */
57 | if (!u.uhave.amulet)
58 | return;
59 | #endif
60 | if ((((amu = uamul) != 0 && amu->otyp == AMULET_OF_YENDOR) ||
61 | ((amu = uwep) != 0 && amu->otyp == AMULET_OF_YENDOR))
62 | && !rn2(15)) {
63 | for(ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
64 | if(ttmp->ttyp == MAGIC_PORTAL) {
65 | int du = distu(ttmp->tx, ttmp->ty);
66 | if (du <= 9)
67 | pline("%s feels hot!", The(xname(amu)));
68 | else if (du <= 64)
69 | pline("%s feels very warm.", The(xname(amu)));
70 | else if (du <= 144)
71 | pline("%s feels warm.", The(xname(amu)));
72 | /* else, the amulet feels normal */
73 | break;
74 | }
75 | }
76 | }
77 |
78 | if (!flags.no_of_wizards)
79 | return;
80 | /* find Wizard, and wake him if necessary */
81 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
82 | if (!DEADMONSTER(mtmp) && mtmp->iswiz && mtmp->msleeping && !rn2(40)) {
83 | mtmp->msleeping = 0;
84 | if (distu(mtmp->mx,mtmp->my) > 2)
85 | You(
86 | "get the creepy feeling that somebody noticed your taking the Amulet."
87 | );
88 | return;
89 | }
90 | }
91 |
92 | #endif /* OVL0 */
93 | #ifdef OVLB
94 |
95 | int
96 | mon_has_amulet(mtmp)
97 | register struct monst *mtmp;
98 | {
99 | register struct obj *otmp;
100 |
101 | for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
102 | if(otmp->otyp == AMULET_OF_YENDOR) return(1);
103 | return(0);
104 | }
105 |
106 | int
107 | mon_has_special(mtmp)
108 | register struct monst *mtmp;
109 | {
110 | register struct obj *otmp;
111 |
112 | for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
113 | if(otmp->otyp == AMULET_OF_YENDOR ||
114 | is_quest_artifact(otmp) ||
115 | otmp->otyp == BELL_OF_OPENING ||
116 | otmp->otyp == CANDELABRUM_OF_INVOCATION ||
117 | otmp->otyp == SPE_BOOK_OF_THE_DEAD) return(1);
118 | return(0);
119 | }
120 |
121 | /*
122 | * New for 3.1 Strategy / Tactics for the wiz, as well as other
123 | * monsters that are "after" something (defined via mflag3).
124 | *
125 | * The strategy section decides *what* the monster is going
126 | * to attempt, the tactics section implements the decision.
127 | */
128 | #define STRAT(w, x, y, typ) (w | ((long)(x)<<16) | ((long)(y)<<8) | (long)typ)
129 |
130 | #define M_Wants(mask) (mtmp->data->mflags3 & (mask))
131 |
132 | STATIC_OVL short
133 | which_arti(mask)
134 | register int mask;
135 | {
136 | switch(mask) {
137 | case M3_WANTSAMUL: return(AMULET_OF_YENDOR);
138 | case M3_WANTSBELL: return(BELL_OF_OPENING);
139 | case M3_WANTSCAND: return(CANDELABRUM_OF_INVOCATION);
140 | case M3_WANTSBOOK: return(SPE_BOOK_OF_THE_DEAD);
141 | default: break; /* 0 signifies quest artifact */
142 | }
143 | return(0);
144 | }
145 |
146 | /*
147 | * If "otyp" is zero, it triggers a check for the quest_artifact,
148 | * since bell, book, candle, and amulet are all objects, not really
149 | * artifacts right now. [MRS]
150 | */
151 | STATIC_OVL boolean
152 | mon_has_arti(mtmp, otyp)
153 | register struct monst *mtmp;
154 | register short otyp;
155 | {
156 | register struct obj *otmp;
157 |
158 | for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj) {
159 | if(otyp) {
160 | if(otmp->otyp == otyp)
161 | return(1);
162 | }
163 | else if(is_quest_artifact(otmp)) return(1);
164 | }
165 | return(0);
166 |
167 | }
168 |
169 | STATIC_OVL struct monst *
170 | other_mon_has_arti(mtmp, otyp)
171 | register struct monst *mtmp;
172 | register short otyp;
173 | {
174 | register struct monst *mtmp2;
175 |
176 | for(mtmp2 = fmon; mtmp2; mtmp2 = mtmp2->nmon)
177 | /* no need for !DEADMONSTER check here since they have no inventory */
178 | if(mtmp2 != mtmp)
179 | if(mon_has_arti(mtmp2, otyp)) return(mtmp2);
180 |
181 | return((struct monst *)0);
182 | }
183 |
184 | STATIC_OVL struct obj *
185 | on_ground(otyp)
186 | register short otyp;
187 | {
188 | register struct obj *otmp;
189 |
190 | for (otmp = fobj; otmp; otmp = otmp->nobj)
191 | if (otyp) {
192 | if (otmp->otyp == otyp)
193 | return(otmp);
194 | } else if (is_quest_artifact(otmp))
195 | return(otmp);
196 | return((struct obj *)0);
197 | }
198 |
199 | STATIC_OVL boolean
200 | you_have(mask)
201 | register int mask;
202 | {
203 | switch(mask) {
204 | case M3_WANTSAMUL: return(boolean)(u.uhave.amulet);
205 | case M3_WANTSBELL: return(boolean)(u.uhave.bell);
206 | case M3_WANTSCAND: return(boolean)(u.uhave.menorah);
207 | case M3_WANTSBOOK: return(boolean)(u.uhave.book);
208 | case M3_WANTSARTI: return(boolean)(u.uhave.questart);
209 | default: break;
210 | }
211 | return(0);
212 | }
213 |
214 | STATIC_OVL long
215 | target_on(mask, mtmp)
216 | register int mask;
217 | register struct monst *mtmp;
218 | {
219 | register short otyp;
220 | register struct obj *otmp;
221 | register struct monst *mtmp2;
222 |
223 | if(!M_Wants(mask)) return(STRAT_NONE);
224 |
225 | otyp = which_arti(mask);
226 | if(!mon_has_arti(mtmp, otyp)) {
227 | if(you_have(mask))
228 | return(STRAT(STRAT_PLAYER, u.ux, u.uy, mask));
229 | else if((otmp = on_ground(otyp)))
230 | return(STRAT(STRAT_GROUND, otmp->ox, otmp->oy, mask));
231 | else if((mtmp2 = other_mon_has_arti(mtmp, otyp)))
232 | return(STRAT(STRAT_MONSTR, mtmp2->mx, mtmp2->my, mask));
233 | }
234 | return(STRAT_NONE);
235 | }
236 |
237 | STATIC_OVL long
238 | strategy(mtmp)
239 | register struct monst *mtmp;
240 | {
241 | long strat, dstrat;
242 |
243 | if(!is_covetous(mtmp->data)) return(STRAT_NONE);
244 |
245 | switch((mtmp->mhp*3)/mtmp->mhpmax) { /* 0-3 */
246 |
247 | default:
248 | case 0: /* panic time - mtmp is almost snuffed */
249 | return(STRAT_HEAL);
250 |
251 | case 1: /* the wiz is less cautious */
252 | if(mtmp->data != &mons[PM_WIZARD_OF_YENDOR])
253 | return(STRAT_HEAL);
254 | /* else fall through */
255 |
256 | case 2: dstrat = STRAT_HEAL;
257 | break;
258 |
259 | case 3: dstrat = STRAT_NONE;
260 | break;
261 | }
262 |
263 | if(flags.made_amulet)
264 | if((strat = target_on(M3_WANTSAMUL, mtmp)) != STRAT_NONE)
265 | return(strat);
266 |
267 | if(u.uevent.invoked) { /* priorities change once gate opened */
268 |
269 | if((strat = target_on(M3_WANTSARTI, mtmp)) != STRAT_NONE)
270 | return(strat);
271 | if((strat = target_on(M3_WANTSBOOK, mtmp)) != STRAT_NONE)
272 | return(strat);
273 | if((strat = target_on(M3_WANTSBELL, mtmp)) != STRAT_NONE)
274 | return(strat);
275 | if((strat = target_on(M3_WANTSCAND, mtmp)) != STRAT_NONE)
276 | return(strat);
277 | } else {
278 |
279 | if((strat = target_on(M3_WANTSBOOK, mtmp)) != STRAT_NONE)
280 | return(strat);
281 | if((strat = target_on(M3_WANTSBELL, mtmp)) != STRAT_NONE)
282 | return(strat);
283 | if((strat = target_on(M3_WANTSCAND, mtmp)) != STRAT_NONE)
284 | return(strat);
285 | if((strat = target_on(M3_WANTSARTI, mtmp)) != STRAT_NONE)
286 | return(strat);
287 | }
288 | return(dstrat);
289 | }
290 |
291 | int
292 | tactics(mtmp)
293 | register struct monst *mtmp;
294 | {
295 | long strat = strategy(mtmp);
296 |
297 | mtmp->mstrategy = (mtmp->mstrategy & STRAT_WAITMASK) | strat;
298 |
299 | switch (strat) {
300 | case STRAT_HEAL: /* hide and recover */
301 | /* if wounded, hole up on or near the stairs (to block them) */
302 | /* unless, of course, there are no stairs (e.g. endlevel) */
303 | if (In_W_tower(mtmp->mx, mtmp->my, &u.uz) ||
304 | (mtmp->iswiz && !xupstair && !mon_has_amulet(mtmp))) {
305 | if (!rn2(3 + mtmp->mhp/10)) rloc(mtmp);
306 | } else if (xupstair &&
307 | (mtmp->mx != xupstair || mtmp->my != yupstair)) {
308 | (void) mnearto(mtmp, xupstair, yupstair, TRUE);
309 | }
310 | /* if you're not around, cast healing spells */
311 | if (distu(mtmp->mx,mtmp->my) > (BOLT_LIM * BOLT_LIM))
312 | if(mtmp->mhp <= mtmp->mhpmax - 8) {
313 | mtmp->mhp += rnd(8);
314 | return(1);
315 | }
316 | /* fall through :-) */
317 |
318 | case STRAT_NONE: /* harrass */
319 | if(!rn2(5)) mnexto(mtmp);
320 | return(0);
321 |
322 | default: /* kill, maim, pillage! */
323 | {
324 | long where = (strat & STRAT_STRATMASK);
325 | xchar tx = STRAT_GOALX(strat),
326 | ty = STRAT_GOALY(strat);
327 | int targ = strat & STRAT_GOAL;
328 | struct obj *otmp;
329 |
330 | if(!targ) { /* simply wants you to close */
331 | return(0);
332 | }
333 | if((u.ux == tx && u.uy == ty) || where == STRAT_PLAYER) {
334 | /* player is standing on it (or has it) */
335 | mnexto(mtmp);
336 | return(0);
337 | }
338 | if(where == STRAT_GROUND) {
339 | if(!MON_AT(tx, ty) || (mtmp->mx == tx && mtmp->my == ty)) {
340 | /* teleport to it and pick it up */
341 | rloc_to(mtmp, tx, ty); /* clean old pos */
342 |
343 | if ((otmp = on_ground(which_arti(targ))) != 0) {
344 | if (cansee(mtmp->mx, mtmp->my))
345 | pline("%s picks up %s.",
346 | Monnam(mtmp),
347 | (distu(mtmp->my, mtmp->my) <= 5) ?
348 | doname(otmp) : distant_name(otmp, doname));
349 | obj_extract_self(otmp);
350 | (void) mpickobj(mtmp, otmp);
351 | return(1);
352 | } else return(0);
353 | }
354 | } else { /* a monster has it - 'port beside it. */
355 | (void) mnearto(mtmp, tx, ty, TRUE);
356 | return(0);
357 | }
358 | }
359 | }
360 | /* NOTREACHED */
361 | return(0);
362 | }
363 |
364 | void
365 | aggravate()
366 | {
367 | register struct monst *mtmp;
368 |
369 | for(mtmp = fmon; mtmp; mtmp = mtmp->nmon)
370 | if (!DEADMONSTER(mtmp)) {
371 | mtmp->msleeping = 0;
372 | if(!mtmp->mcanmove && !rn2(5)) {
373 | mtmp->mfrozen = 0;
374 | mtmp->mcanmove = 1;
375 | }
376 | }
377 | }
378 |
379 | void
380 | clonewiz()
381 | {
382 | register struct monst *mtmp2;
383 |
384 | if ((mtmp2 = makemon(&mons[PM_WIZARD_OF_YENDOR],
385 | u.ux, u.uy, NO_MM_FLAGS)) != 0) {
386 | mtmp2->msleeping = mtmp2->mtame = mtmp2->mpeaceful = 0;
387 | if (!u.uhave.amulet && rn2(2)) { /* give clone a fake */
388 | (void) add_to_minv(mtmp2, mksobj(FAKE_AMULET_OF_YENDOR,
389 | TRUE, FALSE));
390 | }
391 | mtmp2->m_ap_type = M_AP_MONSTER;
392 | mtmp2->mappearance = wizapp[rn2(SIZE(wizapp))];
393 | newsym(mtmp2->mx,mtmp2->my);
394 | }
395 | }
396 |
397 | /* also used by newcham() */
398 | int
399 | pick_nasty()
400 | {
401 | /* To do? Possibly should filter for appropriate forms when
402 | in the elemental planes or surrounded by water or lava. */
403 | return nasties[rn2(SIZE(nasties))];
404 | }
405 |
406 | /* create some nasty monsters, aligned or neutral with the caster */
407 | /* a null caster defaults to a chaotic caster (e.g. the wizard) */
408 | void
409 | nasty(mcast)
410 | struct monst *mcast;
411 | {
412 | register struct monst *mtmp;
413 | register int i, j, tmp;
414 | int castalign = (mcast ? mcast->data->maligntyp : -1);
415 |
416 | if(!rn2(10) && Inhell) msummon(&mons[PM_WIZARD_OF_YENDOR]);
417 | else {
418 | tmp = (u.ulevel > 3) ? u.ulevel/3 : 1; /* just in case -- rph */
419 |
420 | for(i = rnd(tmp); i > 0; --i)
421 | for(j=0; j<20; j++) {
422 | if ((mtmp = makemon(&mons[pick_nasty()],
423 | u.ux, u.uy, NO_MM_FLAGS)) != 0) {
424 | mtmp->msleeping = mtmp->mpeaceful = mtmp->mtame = 0;
425 | set_malign(mtmp);
426 | } else /* GENOD? */
427 | mtmp = makemon((struct permonst *)0,
428 | u.ux, u.uy, NO_MM_FLAGS);
429 | if(mtmp && (mtmp->data->maligntyp == 0 ||
430 | sgn(mtmp->data->maligntyp) == sgn(castalign)) )
431 | break;
432 | }
433 | }
434 | return;
435 | }
436 |
437 | /* Let's resurrect the wizard, for some unexpected fun. */
438 | void
439 | resurrect()
440 | {
441 | struct monst *mtmp, **mmtmp;
442 | long elapsed;
443 | const char *verb;
444 |
445 | if (!flags.no_of_wizards) {
446 | /* make a new Wizard */
447 | verb = "kill";
448 | mtmp = makemon(&mons[PM_WIZARD_OF_YENDOR], u.ux, u.uy, MM_NOWAIT);
449 | } else {
450 | /* look for a migrating Wizard */
451 | verb = "elude";
452 | mmtmp = &migrating_mons;
453 | while ((mtmp = *mmtmp) != 0) {
454 | if (mtmp->iswiz &&
455 | /* if he has the Amulet, he won't bring it to you */
456 | !mon_has_amulet(mtmp) &&
457 | (elapsed = monstermoves - mtmp->mlstmv) > 0L) {
458 | mon_catchup_elapsed_time(mtmp, elapsed);
459 | if (elapsed >= LARGEST_INT) elapsed = LARGEST_INT - 1;
460 | elapsed /= 50L;
461 | if (mtmp->msleeping && rn2((int)elapsed + 1))
462 | mtmp->msleeping = 0;
463 | if (mtmp->mfrozen == 1) /* would unfreeze on next move */
464 | mtmp->mfrozen = 0, mtmp->mcanmove = 1;
465 | if (mtmp->mcanmove && !mtmp->msleeping) {
466 | *mmtmp = mtmp->nmon;
467 | mon_arrive(mtmp, TRUE);
468 | /* note: there might be a second Wizard; if so,
469 | he'll have to wait til the next resurrection */
470 | break;
471 | }
472 | }
473 | mmtmp = &mtmp->nmon;
474 | }
475 | }
476 |
477 | if (mtmp) {
478 | mtmp->msleeping = mtmp->mtame = mtmp->mpeaceful = 0;
479 | set_malign(mtmp);
480 | pline("A voice booms out...");
481 | verbalize("So thou thought thou couldst %s me, fool.", verb);
482 | }
483 |
484 | }
485 |
486 | /* Here, we make trouble for the poor shmuck who actually */
487 | /* managed to do in the Wizard. */
488 | void
489 | intervene()
490 | {
491 | int which = Is_astralevel(&u.uz) ? rnd(4) : rn2(6);
492 | /* cases 0 and 5 don't apply on the Astral level */
493 | switch (which) {
494 | case 0:
495 | case 1: You_feel("vaguely nervous.");
496 | break;
497 | case 2: if (!Blind)
498 | You("notice a %s glow surrounding you.",
499 | hcolor(Black));
500 | rndcurse();
501 | break;
502 | case 3: aggravate();
503 | break;
504 | case 4: nasty((struct monst *)0);
505 | break;
506 | case 5: resurrect();
507 | break;
508 | }
509 | }
510 |
511 | void
512 | wizdead()
513 | {
514 | flags.no_of_wizards--;
515 | if (!u.uevent.udemigod) {
516 | u.uevent.udemigod = TRUE;
517 | u.udg_cnt = rn1(250, 50);
518 | }
519 | }
520 |
521 | const char *random_insult[] = {
522 | "antic",
523 | "blackguard",
524 | "caitiff",
525 | "chucklehead",
526 | "coistrel",
527 | "craven",
528 | "cretin",
529 | "cur",
530 | "dastard",
531 | "demon fodder",
532 | "dimwit",
533 | "dolt",
534 | "fool",
535 | "footpad",
536 | "imbecile",
537 | "knave",
538 | "maledict",
539 | "miscreant",
540 | "niddering",
541 | "poltroon",
542 | "rattlepate",
543 | "reprobate",
544 | "scapegrace",
545 | "varlet",
546 | "villein", /* (sic.) */
547 | "wittol",
548 | "worm",
549 | "wretch",
550 | };
551 |
552 | const char *random_malediction[] = {
553 | "Hell shall soon claim thy remains,",
554 | "I chortle at thee, thou pathetic",
555 | "Prepare to die, thou",
556 | "Resistance is useless,",
557 | "Surrender or die, thou",
558 | "There shall be no mercy, thou",
559 | "Thou shalt repent of thy cunning,",
560 | "Thou art as a flea to me,",
561 | "Thou art doomed,",
562 | "Thy fate is sealed,",
563 | "Verily, thou shalt be one dead"
564 | };
565 |
566 | /* Insult or intimidate the player */
567 | void
568 | cuss(mtmp)
569 | register struct monst *mtmp;
570 | {
571 | if (mtmp->iswiz) {
572 | if (!rn2(5)) /* typical bad guy action */
573 | pline("%s laughs fiendishly.", Monnam(mtmp));
574 | else
575 | if (u.uhave.amulet && !rn2(SIZE(random_insult)))
576 | verbalize("Relinquish the amulet, %s!",
577 | random_insult[rn2(SIZE(random_insult))]);
578 | else if (u.uhp < 5 && !rn2(2)) /* Panic */
579 | verbalize(rn2(2) ?
580 | "Even now thy life force ebbs, %s!" :
581 | "Savor thy breath, %s, it be thy last!",
582 | random_insult[rn2(SIZE(random_insult))]);
583 | else if (mtmp->mhp < 5 && !rn2(2)) /* Parthian shot */
584 | verbalize(rn2(2) ?
585 | "I shall return." :
586 | "I'll be back.");
587 | else
588 | verbalize("%s %s!",
589 | random_malediction[rn2(SIZE(random_malediction))],
590 | random_insult[rn2(SIZE(random_insult))]);
591 | } else if(is_lminion(mtmp->data)) {
592 | com_pager(rn2(QTN_ANGELIC - 1 + (Hallucination ? 1 : 0)) +
593 | QT_ANGELIC);
594 | } else {
595 | if (!rn2(5))
596 | pline("%s casts aspersions on your ancestry.", Monnam(mtmp));
597 | else
598 | com_pager(rn2(QTN_DEMONIC) + QT_DEMONIC);
599 | }
600 | }
601 |
602 | #endif /* OVLB */
603 |
604 | /*wizard.c*/