view xrogue/command.c @ 140:856017d63519

xrogue: don't segfault when backstabbing while empty-handed. The code for backstabbing checked the weapon's properties without making sure it was not NULL.
author John "Elwin" Edwards
date Tue, 05 May 2015 12:12:20 -0400
parents 0d151573bdb0
children 9aa9b9a2e159
line wrap: on
line source

/*
    command.c  -  Read and execute the user 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 <signal.h>
#include <stdlib.h>
#include <string.h>
#include "mach_dep.h"
#include "rogue.h"

/*
 * command:
 *      Process the user commands
 */

command()
{
    unsigned int ch;
    struct linked_list *item;
    unsigned int countch = 0, direction = 0, newcount = FALSE;
    int segment = 1;
    int monst_limit, monst_current;

    monst_limit = monst_current = 1;
    while (playing) {
        /*
         * Let the daemons start up, but only do them once a round
         * (round = 10 segments).
         */
        if (segment >= 10) {
            do_daemons(BEFORE);
            do_fuses(BEFORE);
        }

        after = TRUE;
        do {
            /* One more tick of the clock. */
            if (segment >= 10 && after && (++turns % DAYLENGTH) == 0) {
                daytime ^= TRUE;
                if (levtype == OUTSIDE) {
                    if (daytime) msg("A bright star flares above the horizon.");
                    else msg("The bright star travels beyond the horizon.");
                }
                light(&hero);
            }

            /*
             * Don't bother with these updates unless the player's going
             * to do something.
             */
            if (player.t_action == A_NIL && player.t_no_move <= 1) {
                look(after, FALSE);
                lastscore = purse;
                wmove(cw, hero.y, hero.x);
                if (!((running || count) && jump)) {
                    status(FALSE);
                }
            }

            /* Draw the screen */
            if (!((running || count) && jump)) {
                wmove(cw, hero.y, hero.x);
                draw(cw);
            }

            after = TRUE;

            /*
             * Read command or continue run
             */
            if (--player.t_no_move <= 0) {
                take = 0;               /* Nothing here to start with */
                player.t_no_move = 0;   /* Be sure we don't go too negative */
                if (!running) door_stop = FALSE;

                /* Was the player being held? */
                if (player.t_action == A_FREEZE) {
                    player.t_action = A_NIL;
                    msg("You can move again.");
                }

                if (player.t_action != A_NIL) ch = player.t_action;
                else if (running) {
                    char scratch;

                    /* If in a corridor or maze, if we are at a turn with
                     * only one way to go, turn that way.
                     */
                    scratch = winat(hero.y, hero.x);
                    if ((scratch==PASSAGE||scratch==DOOR||levtype==MAZELEV)  &&
                        off(player, ISHUH)                                   && 
                        off(player, ISBLIND)) {
                        int y, x;
                        if (getdelta(runch, &y, &x) == TRUE) {
                            corr_move(y, x);
                        }
                    }
                    ch = runch;
                }
                else if (count) ch = countch;
                else {
                    ch = wgetch(cw);
                    if (mpos != 0 && !running)  /* Erase message if its there */
                        msg("");
                }

                /*
                 * check for prefixes
                 */
                if (isascii(ch) && isdigit(ch))
                {
                    count = 0;
                    newcount = TRUE;
                    while (isascii(ch) && isdigit(ch))
                    {
                        count = count * 10 + (ch - '0');
                        ch = wgetch(cw);
                    }
                    countch = ch;
                    /*
                     * turn off count for commands which don't make sense
                     * to repeat
                     */
                    switch (ch) {
                        case 'h': case 'j': case 'k': case 'l':
                        case 'y': case 'u': case 'b': case 'n':
                        case 'H': case 'J': case 'K': case 'L':
                        case 'Y': case 'U': case 'B': case 'N':
                        case C_SEARCH: case '.':
                            break;
                        default:
                            count = 0;
                    }
                }

                /* Save current direction */
                if (!running) { /* If running, it is already saved */
                    switch (ch) {
                        case 'h': case 'j': case 'k': case 'l':
                        case 'y': case 'u': case 'b': case 'n':
                        case 'H': case 'J': case 'K': case 'L':
                        case 'Y': case 'U': case 'B': case 'N':
                            runch = tolower(ch);
                    }
                }

                /* Perform the action */
                switch (ch) {
                    case 'f':
                        if (!on(player, ISBLIND))
                        {
                            door_stop = TRUE;
                            firstmove = TRUE;
                        }
                        if (count && !newcount)
                            ch = direction;
                        else
                            ch = wgetch(cw);
                        switch (ch)
                        {
                            case 'h': case 'j': case 'k': case 'l':
                            case 'y': case 'u': case 'b': case 'n':
                                ch = toupper(ch);
                        }
                        direction = ch;
                }
                newcount = FALSE;

                /*
                 * execute a command
                 */
                if (count && !running)
                    count--;

                switch (ch) {
                    case '!' : shell();
                    case KEY_LEFT       : do_move(0, -1);
                    when KEY_DOWN       : do_move(1, 0);
                    when KEY_UP         : do_move(-1, 0);
                    when KEY_RIGHT      : do_move(0, 1);
                    when KEY_HOME       : do_move(-1, -1);
                    when KEY_A1         : do_move(-1, -1);
                    when KEY_PPAGE      : do_move(-1, 1);
                    when KEY_A3         : do_move(-1, 1);
                    when KEY_END         : do_move(1, -1);
                    when KEY_C1         : do_move(1, -1);
                    when KEY_NPAGE      : do_move(1, 1);
                    when KEY_C3         : do_move(1, 1);
#ifdef CTL_RIGHT
                    when CTL_RIGHT      : do_run('l');
                    when CTL_LEFT       : do_run('h');
                    when CTL_UP         : do_run('k');
                    when CTL_DOWN       : do_run('j');
                    when CTL_HOME       : do_run('y');
                    when CTL_PGUP       : do_run('u');
                    when CTL_END        : do_run('b');
                    when CTL_PGDN       : do_run('n');
#endif
                    when 'h' : do_move(0, -1);
                    when 'j' : do_move(1, 0);
                    when 'k' : do_move(-1, 0);
                    when 'l' : do_move(0, 1);
                    when 'y' : do_move(-1, -1);
                    when 'u' : do_move(-1, 1);
                    when 'b' : do_move(1, -1);
                    when 'n' : do_move(1, 1);
                    when 'H' : do_run('h');
                    when 'J' : do_run('j');
                    when 'K' : do_run('k');
                    when 'L' : do_run('l');
                    when 'Y' : do_run('y');
                    when 'U' : do_run('u');
                    when 'B' : do_run('b');
                    when 'N' : do_run('n');
                    when A_ATTACK:
                        /* Is our attackee still there? */
                        if (isalpha(winat(player.t_newpos.y,
                                          player.t_newpos.x))) {
                            /* Our friend is still here */
                            player.t_action = A_NIL;
                            fight(&player.t_newpos, cur_weapon, FALSE);
                        }
                        else {  /* Our monster has moved */
                            player.t_action = A_NIL;
                        }
                    when A_PICKUP:
                        player.t_action = A_NIL;
                        if (add_pack((struct linked_list *)NULL, FALSE)) {
                            char tch;
                            tch = mvwinch(stdscr, hero.y, hero.x);
                            if (tch != FLOOR && tch != PASSAGE) {
                                player.t_action = A_PICKUP; /*get more */
                                player.t_no_move += 2 * movement(&player);
                            }
                        }
                    when A_THROW:
                        if (player.t_action == A_NIL) {
                            item = get_item(pack, "throw", ALL, FALSE, FALSE);
                            if (item != NULL && get_dir(&player.t_newpos)) {
                                player.t_action = A_THROW;
                                player.t_using = item;
                                player.t_no_move = 2 * movement(&player);
                            }
                            else
                                after = FALSE;
                        }
                        else {
                            missile(player.t_newpos.y, player.t_newpos.x, 
                                    player.t_using, &player);
                            player.t_action = A_NIL;
                            player.t_using = 0;
                        }
                    when 'a' :
                        if (player.t_action == A_NIL) {
                            if (get_dir(&player.t_newpos)) {
                                player.t_action = 'a';
                                player.t_no_move = 1 + movement(&player);
                            }
                            else
                                after = FALSE;
                        }
                        else {
                            affect();
                            player.t_action = A_NIL;
                        }
                    when 'A' : choose_qst();  
                    when 'F' : /* frighten a monster */
                        if (player.t_action == A_NIL) {
                            player.t_action = 'F';
                            player.t_no_move = 2*movement(&player);
                        }
                        else {
                after = FALSE;
                            player.t_action = A_NIL;
                            fright();
                        }
                    when 'g' : /* Give command: give slime-molds to monsters */
                        if (player.t_action == A_NIL) {
                            player.t_action = 'g';
                            player.t_no_move = 2*movement(&player);
                        }
                        else {
                after = FALSE;
                            player.t_action = A_NIL;
                            give();
                        }
                    when 'G' :
                        if (player.t_action == A_NIL) {
                            player.t_action = 'G';
                            player.t_no_move = movement(&player);
                        }
                        else {
                            player.t_action = A_NIL;
                            gsense();
                        }
                    when 'i' : after = FALSE; inventory(pack, ALL);
                    when 'I' : after = FALSE; picky_inven();
                    when 'm' : nameitem((struct linked_list *)NULL, TRUE);
                    when 'o' : option();
                    when 'O' : msg("Charactor type: %s    Quest item: %s", char_class[char_type].name, rel_magic[quest_item].mi_name);
                    when ',' :
                    case 'P' :
                        if (levtype != POSTLEV) {
                            /* We charge 2 movement units per item */
                            player.t_no_move =
                                2 * grab(hero.y, hero.x) * movement(&player);
                        }
                        else {
                            /* Let's quote the wise guy a price */
                            buy_it();
                            after = FALSE;
                        }
                    when 'Q' : after = FALSE; quit(0);
                    when 'S' : 
                        after = FALSE;
                        if (save_game())
                            exit_game(EXIT_CLS | EXIT_ENDWIN);
                    when 'v' : after = FALSE;
                               msg("Advanced xrogue, Version %s  ", release);
                    when 'X' :  /* trap sense */
            after = FALSE;
                        if (player.t_action == A_NIL) {
                            player.t_action = 'X';
                            player.t_no_move = movement(&player);
                        }
                        else {
                            xsense();
                            player.t_action = A_NIL;
                        }
                    when '.' :
                        player.t_no_move = movement(&player);  /* Rest */
                        player.t_action = A_NIL;
                    when ' ' : after = FALSE;   /* Do Nothing */
                    when '>' : after = FALSE; d_level();
                    when '<' : after = FALSE; u_level();
                    when '=' : after = FALSE; display();
                    when '?' : after = FALSE; help();

            /* no character descriptions yet until updated (help.c) */
            /* when '\\' : after = FALSE; ident_hero(); */
            when '\\' : msg("Charon (the Boatman) looks at you... ");

                    when '/' : after = FALSE; identify(NULL);
                    when C_COUNT : count_gold();
                    when C_DIP : dip_it();
                    when C_DROP : player.t_action = C_DROP; 
                                  drop((struct linked_list *)NULL);
                    when C_EAT : eat();
                    when C_QUAFF : quaff(-1, NULL, NULL, TRUE);
                    when C_READ : read_scroll(-1, NULL, TRUE);
                    when C_SETTRAP : set_trap(&player, hero.y, hero.x);
                    when C_SEARCH :
                        if (player.t_action == A_NIL) {
                            player.t_action = C_SEARCH;
                            player.t_no_move = 2 + movement(&player);
                        }
                        else {
                            search(FALSE, FALSE);
                            player.t_action = A_NIL;
                        }
                    when C_TAKEOFF : take_off();
                    when C_USE : use_mm(-1);
                    when C_WEAR : wear();
                    when C_WIELD : wield();
                    when C_ZAP : if (!player_zap(NULL, FALSE)) after=FALSE;
                    when C_CAST : cast();
                    when C_CHANT : chant();
                    when C_PRAY : pray();
                    when CTRL('B') : msg("Current score: %d",
                    pstats.s_exp + (long) purse);
                    when CTRL('E') : msg("Current food level: %d(2000)",
                    food_left);
                    when CTRL('L') : after = FALSE; clearok(curscr, TRUE);
                                    touchwin(cw);
                    when CTRL('N') : nameit();
                    when CTRL('O') : after = FALSE; opt_player();
                    when CTRL('R') : after = FALSE; msg(huh);
                    when CTRL('T') :
                        if (player.t_action == A_NIL) {
                            if (get_dir(&player.t_newpos)) {
                                player.t_action = CTRL('T');
                                player.t_no_move = 2 * movement(&player);
                            }
                            else
                                after = FALSE;
                        }
                        else {
                            steal();
                            player.t_action = A_NIL;
                        }
                    when ESC :  /* Escape */
                        door_stop = FALSE;
                        count = 0;
                        after = FALSE;
                    when '#':
                        if (levtype == POSTLEV)         /* buy something */
                            buy_it();
                        after = FALSE;
                    when '$':
                        if (levtype == POSTLEV)         /* price something */
                            price_it();
                        after = FALSE;
                    when '%':
                        if (levtype == POSTLEV)         /* sell something */
                            sell_it();
                        after = FALSE;
            when '+':   /* instant karma! */
            switch (rnd(100)) {
                case 0:  msg("You waste some time. ");
                when 5:  msg("An oak tree in the garden. ");
                when 10: msg("Character is what you become in the dark. ");
                when 15: msg("May you live all the days of your life. ");
                when 20: msg("A hero is no braver than an ordinary man, but he is brave five minutes longer. ");
                when 25: msg("Get down! ");
                when 30: msg("Go back to sleep. ");
                when 35: msg("Be here now. ");
                when 40: msg("Choose the rock that feels right to you. ");
                when 45: msg("Wait... ");
                when 50: msg("You take a break (yawn)... ");
                when 55: msg("Without danger there is no pleasure. ");
                when 60: msg("Define meaningless? ");
                when 65: msg("Don't push your luck! ");
                when 70: msg("Gung ho. ");
                when 75: msg("You are inside a computer. ");
                when 80: msg("Directive is now required... ");
                when 85: msg("Charon (the Boatman) awaits you... ");
                when 95: msg(nothing);
                otherwise: msg("");
            }
            after = FALSE;
                    when CTRL('P') :
#ifdef WIZARD
                        after = FALSE;
                        if (wizard)
                        {
                            wizard = FALSE;
                            trader = 0;
                            msg("Not wizard any more");
                        }
                        else
                        {
                            if (waswizard || passwd())
                            {
                                msg("Welcome, O Mighty Wizard! ");
                                wizard = waswizard = TRUE;
                            }
                            else
                                msg("Sorry");
                        }
#else
                        msg("Sorry");
#endif

                    otherwise :
                        after = FALSE;
                        if (wizard) switch (ch) {
                            case 'M' : create_obj(TRUE, 0, 0);
                            when 'V' : msg("vlevel = %d  turns = %d",
                                           vlevel, turns);
                            when CTRL('A') : activity();
                            when CTRL('C') : do_teleport();
                            when CTRL('D') : level++;
                                           take_with();
                                           new_level(NORMLEV);
                            when CTRL('F') : overlay(stdscr,cw);
                            when CTRL('G') :
                            {
                                item=get_item(pack,"charge",STICK,FALSE,FALSE);
                                if (item != NULL) {
                                    (OBJPTR(item))->o_charges=10000;
                                }
                            }
                            when CTRL('H') :
                            {
                                register int i, j;
                                register struct object *obj;

                                for (i = 0; i < 9; i++)
                                    raise_level();
                                /*
                                 * Give the rogue a sword 
                                 */
                                if (cur_weapon==NULL || cur_weapon->o_type !=
                                    RELIC) {
                                if (player.t_ctype == C_THIEF   ||
                                    player.t_ctype == C_ASSASSIN ||
                                    player.t_ctype == C_MONK)
                                      item = spec_item(WEAPON, BASWORD, 20, 20);
                                else
                                      item = spec_item(WEAPON,TWOSWORD, 20, 20);
                                    if (add_pack(item, TRUE))
                                    {
                                        cur_weapon = OBJPTR(item);
                                        (OBJPTR(item))->o_flags |= (ISKNOW|ISPROT);
                                    }
                                    else
                                        o_discard(item);
                                /*
                                 * And his suit of armor
                                 */
                                if (player.t_ctype == C_THIEF   ||
                                    player.t_ctype == C_ASSASSIN ||
                                    player.t_ctype == C_MONK)
                                      j = PADDED_ARMOR;
                                else
                                      j = PLATE_ARMOR;
                                    item = spec_item(ARMOR, j, 20, 0);
                                    obj = OBJPTR(item);
                                    obj->o_flags |= (ISKNOW | ISPROT);
                                    obj->o_weight = armors[j].a_wght;
                                    if (add_pack(item, TRUE))
                                        cur_armor = obj;
                                    else
                                        o_discard(item);
                                }
                                purse += 20000;
                            }
                            when CTRL('I') : inventory(lvl_obj, ALL);
                            when CTRL('J') : teleport();
                            when CTRL('K') : whatis((struct linked_list *)NULL);
                            when CTRL('W') : wanderer();
                            when CTRL('X') : overlay(mw,cw);
                            when CTRL('Y') : msg("food left: %d\tfood level: %d", 
                                                    food_left, foodlev);
                            otherwise :
                                msg("Illegal wizard command '%s'.", unctrl(ch));
                                count = 0;
                        }
                        else
                        {
                            msg("Illegal command '%s'.", unctrl(ch));
                            count = 0;
                            after = FALSE;
                        }
                }

                /*
                 * If he ran into something to take, let him pick it up.
                 * unless it's a trading post
                 */
                if (auto_pickup && take != 0 && levtype != POSTLEV) {
                    /* get ready to pick it up */
                    player.t_action = A_PICKUP;
                    player.t_no_move += 2 * movement(&player);
                }
            }

            /* If he was fighting, let's stop (for now) */
            if (player.t_quiet < 0) player.t_quiet = 0;

            if (!running)
                door_stop = FALSE;

            if (after && segment >= 10) {
                /*
                 * Kick off the rest if the daemons and fuses
                 */

                /* 
                 * If player is infested, take off a hit point 
                 */
                if (on(player, HASINFEST)) {
            pstats.s_hpt -= infest_dam;
                    if (pstats.s_hpt == 50 || pstats.s_hpt == 25)
            msg("You feel yourself withering away... ");
                    if (pstats.s_hpt < 1) {
            msg("You die a festering mass.  --More--");
            wait_for(' ');
            pstats.s_hpt = -1;
            death(D_INFESTATION);
            }
                }

                /*
                 * The eye of Vecna is a constant drain on the player
                 */
                if (cur_relic[EYE_VECNA]) {
            pstats.s_hpt -= 1;
                    if (pstats.s_hpt == 50 || pstats.s_hpt == 25)
            msg("You feel Vecna's eye looking about. ");
                    if (pstats.s_hpt <= 10 && pstats.s_hpt >= 3)
            msg("Vecna's eye moves about very quickly. ");
                    if (pstats.s_hpt < 1) {
            msg("Vecna's curse is upon you!  --More--");
            wait_for(' ');
            pstats.s_hpt = -1;
            death(D_RELIC);
            }
                }

                /* 
                 * if player has body rot then take off three hits 
                 */
                if (on(player, DOROT)) {
             pstats.s_hpt -= rnd(3)+1;
                     if (pstats.s_hpt == 50 || pstats.s_hpt == 25) 
             msg("Something really begins to stink and smell! ");
                     if (pstats.s_hpt < 1) {
             msg("You keel over with rot.  --More--");
             wait_for(' ');
             pstats.s_hpt = -1;
             death(D_ROT);
             }
                }
                do_daemons(AFTER);
                do_fuses(AFTER);
            }
        } while (after == FALSE);

        /* Make the monsters go */
        if (--monst_current <= 0)
            monst_current = monst_limit = runners(monst_limit);

        if (++segment > 10) segment = 1;
        reap(); /* bury all the dead monsters */
    }
}

