Mercurial > hg > early-roguelike
view arogue5/move.c @ 280:70aa5808c782
Fix potential segfaults at restore related to ctime().
In some games, restore() passes the result of ctime() to mvprintw() or
some other variadic message-formatting function. If ctime() has not
been declared properly, its return type is inferred to be int instead
of char *. This does not cause a warning because the compiler does not
know the correct type of variadic arguments.
On platforms where ints and pointers are not the same size, this can,
probably depending on alignment, result in a segfault that is not easy
to trace.
Including time.h fixes the problem. Some games manually declared
ctime() and avoided the bug. These declarations have also been
replaced with the include.
author | John "Elwin" Edwards |
---|---|
date | Fri, 15 Sep 2017 20:51:10 -0400 |
parents | 56e748983fa8 |
children | e52a8a7ad4c5 |
line wrap: on
line source
/* * Hero movement commands * * Advanced Rogue * Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T * All rights reserved. * * Based on "Super-Rogue" * Copyright (C) 1984 Robert D. Kindelberger * 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 "curses.h" #include <ctype.h> #include "rogue.h" /* * Used to hold the new hero position */ static coord nh; static const char Moves[3][3] = { { 'y', 'k', 'u' }, { 'h', '\0', '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; register const char *mname = NULL; register bool is_player = (th == &player), can_see; register struct linked_list *mitem = NULL; /* 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 = monsters[th->t_index].m_name; } 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("The %s fell into a trap!", mname); /* See if the fall killed the monster */ if ((th->t_stats.s_hpt -= roll(1, 10)) <= 0) { killed(mitem, 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, ' '); turn_on(*th, ISELSEWHERE); detach(mlist, mitem); attach(tlist, mitem); /* remember him next level */ } } when 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); } when SLEEPTRAP: if (is_player) { msg("A strange white mist envelops you."); if (!ISWEARING(R_ALERT)) { msg("You fall asleep."); no_command += SLEEPTIME; } } else { if (can_see) msg("A strange white mist envelops the %s.",mname); if (on(*th, ISUNDEAD)) { if (can_see) msg("The mist doesn't seem to affect the %s.",mname); } else { th->t_no_move += SLEEPTIME; } } 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 the %s.", mname); if ((th->t_stats.s_hpt -= roll(1, 6)) <= 0) { if (can_see) msg("The arrow killed the %s.", mname); killed(mitem, FALSE, FALSE); } } } 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 the %s.", mname); 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("The %s seems to have disappeared!", mname); } 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 the %s in the shoulder.", mname); if ((th->t_stats.s_hpt -= roll(1,4)) <= 0) { if (can_see) msg("The dart killed the %s.", mname); killed(mitem, FALSE, FALSE); } 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 the %s.", mname); killed(mitem, FALSE, FALSE); } } } } else { if (is_player) msg("A small dart whizzes by your ear and vanishes."); else if (can_see) msg("A small dart whizzes by the %s's ear and vanishes.", mname); } 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(cw,' '); death(D_DROWN); } } } else { if (i < 60) { if (can_see) { /* Drowns */ if (i < 30) msg("The %s drowned in the pool!", mname); /* Teleported to another level */ else msg("The %s disappeared!", mname); } killed(mitem, FALSE, FALSE); } } } 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("The %s fell into a trap!", mname); killed(mitem, FALSE, FALSE); } } /* Move the cursor back onto the hero */ wmove(cw, hero.y, hero.x); md_flushinp(); /* flush typeahead */ 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; 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); } /* * 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 = 0, locx = 0; /* 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 used once already"); return; } if ((what = get_item(pack, "dip", ALL)) == NULL) { msg(""); after = FALSE; return; } ob = OBJPTR(what); mpos = 0; 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]) { msg("You'll have to take it off first."); return; } 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; sprintf(outstring,"The %s %s glows blue for a moment.", ws_made[wh],ws_type[wh]); msg(outstring); } 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; sprintf(outstring,"The %s %s glows red for a moment.", ws_made[wh],ws_type[wh]); msg(outstring); } 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); } 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; coord old_hero; int i, wasfirstmove; wasfirstmove = firstmove; firstmove = FALSE; curprice = -1; /* if in trading post, we've moved off obj */ 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 ((on(player, ISHUH) && rnd(100) < 80) || (on(player, ISDANCE) && rnd(100) < 80) || (ISWEARING(R_DELUSION) && rnd(100) < 25)) 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; 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)) return; } /* Take care of hero being held */ if (on(player, ISHELD) && !isalpha(ch)) { msg("You are being held"); return; } /* 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: running = FALSE; break; case POST: running = FALSE; new_level(POSTLEV); return; default: break; } if (isalpha(ch)) { /* if its a monster then fight it */ running = FALSE; i = 1; if (player.t_ctype == C_FIGHTER) i += pstats.s_lvl/10; while (i--) fight(&nh, cur_weapon, FALSE); return; } /* * if not fighting then move the hero */ 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); } ch = CCHAR( winat(old_hero.y, old_hero.x) ); wmove(cw, unc(old_hero)); waddch(cw, ch); wmove(cw, unc(hero)); waddch(cw, PLAYER); }