view urogue/misc.c @ 302:fa70bba6bb3f rel2021.03

Update the README.
author John "Elwin" Edwards
date Thu, 18 Mar 2021 20:53:49 -0400
parents c495a4f288c6
children e52a8a7ad4c5
line wrap: on
line source

/*
    misc.c - all sorts of miscellaneous routines
  
    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
    Copyright (C) 1985, 1986, 1992, 1993, 1995 Herb Chong
    All rights reserved.

    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
    All rights reserved.

    Based on "Rogue: Exploring the Dungeons of Doom"
    Copyright (C) 1980, 1981 Michael Toy, Ken Arnold and Glenn Wichman
    All rights reserved.

    See the file LICENSE.TXT for full copyright and licensing information.
*/

#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "rogue.h"

/*
    tr_name()
        print the name of a trap
*/

char *
tr_name(char ch, char *trname)
{
    const char *s = NULL;

    if (trname == NULL)
        return(" Your found an error in UltraRogue #100.");

    switch(ch)
    {
        case TRAPDOOR:
            s = "trapdoor.";
            break;
        case BEARTRAP:
            s = "beartrap.";
            break;
        case SLEEPTRAP:
            s = "sleeping gas trap.";
            break;
        case ARROWTRAP:
            s = "arrow trap.";
            break;
        case TELTRAP:
            s = "teleport trap.";
            break;
        case DARTTRAP:
            s = "dart trap.";
            break;
        case POOL:
            s = "shimmering pool.";
            break;
        case MAZETRAP:
            s = "maze entrance.";
            break;
        case FIRETRAP:
            s = "fire trap.";
            break;
        case POISONTRAP:
            s = "poison pool trap.";
            break;
        case LAIR:
            s = "monster lair.";
            break;
        case RUSTTRAP:
            s = "rust trap.";
            break;
        default:
            ;
    }

    sprintf(trname, "You found a %s.", s);

    return(trname);
}

/*
    look()
        A quick glance all around the player
*/

