/* 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 #include #include #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 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 ." */ 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; case 8: charp = "queasy"; break; case 9: charp = "wounded"; break; case 11: charp = "unusual"; break; case 12: charp = "no pain"; break; case 13: charp = "strange"; break; case 14: charp = "noticable"; break; case 15: charp = "bizzare"; break; case 16: charp = "distressed";break; case 17: charp = "different"; break; case 18: charp = "a touch of ague"; break; case 19: charp = "a migrane coming on"; break; case 20: charp = "Excedrin headache #666"; break; case 21: charp = "a disturbance in the force"; break; case 22: charp = "like someone dropped a house on you"; break; case 23: charp = "as if every nerve in your body is on fire"; break; case 24: charp = "like thousands of red-hot army ants are crawling under your skin"; break; default: charp = "ill"; break; } msg("You feel %s.", charp); } /* const_bonus() Hit point adjustment for changing levels */ int const_bonus(void) { int ret_val = -2; if (pstats.s_const > 12) ret_val = pstats.s_const - 12; else if (pstats.s_const > 6) ret_val = 0; else if (pstats.s_const > 3) ret_val = -1; return(ret_val); } /* int_wis_bonus() Spell point adjustment for changing levels */ int int_wis_bonus(void) { int ret_val = -2; int casters_stat; switch (player.t_ctype) { case C_PALADIN: case C_CLERIC: casters_stat = pstats.s_wisdom; break; case C_RANGER: case C_DRUID: casters_stat = pstats.s_wisdom; break; case C_MAGICIAN: casters_stat = pstats.s_intel; break; case C_ILLUSION: casters_stat = pstats.s_intel; break; default: if (is_wearing(R_WIZARD)) casters_stat = pstats.s_intel; else if (is_wearing(R_PIETY)) casters_stat = pstats.s_wisdom; else casters_stat = (rnd(2) ? pstats.s_wisdom : pstats.s_intel); } if (casters_stat > 12) ret_val = casters_stat - 12; else if (casters_stat > 6) ret_val = 0; else if (casters_stat > 3) ret_val = -1; return(ret_val); } void electrificate(void) { int affect_dist = 4 + player.t_stats.s_lvl / 4; struct linked_list *item, *nitem; for (item = mlist; item != NULL; item = nitem) { struct thing *tp = THINGPTR(item); char *mname = monsters[tp->t_index].m_name; nitem = next(item); if (DISTANCE(tp->t_pos, hero) < affect_dist) { int damage = roll(2, player.t_stats.s_lvl); debug("Charge does %d (%d)", damage, tp->t_stats.s_hpt - damage); if (on(*tp, NOBOLT)) continue; if ((tp->t_stats.s_hpt -= damage) <= 0) { msg("The %s is killed by an electric shock.", mname); killed(&player, item, NOMESSAGE, POINTS); continue; } if (rnd(tp->t_stats.s_intel / 5) == 0) { turn_on(*tp, ISFLEE); msg("The %s is shocked by electricity.", mname); } else msg("The %s is zapped by your electricity.", mname); summon_help(tp, NOFORCE); turn_off(*tp, ISFRIENDLY); turn_off(*tp, ISCHARMED); turn_on(*tp, ISRUN); turn_off(*tp, ISDISGUISE); chase_it(&tp->t_pos, &player); fighting = after = running = FALSE; } } } /* feed_me - Print out interesting messages about food consumption */ static char *f_hungry[] = { "want a cookie", "feel like a snack", "feel like some fruit", "start having the munchies", "are starting to get hungry" }; static char *f_weak[] = { "are really hungry", "could eat a horse", "want some food - now", "are starting to feel weak", "feel a gnawing in your stomach", "are even willing to eat up cram", "feel lightheaded from not eating" }; static char *f_faint[] = { "get dizzy from not eating", "are starving for nutrients", "feel too weak from lack of food", "see a mirage of an incredible banquet", "have incredible cramps in your stomach" }; static char *f_plop[] = { "faint", "pass out", "keel over", "black out" }; void feed_me(int hunger) { char *charp = NULL, *charp2 = NULL; switch (hunger) { case F_OK: default: debug("feed_me(%d) called.", hunger); break; case F_HUNGRY: charp = f_hungry[rnd(sizeof(f_hungry) / sizeof(char *))]; break; case F_WEAK: charp = f_weak[rnd(sizeof(f_weak) / sizeof(char *))]; break; case F_FAINT: charp = f_faint[rnd(sizeof(f_faint) / sizeof(char *))]; charp2 = f_plop[rnd(sizeof(f_plop) / sizeof(char *))]; break; } msg("You %s.", charp); if (hunger == F_FAINT) msg("You %s.", charp2); } /* get_monster_number() prompt player for a monster on list returns 0 if none selected */ int get_monster_number(char *message) { int i; int pres_monst = 1; int ret_val = -1; char buf[2 * LINELEN]; char monst_name[2 * LINELEN]; while (ret_val == -1) { msg("Which monster do you wish to %s? (* for list)", message); if ((get_string(buf, cw)) != NORM) return(0); if ((i = atoi(buf)) != 0) ret_val = i; else if (buf[0] != '*') { for (i = 1; i < nummonst; i++) if ((strcmp(monsters[i].m_name, buf) == 0)) ret_val = i; } /* The following hack should be redone by the windowing code */ else while (pres_monst < nummonst) /* Print out the monsters */ { int num_lines = LINES - 3; wclear(hw); touchwin(hw); wmove(hw, 2, 0); for (i = 0; i < num_lines && pres_monst < nummonst; i++) { sprintf(monst_name, "[%d] %s\n", pres_monst, monsters[pres_monst].m_name); waddstr(hw, monst_name); pres_monst++; } if (pres_monst < nummonst) { mvwaddstr(hw, LINES - 1, 0, morestr); wrefresh(hw); wait_for(' '); } else { mvwaddstr(hw, 0, 0, "Which monster"); waddstr(hw, "? "); wrefresh(hw); } } get_monst: get_string(monst_name, hw); ret_val = atoi(monst_name); if ((ret_val < 1 || ret_val > nummonst - 1)) { mvwaddstr(hw, 0, 0, "Please enter a number in the displayed range -- "); wrefresh(hw); goto get_monst; } /* Set up for redraw */ clearok(cw, TRUE); touchwin(cw); } return(ret_val); }