view rogue4/monsters.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 1b73a8641b37
children e52a8a7ad4c5
line wrap: on
line source

/*
 * File with various monster functions in it
 *
 * @(#)monsters.c	4.24 (Berkeley) 4/6/82
 *
 * Rogue: Exploring the Dungeons of Doom
 * Copyright (C) 1980, 1981, 1982 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 <string.h>
#include <ctype.h>
#include "rogue.h"

int exp_add(THING *tp);

/*
 * List of monsters in rough order of vorpalness
 *
 * NOTE: This not initialized using strings so that xstr doesn't set up
 * the string not to be saved.  Otherwise genocide is lost through
 * saving a game.
 */
char lvl_mons[] =  {
    'K', 'J', 'B', 'S', 'H', 'E', 'A', 'O', 'Z', 'G', 'L', 'C', 'R',
    'Q', 'N', 'Y', 'T', 'W', 'F', 'I', 'X', 'U', 'M', 'V', 'P', 'D',
    '\0'
};

char wand_mons[] = {
    'K', 'J', 'B', 'S', 'H', ' ', 'A', 'O', 'Z', 'G', ' ', 'C', 'R',
    'Q', ' ', 'Y', 'T', 'W', ' ', 'I', 'X', 'U', ' ', 'V', 'P', ' ',
    '\0'
};

/*
 * randmonster:
 *	Pick a monster to show up.  The lower the level,
 *	the meaner the monster.
 */
char
randmonster(bool wander)
{
    register int d;
    register char *mons;

    mons = wander ? wand_mons : lvl_mons;
    do
    {
	d = level + (rnd(10) - 5);
	if (d < 1)
	    d = rnd(5) + 1;
	if (d > 26)
	    d = rnd(5) + 22;
    } while (mons[--d] == ' ');
    return mons[d];
}

/*
 * new_monster:
 *	Pick a new monster and add it to the list
 */
void
new_monster(THING *tp, char type, coord *cp)
{
    register struct monster *mp;
    register int lev_add;

    if ((lev_add = level - AMULETLEVEL) < 0)
	lev_add = 0;
    attach(mlist, tp);
    tp->t_type = type;
    tp->t_disguise = type;
    tp->t_pos = *cp;
    tp->t_oldch = mvinch(cp->y, cp->x);
    tp->t_room = roomin(cp);
    moat(cp->y, cp->x) = tp;
    mp = &monsters[tp->t_type-'A'];
    tp->t_stats.s_lvl = mp->m_stats.s_lvl + lev_add;
    tp->t_stats.s_maxhp = tp->t_stats.s_hpt = roll(tp->t_stats.s_lvl, 8);
    tp->t_stats.s_arm = mp->m_stats.s_arm - lev_add;
    strncpy(tp->t_stats.s_dmg,mp->m_stats.s_dmg,16);
    tp->t_stats.s_str = mp->m_stats.s_str;
    tp->t_stats.s_exp = mp->m_stats.s_exp + lev_add * 10 + exp_add(tp);
    tp->t_flags = mp->m_flags;
    tp->t_turn = TRUE;
    tp->t_pack = NULL;
    if (ISWEARING(R_AGGR))
	runto(cp, &hero);
    if (type == 'M')
	switch (rnd(level > 25 ? 9 : 8))
	{
	    case 0: tp->t_disguise = GOLD;
	    when 1: tp->t_disguise = POTION;
	    when 2: tp->t_disguise = SCROLL;
	    when 3: tp->t_disguise = STAIRS;
	    when 4: tp->t_disguise = WEAPON;
	    when 5: tp->t_disguise = ARMOR;
	    when 6: tp->t_disguise = RING;
	    when 7: tp->t_disguise = STICK;
	    when 8: tp->t_disguise = AMULET;
	}
}

/*
 * expadd:
 *	Experience to add for this monster's level/hit points
 */
int
exp_add(THING *tp)
{
    register int mod;

    if (tp->t_stats.s_lvl == 1)
	mod = tp->t_stats.s_maxhp / 8;
    else
	mod = tp->t_stats.s_maxhp / 6;
    if (tp->t_stats.s_lvl > 9)
	mod *= 20;
    else if (tp->t_stats.s_lvl > 6)
	mod *= 4;
    return mod;
}

