/*
    move.c - Hero movement commands
      
    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
    All rights reserved.

    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
    All rights reserved.

    Based on "Rogue: Exploring the Dungeons of Doom"
    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
    All rights reserved.

    See the file LICENSE.TXT for full copyright and licensing information.
*/

#include <stdlib.h>
#include <ctype.h>
#include "rogue.h"

/*
    do_run()
        Start the hero running
*/

void
do_run(char ch)
{
    running = TRUE;
    after = FALSE;
    runch = ch;

    if (doorstop && !on(player, ISBLIND))
    {
        door_stop = TRUE;
        firstmove = TRUE;
    }
}

/*
    step_ok()
        returns true if it is ok for type to step on ch flgptr will be
        NULL if we don't know what the monster is yet!
*/

int
step_ok(int y, int x, int can_on_monst, struct thing *flgptr)
{
    struct linked_list *item;
    char ch;

    /* What is here?  Don't check monster window if MONSTOK is set */

    if (can_on_monst == MONSTOK)
        ch = CCHAR( mvinch(y, x) );
    else
        ch = winat(y, x);

    switch (ch)
    {
        case ' ':
        case '|':
        case '-':
        case SECRETDOOR:
            if (flgptr && on(*flgptr, CANINWALL))
                return(TRUE);

            return(FALSE);

        case SCROLL:
            /*
             * If it is a scroll, it might be a scare monster scroll so
             * we need to look it up to see what type it is.
             */

            if (flgptr && flgptr->t_ctype == C_MONSTER)
            {
                item = find_obj(y, x);

                if (item != NULL && (OBJPTR(item))->o_type == SCROLL
                     && (OBJPTR(item))->o_which == S_SCARE
                     && rnd(flgptr->t_stats.s_intel) < 12)
                return(FALSE); /* All but smart ones are scared */
            }
            return(TRUE);

        default:
            return(!isalpha(ch));
    }
}

/*
    corr_move()
        Check to see that a move is legal.  If so, return correct
        character. If not, if player came from a legal place, then try to turn
        him.
*/

void
corr_move(int dy, int dx)
{
    char    ch;
    short   legal = 0;  /* Number of legal alternatives */
    int     y = 0, x = 0;   /* Holds legal new position */
    int    *ny, *nx;    /* Point to which direction to change */

    /* New position */

    player.t_nxtpos.y = hero.y + dy;
    player.t_nxtpos.x = hero.x + dx;

    /* A bad diagonal move is illegal */

    if (!diag_ok(&hero, &player.t_nxtpos, &player))
        return;

    /* If it is a legal move, just return */

    if (player.t_nxtpos.x >= 0 && player.t_nxtpos.x < COLS && player.t_nxtpos.y > 0 && player.t_nxtpos.y < LINES - 2)
    {
        ch = winat(player.t_nxtpos.y, player.t_nxtpos.x);

        switch (ch)
        {
            case ' ':
            case '|':
            case '-':
                break;
            default:
                return;
        }
    }

    /* Check the legal alternatives */

    if (dy == 0)
    {
        ny = &dy;
        nx = &dx;
    }
    else
    {
        ny = &dx;
        nx = &dy;
    }

    for (*nx = 0, *ny = -1; *ny < 2; *ny += 2)
    {
        /* New position */
        player.t_nxtpos.y = hero.y + dy;
        player.t_nxtpos.x = hero.x + dx;

        if (player.t_nxtpos.x < 0 || player.t_nxtpos.x > COLS - 1 || player.t_nxtpos.y < 1 || player.t_nxtpos.y > LINES - 3)
            continue;

        ch = winat(player.t_nxtpos.y, player.t_nxtpos.x);

        switch (ch)
        {
            case ' ':
            case '|':
            case '-':
                break;
            default:
                legal++;
                y = dy;
                x = dx;
        }
    }

    /* If we have 2 legal moves, make no change */

    if (legal != 1)
        return;

    /* Make the change */

    if (y == 0)         /* Move horizontally */
    {
        if (x == 1)
            runch = 'l';
        else
            runch = 'h';
    }
    else            /* Move vertically */
    {
        if (y == 1)
            runch = 'j';
        else
            runch = 'k';
    }

    return;
}


/*
    do_move()
        Check to see that a move is legal.  If it is handle the
        consequences (fighting, picking up, etc.)
*/

