view arogue7/daemons.c @ 296:000b1c5b8d63

UltraRogue: fix inventory collision after save and restore. Inventory letters are based on "identifiers" stored in objects' o_ident field. Identifiers are allocated by get_ident(), which keeps a list of objects that have them, to avoid giving the same identifier to multiple objects. The list is not stored in the savefile, so after restore, get_ident() was not aware of existing identifiers. This resulted in picked-up objects having the same inventory letters as objects restored from the file. The restore code now adds all objects with identifiers to the list.
author John "Elwin" Edwards
date Mon, 15 Jan 2018 20:20:35 -0500
parents e1cd27c5464f
children
line wrap: on
line source

/*
 * daemon.c  -  All the daemon and fuse functions are in here
 *
 * 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.
 */

/*
 * All the daemon and fuse functions are in here
 *
 */

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

/*
 * doctor:
 *	A healing daemon that restors hit points after rest
 */

void
doctor(struct thing *tp)
{
    register int ohp;
    register int limit, new_points;
    register struct stats *curp; /* current stats pointer */
    register struct stats *maxp; /* max stats pointer */

    curp = &(tp->t_stats);
    maxp = &(tp->maxstats);
    if (curp->s_hpt == maxp->s_hpt) {
	tp->t_quiet = 0;
	return;
    }
    tp->t_quiet++;
    switch (tp->t_ctype) {
	case C_MAGICIAN:
	    limit = 8 - curp->s_lvl;
	    new_points = curp->s_lvl - 3;
	when C_THIEF:
	case C_ASSASIN:
	case C_MONK:
	    limit = 8 - curp->s_lvl;
	    new_points = curp->s_lvl - 2;
	when C_CLERIC:
	case C_DRUID:
	    limit = 8 - curp->s_lvl;
	    new_points = curp->s_lvl - 3;
	when C_FIGHTER:
	case C_RANGER:
	case C_PALADIN:
	    limit = 16 - curp->s_lvl*2;
	    new_points = curp->s_lvl - 5;
	when C_MONSTER:
	    limit = 16 - curp->s_lvl;
	    new_points = curp->s_lvl - 6;
	otherwise:
	    debug("what a strange character you are!");
	    return;
    }
    ohp = curp->s_hpt;
    if (off(*tp, HASDISEASE) && off(*tp, DOROT)) {
	if (curp->s_lvl < 8) {
	    if (tp->t_quiet > limit) {
		curp->s_hpt++;
		tp->t_quiet = 0;
	    }
	}
	else {
	    if (tp->t_quiet >= 3) {
		curp->s_hpt += rnd(new_points)+1;
		tp->t_quiet = 0;
	    }
	}
    }
    if (tp == &player) {
	if (ISRING(LEFT_1, R_REGEN)) curp->s_hpt++;
	if (ISRING(LEFT_2, R_REGEN)) curp->s_hpt++;
	if (ISRING(LEFT_3, R_REGEN)) curp->s_hpt++;
	if (ISRING(LEFT_4, R_REGEN)) curp->s_hpt++;
	if (ISRING(RIGHT_1, R_REGEN)) curp->s_hpt++;
	if (ISRING(RIGHT_2, R_REGEN)) curp->s_hpt++;
	if (ISRING(RIGHT_3, R_REGEN)) curp->s_hpt++;
	if (ISRING(RIGHT_4, R_REGEN)) curp->s_hpt++;
    }
    if (on(*tp, ISREGEN))
	curp->s_hpt += curp->s_lvl/10 + 1;
    if (ohp != curp->s_hpt) {
	if (curp->s_hpt >= maxp->s_hpt) {
	    curp->s_hpt = maxp->s_hpt;
	    if (off(*tp, WASTURNED) && on(*tp, ISFLEE) && tp != &player) {
		turn_off(*tp, ISFLEE);
		tp->t_oldpos = tp->t_pos;	/* Start our trek over */
	    }
	}
    }
}

/*
 * Swander:
 *	Called when it is time to start rolling for wandering monsters
 */

void
swander(void)
{
    start_daemon(rollwand, NULL, BEFORE);
}

/*
 * rollwand:
 *	Called to roll to see if a wandering monster starts up
 */

int between = 0;