void
look(int wakeup)
{
    int     x, y;
    char   ch, och;
    int  oldx, oldy;
    int inpass, horizontal, vertical, do_light = FALSE, do_blank = FALSE;
    int passcount = 0;
    struct room *rp;
    int ey, ex;

    /* Are we moving vertically or horizontally? */

    if (runch == 'h' || runch == 'l')
        horizontal = TRUE;
    else
        horizontal = FALSE;

    if (runch == 'j' || runch == 'k')
        vertical = TRUE;
    else
        vertical = FALSE;

    getyx(cw, oldy, oldx);  /* Save current position */

    /*
     * Blank out the floor around our last position and check for moving
     * out of a corridor in a maze.
    */

    if (oldrp != NULL && (oldrp->r_flags & ISDARK) &&
        !(oldrp->r_flags & HASFIRE) && off(player, ISBLIND))
        do_blank = TRUE;

    for (x = player.t_oldpos.x - 1; x <= player.t_oldpos.x + 1; x++)
        for (y = player.t_oldpos.y - 1; y <= player.t_oldpos.y + 1;
                y++)
        {
            ch = show(y, x);

            if (do_blank && (y != hero.y || x != hero.x) && ch == FLOOR)
                mvwaddch(cw, y, x, ' ');

            /* Moving out of a corridor? */

            if (levtype == MAZELEV &&
                (ch != '|' && ch != '-') && /* Not a wall */
                ((vertical && x != player.t_oldpos.x &&
                y == player.t_oldpos.y) ||
                 (horizontal && y != player.t_oldpos.y &&
                x == player.t_oldpos.x)))
                do_light = TRUE;    /* Just came to a turn */
        }

    inpass = ((rp = roomin(hero)) == NULL);    /* Are we in a passage? */

    /* Are we coming out of a wall into a corridor in a maze? */
    och = show(player.t_oldpos.y, player.t_oldpos.x);
    ch = show(hero.y, hero.x);

    if (levtype == MAZELEV && (och == '|' || och == '-' ||
        och == SECRETDOOR) && (ch != '|' && ch != '-' && ch != SECRETDOOR))
    {
        do_light = off(player, ISBLIND);    /* Light it up if not blind */
    }

    /* Look around the player */

    ey = hero.y + 1;
    ex = hero.x + 1;

    for (x = hero.x - 1; x <= ex; x++)
        if (x >= 0 && x < COLS)
            for (y = hero.y - 1; y <= ey; y++)
            {
                if (y <= 0 || y >= LINES - 2)
                    continue;

                if (isalpha(mvwinch(mw, y, x)))
                {
                    struct linked_list  *it;
                    struct thing    *tp;

                    if (wakeup)
                        it = wake_monster(y, x);
                    else
                        it = find_mons(y, x);

                    if (it == NULL)
                        continue;

                    tp = THINGPTR(it);
                    tp->t_oldch = CCHAR( mvinch(y, x) );

                    if (isatrap(tp->t_oldch))
                    {
                        struct trap *trp = trap_at(y, x);

                        tp->t_oldch = (trp->tr_flags & ISFOUND) ? tp->t_oldch
                            : trp->tr_show;
                    }

                    if (tp->t_oldch == FLOOR &&
                        (rp->r_flags & ISDARK)
                        && !(rp->r_flags & HASFIRE) &&
                        off(player, ISBLIND))
                        tp->t_oldch = ' ';
                }

                /* Secret doors show as walls */

                if ((ch = show(y, x)) == SECRETDOOR)
                    ch = secretdoor(y, x);

                /*
                 * Don't show room walls if he is in a
                 * passage and check for maze turns
                */

                if (off(player, ISBLIND))
                {
                    if (y == hero.y && x == hero.x || (inpass && (ch == '-' ||
                        ch == '|')))
                        continue;

                    /* Are we at a crossroads in a maze? */

                    if (levtype == MAZELEV && (ch != '|' && ch != '-') &&
                        /* Not a wall */
                        ((vertical && x != hero.x && y == hero.y) ||
                         (horizontal && y != hero.y && x == hero.x)))
                        do_light = TRUE;
                        /* Just came to a turn */
                }
                else if (y != hero.y || x != hero.x)
                    continue;

                wmove(cw, y, x);
                waddch(cw, ch);

                if (door_stop && !firstmove && running)
                {
                    switch (runch)
                    {
                        case 'h':
                            if (x == ex)
                                continue;
                            break;
                        case 'j':
                            if (y == hero.y - 1)
                                continue;
                            break;
                        case 'k':
                            if (y == ey)
                                continue;
                            break;
                        case 'l':
                            if (x == hero.x - 1)
                                continue;
                            break;
                        case 'y':
                            if ((x + y) - (hero.x + hero.y) >= 1)
                                continue;
                            break;
                        case 'u':
                            if ((y - x) - (hero.y - hero.x) >= 1)
                                continue;
                            break;
                        case 'n':
                            if ((x + y) - (hero.x + hero.y) <= -1)
                                continue;
                            break;
                        case 'b':
                            if ((y - x) - (hero.y - hero.x) <= -1)
                                continue;
                            break;
                    }

                    switch (ch)
                    {
                        case DOOR:
                            if (x == hero.x || y == hero.y)
                                running = FALSE;
                            break;
                        case PASSAGE:
                            if (x == hero.x || y == hero.y)
                                passcount++;
                            break;
                        case FLOOR:

                        /*
                         * Stop by new passages in a
                         * maze (floor next to us)
                         */
                            if ((levtype == MAZELEV) &&
                                ((horizontal && x == hero.x && y != hero.y) ||
                                (vertical &&   y == hero.y && x != hero.x)))
                                running = FALSE;

                        case '|':
                        case '-':
                        case ' ':
                            break;

                        default:
                            running = FALSE;
                            break;
                    }
                }
            }

    if (door_stop && !firstmove && passcount > 1)
        running = FALSE;

    /*
     * Do we have to light up the area (just stepped into a new
     * corridor)?
     */

    if (do_light && wakeup &&   /* wakeup will be true on a normal move */
        !(rp->r_flags & ISDARK) &&  /* We have some light */
        !ce(hero, player.t_oldpos)) /* Don't do anything if we didn't move */
        light(&hero);

    mvwaddch(cw, hero.y, hero.x, PLAYER);
    wmove(cw, oldy, oldx);

    if (wakeup)
    {
        player.t_oldpos = hero; /* Don't change if we didn't move */
        oldrp = rp;
    }
}