void
do_move(int dy, int dx)
{
    char    ch;
    coord   old_hero;
    char    hch;

    firstmove = FALSE;

    if (player.t_no_move)
    {
        player.t_no_move--;
        msg("You are still stuck in the bear trap.");
        return;
    }

    /* Do a confused move (maybe) */

    if ((rnd(100) < 80 && on(player, ISHUH)) ||
        (is_wearing(R_DELUSION) && rnd(100) < 25) ||
        on(player, STUMBLER) && rnd(40) == 0)
        player.t_nxtpos = rndmove(&player);
    else
    {
        player.t_nxtpos.y = hero.y + dy;
        player.t_nxtpos.x = hero.x + dx;
    }

    /*
     * Check if he tried to move off the screen or make an illegal
     * diagonal move, and stop him if he did.
     */

    if (player.t_nxtpos.x < 0 || player.t_nxtpos.x > COLS - 1 || player.t_nxtpos.y < 1 || player.t_nxtpos.y >= LINES - 2
        || !diag_ok(&hero, &player.t_nxtpos, &player))
    {
        after = fighting = running = FALSE;
        return;
    }

    if (running && ce(hero, player.t_nxtpos))
        after = running = FALSE;

    ch = winat(player.t_nxtpos.y, player.t_nxtpos.x);

	if (isalpha(ch))
	    debug("Moving onto monster %c",ch);
    
	/* Take care of hero trying to move close to something frightening */
    
	if (on(player, ISFLEE))
    {
        if (rnd(10) < 1)
        {
            turn_off(player, ISFLEE);
            msg("You regain your composure.");
        }
        else if (DISTANCE(player.t_nxtpos, player.t_chasee->t_pos) <
                 DISTANCE(hero,player.t_chasee->t_pos))
            return;
    }

    /* Take care of hero being held */

    if (on(player, ISHELD) && !isalpha(ch))
    {
        if (rnd(pstats.s_str) > 14)
        {
            msg("You break free of the hold.");

            if (--hold_count == 0)
                turn_off(player, ISHELD);
        }
        else
        {
            msg("You are being held.");
            return;
        }
    }
 
    /* Might lose disguise */

    if (on(player, ISDISGUISE) && rnd(11 * pstats.s_dext) == 0)
    {
        extinguish_fuse(FUSE_UNDISGUISE);
        undisguise(NULL);
    }

    /* assume he's not in a wall */

    if (!isalpha(ch))
        turn_off(player, ISINWALL);

    hch = CCHAR( mvinch(hero.y, hero.x) );   /* Where hero was */
    old_hero = hero;    /* Save hero's old position */

    switch (ch)
    {
        case ' ':
        case '|':
        case '-':
        case SECRETDOOR:
            if (off(player, CANINWALL))
            {
                after = running = FALSE;
                return;
            }
            else if (running)
            {
                after = running = FALSE;
                return;
            }
            turn_on(player, ISINWALL);
            break;

        case TRAPDOOR:
        case TELTRAP:
        case BEARTRAP:
        case SLEEPTRAP:
        case ARROWTRAP:
        case DARTTRAP:
        case POOL:
        case MAZETRAP:
        case FIRETRAP:
        case POISONTRAP:
        case LAIR:
        case RUSTTRAP:
            ch = be_trapped(&player, player.t_nxtpos);

            if (!is_wearing(R_LEVITATION) && off(player, CANFLY) &&
                (old_hero.x != hero.x || old_hero.y != hero.y
                 || pool_teleport))
            {
                pool_teleport = FALSE;
                return;
            }

            break;

        case GOLD:
        case POTION:
        case SCROLL:
        case FOOD:
        case WEAPON:
        case ARMOR:
        case RING:
        case ARTIFACT:
        case STICK:
            running = FALSE;
            take = ch;
            break;

        default:
            break;
    }

    if (ch == FIRETRAP)
        light(&hero);

    hero = player.t_nxtpos;      /* Move the hero */
	
	/* adjust lighting */

    if (roomin(hero) == NULL && (hch == '-' || hch == '|' ||
        hch == DOOR || hch == SECRETDOOR))
    {
        /* Leaving a room -- darken it */
        struct room *rp = roomin(old_hero);
        int    is_lit = FALSE;

        if (!(rp->r_flags & ISDARK))
            is_lit = TRUE;

        rp->r_flags |= ISDARK;  /* Fake darkness */
        light(&old_hero);

        if (is_lit)
            rp->r_flags &= ~ISDARK; /* Restore light state */
    }
    else if (ch == DOOR || ch == SECRETDOOR || ch == '|' || ch == '-')
    {
        /* Entering a room */
        running = FALSE;
        if (hch != '|' && hch != '-')
            light(&hero);   /* knows whether the hero can see things in */
    }	
	
	/* handle other situations */
	
    if (ch == STAIRS)
        running = FALSE;
    else if (ch == POST)
    {
        running = FALSE;
        new_level(POSTLEV,0);
        return;
    }
    else if (isalpha(ch))
    {
        struct linked_list  *mp;
        struct thing    *tp;
        char t;

        running = FALSE;

        mp = find_mons(hero.y, hero.x);

        if (mp == NULL)
            return;

        tp = THINGPTR(mp);

        if (good_monster(*tp))  /* Exchange places with your buddy */
        {
            mvwaddch(cw, old_hero.y, old_hero.x, ch);
            mvwaddch(mw, old_hero.y, old_hero.x, ch);
            mvwaddch(mw, hero.y, hero.x, ' ');
            mvwaddch(cw, hero.y, hero.x, tp->t_oldch);

            (*tp).t_pos.x = old_hero.x; /* Update monster position variables */
            (*tp).t_pos.y = old_hero.y;
            (*tp).t_oldpos.x = old_hero.x;
            (*tp).t_oldpos.y = old_hero.y;

            t = (*tp).t_oldch;
            (*tp).t_oldch = player.t_oldch;
            player.t_oldch = t;

            turn_on(*tp, ISRUN);

            mvwaddch(cw, hero.y, hero.x, PLAYER);
			
			/* make sure that the room shows OK */
			
			light(&hero);
			
            wrefresh(cw);
            return;
        }
        else
        {
            hero = old_hero; /* Restore hero -- we'll fight instead of move */

			/* make sure that the room shows OK */
    		light(&hero);

            fight(&player.t_nxtpos, cur_weapon, NOTHROWN);

            return;
        }
    }
    else
        fighting = FALSE;

    ch = winat(old_hero.y, old_hero.x);
    mvwaddch(cw, old_hero.y, old_hero.x, ch);
    mvwaddch(cw, hero.y, hero.x, PLAYER);
}

/*
    light()
        Called to illuminate a room. If it is dark, remove anything that might
        move.
*/

