view rogue5/misc.c @ 81:0e212d46b68f

rogue4: don't put savefile metadata into the savefile. The save_file() function in save.c stored the savefile's device number, inode number, creation time, and modification time in the file. The restore() function read them back, and apparently used to compare them to protect against cheaters. Unfortunately, the types and sizes of these numbers differ from system to system, which ruins the Roguelike Restoration Project's fine portability work. So they have been removed from the savefile. This BREAKS SAVEFILE COMPATIBILITY, but old files can be converted by excising the chunk starting at offset 0x22 with length sizeof(ino_t) + sizeof(dev_t) + 2 * sizeof(time_t). That's 0x14 on i686 and 0x20 on x86_64, at least with current versions of Linux and glibc.
author John "Elwin" Edwards
date Mon, 05 Aug 2013 20:49:41 -0700
parents f502bf60e6e4
children
line wrap: on
line source

/*
 * All sorts of miscellaneous routines
 *
 * @(#)misc.c	4.66 (Berkeley) 08/06/83
 *
 * Rogue: Exploring the Dungeons of Doom
 * Copyright (C) 1980-1983, 1985, 1999 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 <curses.h>
#include <string.h>
#include <ctype.h>
#include "rogue.h"

/*
 * look:
 *	A quick glance all around the player
 */
#undef DEBUG


void
look(int wakeup)
{
    int x, y;
    chtype ch;
    THING *tp;
    PLACE *pp;
    struct room *rp;
    int ey, ex;
    int passcount;
    int pfl, *fp, pch;
    int sy, sx, sumhero = 0, diffhero = 0;
# ifdef DEBUG
    static int done = FALSE;

    if (done)
	return;
    done = TRUE;
# endif /* DEBUG */
    passcount = 0;
    rp = proom;
    if (!ce(oldpos, hero))
    {
	erase_lamp(&oldpos, oldrp);
	oldpos = hero;
	oldrp = rp;
    }
    ey = hero.y + 1;
    ex = hero.x + 1;
    sx = hero.x - 1;
    sy = hero.y - 1;
    if (door_stop && !firstmove && running)
    {
	sumhero = hero.y + hero.x;
	diffhero = hero.y - hero.x;
    }
    pp = INDEX(hero.y, hero.x);
    pch = pp->p_ch;
    pfl = pp->p_flags;

    for (y = sy; y <= ey; y++)
	if (y > 0 && y < NUMLINES - 1) for (x = sx; x <= ex; x++)
	{
	    if (x < 0 || x >= NUMCOLS)
		continue;
	    if (!on(player, ISBLIND))
	    {
		if (y == hero.y && x == hero.x)
		    continue;
	    }

	    pp = INDEX(y, x);
	    ch = pp->p_ch;
	    if (ch == ' ')		/* nothing need be done with a ' ' */
		    continue;
	    fp = &pp->p_flags;
	    if (pch != DOOR && ch != DOOR)
		if ((pfl & F_PASS) != (*fp & F_PASS))
		    continue;
	    if (((*fp & F_PASS) || ch == DOOR) && 
		 ((pfl & F_PASS) || pch == DOOR))
	    {
		if (hero.x != x && hero.y != y &&
		    !step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x)))
			continue;
	    }

	    if ((tp = pp->p_monst) == NULL)
		ch = trip_ch(y, x, ch);
	    else
		if (on(player, SEEMONST) && on(*tp, ISINVIS))
		{
		    if (door_stop && !firstmove)
			running = FALSE;
		    continue;
		}
		else
		{
		    if (wakeup)
			wake_monster(y, x);
		    if (see_monst(tp))
		    {
			if (on(player, ISHALU))
			    ch = rnd(26) + 'A';
			else
			    ch = tp->t_disguise;
		    }
		}
	    if (on(player, ISBLIND) && (y != hero.y || x != hero.x))
		continue;

	    move(y, x);

	    if ((proom->r_flags & ISDARK) && !see_floor && ch == FLOOR)
		ch = ' ';

	    if (tp != NULL || ch != CCHAR( inch() ))
		addch(ch);

	    if (door_stop && !firstmove && running)
	    {
		switch (runch)
		{
		    case 'h':
			if (x == ex)
			    continue;
		    when 'j':
			if (y == sy)
			    continue;
		    when 'k':
			if (y == ey)
			    continue;
		    when 'l':
			if (x == sx)
			    continue;
		    when 'y':
			if ((y + x) - sumhero >= 1)
			    continue;
		    when 'u':
			if ((y - x) - diffhero >= 1)
			    continue;
		    when 'n':
			if ((y + x) - sumhero <= -1)
			    continue;
		    when 'b':
			if ((y - x) - diffhero <= -1)
			    continue;
		}
		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:
		    case '|':
		    case '-':
		    case ' ':
			break;
		    default:
			running = FALSE;
			break;
		}
	    }
	}
    if (door_stop && !firstmove && passcount > 1)
	running = FALSE;
    if (!running || !jump)
	mvaddch(hero.y, hero.x, PLAYER);
