/*	SCCS Id: @(#)music.c	3.1	92/11/26	*/
/* 	Copyright (c) 1989 by Jean-Christophe Collet */
/* NetHack may be freely redistributed.  See license for details. */

/*
 * This file contains the different functions designed to manipulate the
 * musical instruments and their various effects.
 *
 * Actually the list of instruments / effects is :
 *
 * (wooden) flute	may calm snakes if player has enough dexterity
 * magic flute		may put monsters to sleep:  area of effect depends
 *			on player level.
 * (tooled) horn	Will awaken monsters:  area of effect depends on player
 *			level.  May also scare monsters.
 * fire horn		Acts like a wand of fire.
 * frost horn		Acts like a wand of cold.
 * bugle		Will awaken soldiers (if any):  area of effect depends
 *			on player level.
 * (wooden) harp	May calm nymph if player has enough dexterity.
 * magic harp		Charm monsters:  area of effect depends on player
 *			level.
 * (leather) drum	Will awaken monsters like the horn.
 * drum of earthquake	Will initiate an earthquake whose intensity depends
 *			on player level.  That is, it creates ramdom pits
 *			called here chasms.
 */

#include "hack.h"

static void FDECL(awaken_monsters,(int));
static void FDECL(put_monsters_to_sleep,(int));
static void FDECL(charm_snakes,(int));
static void FDECL(calm_nymphs,(int));
static void FDECL(charm_monsters,(int));
static void FDECL(do_earthquake,(int));
static int FDECL(do_improvisation,(struct obj *));

/*
 * Wake every monster in range...
 */

static void
awaken_monsters(distance)
int distance;
{
	register struct monst *mtmp = fmon;

	while(mtmp) {
		if (distu(mtmp->mx, mtmp->my) < distance/3) {
			/* May scare some monsters */
			if (!resist(mtmp, SCROLL_CLASS, 0, NOTELL))
			  mtmp->mflee = 1;
		} else if (distu(mtmp->mx, mtmp->my) < distance) {
			mtmp->msleep = 0;
			mtmp->mcanmove = 1;
			mtmp->mfrozen = 0;
		}
		mtmp = mtmp->nmon;
	}
}

/*
 * Make monsters fall asleep.  Note that they may resist the spell.
 */

static void
put_monsters_to_sleep(distance)
int distance;
{
	register struct monst *mtmp = fmon;

	while(mtmp) {
		  if (distu(mtmp->mx, mtmp->my) < distance)
		    if(mtmp->mcanmove && !resist(mtmp, WAND_CLASS, 0, NOTELL))
		      mtmp->mcanmove = mtmp->mfrozen = 0;
		mtmp = mtmp->nmon;
	}
}

/*
 * Charm snakes in range.  Note that the snakes are NOT tamed.
 */

static void
charm_snakes(distance)
int distance;
{
	register struct monst *mtmp = fmon;

	while (mtmp) {
		if (mtmp->data->mlet == S_SNAKE &&
		    distu(mtmp->mx, mtmp->my) < distance) {
			mtmp->mpeaceful = 1;
			if (cansee(mtmp->mx, mtmp->my))
				pline(
 "%s freezes and sways with the music, then seems quieter.", Monnam(mtmp));
		}
		mtmp = mtmp->nmon;
	}
}

/*
 * Calm nymphs in range.
 */

static void
calm_nymphs(distance)
int distance;
{
	register struct monst *mtmp = fmon;

	while (mtmp) {
		if (mtmp->data->mlet == S_NYMPH &&
		    distu(mtmp->mx, mtmp->my) < distance) {
			mtmp->mpeaceful = 1;
			if (cansee(mtmp->mx, mtmp->my))
				pline(
 "%s listens cheerfully to the music, then seems quieter.", Monnam(mtmp));
		}
		mtmp = mtmp->nmon;
	}
}

/* Awake only soldiers of the level. */

void
awaken_soldiers() {
#ifdef ARMY
	register struct monst *mtmp = fmon;

	while(mtmp) {
	    if (is_mercenary(mtmp->data) && mtmp->data != &mons[PM_GUARD]) {
		mtmp->mpeaceful = mtmp->msleep = 0;
		mtmp->mcanmove = 1;
		if (canseemon(mtmp))
		    pline("%s is now ready for battle!", Monnam(mtmp));
		else
		    Norep("You hear the sound of battle gear being readied.");
	    }
	    mtmp = mtmp->nmon;
	}
#endif /* ARMY */
}

/* Charm monsters in range.  Note that they may resist the spell. */