void
rollwand(void)
{

    if (++between >= 4)
    {
	/* Theives may not awaken a monster */
	if ((roll(1, 6) == 4) &&
	   ((player.t_ctype != C_THIEF && player.t_ctype != C_ASSASIN) || 
	    (rnd(30) >= dex_compute()))) {
	    if (levtype != POSTLEV)
	        wanderer();
	    kill_daemon(rollwand);
	    fuse(swander, NULL, WANDERTIME, BEFORE);
	}
	between = 0;
    }
}
/*
 * this function is a daemon called each turn when the character is a thief
 */
void
trap_look(void)
{
    if (rnd(100) < (2*dex_compute() + 5*pstats.s_lvl))
	search(TRUE, FALSE);
}

/*
 * unconfuse:
 *	Release the poor player from his confusion
 */

void
unconfuse(void)
{
    turn_off(player, ISHUH);
    msg("You feel less confused now");
}


/*
 * unsee:
 *	He lost his see invisible power
 */
void
unsee(void)
{
    if (!ISWEARING(R_SEEINVIS)) {
	turn_off(player, CANSEE);
	msg("The tingling feeling leaves your eyes");
    }
}

/*
 * unstink:
 *	Remove to-hit handicap from player
 */

void
unstink(void)
{
    turn_off(player, HASSTINK);
}

/*
 * unclrhead:
 *	Player is no longer immune to confusion
 */

void
unclrhead(void)
{
    turn_off(player, ISCLEAR);
    msg("The blue aura about your head fades away.");
}

/*
 * unphase:
 *	Player can no longer walk through walls
 */

void
unphase(void)
{
    turn_off(player, CANINWALL);
    msg("Your dizzy feeling leaves you.");
    if (!step_ok(hero.y, hero.x, NOMONST, &player)) death(D_PETRIFY);
}

/*
 * land:
 *	Player can no longer fly
 */

void
land(void)
{
    turn_off(player, ISFLY);
    msg("You regain your normal weight");
    running = FALSE;
}

/*
 * sight:
 *	He gets his sight back
 */

void
sight(void)
{
    if (on(player, ISBLIND))
    {
	extinguish(sight);
	turn_off(player, ISBLIND);
	light(&hero);
	msg("The veil of darkness lifts");
    }
}

/*
 * res_strength:
 *	Restore player's strength
 */

void
res_strength(int howmuch)
{

    /* If lost_str is non-zero, restore that amount of strength,
     * else all of it 
     */
    if (lost_str) {
	chg_str(lost_str);
	lost_str = 0;
    }

    /* Now, add in the restoral, but no greater than maximum strength */
    if (howmuch > 0)
	pstats.s_str =
    	    min(pstats.s_str + howmuch, max_stats.s_str + ring_value(R_ADDSTR));

    updpack(TRUE, &player);
}

/*
 * nohaste:
 *	End the hasting
 */

void
nohaste(void)
{
    turn_off(player, ISHASTE);
    msg("You feel yourself slowing down.");
}

/*
 * noslow:
 *	End the slowing
 */

void
noslow(void)
{
    turn_off(player, ISSLOW);
    msg("You feel yourself speeding up.");
}

/*
 * suffocate:
 *	If this gets called, the player has suffocated
 */

void
suffocate(void)
{
    death(D_SUFFOCATION);
}

/*
 * digest the hero's food
 */
void
stomach(void)
{
    register int oldfood, old_hunger, food_use, i;

    /* 
     * avoid problems of fainting while eating by just not saying it
     * takes food to eat food
     */
    if (player.t_action == C_EAT) 
	return;

    old_hunger = hungry_state;
    if (food_left <= 0)
    {
	/*
	 * the hero is fainting
	 */
	if (player.t_action == A_FREEZE)
		return;
	if (rnd(100) > 20)
	    return;
	if (hungry_state == F_FAINT && rnd(20) == 7) /*must have fainted once*/
		death(D_STARVATION);
	player.t_action = A_FREEZE;
	player.t_no_move = movement(&player) * (rnd(8) + 4);
	if (!terse)
	    addmsg("You feel too weak from lack of food. ");
	msg("You faint");
	running = FALSE;
	if (fight_flush) md_flushinp();
	count = 0;
	hungry_state = F_FAINT;
    }
    else
    {
	oldfood = food_left;
	food_use = 0;
	for (i=0; i<MAXRELIC; i++) { /* each relic eats an additional food */
	    if (cur_relic[i])
		food_use++;
	}
	food_use +=    (ring_eat(LEFT_1)  + ring_eat(LEFT_2)  +
			ring_eat(LEFT_3)  + ring_eat(LEFT_4)  +
	    		ring_eat(RIGHT_1) + ring_eat(RIGHT_2) +
			ring_eat(RIGHT_3) + ring_eat(RIGHT_4) + 
			foodlev);
	if (food_use < 1)
	    food_use = 1;
	food_left -= food_use;
	if (food_left < MORETIME && oldfood >= MORETIME) {
	    msg("You are starting to feel weak");
	    running = FALSE;
	    if (fight_flush) md_flushinp();
	    count = 0;
	    hungry_state = F_WEAK;
	}
	else if (food_left < 2 * MORETIME && oldfood >= 2 * MORETIME)
	{
	    msg(terse ? "Getting hungry" : "You are starting to get hungry");
	    running = FALSE;
	    hungry_state = F_HUNGRY;
	}
	else if(food_left<STOMACHSIZE-MORETIME && oldfood>=STOMACHSIZE-MORETIME)
	{
	    hungry_state = F_OKAY;
	}
    }
    if (old_hunger != hungry_state)  {
	updpack(TRUE, &player);
	status(TRUE);
    }
    wghtchk();
}
/*
 * daemon for curing the diseased
 */
