diff urogue/magic.c @ 256:c495a4f288c6

Import UltraRogue from the Roguelike Restoration Project (r1490)
author John "Elwin" Edwards
date Tue, 31 Jan 2017 19:56:04 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/urogue/magic.c	Tue Jan 31 19:56:04 2017 -0500
@@ -0,0 +1,878 @@
+/*
+    magic.c - This file contains functions for casting magic spells
+   
+    UltraRogue: The Ultimate Adventure in the Dungeons of Doom
+    Copyright (C) 1986, 1992, 1993, 1995 Herb Chong
+    All rights reserved.
+
+    Based on "Advanced Rogue"
+    Copyright (C) 1984, 1985 Michael Morgan, Ken Dalka
+    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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "rogue.h"
+
+/*
+         Cost for each level of spells level:
+*/
+
+static const int spell_cost[] = {1, 5, 17, 29, 53, 91, 159, 247, 396};
+
+static struct spells monst_spells[] =
+{
+    {5, S_SELFTELEP, SCR_MAGIC},
+    {4, P_HEALING, POT_MAGIC | _TWO_},
+    {3, P_REGENERATE, POT_MAGIC},
+    {2, P_HEALING, POT_MAGIC},
+    {4, P_HASTE, POT_MAGIC},
+    {2, P_SEEINVIS, POT_MAGIC},
+    {3, P_SHERO, POT_MAGIC},
+    {5, P_PHASE, POT_MAGIC},
+    {4, P_INVIS, POT_MAGIC},
+    {4, WS_CANCEL, ZAP_MAGIC},
+
+    /* In reverse order of damage ability */
+    {6, WS_ELECT, ZAP_MAGIC | _TWO_},
+    {6, WS_FIRE, ZAP_MAGIC | _TWO_},
+    {6, WS_COLD, ZAP_MAGIC | _TWO_},
+    {6, WS_MISSILE, ZAP_MAGIC | _TWO_},
+    {5, WS_ELECT, ZAP_MAGIC},
+    {5, WS_FIRE, ZAP_MAGIC},
+    {5, WS_COLD, ZAP_MAGIC},
+    {4, WS_ELECT, ZAP_MAGIC | ISCURSED},
+    {4, WS_FIRE, ZAP_MAGIC | ISCURSED},
+    {4, WS_COLD, ZAP_MAGIC | ISCURSED},
+    {3, WS_MISSILE, ZAP_MAGIC},
+    {1, WS_MISSILE, ZAP_MAGIC | ISCURSED},
+
+    {-1, -1, 0}
+};
+
+/*
+    Spells that a player can cast Non-mus only know ISKNOW spells until found
+    in the dungeon. Special classes know their spells one level lower, and
+    blessed one above.
+*/
+
+static struct spells player_spells[] =
+{
+    {1, WS_KNOCK, ZAP_MAGIC | ISKNOW},
+    {1, S_SUMFAMILIAR, SCR_MAGIC | SP_DRUID | SP_MAGIC | SP_CLERIC, SP_ILLUSION },
+    {1, S_GFIND, SCR_MAGIC | ISKNOW},
+    {1, P_MONSTDET, POT_MAGIC | ISKNOW | SP_DRUID},
+    {1, P_TREASDET, POT_MAGIC | ISKNOW | SP_MAGIC},
+    {1, S_FOODDET, SCR_MAGIC | ISKNOW | SP_CLERIC},
+    {1, S_LIGHT, SCR_MAGIC | ISKNOW | SP_ILLUSION},
+
+    {2, WS_CLOSE, ZAP_MAGIC | ISKNOW},
+    {2, S_IDENTIFY, SCR_MAGIC | ISKNOW},
+    {2, WS_HIT, ZAP_MAGIC | ISKNOW | SP_PRAYER},
+    {2, P_SHIELD, POT_MAGIC | ISKNOW | SP_MAGIC},
+    {2, P_COLDRESIST, POT_MAGIC | SP_WIZARD},
+    {2, P_SEEINVIS, POT_MAGIC | SP_ILLUSION},
+    {2, S_CONFUSE, SCR_MAGIC | SP_CLERIC},
+    {2, P_SMELL, POT_MAGIC | SP_DRUID},
+    {2, WS_MISSILE, ZAP_MAGIC | SP_MAGIC},
+    {2, P_HEAR, POT_MAGIC},
+
+    {3, P_CLEAR, POT_MAGIC | ISKNOW},
+    {3, P_HEALING, POT_MAGIC | ISKNOW | SP_PRAYER},
+    {3, S_CURING, SCR_MAGIC | ISKNOW | SP_PRAYER},
+    {3, WS_MONSTELEP, ZAP_MAGIC | SP_MAGIC},
+    {3, WS_CANCEL, ZAP_MAGIC | SP_WIZARD},
+    {3, S_SELFTELEP, SCR_MAGIC | SP_WIZARD},
+    {3, P_FIRERESIST, POT_MAGIC | SP_WIZARD | SP_DRUID},
+    {3, S_MAP, SCR_MAGIC | SP_ILLUSION | SP_DRUID},
+    {3, S_REMOVECURSE, SCR_MAGIC | SP_PRAYER},
+    {3, S_HOLD, SCR_MAGIC | SP_CLERIC},
+    {3, S_SLEEP, SCR_MAGIC | SP_DRUID},
+    {3, P_HASOXYGEN, POT_MAGIC | SP_DRUID},
+    {3, WS_XENOHEALING, ZAP_MAGIC | SP_DRUID},
+    {3, P_RESTORE, POT_MAGIC},
+
+    {4, S_MSHIELD, SCR_MAGIC | ISKNOW | SP_ILLUSION},
+    {4, P_INVIS, POT_MAGIC | SP_ILLUSION},
+    {4, S_REFLECT, SCR_MAGIC | SP_ILLUSION},
+    {4, P_TRUESEE, POT_MAGIC | SP_ILLUSION},
+    {4, P_REGENERATE, POT_MAGIC | SP_CLERIC},
+    {4, WS_DRAIN, ZAP_MAGIC | SP_CLERIC},
+    {4, P_HASTE, POT_MAGIC | SP_ILLUSION | SP_CLERIC},
+    {4, P_LEVITATION, POT_MAGIC | SP_WIZARD | SP_DRUID},
+    {4, WS_WEB, ZAP_MAGIC | SP_MAGIC},
+    {4, P_PHASE, POT_MAGIC},
+
+    {5, P_SHERO, POT_MAGIC | ISKNOW},
+    {5, S_PETRIFY, SCR_MAGIC | SP_MAGIC},
+    {5, S_SCARE, SCR_MAGIC | _TWO_ | SP_PRAYER},
+    {5, WS_COLD, ZAP_MAGIC | SP_DRUID},
+    {5, WS_FIRE, ZAP_MAGIC | SP_CLERIC},
+    {5, WS_ELECT, ZAP_MAGIC | SP_WIZARD},
+    {5, WS_ANTIMATTER, ZAP_MAGIC | SP_ILLUSION},
+    {5, S_ELECTRIFY, SCR_MAGIC | SP_ILLUSION},
+
+    {6, WS_DISINTEGRATE, ZAP_MAGIC | ISKNOW},
+    {6, S_OWNERSHIP, SCR_MAGIC | SP_ALL},
+
+    {7, S_ENCHANT, SCR_MAGIC | SP_MAGIC},
+
+    {-1, -1, 0}
+};
+
+/*
+    incant()
+       Cast a spell
+*/
+
+void
+incant(struct thing *caster, coord dir)
+{
+    int i;
+    struct stats    *curp;
+    struct stats    *maxp;
+    int is_player = (caster == &player);
+    int points_casters;
+    char    *casters_name = (on(player, ISBLIND)) ? "it" :
+            monsters[caster->t_index].m_name;
+    struct spells   *sp;
+    char *cast_name;                      /* = spell_name(sp)           */
+    char *spell_type;                     /* spell or prayer            */
+    int   casting_cost;                   /* from spell_cost[]          */
+    int   spell_roll;                     /* sucess/fail 1D100 die roll */
+    int   fumble_chance;                  /* Spell fumble chance        */
+    int   num_fumbles = 0;                /* for fumble_spell()         */
+    int   bless_or_curse = ISNORMAL;      /* blessed or cursed?         */
+    int   message_flags = CAST_NORMAL;    /* which message to print out */
+    int   class_casters;                  /* For determining ISKNOW     */
+    int   stat_casters;                   /* s_intel or s_wisdom        */
+    int   level_casters;                  /* spellcasting level         */
+    char  buf[2 * LINELEN];
+    struct spells sorted_spells[MAX_SPELLS];
+    char  spellbuf[2 * LINELEN];
+    char  spellbuf2[2 * LINELEN];
+
+    curp = &(caster->t_stats);
+    maxp = &(caster->maxstats);
+    points_casters = curp->s_power;
+
+    if (points_casters <= 0)
+    {
+        if (is_player)
+            msg("You don't have any spell points.");
+
+        return;
+    }
+
+    /*
+     * Paladins, Rangers, ringwearers, and monsters cast at 4 levels
+     * below. Other non-specialists at 8 below
+    */
+
+    level_casters = curp->s_lvl;
+
+    switch (caster->t_ctype)
+    {
+        case C_PALADIN:
+            level_casters -= 4;
+            /* fallthrough */
+        case C_CLERIC:
+            class_casters = SP_CLERIC;
+            stat_casters = curp->s_wisdom;
+            break;
+        case C_RANGER:
+            level_casters -= 4;
+            /* fallthrough */
+        case C_DRUID:
+            class_casters = SP_DRUID;
+            stat_casters = curp->s_wisdom;
+            break;
+        case C_MAGICIAN:
+            class_casters = SP_WIZARD;
+            stat_casters = curp->s_intel;
+            break;
+        case C_ILLUSION:
+            class_casters = SP_ILLUSION;
+            stat_casters = curp->s_intel;
+            break;
+        case C_MONSTER:
+            if (off(*caster, ISUNIQUE))
+                level_casters -= 4;
+            class_casters = 0x0;
+            stat_casters = curp->s_intel;
+            break;
+
+        default:
+            if (is_wearing(R_WIZARD))
+            {
+                level_casters -= 4;
+                class_casters = (rnd(4) ? SP_WIZARD : SP_ILLUSION);
+                stat_casters = curp->s_intel;
+            }
+            else if (is_wearing(R_PIETY))
+            {
+                level_casters -= 4;
+                class_casters = (rnd(4) ? SP_CLERIC : SP_DRUID);
+                stat_casters = curp->s_wisdom;
+            }
+            else
+            {
+                level_casters -= 8;
+                class_casters = 0x0;
+                stat_casters = (rnd(2) ? curp->s_wisdom : curp->s_intel);
+            }
+    }
+
+    /* Bug - What about when WIS == INT? */
+
+    spell_type = (stat_casters == curp->s_intel) ? "spell" : "prayer";
+
+    if (!is_player && (sp = pick_monster_spell(caster)) == NULL)
+            return;
+    else if (is_player)
+    {
+        int num_spells = -1;    /* num of spells cheap enough */
+
+        sorted_spells[0].sp_cost = -1;
+
+        for (sp = player_spells; sp->sp_level != -1; sp++)
+        {
+            if (sp->sp_flags & class_casters)  /* Does class know spell? */
+            {
+                int rnd_number = rnd(2 * sp->sp_level) - sp->sp_level;
+
+                /* Knows normal spell one level below others */
+
+                casting_cost = spell_cost[sp->sp_level - 1] + rnd_number;
+
+                if (points_casters >= casting_cost)
+                {
+                    sorted_spells[++num_spells] = *sp;
+                    sorted_spells[num_spells].sp_cost = casting_cost;
+                    sorted_spells[num_spells].sp_level = sp->sp_level - 1;
+                }
+
+                /* Knows blessed spell one level above others */
+
+                casting_cost = spell_cost[sp->sp_level + 1] + rnd_number;
+
+                if (points_casters >= casting_cost)
+                {
+                    sorted_spells[++num_spells] = *sp;
+                    sorted_spells[num_spells].sp_level = sp->sp_level + 1;
+                    sorted_spells[num_spells].sp_cost = casting_cost;
+                    sorted_spells[num_spells].sp_flags |= ISBLESSED;
+                }
+            } /* If class doesn't know spell, see if its a ISKNOW */
+            else if (sp->sp_flags & ISKNOW)
+            {
+                int rnd_number = rnd(4 * sp->sp_level) - sp->sp_level;
+
+                casting_cost = spell_cost[sp->sp_level] + rnd_number;
+
+                if (points_casters >= casting_cost)
+                {
+                    sorted_spells[++num_spells] = *sp;
+                    sorted_spells[num_spells].sp_cost = casting_cost;
+                }
+            }
+            /* else this spell is unknown */
+        }
+
+        if (sorted_spells[0].sp_cost == -1)
+        {
+            msg("You don't have enough %s points.", spell_type);
+            after = FALSE;
+            return;
+        }
+
+        qsort(sorted_spells,num_spells + 1,sizeof(struct spells),sort_spells);
+
+        do         /* Prompt for spells */
+        {
+            struct spells   *which_spell = NULL;
+
+            buf[0] = '\0';
+            msg("");/* Get rid of --More-- */
+            msg("Which %s are you casting [%d points left] (* for list)? ",
+                spell_type, points_casters);
+
+            switch(get_string(buf, cw))
+            {
+                case NORM:  break;
+                case QUIT:  return; /* ESC - lose turn */
+                default:    continue;
+            }
+
+            if (buf[0] == '*')    /* print list */
+            {
+                add_line("Cost  Abbreviation    Full Name");
+
+                for (i = 0; i <= num_spells; i++)
+                {
+                    sp = &sorted_spells[i];
+                    sprintf(buf, "[%3d] %-12s\t%s",
+                        sp->sp_cost, spell_abrev(sp,spellbuf2),
+                        spell_name(sp,spellbuf));
+                    add_line(buf);
+                }
+                end_line();
+                sp = NULL;
+                continue;
+            }
+
+            if (isupper(buf[0])) /* Uppercase Abbreviation */
+            {
+                for (i = 0; i <= num_spells; i++)
+                {
+                    sp = &sorted_spells[i];
+
+                    if ((strcmp(spell_abrev(sp,spellbuf2), buf) == 0))
+                    {
+                        which_spell = sp;
+                        break;
+                    }
+                }
+            }
+            else /* Full Spell Name */
+            {
+                for (i = 0; i <= num_spells; i++)
+                {
+                    sp = &sorted_spells[i];
+
+                    if ((strcmp(spell_name(sp,spellbuf), buf) == 0))
+                    {
+                        which_spell = sp;
+                        break;
+                    }
+                }
+            }
+
+            sp = which_spell;
+        }
+        while (sp == NULL);
+    }
+
+    /* Common monster and player code */
+
+    cast_name = spell_name(sp,spellbuf);
+
+    fumble_chance = (10 * sp->sp_level / 4 - 10 * level_casters / 13) * 5;
+
+    if (cur_weapon != NULL && wield_ok(caster, cur_weapon, FALSE) == FALSE)
+    {
+        switch (caster->t_ctype)
+        {
+            case C_MAGICIAN:
+            case C_ILLUSION:
+                msg("You should have both hands free.");
+                fumble_chance += rnd(level_casters) * 5;
+                break;
+
+            case C_CLERIC:
+            case C_DRUID:
+            case C_PALADIN:
+                msg("Your god looks askance at the weapon you wield.");
+                fumble_chance += rnd(level_casters) * 5;
+                break;
+
+            default:
+                break;
+        }
+    }
+
+    if (fumble_chance >= MAX_FUMBLE_CHANCE)
+        fumble_chance = MAX_FUMBLE_CHANCE;
+    else if (fumble_chance <= MIN_FUMBLE_CHANCE + sp->sp_level)
+        fumble_chance = MIN_FUMBLE_CHANCE + sp->sp_level;
+
+    if (fumble_chance > (30 + rnd(50)))
+    {
+        if (is_player)
+        {
+            int  answer;
+
+            msg("Are you sure you want to try for that hard a %s? [n]",
+                spell_type);
+
+            answer = readchar();
+
+            if (tolower(answer) != 'y')
+            {
+                after = FALSE;
+                return;
+            }
+            else
+                msg("Here goes...");
+        }
+        else  /* Only if the monster is desperate */
+        {
+            if (curp->s_hpt > maxp->s_hpt / 2)
+                return;
+        }
+    }
+    
+	/* casting costs food energy */
+	
+	food_left -= sp->sp_cost;
+	
+    spell_roll = rnd(100);
+
+    debug("%s(%d) cast '%s' fumble %%%d (rolled %d) ",
+          monsters[caster->t_index].m_name, curp->s_power, cast_name,
+          fumble_chance, spell_roll);
+
+    caster->t_rest_hpt = caster->t_rest_pow = 0;
+
+    if (!is_player)    /* Stop running. */
+    {
+        running = FALSE;
+        msg("The %s is casting '%s'.", casters_name, cast_name);
+    }
+
+    /* The Crown of Might insures that your spells never fumble */
+
+    if (spell_roll < fumble_chance)
+    {
+        if (is_carrying(TR_CROWN))
+            message_flags |= CAST_CROWN;
+        else
+        {
+            message_flags |= CAST_CURSED;
+
+            curp->s_power -= min(curp->s_power,
+                (2 * sp->sp_cost)); /* 2x cost */
+            num_fumbles = rnd(((fumble_chance - spell_roll) / 10)
+                 + 1) + rnd(sp->sp_level) + rnd(curp->s_lvl);
+            num_fumbles = min(10, max(0, num_fumbles));
+
+            if (num_fumbles >= 6 && rnd(1) == 0)
+                bless_or_curse = ISCURSED;
+            else if (num_fumbles < 4)
+            {
+                if (is_player)
+                    msg("Your %s fails.", spell_type);
+                return;
+            }
+        }
+    }
+    else if (spell_roll > MAX_FUMBLE_CHANCE)
+    {
+        if (is_player)
+        {
+            message_flags |= CAST_BLESSED;
+            pstats.s_exp += 3 * sp->sp_cost * curp->s_lvl;
+            check_level();
+        }
+
+        maxp->s_power += sp->sp_cost;
+        bless_or_curse = ISBLESSED;
+    }
+    else
+    {
+        if (is_player) /* extra exp for sucessful spells */
+        {
+            if (player.t_ctype == C_MAGICIAN || player.t_ctype == C_ILLUSION)
+            {
+                pstats.s_exp += sp->sp_cost * curp->s_lvl;
+                check_level();
+            }
+        }
+
+        bless_or_curse = sp->sp_flags & ISBLESSED;
+        curp->s_power -= sp->sp_cost;
+    }
+
+    /* The Sceptre of Might blesses all your spells */
+
+    if (is_player && ((bless_or_curse & ISBLESSED) == 0) &&
+        is_carrying(TR_SCEPTRE))
+    {
+        message_flags |= CAST_SEPTRE;
+        bless_or_curse = ISBLESSED;
+    }
+
+    if (sp->sp_flags & POT_MAGIC)
+        quaff(caster, sp->sp_which, bless_or_curse);
+    else if (sp->sp_flags & SCR_MAGIC)
+            read_scroll(caster, sp->sp_which, bless_or_curse);
+    else if (sp->sp_flags & ZAP_MAGIC)
+    {
+        if (is_player)
+        {
+            do     /* Must pick a direction */
+            {
+                msg("Which direction?");
+            }
+            while (get_dir() == FALSE);
+        }
+        else
+        {
+            delta.x = dir.x;
+            delta.y = dir.y;
+        }
+        do_zap(caster, sp->sp_which, bless_or_curse);
+    }
+    else
+        msg("What a strange %s!", spell_type);
+
+    /*
+     * Print messages and take fumbles *after* spell has gone off. This
+     * makes ENCHANT, etc more dangerous
+    */
+
+    if (is_player)
+    {
+        if (message_flags & CAST_SEPTRE)
+            msg("The Sceptre enhanced your %s.", spell_type);
+        if (message_flags & CAST_CROWN)
+            msg("The Crown wordlessly corrected your %s.",
+                spell_type);
+
+        switch (message_flags & 0x1)
+        {
+            case CAST_CURSED:
+                msg("You botched your '%s' %s.", cast_name,
+                    spell_type);
+                fumble_spell(caster, num_fumbles);
+                break;
+            case CAST_NORMAL:
+                msg("You sucessfully cast your '%s' %s.",
+                    cast_name, spell_type);
+                break;
+
+            case CAST_BLESSED:
+                msg("Your '%s' %s went superbly.", cast_name,
+                    spell_type);
+                break;
+        }
+    }
+}
+
+/*
+    spell_name()
+        returns pointer to spell name
+*/
+
+char *
+spell_name(struct spells *sp, char *buf)
+{
+    if (buf == NULL)
+        return("UltraRogue Bug #105");
+
+    if (sp->sp_flags & POT_MAGIC)
+        strcpy(buf, p_magic[sp->sp_which].mi_name);
+    else if (sp->sp_flags & SCR_MAGIC)
+        strcpy(buf, s_magic[sp->sp_which].mi_name);
+    else if (sp->sp_flags & ZAP_MAGIC)
+        strcpy(buf, ws_magic[sp->sp_which].mi_name);
+    else
+        strcpy(buf, "unknown spell type");
+
+    if (sp->sp_flags & ISBLESSED)
+        strcat(buf, " 2");
+
+    return(buf);
+}
+
+/*
+    spell_abrev()
+        returns pointer to capital letter spell abbreviation
+*/
+
+char *
+spell_abrev(struct spells *sp, char *buf)
+{
+    if (buf == NULL)
+         return("UltraRogue Bug #106");
+
+    if (sp->sp_flags & POT_MAGIC)
+        strcpy(buf, p_magic[sp->sp_which].mi_abrev);
+    else if (sp->sp_flags & SCR_MAGIC)
+        strcpy(buf, s_magic[sp->sp_which].mi_abrev);
+    else if (sp->sp_flags & ZAP_MAGIC)
+        strcpy(buf, ws_magic[sp->sp_which].mi_abrev);
+    else
+        strcpy(buf, "?????");
+
+    if (sp->sp_flags & ISBLESSED)
+        strcat(buf, " 2");
+
+    return(buf);
+}
+
+/*
+    fumble_spell()
+        he blew it.  Make him pay
+*/
+
+void
+fumble_spell(struct thing *caster, int num_fumbles)
+{
+    struct stats    *curp = &(caster->t_stats);
+    struct stats    *maxp = &(caster->maxstats);
+    int    is_player = (caster == &player);
+
+    debug("Taking %d fumbles.", num_fumbles);
+
+    switch (num_fumbles)
+    {
+        case 10: /* Lose ability */
+            if (rnd(5) == 0)
+                quaff(caster, P_GAINABIL, ISCURSED);
+            break;
+
+        case 9: /* Lose max spell points */
+
+            if (rnd(4) == 0)
+            {
+                maxp->s_power -= rnd(10);
+
+                if (maxp->s_power <= 5)
+                    maxp->s_power = 5;
+            }