/*
    daemons.c - All the daemon and fuse functions are in here
  
    XRogue: Expeditions into the Dungeons of Doom
    Copyright (C) 1991 Robert Pietkivitch
    All rights reserved.
    
    Based on "Advanced Rogue"
    Copyright (C) 1984, 1985 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.
*/

#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 = 10 - curp->s_lvl;
            new_points = curp->s_lvl - 2;
        when C_CLERIC:
        case C_DRUID:
            limit = 12 - curp->s_lvl;
            new_points = curp->s_lvl - 3;
        when C_THIEF:
        case C_ASSASSIN:
            limit = 14 - curp->s_lvl;
            new_points = curp->s_lvl - 4;
        when C_MONK:
            limit = 16 - curp->s_lvl;
            new_points = curp->s_lvl - 5;
        when C_RANGER:
        case C_PALADIN:
            limit = 18 - curp->s_lvl;
            new_points = curp->s_lvl - 6;
        when C_FIGHTER:
            limit = 20 - curp->s_lvl;
            new_points = curp->s_lvl - 7;
        when C_MONSTER:
            limit = 15 - curp->s_lvl;
            new_points = curp->s_lvl - 5;
        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_ASSASSIN) || 
            (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)) 
        msg("You begin to feel weird.. ");
}

/*
 * 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(long 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)
{
    pstats.s_hpt = -1;
    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) > 12)
            return;
        if (hungry_state == F_FAINT && rnd(28) == 7) /*must have fainted once*/
    {
        pstats.s_hpt = -1;
        msg("You starve to death!!  --More-- ");
        wait_for(' ');
                death(D_STARVATION);
    }
        player.t_action = A_FREEZE;
        player.t_no_move = movement(&player) * (rnd(8) + 3);
        if (!terse)
            addmsg("You feel too weak from the lack of food. ");
        msg("You faint");
        running = FALSE;
        if (fight_flush) 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++;
        }
        /* Charge for wearing rings */
        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) 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) {
     pstats.s_hpt = -1;
     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
 * it's a lot like trap_look() 
 */
 
void
ring_search(void)
{
    if (rnd(75) < (2*dex_compute() + 5*pstats.s_lvl)) search(TRUE, FALSE);
    else search(FALSE, FALSE);
}

/*
 * this is called each turn the hero has the ring of teleportation on
 */
 
void
ring_teleport(void)
{
    if (rnd(100) < 3) 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, NULL, 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 loses its bluish tint");
}

/*
 * eat_gold:
 *      an artifact eats gold 
 */
 
void
eat_gold(struct object *obj)
{
    if (purse == 250)
        msg("%s.. Bids you to find some more gold. ", inv_name(obj, FALSE));
    if (purse == 100)
        msg("%s.. Demands that you find more gold! ", inv_name(obj, FALSE));
    if (purse == 50)
        msg("%s.. Commands you to find more gold!! ", inv_name(obj, FALSE));
    if (purse == 0) {
    if (rnd(10) >= 7)
        msg("You feel the artifact gnawing away... ");
        if (--pstats.s_hpt < 1) {
        pstats.s_hpt = -1;
            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);
}