/*
    secret_door()
        Figure out what a secret door looks like.
*/

char
secretdoor(int y, int x)
{
    struct room *rp;
    coord cp;

    cp.x = x;
    cp.y = y;

    if ((rp = roomin(cp)) != NULL)
    {
        if (y == rp->r_pos.y || y == rp->r_pos.y + rp->r_max.y - 1)
            return ('-');
        else
            return ('|');
    }
    return ('p');
}

/*
    find_obj()
        find the unclaimed object at y, x
*/

struct linked_list  *
find_obj(int y, int x)
{
    struct linked_list  *obj, *sobj;
    struct object   *op;

    sobj = lvl_obj;

    for (obj = sobj; obj != NULL; obj = next(obj))
    {
        op = OBJPTR(obj);

        if (op && op->o_pos.y == y && op->o_pos.x == x)
            return(obj);
    }

    return(NULL);
}

/*
    eat()
        He wants to eat something, so let him try
*/

void
eat(void)
{
    struct object   *obj;
    int amount;
    float scale = (float) (LINES * COLS) / (25.0F * 80.0F);

    if ((obj = get_object(pack, "eat", FOOD, NULL)) == NULL)
        return;

    switch (obj->o_which)
    {
        case FD_RATION:
            amount = (int)(scale * (HUNGERTIME + rnd(400) - 200));

            if (rnd(100) > 70)
            {
                msg("Yuk, this food tastes awful.");
                pstats.s_exp++;
                check_level();
            }
            else
                msg("Yum, that tasted good.");
            break;

        case FD_FRUIT:
            amount = (int)(scale * (200 + rnd(HUNGERTIME)));
            msg("My, that was a yummy %s.", fruit);
            break;

        case FD_CRAM:
            amount = (int)(scale * (rnd(HUNGERTIME / 2) + 600));
            msg("The cram tastes dry in your mouth.");
            break;

        case FD_CAKES:
            amount = (int)(scale * ((HUNGERTIME / 3) + rnd(600)));
            msg("Yum, the honey cakes tasted good.");
            break;

        case FD_LEMBA:
            amount = (int)(scale * ((HUNGERTIME / 2) + rnd(900)));
            quaff(&player, P_HEALING, ISNORMAL);
            break;

        case FD_MIRUVOR:
            amount = (int)(scale * ((HUNGERTIME / 3) + rnd(500)));
            quaff(&player, P_HEALING, ISNORMAL);
            quaff(&player, P_RESTORE, ISNORMAL);
            break;

        default:
            msg("What a strange thing to eat!");
            amount = (int)(scale * HUNGERTIME);
    }
    
    food_left += amount;

    if (obj->o_flags & ISBLESSED)
    {
        food_left += 2 * amount;
        msg("You have a tingling feeling in your mouth.");
    }
    else if (food_left > scale * STOMACHSIZE)
    {
        food_left = (int)(scale * STOMACHSIZE);
        msg("You feel satiated and too full to move.");
        no_command = HOLDTIME;
    }

    hungry_state = F_OK;
    updpack();

    if (obj == cur_weapon)
        cur_weapon = NULL;

    if (--obj->o_count <= 0) /* Remove this pack entry if last of food */
        discard_pack(obj);
}

/*
 * Used to modify the player's strength it keeps track of the highest it has
 * been, just in case
 */

void
chg_str(int amt, int both, int lost)
{
    int           ring_str; /* ring strengths */
    struct stats *ptr;      /* for speed */

    ptr = &pstats;

    ring_str = ring_value(R_ADDSTR) + (on(player, POWERSTR) ? 10 : 0) +
        (on(player, SUPERHERO) ? 10 : 0);

    ptr->s_str -= ring_str;
    ptr->s_str += amt;

    if (ptr->s_str < 3)
    {
        ptr->s_str = 3;
        lost = FALSE;
    }
    else if (ptr->s_str > 25)
        ptr->s_str = 25;

    if (both)
        max_stats.s_str = ptr->s_str;

    if (lost)
        lost_str -= amt;

    ptr->s_str += ring_str;

    if (ptr->s_str < 0)
        ptr->s_str = 0;

    updpack();
}

/*
 * Used to modify the player's dexterity it keeps track of the highest it has
 * been, just in case
 */

