view xrogue/move.c @ 306:057c5114e244

Super-Rogue: fix some out-of-range constants. Constants K_ARROW etc., for causes of death other than monsters, are in the 240-255 range. They were often passed to functions taking char, which is usually signed, making the values out of range. The function declarations have been changed to unsigned char, which is also the type used by the scoreboard code.
author John "Elwin" Edwards
date Sat, 17 Apr 2021 15:41:12 -0400
parents e52a8a7ad4c5
children
line wrap: on
line source

/*
    move.c - Hero movement commands

    XRogue: Expeditions into the Dungeons of Doom
    Copyright (C) 1991 Robert Pietkivitch
    All rights reserved.
    
    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
    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 <curses.h>
#include <ctype.h>
#include <string.h>
#include "rogue.h"

/*
 * Used to hold the new hero position
 */

coord move_nh;

static char Moves[3][3] = {
    { 'y', 'k', 'u' },
    { 'h', '.', 'l' },
    { 'b', 'j', 'n' }
};

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

char
be_trapped(struct thing *th, coord *tc)
{
    register struct trap *tp;
    register char ch, *mname = NULL;
    register bool is_player = (th == &player),
                  can_see;
    register struct linked_list *mitem = NULL;
    register struct thing *mp;


    /* Can the player see the creature? */
    can_see = cansee(tc->y, tc->x);
    can_see &= (is_player || !invisible(th));

    tp = trap_at(tc->y, tc->x);
    /*
     * if he's wearing boots of elvenkind, he won't set off the trap
     * unless its a magic pool (they're not really traps)
     */
    if (is_player                                       &&
        cur_misc[WEAR_BOOTS] != NULL                    &&
        cur_misc[WEAR_BOOTS]->o_which == MM_ELF_BOOTS   &&
        tp->tr_type != POOL)
            return '\0';

    /*
     * if the creature is flying then it won't set off the trap
     */
     if (on(*th, ISFLY))
        return '\0';

    tp->tr_flags |= ISFOUND;

    if (!is_player) {
        mitem = find_mons(th->t_pos.y, th->t_pos.x);
        mname = monster_name(th);
    }
    else {
        count = running = FALSE;
        mvwaddch(cw, tp->tr_pos.y, tp->tr_pos.x, tp->tr_type);
    }
    switch (ch = tp->tr_type) {
        case TRAPDOOR:
            if (is_player) {
                level++;
                pstats.s_hpt -= roll(1, 10);
                if (pstats.s_hpt < 1) {
            pstats.s_hpt = -1;
            death(D_FALL);
        }
        wclear(cw);
        wclear(mw);
                new_level(NORMLEV);
                msg("You fell through a trap! ");
            }
            else {
                if (can_see) msg("%s fell into a trap!", prname(mname, TRUE));

                /* 
                 * See if the fall killed the monster 
                 * don't let a UNIQUE die since it might have an artifact
                 * that we need
                 */
                if (off(*th,ISUNIQUE) && (th->t_stats.s_hpt-=roll(1,10)) <= 0){
                    killed(mitem, FALSE, FALSE, FALSE);
                }
                else {  /* Just move monster to next level */
                    check_residue(th);

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

                    /* let him summon on next lvl */
                    if (on (*th, HASSUMMONED)) {
                            turn_off(*th, HASSUMMONED); 
                            turn_on(*th, CANSUMMON);
                    }
                    turn_on(*th,ISELSEWHERE);
                    detach(mlist, mitem);
                    attach(tlist, mitem);       /* remember him next level */

                    /* Make sure that no one is still chasing us */
                    for (mitem = mlist; mitem != NULL; mitem = next(mitem)) {
                        mp = THINGPTR(mitem);
                        if (mp->t_dest == &th->t_pos) {
                            mp->t_dest = &hero;
                            mp->t_wasshot = FALSE;
                            turn_off(*mp, ISFLEE);      /* Don't run away! */
                        }
                    }

                    /* Make sure we were not chasing a monster here */
                    th->t_dest = &hero;
                    if (on(*th, ISFRIENDLY)) turn_off(*th, ISFLEE);
                }
            }
        /* worm hole trap to OUTSIDE */
        when WORMHOLE:
            if (is_player) {
                prev_max = 1000;    /* flag used in n_level.c */
        level++;
                pstats.s_hpt -= roll(1, 10);
                if (pstats.s_hpt < 1) {
            pstats.s_hpt = -1;
            death(D_FALL);
        }
                new_level(OUTSIDE);
                msg("You suddenly find yourself in strange surroundings! ");
        return(ch);
            }
            else {
                if (can_see) msg("%s fell into the worm hole! ", prname(mname, TRUE));

                /* 
                 * See if the fall killed the monster 
                 * don't let a UNIQUE die since it might have an artifact
                 * that we need
                 */
                if (off(*th,ISUNIQUE) && (th->t_stats.s_hpt-=roll(1,10)) <= 0){
                    killed(mitem, FALSE, FALSE, FALSE);
                }
                else {  /* Just move monster to next level */
                    check_residue(th);

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

                    /* let him summon on next lvl */
                    if (on (*th, HASSUMMONED)) {
                            turn_off(*th, HASSUMMONED); 
                            turn_on(*th, CANSUMMON);
                    }

                    turn_on(*th,ISELSEWHERE);
                    detach(mlist, mitem);
                    attach(tlist, mitem);       /* remember him next level */

                    /* Make sure that no one is still chasing us */
                    for (mitem = mlist; mitem != NULL; mitem = next(mitem)) {
                        mp = THINGPTR(mitem);
                        if (mp->t_dest == &th->t_pos) {
                            mp->t_dest = &hero;
                            mp->t_wasshot = FALSE;
                            turn_off(*mp, ISFLEE);      /* Don't run away! */
                        }
                    }

                    /* Make sure we were not chasing a monster here */
                    th->t_dest = &hero;
                    if (on(*th, ISFRIENDLY)) turn_off(*th, ISFLEE);
                }
            }
        when BEARTRAP:
            if (is_stealth(th)) {
                if (is_player) msg("You pass a bear trap.");
                else if (can_see) msg("%s passes a bear trap.", 
                                      prname(mname, TRUE));
            }
            else {
                th->t_no_move += movement(&player) * BEARTIME;
                th->t_action = A_FREEZE;
                if (is_player) msg("You are caught in a bear trap.");
                else if (can_see) msg("%s is caught in a bear trap.",
                                        prname(mname, TRUE));
            }
        when SLEEPTRAP:
            if (is_player) {
                if (!ISWEARING(R_ALERT)) {
                    msg("A strange white mist envelops you.  You fall asleep. ");
                    player.t_no_move += movement(&player) * SLEEPTIME;
                    player.t_action = A_FREEZE;
                }
        else {
            msg("The white mist invigorates you. ");
        }
            }
            else {
                if (can_see) 
                    msg("A strange white mist envelops %s. ",
                        prname(mname, FALSE));
                if (on(*th, ISUNDEAD)) {
                    if (can_see) 
                        msg("The mist doesn't seem to affect %s.",
                           prname(mname, FALSE));
                }
                else {
                    th->t_no_move += movement(th) * SLEEPTIME;
                    th->t_action = A_FREEZE;
                }
            }
        when 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, 8)) < 1) {
            pstats.s_hpt = -1;
                        msg("The arrow killed you.  --More--");
            wait_for(' ');
                        death(D_ARROW);
                    }
                }
                else {
                    if (can_see) 
                        msg("An arrow shot %s.", prname(mname, FALSE));
                    if ((th->t_stats.s_hpt -= roll(1, 8)) < 1) {
                        if (can_see) 
                            msg("The arrow killed %s.", prname(mname, FALSE));
                        killed(mitem, FALSE, FALSE, TRUE);
                    }
                }
            }
            else
            {
                register struct linked_list *item;
                register struct object *arrow;

                if (is_player) msg("An arrow shoots past you.");
                else if (can_see) 
                      msg("An arrow shoots by %s.", prname(mname, FALSE));
                item = new_item(sizeof *arrow);
                arrow = OBJPTR(item);
                arrow->o_type = WEAPON;
                arrow->contents = NULL;
                arrow->o_which = ARROW;
                arrow->o_hplus = rnd(7) - 1;
                arrow->o_dplus = rnd(7) - 1;
                init_weapon(arrow, ARROW);
                arrow->o_count = 1;
                arrow->o_pos = *tc;
                arrow->o_mark[0] = '\0';
                fall(item, FALSE);
            }
        when TELTRAP:
            if (is_player) teleport();
            else {
                register int rm;
                struct room *old_room;  /* old room of monster */

                /* 
                 * 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, ' ');
                /*
                 * check to see if room should go dark
                 */
                if (on(*th, HASFIRE)) {
                    old_room=roomin(&th->t_pos);
                    if (old_room != NULL) {
                        register struct linked_list *fire_item;

                        for (fire_item = old_room->r_fires; fire_item != NULL;
                             fire_item = next(fire_item)) {
                            if (THINGPTR(fire_item) == th) {
                                detach(old_room->r_fires, fire_item);
                                destroy_item(fire_item);

                                if (old_room->r_fires == NULL) {
                                    old_room->r_flags &= ~HASFIRE;
                                    if (can_see) light(&hero);
                                }
                            }
                        }
                    }
                }

                /* Get a new position */
                do {
                    rm = rnd_room();
                    rnd_pos(&rooms[rm], &th->t_pos);
                } until(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 = mvwinch(cw, th->t_pos.y, th->t_pos.x) & A_CHARTEXT;
                /*
                 * check to see if room that creature appears in should
                 * light up
                 */
                if (on(*th, HASFIRE)) {
                    register struct linked_list *fire_item;

                    fire_item = creat_item();
                    ldata(fire_item) = (char *) th;
                    attach(rooms[rm].r_fires, fire_item);

                    rooms[rm].r_flags |= HASFIRE;
                    if(cansee(th->t_pos.y, th->t_pos.x) && 
                       next(rooms[rm].r_fires) == NULL)
                        light(&hero);
                }
                if (can_see) 
                    msg("%s seems to have disappeared!", prname(mname, TRUE));
            }
        when 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. ");
                    if ((pstats.s_hpt -= roll(1, 8)) < 1) {
            pstats.s_hpt = -1;
                        msg("The dart killed you.");
            wait_for(' ');
                        death(D_DART);
                    }

                    /* Now the poison */
                    if (!save(VS_POISON, &player, 0)) {
                        /* 75% chance it will do point damage - else strength */
                        if (rnd(100) < 75) {
                            pstats.s_hpt /= 2;
                            if (pstats.s_hpt < 1) {
                pstats.s_hpt = -1;
                death(D_POISON);
                }
                        }
                        else if (!ISWEARING(R_SUSABILITY))
                                chg_str(-1);
                    }
                }
                else {
                    if (can_see)
                        msg("A small dart stabs the %s. ",
                                prname(mname, FALSE));
                    if ((th->t_stats.s_hpt -= roll(1,8)) < 1) {
                        if (can_see) 
                            msg("The dart killed %s.", prname(mname, FALSE));
                        killed(mitem, FALSE, FALSE, TRUE);
                    }
                    if (!save(VS_POISON, th, 0)) {
                        th->t_stats.s_hpt /= 2 + level;
                        if (th->t_stats.s_hpt < 1) {
                            if (can_see) 
                                msg("The dart killed %s.", prname(mname,FALSE));
                            killed(mitem, FALSE, FALSE, TRUE);
                        }
                    }
                }
            }
            else {
                if (is_player)
                    msg("A small dart whizzes by your ear and vanishes.");
                else if (can_see)
                    msg("A small dart whizzes by %s's ear and vanishes.",
                        prname(mname, FALSE));
            }
        when POOL: {
            register int i;

            i = rnd(100);
            if (is_player) {
                if ((tp->tr_flags & ISGONE)) {
                    if (i < 56) {
                        teleport();        /* teleport away */
                        pool_teleport = TRUE;
                    }
                    else if((i < 72) && level > 4) {
                        level -= rnd(4) + 1;
                        cur_max = level;
                        new_level(NORMLEV);
                        pool_teleport = TRUE;
                        msg("You here a faint groan from below.");
                    }
                    else if(i < 85) {
                        level += rnd(4) + 1;
                        new_level(NORMLEV);
                        pool_teleport = TRUE;
                        msg("You find yourself in strange surroundings.");
                    }
                    else if(i > 96) {
                        msg("Oh no!!! You drown in the pool!!!  --More--");
                        wait_for(' ');
            pstats.s_hpt = -1;
                        death(D_DROWN);
                    }
            else {
            new_level(NORMLEV);
            pool_teleport = TRUE;
            msg("You are whisked away to another region.");
            }
                }
            }
            else {
                if (i < 60) {
                    if (can_see) {
                        /* Drowns */
                        if (i < 50) 
                            msg("%s drowned in the pool!", prname(mname, TRUE));

                        /* Teleported to another level */
                        else msg("%s disappeared!", prname(mname, TRUE));
                    }
                    killed(mitem, FALSE, FALSE, TRUE);
                }
            }
        }
    when MAZETRAP:
        if (is_player) {
            pstats.s_hpt -= roll(1, 10);
            level++;
            if (pstats.s_hpt < 1) {
        pstats.s_hpt = -1;
        death(D_FALL);
        }
        wclear(cw);
        wclear(mw);
            new_level(MAZELEV);
            msg("You are surrounded by twisty passages! ");
        }
        else {
            if (can_see) msg("%s fell into a maze trap!", prname(mname, TRUE));
            if (on(*th, ISUNIQUE)) {
                    check_residue(th);

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

                    /* let him summon on next lvl */
                    if (on (*th, HASSUMMONED)) {
                            turn_off(*th, HASSUMMONED); 
                            turn_on(*th, CANSUMMON);
                    }
                    turn_on(*th,ISELSEWHERE);
                    detach(mlist, mitem);
                    attach(tlist, mitem);       /* remember him next level */

                    /* Make sure that no one is still chasing us */
                    for (mitem = mlist; mitem != NULL; mitem = next(mitem)) {
                        mp = THINGPTR(mitem);
                        if (mp->t_dest == &th->t_pos) {
                            mp->t_dest = &hero;
                            mp->t_wasshot = FALSE;
                            turn_off(*mp, ISFLEE);      /* Don't run away! */
                        }
                    }

                    /* Make sure we were not chasing a monster here */
                    th->t_dest = &hero;
                    if (on(*th, ISFRIENDLY)) turn_off(*th, ISFLEE);
            }
            else
                    killed(mitem, FALSE, FALSE, FALSE);
        }
    }

    /* Move the cursor back onto the hero */
    wmove(cw, hero.y, hero.x);

    flushinp();
    return(ch);
}

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

