diff xrogue/player.c @ 133:e6179860cb76

Import XRogue 8.0 from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 21 Apr 2015 08:55:20 -0400
parents
children ce0cf824c192
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/xrogue/player.c	Tue Apr 21 08:55:20 2015 -0400
@@ -0,0 +1,1478 @@
+/*
+    player.c - functions for dealing with special player abilities
+
+    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.
+
+    See the file LICENSE.TXT for full copyright and licensing information.
+*/
+
+#include <ctype.h>
+#include <curses.h>
+#include "rogue.h"
+
+/*
+ * affect:
+ *      cleric affecting undead
+ */
+
+affect()
+{
+    register struct linked_list *item;
+    register struct thing *tp;
+    register char *mname;
+    bool see;
+    coord new_pos;
+    int lvl;
+
+    if (!(player.t_ctype == C_CLERIC  ||
+          (player.t_ctype == C_PALADIN && pstats.s_lvl > 4) ||
+          cur_relic[HEIL_ANKH] != 0)) {
+        msg("You cannot affect undead.");
+        return;
+    }
+
+    new_pos.y = hero.y + player.t_newpos.y;
+    new_pos.x = hero.x + player.t_newpos.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)) == 0) {
+        debug("Affect what @ %d,%d?", new_pos.y, new_pos.x);
+        return;
+    }
+    tp = THINGPTR(item);
+    mname = monster_name(tp);
+
+    if (on(player, ISINVIS) && off(*tp, CANSEE)) {
+        msg("%s%s cannot see you", see ? "The " : "It",
+            see ? mname : "");
+        return;
+    }
+
+    if (off(*tp, TURNABLE) || on(*tp, WASTURNED)) 
+        goto annoy;
+    turn_off(*tp, TURNABLE);
+
+    lvl = pstats.s_lvl;
+    if (player.t_ctype == C_PALADIN && cur_relic[HEIL_ANKH] == 0) {
+        lvl -= 4;
+    }
+    /* Can cleric kill it? */
+    if (lvl >= 3 * tp->t_stats.s_lvl) {
+        unsigned long test;      /* For overflow check */
+
+        msg("You have destroyed %s%s.", see ? "the " : "it", see ? mname : "");
+        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, TRUE);
+        check_level();
+        return;
+    }
+
+    /* Can cleric turn it? */
+    if (rnd(100) + 1 >
+         (100 * ((2 * tp->t_stats.s_lvl) - lvl)) / 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);
+
+        /* Disrupt it */
+        dsrpt_monster(tp, TRUE, TRUE);
+
+        /* Let player know */
+        msg("You have turned %s%s.", see ? "the " : "it", see ? mname : "");
+
+        /* 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();
+
+        /* 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);
+
+        /* It is okay to turn tail */
+        tp->t_oldpos = tp->t_pos;
+
+        return;
+    }
+
+    /* Otherwise -- no go */
+annoy:
+    if (see && tp->t_stats.s_intel > 16)
+        msg("%s laughs at you...", prname(mname, TRUE));
+    else
+        msg("You do not affect %s%s.", see ? "the " : "it", see ? mname : "");
+
+    /* Annoy monster */
+   if (off(*tp, ISFLEE)) runto(tp, &hero);
+}
+
+/* 
+ * the cleric asks his deity for a spell
+ */
+
+pray()
+{
+    register int num_prayers, prayer_ability, which_prayer;
+
+    which_prayer = num_prayers = prayer_ability =  0;
+
+    if (player.t_ctype != C_CLERIC  && player.t_ctype != C_PALADIN &&
+        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;
+    }
+
+    prayer_ability = pstats.s_lvl * pstats.s_wisdom - 5;
+    if (player.t_ctype != C_CLERIC)
+        prayer_ability /= 2;
+
+    if (cur_relic[HEIL_ANKH]) prayer_ability += 75;
+
+    if (player.t_action != C_PRAY) {
+        num_prayers = 0;
+
+        /* Get the number of avilable prayers */
+        if (pstats.s_wisdom > 16) 
+            num_prayers += pstats.s_wisdom - 16;
+
+        num_prayers += pstats.s_lvl;
+        if (cur_relic[HEIL_ANKH])
+            num_prayers += pstats.s_wisdom - 18;
+
+        if (player.t_ctype != C_CLERIC) 
+            num_prayers /= 2;
+
+        if (num_prayers > MAXPRAYERS) 
+            num_prayers = MAXPRAYERS;
+        if (num_prayers < 1) {
+            msg("You are not permitted to pray yet.");
+            return;
+        }
+
+        /* Prompt for prayer */
+        if (pick_spell( cleric_spells, 
+                        prayer_ability, 
+                        num_prayers, 
+                        pray_time,
+                        "offer",
+                        "prayer"))
+            player.t_action = C_PRAY;
+
+        return;
+    }
+
+    /* We've waited our required praying time. */
+    which_prayer = player.t_selection;
+    player.t_selection = 0;
+    player.t_action = A_NIL;
+
+    if (cleric_spells[which_prayer].s_cost + pray_time > prayer_ability) {
+        msg("Your prayer fails.");
+        return;
+    }
+
+    msg("Your prayer has been granted. ");
+
+    if (cleric_spells[which_prayer].s_type == TYP_POTION)
+        quaff(          cleric_spells[which_prayer].s_which,
+                        NULL,
+                        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 (!player_zap(cleric_spells[which_prayer].s_which,
+                         cleric_spells[which_prayer].s_flag)) {
+             after = FALSE;
+             return;
+         }
+    }
+    pray_time += cleric_spells[which_prayer].s_cost;
+}
+
+/*
+ * the magician is going to try and cast a spell
+ */
+
+cast()
+{
+    register int spell_ability, which_spell, num_spells;
+
+    if (player.t_ctype != C_MAGICIAN && player.t_ctype != C_RANGER) {
+        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 spells!");
+        return;
+    }
+    spell_ability = pstats.s_lvl * pstats.s_intel - 5;
+    if (player.t_ctype != C_MAGICIAN)
+        spell_ability /= 2;
+
+    if (player.t_action != C_CAST) {
+        /* 
+         * Get the number of avilable spells 
+         */
+        num_spells = 0;
+        if (pstats.s_intel > 16) 
+            num_spells += pstats.s_intel - 16;
+
+        num_spells += pstats.s_lvl;
+        if (player.t_ctype != C_MAGICIAN) 
+            num_spells /= 2;
+        if (num_spells > MAXSPELLS)
+            num_spells = MAXSPELLS;
+        if (num_spells < 1) {
+            msg("You are not allowed to cast spells yet.");
+            return;
+        }
+
+    /* prompt for spell */
+        if (pick_spell( magic_spells, 
+                        spell_ability, 
+                        num_spells, 
+                        spell_power,
+                        "cast",
+                        "spell"))
+            player.t_action = C_CAST;
+        return;
+    }
+
+    /* We've waited our required casting time. */
+    which_spell = player.t_selection;
+    player.t_selection = 0;
+    player.t_action = A_NIL;
+
+    if ((spell_power + magic_spells[which_spell].s_cost) > spell_ability) {
+        msg("Your attempt fails.");
+        return;
+    }
+
+    msg("Your spell is successful. ");
+
+    if (magic_spells[which_spell].s_type == TYP_POTION)
+        quaff(  magic_spells[which_spell].s_which,
+                NULL,
+                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 (!player_zap(magic_spells[which_spell].s_which,
+                         magic_spells[which_spell].s_flag)) {
+             after = FALSE;
+             return;
+         }
+    }
+    spell_power += magic_spells[which_spell].s_cost;
+}
+
+/* 
+ * the druid asks his deity for a spell
+ */
+
+chant()
+{
+    register int num_chants, chant_ability, which_chant;
+
+    which_chant = num_chants = chant_ability = 0;
+
+    if (player.t_ctype != C_DRUID && player.t_ctype != C_MONK) {
+        msg("You are not permitted to chant.");
+        return;
+    }
+    if (cur_misc[WEAR_CLOAK] != NULL &&
+        cur_misc[WEAR_CLOAK]->o_which == MM_R_POWERLESS) {
+        msg("You can't seem to chant!");
+        return;
+    }
+    chant_ability = pstats.s_lvl * pstats.s_wisdom - 5;
+    if (player.t_ctype != C_DRUID)
+        chant_ability /= 2;
+
+    if (player.t_action != C_CHANT) {
+        num_chants = 0;
+
+        /* Get the number of avilable chants */
+        if (pstats.s_wisdom > 16) 
+            num_chants += pstats.s_wisdom - 16;
+
+        num_chants += pstats.s_lvl;
+
+        if (player.t_ctype != C_DRUID) 
+            num_chants /= 2;
+
+        if (num_chants > MAXCHANTS) 
+            num_chants = MAXCHANTS;
+
+        if (num_chants < 1) {
+            msg("You are not permitted to chant yet.");
+            return;
+        }
+
+        /* Prompt for chant */
+        if (pick_spell( druid_spells, 
+                        chant_ability, 
+                        num_chants, 
+                        chant_time,
+                        "sing",
+                        "chant"))
+            player.t_action = C_CHANT;
+
+        return;
+    }
+
+    /* We've waited our required chanting time. */
+    which_chant = player.t_selection;
+    player.t_selection = 0;
+    player.t_action = A_NIL;
+
+    if (druid_spells[which_chant].s_cost + chant_time > chant_ability) {
+        msg("Your chant fails.");
+        return;
+    }
+
+    msg("Your chant has been granted. ");
+
+    if (druid_spells[which_chant].s_type == TYP_POTION)
+        quaff(          druid_spells[which_chant].s_which,
+                        NULL,
+                        druid_spells[which_chant].s_flag,
+                        FALSE);
+    else if (druid_spells[which_chant].s_type == TYP_SCROLL)
+        read_scroll(    druid_spells[which_chant].s_which,
+                        druid_spells[which_chant].s_flag,
+                        FALSE);
+    else if (druid_spells[which_chant].s_type == TYP_STICK) {
+         if (!player_zap(druid_spells[which_chant].s_which,
+                         druid_spells[which_chant].s_flag)) {
+             after = FALSE;
+             return;
+         }
+    }
+    chant_time += druid_spells[which_chant].s_cost;
+}
+
+/* Constitution bonus */
+
+const_bonus()   /* Hit point adjustment for changing levels */
+{
+    register int bonus;
+    if (pstats.s_const > 9 && pstats.s_const < 18) 
+        bonus = 0;
+    else if (pstats.s_const >= 18 && pstats.s_const < 20) 
+        bonus = 1;
+    else if (pstats.s_const >= 20 && pstats.s_const < 26) 
+        bonus = 2;
+    else if (pstats.s_const >= 26 && pstats.s_const < 36) 
+        bonus = 3;
+    else if (pstats.s_const >= 36) 
+        bonus = 4;
+    else if (pstats.s_const > 7) 
+        bonus = -1;
+    else
+        bonus = -2;
+    switch(player.t_ctype) {
+        case C_FIGHTER:         bonus = min(bonus, 11);
+        when C_RANGER:          bonus = min(bonus,  9);
+        when C_PALADIN:         bonus = min(bonus,  9);
+        when C_MAGICIAN:        bonus = min(bonus,  8);
+        when C_CLERIC:          bonus = min(bonus,  8);
+        when C_THIEF:           bonus = min(bonus, 10);
+        when C_ASSASSIN:        bonus = min(bonus, 10);
+        when C_DRUID:           bonus = min(bonus,  8);
+        when C_MONK:            bonus = min(bonus,  9);
+        otherwise:              bonus = min(bonus,  7);
+    }
+    return(bonus);
+}
+
+/*
+ * Give away slime-molds to monsters.  If monster is friendly, 
+ * it will give you a "regular" food ration in return.  You have
+ * to give a slime-mold to Alteran (a unique monster) in order to 
+ * get the special "Card of Alteran" quest item.  There's no other
+ * way to get this artifact and remain alive.
+ */
+
+give(th)
+register struct thing *th;
+{
+    /*
+     * Find any monster within one space of you
+     */
+    struct linked_list *ll;
+    struct object *lb;
+    register int x,y;
+    register struct linked_list *mon = NULL;
+    bool gotone = FALSE;
+
+    if (levtype != POSTLEV) {  /* no monsters at trading post  */
+        for (x = hero.x-1; x <= hero.x+1; x++) {
+            for (y = hero.y-1; y <= hero.y+1; y++) {
+                if (y < 1 || x < 0 || y > lines - 3 || x > cols - 1)
+                    continue;
+                if (isalpha(mvwinch(mw, y, x))) {
+                    if ((mon = find_mons(y, x)) != NULL) {
+                        gotone = TRUE;  /* found a monster to give away to */
+                        th = THINGPTR(mon);
+                    }
+                }
+            }
+        }
+    }
+    if (gotone) {
+        if ((ll=get_item(pack, "give away", ALL, FALSE, FALSE)) != NULL) {
+            lb = OBJPTR(ll);
+            mpos = 0;
+            switch(lb->o_type) {
+                case FOOD:
+                    switch (lb->o_which) {
+                        case E_SLIMEMOLD:   /* only slime-molds for now */
+                if (on(*th, CANSELL)) {  /* quartermaster */
+                msg("%s laughs at you. ");
+                return;
+                }
+                if ((on(*th, ISFRIENDLY) || off(*th, ISMEAN)) &&
+                off(*th, ISUNIQUE) && off(*th, CANSELL)) {
+                turn_on(*th, ISRUN); /* we want him awake */
+                                msg("%s accepts and promptly eats your gift of food.  --More--", prname(monster_name(th), TRUE));
+                wait_for(' ');
+                    del_pack(ll); /* delete slime-mold */
+                          /* and add a food ration */
+                create_obj(FALSE, FOOD, E_RATION);
+                msg("%s gives you food in return and nods off to sleep. ", prname(monster_name(th), TRUE));
+                turn_off(*th, ISRUN); /* put him to sleep */
+                return;
+                }
+                else if (on(*th, CARRYCARD) && on(*th, ISUNIQUE)) {
+                /* Now you get the Card of Alteran */
+                msg("%s gives you a strange rectangular card. --More--", prname(monster_name(th), TRUE));
+                wait_for(' ');
+                    del_pack(ll); /* get rid of slime-mold */
+                create_obj(FALSE, RELIC, ALTERAN_CARD);
+                msg("%s bids you farewell. ", prname(monster_name(th), TRUE));
+                killed(mon, FALSE, FALSE, FALSE);
+                return;
+                }
+                else if (on(*th, ISUNIQUE) && off(*th, ISMEAN)) {
+                /* Dragons */
+                msg("%s is set free by your generosity. ", prname(monster_name(th), TRUE));
+                    del_pack(ll);  /* get rid of it */
+                /* just let him roam around */
+                turn_on(*th, ISRUN);
+                if (on(*th, ISFLEE)) turn_off(*th, ISFLEE);
+                runto(th, &player);
+                th->t_action = A_NIL;
+                return;
+                }
+                else if (on(*th, ISRUN) && off(*th, ISUNIQUE)) {
+                /* if NOT sleeping and not a unique */
+                switch (rnd(2)) {
+                    case 0: msg("%s ignores you. ", prname(monster_name(th), TRUE));
+                    when 1: {
+                    msg("%s nips at your hand. ", prname(monster_name(th), TRUE));
+                    if (rnd(100) < 10) { 
+                                del_pack(ll); /* delete it */
+                        if (off(*th, ISMEAN)) {
+                        msg("The slime-mold makes %s sleepy. ", prname(monster_name(th), TRUE));
+                        /* put him to sleep */
+                        turn_off(*th, ISRUN);
+                        return;
+                        }
+                        else {  
+                        switch (rnd(2)) {
+                            case 0: msg("%s's eyes roll back. ", prname(monster_name(th), TRUE));
+                                when 1: msg("%s becomes wanderlust. ", prname(monster_name(th), TRUE));
+                        }
+                        /* just let him roam around */
+                            turn_on(*th, ISRUN);
+                        if (on(*th, ISFLEE))
+                                        turn_off(*th, ISFLEE);
+                        runto(th, &player);
+                        th->t_action = A_NIL;
+                        return;
+                        }
+                    }
+                    }
+                }
+                }
+                else {
+                msg("%s's mouth waters. ", prname(monster_name(th), TRUE));
+                /* this wakes him up */
+                if (off(*th, ISUNIQUE)) turn_on(*th, ISRUN);
+                return;
+                }
+                otherwise:
+                switch (rnd(3)) {  /* mention food (hint hint) */
+                    case 0: msg("You cannot give away the %s! ", foods[lb->o_which].mi_name);
+                    when 1: msg("The %s looks rancid! ", foods[lb->o_which].mi_name);
+                    when 2: msg("You change your mind. ");
+                }
+                return;
+                    }
+            otherwise:
+            switch (rnd(3)) {  /* do not mention other items */
+                    case 0: msg("You feel foolish. ");
+                    when 1: msg("You change your mind. ");
+                    when 2: msg("%s ignores you. ", prname(monster_name(th), TRUE));
+            }
+
+        return;
+            }
+        }
+    }
+    else msg("Your efforts are futile. ");
+    return;
+}
+
+/*
+ * Frighten a monster.  Useful for the 'good' characters.
+ */
+
+fright(th)
+register struct thing *th;
+{
+    /*
+     * Find any monster within one space of you
+     */
+    register int x,y;
+    register struct linked_list *mon;
+    bool gotone = FALSE;
+
+    if (levtype != POSTLEV) {  /* no monsters at trading post  */
+        for (x = hero.x-1; x <= hero.x+1; x++) {
+            for (y = hero.y-1; y <= hero.y+1; y++) {
+                if (y < 1 || x < 0 || y > lines - 3 || x > cols - 1)
+                    continue;
+                if (isalpha(mvwinch(mw, y, x))) {
+                    if ((mon = find_mons(y, x)) != NULL) {
+                        gotone = TRUE;  /* found a monster to give away to */
+                        th = THINGPTR(mon);
+                    }
+                }
+            }
+        }
+    }
+    if (gotone) {  /* If 'good' character or is wearing a ring of fear */
+        if (player.t_ctype == C_RANGER || player.t_ctype == C_PALADIN ||
+            player.t_ctype == C_MONK   || ISWEARING(R_FEAR) != 0) { 
+
+            player.t_action = A_NIL;
+            player.t_no_move = movement(&player);
+            switch (player.t_ctype) {
+                case C_FIGHTER:   /* loss of strength */
+                    pstats.s_str--;
+                    if (pstats.s_str < 3) pstats.s_str = 3;
+                when C_RANGER:    /* loss of charisma */
+        case C_PALADIN:
+                    pstats.s_charisma--;
+                    if (pstats.s_charisma < 3) pstats.s_charisma = 3;
+                when C_CLERIC:    /* loss of wisdom */
+        case C_DRUID:
+                    pstats.s_wisdom--;
+                    if (pstats.s_wisdom < 3) pstats.s_wisdom = 3;
+                when C_MAGICIAN:  /* loss of wisdom intelligence */
+            pstats.s_intel--;
+                    if (pstats.s_intel < 3) pstats.s_intel = 3;