view arogue5/player.c @ 111:7f8f43943b1f

Fix some terribly depressing corruption during restore. In rogue5/state.c, rs_read_daemons() zeroes out the argument and delay if the daemon slot is empty. Unfortunately that code ended up on the wrong side of the brace that closes the for loop, so instead of running after each daemon, it got run once after the loop exited, when the index was of course out of bounds. This tended to manifest, when compiled with -O2, by overwriting hw and setting it to NULL. When inventory() next ran, hw would be passed to wgetch(), which returns ERR when it gets a NULL argument. This made md_readchar() think something was wrong and autosave the game. Upon investigation, rogue3 was found to commit the same mistake. rogue4 and srogue don't zero the data. arogue5 already does it properly. Someday I am going to run all this through Valgrind. Someday when I am a kinder person who will not be driven to invoke hordes of trolls and centaurs upon the original authors.
author John "Elwin" Edwards
date Wed, 08 Jan 2014 16:44:16 -0500
parents 34a87d84ea31
children beab22b087a1
line wrap: on
line source

/*
 * This file contains functions for dealing with special player abilities
 *
 * Advanced Rogue
 * Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka and AT&T
 * All rights reserved.
 *
 * See the file LICENSE.TXT for full copyright and licensing information.
 */

#include "curses.h"
#include "rogue.h"


/*
 * affect:
 *	cleric affecting undead
 */

affect()
{
    register struct linked_list *item;
    register struct thing *tp;
    register const char *mname;
    bool see;
    coord new_pos;

    if (player.t_ctype != C_CLERIC && cur_relic[HEIL_ANKH] == 0) {
	msg("Only clerics can affect undead.");
	return;
    }

    new_pos.y = hero.y + delta.y;
    new_pos.x = hero.x + delta.x;

    if (cansee(new_pos.y, new_pos.x)) see = TRUE;
    else see = FALSE;

    /* Anything there? */
    if (new_pos.y < 0 || new_pos.y > LINES-3 ||
	new_pos.x < 0 || new_pos.x > COLS-1 ||
	mvwinch(mw, new_pos.y, new_pos.x) == ' ') {
	msg("Nothing to affect.");
	return;
    }

    if ((item = find_mons(new_pos.y, new_pos.x)) == NULL) {
	debug("Affect what @ %d,%d?", new_pos.y, new_pos.x);
	return;
    }
    tp = THINGPTR(item);
    mname = monsters[tp->t_index].m_name;

    if (on(player, ISINVIS) && off(*tp, CANSEE)) {
	sprintf(outstring,"%s%s cannot see you", see ? "The " : "It",
	    see ? mname : "");
	msg(outstring);
	return;
    }

    if (off(*tp, TURNABLE) || on(*tp, WASTURNED)) 
	goto annoy;
    turn_off(*tp, TURNABLE);

    /* Can cleric kill it? */
    if (pstats.s_lvl >= 3 * tp->t_stats.s_lvl) {
	unsigned long test;	/* For overflow check */

	sprintf(outstring,"You have destroyed %s%s.", see ? "the " : "it", see ? mname : "");
	msg(outstring);
	test = pstats.s_exp + tp->t_stats.s_exp;

	/* Be sure there is no overflow before increasing experience */
	if (test > pstats.s_exp) pstats.s_exp = test;
	killed(item, FALSE, TRUE);
	check_level(TRUE);
	return;
    }

    /* Can cleric turn it? */
    if (rnd(100) + 1 >
	 (100 * ((2 * tp->t_stats.s_lvl) - pstats.s_lvl)) / pstats.s_lvl) {
	unsigned long test;	/* Overflow test */

	/* Make the monster flee */
	turn_on(*tp, WASTURNED);	/* No more fleeing after this */
	turn_on(*tp, ISFLEE);
	runto(tp, &hero);

	/* Let player know */
	sprintf(outstring,"You have turned %s%s.", see ? "the " : "it", see ? mname : "");
	msg(outstring);

	/* get points for turning monster -- but check overflow first */
	test = pstats.s_exp + tp->t_stats.s_exp/2;
	if (test > pstats.s_exp) pstats.s_exp = test;
	check_level(TRUE);

	/* If monster was suffocating, stop it */
	if (on(*tp, DIDSUFFOCATE)) {
	    turn_off(*tp, DIDSUFFOCATE);
	    extinguish(suffocate);
	}

	/* If monster held us, stop it */
	if (on(*tp, DIDHOLD) && (--hold_count == 0))
		turn_off(player, ISHELD);
	turn_off(*tp, DIDHOLD);
	return;
    }

    /* Otherwise -- no go */
annoy:
    sprintf(outstring,"You do not affect %s%s.", see ? "the " : "it", see ? mname : "");
    msg(outstring);

    /* Annoy monster */
   if (off(*tp, ISFLEE)) runto(tp, &hero);
}