void
light(coord *cp)
{
    struct room *rp;
    int j, k, x, y;
    char    ch, rch;
    struct linked_list  *item;
    int jlow, jhigh, klow, khigh;   /* Boundaries of lit area */

    if ((rp = roomin(*cp)) != NULL && !on(player, ISBLIND))
    {

        /* is he wearing ring of illumination and in same room? */

        if ((is_wearing(R_LIGHT) || on(player, ISELECTRIC)) &&
            cp == &hero)
            rp->r_flags &= ~ISDARK;

        /* If we are in a maze, don't look at the whole room (level) */

        if (levtype == MAZELEV)
        {
            jlow = max(0, hero.y - 2 - rp->r_pos.y);
            jhigh = min(rp->r_max.y, hero.y + 2 - rp->r_pos.y + 1);
            klow = max(0, hero.x - 2 - rp->r_pos.x);
            khigh = min(rp->r_max.x, hero.x + 2 - rp->r_pos.x + 1);
        }
        else
        {
            jlow = klow = 0;
            jhigh = rp->r_max.y;
            khigh = rp->r_max.x;
        }

        for (j = 0; j < rp->r_max.y; j++)
        {
            for (k = 0; k < rp->r_max.x; k++)
            {
                /* Is this in the given area -- needed for maze */

                if ((j < jlow || j >= jhigh) && (k < klow || k >= khigh))
                    continue;

                y = rp->r_pos.y + j;
                x = rp->r_pos.x + k;

                ch = show(y, x);
                wmove(cw, y, x);

                /* Figure out how to display a secret door */

                if (ch == SECRETDOOR)
                {
                    if (j == 0 || j == rp->r_max.y - 1)
                        ch = '-';
                    else
                        ch = '|';
                }

                /*
                 * For monsters, if they were previously not
                 * seen and now can be seen, or vice-versa,
                 * make sure that will happen.
                 */

                if (isalpha(ch))
                {
                    struct thing    *tp;

                    item = wake_monster(y, x);

                    if (item == NULL)
                        continue;

                    tp = THINGPTR(item);

                    /* Previously not seen -- now can see it */

                    if (tp->t_oldch == ' ' && cansee(tp->t_pos.y, tp->t_pos.x))
                        tp->t_oldch = CCHAR( mvinch(y, x) );

                    /* Previously seen -- now can't see it */

                    else if (off(player, ISBLIND) && tp->t_oldch != ' ' &&
                         !cansee(tp->t_pos.y, tp->t_pos.x))
                        tp->t_oldch = ' ';
                }

                /*
                 * If the room is a dark room, we might want
                 * to remove monsters and the like from it
                 * (since they might move). A dark room or
                 * not in line-of-sight in a maze.
                 */

                if (((rp->r_flags & ISDARK) &&
                    !(rp->r_flags & HASFIRE)) ||
                    (levtype == MAZELEV &&
                     !maze_view(y, x)))
                {
                    rch = CCHAR( mvwinch(cw, y, x) );

                    switch (rch)
                    {
                        case DOOR:
                        case STAIRS:
                        case TRAPDOOR:
                        case TELTRAP:
                        case BEARTRAP:
                        case SLEEPTRAP:
                        case ARROWTRAP:
                        case DARTTRAP:
                        case POOL:
                        case MAZETRAP:
                        case FIRETRAP:
                        case POISONTRAP:
                        case LAIR:
                        case RUSTTRAP:
                        case POST:
                        case '|':
                        case '-':
                        case ' ':
                            ch = rch;
                            break;

                        case    FLOOR:
                            ch = (on(player, ISBLIND) ? FLOOR : ' ');
                            break;
                        default:
                            ch = ' ';
                            break;
                    }
                }
                mvwaddch(cw, y, x, ch);
            }
        }
    }
}

/*
    blue_light()
        magically light up a room (or level or make it dark)
*/

int
blue_light(int flags)
{
    struct room *rp;
    int  blessed = (flags & ISBLESSED);
    int  cursed = (flags & ISCURSED);
    int  ret_val = FALSE;   /* Whether or not affect is known */

    rp = roomin(hero); /* What room is hero in? */

    /* Darken the room if the magic is cursed */

    if (cursed)
    {
        if ((rp == NULL) || (rp->r_flags & ISDARK))
            nothing_message(flags);
        else
        {
            if (!(rp->r_flags & HASFIRE))
                msg("The room suddenly goes dark.");
            else
                nothing_message(flags);

            rp->r_flags |= ISDARK;
            ret_val = TRUE;
        }
    }
    else
    {
        ret_val = TRUE;

        if (rp && (rp->r_flags & ISDARK) && !(rp->r_flags & HASFIRE))
        {
            msg("The room is lit by a %s blue light.",
                 blessed ? "bright" : "shimmering");
        }
        else if (winat(hero.y, hero.x) == PASSAGE)
            msg("The corridor glows %sand then fades.", blessed ? "brightly " : "");
        else
        {
            ret_val = FALSE;
            nothing_message(flags);
        }

        if (blessed)
        {
            short   i;  /* Index through rooms */

            for (i = 0; i < MAXROOMS; i++)
                rooms[i].r_flags &= ~ISDARK;
        }
        else if (rp)
            rp->r_flags &= ~ISDARK;
    }

    /* Light the room and put the player back up */

    light(&hero);
    mvwaddch(cw, hero.y, hero.x, PLAYER);

    return (ret_val);
}

/*
    show()
        returns what a certain thing will display as to the un-initiated
*/

char
show(int y, int x)
{
    char    ch = winat(y, x);
    struct linked_list  *it;
    struct thing    *tp;

    if (isatrap(ch))
    {
        struct trap *trp = trap_at(y, x);
        return (trp->tr_flags & ISFOUND) ? ch : trp->tr_show;
    }
    else if (isalpha(ch))
    {
        if ((it = find_mons(y, x)) == NULL)
        {
            debug("Can't find monster in move.");
            return ' ';
        }
        tp = THINGPTR(it);

        if (on(*tp, ISDISGUISE))
            ch = tp->t_disguise;    /* As a mimic */
        else if (on(*tp, ISINVIS) || (on(*tp, ISSHADOW) &&
            rnd(100) < 90) || on(*tp, CANSURPRISE))
        {
            if (off(player, CANSEE) || on(*tp, CANSURPRISE))
                ch = CCHAR( mvwinch(stdscr, y, x) ); /* Invisible */
        }
        else if (on(*tp, CANINWALL))
        {
            char    tch;

            tch = CCHAR( mvwinch(stdscr, y, x) );

            if (tch == WALL || tch == '-' || tch == '|')
                ch = CCHAR( winch(stdscr) ); /* As Xorn */
        }
    }
    return(ch);
}

