1 /* NetHack 3.7	music.c	$NHDT-Date: 1596498191 2020/08/03 23:43:11 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.69 $ */
2 /*      Copyright (c) 1989 by Jean-Christophe Collet */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /*
6  * This file contains the different functions designed to manipulate the
7  * musical instruments and their various effects.
8  *
9  * The list of instruments / effects is :
10  *
11  * (wooden) flute       may calm snakes if player has enough dexterity
12  * magic flute          may put monsters to sleep:  area of effect depends
13  *                      on player level.
14  * (tooled) horn        Will awaken monsters:  area of effect depends on
15  *                      player level.  May also scare monsters.
16  * fire horn            Acts like a wand of fire.
17  * frost horn           Acts like a wand of cold.
18  * bugle                Will awaken soldiers (if any):  area of effect depends
19  *                      on player level.
20  * (wooden) harp        May calm nymph if player has enough dexterity.
21  * magic harp           Charm monsters:  area of effect depends on player
22  *                      level.
23  * (leather) drum       Will awaken monsters like the horn.
24  * drum of earthquake   Will initiate an earthquake whose intensity depends
25  *                      on player level.  That is, it creates random pits
26  *                      called here chasms.
27  */
28 
29 #include "hack.h"
30 
31 static void awaken_monsters(int);
32 static void charm_snakes(int);
33 static void calm_nymphs(int);
34 static void charm_monsters(int);
35 static void do_earthquake(int);
36 static const char *generic_lvl_desc(void);
37 static int do_improvisation(struct obj *);
38 
39 /*
40  * Wake every monster in range...
41  */
42 
43 static void
awaken_monsters(int distance)44 awaken_monsters(int distance)
45 {
46     register struct monst *mtmp;
47     register int distm;
48 
49     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
50         if (DEADMONSTER(mtmp))
51             continue;
52         if ((distm = distu(mtmp->mx, mtmp->my)) < distance) {
53             wakeup(mtmp, FALSE, FALSE);
54             mtmp->mcanmove = 1;
55             mtmp->mfrozen = 0;
56             /* may scare some monsters -- waiting monsters excluded */
57             if (!unique_corpstat(mtmp->data)
58                 && (mtmp->mstrategy & STRAT_WAITMASK) != 0)
59                 mtmp->mstrategy &= ~STRAT_WAITMASK;
60             else if (distm < distance / 3
61                      && !resist(mtmp, TOOL_CLASS, 0, NOTELL)
62                      /* some monsters are immune */
63                      && onscary(0, 0, mtmp))
64                 monflee(mtmp, 0, FALSE, TRUE);
65         }
66     }
67 }
68 
69 /*
70  * Make monsters fall asleep.  Note that they may resist the spell.
71  */
72 
73 void
put_monsters_to_sleep(struct monst * caster,int distance)74 put_monsters_to_sleep(struct monst *caster, int distance)
75 {
76     register struct monst *mtmp;
77 
78     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
79         if (DEADMONSTER(mtmp))
80             continue;
81         if (mtmp == caster) /* immune to own effects */
82             continue;
83         xchar cx = (caster == &g.youmonst) ? u.ux : caster->mx;
84         xchar cy = (caster == &g.youmonst) ? u.uy : caster->my;
85 
86         if (dist2(cx, cy, mtmp->mx, mtmp->my) < distance
87             && sleep_monst(mtmp, d(10, 10), TOOL_CLASS)) {
88             mtmp->msleeping = 1; /* 10d10 turns + wake_nearby to rouse */
89             slept_monst(mtmp);
90         }
91     }
92 }
93 
94 /*
95  * Charm snakes in range.  Note that the snakes are NOT tamed.
96  */
97 
98 static void
charm_snakes(int distance)99 charm_snakes(int distance)
100 {
101     register struct monst *mtmp;
102     int could_see_mon, was_peaceful;
103 
104     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
105         if (DEADMONSTER(mtmp))
106             continue;
107         if (mtmp->data->mlet == S_SNAKE && mtmp->mcanmove
108             && distu(mtmp->mx, mtmp->my) < distance) {
109             was_peaceful = mtmp->mpeaceful;
110             mtmp->mpeaceful = 1;
111             mtmp->mavenge = 0;
112             mtmp->mstrategy &= ~STRAT_WAITMASK;
113             could_see_mon = canseemon(mtmp);
114             mtmp->mundetected = 0;
115             newsym(mtmp->mx, mtmp->my);
116             if (canseemon(mtmp)) {
117                 if (!could_see_mon)
118                     You("notice %s, swaying with the music.", a_monnam(mtmp));
119                 else
120                     pline("%s freezes, then sways with the music%s.",
121                           Monnam(mtmp),
122                           was_peaceful ? "" : ", and now seems quieter");
123             }
124         }
125     }
126 }
127 
128 /*
129  * Calm nymphs in range.
130  */
131 
132 static void
calm_nymphs(int distance)133 calm_nymphs(int distance)
134 {
135     register struct monst *mtmp;
136 
137     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
138         if (DEADMONSTER(mtmp))
139             continue;
140         if (mtmp->data->mlet == S_NYMPH && mtmp->mcanmove
141             && distu(mtmp->mx, mtmp->my) < distance) {
142             wakeup(mtmp, FALSE, TRUE);
143             mtmp->mpeaceful = 1;
144             mtmp->mavenge = 0;
145             mtmp->mstrategy &= ~STRAT_WAITMASK;
146             if (canseemon(mtmp))
147                 pline(
148                     "%s listens cheerfully to the music, then seems quieter.",
149                       Monnam(mtmp));
150         }
151     }
152 }
153 
154 /* Awake soldiers anywhere the level (and any nearby monster). */
155 void
awaken_soldiers(struct monst * bugler)156 awaken_soldiers(struct monst* bugler  /* monster that played instrument */)
157 {
158     register struct monst *mtmp;
159     int distance, distm;
160 
161     /* distance of affected non-soldier monsters to bugler */
162     distance = ((bugler == &g.youmonst) ? u.ulevel : bugler->data->mlevel) * 30;
163 
164     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
165         if (DEADMONSTER(mtmp))
166             continue;
167         if (is_mercenary(mtmp->data) && mtmp->data != &mons[PM_GUARD]) {
168             mtmp->mpeaceful = mtmp->msleeping = mtmp->mfrozen = 0;
169             mtmp->mcanmove = 1;
170             mtmp->mstrategy &= ~STRAT_WAITMASK;
171             if (canseemon(mtmp))
172                 pline("%s is now ready for battle!", Monnam(mtmp));
173             else if (!Deaf)
174                 Norep("%s the rattle of battle gear being readied.",
175                       "You hear");  /* Deaf-aware */
176         } else if ((distm = ((bugler == &g.youmonst)
177                                  ? distu(mtmp->mx, mtmp->my)
178                                  : dist2(bugler->mx, bugler->my, mtmp->mx,
179                                          mtmp->my))) < distance) {
180             wakeup(mtmp, FALSE, FALSE);
181             mtmp->mcanmove = 1;
182             mtmp->mfrozen = 0;
183             /* may scare some monsters -- waiting monsters excluded */
184             if (!unique_corpstat(mtmp->data)
185                 && (mtmp->mstrategy & STRAT_WAITMASK) != 0)
186                 mtmp->mstrategy &= ~STRAT_WAITMASK;
187             else if (distm < distance / 3
188                      && !resist(mtmp, TOOL_CLASS, 0, NOTELL))
189                 monflee(mtmp, 0, FALSE, TRUE);
190         }
191     }
192 }
193 
194 /* Charm monsters in range.  Note that they may resist the spell.
195  * If swallowed, range is reduced to 0.
196  */
197 static void
charm_monsters(int distance)198 charm_monsters(int distance)
199 {
200     struct monst *mtmp, *mtmp2;
201 
202     if (u.uswallow) {
203         if (!resist(u.ustuck, TOOL_CLASS, 0, NOTELL))
204             (void) tamedog(u.ustuck, (struct obj *) 0, FALSE);
205     } else {
206         for (mtmp = fmon; mtmp; mtmp = mtmp2) {
207             mtmp2 = mtmp->nmon;
208             if (DEADMONSTER(mtmp))
209                 continue;
210 
211             if (distu(mtmp->mx, mtmp->my) <= distance) {
212                 if (!resist(mtmp, TOOL_CLASS, 0, NOTELL))
213                     (void) tamedog(mtmp, (struct obj *) 0, FALSE);
214             }
215         }
216     }
217 }
218 
219 /* Generate earthquake :-) of desired force.
220  * That is:  create random chasms (pits).
221  */
222 static void
do_earthquake(int force)223 do_earthquake(int force)
224 {
225     static const char into_a_chasm[] = " into a chasm";
226     register int x, y;
227     struct monst *mtmp;
228     struct obj *otmp;
229     struct trap *chasm, *trap_at_u = t_at(u.ux, u.uy);
230     int start_x, start_y, end_x, end_y, amsk;
231     aligntyp algn;
232     schar filltype;
233     unsigned tu_pit = 0;
234 
235     if (trap_at_u)
236         tu_pit = is_pit(trap_at_u->ttyp);
237     if (force > 13) /* sanity precaution; maximum used is actually 10 */
238         force = 13;
239     start_x = u.ux - (force * 2);
240     start_y = u.uy - (force * 2);
241     end_x = u.ux + (force * 2);
242     end_y = u.uy + (force * 2);
243     start_x = max(start_x, 1);
244     start_y = max(start_y, 0);
245     end_x = min(end_x, COLNO - 1);
246     end_y = min(end_y, ROWNO - 1);
247     for (x = start_x; x <= end_x; x++)
248         for (y = start_y; y <= end_y; y++) {
249             if ((mtmp = m_at(x, y)) != 0) {
250                 /* peaceful monster will become hostile */
251                 wakeup(mtmp, TRUE, TRUE);
252                 if (mtmp->mundetected) {
253                     mtmp->mundetected = 0;
254                     newsym(x, y);
255                     if (ceiling_hider(mtmp->data)) {
256                         if (cansee(x, y))
257                             pline("%s is shaken loose from the ceiling!",
258                                   Amonnam(mtmp));
259                         else if (!is_flyer(mtmp->data))
260                             You_hear("a thump.");
261                     }
262                 }
263                 if (M_AP_TYPE(mtmp) != M_AP_NOTHING
264                     && M_AP_TYPE(mtmp) != M_AP_MONSTER)
265                     seemimic(mtmp);
266             }
267             if (rn2(14 - force))
268                 continue;
269 
270        /*
271         * Possible extensions:
272         *  When a door is trapped, explode it instead of silently
273         *   turning it into an empty doorway.
274         *  Trigger divine wrath when an altar is dumped into a chasm.
275         *  Sometimes replace sink with fountain or fountain with pool
276         *   instead of always producing a pit.
277         *  Sometimes release monster and/or treasure from a grave or
278         *   a throne instead of just dumping them into the chasm.
279         *  Chance to destroy wall segments?  Trees too?
280         *  Honor non-diggable for locked doors, walls, and trees.
281         *   Treat non-passwall as if it was non-diggable?
282         *  Conjoin some of the umpteen pits when they're adjacent?
283         *
284         *  Replace 'goto do_pit;' with 'do_pit = TRUE; break;' and
285         *   move the pit code to after the switch.
286         */
287 
288             switch (levl[x][y].typ) {
289             case FOUNTAIN: /* make the fountain disappear */
290                 if (cansee(x, y))
291                     pline_The("fountain falls%s.", into_a_chasm);
292                 goto do_pit;
293             case SINK:
294                 if (cansee(x, y))
295                     pline_The("kitchen sink falls%s.", into_a_chasm);
296                 goto do_pit;
297             case ALTAR:
298                 /* always preserve the high altars */
299                 if (Is_astralevel(&u.uz) || Is_sanctum(&u.uz))
300                     break;
301                 /* no need to check for high altar here; we've just
302                    excluded those */
303                 amsk = altarmask_at(x, y);
304                 algn = Amask2align(amsk & AM_MASK);
305                 if (cansee(x, y))
306                     pline_The("%s altar falls%s.",
307                               align_str(algn), into_a_chasm);
308                 goto do_pit;
309             case GRAVE:
310                 if (cansee(x, y))
311                     pline_The("headstone topples%s.", into_a_chasm);
312                 goto do_pit;
313             case THRONE:
314                 if (cansee(x, y))
315                     pline_The("throne falls%s.", into_a_chasm);
316                 goto do_pit;
317             case SCORR:
318                 levl[x][y].typ = CORR;
319                 unblock_point(x, y);
320                 if (cansee(x, y))
321                     pline("A secret corridor is revealed.");
322                 /*FALLTHRU*/
323             case CORR:
324             case ROOM: /* Try to make a pit. */
325  do_pit:
326                 /* maketrap() won't replace furniture with a trap,
327                    so remove the furniture first */
328                 if (levl[x][y].typ != CORR) {
329                     if (levl[x][y].typ != DOOR) {
330                         levl[x][y].typ = ROOM;
331                         /* clear blessed fountain, disturbed grave */
332                         levl[x][y].horizontal = 0;
333                     }
334                     /* clear doormask, altarmask, looted throne */
335                     levl[x][y].flags = 0; /* same as 'doormask = D_NODOOR' */
336                 }
337                 chasm = maketrap(x, y, PIT);
338                 if (!chasm)
339                     break; /* no pit if portal at that location */
340                 chasm->tseen = 1;
341 
342                 /* Let liquid flow into the newly created chasm.
343                    Adjust corresponding code in apply.c for exploding
344                    wand of digging if you alter this sequence. */
345                 filltype = fillholetyp(x, y, FALSE);
346                 if (filltype != ROOM) {
347                     levl[x][y].typ = filltype; /* flags set via doormask */
348                     liquid_flow(x, y, filltype, chasm, (char *) 0);
349                 }
350 
351                 mtmp = m_at(x, y); /* (redundant?) */
352                 if ((otmp = sobj_at(BOULDER, x, y)) != 0) {
353                     if (cansee(x, y))
354                         pline("KADOOM!  The boulder falls into a chasm%s!",
355                               (x == u.ux && y == u.uy) ? " below you" : "");
356                     if (mtmp)
357                         mtmp->mtrapped = 0;
358                     obj_extract_self(otmp);
359                     (void) flooreffects(otmp, x, y, "");
360                     break; /* from switch, not loop */
361                 }
362 
363                 /* We have to check whether monsters or player
364                    falls in a chasm... */
365                 if (mtmp) {
366                     if (grounded(mtmp->data)) {
367                         boolean m_already_trapped = mtmp->mtrapped;
368 
369                         mtmp->mtrapped = 1;
370                         if (!m_already_trapped) { /* suppress messages */
371                             if (cansee(x, y))
372                                 pline("%s falls into a chasm!", Monnam(mtmp));
373                             else if (humanoid(mtmp->data))
374                                 You_hear("a scream!");
375                         }
376                         /* Falling is okay for falling down
377                            within a pit from jostling too */
378                         mselftouch(mtmp, "Falling, ", TRUE);
379                         if (!DEADMONSTER(mtmp)) {
380                             mtmp->mhp -= rnd(m_already_trapped ? 4 : 6);
381                             if (DEADMONSTER(mtmp)) {
382                                 if (!cansee(x, y)) {
383                                     pline("It is destroyed!");
384                                 } else {
385                                     You("destroy %s!",
386                                         mtmp->mtame
387                                          ? x_monnam(mtmp, ARTICLE_THE, "poor",
388                                                     has_mgivenname(mtmp)
389                                                      ? SUPPRESS_SADDLE : 0,
390                                                     FALSE)
391                                          : mon_nam(mtmp));
392                                 }
393                                 xkilled(mtmp, XKILL_NOMSG);
394                             }
395                         }
396                     }
397                 } else if (x == u.ux && y == u.uy) {
398                     if (u.utrap && u.utraptype == TT_BURIEDBALL) {
399                         /* Note:  the chain should break if a pit gets
400                            created at the buried ball's location, which
401                            is not necessarily here.  But if we don't do
402                            things this way, entering the new pit below
403                            will override current trap anyway, but too
404                            late to get Lev and Fly handling. */
405                         Your("chain breaks!");
406                         reset_utrap(TRUE);
407                     }
408                     if (Levitation || Flying || !grounded(g.youmonst.data)) {
409                         if (!tu_pit) { /* no pit here previously */
410                             pline("A chasm opens up under you!");
411                             You("don't fall in!");
412                         }
413                     } else if (!tu_pit || !u.utrap || u.utraptype != TT_PIT) {
414                         /* no pit here previously, or you were
415                            not in it even if there was */
416                         You("fall into a chasm!");
417                         set_utrap(rn1(6, 2), TT_PIT);
418                         losehp(Maybe_Half_Phys(rnd(6)),
419                                "fell into a chasm", NO_KILLER_PREFIX);
420                         selftouch("Falling, you");
421                     } else if (u.utrap && u.utraptype == TT_PIT) {
422                         boolean keepfooting =
423                                 ((Fumbling && !rn2(5))
424                                  || (!rnl(Role_if(PM_ARCHEOLOGIST) ? 3 : 9))
425                                  || ((ACURR(A_DEX) > 7) && rn2(5)));
426 
427                         You("are jostled around violently!");
428                         set_utrap(rn1(6, 2), TT_PIT);
429                         losehp(Maybe_Half_Phys(rnd(keepfooting ? 2 : 4)),
430                                "hurt in a chasm", NO_KILLER_PREFIX);
431                         if (keepfooting)
432                             exercise(A_DEX, TRUE);
433                         else
434                             selftouch((Upolyd && (slithy(g.youmonst.data)
435                                                   || nolimbs(g.youmonst.data)))
436                                       ? "Shaken, you"
437                                       : "Falling down, you");
438                     }
439                 } else {
440                     newsym(x, y);
441                 }
442                 break;
443             case SDOOR:
444                 cvt_sdoor_to_door(&levl[x][y]); /* .typ = DOOR */
445                 if (cansee(x, y))
446                     pline("A secret door is revealed.");
447                 /*FALLTHRU*/
448             case DOOR: /* make the door collapse */
449                 /* if already doorless, treat like room or corridor */
450                 if (doorstate(&levl[x][y]) == D_NODOOR)
451                     goto do_pit;
452                 /* wasn't doorless, now it will be */
453                 set_doorstate(&levl[x][y], D_NODOOR);
454                 unblock_point(x, y);
455                 newsym(x, y); /* before pline */
456                 if (cansee(x, y))
457                     pline_The("door collapses.");
458                 if (*in_rooms(x, y, SHOPBASE))
459                     add_damage(x, y, 0L);
460                 break;
461             }
462         }
463 }
464 
465 static const char *
generic_lvl_desc(void)466 generic_lvl_desc(void)
467 {
468     if (Is_astralevel(&u.uz))
469         return "astral plane";
470     else if (In_endgame(&u.uz))
471         return "plane";
472     else if (Is_sanctum(&u.uz))
473         return "sanctum";
474     else if (In_sokoban(&u.uz))
475         return "puzzle";
476     else if (In_V_tower(&u.uz))
477         return "tower";
478     else
479         return "dungeon";
480 }
481 
482 const char *beats[] = {
483     "stepper", "one drop", "slow two", "triple stroke roll",
484     "double shuffle", "half-time shuffle", "second line", "train"
485 };
486 
487 /*
488  * The player is trying to extract something from his/her instrument.
489  */
490 static int
do_improvisation(struct obj * instr)491 do_improvisation(struct obj* instr)
492 {
493     int damage, mode, do_spec = !(Stunned || Confusion);
494     struct obj itmp;
495     boolean mundane = FALSE;
496 
497     itmp = *instr;
498     itmp.oextra = (struct oextra *) 0; /* ok on this copy as instr maintains
499                                           the ptr to free at some point if
500                                           there is one */
501 
502     /* if won't yield special effect, make sound of mundane counterpart */
503     if (!do_spec || instr->spe <= 0)
504         while (objects[itmp.otyp].oc_magic) {
505             itmp.otyp -= 1;
506             mundane = TRUE;
507         }
508 
509 
510 #define PLAY_NORMAL   0x00
511 #define PLAY_STUNNED  0x01
512 #define PLAY_CONFUSED 0x02
513 #define PLAY_HALLU    0x04
514     mode = PLAY_NORMAL;
515     if (Stunned)
516         mode |= PLAY_STUNNED;
517     if (Confusion)
518         mode |= PLAY_CONFUSED;
519     if (Hallucination)
520         mode |= PLAY_HALLU;
521 
522     if (!rn2(2)) {
523         /*
524          * TEMPORARY?  for multiple impairments, don't always
525          * give the generic "it's far from music" message.
526          */
527         /* remove if STUNNED+CONFUSED ever gets its own message below */
528         if (mode == (PLAY_STUNNED | PLAY_CONFUSED))
529             mode = !rn2(2) ? PLAY_STUNNED : PLAY_CONFUSED;
530         /* likewise for stunned and/or confused combined with hallucination */
531         if (mode & PLAY_HALLU)
532             mode = PLAY_HALLU;
533     }
534 
535     /* 3.6.3: most of these gave "You produce <blah>" and then many of
536        the instrument-specific messages below which immediately follow
537        also gave "You produce <something>."  That looked strange so we
538        now use a different verb here */
539     switch (mode) {
540     case PLAY_NORMAL:
541         You("start playing %s.", yname(instr));
542         break;
543     case PLAY_STUNNED:
544         if (!Deaf)
545             You("radiate an obnoxious droning sound.");
546         else
547             You_feel("a monotonous vibration.");
548         break;
549     case PLAY_CONFUSED:
550         if (!Deaf)
551             You("generate a raucous noise.");
552         else
553             You_feel("a jarring vibration.");
554         break;
555     case PLAY_HALLU:
556         You("disseminate a kaleidoscopic display of floating butterflies.");
557         break;
558     /* TODO? give some or all of these combinations their own feedback;
559        hallucination ones should reference senses other than hearing... */
560     case PLAY_STUNNED | PLAY_CONFUSED:
561     case PLAY_STUNNED | PLAY_HALLU:
562     case PLAY_CONFUSED | PLAY_HALLU:
563     case PLAY_STUNNED | PLAY_CONFUSED | PLAY_HALLU:
564     default:
565         pline("What you perform is quite far from music...");
566         break;
567     }
568 #undef PLAY_NORMAL
569 #undef PLAY_STUNNED
570 #undef PLAY_CONFUSED
571 #undef PLAY_HALLU
572 
573     switch (itmp.otyp) { /* note: itmp.otyp might differ from instr->otyp */
574     case MAGIC_FLUTE: /* Make monster fall asleep */
575         consume_obj_charge(instr, TRUE);
576 
577         You("%sproduce %s music.", !Deaf ? "" : "seem to ",
578             Hallucination ? "elevator" : "soft");
579         put_monsters_to_sleep(&g.youmonst, u.ulevel * 5);
580         exercise(A_DEX, TRUE);
581         break;
582     case FLUTE: /* May charm snakes */
583         do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25);
584         if (!Deaf)
585             pline("%s.", Tobjnam(instr, do_spec ? "trill" : "toot"));
586         else
587             You_feel("%s %s.", yname(instr), do_spec ? "trill" : "toot");
588         if (do_spec)
589             charm_snakes(u.ulevel * 3);
590         exercise(A_DEX, TRUE);
591         break;
592     case FIRE_HORN:  /* Idem wand of fire */
593     case FROST_HORN: /* Idem wand of cold */
594         consume_obj_charge(instr, TRUE);
595 
596         if (!getdir((char *) 0)) {
597             pline("%s.", Tobjnam(instr, "vibrate"));
598             break;
599         } else if (!u.dx && !u.dy && !u.dz) {
600             if ((damage = zapyourself(instr, TRUE)) != 0) {
601                 char buf[BUFSZ];
602 
603                 Sprintf(buf, "using a magical horn on %sself", uhim());
604                 losehp(damage, buf, KILLED_BY); /* fire or frost damage */
605             }
606         } else {
607             buzz((instr->otyp == FROST_HORN) ? AD_COLD - 1 : AD_FIRE - 1,
608                  rn1(6, 6), u.ux, u.uy, u.dx, u.dy);
609         }
610         makeknown(instr->otyp);
611         break;
612     case TOOLED_HORN: /* Awaken or scare monsters */
613         if (!Deaf)
614             You("produce a frightful, grave sound.");
615         else
616             You("blow into the horn.");
617         awaken_monsters(u.ulevel * 30);
618         exercise(A_WIS, FALSE);
619         break;
620     case BUGLE: /* Awaken & attract soldiers */
621         if (!Deaf)
622             You("extract a loud noise from %s.", yname(instr));
623         else
624             You("blow into the bugle.");
625         awaken_soldiers(&g.youmonst);
626         exercise(A_WIS, FALSE);
627         break;
628     case MAGIC_HARP: /* Charm monsters */
629         consume_obj_charge(instr, TRUE);
630 
631         if (!Deaf)
632             pline("%s very attractive music.", Tobjnam(instr, "produce"));
633         else
634             You_feel("very soothing vibrations.");
635         charm_monsters((u.ulevel - 1) / 3 + 1);
636         exercise(A_DEX, TRUE);
637         break;
638     case HARP: /* May calm Nymph */
639         do_spec &= (rn2(ACURR(A_DEX)) + u.ulevel > 25);
640         if (!Deaf)
641             pline("%s %s.", Yname2(instr),
642                   do_spec ? "produces a lilting melody" : "twangs");
643         else
644             You_feel("soothing vibrations.");
645         if (do_spec)
646             calm_nymphs(u.ulevel * 3);
647         exercise(A_DEX, TRUE);
648         break;
649     case DRUM_OF_EARTHQUAKE: /* create several pits */
650         /* a drum of earthquake does not cause deafness
651            while still magically functional, nor afterwards
652            when it invokes the LEATHER_DRUM case instead and
653            mundane is flagged */
654         consume_obj_charge(instr, TRUE);
655 
656         You("produce a heavy, thunderous rolling!");
657         pline_The("entire %s is shaking around you!", generic_lvl_desc());
658         do_earthquake((u.ulevel - 1) / 3 + 1);
659         /* shake up monsters in a much larger radius... */
660         awaken_monsters(ROWNO * COLNO);
661         makeknown(DRUM_OF_EARTHQUAKE);
662         break;
663     case LEATHER_DRUM: /* Awaken monsters */
664         if (!mundane) {
665             if (!Deaf) {
666                 You("beat a deafening row!");
667                 incr_itimeout(&HDeaf, rn1(20, 30));
668             } else {
669                 You("pound on the drum.");
670             }
671             exercise(A_WIS, FALSE);
672         } else
673             You("%s %s.",
674                 rn2(2) ? "butcher" : rn2(2) ? "manage" : "pull off",
675                 an(beats[rn2(SIZE(beats))]));
676         awaken_monsters(u.ulevel * (mundane ? 5 : 40));
677         g.context.botl = TRUE;
678         break;
679     default:
680         impossible("What a weird instrument (%d)!", instr->otyp);
681         return 0;
682     }
683     return 2; /* That takes time */
684 }
685 
686 /*
687  * So you want music...
688  */
689 int
do_play_instrument(struct obj * instr)690 do_play_instrument(struct obj* instr)
691 {
692     char buf[BUFSZ] = DUMMY, c = 'y';
693     char *s;
694     int x, y;
695     boolean ok;
696 
697     if (Underwater) {
698         You_cant("play music underwater!");
699         return 0;
700     } else if ((instr->otyp == FLUTE || instr->otyp == MAGIC_FLUTE
701                 || instr->otyp == TOOLED_HORN || instr->otyp == FROST_HORN
702                 || instr->otyp == FIRE_HORN || instr->otyp == BUGLE)
703                && !can_blow(&g.youmonst)) {
704         You("are incapable of playing %s.", the(distant_name(instr, xname)));
705         return 0;
706     }
707     if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE
708         && !(Stunned || Confusion || Hallucination)) {
709         c = ynq("Improvise?");
710         if (c == 'q')
711             goto nevermind;
712     }
713 
714     if (c == 'n') {
715         if (u.uevent.uheard_tune == 2)
716             c = ynq("Play the passtune?");
717         if (c == 'q') {
718             goto nevermind;
719         } else if (c == 'y') {
720             Strcpy(buf, g.tune);
721         } else {
722             getlin("What tune are you playing? [5 notes, A-G]", buf);
723             (void) mungspaces(buf);
724             if (*buf == '\033')
725                 goto nevermind;
726 
727             /* convert to uppercase and change any "H" to the expected "B" */
728             for (s = buf; *s; s++) {
729                 *s = highc(*s);
730                 if (*s == 'H')
731                     *s = 'B';
732             }
733         }
734 
735         You(!Deaf ? "extract a strange sound from %s!"
736                   : "can feel %s emitting vibrations.", the(xname(instr)));
737 
738 
739         /* Check if there was the Stronghold drawbridge near
740          * and if the tune conforms to what we're waiting for.
741          */
742         if (Is_stronghold(&u.uz)) {
743             exercise(A_WIS, TRUE); /* just for trying */
744             if (!strcmp(buf, g.tune)) {
745                 /* Search for the drawbridge */
746                 for (y = u.uy - 1; y <= u.uy + 1; y++)
747                     for (x = u.ux - 1; x <= u.ux + 1; x++)
748                         if (isok(x, y))
749                             if (find_drawbridge(&x, &y)) {
750                                 /* tune now fully known */
751                                 u.uevent.uheard_tune = 2;
752                                 if (levl[x][y].typ == DRAWBRIDGE_DOWN)
753                                     close_drawbridge(x, y);
754                                 else
755                                     open_drawbridge(x, y);
756                                 return 1;
757                             }
758             } else if (!Deaf) {
759                 if (u.uevent.uheard_tune < 1)
760                     u.uevent.uheard_tune = 1;
761                 /* Okay, it wasn't the right tune, but perhaps
762                  * we can give the player some hints like in the
763                  * Mastermind game */
764                 ok = FALSE;
765                 for (y = u.uy - 1; y <= u.uy + 1 && !ok; y++)
766                     for (x = u.ux - 1; x <= u.ux + 1 && !ok; x++)
767                         if (isok(x, y))
768                             if (IS_DRAWBRIDGE(levl[x][y].typ)
769                                 || is_drawbridge_wall(x, y) >= 0)
770                                 ok = TRUE;
771                 if (ok) { /* There is a drawbridge near */
772                     int tumblers, gears;
773                     boolean matched[5];
774 
775                     tumblers = gears = 0;
776                     for (x = 0; x < 5; x++)
777                         matched[x] = FALSE;
778 
779                     for (x = 0; x < (int) strlen(buf); x++)
780                         if (x < 5) {
781                             if (buf[x] == g.tune[x]) {
782                                 gears++;
783                                 matched[x] = TRUE;
784                             } else {
785                                 for (y = 0; y < 5; y++)
786                                     if (!matched[y] && buf[x] == g.tune[y]
787                                         && buf[y] != g.tune[y]) {
788                                         tumblers++;
789                                         matched[y] = TRUE;
790                                         break;
791                                     }
792                             }
793                         }
794                     if (tumblers) {
795                         if (gears)
796                             You_hear("%d tumbler%s click and %d gear%s turn.",
797                                      tumblers, plur(tumblers), gears,
798                                      plur(gears));
799                         else
800                             You_hear("%d tumbler%s click.", tumblers,
801                                      plur(tumblers));
802                     } else if (gears) {
803                         You_hear("%d gear%s turn.", gears, plur(gears));
804                         /* could only get `gears == 5' by playing five
805                            correct notes followed by excess; otherwise,
806                            tune would have matched above */
807                         if (gears == 5)
808                             u.uevent.uheard_tune = 2;
809                     }
810                 }
811             }
812         }
813         return 1;
814     } else
815         return do_improvisation(instr);
816 
817  nevermind:
818     pline1(Never_mind);
819     return 0;
820 }
821 
822 /*music.c*/
823