view urogue/command.c @ 276:4573b355cdc1

UltraRogue: prevent bad array accesses in call(). Marking non-magic items caused segfaults because item_color was set to NULL. item_type could also be used as an out-of-bounds index. These problems have been fixed by only using these variables when the mark argument is false, in which case they are properly initialized. A fall-through case statement was also fixed.
author John "Elwin" Edwards
date Sun, 10 Sep 2017 21:04:22 -0400
parents 88bd51f231e7
children e52a8a7ad4c5
line wrap: on
line source

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

/*
    command()
        Process the user commands
*/

void
command(void)
{
    static char repcommand;     /* Command to repeat if we are repeating */
    static int fight_to_death; /* Flags if we are fighting to death     */
    static coord dir;          /* Last direction specified              */

    object  *obj;
    char     ch;
    int      ntimes = 1; /* Number of player moves */
    coord    nullcoord;

    nullcoord.x = nullcoord.y = 0;

    if (on(player, CANFLY) && rnd(2))
        ntimes++;

    if (on(player, ISHASTE))
        ntimes++;

    if (fighting && att_bonus())
        ntimes *= 2;

    if (on(player, ISSLOW))
    {
        if (player.t_turn != TRUE)
            ntimes--;

        player.t_turn ^= TRUE;
    }

    if (ntimes == 0)
        return;

    while (ntimes--)
    {
        moving = FALSE;

        /* If player is infested, take off a hit point */

        if (on(player, HASINFEST) && !is_wearing(R_HEALTH))
        {
            if ((pstats.s_hpt -= infest_dam) <= 0)
            {
                death(D_INFESTATION);
                return;
            }
        }

        look(after);

        if (!running)
            door_stop = FALSE;

        status(FALSE);
        wmove(cw, hero.y, hero.x);

        if (!((running || count) && jump))
            wrefresh(cw);   /* Draw screen */

        take = 0;
        after = TRUE;

        /*
         * Read command or continue run
         */

        if (!no_command)
        {
            if (fighting)
            {
                ch = (fight_to_death) ? 'F' : 'f';
            }
            else if (running)
            {
                /*
                 * If in a corridor, if we are at a turn with
                 * only one way to go, turn that way.
                 */

                if ((winat(hero.y, hero.x) == PASSAGE) && off(player, ISHUH) &&
                    (off(player, ISBLIND)))
                    switch (runch)
                    {
                        case 'h': corr_move(0, -1); break;
                        case 'j': corr_move(1, 0); break;
                        case 'k': corr_move(-1, 0); break;
                        case 'l': corr_move(0, 1); break;
                    }

                ch = runch;
            }
            else if (count)
                ch = repcommand;
            else
            {
                ch = readchar();

                if (mpos != 0 && !running)
                    msg("");    /* Erase message if its there */
            }
        }
        else
        {
            ch = '.';
            fighting = moving = FALSE;
        }

        if (no_command)
        {
            if (--no_command == 0)
                msg("You can move again.");
        }
        else
        {

            /*
             * check for prefixes
             */

            if (isdigit(ch))
            {
                count = 0;
                while (isdigit(ch))
                {
                    count = count * 10 + (ch - '0');
                    ch = readcharw(cw);
                }
                repcommand = ch;

                /*
                 * Preserve count for commands which can be
                 * repeated.
                 */

                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 'q':
                    case 'r':
                    case 's':
                    case 'm':
                    case 't':
                    case 'C':
                    case 'I':
                    case '.':
                    case 'z':
                    case 'p':
                        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':
                        runch = ch;
                        break;
                    case 'H':
                    case 'J':
                    case 'K':
                    case 'L':
                    case 'Y':
                    case 'U':
                    case 'B':
                    case 'N':
                        runch = (char) tolower(ch);
                        break;
                }

            /*
             * execute a command
             */

            if (count && !running)
                count--;

            switch(ch)
            {
                /*
                 * Movement and combat commands
                 */

                case 'h': do_move(0,-1); break;
                case 'j': do_move(1, 0);  break;
                case 'k': do_move(-1, 0); break;
                case 'l': do_move(0, 1); break;
                case 'y': do_move(-1, -1); break;
                case 'u': do_move(-1, 1); break;
                case 'b': do_move(1, -1); break;
                case 'n': do_move(1, 1); break;
                case 'H': do_run('h'); break;
                case 'J': do_run('j'); break;
                case 'K': do_run('k'); break;
                case 'L': do_run('l'); break;
                case 'Y': do_run('y'); break;
                case 'U': do_run('u'); break;
                case 'B': do_run('b'); break;
                case 'N': do_run('n'); break;
                case 'm':
                    moving = TRUE;
                    if (!get_dir())
                    {
                        after = FALSE;
                        break;
                    }
                    do_move(delta.y, delta.x);
                    break;
                case 'F':
                case 'f':
                    fight_to_death = (ch == 'F');
                    if (!fighting)
                    {
                        if (get_dir())
                        {
                            dir = delta;
                            beast = NULL;
                        }
                        else
                        {
                            after = FALSE;
                            break;
                        }
                    }
                    do_fight(dir, (ch == 'F') ? TRUE : FALSE);
                    break;
                case 't':
                    if (get_dir())
                        missile(delta.y, delta.x, get_item("throw", 0),
                            &player);
                    else
                        after = FALSE;

                    /*
                     * Informational commands - Do not do
                     * after daemons
                     */
                     break;

                case 0x7f:            /* sometime generated by */
                                      /* suspend/foreground    */
                case ESCAPE:
                case ' ':
                    after = FALSE;    /* do nothing */
                    break;
                case 'Q':
                    after = FALSE;
                    quit();
                    break;
                case 'i':
                    after = FALSE;
                    inventory(pack, '*');
                    break;
                case 'I':
                    after = FALSE;
                    inventory(pack, 0);
                    break;
                case '~':
                    after = FALSE;
                    next_exp_level(MESSAGE);
                    break;
                case '>':
                    after = FALSE;
                    d_level();
                    break;
                case '<':
                    after = FALSE;
                    u_level();
                    break;
                case '?':
                    after = FALSE;
                    help();
                    break;
                case '/':
                    after = FALSE;
                    identify();
                    break;
                case 'v':
                    after = FALSE;
                    msg("UltraRogue Version %s.", release);
                    break;
                case 'o':
                    after = FALSE;
                    option();
                    strcpy(fd_data[1].mi_name, fruit);
                    break;
                case 12:    /* ctrl-l */
                case 18:    /* ctrl-r */
                    after = FALSE;
                    clearok(cw, TRUE);
					wrefresh(cw);
                    break;
                case 16: /* ctrl-p */
                {
                    int decrement = FALSE;
                    after = FALSE;

                    if (mpos == 0)
                        decrement = TRUE;

                    msg_index = (msg_index + 9) % 10;
                    msg(msgbuf[msg_index]);
                    if (decrement)
                        msg_index = (msg_index + 9) % 10;
                }
                break;

                case 'S':
                    after = FALSE;
                    if (save_game())
                    {
                        wclear(cw);
                        wrefresh(cw);
                        endwin();
			printf("\n");
                        exit(0);
                    }
                    break;

                /*
                 * Other misc commands
                 */

                case '.':   break;   /* rest */
                case ',':   add_pack(NULL, NOMESSAGE); break;
                case 'q': quaff(&player, -1, ISNORMAL); break;
                case 'r': read_scroll(&player, -1, ISNORMAL); break;
                case 'd': drop(NULL); break;
                case '^': set_trap(&player, hero.y, hero.x); break;
                case 'c': incant(&player, nullcoord); break;
                case 'D': dip_it(); break;
                case 'e': eat(); break;
                case '=': listen(); break;
                case 'A': apply(); break;
                case 'w': wield(); break;
                case 'W': wear(); break;
                case 'T': take_off(); break;
                case 'P': ring_on(); break;
                case 'R': ring_off(); break;
                case 'p': prayer(); break;
                case 'C': call(FALSE); break;
                case 'M': call(TRUE); break;
                case 's': search(FALSE); break;

                /*
                 * Directional commands - get_dir sets delta
                 */
                case 20:    /* ctrl-t */
                    if (get_dir())
                        steal();
                    else
                        after = FALSE;
                    break;

                case 'z':
                    if (get_dir())
                        do_zap(&player, -1, ISNORMAL);
                    else
                        after = FALSE;
                    break;

                case 'a':
                    if (get_dir())
                        affect();
                    else
                        after = FALSE;
                    touchwin(cw);
                    break;

                /*
                 * wizard commands
                 */

#ifdef WIZARD
                case 0x17:    /* ctrl-w */
                    after = FALSE;

                    if (!wizard)
                    {
                        if (!canwizard)
                        {
                            msg("Illegal command '^W'.");
                            break;
                        }

                        if (passwd())
                        {
                            msg("Welcome, oh mighty wizard.");
                            wizard = waswizard = TRUE;
                        }
                        else
                        {
                            msg("Incorrect password.");
                            break;
                        }
                    }

                    msg("Wizard command: ");
                    mpos = 0;
                    ch = readchar();

                    switch (ch)
                    {
                        case 'v':
                            wiz_verbose = !wiz_verbose;
                            break;

                        case 'e':
                            wizard = FALSE;
                            msg("Not wizard any more.");
                            break;

                        case 's': activity(); break;
                        case 't': teleport(); break;
                        case 'm': overlay(mw, cw); break;
                        case 'f': overlay(stdscr, cw); break;
                        case 'i': inventory(lvl_obj, 0); break;
                        case 'c': buy_it('\0', ISNORMAL); break;
                        case 'I': whatis(NULL); break;
                        case 'F':
                             msg("food left: %d\tfood level: %d",
                                  food_left,foodlev);
                             break;
                        case 'M':
                            creat_mons(&player, get_monster_number("create"),
                                       MESSAGE);
                            break;

                        case 'r':
                            msg("rnd(4)%d, rnd(40)%d, rnd(100)%d",
                                 rnd(4), rnd(40), rnd(100));
                            break;

                        case 'C':
                            obj = get_object(pack, "charge", STICK, NULL);

                            if (obj != NULL)
                                obj->o_charges = 10000;

                            break;

                        case 'w':
                            obj = get_object(pack, "price", 0, NULL);

                            if (obj != NULL)
                                msg("Worth %d.", get_worth(obj));

                            break;

                        case 'g':
                            {
                                int tlev;

                                prbuf[0] = '\0';
                                msg("Which level? ");

                                if (get_string(prbuf, cw) == NORM)
                                {
                                    msg("");

                                    if ((tlev = atoi(prbuf)) < 1)
                                        msg("Illegal level.");
                                    else if (tlev > 3000)
                                    {
                                        levtype = THRONE;
                                        level = tlev - 3000;
                                    }
                                    else if (tlev > 2000)
                                    {
                                        levtype = MAZELEV;
                                        level = tlev - 2000;
                                    }
                                    else if (tlev > 1000)
                                    {
                                        levtype = POSTLEV;
                                        level = tlev - 1000;
                                    }
                                    else
                                    {
                                        levtype = NORMLEV;
                                        level = tlev;
                                    }

                                    new_level(levtype,0);
                                }
                            }
                            break;

                        case 'o':   make_omnipotent(); break;

                        case ESCAPE: /* Escape */
                            door_stop = FALSE;

                            count = 0;
                            after = FALSE;
                            break;

                        default:
                            msg("Illegal wizard command '%s', %d.",
                                unctrl(ch), ch);
                            count = 0;
                            break;

                    }

                break;
#endif

                default:
                    msg("Illegal command '%s', %d.",
                        unctrl(ch), ch);
                    count = 0;
                    after = FALSE;
                    break;
            }

            /*
             * turn off flags if no longer needed
             */
            if (!running)
                door_stop = FALSE;
        }

        /*
         * If he ran into something to take, let him pick it up.
         */
        if (take != 0)
            if (!moving)
                pick_up(take);
            else
                show_floor();
        if (!running)
            door_stop = FALSE;
    } /* end while */
}


