view urogue/move.c @ 298:5a94c9b3181e

UltraRogue: clear the next_obj field when removing items from the floor. The next_obj field is a pointer which the top item in a stack uses to keep a list of the other items. When removing an item from the stack, rem_obj() failed to set next_obj to NULL, which can cause items in monster inventory to point to items earlier in the inventory list. That causes infinite co-recursion when saving or restoring.
author John "Elwin" Edwards
date Thu, 08 Feb 2018 20:54:34 -0500
parents c495a4f288c6
children e52a8a7ad4c5
line wrap: on
line source

/*
    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);