/*
    be_trapped()
        The guy stepped on a trap.... Make him pay.
*/

char
be_trapped(struct thing *th, coord tc)
{
    struct trap *tp;
    char   ch, *mname = NULL;
    int    is_player = (th == &player), can_see = cansee(tc.y, tc.x);
    struct linked_list  *mitem = NULL;

    tp = trap_at(tc.y, tc.x);
    ch = tp->tr_type;

    if (!is_player)
    {
        mitem = find_mons(th->t_pos.y, th->t_pos.x);
        mname = monsters[th->t_index].m_name;

        /* Flying monsters do not set off traps */

        if (!mitem || (on(*th, CANFLY) &&
             (ch == BEARTRAP || ch == MAZETRAP || ch == TRAPDOOR
              || ch == ARROWTRAP || ch == DARTTRAP)))
        {
            debug("%s avoided trap.", mname);
            return(ch);
        }
    }
    else
    {
        short   thief_bonus = -50;

        count = running = FALSE;
        mvwaddch(cw, tp->tr_pos.y, tp->tr_pos.x, tp->tr_type);

        if (no_command)
            return(ch);

        if (player.t_ctype == C_THIEF || player.t_ctype == C_NINJA)
            thief_bonus = 10;

        if (((is_wearing(R_LEVITATION) || on(player, CANFLY)) &&
            (ch != FIRETRAP ||
                 (ch == FIRETRAP && !(tp->tr_flags & ISFOUND))))
            || (moving && (tp->tr_flags & ISFOUND) && rnd(100) <
              thief_bonus + 2 * pstats.s_dext + 5 * pstats.s_lvl) &&
            (ch == BEARTRAP || ch == MAZETRAP || ch == TRAPDOOR
             || ch == ARROWTRAP || ch == DARTTRAP))
        {
            static char trname[1024];
            msg(tr_name(ch,trname));
            tp->tr_flags |= ISFOUND;
            return(ch);
        }

        if (moving)
            msg("Your attempt fails.");
    }

    tp->tr_flags |= ISFOUND;

    switch(ch)
    {
        case TRAPDOOR:
            if (is_player)
            {
                level++;
                new_level(NORMLEV,0);
                addmsg("You fell into a trap");

                if (player.t_ctype != C_THIEF
                    && player.t_ctype != C_ASSASIN
                    && player.t_ctype != C_NINJA
                    && rnd(pstats.s_dext) < 4)
                {
                    addmsg(" and were damaged by the fall");

                    if ((pstats.s_hpt -= roll(1, 6)) <= 0)
                    {
                        addmsg("!  The fall killed you.");
                        endmsg();
                        death(D_FALL);

                        return(ch);
                    }
                }

                addmsg("!");
                endmsg();

                if (off(player, ISCLEAR) && rnd(4) < 3)
                {
                    if (on(player, ISHUH))
                        lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
                    else
                        light_fuse(FUSE_UNCONFUSE,0,rnd(8)+HUHDURATION, AFTER);

                    turn_on(player, ISHUH);
                }
                else
                    msg("You feel dizzy for a moment, but it quickly passes.");
            }
            else
            {
                if (can_see)
                    msg("The %s fell into a trap!", mname);

                if (on(*th, ISFAMILIAR))
				    turn_off(player, HASFAMILIAR);

                killed(NULL,mitem,NOMESSAGE,NOPOINTS);
            }
            break;

        case BEARTRAP:
            if (is_stealth(th))
            {
                if (is_player)
                    msg("You pass a bear trap.");
                else if (can_see)
                    msg("The %s passes a bear trap.", mname);
            }
            else
            {
                th->t_no_move += BEARTIME;

                if (is_player)
                    msg("You are caught in a bear trap.");
                else if (can_see)
                    msg("The %s is caught in a bear trap.", mname);
            }
            break;

        case SLEEPTRAP:
            if (is_player)
            {
                msg("A strange white mist envelops you.");

                if (!is_wearing(R_ALERT))
                {
                    if (!is_wearing(R_BREATHE) && off(player, HASOXYGEN))
                    {
                        msg("You fall asleep.");
                        no_command += SLEEPTIME;
                    }
                }
            }
            else
            {
                if (can_see)
                    msg("A strange white mist envelops the %s.", mname);

                if (on(*th, ISUNDEAD))
                {
                    if (can_see)
                        msg("The mist doesn't seem to affect the %s.", mname);
                }

                if (on(*th, ISUNDEAD) || on(*th, HASOXYGEN))
                {
                    if (can_see)
                        msg("The mist doesn't seem to affect the %s.", mname);
                }
                else
                {
                    th->t_no_move += SLEEPTIME;
                }
            }
            break;

        case ARROWTRAP:
            if (swing(th->t_ctype, th->t_stats.s_lvl - 1, th->t_stats.s_arm, 1))
            {
                if (is_player)
                {
                    msg("Oh no! An arrow shot you.");

                    if ((pstats.s_hpt -= roll(1, 6)) <= 0)
                    {
                        msg("The arrow killed you.");
                        death(D_ARROW);
                        return(ch);
                    }
                }
                else
                {
                    if (can_see)
                        msg("An arrow shot the %s.", mname);

                    if (on(*th, NOSHARP))
                    {
                        if (can_see)
                            msg("The arrow has no effect!");
                    }
                    else
                    {
                        if ((th->t_stats.s_hpt -= roll(1, 6)) <= 0)
                        {
                            if (can_see)
                                msg("The arrow killed the %s.", mname);
								
							if (on(*th, ISFAMILIAR))
							    turn_off(player, HASFAMILIAR);

                            killed(NULL, mitem, NOMESSAGE, NOPOINTS);
                        }
                    }
                }
            }
            else
            {
                struct linked_list  *itm;
                struct object *an_arrow;

                if (is_player)
                    msg("An arrow shoots past you.");
                else if (can_see)
                    msg("An arrow shoots by the %s.", mname);

                itm = new_item(sizeof *an_arrow);
                an_arrow = OBJPTR(itm);
                an_arrow->o_type = WEAPON;
                an_arrow->o_which = ARROW;
                an_arrow->o_hplus = rnd(3) - 1;
                an_arrow->o_dplus = rnd(3) - 1;
                init_weapon(an_arrow, ARROW);
                an_arrow->o_count = 1;
                an_arrow->o_pos = tc;
                an_arrow->o_mark[0] = '\0';
                fall(&player, itm, FALSE, FALSE);
            }
            break;

        case TELTRAP:
            if (is_player)
            {
                teleport();

                if (off(player, ISCLEAR))
                {
                    msg("Wait, what's going on here. Huh? What? Who?");

                    if (on(player, ISHUH))
                        lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
                    else
                        light_fuse(FUSE_UNCONFUSE,0,rnd(8)+HUHDURATION, AFTER);

                    turn_on(player, ISHUH);
                }
                else
                    msg("You feel dizzy for a moment, but it quickly passes.");
            }
            else
            {
                int rm;

                /* Erase the monster from the old position */

                if (isalpha(mvwinch(cw, th->t_pos.y, th->t_pos.x)))
                    mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch);

                mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' ');

                /* Get a new position */

                do
                {
                    rm = rnd_room();
                    rnd_pos(&rooms[rm], &th->t_pos);
                }
                while(winat(th->t_pos.y, th->t_pos.x) != FLOOR);

                /* Put it there */

                mvwaddch(mw, th->t_pos.y, th->t_pos.x, th->t_type);
                th->t_oldch = CCHAR( mvwinch(cw, th->t_pos.y, th->t_pos.x) );

                if (can_see)
                    msg("The %s seems to have disappeared!", mname);
            }
            break;

        case DARTTRAP:

            if (swing(th->t_ctype, th->t_stats.s_lvl + 1,th->t_stats.s_arm, 1))
            {
                if (is_player)
                {
                    msg("A small dart just hit you in the shoulder.");

                    if ((pstats.s_hpt -= roll(1, 4)) <= 0)
                    {
                        msg("The dart killed you.");
                        death(D_DART);
                        return(ch);
                    }

                    /* Now the poison */

                    if (player.t_ctype != C_PALADIN
                        && !(player.t_ctype == C_NINJA &&
                        pstats.s_lvl > 12) && !save(VS_POISON))
                    {

                        /*
                         * 75% chance it will do point
                         * damage - else strength
                         */

                        if (rnd(100) < 75)
                        {
                            pstats.s_hpt /= 2;

                            if (pstats.s_hpt == 0)
                            {
                                death(D_POISON);
                                return(ch);
                            }
                        }
                        else if (!is_wearing(R_SUSABILITY))
                            chg_str(-1, FALSE, FALSE);
                    }
                }
                else
                {
                    int orig_hp = th->t_stats.s_hpt;

                    if (can_see)
                        msg("A small dart just hit the %s.", mname);

                    /*
                     * Poison has no effect on poisonous or
                     * undead monsters
                     */

                    if (off(*th, CANPOISON) &&
                        off(*th, ISUNDEAD) &&
                        !save_throw(VS_POISON, th))
                        th->t_stats.s_hpt /= 2;

                    /* Now the dart damage */

                    if (off(*th, NOSHARP))
                        th->t_stats.s_hpt -= roll(1, 4);

                    if (orig_hp == th->t_stats.s_hpt)
                        if (can_see)
                            msg("The dart has not effect!");
                        else if (th->t_stats.s_hpt < 0)
                        {
                            if (can_see)
                                msg("The dart killed the %s.", mname);
								
							if (on(*th, ISFAMILIAR))
							    turn_off(player, HASFAMILIAR);

                            killed(NULL, mitem, NOMESSAGE, NOPOINTS);
                        }
                }
            }
            else
            {
                if (is_player)
                    msg("A small dart whizzes by your ear and vanishes.");
                else if (can_see)
                    msg("A small dart whizzes by the %s and vanishes.", mname);
            }
            break;

        case POOL:
        {
            int i;

            i = rnd(100);

            if (is_player)
            {
                if (on(player, ISELECTRIC))
                {
                    msg("Oh no!!! The water shorts you out");
                    extinguish_fuse(FUSE_UNELECTRIFY);
                    turn_off(player, ISELECTRIC);

                    if (!is_wearing(R_ELECTRESIST))
                    {
                        if ((pstats.s_hpt -= roll(1, 10)) <= 0)
                        {
                            addmsg("!  The shock killed you.");
                            endmsg();
                            death(D_DROWN);
                            return(ch);
                        }
                    }
                }

                if ((tp->tr_flags & ISGONE))
                {
                    if (i < 30)
                    {
                        teleport(); /* teleport away */

                        if (off(player, ISCLEAR))
                        {
                            if (on(player, ISHUH))
                               lengthen_fuse(FUSE_UNCONFUSE,rnd(8)+HUHDURATION);
                            else
                               light_fuse(FUSE_UNCONFUSE,0,rnd(8)+HUHDURATION,AFTER);
                            turn_on(player, ISHUH);
                        }
                        else
                            msg("You feel dizzy for a moment, but it quickly passes.");

                        pool_teleport = TRUE;
                    }
                    else if ((i < 45) && level > 2)
                    {
                        level -= rnd(2) + 1;
                        new_level(NORMLEV,0);
                        pool_teleport = TRUE;
                        msg("You here a faint groan from below.");

                        if (off(player, ISCLEAR))
                        {
                            if (on(player, ISHUH))
                                lengthen_fuse(FUSE_UNCONFUSE,rnd(8)+HUHDURATION);
                            else
                                light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER);

                            turn_on(player, ISHUH);
                        }
                        else
                            msg("You feel dizzy for a moment, but it quickly passes.");
                    }
                    else if (i < 70)
                    {
                        level += rnd(4) + 1;
                        new_level(NORMLEV,0);
                        pool_teleport = TRUE;
                        msg("You find yourself in strange surroundings.");

                        if (off(player, ISCLEAR))
                        {
                            if (on(player, ISHUH))
                                lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
                            else
                                light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER);

                            turn_on(player, ISHUH);
                        }
                        else
                            msg("You feel dizzy for a moment, but it quickly passes.");
                    }
                    else if (i > 95)
                    {
                        if (is_wearing(R_BREATHE) || on(player, HASOXYGEN))
                            msg("You splash in the pool unharmed.");
                        else
                        {
                            msg("Oh no!!! You drown in the pool!!! --More--");
                            wait_for(' ');
                            death(D_DROWN);
                            return(ch);
                        }
                    }
                }
            }
            else
            {
                if (can_see)
                    msg("The %s fell into the pool!", mname);

                if (i < 15)
                {
                    if (off(*th, HASOXYGEN))
                    {
                        if (can_see)
                            msg("The %s has drowned!", mname);
							
                        if (on(*th, ISFAMILIAR))
						    turn_off(player, HASFAMILIAR);
							
                        killed(NULL, mitem, NOMESSAGE, NOPOINTS);
                    }
                }
            }
        }
        break;

        case MAZETRAP:

            if (is_player)
            {
                level++;
                new_level(MAZELEV,0);
                addmsg("You are surrounded by twisty passages");

                if (rnd(4) < 1)
                {
                    addmsg(" and were damaged by the fall");

                    if ((pstats.s_hpt -= roll(1, 6)) <= 0)
                    {
                        addmsg("!  The fall killed you.");
                        endmsg();
                        death(D_FALL);
                        return(ch);
                    }
                }

                addmsg("!");
                endmsg();

                if (off(player, ISCLEAR))
                {
                    if (on(player, ISHUH))
                        lengthen_fuse(FUSE_UNCONFUSE, rnd(8) + HUHDURATION);
                    else
                    {
                        light_fuse(FUSE_UNCONFUSE, 0, rnd(8) + HUHDURATION, AFTER);
                        turn_on(player, ISHUH);
                    }
                }
                else
                    msg("You feel dizzy for a moment, but it quickly passes.");
            }
            else
            {
                if (can_see)
                    msg("The %s fell into a trap!", mname);

                if (on(*th, ISFAMILIAR))
					turn_off(player, HASFAMILIAR);

                killed(NULL, mitem, NOMESSAGE, NOPOINTS);
            }
            break;

        case FIRETRAP:
        {
            struct room *rp = roomin(hero);

            if (is_player)
            {
                if (is_wearing(R_FIRERESIST) || on(player, NOFIRE))
                    msg("You pass through the flames unharmed.");
                else
                {
                    addmsg("You are burned by the flames");

                    if ((pstats.s_hpt -= roll(pstats.s_lvl, 2)) <= 0)
                    {
                        addmsg("!  The flames killed you.");
                        endmsg();
                        death(D_FIRE);
                        return(ch);
                    }

                    addmsg("!");

                    endmsg();
                }
            }
            else
            {
                if (on(*th, CANBBURN))
                {
                    if (can_see)
                        msg("The %s is burned to death by the flames.", mname);

                    killed(NULL, mitem, NOMESSAGE, NOPOINTS);
                }
                else if (on(*th, NOFIRE))
                {
                    if (can_see)
                        msg("The %s passes through the flames unharmed.", mname);
                }
                else
                {
                    if (can_see)
                        msg("The %s is burned by the flames.", mname);

                    if ((th->t_stats.s_hpt -= roll(th->t_stats.s_lvl, 3)) < 0)
                    {
                        if (can_see)
                            msg("The %s is burned to death by the flames.", mname);

                        if (on(*th, ISFAMILIAR))
						    turn_off(player, HASFAMILIAR);
							
						killed(NULL, mitem, NOMESSAGE, NOPOINTS);
                    }
                    else if (th->t_stats.s_intel < rnd(20))
                    {
                        if (can_see)
                            msg("The %s turns and runs away in fear.", mname);

                        turn_on(*th, ISFLEE);
                    }
                }
            }

            if (rp != NULL)
            {
                rp->r_flags &= ~ISDARK;
                light(&hero);
            }
        }
        break;

        case POISONTRAP:
            if (is_player)
            {
                msg("You fall into a pool of poison.");

                if (rnd(4) > 0)
                {
                    msg("You swallow some of the liquid and feel very sick.");
                    pstats.s_hpt -= pstats.s_hpt / 3;

                    if (player.t_ctype != C_PALADIN
                        && !(player.t_ctype == C_NINJA &&
                        pstats.s_lvl > 12)
                            && !is_wearing(R_SUSABILITY))
                        chg_str(-2, FALSE, FALSE);
                }
                else
                    msg("The stuff tastes horrible.");
            }
            else
            {
                if (can_see)
                    msg("The %s falls into the pool of poison.", mname);

                if (rnd(4) > 0 && off(*th, ISUNDEAD))
                    if (th->t_stats.s_hpt *= 2.0 / 3.0 < 0)
					{
					    if (can_see)
						    msg("The %s dies from the poison.", mname);
						
						if (on(*th, ISFAMILIAR))
						    turn_off(player, HASFAMILIAR);
							
						killed(NULL, mitem, NOMESSAGE, NOPOINTS);
					}
            }
            break;

        case LAIR:
            if (is_player)
            {
                msg("You found a monster lair!");
                mpos = 0;
                new_level(THRONE,0);
            }
            else
            {
                if (can_see)
                    msg("The %s fell into a trap!", mname);

                if (on(*th, ISFAMILIAR))
				    turn_off(player, HASFAMILIAR);

                killed(NULL, mitem, NOMESSAGE, NOPOINTS);
            }
            break;

        case RUSTTRAP:
            if (is_player)
            {
                msg("You are splashed by water.");

                if (cur_armor != NULL &&
                    cur_armor->o_which != SOFT_LEATHER &&
                    cur_armor->o_which != HEAVY_LEATHER &&
                    cur_armor->o_which != CUIRBOLILLI &&
                    cur_armor->o_which != PADDED_ARMOR &&
                    cur_armor->o_which != CRYSTAL_ARMOR &&
                    cur_armor->o_which != MITHRIL &&
                    !(cur_armor->o_flags & ISPROT) &&
                    cur_armor->o_ac < pstats.s_arm + 1)
                {
                    msg("Your armor weakens!");
                    cur_armor->o_ac++;
                }
                else if (cur_armor != NULL && (cur_armor->o_flags & ISPROT))
                    msg("The rust vanishes instantly!");
            }
            else
            {
                if (can_see)
                    msg("The %s is splashed by water.", mname);
            }
    }

    return(ch);
}