# ifdef DEBUG
    done = FALSE;
# endif /* DEBUG */
}

/*
 * trip_ch:
 *	Return the character appropriate for this space, taking into
 *	account whether or not the player is tripping.
 */
int
trip_ch(int y, int x, int ch)
{
    if (on(player, ISHALU) && after)
	switch (ch)
	{
	    case FLOOR:
	    case ' ':
	    case PASSAGE:
	    case '-':
	    case '|':
	    case DOOR:
	    case TRAP:
		break;
	    default:
		if (y != stairs.y || x != stairs.x || !seenstairs)
		    ch = rnd_thing();
		break;
	}
    return ch;
}

/*
 * erase_lamp:
 *	Erase the area shown by a lamp in a dark room.
 */

void
erase_lamp(const coord *pos, const struct room *rp)
{
    int y, x, ey, sy, ex;

    if (!(see_floor && (rp->r_flags & (ISGONE|ISDARK)) == ISDARK
	&& !on(player,ISBLIND)))
	    return;

    ey = pos->y + 1;
    ex = pos->x + 1;
    sy = pos->y - 1;
    for (x = pos->x - 1; x <= ex; x++)
	for (y = sy; y <= ey; y++)
	{
	    if (y == hero.y && x == hero.x)
		continue;
	    move(y, x);
	    if (CCHAR( inch() ) == FLOOR)
		addch(' ');
	}
}

/*
 * show_floor:
 *	Should we show the floor in her room at this time?
 */
int
show_floor(void)
{
    if ((proom->r_flags & (ISGONE|ISDARK)) == ISDARK && !on(player, ISBLIND))
	return see_floor;
    else
	return TRUE;
}

/*
 * find_obj:
 *	Find the unclaimed object at y, x
 */
THING *
find_obj(int y, int x)
{
    THING *obj;

    for (obj = lvl_obj; obj != NULL; obj = next(obj))
    {
	if (obj->o_pos.y == y && obj->o_pos.x == x)
		return obj;
    }
#ifdef MASTER
    sprintf(prbuf, "Non-object %d,%d", y, x);
    msg(prbuf);
    return NULL;
#else
    /* NOTREACHED */
    return NULL;
#endif
}

/*
 * eat:
 *	She wants to eat something, so let her try
 */

void
eat(void)
{
    THING *obj;

    if ((obj = get_item("eat", FOOD)) == NULL)
	return;
    if (obj->o_type != FOOD)
    {
	if (!terse)
	    msg("ugh, you would get ill if you ate that");
	else
	    msg("that's Inedible!");
	return;
    }
    if (food_left < 0)
	food_left = 0;
    if ((food_left += HUNGERTIME - 200 + rnd(400)) > STOMACHSIZE)
	food_left = STOMACHSIZE;
    hungry_state = 0;
    if (obj == cur_weapon)
	cur_weapon = NULL;
    if (obj->o_which == 1)
	msg("my, that was a yummy %s", fruit);
    else
	if (rnd(100) > 70)
	{
	    pstats.s_exp++;
	    msg("%s, this food tastes awful", choose_str("bummer", "yuk"));
	    check_level();
	}
	else
	    msg("%s, that tasted good", choose_str("oh, wow", "yum"));
    leave_pack(obj, FALSE, FALSE);
}

/*
 * check_level:
 *	Check to see if the guy has gone up a level.
 */

void
check_level(void)
{
    int i, add, olevel;

    for (i = 0; e_levels[i] != 0; i++)
	if (e_levels[i] > pstats.s_exp)
	    break;
    i++;
    olevel = pstats.s_lvl;
    pstats.s_lvl = i;
    if (i > olevel)
    {
	add = roll(i - olevel, 10);
	max_hp += add;
	pstats.s_hpt += add;
	msg("welcome to level %d", i);
    }
}

/*
 * chg_str:
 *	used to modify the playes strength.  It keeps track of the
 *	highest it has been, just in case
 */

void
chg_str(int amt)
{
    int comp;

    if (amt == 0)
	return;
    add_str(&pstats.s_str, amt);
    comp = pstats.s_str;
    if (ISRING(LEFT, R_ADDSTR))
	add_str(&comp, -cur_ring[LEFT]->o_arm);
    if (ISRING(RIGHT, R_ADDSTR))
	add_str(&comp, -cur_ring[RIGHT]->o_arm);
    if (comp > max_stats.s_str)
	max_stats.s_str = comp;
}

/*
 * add_str:
 *	Perform the actual add, checking upper and lower bound limits
 */