static void
charm_monsters(distance)
int distance;
{
	register struct monst *mtmp = fmon, *mtmp2;

	while(mtmp) {
		mtmp2 = mtmp->nmon;
		if (distu(mtmp->mx, mtmp->my) <= distance)
		    if(!resist(mtmp, SCROLL_CLASS, 0, NOTELL))
			(void) tamedog(mtmp, (struct obj *) 0);
		mtmp = mtmp2;
	}

}

/* Generate earthquake :-) of desired force.
 * That is:  create random chasms (pits).
 */

static void
do_earthquake(force)
int force;
{
	register int x,y;
	struct monst *mtmp;
	struct obj *otmp;
	struct trap *chasm;
	int start_x, start_y, end_x, end_y;

	start_x = u.ux - (force * 2);
	start_y = u.uy - (force * 2);
	end_x = u.ux + (force * 2);
	end_y = u.uy + (force * 2);
	if (start_x < 1) start_x = 1;
	if (start_y < 1) start_y = 1;
	if (end_x >= COLNO) end_x = COLNO - 1;
	if (end_y >= ROWNO) end_y = ROWNO - 1;
	for (x=start_x; x<=end_x; x++) for (y=start_y; y<=end_y; y++) {
	    if (mtmp = m_at(x,y)) {
		if (mtmp->mundetected && is_hider(mtmp->data)) {
		    mtmp->mundetected = 0;
		    if (cansee(x,y))
			pline("%s is shaken loose from the ceiling!",
							    Amonnam(mtmp));
		    else
			You("hear a thumping sound.");
		    if (x==u.ux && y==u.uy)
			You("easily dodge the falling %s.",
							    mon_nam(mtmp));
		    newsym(x,y);
		}
	    }
	    if (!rn2(14 - force)) switch (levl[x][y].typ) {
		  case FOUNTAIN : /* Make the fountain disappear */
			if (cansee(x,y))
				pline("The fountain falls into a chasm.");
			goto do_pit;
#ifdef SINKS
		  case SINK :
			if (cansee(x,y))
				pline("The kitchen sink falls into a chasm.");
			goto do_pit;
#endif
		  case ALTAR :
			if (cansee(x,y))
				pline("The altar falls into a chasm.");
			goto do_pit;
		  case THRONE :
			if (cansee(x,y))
				pline("The throne falls into a chasm.");
			/* Falls into next case */
		  case ROOM :
		  case CORR : /* Try to make a pit */
do_pit:		    chasm = maketrap(x,y,PIT);
		    chasm->tseen = 1;

		    levl[x][y].doormask = 0;

		    mtmp = m_at(x,y);

		    if (otmp = sobj_at(BOULDER, x, y)) {
			if (cansee(x, y))
			   pline("KADOOM! The boulder falls into a chasm%s!",
			      ((x == u.ux) && (y == u.uy)) ? " below you" : "");
			if (mtmp)
				mtmp->mtrapped = 0;
			freeobj(otmp);
			(void) flooreffects(otmp, x, y, "");
			break;
		    }	

		    /* We have to check whether monsters or player
		       falls in a chasm... */

		    if (mtmp) {
			if(!is_flyer(mtmp->data) && !is_clinger(mtmp->data)) {
			    mtmp->mtrapped = 1;
			    if(cansee(x,y))
				pline("%s falls into a chasm!", Monnam(mtmp));
			    else if (flags.soundok && humanoid(mtmp->data))
				You("hear a scream!");
			    if ((mtmp->mhp -= rnd(6)) <= 0) {
				if(!cansee(x,y))
				    pline("It is destroyed!");
				else {
				    You("destroy %s!", mtmp->mtame ?
					x_monnam(mtmp, 0, "poor", 0) :
					mon_nam(mtmp));
				}
				xkilled(mtmp,0);
			    }
			}
		    } else if (x == u.ux && y == u.uy) {
			    if (Levitation
#ifdef POLYSELF
				|| is_flyer(uasmon) || is_clinger(uasmon)
#endif
				) {
				    pline("A chasm opens up under you!");
				    You("don't fall in!");
			    } else {
				    You("fall into a chasm!");
				    u.utrap = rn1(6,2);
				    u.utraptype = TT_PIT;
				    losehp(rnd(6),"fell into a chasm",
					NO_KILLER_PREFIX);
				    selftouch("Falling, you");
			    }
		    } else newsym(x,y);
		    break;
		  case DOOR : /* Make the door collapse */
		    if (levl[x][y].doormask == D_NODOOR) break;
		    if (cansee(x,y))
			pline("The door collapses.");
		    levl[x][y].doormask = D_NODOOR;
		    newsym(x,y);
		    break;
	    }
	}
}