/*
 * wanderer:
 *	Create a new wandering monster and aim it at the player
 */
void
wanderer(void)
{
    register int i;
    register struct room *rp;
    register THING *tp;
    coord cp = {0,0};
    register int cnt = 0;

    tp = new_item();
    do
    {
        /* Avoid endless loop when all rooms are filled with monsters
	 * and the player room is not accessible to the monsters.
	 */
	if (cnt++ >= 500)
	{
	    discard(tp);
	    return;
	}
	i = rnd_room();
	if ((rp = &rooms[i]) == proom)
	    continue;
	rnd_pos(rp, &cp);
    } until (rp != proom && step_ok(winat(cp.y, cp.x)));
    new_monster(tp, randmonster(TRUE), &cp);
    runto(&tp->t_pos, &hero);
#ifdef WIZARD
    if (wizard)
	msg("started a wandering %s", monsters[tp->t_type-'A'].m_name);
#endif
}

/*
 * wake_monster:
 *	What to do when the hero steps next to a monster
 */
THING *
wake_monster(int y, int x)
{
    register THING *tp;
    register struct room *rp;
    register char ch;

#ifdef WIZARD
    if ((tp = moat(y, x)) == NULL)
	msg("can't find monster in wake_monster");
#else
    tp = moat(y, x);
#endif
    ch = tp->t_type;
    /*
     * Every time he sees mean monster, it might start chasing him
     */
    if (!on(*tp, ISRUN) && rnd(3) != 0 && on(*tp, ISMEAN) && !on(*tp, ISHELD)
	&& !ISWEARING(R_STEALTH))
    {
	tp->t_dest = &hero;
	tp->t_flags |= ISRUN;
    }
    if (ch == 'U' && !on(player, ISBLIND) && !on(*tp, ISFOUND)
	&& !on(*tp, ISCANC) && on(*tp, ISRUN))
    {
        rp = proom;
	if ((rp != NULL && !(rp->r_flags & ISDARK))
	    || DISTANCE(y, x, hero.y, hero.x) < LAMPDIST)
	{
	    tp->t_flags |= ISFOUND;
	    if (!save(VS_MAGIC))
	    {
		if (on(player, ISHUH))
		    lengthen(unconfuse, rnd(20) + HUHDURATION);
		else
		    fuse(unconfuse, 0, rnd(20) + HUHDURATION, AFTER);
		player.t_flags |= ISHUH;
		msg("the umber hulk's gaze has confused you");
	    }
	}
    }
    /*
     * Let greedy ones guard gold
     */
    if (on(*tp, ISGREED) && !on(*tp, ISRUN))
    {
	tp->t_flags |= ISRUN;
	if (proom->r_goldval)
	    tp->t_dest = &proom->r_gold;
	else
	    tp->t_dest = &hero;
    }
    return tp;
}

/*
 * genocide:
 *	Wipe one monster out of existence (for now...)
 */
void
genocide(void)
{
    register THING *mp;
    register char c;
    register int i;
    register THING *nmp;

    addmsg("which monster");
    if (!terse)
	addmsg(" do you wish to wipe out");
    msg("? ");
    while (!isalpha(c = readchar()))
	if (c == ESCAPE)
	    return;
	else
	{
	    mpos = 0;
	    msg("please specifiy a letter between 'A' and 'Z'");
	}
    mpos = 0;
    if (islower(c))
	c = toupper(c);
    for (mp = mlist; mp; mp = nmp)
    {
	nmp = next(mp);
	if (mp->t_type == c)
	    remove_monster(&mp->t_pos, mp, FALSE);
    }
    for (i = 0; i < 26; i++)
	if (lvl_mons[i] == c)
	{
	    lvl_mons[i] = ' ';
	    wand_mons[i] = ' ';
	    break;
	}
    if (!terse)
	addmsg("there will be ");
    msg("no more %ss", monsters[c - 'A'].m_name);
}

/*
 * give_pack:
 *	Give a pack to a monster if it deserves one
 */
void
give_pack(THING *tp)
{
    if (rnd(100) < monsters[tp->t_type-'A'].m_carry)
	attach(tp->t_pack, new_thing());
}