void
add_str(int *sp, int amt)
{
    if ((*sp += amt) < 3)
	*sp = 3;
    else if (*sp > 31)
	*sp = 31;
}

/*
 * add_haste:
 *	Add a haste to the player
 */
int
add_haste(int potion)
{
    if (on(player, ISHASTE))
    {
	no_command += rnd(8);
	player.t_flags &= ~(ISRUN|ISHASTE);
	extinguish(nohaste);
	msg("you faint from exhaustion");
	return FALSE;
    }
    else
    {
	player.t_flags |= ISHASTE;
	if (potion)
	    fuse(nohaste, 0, rnd(4)+4, AFTER);
	return TRUE;
    }
}

/*
 * aggravate:
 *	Aggravate all the monsters on this level
 */

void
aggravate(void)
{
    THING *mp;

    for (mp = mlist; mp != NULL; mp = next(mp))
	runto(&mp->t_pos);
}

/*
 * vowelstr:
 *      For printfs: if string starts with a vowel, return "n" for an
 *	"an".
 */
char *
vowelstr(const 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_current:
 *	See if the object is one of the currently used items
 */
int
is_current(const THING *obj)
{
    if (obj == NULL)
	return FALSE;
    if (obj == cur_armor || obj == cur_weapon || obj == cur_ring[LEFT]
	|| obj == cur_ring[RIGHT])
    {
	if (!terse)
	    addmsg("That's already ");
	msg("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;
    static coord last_delt= {0,0};

    if (again && last_dir != '\0')
    {
	delta.y = last_delt.y;
	delta.x = last_delt.x;
	dir_ch = last_dir;
    }
    else
    {
	if (!terse)
	    msg(prompt = "which direction? ");
	else
	    prompt = "direction: ";
	do
	{
	    gotit = TRUE;
	    switch (dir_ch = readchar())
	    {
		case 'h': case'H': delta.y =  0; delta.x = -1;
		when 'j': case'J': delta.y =  1; delta.x =  0;
		when 'k': case'K': delta.y = -1; delta.x =  0;
		when 'l': case'L': delta.y =  0; delta.x =  1;
		when 'y': case'Y': delta.y = -1; delta.x = -1;
		when 'u': case'U': delta.y = -1; delta.x =  1;
		when 'b': case'B': delta.y =  1; delta.x = -1;
		when 'n': case'N': delta.y =  1; delta.x =  1;
		when ESCAPE: last_dir = '\0'; reset_last(); msg(""); return FALSE;
		otherwise:
		    mpos = 0;
		    msg(prompt);
		    gotit = FALSE;
	    }
	} until (gotit);
	if (isupper(dir_ch))
	    dir_ch = tolower(dir_ch);
	last_dir = dir_ch;
	last_delt.y = delta.y;
	last_delt.x = delta.x;
    }
    if (on(player, ISHUH) && rnd(5) == 0)
	do
	{
	    delta.y = rnd(3) - 1;
	    delta.x = rnd(3) - 1;
	} while (delta.y == 0 && delta.x == 0);
    mpos = 0;
    msg("");
    return TRUE;
}

/*
 * sign:
 *	Return the sign of the number
 */
int
sign(int nm)
{
    if (nm < 0)
	return -1;
    else
	return (nm > 0);
}

/*
 * spread:
 *	Give a spread around a given number (+/- 20%)
 */
int
spread(int nm)
{
    return nm - nm / 20 + rnd(nm / 10);
}

/*
 * call_it:
 *	Call an object something after use.
 */

void
call_it(struct obj_info *info)
{
    if (info->oi_know)
    {
	if (info->oi_guess)
	{
	    free(info->oi_guess);
	    info->oi_guess = NULL;
	}
    }
    else if (!info->oi_guess)
    {
	msg(terse ? "call it: " : "what do you want to call it? ");
	if (get_str(prbuf, stdscr) == NORM)
	{
	    if (info->oi_guess != NULL)
		free(info->oi_guess);
	    info->oi_guess = malloc(strlen(prbuf) + 1);
		if (info->oi_guess != NULL)
			strcpy(info->oi_guess, prbuf);
	}
	msg("");
    }
}

/*
 * rnd_thing:
 *	Pick a random thing appropriate for this level
 */
int
rnd_thing(void)
{
    int i;
    int thing_list[] = {
	POTION, SCROLL, RING, STICK, FOOD, WEAPON, ARMOR, STAIRS, GOLD, AMULET
    };

    if (level >= AMULETLEVEL)
        i = rnd(sizeof thing_list / sizeof (int));
    else
        i = rnd(sizeof thing_list / sizeof (int) - 1);
    return thing_list[i];
}

/*
 str str:
 *	Choose the first or second string depending on whether it the
 *	player is tripping
 */
const char *
choose_str(const char *ts, const char *ns)
{
	return (on(player, ISHALU) ? ts : ns);
}