/*
 * The player is trying to extract something from his/her instrument.
 */

static int
do_improvisation(instr)
struct obj *instr;
{
	int damage;

#ifdef MAC
	mac_speaker ( instr , "C" ) ;
#endif

	if (Confusion)
	  pline("What you produce is quite far from music...");
	else
	  You("start playing %s.", the(xname(instr)));
	switch (instr->otyp) {
	      case WOODEN_FLUTE:	/* May charm snakes */
		if (rn2(ACURR(A_DEX)) + u.ulevel > 25)
		  charm_snakes((int)u.ulevel*3);
		exercise(A_DEX, TRUE);
		break;
	      case MAGIC_FLUTE: /* Make monster fall asleep */
		if (instr->spe > 0) {
			instr->spe--;
			You("produce soft music.");
			put_monsters_to_sleep((int)u.ulevel*5);
		}
		exercise(A_DEX, TRUE);
		break;
	      case TOOLED_HORN:	/* Awaken monsters or scare monsters */
		You("produce a frightful, grave sound.");
		awaken_monsters((int)u.ulevel*30);
		exercise(A_WIS, FALSE);
		break;
	      case FROST_HORN:	/* Idem wand of cold */
	      case FIRE_HORN:	/* Idem wand of fire */
		if (instr->spe > 0) {
			instr->spe--;
			if (!getdir(NULL)) {
				if (!Blind)
				    pline("%s glows then fades.",
					  The(xname(instr)));
			} else {
				if (!u.dx && !u.dy && !u.dz) {
					if((damage = zapyourself(instr)))
					  losehp(damage,
		self_pronoun("using a magical horn on %sself", "him"),
					  NO_KILLER_PREFIX);
					makeknown(instr->otyp);
					return(2);
				}
				buzz((instr->otyp == FROST_HORN) ? AD_COLD-1 : AD_FIRE-1, rn1(6,6), u.ux, u.uy, u.dx, u.dy);
				makeknown(instr->otyp);
				return(2);
			}
		}
		break;
	      case BUGLE:	/* Awaken & attract soldiers */
		You("extract a loud noise from %s.", the(xname(instr)));
		awaken_soldiers();
		exercise(A_WIS, FALSE);
		break;
	      case WOODEN_HARP:	/* May calm Nymph */
		if (rn2(ACURR(A_DEX)) + u.ulevel > 25)
		  calm_nymphs((int)u.ulevel*3);
		exercise(A_DEX, TRUE);
		break;
	      case MAGIC_HARP:	/* Charm monsters */
		if (instr->spe > 0) {
			pline("%s produces very attractive music.",
			      The(xname(instr)));
			instr->spe--;
			charm_monsters(((int)u.ulevel - 1) / 3 + 1);
		}
		exercise(A_DEX, TRUE);
		break;
	      case LEATHER_DRUM:	/* Awaken monsters */
		You("beat a deafening row!");
		awaken_monsters((int)u.ulevel * 40);
		exercise(A_WIS, FALSE);
		break;
	      case DRUM_OF_EARTHQUAKE:	/* create several pits */
		if (instr->spe > 0) {
			You("produce a heavy, thunderous rolling!");
			pline("The entire dungeon is shaking around you!");
			instr->spe--;
			do_earthquake(((int)u.ulevel - 1) / 3 + 1);
			makeknown(DRUM_OF_EARTHQUAKE);
		}
		break;
	      default:
		impossible("What a weird instrument (%d)!",instr->otyp);
		break;
	}
	return (2);		/* That takes time */
}

#ifdef SYSV386MUSIC
/*
 * Play audible music on the machine's speaker if appropriate.
 */

static int
atconsole()
{
    /*
     * Kluge alert: This code assumes that your [34]86 has no X terminals
     * attached and that the console tty type is AT386 (this is always true
     * under AT&T UNIX for these boxen). The theory here is that your remote
     * ttys will have terminal type `ansi' or something else other than
     * `AT386' or `xterm'. We'd like to do better than this, but testing
     * to see if we're running on the console physical terminal is quite
     * difficult given the presence of virtual consoles and other modern
     * UNIX impedimenta...
     */
    char	*termtype = getenv("TERM");

     return(!strcmp(termtype, "AT386") || !strcmp(termtype, "xterm"));
}