void
do_after_effects(void)
{
    int i;

    /* Kick off the rest of the daemons and fuses */

    look(FALSE);
    do_daemons(AFTER);
    do_fuses(AFTER);

    /* Special abilities */

    if ((player.t_ctype == C_THIEF || player.t_ctype == C_ASSASIN ||
     player.t_ctype == C_NINJA || player.t_ctype == C_RANGER) &&
     (rnd(100) < (2 * pstats.s_dext + 5 * pstats.s_lvl)))
        search(TRUE);

    for (i = 0; i < ring_value(R_SEARCH); i++)
        search(FALSE);

    if (is_wearing(R_TELEPORT) && rnd(50) < 2)
    {
        teleport();

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

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

    /* accidents and general clumsiness */

    if (fighting && rnd(50) == 0)
    {
        msg("You become tired of this nonsense.");
        fighting = after = FALSE;
    }

    if (on(player, ISELECTRIC))
        electrificate();

    if (!fighting && (no_command == 0) && cur_weapon != NULL
        && rnd(on(player, STUMBLER) ? 399 : 9999) == 0
        && rnd(pstats.s_dext) <
        2 - hitweight() + (on(player, STUMBLER) ? 4 : 0))
    {
        msg("You trip and stumble over your weapon.");
        running = after = FALSE;

        if (rnd(8) == 0 && (pstats.s_hpt -= roll(1, 10)) <= 0)
        {
            msg("You break your neck and die.");
            death(D_FALL);
            return;
        }
        else if (cur_weapon->o_flags & ISPOISON && rnd(4) == 0)
        {
            msg("You are cut by your %s!",
                inv_name(cur_weapon, LOWERCASE));

            if (player.t_ctype != C_PALADIN
                && !(player.t_ctype == C_NINJA &&
                pstats.s_lvl > 12)
                && !save(VS_POISON))
            {
                if (pstats.s_hpt == 1)
                {
                    msg("You die from the poison in the cut.");
                    death(D_POISON);
                    return;
                }
                else
                {
                    msg("You feel very sick now.");
                    pstats.s_hpt /= 2;
                    chg_str(-2, FALSE, FALSE);
                }
            }
       }
   }

   /* Time to enforce weapon and armor restrictions */
   if (rnd(9999) == 0)
        if (((cur_weapon == NULL) ||
            (wield_ok(&player, cur_weapon, NOMESSAGE)))
            && ((cur_armor == NULL) ||
            (wear_ok(&player, cur_armor, NOMESSAGE))))
        {
            switch (player.t_ctype)
            {
                case C_CLERIC:
                case C_DRUID:
                case C_RANGER:
                case C_PALADIN:
                    if (rnd(luck) != 0)
                        /* You better have done
                         * little wrong */
                        goto bad_cleric;

                    msg("You are enraptured by the renewed "
                        "power of your god.");
                    break;

                case C_MAGICIAN:
                case C_ILLUSION:
                    msg("You become in tune with the universe.");
                    break;

                case C_THIEF:
                case C_NINJA:
                case C_ASSASIN:
                    msg("You become supernaly sensitive to your "
                        "surroundings.");
                    break;

                case C_FIGHTER:
                    msg("You catch your second wind.");
                    break;

                default:
                    msg("What a strange type you are!");
                    break;
           }
           pstats.s_hpt = max_stats.s_hpt += rnd(pstats.s_lvl) + 1;
            pstats.s_power = max_stats.s_power += rnd(pstats.s_lvl) + 1;
        }
        else
        {   /* he blew it - make him pay */

            int death_cause = 0;

            switch (player.t_ctype)
            {
                case C_CLERIC:
                case C_DRUID:
                case C_RANGER:
                case C_PALADIN:
            bad_cleric:
                    msg("Your god scourges you for your misdeeds.");
                    death_cause = D_GODWRATH;
                    break;

                case C_MAGICIAN:
                case C_ILLUSION:
                    msg("You short out your manna on the unfamiliar %s.",
                        (cur_armor != NULL ? "armor" : "weapon"));

                   death_cause = D_SPELLFUMBLE;
                   break;

                case C_THIEF:
                case C_NINJA:
                case C_ASSASIN:
                    msg("You trip and fall because of the unfamiliar %s.",
                        (cur_armor != NULL ? "armor" : "weapon"));
                    death_cause = D_CLUMSY;
                    break;

                case C_FIGHTER:
                    debug("Fighter getting raw deal?");
                   break;

                default:
                    msg("What a strange type you are!");
                    break;
            }

            aggravate();
            pstats.s_power /= 2;
            pstats.s_hpt /= 2;
            player.t_no_move++;

            if ((pstats.s_hpt -= rnd(pstats.s_lvl)) <= 0)
            {
                death(death_cause);
            }
        }

    if (rnd(500000) == 0)
    {
        new_level(THRONE,0);
        fighting = running = after = FALSE;
        command();
    }
}

void
make_omnipotent(void)
{
    int i;
    struct linked_list  *item;
    struct object *obj;

    for (i = 0; i < 20; i++)
        raise_level();

    max_stats.s_hpt += 1000;
    max_stats.s_power += 1000;
    pstats.s_hpt = max_stats.s_hpt;
    pstats.s_power = max_stats.s_power;
    max_stats.s_str = pstats.s_str = 25;
    max_stats.s_intel = pstats.s_intel = 25;
    max_stats.s_wisdom = pstats.s_wisdom = 25;
    max_stats.s_dext = pstats.s_dext = 25;
    max_stats.s_const = pstats.s_const = 25;

    if (cur_weapon == NULL || cur_weapon->o_which != CLAYMORE)
    {
        item = spec_item(WEAPON, CLAYMORE, 10, 10);
        cur_weapon = OBJPTR(item);
        cur_weapon->o_flags |= ISKNOW;
        add_pack(item, NOMESSAGE);
    }

    /* and a kill-o-zap stick */

    item = spec_item(STICK, WS_DISINTEGRATE, 10000, 0);
    obj = OBJPTR(item);
    obj->o_flags |= ISKNOW;
    know_items[TYP_STICK][WS_DISINTEGRATE] = TRUE;

    if (guess_items[TYP_STICK][WS_DISINTEGRATE])
    {
        ur_free(guess_items[TYP_STICK][WS_DISINTEGRATE]);
        guess_items[TYP_STICK][WS_DISINTEGRATE] = NULL;
    }

    add_pack(item, NOMESSAGE);