/*
    dip_it()
        Dip an object into a magic pool
*/

void
dip_it(void)
{
    struct linked_list  *what;
    struct object   *ob;
    struct trap *tp;
    int wh, i;

    tp = trap_at(hero.y, hero.x);

    if (tp == NULL || !(tp->tr_type == POOL || tp->tr_type == POISONTRAP))
    {
        msg("I see no pools here.");
        return;
    }

    if (tp->tr_flags & ISGONE)
    {
        msg("This %s appears to have been used once already.",
         (tp->tr_type == POOL ? "shimmering pool" : "poison pool"));
        return;
    }

    if ((what = get_item("dip", 0)) == NULL)
    {
        msg("");
        after = FALSE;
        return;
    }

    ob = OBJPTR(what);
    mpos = 0;

    if (ob == cur_armor)
    {
        msg("You have to take off your armor before you can dip it.");
        return;
    }
    else if (ob == cur_ring[LEFT_1] || ob == cur_ring[LEFT_2] ||
         ob == cur_ring[LEFT_3] || ob == cur_ring[LEFT_4] ||
         ob == cur_ring[RIGHT_1] || ob == cur_ring[RIGHT_2] ||
         ob == cur_ring[RIGHT_3] || ob == cur_ring[RIGHT_4])
    {
        msg("You have to take that ring off before you can dip it.");
        return;
    }

    tp->tr_flags |= ISGONE;

    if (ob != NULL && tp->tr_type == POOL)
    {
        wh = ob->o_which;
        ob->o_flags |= ISKNOW;
        i = rnd(100);

        switch (ob->o_type)
        {
            case WEAPON:
                if (i < 50)
                {
                    if (!(ob->o_flags & ISCURSED))
                    {
                        ob->o_hplus += 1;
                        ob->o_dplus += 1;
                    }
                    else
                    {
                        ob->o_hplus = rnd(2);
                        ob->o_dplus = rnd(2);
                    }

                    ob->o_flags &= ~ISCURSED;
                    msg("The %s glows blue for a moment.", weaps[wh].w_name);
                }
                else if (i < 70)    /* curse weapon here */
                {
                    if (!(ob->o_flags & ISCURSED))
                    {
                        ob->o_hplus = -(rnd(2) + 1);
                        ob->o_dplus = -(rnd(2) + 1);
                    }
                    else    /* if already cursed */
                    {
                        ob->o_hplus--;
                        ob->o_dplus--;
                    }

                    ob->o_flags |= ISCURSED;
                    msg("The %s glows red for a moment.", weaps[wh].w_name);
                }
                else
                    msg("Nothing seems to happen.");
                break;

            case ARMOR:

                if (i < 50)     /* enchant armor */
                {
                    if (!(ob->o_flags & ISCURSED))
                        ob->o_ac -= rnd(2) + 1;
                    else
                        ob->o_ac = -rnd(3) + armors[wh].a_class;

                    ob->o_flags &= ~ISCURSED;
                    msg("The %s glows blue for a moment.", armors[wh].a_name);
                }
                else if (i < 75)   /* curse armor */
                {
                    if (!(ob->o_flags & ISCURSED))
                        ob->o_ac = rnd(3) + armors[wh].a_class;
                    else
                        ob->o_ac += rnd(2) + 1;

                    ob->o_flags |= ISCURSED;
                    msg("The %s glows red for a moment.", armors[wh].a_name);
                }
                else
                    msg("Nothing seems to happen");
                break;

            case STICK:
            {
                int j;

                j = rnd(8) + 1;

                if (i < 50)     /* add charges */
                {
                    ob->o_charges += j;
                    know_items[TYP_STICK][wh] = TRUE;

                    if (ob->o_flags & ISCURSED)
                        ob->o_flags &= ~ISCURSED;

                    msg("The %s %s glows blue for a moment.",
                        ws_made[wh], ws_type[wh]);
                }
                else if (i < 65)    /* remove charges */
                {
                    if ((ob->o_charges -= i) < 0)
                        ob->o_charges = 0;

                    know_items[TYP_STICK][wh] = TRUE;

                    if (ob->o_flags & ISBLESSED)
                        ob->o_flags &= ~ISBLESSED;
                    else
                        ob->o_flags |= ISCURSED;

                    msg("The %s %s glows red for a moment.",
                        ws_made[wh], ws_type[wh]);
                }
                else
                    msg("Nothing seems to happen.");
            }
            break;

            case SCROLL:

                know_items[TYP_SCROLL][wh] = TRUE;
                msg("The '%s' scroll unfurls.", s_names[wh]);
                break;

            case POTION:

                know_items[TYP_POTION][wh] = TRUE;
                msg("The %s potion bubbles for a moment.", p_colors[wh]);
                break;

            case RING:
                if (i < 50)     /* enchant ring */
                {
                    if (!(ob->o_flags & ISCURSED))
                        ob->o_ac += rnd(2) + 1;
                    else
                        ob->o_ac = rnd(2) + 1;

                    ob->o_flags &= ~ISCURSED;
                }
                else if (i < 80)    /* curse ring */
                {
                    if (!(ob->o_flags & ISCURSED))
                        ob->o_ac = -(rnd(2) + 1);
                    else
                        ob->o_ac -= (rnd(2) + 1);

                    ob->o_flags |= ISCURSED;
                }

                know_items[TYP_RING][wh] = TRUE;
                msg("The %s ring vibrates for a moment.", r_stones[wh]);
                break;

            default:
                msg("The pool bubbles for a moment.");
        }
    }
    else if (ob != NULL && tp->tr_type == POISONTRAP)
    {
        if ((player.t_ctype == C_PALADIN) ||
            (player.t_ctype == C_CLERIC && rnd(2)))
        {
            msg("Trying to use poison is evil.");
            luck += 2;
        }

        if (ob->o_type != WEAPON || rnd(10) > 0)
            msg("Nothing seems to happen.");
        else
        {
            msg("Your %s is covered with a black sticky liquid.",
                weaps[ob->o_which].w_name);
            ob->o_flags |= ISPOISON;
        }
    }
    else
        msg("Nothing seems to happen.");
}