bool
blue_light(bool blessed, bool cursed)
{
    register struct room *rp;
    bool 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) || !lit_room(rp)) msg(nothing);
        else {
            rp->r_flags |= ISDARK;
            if (!lit_room(rp) && (levtype != OUTSIDE || !daytime) &&
        !ISWEARING(R_LIGHT)) 
                msg("The %s suddenly goes dark.",
                    levtype == OUTSIDE ? "area" : "room");
            else msg(nothing);
            ret_val = TRUE;
        }
    }
    else {
        ret_val = TRUE;
        if (rp && !lit_room(rp) &&
            (levtype != OUTSIDE || !daytime)) {
            addmsg("The %s is lit", levtype == OUTSIDE ? "area" : "room");
            addmsg(" by a %s blue light.", blessed ? "bright" : "shimmering");
            endmsg();
        }
        else if (winat(hero.y, hero.x) == PASSAGE)
            msg("The corridor glows %sand then fades",
                    blessed ? "brightly " : "");
        else {
            ret_val = FALSE;
            msg(nothing);
        }
        if (blessed) {
            register int 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);
}

/*
 * 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)
{
    int legal=0;                /* Number of legal alternatives */
    register int y, x,          /* Indexes though possible positions */
                 locy = 0, locx = 0;    /* Hold delta of chosen location */

    /* New position */
    move_nh.y = hero.y + dy;
    move_nh.x = hero.x + dx;

    /* If it is a legal move, just return */
    if (move_nh.x >= 0 && move_nh.x < cols && move_nh.y > 0 && move_nh.y < lines - 2) {
        
        switch (winat(move_nh.y, move_nh.x)) {
            case WALL:
            case VERTWALL:
            case HORZWALL:
                break;
            default:
                if (diag_ok(&hero, &move_nh, &player))
                        return;
        }
    }

    /* Check legal places surrounding the player -- ignore previous position */
    for (y = hero.y - 1; y <= hero.y + 1; y++) {
        if (y < 1 || y > lines - 3)
            continue;
        for (x = hero.x - 1; x <= hero.x + 1; x++) {
            /* Ignore borders of the screen */
            if (x < 0 || x > cols - 1)
                continue;
            
            /* 
             * Ignore where we came from, where we are, and where we couldn't go
             */
            if ((x == hero.x - dx && y == hero.y - dy) ||
                (x == hero.x + dx && y == hero.y + dy) ||
                (x == hero.x && y == hero.y))
                continue;

            switch (winat(y, x)) {
                case WALL:
                case VERTWALL:
                case HORZWALL:
                    break;
                default:
                    move_nh.y = y;
                    move_nh.x = x;
                    if (diag_ok(&hero, &move_nh, &player)) {
                        legal++;
                        locy = y - (hero.y - 1);
                        locx = x - (hero.x - 1);
                    }
            }
        }
    }

    /* If we have 2 or more legal moves, make no change */
    if (legal != 1) {
        return;
    }

    runch = Moves[locy][locx];

    /*
     * For mazes, pretend like it is the beginning of a new run at each turn
     * in order to get the lighting correct.
     */
    if (levtype == MAZELEV) firstmove = TRUE;
    return;
}

/*
 * dip_it:
 *      Dip an object into a magic pool
 */

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

        tp = trap_at(hero.y,hero.x);
        if (tp == NULL || tp->tr_type != POOL) {
            msg("I see no shimmering pool here");
            return;
        }
        if (tp->tr_flags & ISGONE) {
            msg("This shimmering pool appears to have been used once already.");
            return;
        }

        /* It takes 3 movement periods to dip something */
        if (player.t_action != C_DIP) {
            if ((what = get_item(pack, "dip", ALL, FALSE, FALSE)) == NULL) {
                msg("");
                after = FALSE;
                return;
            }

            ob = OBJPTR(what);
            if (ob == cur_armor               || 
                ob == cur_misc[WEAR_BOOTS]    ||
                ob == cur_misc[WEAR_JEWEL]    ||
                ob == cur_misc[WEAR_GAUNTLET] ||
                ob == cur_misc[WEAR_CLOAK]    ||
                ob == cur_misc[WEAR_BRACERS]  ||
                ob == cur_misc[WEAR_NECKLACE] ||
                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]) {
                mpos = 0;
                msg("You'll have to take it off first.");
                return;
            }

            player.t_using = what;      /* Remember what it is */
            player.t_action = C_DIP;    /* We are dipping */
            player.t_no_move = 3 * movement(&player);
            return;
        }

        /* We have waited our time, let's dip it */
        what = player.t_using;
        player.t_using = NULL;
        player.t_action = A_NIL;

        ob = OBJPTR(what);

        tp->tr_flags |= ISGONE;
        if (ob != NULL) {
            wh = ob->o_which;
            ob->o_flags |= ISKNOW;
            i = rnd(100);
            if (ob->o_group != 0)
                ob->o_group = newgrp(); /* change the group */
            switch(ob->o_type) {
                case WEAPON:
                    if(i < 60) {                /* enchant weapon here */
                        if ((ob->o_flags & ISCURSED) == 0) {
                                ob->o_hplus += 1;
                                ob->o_dplus += 1;
                        }
                        else {          /* weapon was prev cursed here */
                                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 < 75) {   /* curse weapon here */
                        if ((ob->o_flags & ISCURSED) == 0) {
                                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);
                when ARMOR:
                    if (i < 60) {       /* enchant armor */
                        if((ob->o_flags & ISCURSED) == 0)
                            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) == 0)
                            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);
                when STICK: {
                    int j;
                    j = rnd(14) + 1;