/*
 * the magic user is going to try and cast a spell
 */
cast()
{
    register int i, num_spells, spell_ability;
    int  which_spell;
    bool nohw = FALSE;

    i = num_spells = spell_ability = which_spell = 0;

    if (player.t_ctype != C_MAGICIAN && pstats.s_intel < 16) {
	msg("You are not permitted to cast spells.");
	return;
    }
    if (cur_misc[WEAR_CLOAK] != NULL &&
	cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) {
	msg("You can't seem to cast a spell!");
	return;
    }
    num_spells = 0;

    /* Get the number of avilable spells */
    if (pstats.s_intel >= 16) 
	num_spells = pstats.s_intel - 15;

    if (player.t_ctype == C_MAGICIAN) 
	num_spells += pstats.s_lvl;

    if (num_spells > MAXSPELLS) 
	num_spells = MAXSPELLS;

    spell_ability = pstats.s_lvl * pstats.s_intel;
    if (player.t_ctype != C_MAGICIAN)
	spell_ability /= 2;

    /* Prompt for spells */
    msg("Which spell are you casting? (* for list): ");

    which_spell = (int) (readchar() - 'a');
    if (which_spell == (int) ESCAPE - (int) 'a') {
	mpos = 0;
	msg("");
	after = FALSE;
	return;
    }
    if (which_spell >= 0 && which_spell < num_spells) nohw = TRUE;

    else if (slow_invent) {
	register char c;
        char *spellp;

	for (i=0; i<num_spells; i++) {
	    msg("");
	    if (magic_spells[i].s_type == TYP_POTION)
		spellp = p_magic[magic_spells[i].s_which].mi_name;
	    else if (magic_spells[i].s_type == TYP_SCROLL)
		spellp = s_magic[magic_spells[i].s_which].mi_name;
	    else if (magic_spells[i].s_type == TYP_STICK)
		spellp = ws_magic[magic_spells[i].s_which].mi_name;
            mvwprintw(msgw, 0, 0, "[%c] A spell of %s", (char) ((int) 'a' + i),
                      spellp);
	    waddstr(msgw, morestr);
	    draw(msgw);
	    do {
		c = readchar();
	    } while (c != ' ' && c != ESCAPE);
	    if (c == ESCAPE)
		break;
	}
	msg("");
	mvwaddstr(msgw, 0, 0, "Which spell are you casting? ");
	draw(msgw);
    }
    else {
	/* Set up for redraw */
	msg("");
	clearok(cw, TRUE);
	touchwin(cw);

	/* Now display the possible spells */
	wclear(hw);
	touchwin(hw);
	mvwaddstr(hw, 2, 0, "	Cost		Spell");
	mvwaddstr(hw, 3, 0, "-----------------------------------------------");
	for (i=0; i<num_spells; i++) {
	    mvwaddch(hw, i+4, 0, '[');
	    waddch(hw, (char) ((int) 'a' + i));
	    waddch(hw, ']');
	    sprintf(prbuf, "	%3d", magic_spells[i].s_cost);
	    waddstr(hw, prbuf);
	    waddstr(hw, "	A spell of ");
	    if (magic_spells[i].s_type == TYP_POTION)
		waddstr(hw, p_magic[magic_spells[i].s_which].mi_name);
	    else if (magic_spells[i].s_type == TYP_SCROLL)
		waddstr(hw, s_magic[magic_spells[i].s_which].mi_name);
	    else if (magic_spells[i].s_type == TYP_STICK)
		waddstr(hw, ws_magic[magic_spells[i].s_which].mi_name);
	}
	sprintf(prbuf,"[Current spell power = %d]",spell_ability - spell_power);
	mvwaddstr(hw, 0, 0, prbuf);
	waddstr(hw, " Which spell are you casting? ");
	draw(hw);
    }

    if (!nohw) {
	which_spell = (int) (wgetch(hw) - 'a');
	while (which_spell < 0 || which_spell >= num_spells) {
	    if (which_spell == (int) ESCAPE - (int) 'a') {
		after = FALSE;
		return;
	    }
	    wmove(hw, 0, 0);
	    wclrtoeol(hw);
	    waddstr(hw, "Please enter one of the listed spells. ");
	    draw(hw);
	    which_spell = (int) (wgetch(hw) - 'a');
	}
    }

    if ((spell_power + magic_spells[which_spell].s_cost) > spell_ability) {
	msg("Your attempt fails.");
	return;
    }
    if (nohw)
	msg("Your spell is successful.");
    else {
	mvwaddstr(hw, 0, 0, "Your spell is successful.--More--");
	wclrtoeol(hw);
	draw(hw);
	wait_for(hw,' ');
    }
    if (magic_spells[which_spell].s_type == TYP_POTION)
        quaff(	magic_spells[which_spell].s_which,
        	magic_spells[which_spell].s_flag,
		FALSE);
    else if (magic_spells[which_spell].s_type == TYP_SCROLL)
        read_scroll(	magic_spells[which_spell].s_which,
        		magic_spells[which_spell].s_flag,
			FALSE);
    else if (magic_spells[which_spell].s_type == TYP_STICK) {
	 if (!do_zap(	TRUE, 
			magic_spells[which_spell].s_which,
			magic_spells[which_spell].s_flag)) {
	     after = FALSE;
	     return;
	 }
    }
    spell_power += magic_spells[which_spell].s_cost;
}