/*
    trap_at()
        find the trap at (y,x) on screen.
*/

struct trap *
trap_at(int y, int x)
{
    struct trap *tp, *ep;

    ep = &traps[ntraps];

    for (tp = traps; tp < ep; tp++)
        if (tp->tr_pos.y == y && tp->tr_pos.x == x)
            break;

    if (tp == ep)
    {
        debug((sprintf(prbuf, "Trap at %d,%d not in array", y, x), prbuf));
        tp = NULL;
    }

    return(tp);
}

/*
    set_trap()
        set a trap at (y, x) on screen.
*/

void
set_trap(struct thing *tp, int y, int x)
{
    int is_player = (tp == &player);
    int selection = rnd(7) + 1;
    char  ch = 0, och;
    int  thief_bonus = 0;

    switch(och = CCHAR( mvinch(y, x) ))
    {
        case WALL:
        case FLOOR:
        case PASSAGE:
            break;
        default:
            msg("The trap failed!");
            return;
    }

    if (is_player && (player.t_ctype == C_THIEF ||
        player.t_ctype == C_NINJA))
        thief_bonus = 10;

    if (ntraps >= 2 * MAXTRAPS || ++trap_tries >= MAXTRPTRY ||
        rnd(60) >= (tp->t_stats.s_dext + tp->t_stats.s_lvl / 2 +
        thief_bonus))
    {
        if (is_player)
            msg("The trap failed!");

        return;
    }

    /* Set up for redraw */

    clearok(cw, TRUE);
    touchwin(cw);

    if (is_player)
    {
        add_line("[1] Trap Door");
        add_line("[2] Bear Trap");
        add_line("[3] Sleep Trap");
        add_line("[4] Arrow Trap");
        add_line("[5] Teleport Trap");
        add_line("[6] Dart Trap");
        add_line("[7] Fire Trap");
        end_line();
        msg("Which trap? ");

        selection = (short) ((readchar() & 0177) - '0');

        while (selection < 1 || selection > 7)
        {
            if (selection == (short) ESCAPE - (short) '0')
            {
                after = FALSE;
                return;
            }

            msg("");
            msg("Please enter a selection between 1 and 7:  ");
            selection = (short) ((readchar() & 0177) - '0');
        }
    }

    switch (selection)
    {
        case 1: ch = TRAPDOOR; break;
        case 2: ch = BEARTRAP; break;
        case 3: ch = SLEEPTRAP;break;
        case 4: ch = ARROWTRAP;break;
        case 5: ch = TELTRAP;   break;
        case 6: ch = DARTTRAP;  break;
        case 7: ch = FIRETRAP;  break;
    }

    mvaddch(y, x, ch);

    traps[ntraps].tr_type = ch;
    traps[ntraps].tr_flags = ISTHIEFSET;
    traps[ntraps].tr_show = och;
    traps[ntraps].tr_pos.y = y;
    traps[ntraps++].tr_pos.x = x;
}