void
chg_dext(int amt, int both, int lost)
{
    int ring_dext;      /* ring strengths   */
    struct stats *ptr;  /* for speed        */

    ptr = &pstats;

    ring_dext = ring_value(R_ADDHIT) + (on(player, POWERDEXT) ? 10 : 0) +
        (on(player, SUPERHERO) ? 5 : 0);

    ptr->s_dext -= ring_dext;
    ptr->s_dext += amt;

    if (ptr->s_dext < 3)
    {
        ptr->s_dext = 3;
        lost = FALSE;
    }
    else if (ptr->s_dext > 25)
        ptr->s_dext = 25;

    if (both)
        max_stats.s_dext = ptr->s_dext;

    if (lost)
        lost_dext -= amt;

    ptr->s_dext += ring_dext;

    if (ptr->s_dext < 0)
        ptr->s_dext = 0;
}

/*
    add_haste()
        add a haste to the player
*/

void
add_haste(int blessed)
{
    short   hasttime;

    if (blessed)
        hasttime = 10;
    else
        hasttime = 6;

    if (on(player, ISSLOW))    /* Is person slow? */
    {
        extinguish_fuse(FUSE_NOSLOW);
        noslow(NULL);

        if (blessed)
            hasttime = 4;
        else
            return;
    }

    if (on(player, ISHASTE))
    {
        msg("You faint from exhaustion.");
        no_command += rnd(hasttime);
        lengthen_fuse(FUSE_NOHASTE, rnd(hasttime) + (roll(1, 4) * hasttime));
    }
    else
    {
        turn_on(player, ISHASTE);
        light_fuse(FUSE_NOHASTE, 0, roll(hasttime, hasttime), AFTER);
    }
}

/*
    aggravate()
        aggravate all the monsters on this level
*/

void
aggravate(void)
{
    struct linked_list *mi;
    struct thing *tp;

    for (mi = mlist; mi != NULL; mi = next(mi))
    {
        tp = THINGPTR(mi);
        chase_it(&tp->t_pos, &player);
    }
}

/*
    vowelstr()
        for printfs: if string starts with a vowel, return "n" for an "an"
*/

char *
vowelstr(char *str)
{
    switch (*str)
    {
        case 'a':
        case 'A':
        case 'e':
        case 'E':
        case 'i':
        case 'I':
        case 'o':
        case 'O':
        case 'u':
        case 'U':
            return "n";
        default:
            return "";
    }
}

/*
    is_object()
        see if the object is one of the currently used items
*/

int
is_current(struct object *obj)
{
    if (obj == NULL)
        return FALSE;

    if (obj == cur_armor || obj == cur_weapon ||
        obj == cur_ring[LEFT_1] || obj == cur_ring[LEFT_2] ||
        obj == cur_ring[LEFT_3] || obj == cur_ring[LEFT_4] ||
        obj == cur_ring[LEFT_5] ||
        obj == cur_ring[RIGHT_1] || obj == cur_ring[RIGHT_2] ||
        obj == cur_ring[RIGHT_3] || obj == cur_ring[RIGHT_4] ||
        obj == cur_ring[RIGHT_5]) {
        msg("That's already in use.");
        return TRUE;
    }

    return FALSE;
}

/*
    get_dir()
        set up the direction co_ordinate for use in varios "prefix" commands
*/

int
get_dir(void)
{
    char *prompt;
    int   gotit;

    prompt = "Which direction? ";
    msg(prompt);

    do
    {
        gotit = TRUE;

        switch (readchar())
        {
            case 'h':
            case 'H':
                delta.y = 0;
                delta.x = -1;
                break;

            case 'j':
            case 'J':
                delta.y = 1;
                delta.x = 0;
                break;

            case 'k':
            case 'K':
                delta.y = -1;
                delta.x = 0;
                break;

            case 'l':
            case 'L':
                delta.y = 0;
                delta.x = 1;
                break;

            case 'y':
            case 'Y':
                delta.y = -1;
                delta.x = -1;
                break;

            case 'u':
            case 'U':
                delta.y = -1;
                delta.x = 1;
                break;
				
            case 'b':
            case 'B':
                delta.y = 1;
                delta.x = -1;
                break;
				
            case 'n':
            case 'N':
                delta.y = 1;
                delta.x = 1;
                break;
				
            case  ESCAPE:
                return FALSE;

            default:
                mpos = 0;
                msg(prompt);
                gotit = FALSE;
        }
    }
    while(!gotit);

    if (on(player, ISHUH) && rnd(100) > 80)
        do
        {
            delta.y = rnd(3) - 1;
            delta.x = rnd(3) - 1;
        }
        while (delta.y == 0 && delta.x == 0);

    mpos = 0;

    return(TRUE);
}