/* Constitution bonus */

const_bonus()	/* Hit point adjustment for changing levels */
{
    if (pstats.s_const > 6 && pstats.s_const <= 14) 
	return(0);
    if (pstats.s_const > 14) 
	return(pstats.s_const-14);
    if (pstats.s_const > 3) 
	return(-1);
    return(-2);
}


/* Routines for thieves */

/*
 * gsense:
 *	Sense gold
 */

gsense()
{
    /* Only thieves can do this */
    if (player.t_ctype != C_THIEF) {
	msg("You seem to have no gold sense.");
	return;
    }

    if (lvl_obj != NULL) {
	struct linked_list *gitem;
	struct object *cur;
	int gtotal = 0;

	wclear(hw);
	for (gitem = lvl_obj; gitem != NULL; gitem = next(gitem)) {
	    cur = OBJPTR(gitem);
	    if (cur->o_type == GOLD) {
		gtotal += cur->o_count;
		mvwaddch(hw, cur->o_pos.y, cur->o_pos.x, GOLD);
	    }
	}
	if (gtotal) {
	    s_know[S_GFIND] = TRUE;
	    msg("You sense gold!");
	    overlay(hw,cw);
	    return;
	}
    }
    msg("You can sense no gold on this level.");
}

/* 
 * the cleric asks his deity for a spell
 */
pray()
{
    register int i, num_prayers, prayer_ability;
    int which_prayer;
    bool nohw = FALSE;

    which_prayer = num_prayers = prayer_ability = i = 0;

    if (player.t_ctype != C_CLERIC && pstats.s_wisdom < 17 &&
	cur_relic[HEIL_ANKH] == 0) {
	msg("You are not permitted to pray.");
	return;
    }
    if (cur_misc[WEAR_CLOAK] != NULL &&
	cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) {
	msg("You can't seem to pray!");
	return;
    }
    num_prayers = 0;

    /* Get the number of avilable prayers */
    if (pstats.s_wisdom > 16) 
	num_prayers = (pstats.s_wisdom - 15) / 2;

    if (player.t_ctype == C_CLERIC) 
	num_prayers += pstats.s_lvl;

    if (cur_relic[HEIL_ANKH]) num_prayers += 3;

    if (num_prayers > MAXPRAYERS) 
	num_prayers = MAXPRAYERS;

    prayer_ability = pstats.s_lvl * pstats.s_wisdom;
    if (player.t_ctype != C_CLERIC)
	prayer_ability /= 2;

    if (cur_relic[HEIL_ANKH]) prayer_ability *= 2;

    /* Prompt for prayer */
    msg("Which prayer are you offering? (* for list): ");
    which_prayer = (int) (readchar() - 'a');
    if (which_prayer == (int) ESCAPE - (int) 'a') {
	mpos = 0;
	msg("");
	after = FALSE;
	return;
    }
    if (which_prayer >= 0 && which_prayer < num_prayers) nohw = TRUE;

    else if (slow_invent) {
	register char c;
        char *prayerp;

	for (i=0; i<num_prayers; i++) {
	    msg("");
	    if (cleric_spells[i].s_type == TYP_POTION)
		prayerp = p_magic[cleric_spells[i].s_which].mi_name;
	    else if (cleric_spells[i].s_type == TYP_SCROLL)
		prayerp = s_magic[cleric_spells[i].s_which].mi_name;
	    else if (cleric_spells[i].s_type == TYP_STICK)
		prayerp = ws_magic[cleric_spells[i].s_which].mi_name;
            mvwprintw(msgw, 0, 0, "[%c] A prayer for %s", 
                      (char) ((int) 'a' + i), prayerp);
	    waddstr(msgw, morestr);
	    draw(msgw);
	    do {
		c = readchar();
	    } while (c != ' ' && c != ESCAPE);
	    if (c == ESCAPE)
		break;
	}
	msg("");
	mvwaddstr(msgw, 0, 0, "Which prayer are you offering? ");
	draw(msgw);
    }
    else {
	/* Set up for redraw */
	msg("");
	clearok(cw, TRUE);
	touchwin(cw);

	/* Now display the possible prayers */
	wclear(hw);
	touchwin(hw);
	mvwaddstr(hw, 2, 0, "	Cost		Prayer");
	mvwaddstr(hw, 3, 0, "-----------------------------------------------");
	for (i=0; i<num_prayers; i++) {
	    mvwaddch(hw, i+4, 0, '[');
	    waddch(hw, (char) ((int) 'a' + i));
	    waddch(hw, ']');
	    sprintf(prbuf, "	%3d", cleric_spells[i].s_cost);
	    waddstr(hw, prbuf);
	    waddstr(hw, "	A prayer for ");
	    if (cleric_spells[i].s_type == TYP_POTION)
		waddstr(hw, p_magic[cleric_spells[i].s_which].mi_name);
	    else if (cleric_spells[i].s_type == TYP_SCROLL)
		waddstr(hw, s_magic[cleric_spells[i].s_which].mi_name);
	    else if (cleric_spells[i].s_type == TYP_STICK)
		waddstr(hw, ws_magic[cleric_spells[i].s_which].mi_name);
	}
	sprintf(prbuf,"[Current prayer ability = %d]",prayer_ability-pray_time);
	mvwaddstr(hw, 0, 0, prbuf);
	waddstr(hw, " Which prayer are you offering? ");
	draw(hw);
    }

    if (!nohw) {
	which_prayer = (int) (wgetch(hw) - 'a');
	while (which_prayer < 0 || which_prayer >= num_prayers) {
	    if (which_prayer == (int) ESCAPE - (int) 'a') {
		after = FALSE;
		return;
	    }
	    wmove(hw, 0, 0);
	    wclrtoeol(hw);
	    mvwaddstr(hw, 0, 0, "Please enter one of the listed prayers.");
	    draw(hw);
	    which_prayer = (int) (wgetch(hw) - 'a');
	}
    }


    if (cleric_spells[which_prayer].s_cost + pray_time > prayer_ability) {
	msg("Your prayer fails.");
	return;
    }

    if (nohw) {
	msg("Your prayer has been granted.--More--");
	wait_for(msgw,' ');
        msg("");
    }
    else {
	mvwaddstr(hw, 0, 0, "Your prayer has been granted.--More--");
	wclrtoeol(hw);
	draw(hw);
	wait_for(hw,' ');
    }
    if (cleric_spells[which_prayer].s_type == TYP_POTION)
	quaff(		cleric_spells[which_prayer].s_which,
			cleric_spells[which_prayer].s_flag,
			FALSE);
    else if (cleric_spells[which_prayer].s_type == TYP_SCROLL)
	read_scroll(	cleric_spells[which_prayer].s_which,
			cleric_spells[which_prayer].s_flag,
			FALSE);
    else if (cleric_spells[which_prayer].s_type == TYP_STICK) {
	 if (!do_zap(	TRUE, 
			cleric_spells[which_prayer].s_which,
			cleric_spells[which_prayer].s_flag)) {
	     after = FALSE;
	     return;
	 }
    }
    pray_time += cleric_spells[which_prayer].s_cost;
}



/*
 * steal:
 *	Steal in direction given in delta
 */

steal()
{
    register struct linked_list *item;
    register struct thing *tp;
    register const char *mname;
    coord new_pos;
    int thief_bonus = -50;
    bool isinvisible = FALSE;

    new_pos.y = hero.y + delta.y;
    new_pos.x = hero.x + delta.x;

    if (on(player, ISBLIND)) {
	msg("You can't see anything.");
	return;
    }

    /* Anything there? */
    if (new_pos.y < 0 || new_pos.y > LINES-3 ||
	new_pos.x < 0 || new_pos.x > COLS-1 ||
	mvwinch(mw, new_pos.y, new_pos.x) == ' ') {
	msg("Nothing to steal from.");
	return;
    }

    if ((item = find_mons(new_pos.y, new_pos.x)) == NULL)
	debug("Steal from what @ %d,%d?", new_pos.y, new_pos.x);
    tp = THINGPTR(item);
    if (isinvisible = invisible(tp)) mname = "creature";
    else mname = monsters[tp->t_index].m_name;

    /* Can player steal something unnoticed? */
    if (player.t_ctype == C_THIEF) thief_bonus = 10;
    if (on(*tp, ISUNIQUE)) thief_bonus -= 15;
    if (isinvisible) thief_bonus -= 20;
    if (on(*tp, ISINWALL) && off(player, CANINWALL)) thief_bonus -= 50;

    if (rnd(100) <
	(thief_bonus + 2*dex_compute() + 5*pstats.s_lvl -
	 5*(tp->t_stats.s_lvl - 3))) {
	register struct linked_list *s_item, *pack_ptr;
	int count = 0;
	unsigned long test;	/* Overflow check */

	s_item = NULL;	/* Start stolen goods out as nothing */

	/* Find a good item to take */
	for (pack_ptr=tp->t_pack; pack_ptr != NULL; pack_ptr=next(pack_ptr))
	    if ((OBJPTR(pack_ptr))->o_type != RELIC &&
		rnd(++count) == 0)
		s_item = pack_ptr;

	/* 
	 * Find anything?
	 *
	 * if we have a merchant, and his pack is empty then the
	 * rogue has already stolen once
	 */
	if (s_item == NULL) {
	    if (tp->t_index == NUMMONST)
		msg("The %s seems to be shielding his pack from you.", mname);
	    else
	        msg("The %s apparently has nothing to steal.", mname);
	    return;
	}

	/* Take it from monster */
	if (tp->t_pack) detach(tp->t_pack, s_item);

	/* Give it to player */
	if (add_pack(s_item, FALSE, NULL) == FALSE) {
	   (OBJPTR(s_item))->o_pos = hero;
	   fall(s_item, TRUE);
	}

	/* Get points for stealing -- but first check for overflow */
	test = pstats.s_exp + tp->t_stats.s_exp/2;
	if (test > pstats.s_exp) pstats.s_exp = test;

	/*
	 * Do adjustments if player went up a level
	 */
	check_level(TRUE);
    }

    else {
	msg("Your attempt fails.");

	/* Annoy monster (maybe) */
	if (rnd(35) >= dex_compute() + thief_bonus) {
	    if (tp->t_index == NUMMONST) {
		if (!isinvisible)
		    msg("The %s looks insulted and leaves", mname);
		killed(item, FALSE, FALSE);
	    }
	    else
	        runto(tp, &hero);
	}
    }
}