/*
    rndmove()
        move in a random direction if the monster/person is confused
*/

coord
rndmove(struct thing *who)
{
    int x, y;
    int ex, ey, nopen = 0;
    coord    ret;    /* what we will be returning */
    coord    dest;

    ret = who->t_pos;

    /*
     * Now go through the spaces surrounding the player and set that
     * place in the array to true if the space can be moved into
     */

    ey = ret.y + 1;
    ex = ret.x + 1;

    for (y = who->t_pos.y - 1; y <= ey; y++)
        if (y > 0 && y < LINES - 2)
            for (x = who->t_pos.x - 1; x <= ex; x++)
            {
                if (x < 0 || x >= COLS)
                    continue;

                if (step_ok(y, x, NOMONST, who))
                {
                    dest.y = y;
                    dest.x = x;

                    if (!diag_ok(&who->t_pos, &dest, who))
                        continue;

                    if (rnd(++nopen) == 0)
                        ret = dest;
                }
            }

    return(ret);
}

/*
    isatrap()
        Returns TRUE if this character is some kind of trap
*/

int
isatrap(int ch)
{
    switch(ch)
    {
        case DARTTRAP:
        case TELTRAP:
        case TRAPDOOR:
        case ARROWTRAP:
        case SLEEPTRAP:
        case POOL:
        case MAZETRAP:
        case FIRETRAP:
        case POISONTRAP:
        case LAIR:
        case RUSTTRAP:
        case BEARTRAP:
            return (TRUE);
        default:
            return (FALSE);
    }
}
