Import UltraRogue from the Roguelike Restoration Project (r1490)
This commit is contained in:
parent
c346921c68
commit
11c74c273d
59 changed files with 42044 additions and 0 deletions
878
urogue/magic.c
Normal file
878
urogue/magic.c
Normal file
|
|
@ -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;
|
||||
}
|
||||
break;
|
||||
|
||||
case 8: /* Lose all current spell points */
|
||||
|
||||
if (rnd(3) == 0)
|
||||
curp->s_power = 0;
|
||||
else
|
||||
curp->s_power /= 2;
|
||||
break;
|
||||
|
||||
case 7: /* Freeze */
|
||||
|
||||
if (rnd(2) == 0)
|
||||
{
|
||||
if (is_player)
|
||||
no_command++;
|
||||
else
|
||||
caster->t_no_move++;
|
||||
}
|
||||
break;
|
||||
|
||||
case 6: /* Cast a cursed spell - see below */
|
||||
break;
|
||||
|
||||
case 5: /* Become dazed and confused */
|
||||
|
||||
if (rnd(5) == 0)
|
||||
quaff(caster, P_CLEAR, ISCURSED);
|
||||
break;
|
||||
|
||||
case 4: /* Lose hit points */
|
||||
|
||||
if (is_player)
|
||||
feel_message();
|
||||
if ((curp->s_hpt -= rnd(10)) <= 0)
|
||||
{
|
||||
if (is_player)
|
||||
death(D_SPELLFUMBLE);
|
||||
else
|
||||
killed(caster, find_mons(caster->t_pos.y, caster->t_pos.x),
|
||||
NOMESSAGE, NOPOINTS);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case 3: /* Spell fails */
|
||||
break;
|
||||
|
||||
case 2: /* Freeze */
|
||||
|
||||
if (is_player)
|
||||
no_command++;
|
||||
else
|
||||
caster->t_no_move++;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
case 1: /* Take double spell points - handled in incant() */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
learn_new_spells()
|
||||
go through player_spells and ISKNOW identified potions,
|
||||
scrolls, and sticks
|
||||
*/
|
||||
|
||||
void
|
||||
learn_new_spells(void)
|
||||
{
|
||||
struct spells *sp;
|
||||
int kludge = 0;
|
||||
char spellbuf[2*LINELEN];
|
||||
|
||||
for (sp = player_spells; sp->sp_level != -1; sp++)
|
||||
{
|
||||
if (sp->sp_flags & POT_MAGIC)
|
||||
kludge = TYP_POTION;
|
||||
else if (sp->sp_flags & SCR_MAGIC)
|
||||
kludge = TYP_SCROLL;
|
||||
else if (sp->sp_flags & ZAP_MAGIC)
|
||||
kludge = TYP_STICK;
|
||||
|
||||
if (know_items[kludge][sp->sp_which])
|
||||
{
|
||||
if ((sp->sp_flags & ISKNOW) == FALSE)
|
||||
debug("Learned new spell '%s'", spell_name(sp,spellbuf));
|
||||
sp->sp_flags |= ISKNOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pick_monster_spell()
|
||||
decide which spell from monst_spells will be cast
|
||||
returns pointer to spell in monst_spells
|
||||
*/
|
||||
|
||||
struct spells *
|
||||
pick_monster_spell(struct thing *caster)
|
||||
{
|
||||
struct spells *sp = NULL;
|
||||
struct stats *curp = &(caster->t_stats);
|
||||
int points_casters = curp->s_power;
|
||||
|
||||
/* Discover castable spells */
|
||||
|
||||
for (sp = monst_spells; sp->sp_level != -1; sp++)
|
||||
{
|
||||
int rnd_number = rnd(2 * sp->sp_level) - sp->sp_level;
|
||||
int casting_cost = spell_cost[sp->sp_level] + rnd_number;
|
||||
|
||||
if (points_casters >= casting_cost)
|
||||
sp->sp_flags |= ISKNOW;
|
||||
}
|
||||
|
||||
/* Decide which spell to cast */
|
||||
|
||||
if (curp->s_hpt < rnd(caster->maxstats.s_hpt)) /* think defense */
|
||||
{
|
||||
int i;
|
||||
static const int run_or_heal[NUM_RUN] =
|
||||
{ M_SELFTELEP, M_HLNG2, M_HLNG, M_REGENERATE };
|
||||
|
||||
for (i = 0; i < NUM_RUN; i++)
|
||||
{
|
||||
sp = &monst_spells[run_or_heal[i]];
|
||||
|
||||
if ((sp->sp_flags & ISKNOW) && rnd(1))
|
||||
return(sp);
|
||||
}
|
||||
}
|
||||
|
||||
if (on(*caster, ISSLOW)) /* cancel a slow */
|
||||
{
|
||||
sp = &monst_spells[M_HASTE];
|
||||
|
||||
if (sp->sp_flags & ISKNOW)
|
||||
return (sp);
|
||||
}
|
||||
|
||||
if (on(*caster, ISFLEE)) /* stop running away */
|
||||
{
|
||||
sp = &monst_spells[M_SHERO];
|
||||
|
||||
if (sp->sp_flags & ISKNOW)
|
||||
return (sp);
|
||||
}
|
||||
|
||||
if (on(player, ISINVIS) || on(player, ISDISGUISE))
|
||||
{
|
||||
if (off(*caster, CANSEE)) /* look for him */
|
||||
{
|
||||
sp = &monst_spells[M_SEEINVIS];
|
||||
|
||||
if (sp->sp_flags & ISKNOW)
|
||||
return (sp);
|
||||
}
|
||||
else if (off(*caster, ISINVIS)) /* go invisible */
|
||||
{
|
||||
sp = &monst_spells[M_INVIS];
|
||||
|
||||
if (sp->sp_flags & ISKNOW)
|
||||
return (sp);
|
||||
}
|
||||
}
|
||||
|
||||
if (on(player, CANINWALL) && (off(*caster, CANINWALL)) &&
|
||||
(rnd(5) == 0))
|
||||
{
|
||||
sp = &monst_spells[M_PHASE];
|
||||
|
||||
if (sp->sp_flags & ISKNOW)
|
||||
return (sp);
|
||||
}
|
||||
|
||||
if (rnd(5) == 0 && has_defensive_spell(player))
|
||||
{
|
||||
sp = &monst_spells[M_CANCEL];
|
||||
|
||||
if (sp->sp_flags & ISKNOW)
|
||||
return (sp);
|
||||
}
|
||||
|
||||
/* Cast an offensive spell */
|
||||
|
||||
for (sp = &monst_spells[M_OFFENSE]; sp->sp_level != 1; sp++)
|
||||
{
|
||||
if ((rnd(3) == 0) && (sp->sp_flags & ISKNOW))
|
||||
return (sp);
|
||||
|
||||
if ((rnd(3) == 0) && (sp->sp_flags & ISKNOW))
|
||||
{
|
||||
if (sp->sp_which != WS_MISSILE &&
|
||||
DISTANCE(caster->t_pos, hero) > BOLT_LENGTH)
|
||||
continue;
|
||||
else
|
||||
return(sp);
|
||||
}
|
||||
}
|
||||
|
||||
return(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
sort_spells()
|
||||
called by qsort()
|
||||
*/
|
||||
|
||||
int
|
||||
sort_spells(const void *a, const void *b)
|
||||
{
|
||||
struct spells *sp1, *sp2;
|
||||
int diff;
|
||||
char spellbuf[2 * LINELEN];
|
||||
char spellbuf2[2 * LINELEN];
|
||||
|
||||
union /* hack to prevent 'lint' from complaining */
|
||||
{
|
||||
struct spells *s;
|
||||
const void *vptr;
|
||||
} s1,s2;
|
||||
|
||||
s1.vptr = a;
|
||||
s2.vptr = b;
|
||||
|
||||
sp1 = s1.s;
|
||||
sp2 = s2.s;
|
||||
|
||||
diff = sp1->sp_cost - sp2->sp_cost;
|
||||
|
||||
if (diff != 0)
|
||||
return(diff);
|
||||
else
|
||||
return(strcmp(spell_name(sp1,spellbuf), spell_name(sp1,spellbuf2)));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue