view xrogue/move.c @ 265:7fcb2f9f57e6

Mention UltraRogue in the top-level README.
author John "Elwin" Edwards
date Sun, 19 Feb 2017 19:54:17 -0500
parents f54901b9c39b
children e52a8a7ad4c5
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);
                /*
                 * 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;
                    if(i < 60) {                /* add charges */
                        ob->o_charges += j;