/* * move.c - Hero movement commands * * Advanced Rogue * Copyright (C) 1984, 1985, 1986 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. */ /* * Hero movement commands * */ #include "curses.h" #include #include #include "rogue.h" #ifdef PC7300 #include "menu.h" #endif /* * Used to hold the new hero position */ static coord nh; static char Moves[3][3] = { { 'y', 'k', 'u' }, { 'h', '.', 'l' }, { 'b', 'j', 'n' } }; /* * be_trapped: * The guy stepped on a trap.... Make him pay. */ char be_trapped(struct thing *th, coord *tc) { register struct trap *tp; register char ch, *mname = ""; register bool is_player = (th == &player), can_see; register struct linked_list *mitem = NULL; register struct thing *mp; /* Can the player see the creature? */ can_see = (cansee(tc->y, tc->x) && (is_player || !invisible(th))); tp = trap_at(tc->y, tc->x); /* * if he's wearing boots of elvenkind, he won't set off the trap * unless its a magic pool (they're not really traps) */ if (is_player && cur_misc[WEAR_BOOTS] != NULL && cur_misc[WEAR_BOOTS]->o_which == MM_ELF_BOOTS && tp->tr_type != POOL) return '\0'; /* * if the creature is flying then it won't set off the trap */ if (on(*th, ISFLY)) return '\0'; tp->tr_flags |= ISFOUND; if (!is_player) { mitem = find_mons(th->t_pos.y, th->t_pos.x); mname = monster_name(th); } else { count = running = FALSE; mvwaddch(cw, tp->tr_pos.y, tp->tr_pos.x, tp->tr_type); } switch (ch = tp->tr_type) { case TRAPDOOR: if (is_player) { level++; pstats.s_hpt -= roll(1, 10); if (pstats.s_hpt <= 0) death(D_FALL); new_level(NORMLEV); msg("You fell into a trap!"); } else { if (can_see) msg("%s fell into a trap!", prname(mname, TRUE)); /* * See if the fall killed the monster * don't let a UNIQUE die since it might have an artifact * that we need */ if (off(*th,ISUNIQUE) && (th->t_stats.s_hpt-=roll(1,10)) <= 0){ killed(mitem, FALSE, FALSE, FALSE); } else { /* Just move monster to next level */ check_residue(th); /* Erase the monster from the old position */ if (isalpha(mvwinch(cw, th->t_pos.y, th->t_pos.x))) mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); /* let him summon on next lvl */ if (on (*th, HASSUMMONED)) { turn_off(*th, HASSUMMONED); turn_on(*th, CANSUMMON); } turn_on(*th,ISELSEWHERE); detach(mlist, mitem); attach(tlist, mitem); /* remember him next level */ /* Make sure that no one is still chasing us */ for (mitem = mlist; mitem != NULL; mitem = next(mitem)) { mp = THINGPTR(mitem); if (mp->t_dest == &th->t_pos) { mp->t_dest = &hero; mp->t_wasshot = FALSE; turn_off(*mp, ISFLEE); /* Don't run away! */ } } /* Make sure we were not chasing a monster here */ th->t_dest = &hero; if (on(*th, ISFRIENDLY), turn_off(*th, ISFLEE)); } } when BEARTRAP: if (is_stealth(th)) { if (is_player) msg("You pass a bear trap."); else if (can_see) msg("%s passes a bear trap.", prname(mname, TRUE)); } else { th->t_no_move += movement(&player) * BEARTIME; th->t_action = A_FREEZE; if (is_player) msg("You are caught in a bear trap."); else if (can_see) msg("%s is caught in a bear trap.", prname(mname, TRUE)); } when SLEEPTRAP: if (is_player) { msg("A strange white mist envelops you."); if (!ISWEARING(R_ALERT)) { msg("You fall asleep."); player.t_no_move += movement(&player) * SLEEPTIME; player.t_action = A_FREEZE; } } else { if (can_see) msg("A strange white mist envelops %s.", prname(mname, FALSE)); if (on(*th, ISUNDEAD)) { if (can_see) msg("The mist doesn't seem to affect %s.", prname(mname, FALSE)); } else { th->t_no_move += movement(th) * SLEEPTIME; th->t_action = A_FREEZE; } } when ARROWTRAP: if (swing(th->t_ctype, th->t_stats.s_lvl-1, th->t_stats.s_arm, 1)) { if (is_player) { msg("Oh no! An arrow shot you."); if ((pstats.s_hpt -= roll(1, 6)) <= 0) { msg("The arrow killed you."); death(D_ARROW); } } else { if (can_see) msg("An arrow shot %s.", prname(mname, FALSE)); if ((th->t_stats.s_hpt -= roll(1, 6)) <= 0) { if (can_see) msg("The arrow killed %s.", prname(mname, FALSE)); killed(mitem, FALSE, FALSE, TRUE); } } } else { register struct linked_list *item; register struct object *arrow; if (is_player) msg("An arrow shoots past you."); else if (can_see) msg("An arrow shoots by %s.", prname(mname, FALSE)); item = new_item(sizeof *arrow); arrow = OBJPTR(item); arrow->o_type = WEAPON; arrow->contents = NULL; arrow->o_which = ARROW; arrow->o_hplus = rnd(3) - 1; arrow->o_dplus = rnd(3) - 1; init_weapon(arrow, ARROW); arrow->o_count = 1; arrow->o_pos = *tc; arrow->o_mark[0] = '\0'; fall(item, FALSE); } when TELTRAP: if (is_player) teleport(); else { register int rm; struct room *old_room; /* old room of monster */ /* * Erase the monster from the old position */ if (isalpha(mvwinch(cw, th->t_pos.y, th->t_pos.x))) mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); /* * check to see if room should go dark */ if (on(*th, HASFIRE)) { old_room=roomin(&th->t_pos); if (old_room != NULL) { register struct linked_list *fire_item; for (fire_item = old_room->r_fires; fire_item != NULL; fire_item = next(fire_item)) { if (THINGPTR(fire_item) == th) { detach(old_room->r_fires, fire_item); destroy_item(fire_item); if (old_room->r_fires == NULL) { old_room->r_flags &= ~HASFIRE; if (can_see) light(&hero); } } } } } /* Get a new position */ do { rm = rnd_room(); rnd_pos(&rooms[rm], &th->t_pos); } until(winat(th->t_pos.y, th->t_pos.x) == FLOOR); /* Put it there */ mvwaddch(mw, th->t_pos.y, th->t_pos.x, th->t_type); th->t_oldch = CCHAR( mvwinch(cw, th->t_pos.y, th->t_pos.x) ); /* * check to see if room that creature appears in should * light up */ if (on(*th, HASFIRE)) { register struct linked_list *fire_item; fire_item = creat_item(); ldata(fire_item) = (char *) th; attach(rooms[rm].r_fires, fire_item); rooms[rm].r_flags |= HASFIRE; if(cansee(th->t_pos.y, th->t_pos.x) && next(rooms[rm].r_fires) == NULL) light(&hero); } if (can_see) msg("%s seems to have disappeared!", prname(mname, TRUE)); } when DARTTRAP: if (swing(th->t_ctype, th->t_stats.s_lvl+1, th->t_stats.s_arm, 1)) { if (is_player) { msg("A small dart just hit you in the shoulder."); if ((pstats.s_hpt -= roll(1, 4)) <= 0) { msg("The dart killed you."); death(D_DART); } /* Now the poison */ if (!save(VS_POISON, &player, 0)) { /* 75% chance it will do point damage - else strength */ if (rnd(100) < 75) { pstats.s_hpt /= 2; if (pstats.s_hpt == 0) death(D_POISON); } else if (!ISWEARING(R_SUSABILITY)) chg_str(-1); } } else { if (can_see) msg("A small dart just hit %s in the shoulder.", prname(mname, FALSE)); if ((th->t_stats.s_hpt -= roll(1,4)) <= 0) { if (can_see) msg("The dart killed %s.", prname(mname, FALSE)); killed(mitem, FALSE, FALSE, TRUE); } if (!save(VS_POISON, th, 0)) { th->t_stats.s_hpt /= 2; if (th->t_stats.s_hpt <= 0) { if (can_see) msg("The dart killed %s.", prname(mname,FALSE)); killed(mitem, FALSE, FALSE, TRUE); } } } } else { if (is_player) msg("A small dart whizzes by your ear and vanishes."); else if (can_see) msg("A small dart whizzes by %s's ear and vanishes.", prname(mname, FALSE)); } when POOL: { register int i; i = rnd(100); if (is_player) { if ((tp->tr_flags & ISGONE)) { if (i < 30) { teleport(); /* teleport away */ pool_teleport = TRUE; } else if((i < 45) && level > 2) { level -= rnd(2) + 1; cur_max = level; new_level(NORMLEV); pool_teleport = TRUE; msg("You here a faint groan from below."); } else if(i < 70) { level += rnd(4) + 1; new_level(NORMLEV); pool_teleport = TRUE; msg("You find yourself in strange surroundings."); } else if(i > 95) { msg("Oh no!!! You drown in the pool!!! --More--"); wait_for(' '); death(D_DROWN); } } } else { if (i < 60) { if (can_see) { /* Drowns */ if (i < 30) msg("%s drowned in the pool!", prname(mname, TRUE)); /* Teleported to another level */ else msg("%s disappeared!", prname(mname, TRUE)); } killed(mitem, FALSE, FALSE, TRUE); } } } when MAZETRAP: if (is_player) { pstats.s_hpt -= roll(1, 10); level++; msg("You fell through a trap door!"); if (pstats.s_hpt <= 0) death(D_FALL); new_level(MAZELEV); msg("You are surrounded by twisty passages!"); } else { if (can_see) msg("%s fell into a trap!", prname(mname, TRUE)); if (on(*th, ISUNIQUE)) { check_residue(th); /* Erase the monster from the old position */ if (isalpha(mvwinch(cw, th->t_pos.y, th->t_pos.x))) mvwaddch(cw, th->t_pos.y, th->t_pos.x, th->t_oldch); mvwaddch(mw, th->t_pos.y, th->t_pos.x, ' '); /* let him summon on next lvl */ if (on (*th, HASSUMMONED)) { turn_off(*th, HASSUMMONED); turn_on(*th, CANSUMMON); } turn_on(*th,ISELSEWHERE); detach(mlist, mitem); attach(tlist, mitem); /* remember him next level */ /* Make sure that no one is still chasing us */ for (mitem = mlist; mitem != NULL; mitem = next(mitem)) { mp = THINGPTR(mitem); if (mp->t_dest == &th->t_pos) { mp->t_dest = &hero; mp->t_wasshot = FALSE; turn_off(*mp, ISFLEE); /* Don't run away! */ } } /* Make sure we were not chasing a monster here */ th->t_dest = &hero; if (on(*th, ISFRIENDLY), turn_off(*th, ISFLEE)); } else killed(mitem, FALSE, FALSE, FALSE); } } /* Move the cursor back onto the hero */ wmove(cw, hero.y, hero.x); md_flushinp(); return(ch); } /* * blue_light: * magically light up a room (or level or make it dark) */ bool blue_light(bool blessed, bool cursed) { register struct room *rp; bool 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) || !lit_room(rp)) msg(nothing); else { rp->r_flags |= ISDARK; if (!lit_room(rp) && (levtype != OUTSIDE || !daytime)) msg("The %s suddenly goes dark.", levtype == OUTSIDE ? "area" : "room"); else msg(nothing); ret_val = TRUE; } } else { ret_val = TRUE; if (rp && !lit_room(rp) && (levtype != OUTSIDE || !daytime)) { addmsg("The %s is lit", levtype == OUTSIDE ? "area" : "room"); if (!terse) addmsg(" by a %s blue light.", blessed ? "bright" : "shimmering"); endmsg(); } else if (winat(hero.y, hero.x) == PASSAGE) msg("The corridor glows %sand then fades", blessed ? "brightly " : ""); else { ret_val = FALSE; msg(nothing); } if (blessed) { register int i; /* Index through rooms */ for (i=0; ir_flags &= ~ISDARK; } /* * Light the room and put the player back up */ light(&hero); mvwaddch(cw, hero.y, hero.x, PLAYER); return(ret_val); } /* * 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) { int legal=0; /* Number of legal alternatives */ register int y, x, /* Indexes though possible positions */ locy, locx; /* Hold delta of chosen location */ /* New position */ nh.y = hero.y + dy; nh.x = hero.x + dx; /* If it is a legal move, just return */ if (nh.x >= 0 && nh.x < cols && nh.y > 0 && nh.y < lines - 2) { switch (winat(nh.y, nh.x)) { case WALL: case '|': case '-': break; default: if (diag_ok(&hero, &nh, &player)) return; } } /* Check legal places surrounding the player -- ignore previous position */ for (y = hero.y - 1; y <= hero.y + 1; y++) { if (y < 1 || y > lines - 3) continue; for (x = hero.x - 1; x <= hero.x + 1; x++) { /* Ignore borders of the screen */ if (x < 0 || x > cols - 1) continue; /* * Ignore where we came from, where we are, and where we couldn't go */ if ((x == hero.x - dx && y == hero.y - dy) || (x == hero.x + dx && y == hero.y + dy) || (x == hero.x && y == hero.y)) continue; switch (winat(y, x)) { case WALL: case '|': case '-': break; default: nh.y = y; nh.x = x; if (diag_ok(&hero, &nh, &player)) { legal++; locy = y - (hero.y - 1); locx = x - (hero.x - 1); } } } } /* If we have 2 or more legal moves, make no change */ if (legal != 1) { return; } runch = Moves[locy][locx]; /* * For mazes, pretend like it is the beginning of a new run at each turn * in order to get the lighting correct. */ if (levtype == MAZELEV) firstmove = TRUE; return; } /* * dip_it: * Dip an object into a magic pool */ void dip_it(void) { reg struct linked_list *what; reg struct object *ob; reg struct trap *tp; reg int wh, i; tp = trap_at(hero.y,hero.x); if (tp == NULL || tp->tr_type != POOL) { msg("I see no shimmering pool here"); return; } if (tp->tr_flags & ISGONE) { msg("This shimmering pool appears to have been used once already."); return; } /* It takes 3 movement periods to dip something */ if (player.t_action != C_DIP) { if ((what = get_item(pack, "dip", ALL, FALSE, FALSE)) == NULL) { msg(""); after = FALSE; return; } ob = OBJPTR(what); if (ob == cur_armor || ob == cur_misc[WEAR_BOOTS] || ob == cur_misc[WEAR_JEWEL] || ob == cur_misc[WEAR_GAUNTLET] || ob == cur_misc[WEAR_CLOAK] || ob == cur_misc[WEAR_BRACERS] || ob == cur_misc[WEAR_NECKLACE]|| ob == cur_ring[LEFT_1] || ob == cur_ring[LEFT_2] || ob == cur_ring[LEFT_3] || ob == cur_ring[LEFT_4] || ob == cur_ring[RIGHT_1] || ob == cur_ring[RIGHT_2] || ob == cur_ring[RIGHT_3] || ob == cur_ring[RIGHT_4]) { mpos = 0; msg("You'll have to take it off first."); return; } player.t_using = what; /* Remember what it is */ player.t_action = C_DIP; /* We are dipping */ player.t_no_move = 3 * movement(&player); return; } /* We have waited our time, let's dip it */ what = player.t_using; player.t_using = NULL; player.t_action = A_NIL; ob = OBJPTR(what); tp->tr_flags |= ISGONE; if (ob != NULL) { wh = ob->o_which; ob->o_flags |= ISKNOW; i = rnd(100); switch(ob->o_type) { case WEAPON: if(i < 50) { /* enchant weapon here */ if ((ob->o_flags & ISCURSED) == 0) { ob->o_hplus += 1; ob->o_dplus += 1; } else { /* weapon was prev cursed here */ ob->o_hplus = rnd(2); ob->o_dplus = rnd(2); } ob->o_flags &= ~ISCURSED; msg("The %s glows blue for a moment.",weaps[wh].w_name); } else if(i < 70) { /* curse weapon here */ if ((ob->o_flags & ISCURSED) == 0) { ob->o_hplus = -(rnd(2)+1); ob->o_dplus = -(rnd(2)+1); } else { /* if already cursed */ ob->o_hplus--; ob->o_dplus--; } ob->o_flags |= ISCURSED; msg("The %s glows red for a moment.",weaps[wh].w_name); } else msg(nothing); when ARMOR: if (i < 50) { /* enchant armor */ if((ob->o_flags & ISCURSED) == 0) ob->o_ac -= rnd(2) + 1; else ob->o_ac = -rnd(3)+ armors[wh].a_class; ob->o_flags &= ~ISCURSED; msg("The %s glows blue for a moment",armors[wh].a_name); } else if(i < 75){ /* curse armor */ if ((ob->o_flags & ISCURSED) == 0) ob->o_ac = rnd(3)+ armors[wh].a_class; else ob->o_ac += rnd(2) + 1; ob->o_flags |= ISCURSED; msg("The %s glows red for a moment.",armors[wh].a_name); } else msg(nothing); when STICK: { int j; j = rnd(8) + 1; if(i < 50) { /* add charges */ ob->o_charges += j; ws_know[wh] = TRUE; if (ob->o_flags & ISCURSED) ob->o_flags &= ~ISCURSED; msg("The %s %s glows blue for a moment.", ws_made[wh],ws_type[wh]); } else if(i < 65) { /* remove charges */ if ((ob->o_charges -= i) < 0) ob->o_charges = 0; ws_know[wh] = TRUE; if (ob->o_flags & ISBLESSED) ob->o_flags &= ~ISBLESSED; else ob->o_flags |= ISCURSED; msg("The %s %s glows red for a moment.", ws_made[wh],ws_type[wh]); } else msg(nothing); } when SCROLL: s_know[wh] = TRUE; msg("The '%s' scroll unfurls.",s_names[wh]); when POTION: p_know[wh] = TRUE; msg("The %s potion bubbles for a moment.",p_colors[wh]); when RING: if(i < 50) { /* enchant ring */ if ((ob->o_flags & ISCURSED) == 0) ob->o_ac += rnd(2) + 1; else ob->o_ac = rnd(2) + 1; ob->o_flags &= ~ISCURSED; } else if(i < 80) { /* curse ring */ if ((ob->o_flags & ISCURSED) == 0) ob->o_ac = -(rnd(2) + 1); else ob->o_ac -= (rnd(2) + 1); ob->o_flags |= ISCURSED; } r_know[wh] = TRUE; msg("The %s ring vibrates for a moment.",r_stones[wh]); when MM: m_know[wh] = TRUE; switch (ob->o_which) { case MM_BRACERS: case MM_PROTECT: if(i < 50) { /* enchant item */ if ((ob->o_flags & ISCURSED) == 0) ob->o_ac += rnd(2) + 1; else ob->o_ac = rnd(2) + 1; ob->o_flags &= ~ISCURSED; } else if(i < 80) { /* curse item */ if ((ob->o_flags & ISCURSED) == 0) ob->o_ac = -(rnd(2) + 1); else ob->o_ac -= (rnd(2) + 1); ob->o_flags |= ISCURSED; } msg("The item vibrates for a moment."); when MM_CHOKE: case MM_DISAPPEAR: ob->o_ac = 0; msg ("The dust dissolves in the pool!"); } otherwise: msg("The pool bubbles for a moment."); } updpack(FALSE, &player); } else msg(nothing); } /* * 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) { register struct room *rp, *orp; register char ch; struct linked_list *item; register struct thing *tp = NULL; coord old_hero; register int wasfirstmove, moved, num_hits; bool changed=FALSE; /* Did we switch places with a friendly monster? */ wasfirstmove = firstmove; firstmove = FALSE; curprice = -1; /* if in trading post, we've moved off obj */ /* * Do a confused move (maybe) */ if (player.t_action == A_NIL && ((on(player, ISHUH) && rnd(100) < 80) || (on(player, ISDANCE) && rnd(100) < 80) || (ISWEARING(R_DELUSION) && rnd(100) < 25))) /* Get a random move */ nh = *rndmove(&player); else { nh.y = hero.y + dy; nh.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 (nh.x < 0 || nh.x > cols-1 || nh.y < 1 || nh.y >= lines - 2 || !diag_ok(&hero, &nh, &player)) { after = running = FALSE; player.t_action = A_NIL; return; } if (running && ce(hero, nh)) after = running = FALSE; ch = CCHAR( winat(nh.y, nh.x) ); /* Take care of hero trying to move close to something frightening */ if (on(player, ISFLEE)) { if (rnd(100) < 10) { turn_off(player, ISFLEE); msg("You regain your composure."); } else if (DISTANCE(nh.y, nh.x, player.t_dest->y, player.t_dest->x) < DISTANCE(hero.y, hero.x, player.t_dest->y, player.t_dest->x)) { running = FALSE; msg("You are too terrified to move that way"); player.t_action = A_NIL; player.t_no_move = movement(&player); return; } } /* If we want to move to a monster, see what it is */ if (isalpha(ch)) { item = find_mons(nh.y, nh.x); if (item == NULL) { debug("Cannot find monster in move."); player.t_action = A_NIL; return; } tp = THINGPTR(item); } /* * Take care of hero being held. If the player is being held, he * can't move unless he is either attacking a non-friendly monster * or attacking a friendly monster that can't move. */ if (on(player, ISHELD) && (!isalpha(ch) || (on(*tp, ISFRIENDLY) && off(*tp, ISHELD)))) { msg("You are being held."); player.t_action = A_NIL; return; } /* See if we have to wait for our movement rate */ if (player.t_action == A_NIL) { after = FALSE; firstmove = wasfirstmove; /* Remember if this is first move */ player.t_no_move = movement(&player); if (player.t_ctype == C_MONK) player.t_no_move -= pstats.s_lvl/6; if (on(player, ISFLY)) player.t_no_move /= 2; /* If flying, speed him up */ if (player.t_no_move < 1) player.t_no_move = 1; /* Remember our action */ player.t_action = Moves[dy+1][dx+1]; return; } /* Now let's forget the old move and just do it */ player.t_action = A_NIL; /* If we're moving onto a friendly monster, let's change places. */ if (isalpha(ch) && on(*tp, ISFRIENDLY) && off(*tp, ISHELD)) { coord tpos, /* Where monster may have been going */ current; /* Current hero position */ int action; /* The monster's action */ current = hero; tpos = tp->t_newpos; action = tp->t_action; /* Disrupt whatever our friend was doing */ tp->t_action = A_NIL; /* Tentatively move us to where he is */ hero = tp->t_pos; /* See if we can move him to where we were */ tp->t_newpos = current; do_chase(tp); /* Did we succeed? */ if (ce(tp->t_pos, current)) { /* Reset our idea of what ch is */ ch = CCHAR( winat(nh.y, nh.x) ); /* Let it be known that we made the switch */ changed = TRUE; old_hero = current; /* Make the monster think it didn't move */ tp->t_oldpos = current; tp->t_doorgoal = NULL; /* Let the player know something funny happened. */ msg("What a sidestep!"); } else { /* Restore things -- we couldn't move */ hero = current; tp->t_newpos = tpos; tp->t_action = action; } } /* assume he's not in a wall */ if (!isalpha(ch)) turn_off(player, ISINWALL); switch (ch) { case '|': case '-': if (levtype == OUTSIDE) { hero = nh; new_level(OUTSIDE); return; } case WALL: case SECRETDOOR: if (off(player, CANINWALL) || running) { after = running = FALSE; /* Light if finishing run */ if (levtype == MAZELEV && lit_room(&rooms[0])) look(FALSE, TRUE); after = running = FALSE; return; } turn_on(player, ISINWALL); break; case POOL: if (levtype == OUTSIDE) { lake_check(&nh); running = FALSE; break; } case MAZETRAP: if (levtype == OUTSIDE) { running = FALSE; break; } case TRAPDOOR: case TELTRAP: case BEARTRAP: case SLEEPTRAP: case ARROWTRAP: case DARTTRAP: ch = be_trapped(&player, &nh); if (ch == TRAPDOOR || ch == TELTRAP || pool_teleport || ch == MAZETRAP) { pool_teleport = FALSE; return; } break; case GOLD: case POTION: case SCROLL: case FOOD: case WEAPON: case ARMOR: case RING: case MM: case RELIC: case STICK: running = FALSE; take = ch; break; case DOOR: case STAIRS: case POST: running = FALSE; break; default: break; } if (isalpha(ch)) { /* if its a monster then fight it */ /* * If we were running down a corridor and didn't start right * next to the critter, don't do anything. */ if (running && wasfirstmove == FALSE && roomin(&hero) == NULL) { struct linked_list *item; item = find_mons(nh.y, nh.x); if (item != NULL && !invisible(THINGPTR(item))) { after = running = FALSE; return; } } /* We have to add time because we're attacking */ player.t_no_move = FIGHTBASE; player.t_no_move += weap_move(&player, cur_weapon); if (on(player, ISHASTE)) player.t_no_move /= 2; else if (on(player, ISSLOW)) player.t_no_move *= 2; /* We may attack faster if we're high enough level * and the right class */ switch(player.t_ctype) { case C_FIGHTER: num_hits = player.t_stats.s_lvl/9 + 1; when C_PALADIN: num_hits = player.t_stats.s_lvl/12 + 1; when C_RANGER: num_hits = player.t_stats.s_lvl/13 + 1; when C_MONK: if(cur_weapon) num_hits= 1; else num_hits= player.t_stats.s_lvl/5 + 1; otherwise: num_hits = 1; } /* * The player has already moved the initial movement period. * Let's add that in, do our division, and then subtract it * out so that the total time is divided, not just the * additional attack time. */ moved = movement(&player), player.t_no_move += moved; player.t_no_move /= num_hits; player.t_no_move -= moved; running = FALSE; /* Mark that we are attacking and save the attack coordinate */ player.t_action = A_ATTACK; player.t_newpos = nh; runch = Moves[dy+1][dx+1]; /* Remember the direction */ if (player.t_no_move <= 0) after = FALSE; return; } /* * if not fighting then move the hero */ if (changed == FALSE) { old_hero = hero; /* Save hero's old position */ hero = nh; /* Move the hero */ } rp = roomin(&hero); orp = roomin(&old_hero); /* Unlight any possible cross-corridor */ if (levtype == MAZELEV) { register bool call_light = FALSE; register char wall_check; if (wasfirstmove && lit_room(&rooms[0])) { /* Are we moving out of a corridor? */ switch (runch) { case 'h': case 'l': if (old_hero.y + 1 < lines - 2) { wall_check = CCHAR( winat(old_hero.y + 1, old_hero.x) ); if (!isrock(wall_check)) call_light = TRUE; } if (old_hero.y - 1 > 0) { wall_check = CCHAR( winat(old_hero.y - 1, old_hero.x) ); if (!isrock(wall_check)) call_light = TRUE; } break; case 'j': case 'k': if (old_hero.x + 1 < cols) { wall_check = CCHAR( winat(old_hero.y, old_hero.x + 1) ); if (!isrock(wall_check)) call_light = TRUE; } if (old_hero.x - 1 >= 0) { wall_check = CCHAR( winat(old_hero.y, old_hero.x - 1) ); if (!isrock(wall_check)) call_light = TRUE; } break; default: call_light = TRUE; } player.t_oldpos = old_hero; if (call_light) light(&old_hero); } } else if (orp != NULL && rp == NULL) { /* Leaving a room -- darken it */ orp->r_flags |= FORCEDARK; /* Fake darkness */ light(&old_hero); orp->r_flags &= ~FORCEDARK; /* Restore light state */ } else if (rp != NULL && orp == NULL){/* Entering a room */ light(&hero); if (rp->r_flags & ISTREAS) wake_room(rp); } ch = CCHAR( winat(old_hero.y, old_hero.x) ); wmove(cw, unc(old_hero)); waddch(cw, ch); wmove(cw, unc(hero)); waddch(cw, PLAYER); } /* * do_run: * Start the hero running */ void do_run(char ch) { firstmove = TRUE; running = TRUE; after = FALSE; runch = ch; } /* * getdelta: * Takes a movement character (eg. h, j, k, l) and returns the * y and x delta corresponding to it in the remaining arguments. * Returns TRUE if it could find it, FALSE otherwise. */ bool getdelta(char match, int *dy, int *dx) { int y, x; for (y = 0; y < 3; y++) for (x = 0; x < 3; x++) if (Moves[y][x] == match) { *dy = y - 1; *dx = x - 1; return(TRUE); } return(FALSE); } /* * isatrap: * Returns TRUE if this character is some kind of trap */ bool isatrap(char ch) { switch(ch) { case DARTTRAP: case TELTRAP: case TRAPDOOR: case ARROWTRAP: case SLEEPTRAP: case BEARTRAP: return(TRUE); case MAZETRAP: case POOL: return(levtype != OUTSIDE); default: return(FALSE); } } /* * Called to illuminate a room. * If it is dark, remove anything that might move. */ void light(coord *cp) { register struct room *rp; register int j, k, x, y; register char ch, rch, sch; register struct linked_list *item; int jlow, jhigh, klow, khigh; /* Boundaries of lit area */ if ((rp = roomin(cp)) != NULL) { /* * is he wearing ring of illumination? */ if (&hero == cp && ISWEARING(R_LIGHT)) /* Must be hero's room */ rp->r_flags &= ~ISDARK; /* If we are in a maze, don't look at the whole room (level) */ if (levtype == MAZELEV) { int see_radius; see_radius = 1; /* If we are looking at the hero in a rock, broaden our sights */ if (&hero == cp || &player.t_oldpos == cp) { ch = CCHAR( winat(hero.y, hero.x) ); if (isrock(ch)) see_radius = 2; ch = CCHAR( winat(player.t_oldpos.y, player.t_oldpos.x) ); if (isrock(ch)) see_radius = 2; } jlow = max(0, cp->y - see_radius - rp->r_pos.y); jhigh = min(rp->r_max.y, cp->y + see_radius + 1 - rp->r_pos.y); klow = max(0, cp->x - see_radius - rp->r_pos.x); khigh = min(rp->r_max.x, cp->x + see_radius + 1 - rp->r_pos.x); } 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++) { bool see_here, see_before; /* Is this in the give 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; /* * If we are in a maze do not look at this area unless * we can see it from where we are or where we last were * (for erasing purposes). */ if (levtype == MAZELEV) { /* If we can't see it from here, could we see it before? */ if ((see_here = maze_view(y, x)) == FALSE) { coord savhero; /* Could we see it from where we were? */ savhero = hero; hero = player.t_oldpos; see_before = maze_view(y, x); hero = savhero; if (!see_before) continue; } } 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. This is for dark rooms as opposed to invisibility. * * Call winat() in the test because ch will not reveal * invisible monsters. */ if (isalpha(winat(y, x))) { struct thing *tp; /* The monster */ item = wake_monster(y, x); 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 (!cansee(tp->t_pos.y, tp->t_pos.x) && roomin(&tp->t_pos) != NULL) switch (tp->t_oldch) { /* * Only blank it out if it is in a room and not * the border (or other wall) of the room. */ case DOOR: case SECRETDOOR: case '-': case '|': break; otherwise: 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. */ if ((!lit_room(rp) && (levtype != OUTSIDE)) || (levtype == OUTSIDE && !daytime) || on(player, ISBLIND) || (rp->r_flags & FORCEDARK) || (levtype == MAZELEV && !see_here && see_before)) { sch = CCHAR( mvwinch(cw, y, x) ); /* What's seen */ rch = CCHAR( mvinch(y, x) ); /* What's really there */ switch (rch) { case DOOR: case SECRETDOOR: case STAIRS: case TRAPDOOR: case TELTRAP: case BEARTRAP: case SLEEPTRAP: case ARROWTRAP: case DARTTRAP: case MAZETRAP: case POOL: case POST: case '|': case '-': case WALL: if (isalpha(sch)) ch = rch; else if (sch != FLOOR) ch = sch; else ch = ' '; /* Hide undiscoverd things */ when FLOOR: ch = ' '; otherwise: ch = ' '; } /* Take care of our magic bookkeeping. */ switch (sch) { case MAGIC: case BMAGIC: case CMAGIC: ch = sch; } } mvwaddch(cw, y, x, ch); } } } } /* * lit_room: * Called to see if the specified room is lit up or not. */ bool lit_room(struct room *rp) { register struct linked_list *fire_item; register struct thing *fire_creature; if (!(rp->r_flags & ISDARK)) return(TRUE); /* A definitely lit room */ /* Is it lit by fire light? */ if (rp->r_flags & HASFIRE) { switch ((int)levtype) { case MAZELEV: /* See if a fire creature is in line of sight */ for (fire_item = rp->r_fires; fire_item != NULL; fire_item = next(fire_item)) { fire_creature = THINGPTR(fire_item); if (maze_view(fire_creature->t_pos.y, fire_creature->t_pos.x)) return(TRUE); } /* Couldn't find any in line-of-sight */ return(FALSE); /* We should probably do something special for the outside */ otherwise: return TRUE; } } return(FALSE); } /* * movement: * Given a pointer to a player/monster structure, calculate the * movement rate for that character. */ short movement(struct thing *tp) { register int result; register int carry; /* Percentage carried */ result = 0; /* Adjust for armor (player only) */ if (tp == &player && cur_armor) { int diff; /* Now armor class differs from normal one of same type */ /* Blessed armor adds less */ diff = cur_armor->o_ac - armors[cur_armor->o_which].a_class; switch (cur_armor->o_which) { case LEATHER: case RING_MAIL: case STUDDED_LEATHER: case SCALE_MAIL: case PADDED_ARMOR: diff += 1; when CHAIN_MAIL: case SPLINT_MAIL: case BANDED_MAIL: case PLATE_MAIL: diff += 2; when PLATE_ARMOR: diff += 3; otherwise: debug("forgot an armor in movement()"); } if (diff < 0) diff = 0; result += diff; } /* Adjust for the pack */ carry = 100 * tp->t_stats.s_pack / tp->t_stats.s_carry; if (carry > 75) result++; /* Get a bonus for dexterity */ result -= dext_plus(tp == &player ? dex_compute() : tp->t_stats.s_dext); /* only allow adjust for the minus's */ if (result < 0) result = 0; result += tp->t_movement; /* now add in movement rate */ /* Is the character slowed? */ if (on(*tp, ISSLOW) || on(*tp, ISDANCE)) result *= 2; /* Is the character hasted? */ if (on(*tp, ISHASTE)) result /= 2; /* We have a minimum of 1 */ if (result < 1) result = 1; return(result); } /* * rndmove: * move in a random direction if the monster/person is confused */ coord * rndmove(struct thing *who) { register int x, y; register int ex, ey, nopen = 0; static coord ret; /* what we will be returning */ static coord dest; ret = who->t_pos; /* * Now go through the spaces surrounding the player and * set that place in the array to true if the space can be * moved into */ ey = ret.y + 1; ex = ret.x + 1; for (y = who->t_pos.y - 1; y <= ey; y++) if (y > 0 && y < lines - 2) for (x = who->t_pos.x - 1; x <= ex; x++) { if (x < 0 || x >= cols) continue; if (step_ok(y, x, NOMONST, who) == TRUE) { dest.y = y; dest.x = x; if (!diag_ok(&who->t_pos, &dest, who)) continue; if (rnd(++nopen) == 0) ret = dest; } } return &ret; } #define TRAPTYPES 9 /* 9 total trap types that can be set */ #define WIZARDTRAPS 3 /* Only wizards can set 3 of these */ static char *trap_types[TRAPTYPES] = { "Trap Door", "Bear Trap", "Sleep Trap", "Arrow Trap", "Teleport Trap", "Dart Trap", "Magic pool", "Maze Trap", "Trading Post" }; #ifdef PC7300 #define TRAPWIDTH 13 /* Length of longest named trap from above list */ #define TRAPPREFIX 4 /* Length of prefix (eg. "[9] ") */ static menu_t Display; /* The menu structure */ static mitem_t Dispitems[TRAPTYPES+1]; /* Info for each line */ static char Displines[TRAPTYPES+1][TRAPWIDTH+TRAPPREFIX+1]; #endif /* * set_trap: * set a trap at (y, x) on screen. */ void set_trap(struct thing *tp, int y, int x) { register bool is_player = (tp == &player); register int selection = rnd(TRAPTYPES-WIZARDTRAPS) + '1'; register int i, num_traps; register char ch, och; int thief_bonus = 0; int s_dext; if (is_player && player.t_ctype != C_THIEF && player.t_ctype != C_ASSASIN) { msg("Only thieves and assassins can set traps."); return; } switch (och = CCHAR( mvinch(y, x) )) { case WALL: case FLOOR: case PASSAGE: break; default: if (is_player) msg("The trap failed!"); return; } if (is_player) { int state = 0, /* 0 -> current screen, 1 -> prompt screen, 2 -> done */ units; /* Number of movement units for the given trap */ if (player.t_action == C_SETTRAP) { selection = player.t_selection; player.t_selection = 0; player.t_using = NULL; player.t_action = A_NIL; } else { msg("Which kind of trap do you wish to set? (* for a list): "); num_traps = TRAPTYPES - (wizard ? 0 : WIZARDTRAPS); do { selection = tolower(readchar()); switch (selection) { case '*': if (state != 1) { #ifdef PC7300 for (i=0; i= 0) { if (Display.m_selcnt == 0) { /* Cancelled menu */ msg(""); trap_tries--; /* Don't count this one */ after = FALSE; return; } selection = Display.m_curi->mi_val; state = 2; break; } #endif wclear(hw); touchwin(hw); for (i=0; i= nfloors) { if (state == 1) draw(cw); msg("There is no level below this one."); return; } state = 2; /* Finished */ break; } /* Fall through for non-wizard, unusual trap case */ default: if (state == 1) { /* In the prompt window */ wmove(hw, 0, 0); wprintw(hw, "Please enter a selection between 1 and %d: ", num_traps); if (menu_overlay) /* * Put out the selection. The longest line is * the prompt line (43 characters long). */ over_win(cw, hw, num_traps+3, 45, 0, 43, '\0'); else draw(hw); } else { /* Normal window */ mpos = 0; msg("Please enter a selection between 1 and %d: ", num_traps); } } } while (state != 2); switch ((player.t_selection = selection)) { case '1': units = 20; /* Trap door */ when '2': units = 5; /* Bear trap */ when '3': units = 6; /* Sleeping gas trap */ when '4': units = 5; /* Arrow trap */ when '5': units = 8; /* Teleport trap */ when '6': units = 5; /* Dart trap */ otherwise: units = 10; /* Unknown trap */ } player.t_no_move = units * movement(&player); player.t_action = C_SETTRAP; player.t_using = NULL; return; } } if (is_player && player.t_ctype == C_THIEF) thief_bonus = 30; if (is_player && player.t_ctype == C_ASSASIN) thief_bonus = 20; s_dext = (tp == &player) ? dex_compute() : tp->t_stats.s_dext; if (ntraps >= MAXTRAPS || ++trap_tries >= MAXTRPTRY || levtype == POSTLEV || rnd(80) >= (s_dext + tp->t_stats.s_lvl/2 + thief_bonus)) { if (is_player) msg("The trap failed!"); return; } switch (selection) { case '1': ch = TRAPDOOR; when '2': ch = BEARTRAP; when '3': ch = SLEEPTRAP; when '4': ch = ARROWTRAP; when '5': ch = TELTRAP; when '6': ch = DARTTRAP; when '7': ch = POOL; when '8': ch = MAZETRAP; when '9': ch = POST; } mvaddch(y, x, ch); traps[ntraps].tr_show = och; traps[ntraps].tr_type = ch; traps[ntraps].tr_pos.y = y; traps[ntraps].tr_pos.x = x; if (is_player) traps[ntraps].tr_flags = ISTHIEFSET; if (ch == POOL || ch == POST) { traps[ntraps].tr_flags |= ISFOUND; } ntraps++; } /* * show: * returns what a certain thing will display as to the un-initiated */ char show(int y, int x) { register char ch = CCHAR( winat(y, x) ); register struct linked_list *it; register struct thing *tp; if (isatrap(ch)) { register 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) { msg("Show: Can't find monster in show (%d, %d)", y, x); return(mvwinch(stdscr, y, x)); } tp = THINGPTR(it); if (on(*tp, ISDISGUISE)) ch = tp->t_disguise; /* As a mimic */ /* Hide invisible creatures */ else if (invisible(tp)) { /* We can't see surprise-type creatures through "see invisible" */ if (off(player,CANSEE) || on(*tp,CANSURPRISE)) ch = CCHAR( mvwinch(stdscr, y, x) ); /* Invisible */ } else if (on(*tp, CANINWALL)) { if (isrock(mvwinch(stdscr, y, x))) ch = CCHAR( winch(stdscr) ); /* As Xorn */ } } return ch; } /* * trap_at: * find the trap at (y,x) on screen. */ struct trap * trap_at(int y, int x) { register struct trap *tp, *ep; ep = &traps[ntraps]; for (tp = traps; tp < ep; tp++) if (tp->tr_pos.y == y && tp->tr_pos.x == x) break; if (tp == ep) debug((sprintf(prbuf, "Trap at %d,%d not in array", y, x), prbuf)); return tp; } /* * weap_move: * Calculate how many segments it will take to swing the given * weapon (note that the weapon may actually be a stick or * even something else). * wielder: Who's wielding the weapon * weap: The weapon */ int weap_move(struct thing *wielder, struct object *weap) { register int weap_rate; int dexterity; int strength; if (weap == NULL) return(1); /* hand, claw, bite attacks are quick */ switch (weap->o_type) { case STICK: if (EQUAL(ws_type[weap->o_which], "staff")) weap_rate = 2; else weap_rate = 1; /* A wand */ when WEAPON: weap_rate = weaps[weap->o_which].w_rate; /* Adjust for blessed or cursed weapon */ if (weap->o_hplus < 0) /* Cursed */ weap_rate -= (weap->o_hplus - 2) / 3; else if (weap_rate > 0) /* Blessed */ weap_rate -= (2*weap->o_hplus + weap_rate - 1) / weap_rate; when RELIC: switch (weap->o_which) { case MUSTY_DAGGER: case HRUGGEK_MSTAR: case AXE_AKLAD: case YEENOGHU_FLAIL: case MING_STAFF: case ORCUS_WAND: case ASMO_ROD: /* These operate in the blink of an eye */ weap_rate = 1; otherwise: /* What is it? */ weap_rate = 10; debug("unknown weapon in weap_move()"); } otherwise: /* What is it? */ weap_rate = 10; debug("unknown weapon in weap_move()"); } /* Put in a dexterity bonus */ if (wielder == &player) dexterity = dex_compute(); else dexterity = wielder->t_stats.s_dext; weap_rate -= dext_plus(dexterity) / 2; /* Put in a strength bonus */ if (wielder == &player) strength = str_compute(); else strength = wielder->t_stats.s_str; weap_rate -= str_plus(strength) / 2; /* It can't speed you up and it must take SOME time */ if (weap_rate <= 0) weap_rate = 1; /* Do we need to adjust for fast/slow movement? */ if (on(*wielder, ISSLOW) || on(*wielder, ISDANCE)) weap_rate *= 2; if (on(*wielder, ISHASTE)) weap_rate /= 2; /* Return the result */ return(weap_rate); }