Mercurial > hg > early-roguelike
view urogue/move.c @ 298:5a94c9b3181e
UltraRogue: clear the next_obj field when removing items from the floor.
The next_obj field is a pointer which the top item in a stack uses to
keep a list of the other items. When removing an item from the stack,
rem_obj() failed to set next_obj to NULL, which can cause items in
monster inventory to point to items earlier in the inventory list.
That causes infinite co-recursion when saving or restoring.
author | John "Elwin" Edwards |
---|---|
date | Thu, 08 Feb 2018 20:54:34 -0500 |
parents | c495a4f288c6 |
children | e52a8a7ad4c5 |
line wrap: on
line source
/* move.c - Hero movement 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 <stdlib.h> #include <ctype.h> #include "rogue.h" /* do_run() Start the hero running */ void do_run(char ch) { running = TRUE; after = FALSE; runch = ch; if (doorstop && !on(player, ISBLIND)) { door_stop = TRUE; firstmove = TRUE; } } /* step_ok() returns true if it is ok for type to step on ch flgptr will be NULL if we don't know what the monster is yet! */ int step_ok(int y, int x, int can_on_monst, struct thing *flgptr) { struct linked_list *item; char ch; /* What is here? Don't check monster window if MONSTOK is set */ if (can_on_monst == MONSTOK) ch = CCHAR( mvinch(y, x) ); else ch = winat(y, x); switch (ch) { case ' ': case '|': case '-': case SECRETDOOR: if (flgptr && on(*flgptr, CANINWALL)) return(TRUE); return(FALSE); case SCROLL: /* * If it is a scroll, it might be a scare monster scroll so * we need to look it up to see what type it is. */ if (flgptr && flgptr->t_ctype == C_MONSTER) { item = find_obj(y, x); if (item != NULL && (OBJPTR(item))->o_type == SCROLL && (OBJPTR(item))->o_which == S_SCARE && rnd(flgptr->t_stats.s_intel) < 12) return(FALSE); /* All but smart ones are scared */ } return(TRUE); default: return(!isalpha(ch)); } } /* corr_move() Check to see that a move is legal. If so, return correct character. If not, if player came from a legal place, then try to turn him. */ void corr_move(int dy, int dx) { char ch; short legal = 0; /* Number of legal alternatives */ int y = 0, x = 0; /* Holds legal new position */ int *ny, *nx; /* Point to which direction to change */ /* New position */ player.t_nxtpos.y = hero.y + dy; player.t_nxtpos.x = hero.x + dx; /* A bad diagonal move is illegal */ if (!diag_ok(&hero, &player.t_nxtpos, &player)) return; /* If it is a legal move, just return */ if (player.t_nxtpos.x >= 0 && player.t_nxtpos.x < COLS && player.t_nxtpos.y > 0 && player.t_nxtpos.y < LINES - 2) { ch = winat(player.t_nxtpos.y, player.t_nxtpos.x); switch (ch) { case ' ': case '|': case '-': break; default: return; } } /* Check the legal alternatives */ if (dy == 0) { ny = &dy; nx = &dx; } else { ny = &dx; nx = &dy; } for (*nx = 0, *ny = -1; *ny < 2; *ny += 2) { /* New position */ player.t_nxtpos.y = hero.y + dy; player.t_nxtpos.x = hero.x + dx; if (player.t_nxtpos.x < 0 || player.t_nxtpos.x > COLS - 1 || player.t_nxtpos.y < 1 || player.t_nxtpos.y > LINES - 3) continue; ch = winat(player.t_nxtpos.y, player.t_nxtpos.x); switch (ch) { case ' ': case '|': case '-': break; default: legal++; y = dy; x = dx; } } /* If we have 2 legal moves, make no change */ if (legal != 1) return; /* Make the change */ if (y == 0) /* Move horizontally */ { if (x == 1) runch = 'l'; else runch = 'h'; } else /* Move vertically */ { if (y == 1) runch = 'j'; else runch = 'k'; } return; } /* do_move() Check to see that a move is legal. If it is handle the consequences (fighting, picking up, etc.) */ void do_move(int dy, int dx) { char ch; coord old_hero; char hch; firstmove = FALSE; if (player.t_no_move) { player.t_no_move--; msg("You are still stuck in the bear trap."); return; } /* Do a confused move (maybe) */ if ((rnd(100) < 80 && on(player, ISHUH)) || (is_wearing(R_DELUSION) && rnd(100) < 25) || on(player, STUMBLER) && rnd(40) == 0) player.t_nxtpos = rndmove(&player); else { player.t_nxtpos.y = hero.y + dy; player.t_nxtpos.x = hero.x + dx; } /* * Check if he tried to move off the screen or make an illegal * diagonal move, and stop him if he did. */ if (player.t_nxtpos.x < 0 || player.t_nxtpos.x > COLS - 1 || player.t_nxtpos.y < 1 || player.t_nxtpos.y >= LINES - 2 || !diag_ok(&hero, &player.t_nxtpos, &player)) { after = fighting = running = FALSE; return; } if (running && ce(hero, player.t_nxtpos)) after = running = FALSE; ch = winat(player.t_nxtpos.y, player.t_nxtpos.x); if (isalpha(ch)) debug("Moving onto monster %c",ch); /* Take care of hero trying to move close to something frightening */ if (on(player, ISFLEE)) { if (rnd(10) < 1) { turn_off(player, ISFLEE); msg("You regain your composure."); } else if (DISTANCE(player.t_nxtpos, player.t_chasee->t_pos) < DISTANCE(hero,player.t_chasee->t_pos)) return; } /* Take care of hero being held */ if (on(player, ISHELD) && !isalpha(ch)) { if (rnd(pstats.s_str) > 14) { msg("You break free of the hold."); if (--hold_count == 0) turn_off(player, ISHELD); } else { msg("You are being held."); return; } } /* Might lose disguise */ if (on(player, ISDISGUISE) && rnd(11 * pstats.s_dext) == 0) { extinguish_fuse(FUSE_UNDISGUISE); undisguise(NULL); } /* assume he's not in a wall */ if (!isalpha(ch)) turn_off(player, ISINWALL); hch = CCHAR( mvinch(hero.y, hero.x) ); /* Where hero was */ old_hero = hero; /* Save hero's old position */ switch (ch) { case ' ': case '|': case '-': case SECRETDOOR: if (off(player, CANINWALL)) { after = running = FALSE; return; } else if (running) { after = running = FALSE; return; } turn_on(player, ISINWALL); break; case TRAPDOOR: case TELTRAP: case BEARTRAP: case SLEEPTRAP: case ARROWTRAP: case DARTTRAP: case POOL: case MAZETRAP: case FIRETRAP: case POISONTRAP: case LAIR: case RUSTTRAP: ch = be_trapped(&player, player.t_nxtpos); if (!is_wearing(R_LEVITATION) && off(player, CANFLY) && (old_hero.x != hero.x || old_hero.y != hero.y || pool_teleport)) { pool_teleport = FALSE; return; } break; case GOLD: case POTION: case SCROLL: case FOOD: case WEAPON: case ARMOR: case RING: case ARTIFACT: case STICK: running = FALSE; take = ch; break; default: break; } if (ch == FIRETRAP) light(&hero); hero = player.t_nxtpos; /* Move the hero */ /* adjust lighting */ if (roomin(hero) == NULL && (hch == '-' || hch == '|' || hch == DOOR || hch == SECRETDOOR)) { /* Leaving a room -- darken it */ struct room *rp = roomin(old_hero); int is_lit = FALSE; if (!(rp->r_flags & ISDARK)) is_lit = TRUE; rp->r_flags |= ISDARK; /* Fake darkness */ light(&old_hero); if (is_lit) rp->r_flags &= ~ISDARK; /* Restore light state */ } else if (ch == DOOR || ch == SECRETDOOR || ch == '|' || ch == '-') { /* Entering a room */ running = FALSE; if (hch != '|' && hch != '-') light(&hero); /* knows whether the hero can see things in */ } /* handle other situations */ if (ch == STAIRS) running = FALSE; else if (ch == POST) { running = FALSE; new_level(POSTLEV,0); return; } else if (isalpha(ch)) { struct linked_list *mp; struct thing *tp; char t; running = FALSE; mp = find_mons(hero.y, hero.x); if (mp == NULL) return; tp = THINGPTR(mp); if (good_monster(*tp)) /* Exchange places with your buddy */ { mvwaddch(cw, old_hero.y, old_hero.x, ch); mvwaddch(mw, old_hero.y, old_hero.x, ch); mvwaddch(mw, hero.y, hero.x, ' '); mvwaddch(cw, hero.y, hero.x, tp->t_oldch); (*tp).t_pos.x = old_hero.x; /* Update monster position variables */ (*tp).t_pos.y = old_hero.y; (*tp).t_oldpos.x = old_hero.x; (*tp).t_oldpos.y = old_hero.y; t = (*tp).t_oldch; (*tp).t_oldch = player.t_oldch; player.t_oldch = t; turn_on(*tp, ISRUN); mvwaddch(cw, hero.y, hero.x, PLAYER); /* make sure that the room shows OK */ light(&hero); wrefresh(cw); return; } else { hero = old_hero; /* Restore hero -- we'll fight instead of move */ /* make sure that the room shows OK */ light(&hero); fight(&player.t_nxtpos, cur_weapon, NOTHROWN); return; } } else fighting = FALSE; ch = winat(old_hero.y, old_hero.x); mvwaddch(cw, old_hero.y, old_hero.x, ch); mvwaddch(cw, hero.y, hero.x, PLAYER); } /* light() Called to illuminate a room. If it is dark, remove anything that might move. */ void light(coord *cp) { struct room *rp; int j, k, x, y; char ch, rch; struct linked_list *item; int jlow, jhigh, klow, khigh; /* Boundaries of lit area */ if ((rp = roomin(*cp)) != NULL && !on(player, ISBLIND)) { /* is he wearing ring of illumination and in same room? */ if ((is_wearing(R_LIGHT) || on(player, ISELECTRIC)) && cp == &hero) rp->r_flags &= ~ISDARK; /* If we are in a maze, don't look at the whole room (level) */ if (levtype == MAZELEV) { jlow = max(0, hero.y - 2 - rp->r_pos.y); jhigh = min(rp->r_max.y, hero.y + 2 - rp->r_pos.y + 1); klow = max(0, hero.x - 2 - rp->r_pos.x); khigh = min(rp->r_max.x, hero.x + 2 - rp->r_pos.x + 1); } else { jlow = klow = 0; jhigh = rp->r_max.y; khigh = rp->r_max.x; } for (j = 0; j < rp->r_max.y; j++) { for (k = 0; k < rp->r_max.x; k++) { /* Is this in the given area -- needed for maze */ if ((j < jlow || j >= jhigh) && (k < klow || k >= khigh)) continue; y = rp->r_pos.y + j; x = rp->r_pos.x + k; ch = show(y, x); wmove(cw, y, x); /* Figure out how to display a secret door */ if (ch == SECRETDOOR) { if (j == 0 || j == rp->r_max.y - 1) ch = '-'; else ch = '|'; } /* * For monsters, if they were previously not * seen and now can be seen, or vice-versa, * make sure that will happen. */ if (isalpha(ch)) { struct thing *tp; item = wake_monster(y, x); if (item == NULL) continue; tp = THINGPTR(item); /* Previously not seen -- now can see it */ if (tp->t_oldch == ' ' && cansee(tp->t_pos.y, tp->t_pos.x)) tp->t_oldch = CCHAR( mvinch(y, x) ); /* Previously seen -- now can't see it */ else if (off(player, ISBLIND) && tp->t_oldch != ' ' && !cansee(tp->t_pos.y, tp->t_pos.x)) tp->t_oldch = ' '; } /* * If the room is a dark room, we might want * to remove monsters and the like from it * (since they might move). A dark room or * not in line-of-sight in a maze. */ if (((rp->r_flags & ISDARK) && !(rp->r_flags & HASFIRE)) || (levtype == MAZELEV && !maze_view(y, x))) { rch = CCHAR( mvwinch(cw, y, x) ); switch (rch) { case DOOR: case STAIRS: case TRAPDOOR: case TELTRAP: case BEARTRAP: case SLEEPTRAP: case ARROWTRAP: case DARTTRAP: case POOL: case MAZETRAP: case FIRETRAP: case POISONTRAP: case LAIR: case RUSTTRAP: case POST: case '|': case '-': case ' ': ch = rch; break; case FLOOR: ch = (on(player, ISBLIND) ? FLOOR : ' '); break; default: ch = ' '; break; } } mvwaddch(cw, y, x, ch); } } } } /* blue_light() magically light up a room (or level or make it dark) */ int blue_light(int flags) { struct room *rp; int blessed = (flags & ISBLESSED); int cursed = (flags & ISCURSED); int ret_val = FALSE; /* Whether or not affect is known */ rp = roomin(hero); /* What room is hero in? */ /* Darken the room if the magic is cursed */ if (cursed) { if ((rp == NULL) || (rp->r_flags & ISDARK)) nothing_message(flags); else { if (!(rp->r_flags & HASFIRE)) msg("The room suddenly goes dark."); else nothing_message(flags); rp->r_flags |= ISDARK; ret_val = TRUE; } } else { ret_val = TRUE; if (rp && (rp->r_flags & ISDARK) && !(rp->r_flags & HASFIRE)) { msg("The room is lit by a %s blue light.", blessed ? "bright" : "shimmering"); } else if (winat(hero.y, hero.x) == PASSAGE) msg("The corridor glows %sand then fades.", blessed ? "brightly " : ""); else { ret_val = FALSE; nothing_message(flags); } if (blessed) { short i; /* Index through rooms */ for (i = 0; i < MAXROOMS; i++) rooms[i].r_flags &= ~ISDARK; } else if (rp) rp->r_flags &= ~ISDARK; } /* Light the room and put the player back up */ light(&hero); mvwaddch(cw, hero.y, hero.x, PLAYER); return (ret_val); } /* show() returns what a certain thing will display as to the un-initiated */ char show(int y, int x) { char ch = winat(y, x); struct linked_list *it; struct thing *tp; if (isatrap(ch)) { struct trap *trp = trap_at(y, x); return (trp->tr_flags & ISFOUND) ? ch : trp->tr_show; } else if (isalpha(ch)) { if ((it = find_mons(y, x)) == NULL) { debug("Can't find monster in move."); return ' '; } tp = THINGPTR(it); if (on(*tp, ISDISGUISE)) ch = tp->t_disguise; /* As a mimic */ else if (on(*tp, ISINVIS) || (on(*tp, ISSHADOW) && rnd(100) < 90) || on(*tp, CANSURPRISE)) { if (off(player, CANSEE) || on(*tp, CANSURPRISE)) ch = CCHAR( mvwinch(stdscr, y, x) ); /* Invisible */ } else if (on(*tp, CANINWALL)) { char tch; tch = CCHAR( mvwinch(stdscr, y, x) ); if (tch == WALL || tch == '-' || tch == '|') ch = CCHAR( winch(stdscr) ); /* As Xorn */ } } return(ch); } /* be_trapped() The guy stepped on a trap.... Make him pay. */ char be_trapped(struct thing *th, coord tc) { struct trap *tp; char ch, *mname = NULL; int is_player = (th == &player), can_see = cansee(tc.y, tc.x); struct linked_list *mitem = NULL; tp = trap_at(tc.y, tc.x); ch = tp->tr_type; if (!is_player) { mitem = find_mons(th->t_pos.y, th->t_pos.x); mname = monsters[th->t_index].m_name; /* Flying monsters do not set off traps */ if (!mitem || (on(*th, CANFLY) && (ch == BEARTRAP || ch == MAZETRAP || ch == TRAPDOOR || ch == ARROWTRAP || ch == DARTTRAP))) { debug("%s avoided trap.", mname); return(ch); } } else { short thief_bonus = -50; count = running = FALSE; mvwaddch(cw, tp->tr_pos.y, tp->tr_pos.x, tp->tr_type); if (no_command) return(ch); if (player.t_ctype == C_THIEF || player.t_ctype == C_NINJA) thief_bonus = 10; if (((is_wearing(R_LEVITATION) || on(player, CANFLY)) && (ch != FIRETRAP || (ch == FIRETRAP && !(tp->tr_flags & ISFOUND)))) || (moving && (tp->tr_flags & ISFOUND) && rnd(100) < thief_bonus + 2 * pstats.s_dext + 5 * pstats.s_lvl) && (ch == BEARTRAP || ch == MAZETRAP || ch == TRAPDOOR || ch == ARROWTRAP || ch == DARTTRAP)) { static char trname[1024]; msg(tr_name(ch,trname)); tp->tr_flags |= ISFOUND; return(ch); } if (moving) msg("Your attempt fails."); } tp->tr_flags |= ISFOUND; switch(ch) { case TRAPDOOR: if (is_player) { level++; new_level(NORMLEV,0); addmsg("You fell into a trap"); if (player.t_ctype != C_THIEF && player.t_ctype != C_ASSASIN && player.t_ctype != C_NINJA && rnd(pstats.s_dext) < 4) { addmsg(" and were damaged by the fall"); if ((pstats.s_hpt -= roll(1, 6)) <= 0) { addmsg("! The fall killed you."); endmsg(); death(D_FALL); return(ch); } } addmsg("!"); endmsg(); if (off(player, ISCLEAR) && rnd(4) < 3) { 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."); } else { if (can_see) msg("The %s fell into a trap!", mname); if (on(*th, ISFAMILIAR)) turn_off(player, HASFAMILIAR); killed(NULL,mitem,NOMESSAGE,NOPOINTS); } break; case BEARTRAP: if (is_stealth(th)) { if (is_player) msg("You pass a bear trap."); else if (can_see) msg("The %s passes a bear trap.", mname); } else { th->t_no_move += BEARTIME; if (is_player) msg("You are caught in a bear trap."); else if (can_see) msg("The %s is caught in a bear trap.", mname); } break; case SLEEPTRAP: if (is_player) { msg("A strange white mist envelops you."); if (!is_wearing(R_ALERT)) { if (!is_wearing(R_BREATHE) && off(player, HASOXYGEN)) { msg("You fall asleep."); no_command += SLEEPTIME; } } } else { if (can_see) msg("A strange white mist envelops the %s.", mname);