static void
speaker(instr, buf)
struct obj *instr;
char	*buf;
{
    /*
     * For this to work, you need to have installed the PD speaker-control
     * driver for PC-compatible UNIX boxes that I (eric@snark.uu.net)
     * posted to comp.sources.unix in Feb 1990. A copy may be included
     * with your nethack distribution.
     */
    int	fd;

    if ((fd = open("/dev/speaker", 1)) != -1)
    {
	/* emit a prefix to modify instrumental `timbre' */
	switch (instr->otyp)
	{
	case WOODEN_FLUTE:
	case MAGIC_FLUTE:
	    (void) write(fd, ">ol", 1); /* up one octave & lock */
	    break;
	case TOOLED_HORN:
	case FROST_HORN:
	case FIRE_HORN:
	    (void) write(fd, "<<ol", 2); /* drop two octaves & lock */
	    break;
	case BUGLE:
	    (void) write(fd, "ol", 2); /* octave lock */
	    break;
	case WOODEN_HARP:
	case MAGIC_HARP:
	    (void) write(fd, "l8mlol", 4); /* fast, legato, octave lock */
	    break;
	}
	(void) write(fd, buf, strlen(buf));
	(void) close(fd);
    }
}
#endif /* SYSV386MUSIC */

/*
 * So you want music...
 */

int
do_play_instrument(instr)
struct obj *instr;
{
    char buf[BUFSZ], *s, c = 'y';
    int x,y;
    boolean ok;

    if (Underwater) {
	You("can't play music underwater!");
	return(0);
    }
    if (instr->otyp != LEATHER_DRUM && instr->otyp != DRUM_OF_EARTHQUAKE) {
	c = yn("Improvise?");
    }
    if (c == 'n') {
	getlin("What tune are you playing? [what 5 notes]", buf);
	for (s=buf; *s; s++) *s = highc(*s);
	You("extract a strange sound from %s!", the(xname(instr)));
#ifdef SYSV386MUSIC 
	/* if user is at the console, play through the console speaker */
	if (atconsole())
	    speaker(instr, buf);
#endif /* SYSV386MUSIC */
#ifdef MAC
	mac_speaker ( instr , buf ) ;
#endif
	/* Check if there was the Stronghold drawbridge near
	 * and if the tune conforms to what we're waiting for.
	 */
	if(Is_stronghold(&u.uz)) {
	    exercise(A_WIS, TRUE);		/* just for trying */
	    if(!strcmp(buf,tune)) {
		/* Search for the drawbridge */
		for(y=u.uy-1; y<=u.uy+1; y++)
		    for(x=u.ux-1;x<=u.ux+1;x++)
			if(isok(x,y))
			if(find_drawbridge(&x,&y)) {
			    if(levl[x][y].typ == DRAWBRIDGE_DOWN)
				close_drawbridge(x,y);
			    else
				open_drawbridge(x,y);
			    return 0;
			}
	    } else if(flags.soundok) {
		if (u.uevent.uheard_tune < 1) u.uevent.uheard_tune = 1;
		/* Okay, it wasn't the right tune, but perhaps
		 * we can give the player some hints like in the
		 * Mastermind game */
		ok = FALSE;
		for(y = u.uy-1; y <= u.uy+1 && !ok; y++)
		    for(x = u.ux-1; x <= u.ux+1 && !ok; x++)
			if(isok(x,y))
			if(IS_DRAWBRIDGE(levl[x][y].typ) ||
			   is_drawbridge_wall(x,y) >= 0)
				ok = TRUE;
		if(ok) { /* There is a drawbridge near */
		    int tumblers, gears;
		    boolean matched[5];

		    tumblers = gears = 0;
		    for(x=0; x < 5; x++)
			matched[x] = FALSE;

		    for(x=0; x < (int)strlen(buf); x++)
			if(x < 5) {
			    if(buf[x] == tune[x]) {
				gears++;
				matched[x] = TRUE;
			    } else
				for(y=0; y < 5; y++)
				    if(!matched[y] &&
				       buf[x] == tune[y] &&
				       buf[y] != tune[y]) {
					tumblers++;
					matched[y] = TRUE;
					break;
				    }
			}
		    if(tumblers)
			if(gears)
			    You("hear %d tumbler%s click and %d gear%s turn.",
				tumblers, plur(tumblers), gears, plur(gears));
			else
			    You("hear %d tumbler%s click.",
				tumblers, plur(tumblers));
		    else if(gears) {
			You("hear %d gear%s turn.", gears, plur(gears));
			if (gears == 5) u.uevent.uheard_tune = 2;
		    }
		}
	    }
	  }
	return 1;
    } else
	    return do_improvisation(instr);
}

/*music.c*/