/*
    is_wearing()
        is the hero wearing a particular ring
*/

int
is_wearing(int type)
{
#define ISRING(h, r) (cur_ring[h] != NULL && cur_ring[h]->o_which == r)

    return(
        ISRING(LEFT_1,  type) || ISRING(LEFT_2, type) ||
        ISRING(LEFT_3,  type) || ISRING(LEFT_4, type) ||
        ISRING(LEFT_5,  type) ||
        ISRING(RIGHT_1, type) || ISRING(RIGHT_2, type) ||
        ISRING(RIGHT_3, type) || ISRING(RIGHT_4, type) ||
        ISRING(RIGHT_5, type)  );
}

/*
    maze_view()
        Returns true if the player can see the specified location
        within the confines of a maze (within one column or row)
*/

int
maze_view(int y, int x)
{
    int start, goal, delt, ycheck = 0, xcheck = 0, absy, absx;
    int row;

    /* Get the absolute value of y and x differences */

    absy = hero.y - y;
    absx = hero.x - x;

    if (absy < 0)
        absy = -absy;

    if (absx < 0)
        absx = -absx;

    /* Must be within one row or column */

    if (absy > 1 && absx > 1)
        return(FALSE);

    if (absy <= 1)      /* Go along row */
    {
        start = hero.x;
        goal = x;
        row = TRUE;
        ycheck = hero.y;
    }
    else            /* Go along column */
    {
        start = hero.y;
        goal = y;
        row = FALSE;
        xcheck = hero.x;
    }

    if (start <= goal)
        delt = 1;
    else
        delt = -1;

    while (start != goal)
    {
        if (row)
            xcheck = start;
        else
            ycheck = start;

        switch(CCHAR(winat(ycheck, xcheck)))
        {
            case '|':
            case '-':
            case WALL:
            case DOOR:
            case SECRETDOOR:
                return(FALSE);

            default:
                break;
        }
        start += delt;
    }

    return(TRUE);
}

/*
    listen()
        listen for monsters less than 5 units away
*/

void
listen(void)
{
    struct linked_list  *item;
    struct thing    *tp;
    int thief_bonus = -50;
    int mcount = 0;

    if (player.t_ctype == C_THIEF)
        thief_bonus = 10;

    for (item = mlist; item != NULL; item = next(item))
    {
        tp = THINGPTR(item);

        if (DISTANCE(hero, tp->t_pos) < 81
            && rnd(70) < (thief_bonus + 4 * pstats.s_dext +
            6 * pstats.s_lvl))
        {
            msg("You hear a%s %s nearby.",
                vowelstr(monsters[tp->t_index].m_name),
                monsters[tp->t_index].m_name);
            mcount++;
        }
    }

    if (mcount == 0)
        msg("You hear nothing.");
}

/*
 * nothing_message - print out "Nothing <adverb> happens."
 */

static const char *nothings[] =
{
    "",
    "unusual ",
    "seems to ",
    "at all ",
    "really ",
    "noticeable ",
    "different ",
    "strange ",
    "wierd ",
    "bizzare ",
    "wonky ",
    ""
};

void
nothing_message(int flags)
{
    int adverb = rnd(sizeof(nothings) / sizeof(char *));

    NOOP(flags);   

    msg("Nothing %shappens.", nothings[adverb]);
}

/*
    feel_message()
        print out "You feel <description>."
*/

void
feel_message(void)
{
    char *charp;

    switch (rnd(25))
    {
        case 1:  charp = "bad";      break;
        case 2:  charp = "hurt";     break;
        case 3:  charp = "sick";     break;
        case 4:  charp = "faint";    break;
        case 5:  charp = "yucky";    break;
        case 6:  charp = "wonky";    break;
        case 7:  charp = "wierd";    break;