void
cure_disease(void)
{
    turn_off(player, HASDISEASE);
    if (off (player, HASINFEST))
	msg(terse ? "You feel yourself improving"
		: "You begin to feel yourself improving again");
}

/*
 * appear:
 *	Become visible again
 */
void
appear(void)
{
    turn_off(player, ISINVIS);
    PLAYER = VPLAYER;
    msg("The tingling feeling leaves your body");
    light(&hero);
}
/*
 * dust_appear:
 *	dust of disappearance wears off
 */
void
dust_appear(void)
{
    turn_off(player, ISINVIS);
    PLAYER = VPLAYER;
    msg("You become visible again");
    light(&hero);
}
/*
 * unchoke:
 * 	the effects of "dust of choking and sneezing" wear off
 */
void
unchoke(void)
{
    if (!find_slot(unconfuse))
	turn_off(player, ISHUH);
    if (!find_slot(sight))
	turn_off(player, ISBLIND);
    light(&hero);
    msg("Your throat and eyes return to normal");
}
/*
 * make some potion for the guy in the Alchemy jug
 */
void
alchemy(struct object *obj)
{
    register struct object *tobj = NULL;
    register struct linked_list *item;

    /*
     * verify that the object pointer we have still points to an alchemy
     * jug (hopefully the right one!) because the hero could have thrown
     * it away
     */
    for (item = pack; item != NULL; item = next(item)) {
	tobj = OBJPTR(item);
	if (tobj	 == obj		&& 
	    tobj->o_type == MM		&& 
	    tobj->o_which== MM_JUG	&&
	    tobj->o_ac   == JUG_EMPTY	)
		break;
    }
    if (item == NULL) { 	/* not in the pack, check the level */
	for (item = lvl_obj; item != NULL; item = next(item)) {
	    tobj = OBJPTR(item);
	    if (tobj	     == obj		&& 
		tobj->o_type == MM		&& 
		tobj->o_which== MM_JUG		&&
		tobj->o_ac   == JUG_EMPTY	)
		    break;
	}
    }
    if (item == NULL)	/* can't find it.....too bad */
	return;
    
    switch(rnd(11)) {
	case 0: tobj->o_ac = P_PHASE;
	when 1: tobj->o_ac = P_CLEAR;
	when 2: tobj->o_ac = P_SEEINVIS;
	when 3: tobj->o_ac = P_HEALING;
	when 4: tobj->o_ac = P_MFIND;
	when 5: tobj->o_ac = P_TFIND;
	when 6: tobj->o_ac = P_HASTE;
	when 7: tobj->o_ac = P_RESTORE;
	when 8: tobj->o_ac = P_FLY;
	when 9: tobj->o_ac = P_SKILL;
	when 10:tobj->o_ac = P_FFIND;
    }
}
/*
 * otto's irresistable dance wears off 
 */

void
undance(void)
{
    turn_off(player, ISDANCE);
    msg ("Your feet take a break.....whew!");
}

/* 
 * if he has our favorite necklace of strangulation then take damage every turn
 */
void
strangle(void)
{
     if ((pstats.s_hpt -= 6) <= 0) death(D_STRANGLE);
}
/*
 * if he has on the gauntlets of fumbling he might drop his weapon each turn
 */
void
fumble(void)
{
    register struct linked_list *item;

    if (cur_weapon!=NULL			&&
	!(cur_weapon->o_flags & ISCURSED)	&&
	cur_weapon->o_type != RELIC		&&
	rnd(100)<3) {
	for (item = pack; item != NULL; item = next(item)) {
	    if (OBJPTR(item) == cur_weapon)
		break;
	}
	if (item != NULL) {
	    switch(mvwinch(stdscr, hero.y, hero.x)) {
	    case PASSAGE: 
	    case SCROLL:
	    case POTION:
	    case WEAPON:
	    case FLOOR:
	    case STICK:
	    case ARMOR:
	    case POOL:
	    case RELIC:
	    case GOLD:
	    case FOOD:
	    case RING:
	    case MM:
		drop(item);
		running = FALSE;
		break;
	    default:
		break;
	    }
	}
    }
}
/*
 * this is called each turn the hero has the ring of searching on
 */
void
ring_search(void)
{
    search(FALSE, FALSE);
}
/*
 * this is called each turn the hero has the ring of teleportation on
 */
void
ring_teleport(void)
{
    if (rnd(100) < 2) teleport();
}
/* 
 * this is called to charge up the quill of Nagrom
 */
void
quill_charge(void)
{
    register struct object *tobj = NULL;
    register struct linked_list *item;

    /*
     * find the Quill of Nagrom in the hero's pack. It should still be there
     * because it can't be dropped. If its not then don't do anything.
     */
    for (item = pack; item != NULL; item = next(item)) {
	tobj = OBJPTR(item);
	if (tobj->o_type == RELIC && tobj->o_which == QUILL_NAGROM)
		break;
    }
    if (item == NULL)
	return;
    if (tobj->o_charges < QUILLCHARGES)
	tobj->o_charges++;
    fuse (quill_charge, 0, player.t_ctype == C_MAGICIAN ? 4 : 8, AFTER);
}
/*
 * take the skills away gained (or lost) by the potion of skills
 */
void
unskill(void)
{
    if (pstats.s_lvladj != 0) {
	pstats.s_lvl -= pstats.s_lvladj;
	pstats.s_lvladj = 0;
	msg("You feel your normal skill level return.");
	status(TRUE);
    }
}
/*
 * charge up the cloak of Emori
 */

void
cloak_charge(struct object *obj)
{
	if (obj->o_charges < 1)
		obj->o_charges = 1;
}

/*
 * nofire:
 *	He lost his fire resistance
 */
void
nofire(void)
{
    if (!ISWEARING(R_FIRE)) {
	turn_off(player, NOFIRE);
	msg("Your feeling of fire resistance leaves you");
    }
}

/*
 * nocold:
 *	He lost his cold resistance
 */
void
nocold(void)
{
    if (!ISWEARING(R_WARMTH)) {
	turn_off(player, NOCOLD);
	msg("Your feeling of warmth leaves you");
    }
}

/*
 * nobolt:
 *	He lost his protection from lightning
 */
void
nobolt(void)
{
    turn_off(player, NOBOLT);
    msg("Your skin looses its bluish tint");
}
/*
 * eat_gold:
 *	an artifact eats gold 
 */
void
eat_gold(struct object *obj)
{
    if (purse == 1)
	msg("%s demand you find more gold", inv_name(obj, FALSE));
    if (purse == 0) {
	if (--pstats.s_hpt <= 0)
	    death(D_RELIC);
    }
    else
	purse--;
}
/*
 * give the hero back some spell points
 */
void
spell_recovery(void)
{
    int time;

    time = SPELLTIME - max(17-pstats.s_intel, 0);
    time = max(time, 5);
    if (spell_power > 0) spell_power--;
    fuse(spell_recovery, NULL, time, AFTER);
}
/*
 * give the hero back some prayer points
 */
void
prayer_recovery(void)
{
    int time;

    time = SPELLTIME - max(17-pstats.s_wisdom, 0);
    time = max(time, 5);
    if (pray_time > 0) pray_time--;
    fuse(prayer_recovery, NULL, time, AFTER);
}
/*
 * give the hero back some chant points
 */
void
chant_recovery(void)
{
    int time;

    time = SPELLTIME - max(17-pstats.s_wisdom, 0);
    time = max(time, 5);
    if (chant_time > 0) chant_time--;
    fuse(chant_recovery, NULL, time, AFTER);
}