/*
 * display
 *      tell the player what is at a certain coordinates assuming
 *      it can be seen.
 */
display()
{
    coord c;
    struct linked_list *item;
    struct thing *tp;
    int what;

    msg("What do you want to display (* for help)?");
    c = get_coordinates();
    mpos = 0;
    if (!cansee(c.y, c.x)) {
        msg("You can't see what is there.");
        return;
    }
    what = mvwinch(cw, c.y, c.x);
    if (isalpha(what)) {
        item = find_mons(c.y, c.x);
        tp = THINGPTR(item);
        msg("%s", monster_name(tp));
        return;
    }
    if ((item = find_obj(c.y, c.x)) != NULL) {
        msg("%s", inv_name(OBJPTR(item), FALSE));
        return;
    }
    identify(what);
}

/*
 * quit:
 *      Have player make certain, then exit.
 */

/*UNUSED*/
void
quit(sig)
int sig;
{
    register int oy, ox;

	NOOP(sig);

    /*
     * Reset the signal in case we got here via an interrupt
     */

    if ((VOID(*)())signal(SIGINT, quit) != (VOID(*)())quit)
        mpos = 0;

    getyx(cw, oy, ox);
    if (level < 1) {    /* if not down in the dungeon proper; exit the game */
        wclear(hw);
        wmove(hw, lines-1, 0);
        draw(hw);
        wmove(hw, 12, 30);
        wprintw(hw, "Good-bye!");
        draw(hw);
        exit_game(EXIT_ENDWIN);
    }
    msg("Really quit? <yes or no> ");   /* otherwise ask about quitting */
    draw(cw);
    prbuf[0] = '\0';
    if ((get_str(prbuf, msgw) == NORM) && strcmp(prbuf, "yes") == 0) {
        clear();
        move(lines-1, 0);
        draw(stdscr);
        writelog(pstats.s_exp + (long) purse, CHICKEN, 0);
        score(pstats.s_exp + (long) purse, CHICKEN, 0);
        exit_game(EXIT_ENDWIN);
    }
    else {
        signal(SIGINT, quit);
        wmove(msgw, 0, 0);
        wclrtoeol(msgw);
        draw(msgw);
        status(FALSE);
        wmove(cw, oy, ox);
        draw(cw);
        mpos = 0;
        count = 0;
        running = FALSE;
    }
}

/*
 * bugkill:
 *      killed by a program bug instead of voluntarily.
 */

bugkill(sig)
int sig;
{
    signal(sig, quit);      /* If we get it again, give up */