/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | \\._.// *
* -----------------------------------------------------------| (0...0) *
* SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by Derek Snider | ).:.( *
* -----------------------------------------------------------| {o o} *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, | / ' ' \ *
* Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek, |~'~.VxvxV.~'~*
* Tricops and Fireblade | *
* ------------------------------------------------------------------------ *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Spell handling module *
****************************************************************************/
#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#ifdef sun
#include <strings.h>
#endif
#include <time.h>
#include "mud.h"
/*
* Local functions.
*/
void say_spell args( ( CHAR_DATA *ch, int sn ) );
ch_ret spell_affect args( ( int sn, int wil, CHAR_DATA *ch, void *vo ));
ch_ret spell_affectchar args( ( int sn, int wil, CHAR_DATA *ch, void *vo) );
int dispel_casting(AFFECT_DATA *paf,CHAR_DATA *ch,CHAR_DATA*victim,int affect,bool dispel);
void check_bodyparts(CHAR_DATA *ch);
bool check_counterspell(CHAR_DATA *ch, CHAR_DATA *victim);
extern SPECIES_DATA *find_species( char *name );
extern NATION_DATA *find_nation( char *name);
extern void learn_talent( CHAR_DATA *ch, int i);
/*
* Is immune to a damage type
*/
bool is_immune( CHAR_DATA *ch, sh_int damtype )
{
switch( damtype )
{
case SD_FIRE: if (IS_SET(ch->immune, RIS_FIRE)) return TRUE;
case SD_COLD: if (IS_SET(ch->immune, RIS_COLD)) return TRUE;
case SD_ELECTRICITY: if (IS_SET(ch->immune, RIS_ELECTRICITY)) return TRUE;
case SD_ENERGY: if (IS_SET(ch->immune, RIS_ENERGY)) return TRUE;
case SD_ACID: if (IS_SET(ch->immune, RIS_ACID)) return TRUE;
case SD_POISON: if (IS_SET(ch->immune, RIS_POISON)) return TRUE;
case SD_DRAIN: if (IS_SET(ch->immune, RIS_DRAIN)) return TRUE;
}
return FALSE;
}
/*
* Lookup a skill by name, only stopping at skills the player has.
*/
int ch_slookup( CHAR_DATA *ch, const char *name )
{
int sn;
if ( IS_NPC(ch) )
return skill_lookup( name );
for ( sn = 0; sn < top_sn; sn++ )
{
if ( !skill_table[sn]->name )
break;
if ( LEARNED(ch, sn) > 0
&& get_best_talent(ch, sn) != -1
&& LOWER(name[0]) == LOWER(skill_table[sn]->name[0])
&& !str_prefix( name, skill_table[sn]->name ) )
return sn;
}
return -1;
}
/*
* Lookup an herb by name.
*/
int herb_lookup( const char *name )
{
int sn;
for ( sn = 0; sn < top_herb; sn++ )
{
if ( !herb_table[sn] || !herb_table[sn]->name )
return -1;
if ( LOWER(name[0]) == LOWER(herb_table[sn]->name[0])
&& !str_prefix( name, herb_table[sn]->name ) )
return sn;
}
return -1;
}
/*
* Lookup a skill by name.
*/
int skill_lookup( const char *name )
{
int sn;
if ( (sn=bsearch_skill_exact(name, gsn_first_spell, gsn_first_skill-1)) == -1 )
if ( (sn=bsearch_skill_exact(name, gsn_first_skill, gsn_top_sn-1)) == -1)
if ( (sn=bsearch_skill_prefix(name, gsn_first_spell, gsn_first_skill-1)) == -1 )
if ( (sn=bsearch_skill_prefix(name, gsn_first_skill, gsn_top_sn-1)) == -1 )
{
for ( sn = gsn_top_sn; sn < top_sn; sn++ )
{
if ( !skill_table[sn] || !skill_table[sn]->name )
return -1;
if ( LOWER(name[0]) == LOWER(skill_table[sn]->name[0])
&& !str_prefix( name, skill_table[sn]->name ) )
return sn;
}
return -1;
}
return sn;
}
/*
* Return a skilltype pointer based on sn -Thoric
* Returns NULL if bad or unused
*/
SKILLTYPE *get_skilltype( int sn )
{
if ( sn >= TYPE_HERB )
return IS_VALID_HERB(sn-TYPE_HERB) ? herb_table[sn-TYPE_HERB] : NULL;
if ( sn >= TYPE_HIT )
return NULL;
return IS_VALID_SN(sn) ? skill_table[sn] : NULL;
}
/*
* Perform a binary search on a section of the skill table -Thoric
* Each different section of the skill table is sorted alphabetically
*
* Check for prefix matches
*/
int bsearch_skill_prefix( const char *name, int first, int top )
{
int sn;
for (;;)
{
sn = (first + top) >> 1;
if (sn < 0)
return -1;
if (LOWER(name[0]) == LOWER(skill_table[sn]->name[0])
&& !str_prefix(name, skill_table[sn]->name))
return sn;
if (first >= top)
return -1;
if (strcmp(name, skill_table[sn]->name) < 1)
top = sn - 1;
else
first = sn + 1;
}
return -1;
}
/*
* Perform a binary search on a section of the skill table -Thoric
* Each different section of the skill table is sorted alphabetically
*
* Check for exact matches only
*/
int bsearch_skill_exact( const char *name, int first, int top )
{
int sn;
for (;;)
{
sn = (first + top) >> 1;
if (sn < 0)
return -1;
if (!str_cmp(name, skill_table[sn]->name))
return sn;
if (first >= top)
return -1;
if (strcmp(name, skill_table[sn]->name) < 1)
top = sn - 1;
else
first = sn + 1;
}
return -1;
}
/*
* Perform a binary search on a section of the skill table -Thoric
* Each different section of the skill table is sorted alphabetically
*
* Check exact match first, then a prefix match
*/
int bsearch_skill( const char *name, int first, int top )
{
int sn = bsearch_skill_exact(name, first, top);
return (sn == -1) ? bsearch_skill_prefix(name, first, top) : sn;
}
/*
* Perform a binary search on a section of the skill table
* Each different section of the skill table is sorted alphabetically
* Only match skills player knows -Thoric
*/
int ch_bsearch_skill_prefix( CHAR_DATA *ch, const char *name, int first, int top )
{
int sn;
for (;;)
{
sn = (first + top) >> 1;
if ( LOWER(name[0]) == LOWER(skill_table[sn]->name[0])
&& !str_prefix(name, skill_table[sn]->name)
&& LEARNED(ch, sn) > 0
&& get_best_talent(ch, sn) != -1 )
return sn;
if (first >= top)
return -1;
if (strcmp( name, skill_table[sn]->name) < 1)
top = sn - 1;
else
first = sn + 1;
}
return -1;
}
int ch_bsearch_skill_exact( CHAR_DATA *ch, const char *name, int first, int top )
{
int sn;
for (;;)
{
sn = (first + top) >> 1;
if (!str_cmp(name, skill_table[sn]->name)
&& LEARNED(ch, sn) > 0
&& get_best_talent(ch, sn) != -1 )
return sn;
if (first >= top)
return -1;
if (strcmp(name, skill_table[sn]->name) < 1)
top = sn - 1;
else
first = sn + 1;
}
return -1;
}
int ch_bsearch_skill( CHAR_DATA *ch, const char *name, int first, int top )
{
int sn = ch_bsearch_skill_exact(ch, name, first, top);
return (sn == -1) ? ch_bsearch_skill_prefix(ch, name, first, top) : sn;
}
int find_spell( CHAR_DATA *ch, const char *name, bool know )
{
if ( IS_NPC(ch) || !know )
return bsearch_skill( name, gsn_first_spell, gsn_first_skill-1 );
else
return ch_bsearch_skill( ch, name, gsn_first_spell, gsn_first_skill-1 );
}
int find_skill( CHAR_DATA *ch, const char *name, bool know )
{
if ( IS_NPC(ch) || !know )
return bsearch_skill( name, gsn_first_skill, gsn_top_sn-1 );
else
return ch_bsearch_skill( ch, name, gsn_first_skill, gsn_top_sn-1 );
}
/*
* Lookup a skill by slot number.
* Used for object loading.
*/
int slot_lookup( int slot )
{
extern bool fBootDb;
int sn;
if ( slot <= 0 )
return -1;
for ( sn = 0; sn < top_sn; sn++ )
if ( slot == skill_table[sn]->slot )
return sn;
if ( fBootDb )
{
bug( "Slot_lookup: bad slot %d.", slot );
/* abort( ); */
}
return -1;
}
/*
* Fancy message handling for a successful casting -Thoric
*/
void successful_casting( SKILLTYPE *skill, CHAR_DATA *ch,
CHAR_DATA *victim, OBJ_DATA *obj )
{
sh_int chitroom = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
sh_int chit = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_HIT);
sh_int chitme = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_HITME);
if ( skill->target != TAR_CHAR_OFFENSIVE )
{
chit = chitroom;
chitme = chitroom;
}
if ( ch && ch != victim )
{
if ( skill->hit_char && skill->hit_char[0] != '\0' )
act( chit, skill->hit_char, ch, obj, victim, TO_CHAR );
else
if ( skill->type == SKILL_SPELL )
act( chit, "Ok.", ch, NULL, NULL, TO_CHAR );
}
if ( ch && skill->hit_room && skill->hit_room[0] != '\0' )
act( chitroom, skill->hit_room, ch, obj, victim, TO_NOTVICT );
if ( ch && victim && skill->hit_vict && skill->hit_vict[0] != '\0' )
{
if ( ch != victim )
act( chitme, skill->hit_vict, ch, obj, victim, TO_VICT );
else
act( chitme, skill->hit_vict, ch, obj, victim, TO_CHAR );
}
else
if ( ch && ch == victim && skill->type == SKILL_SPELL )
act( chitme, "Ok.", ch, NULL, NULL, TO_CHAR );
}
/*
* Fancy message handling for a failed casting -Thoric
*/
void failed_casting( SKILLTYPE *skill, CHAR_DATA *ch,
CHAR_DATA *victim, OBJ_DATA *obj )
{
sh_int chitroom = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
sh_int chit = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_HIT);
sh_int chitme = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_HITME);
if ( skill->target != TAR_CHAR_OFFENSIVE )
{
chit = chitroom;
chitme = chitroom;
}
if ( ch && ch != victim )
{
if ( skill->miss_char && skill->miss_char[0] != '\0' )
act( chit, skill->miss_char, ch, obj, victim, TO_CHAR );
else
if ( skill->type == SKILL_SPELL )
act( chit, "You failed.", ch, NULL, NULL, TO_CHAR );
}
if ( ch && skill->miss_room && skill->miss_room[0] != '\0'
&& str_cmp(skill->miss_room, "supress") )
act( chitroom, skill->miss_room, ch, obj, victim, TO_NOTVICT );
if ( ch && victim && skill->miss_vict && skill->miss_vict[0] != '\0' )
{
if ( ch != victim )
act( chitme, skill->miss_vict, ch, obj, victim, TO_VICT );
else
act( chitme, skill->miss_vict, ch, obj, victim, TO_CHAR );
}
else
if ( ch && ch == victim )
{
if ( skill->miss_char && skill->miss_char[0] != '\0' )
act( chitme, skill->miss_char, ch, obj, victim, TO_CHAR );
else
if ( skill->type == SKILL_SPELL )
act( chitme, "You failed.", ch, NULL, NULL, TO_CHAR );
}
}
/*
* Fancy message handling for being immune to something -Thoric
*/
void immune_casting( SKILLTYPE *skill, CHAR_DATA *ch,
CHAR_DATA *victim, OBJ_DATA *obj )
{
sh_int chitroom = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
sh_int chit = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_HIT);
sh_int chitme = (skill->type == SKILL_SPELL ? AT_MAGIC : AT_HITME);
if ( skill->target != TAR_CHAR_OFFENSIVE )
{
chit = chitroom;
chitme = chitroom;
}
if ( ch && ch != victim )
{
if ( skill->imm_char && skill->imm_char[0] != '\0' )
act( chit, skill->imm_char, ch, obj, victim, TO_CHAR );
else
if ( skill->miss_char && skill->miss_char[0] != '\0' )
act( chit, skill->hit_char, ch, obj, victim, TO_CHAR );
else
if ( skill->type == SKILL_SPELL || skill->type == SKILL_SKILL )
act( chit, "That appears to have no effect.", ch, NULL, NULL, TO_CHAR );
}
if ( ch && skill->imm_room && skill->imm_room[0] != '\0' )
act( chitroom, skill->imm_room, ch, obj, victim, TO_NOTVICT );
else
if ( ch && skill->miss_room && skill->miss_room[0] != '\0' )
act( chitroom, skill->miss_room, ch, obj, victim, TO_NOTVICT );
if ( ch && victim && skill->imm_vict && skill->imm_vict[0] != '\0' )
{
if ( ch != victim )
act( chitme, skill->imm_vict, ch, obj, victim, TO_VICT );
else
act( chitme, skill->imm_vict, ch, obj, victim, TO_CHAR );
}
else
if ( ch && victim && skill->miss_vict && skill->miss_vict[0] != '\0' )
{
if ( ch != victim )
act( chitme, skill->miss_vict, ch, obj, victim, TO_VICT );
else
act( chitme, skill->miss_vict, ch, obj, victim, TO_CHAR );
}
else
if ( ch && ch == victim )
{
if ( skill->imm_char && skill->imm_char[0] != '\0' )
act( chit, skill->imm_char, ch, obj, victim, TO_CHAR );
else
if ( skill->miss_char && skill->miss_char[0] != '\0' )
act( chit, skill->hit_char, ch, obj, victim, TO_CHAR );
else
if ( skill->type == SKILL_SPELL || skill->type == SKILL_SKILL )
act( chit, "That appears to have no affect.", ch, NULL, NULL, TO_CHAR );
}
}
/*
* Utter mystical words for an sn.
*/
void say_spell( CHAR_DATA *ch, int sn )
{
char buf [MAX_STRING_LENGTH];
char buf2 [MAX_STRING_LENGTH];
CHAR_DATA *rch;
char *pName;
int iSyl;
int length;
SKILLTYPE *skill = get_skilltype( sn );
struct syl_type
{
char * old;
char * new;
};
static const struct syl_type syl_table[] =
{
{ " ", " " },
{ "ar", "kata" },
{ "fi", "kh" },
{ "ice", "nyst" },
{ "ing", "nay" },
{ "el", "r" },
{ "ld", "ck" },
{ "light", "khar" },
{ "ne", "lyx" },
{ "ow", "er" },
{ "re", "an" },
{ "ru", "tanta" },
{ "tra", "ill" },
{ "sha", "se" },
{ "shi", "er" },
{ "a", "o" }, { "b", "b" }, { "c", "q" }, { "d", "dh" },
{ "e", "y" }, { "f", "z" }, { "g", "o" }, { "h", "p" },
{ "i", "u" }, { "j", "n" }, { "k", "t" }, { "l", "r" },
{ "m", "w" }, { "n", "i" }, { "o", "a" }, { "p", "s" },
{ "q", "d" }, { "r", "f" }, { "s", "g" }, { "t", "h" },
{ "u", "j" }, { "v", "y" }, { "w", "r" }, { "x", "n" },
{ "y", "i" }, { "z", "k" },
{ "", "" }
};
buf[0] = '\0';
for ( pName = skill->name; *pName != '\0'; pName += length )
{
for ( iSyl = 0; (length = strlen(syl_table[iSyl].old)) != 0; iSyl++ )
{
if ( !str_prefix( syl_table[iSyl].old, pName ) )
{
strcat( buf, syl_table[iSyl].new );
break;
}
}
if ( length == 0 )
length = 1;
}
sprintf( buf2, "$n closes $s eyes and utters the words, '%s!'", buf);
sprintf( buf, "$n closes $s eyes and utters the words, '%s!'", skill->name );
/* by saying the spell out loud, you give yourself away -keo */
if (IS_AFFECTED(ch, AFF_HIDE))
xREMOVE_BIT(ch->affected_by, AFF_HIDE);
for ( rch = ch->in_room->first_person; rch; rch = rch->next_in_room )
{
if ( rch != ch )
act( AT_MAGIC, buf2, ch, NULL, rch, TO_VICT );
}
return;
}
/*
* Make adjustments to saving throw based in RIS -Thoric
*/
int ris_save( CHAR_DATA *ch, int chance, int ris )
{
sh_int modifier;
modifier = 10;
if ( IS_SET(ch->immune, ris ) )
modifier -= 10;
if ( IS_SET(ch->resistant, ris ) )
modifier -= 2;
if ( IS_SET(ch->susceptible, ris ) )
modifier += 2;
if ( modifier <= 0 )
return 1000;
if ( modifier == 10 )
return chance;
return (chance * modifier) / 10;
}
/* -Thoric
* Fancy dice expression parsing complete with order of operations,
* simple exponent support, dice support as well as a few extra
* variables: H = hp, M = mana, V = move, S = str, X = dex
* I = int, W = wil, C = con, P = per, E = end, A = age
*
* Used for spell dice parsing, ie: 3d8+W-6
*
*/
int rd_parse(CHAR_DATA *ch, char *exp)
{
int x, lop = 0, gop = 0, eop = 0;
char operation;
char *sexp[2];
int total = 0, len = 0;
/* take care of nulls coming in */
if (!exp || !strlen(exp))
return 0;
/* get rid of brackets if they surround the entire expresion */
if ( (*exp == '(') && !index(exp+1,'(') && exp[strlen(exp)-1] == ')' )
{
exp[strlen(exp)-1] = '\0';
exp++;
}
/* check if the expresion is just a number */
len = strlen(exp);
if ( len == 1 && isalpha(exp[0]) )
{
switch(exp[0])
{
case 'H': case 'h': return ch->hit;
case 'M': case 'm': return ch->mana;
case 'V': case 'v': return ch->move;
case 'S': case 's': return get_curr_str(ch);
case 'I': case 'i': return get_curr_int(ch);
case 'W': case 'w': return get_curr_wil(ch);
case 'X': case 'x': return get_curr_dex(ch);
case 'C': case 'c': return get_curr_con(ch);
case 'P': case 'p': return get_curr_per(ch);
case 'E': case 'e': return get_curr_end(ch);
}
}
for (x = 0; x < len; ++x)
if (!isdigit(exp[x]) && !isspace(exp[x]))
break;
if (x == len)
return atoi(exp);
/* break it into 2 parts */
for (x = 0; x < strlen(exp); ++x)
switch(exp[x])
{
case '^':
if (!total)
eop = x;
break;
case '-': case '+':
if (!total)
lop = x;
break;
case '*': case '/': case '%': case 'd': case 'D':
case '<': case '>': case '{': case '}': case '=':
if (!total)
gop = x;
break;
case '(':
++total;
break;
case ')':
--total;
break;
}
if (lop)
x = lop;
else
if (gop)
x = gop;
else
x = eop;
operation = exp[x];
exp[x] = '\0';
sexp[0] = exp;
sexp[1] = (char *)(exp+x+1);
/* work it out */
total = rd_parse(ch, sexp[0]);
switch(operation)
{
case '-': total -= rd_parse(ch, sexp[1]); break;
case '+': total += rd_parse(ch, sexp[1]); break;
case '*': total *= rd_parse(ch, sexp[1]); break;
case '/': total /= rd_parse(ch, sexp[1]); break;
case '%': total %= rd_parse(ch, sexp[1]); break;
case 'd': case 'D': total = dice( total, rd_parse(ch, sexp[1])); break;
case '<': total = (total < rd_parse(ch, sexp[1])); break;
case '>': total = (total > rd_parse(ch, sexp[1])); break;
case '=': total = (total == rd_parse(ch, sexp[1])); break;
case '{': total = UMIN( total, rd_parse(ch, sexp[1]) ); break;
case '}': total = UMAX( total, rd_parse(ch, sexp[1])); break;
case '^':
{
int y = rd_parse(ch, sexp[1]), z = total;
for (x = 1; x < y; ++x, z *= total);
total = z;
break;
}
}
return total;
}
/* wrapper function so as not to destroy exp */
int dice_parse(CHAR_DATA *ch, char *exp)
{
char buf[MAX_INPUT_LENGTH];
strcpy( buf, exp );
return rd_parse(ch, buf);
}
/*
* Process the spell's required components, if any -Thoric
* -----------------------------------------------
* T### check for item of type ###
* V##### check for item of vnum #####
* Kword check for item with keyword 'word'
* G##### check if player has ##### amount of gold
* H#### check if player has #### amount of hitpoints
*
* Special operators:
* ! spell fails if player has this
* + don't consume this component
* @@ decrease component's value[0], and extract if it reaches 0
* # decrease component's value[1], and extract if it reaches 0
* $ decrease component's value[2], and extract if it reaches 0
* % decrease component's value[3], and extract if it reaches 0
* ^ decrease component's value[4], and extract if it reaches 0
* & decrease component's value[5], and extract if it reaches 0
*/
bool process_spell_components( CHAR_DATA *ch, int sn )
{
SKILLTYPE *skill = get_skilltype(sn);
char *comp = skill->components;
char *check;
char arg[MAX_INPUT_LENGTH];
bool consume, fail, found;
int val, value;
OBJ_DATA *obj;
/* if no components necessary, then everything is cool */
if ( !comp || comp[0] == '\0' )
return TRUE;
while ( comp[0] != '\0' )
{
comp = one_argument( comp, arg );
consume = TRUE;
fail = found = FALSE;
val = -1;
switch( arg[1] )
{
default: check = arg+1; break;
case '!': check = arg+2; fail = TRUE; break;
case '+': check = arg+2; consume = FALSE; break;
case '@': check = arg+2; val = 0; break;
case '#': check = arg+2; val = 1; break;
case '$': check = arg+2; val = 2; break;
case '%': check = arg+2; val = 3; break;
case '^': check = arg+2; val = 4; break;
case '&': check = arg+2; val = 5; break;
/* reserve '*', '(' and ')' for v6, v7 and v8 */
}
value = atoi(check);
obj = NULL;
switch( UPPER(arg[0]) )
{
case 'T':
for ( obj = ch->first_carrying; obj; obj = obj->next_content )
if ( obj->item_type == value )
{
if ( fail )
{
send_to_char( "Something disrupts the casting of this spell...\n\r", ch );
return FALSE;
}
found = TRUE;
break;
}
break;
case 'V':
for ( obj = ch->first_carrying; obj; obj = obj->next_content )
if ( obj->pIndexData->vnum == value )
{
if ( fail )
{
send_to_char( "Something disrupts the casting of this spell...\n\r", ch );
return FALSE;
}
found = TRUE;
break;
}
break;
case 'K':
for ( obj = ch->first_carrying; obj; obj = obj->next_content )
if ( nifty_is_name( check, obj->name ) )
{
if ( fail )
{
send_to_char( "Something disrupts the casting of this spell...\n\r", ch );
return FALSE;
}
found = TRUE;
break;
}
break;
case 'G':
if ( ch->gold >= value ) {
if ( fail )
{
send_to_char( "Something disrupts the casting of this spell...\n\r", ch );
return FALSE;
}
else
{
if ( consume )
{
set_char_color( AT_GOLD, ch );
send_to_char( "You feel a little lighter...\n\r", ch );
ch->gold -= value;
}
continue;
}
break;
}
case 'H':
if ( ch->hit >= value ) {
if ( fail )
{
send_to_char( "Something disrupts the casting of this spell...\n\r", ch );
return FALSE;
}
else
{
if ( consume )
{
set_char_color( AT_BLOOD, ch );
send_to_char( "You feel a little weaker...\n\r", ch );
ch->hit -= value;
update_pos( ch );
}
continue;
}
break;
}
}
/* having this component would make the spell fail... if we get
here, then the caster didn't have that component */
if ( fail )
continue;
if ( !found )
{
send_to_char( "Something is missing...\n\r", ch );
return FALSE;
}
if ( obj )
{
if ( val >=0 && val < 6 )
{
separate_obj(obj);
if ( obj->value[val] <= 0 )
{
act( AT_MAGIC, "$p disappears in a puff of smoke!", ch, obj, NULL, TO_CHAR );
act( AT_MAGIC, "$p disappears in a puff of smoke!", ch, obj, NULL, TO_ROOM );
extract_obj( obj );
return FALSE;
}
else
if ( --obj->value[val] == 0 )
{
act( AT_MAGIC, "$p glows briefly, then disappears in a puff of smoke!", ch, obj, NULL, TO_CHAR );
act( AT_MAGIC, "$p glows briefly, then disappears in a puff of smoke!", ch, obj, NULL, TO_ROOM );
extract_obj( obj );
}
else
act( AT_MAGIC, "$p glows briefly and a whisp of smoke rises from it.", ch, obj, NULL, TO_CHAR );
}
else
if ( consume )
{
separate_obj(obj);
act( AT_MAGIC, "$p glows brightly, then disappears in a puff of smoke!", ch, obj, NULL, TO_CHAR );
act( AT_MAGIC, "$p glows brightly, then disappears in a puff of smoke!", ch, obj, NULL, TO_ROOM );
extract_obj( obj );
}
else
{
int count = obj->count;
obj->count = 1;
act( AT_MAGIC, "$p glows briefly.", ch, obj, NULL, TO_CHAR );
obj->count = count;
}
}
}
return TRUE;
}
int pAbort;
/*
* Locate targets.
*/
void *locate_targets( CHAR_DATA *ch, char *arg, int sn,
CHAR_DATA **victim, OBJ_DATA **obj )
{
SKILLTYPE *skill = get_skilltype( sn );
void *vo = NULL;
*victim = NULL;
*obj = NULL;
switch ( skill->target )
{
default:
bug( "Do_cast: bad target for sn %d.", sn );
return &pAbort;
case TAR_IGNORE:
break;
case TAR_CHAR_OFFENSIVE:
if ( arg[0] == '\0' )
{
if ( ( *victim = ch->last_hit ) == NULL )
{
send_to_char( "Cast the spell on whom?\n\r", ch );
return &pAbort;
}
} else {
if ( ( *victim = get_char_room( ch, arg ) ) == NULL )
{
if (skill->range > 0)
break;
send_to_char( "They aren't here.\n\r", ch );
return &pAbort;
}
}
if ( ch == *victim )
{
if ( SPELL_FLAG(get_skilltype(sn), SF_NOSELF))
{
send_to_char( "You can't cast this on yourself!\n\r", ch);
return &pAbort;
} else if (skill->range < 1)
send_to_char( "Cast this on yourself? Okay...\n\r", ch );
}
if ( !IS_NPC(ch) )
{
if ( !IS_NPC(*victim) )
{
if ( get_timer( ch, TIMER_PKILLED ) > 0 )
{
send_to_char( "You have been killed in the last 5 minutes.\n\r", ch);
return &pAbort;
}
if ( get_timer( *victim, TIMER_PKILLED ) > 0 )
{
send_to_char( "This player has been killed in the last 5 minutes.\n\r", ch );
return &pAbort;
}
}
if ( IS_AFFECTED(ch, AFF_CHARM) && ch->master == *victim )
{
send_to_char( "You can't do that on your own follower.\n\r", ch );
return &pAbort;
}
}
vo = (void *) *victim;
break;
case TAR_CHAR_DEFENSIVE:
if ( arg[0] == '\0' )
*victim = ch;
else
{
if (ch->curr_talent[TAL_MOTION] >= 100
&& ch->curr_talent[TAL_SEEKING] >= 100) {
if ( ( *victim = get_char_world( ch, arg ) ) == NULL )
{
send_to_char( "They aren't here.\n\r", ch );
return &pAbort;
}
} else {
if ( ( *victim = get_char_room( ch, arg ) ) == NULL )
{
send_to_char( "They aren't here.\n\r", ch );
return &pAbort;
}
}
}
if ( ch == *victim &&
SPELL_FLAG(get_skilltype(sn), SF_NOSELF))
{
send_to_char( "You can't cast this on yourself!\n\r", ch);
return &pAbort;
}
vo = (void *) *victim;
break;
case TAR_CHAR_SELF:
if ( arg[0] != '\0' && !nifty_is_name( arg, ch->name ) )
{
send_to_char( "You cannot cast this spell on another.\n\r", ch );
return &pAbort;
}
vo = (void *) ch;
break;
case TAR_OBJ_INV:
if ( arg[0] == '\0' )
{
send_to_char( "What should the spell be cast upon?\n\r", ch );
return &pAbort;
}
if ( ( *obj = get_obj_carry( ch, arg ) ) == NULL )
{
send_to_char( "You are not carrying that.\n\r", ch );
return &pAbort;
}
vo = (void *) *obj;
break;
}
return vo;
}
/*
* The kludgy global is for spells who want more stuff from command line.
*/
char *target_name;
char *ranged_target_name = NULL;
/*
* Cast a spell. Multi-caster and component support by Thoric
* Ranged spell support by Scion
*/
void do_cast( CHAR_DATA *ch, char *argument )
{
char arg1[MAX_INPUT_LENGTH];
char arg2[MAX_INPUT_LENGTH];
static char staticbuf[MAX_INPUT_LENGTH];
CHAR_DATA *victim;
OBJ_DATA *obj;
void *vo = NULL;
int mana;
int sn;
ch_ret retcode;
bool dont_wait = FALSE;
SKILLTYPE *skill = NULL;
struct timeval time_used;
retcode = rNONE;
switch( ch->substate )
{
default:
/* no ordering charmed mobs to cast spells */
if ( IS_NPC(ch)
&& ( IS_AFFECTED( ch, AFF_CHARM ) || IS_AFFECTED( ch, AFF_POSSESS ) ) )
{
send_to_char( "You can't seem to do that right now...\n\r", ch );
return;
}
if ( IS_SET( ch->in_room->room_flags, ROOM_NO_MAGIC ) )
{
set_char_color( AT_MAGIC, ch );
send_to_char( "Your magic flickers and dies.\n\r", ch );
return;
}
/* arg1 = spell name
arg2 = dir or target
target_name = dir+target || dir || target
*/
target_name = one_argument( argument, arg1 );
one_argument( target_name, arg2 );
if ( ranged_target_name )
DISPOSE( ranged_target_name );
ranged_target_name = str_dup( target_name );
if ( arg1[0] == '\0' )
{
send_to_char( "Cast which what where?\n\r", ch );
return;
}
/* Regular mortal spell casting */
if (!ch->pcdata || !IS_SET(ch->pcdata->permissions, PERMIT_MISC) )
{
if ( ( sn = find_spell( ch, arg1, TRUE ) ) < 0
|| ( !IS_NPC(ch) && get_best_talent(ch, sn) == -1 ) )
{
send_to_char( "You don't know how to do that.\n\r", ch );
return;
}
if ( (skill=get_skilltype(sn)) == NULL )
{
send_to_char( "You can't do that.\n\r", ch );
return;
}
}
else
/*
* Godly "spell builder" spell casting with debugging messages
*/
{
if ( (sn=skill_lookup(arg1)) < 0 )
{
send_to_char( "That's not a spell.\n\r", ch );
return;
}
if ( sn >= MAX_SKILL )
{
send_to_char( "Someone used too many skill numbers, that spell is unusable.\n\r", ch );
return;
}
if ( (skill=get_skilltype(sn)) == NULL )
{
send_to_char( "Something is severely wrong with that spell.\n\r", ch );
return;
}
if ( skill->type != SKILL_SPELL )
{
send_to_char( "That isn't a spell.\n\r", ch );
return;
}
if ( !skill->spell_fun )
{
send_to_char( "We didn't finish that one yet.\n\r", ch );
return;
}
}
/*
* Something else removed by Merc -Thoric
*/
/* Band-aid alert! !IS_NPC check -- Blod */
if ( ch->position < skill->minimum_position && !IS_NPC(ch) )
{
switch( ch->position )
{
default:
send_to_char( "You can't concentrate enough.\n\r", ch );
break;
case POS_SITTING:
send_to_char( "You can't summon enough energy sitting down.\n\r", ch );
break;
case POS_RESTING:
send_to_char( "You're too relaxed to cast that spell.\n\r", ch );
break;
case POS_KNEELING:
send_to_char( "You should get to your feet first.\n\r",ch);
break;
case POS_SQUATTING:
send_to_char("You should stand up first.\n\r", ch);
break;
case POS_SLEEPING:
send_to_char( "You dream about great feats of magic.\n\r", ch );
break;
}
return;
}
if ( skill->spell_fun == spell_null )
{
send_to_char( "That's not a spell!\n\r", ch );
return;
}
if ( !skill->spell_fun )
{
send_to_char( "You cannot cast that... yet.\n\r", ch );
return;
}
mana = IS_NPC(ch) ? 0 : skill->min_mana;
if (!IS_NPC(ch) && GET_ADEPT(ch, skill_lookup(skill->name)))
mana=mana-(mana/4);
/*
* Locate targets.
*/
vo = locate_targets( ch, arg2, sn, &victim, &obj );
if ( vo == &pAbort )
return;
if ( victim
&& !IS_SAME_PLANE(ch, victim) )
{
set_char_color( AT_MAGIC, ch );
send_to_char( "You don't seem to be able to cast spells on them.\n\r", ch );
return;
}
if ( !IS_NPC(ch) && ch->mana < mana )
{
send_to_char( "You don't have enough mana.\n\r", ch );
return;
}
if ((SPELL_FLAG(skill, SF_INSTANT))
|| (IS_NPC(ch)))
break;
if (number_percent() < ch->curr_talent[TAL_TIME]) {
learn_talent(ch, TAL_TIME);
act( AT_MAGIC, "You sidestep time.", ch, NULL, NULL, TO_CHAR);
dont_wait = TRUE;
break;
}
/* multi-participant spells -Thoric */
/* we don't use multi-participent spells, lets make this */
/* for all spells instead -keo */
add_timer( ch, TIMER_DO_FUN, UMIN(skill->beats / 10, 3),
do_cast, 1 );
act( AT_MAGIC, "You begin to channel...", ch, NULL, NULL, TO_CHAR);
act( AT_MAGIC, "$n begins to channel...", ch, NULL, NULL, TO_ROOM);
sprintf( staticbuf, "%s %s", arg2, target_name );
ch->alloc_ptr = str_dup( staticbuf );
ch->tempnum = sn;
mana_from_char(ch, mana);
return;
case SUB_TIMER_DO_ABORT:
DISPOSE( ch->alloc_ptr );
if ( IS_VALID_SN((sn = ch->tempnum)) )
{
if ( (skill=get_skilltype(sn)) == NULL )
{
send_to_char( "Something went wrong...\n\r", ch );
bug( "do_cast: SUB_TIMER_DO_ABORT: bad sn %d", sn );
return;
}
}
set_char_color( AT_MAGIC, ch );
send_to_char( "You stop channeling...\n\r", ch );
return;
case 1:
sn = ch->tempnum;
if ( (skill=get_skilltype(sn)) == NULL )
{
send_to_char( "Something went wrong...\n\r", ch );
bug( "do_cast: substate 1: bad sn %d", sn );
return;
}
if ( !ch->alloc_ptr || !IS_VALID_SN(sn) || skill->type != SKILL_SPELL )
{
send_to_char( "Something cancels out the spell!\n\r", ch );
bug( "do_cast: ch->alloc_ptr NULL or bad sn (%d)", sn );
return;
}
strcpy( staticbuf, ch->alloc_ptr );
target_name = one_argument(staticbuf, arg2);
DISPOSE( ch->alloc_ptr );
ch->substate = SUB_NONE;
dont_wait = TRUE;
send_to_char( "You concentrate all the energy into a burst of mystical words!\n\r", ch );
vo = locate_targets( ch, arg2, sn, &victim, &obj );
if ( vo == &pAbort )
return;
}
say_spell( ch, sn );
if ( !dont_wait )
WAIT_STATE( ch, skill->beats );
/*
* Getting ready to cast... check for spell components -Thoric
*/
if ( !process_spell_components( ch, sn ) )
{
learn_from_failure( ch, sn );
return;
}
if ( !IS_NPC(ch)
&& (number_percent( ) + skill->difficulty * 5) > LEARNED(ch, sn) )
{
/* Some more interesting loss of concentration messages -Thoric */
switch( number_bits(2) )
{
case 0: /* too busy */
if ( IS_FIGHTING(ch) )
send_to_char( "This round of battle is too hectic to concentrate properly.\n\r", ch );
else
send_to_char( "You lost your concentration.\n\r", ch );
break;
case 1: /* irritation */
if ( number_bits(2) == 0 )
{
switch( number_bits(2) )
{
case 0: send_to_char( "A tickle in your nose prevents you from keeping your concentration.\n\r", ch ); break;
case 1: send_to_char( "An itch on your leg keeps you from properly casting your spell.\n\r", ch ); break;
case 2: send_to_char( "Something in your throat prevents you from uttering the proper phrase.\n\r", ch ); break;
case 3: send_to_char( "A twitch in your eye disrupts your concentration for a moment.\n\r", ch ); break;
}
}
else
send_to_char( "Something distracts you, and you lose your concentration.\n\r", ch );
break;
case 2: /* not enough time */
if ( IS_FIGHTING(ch) )
send_to_char( "There wasn't enough time this round to complete the casting.\n\r", ch );
else
send_to_char( "You lost your concentration.\n\r", ch );
break;
case 3:
send_to_char( "You get a mental block mid-way through the casting.\n\r", ch );
break;
}
learn_from_failure( ch, sn );
return;
}
else
{
if ( ch != victim && (skill->target == TAR_CHAR_OFFENSIVE)) {
if (check_counterspell(ch, victim) ||
check_displacement(ch,victim) ||
check_antimagic(ch,victim)) {
learn_from_success(ch, sn);
return;
}
}
/*
* check for immunity to magic if victim is known...
* and it is a TAR_CHAR_DEFENSIVE/SELF spell
* otherwise spells will have to check themselves
*/
if ( ((skill->target == TAR_CHAR_DEFENSIVE
|| skill->target == TAR_CHAR_SELF)
&& victim && IS_SET(victim->immune, RIS_MAGIC)) )
{
immune_casting( skill, ch, victim, NULL );
retcode = rSPELL_FAILED;
}
else
{
start_timer(&time_used);
if (!char_died(ch))
retcode = (*skill->spell_fun) ( sn, get_curr_wil(ch), ch, vo );
end_timer(&time_used);
update_userec(&time_used, &skill->userec);
}
}
if ( retcode == rCHAR_DIED || retcode == rERROR || char_died(ch) )
return;
/* learning */
if ( retcode != rSPELL_FAILED )
learn_from_success( ch, sn );
else
learn_from_failure( ch, sn );
/*
* Fixed up a weird mess here, and added double safeguards -Thoric
*/
if ( skill->target == TAR_CHAR_OFFENSIVE && victim && !char_died(victim) && victim != ch )
{
CHAR_DATA *vch, *vch_next;
for ( vch = ch->in_room->first_person; vch; vch = vch_next )
{
vch_next = vch->next_in_room;
}
}
return;
}
/*
* Cast spells at targets using a magical object.
*/
ch_ret obj_cast_spell( int sn, int level, CHAR_DATA *ch, CHAR_DATA *victim, OBJ_DATA *obj )
{
void *vo;
ch_ret retcode = rNONE;
SKILLTYPE *skill = get_skilltype( sn );
struct timeval time_used;
if ( sn == -1 )
return retcode;
if ( !skill || !skill->spell_fun )
{
bug( "Obj_cast_spell: bad sn %d.", sn );
return rERROR;
}
if ( IS_SET( ch->in_room->room_flags, ROOM_NO_MAGIC ) )
{
set_char_color( AT_MAGIC, ch );
send_to_char( "Nothing seems to happen...\n\r", ch );
return rNONE;
}
if ( IS_SET( ch->in_room->room_flags, ROOM_SAFE ) &&
skill->target == TAR_CHAR_OFFENSIVE)
{
set_char_color( AT_MAGIC, ch );
send_to_char( "Nothing seems to happen...\n\r", ch );
return rNONE;
}
/*
* Basically this was added to cut down on level 5 players using level
* 40 scrolls in battle too often ;) -Thoric
*/
if ( (skill->target == TAR_CHAR_OFFENSIVE
|| number_bits(7) == 1) /* 1/128 chance if non-offensive */
&& skill->type != SKILL_HERB
&& !chance( ch, 100 ) )
{
switch( number_bits(2) )
{
case 0: failed_casting( skill, ch, victim, NULL ); break;
case 1:
act( AT_MAGIC, "The $t spell backfires!", ch, skill->name, victim, TO_CHAR );
if ( victim )
act( AT_MAGIC, "$n's $t spell backfires!", ch, skill->name, victim, TO_VICT );
act( AT_MAGIC, "$n's $t spell backfires!", ch, skill->name, victim, TO_NOTVICT );
case 2: failed_casting( skill, ch, victim, NULL ); break;
case 3:
act( AT_MAGIC, "The $t spell backfires!", ch, skill->name, victim, TO_CHAR );
if ( victim )
act( AT_MAGIC, "$n's $t spell backfires!", ch, skill->name, victim, TO_VICT );
act( AT_MAGIC, "$n's $t spell backfires!", ch, skill->name, victim, TO_NOTVICT );
}
return rNONE;
}
target_name = "";
switch ( skill->target )
{
default:
bug( "Obj_cast_spell: bad target for sn %d.", sn );
return rERROR;
case TAR_IGNORE:
vo = NULL;
if ( victim )
target_name = victim->name;
else
if ( obj )
target_name = obj->name;
break;
case TAR_CHAR_OFFENSIVE:
if ( victim != ch )
{
if ( !victim )
victim = ch->last_hit;
if ( !victim )
{
send_to_char( "You can't do that.\n\r", ch );
return rNONE;
}
}
vo = (void *) victim;
break;
case TAR_CHAR_DEFENSIVE:
if ( victim == NULL )
victim = ch;
vo = (void *) victim;
if ( skill->type != SKILL_HERB
&& IS_SET(victim->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rNONE;
}
break;
case TAR_CHAR_SELF:
vo = (void *) ch;
if ( skill->type != SKILL_HERB
&& IS_SET(ch->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rNONE;
}
break;
case TAR_OBJ_INV:
if ( obj == NULL )
{
send_to_char( "You can't do that.\n\r", ch );
return rNONE;
}
vo = (void *) obj;
break;
}
start_timer(&time_used);
retcode = (*skill->spell_fun) ( sn, get_curr_wil(ch), ch, vo );
end_timer(&time_used);
update_userec(&time_used, &skill->userec);
if ( retcode == rSPELL_FAILED )
retcode = rNONE;
if ( retcode == rCHAR_DIED || retcode == rERROR )
return retcode;
if ( char_died(ch) )
return rCHAR_DIED;
return retcode;
}
/*
* Spell functions.
*/
ch_ret spell_blindness( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
AFFECT_DATA af;
int tmp;
SKILLTYPE *skill = get_skilltype(sn);
if ( SPELL_FLAG(skill, SF_PKSENSITIVE)
&& !IS_NPC(ch) && !IS_NPC(victim) )
tmp = level/2;
else
tmp = level;
if ( IS_SET( victim->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( IS_AFFECTED(victim, AFF_BLIND))
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
af.type = sn;
af.location = APPLY_HITROLL;
af.modifier = -4;
af.duration = (1 + (level / 3)) * DUR_CONV;
af.bitvector = meb(AFF_BLIND);
affect_to_char( victim, &af );
set_char_color( AT_MAGIC, victim );
send_to_char( "You are blinded!\n\r", victim );
if ( ch != victim )
{
act( AT_MAGIC, "You weave a spell of blindness around $N.", ch, NULL, victim, TO_CHAR );
act( AT_MAGIC, "$n weaves a spell of blindness about $N.", ch, NULL, victim, TO_NOTVICT );
}
return rNONE;
}
ch_ret spell_balance_mind(int sn, int level, CHAR_DATA *ch, void *vo)
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
victim->mental_state = 0;
act( AT_MAGIC, "You balance $N's mind.", ch, NULL, victim, TO_CHAR);
act( AT_MAGIC, "Your mind is balanced.", ch, NULL, victim, TO_VICT);
return rNONE;
}
ch_ret spell_bloat(int sn, int level, CHAR_DATA *ch, void *vo)
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
if (IS_NPC(victim) || victim->pcdata->condition[COND_THIRST] < 0)
{
act( AT_MAGIC, "$N seems unaffected.", ch, NULL, victim, TO_CHAR);
return rSPELL_FAILED;
}
victim->pcdata->condition[COND_THIRST] += 10;
act( AT_MAGIC, "You feel bloated.", ch, NULL, victim, TO_VICT);
act( AT_MAGIC, "You fill $N up with water.", ch, NULL, victim, TO_CHAR);
return rNONE;
}
ch_ret spell_change_sex( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
AFFECT_DATA af;
SKILLTYPE *skill = get_skilltype(sn);
if ( IS_SET( victim->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( is_affected( victim, sn ) )
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
af.type = sn;
af.duration = 10 * level * DUR_CONV;
af.location = APPLY_SEX;
do
{
af.modifier = number_range( 0, 2 ) - victim->sex;
}
while ( af.modifier == 0 );
xCLEAR_BITS(af.bitvector);
affect_to_char( victim, &af );
/* set_char_color( AT_MAGIC, victim );
send_to_char( "You feel different.\n\r", victim );
if ( ch != victim )
send_to_char( "Ok.\n\r", ch );*/
successful_casting( skill, ch, victim, NULL );
return rNONE;
}
ch_ret spell_cure_blindness( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
SKILLTYPE *skill = get_skilltype(sn);
set_char_color( AT_MAGIC, ch );
if ( IS_SET( victim->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( !is_affected( victim, gsn_blindness ) )
{
if ( ch != victim )
send_to_char( "You work your cure, but it has no apparent effect.\n\r", ch );
else
send_to_char( "You don't seem to be blind.\n\r", ch );
return rSPELL_FAILED;
}
affect_strip( victim, gsn_blindness );
set_char_color( AT_MAGIC, victim);
send_to_char( "Your vision returns!\n\r", victim );
if ( ch != victim )
send_to_char( "You work your cure, restoring vision.\n\r", ch );
return rNONE;
}
ch_ret spell_cure_poison( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
SKILLTYPE *skill = get_skilltype(sn);
if ( IS_SET( victim->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( is_affected( victim, gsn_poison ) )
{
affect_strip( victim, gsn_poison );
set_char_color( AT_MAGIC, victim);
send_to_char( "A warm feeling runs through your body.\n\r", victim );
victim->mental_state = URANGE( -100, victim->mental_state, -10 );
if ( ch != victim )
{
act( AT_MAGIC, "A flush of health washes over $N.", ch, NULL, victim, TO_NOTVICT );
act( AT_MAGIC, "You lift the poison from $N's body.", ch, NULL, victim, TO_CHAR );
}
return rNONE;
}
else
{
set_char_color( AT_MAGIC, ch );
if ( ch != victim )
send_to_char( "You work your cure, but it has no apparent effect.\n\r", ch );
else
send_to_char( "You don't seem to be poisoned.\n\r", ch );
return rSPELL_FAILED;
}
}
ch_ret spell_curse( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
AFFECT_DATA af;
SKILLTYPE *skill = get_skilltype(sn);
if ( IS_SET( victim->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( IS_AFFECTED(victim, AFF_CURSE))
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
af.type = sn;
af.duration = (4*level) * DUR_CONV;
af.location = APPLY_HITROLL;
af.modifier = -1;
af.bitvector = meb(AFF_CURSE);
affect_to_char( victim, &af );
af.location = APPLY_SAVING_SPELL;
af.modifier = 1;
affect_to_char( victim, &af );
set_char_color( AT_MAGIC, victim);
send_to_char( "You feel unclean.\n\r", victim );
if ( ch != victim )
{
act( AT_MAGIC, "You utter a curse upon $N.", ch, NULL, victim, TO_CHAR );
act( AT_MAGIC, "$n utters a curse upon $N.", ch, NULL, victim, TO_NOTVICT );
}
return rNONE;
}
/* modified version of spell_transport -keo */
ch_ret spell_pigeon( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim;
char arg3[MAX_STRING_LENGTH];
OBJ_DATA *obj;
target_name = one_argument(target_name, arg3 );
if ( ( victim = get_char_world( ch, target_name ) ) == NULL
|| victim == ch
|| IS_SET(ch->in_room->room_flags, ROOM_INDOORS)
|| IS_SET(victim->in_room->room_flags, ROOM_INDOORS)
|| IS_SET(victim->in_room->room_flags, ROOM_PROTOTYPE))
{
send_to_char("The pigeon fails to find the recipient.\n\r", ch);
return rSPELL_FAILED;
}
if ( (obj = get_obj_carry( ch, arg3 ) ) == NULL
|| ( victim->carry_weight + get_obj_weight ( obj ) ) > can_carry_w(victim))
{
send_to_char("The pigeon flies off then returns with the item undelivered.\n\r", ch);
return rSPELL_FAILED;
}
if (obj->weight > 10) {
send_to_char("The pigeon cannot carry that much weight.\n\r", ch);
return rSPELL_FAILED;
}
if (IS_OBJ_STAT(obj, ITEM_ARTIFACT)) {
send_to_char("The item squirms out of the pigeon's grasp!\n\r", ch);
return rSPELL_FAILED;
}
separate_obj(obj);
if ( IS_OBJ_STAT( obj, ITEM_NODROP ) )
{
send_to_char( "You can't seem to let go of it.\n\r", ch );
return rSPELL_FAILED;
}
act( AT_MAGIC, "You attach $p to the pigeon's legs and it flies away.", ch, obj, NULL, TO_CHAR);
act( AT_MAGIC, "$n attaches $p to a pigeons's legs and it flies away.", ch, obj, NULL, TO_ROOM);
obj_from_char( obj );
obj_to_char( obj, victim );
act( AT_MAGIC, "A pigeon flies in and drops $p into your hands.", victim, obj, NULL, TO_CHAR);
act( AT_MAGIC, "A pigeon flies in and drops $p into $n's hands.", victim, obj, NULL, TO_ROOM);
save_char_obj(ch);
save_char_obj(victim);
return rNONE;
}
ch_ret spell_poison( int sn, int wil, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
AFFECT_DATA af;
bool first = TRUE;
if ( IS_AFFECTED( victim, AFF_POISON ) )
first = FALSE;
af.type = sn;
af.duration = wil * DUR_CONV;
af.location = APPLY_STR;
af.modifier = -2;
af.bitvector = meb(AFF_POISON);
affect_join( victim, &af );
set_char_color( AT_GREEN, victim );
send_to_char( "You feel very sick.\n\r", victim );
victim->mental_state = URANGE( 10, victim->mental_state
+ (first ? 5 : 0), 100 );
if ( ch != victim )
{
act( AT_GREEN, "$N shivers as your poison spreads through $S body.", ch, NULL, victim, TO_CHAR );
act( AT_GREEN, "$N shivers as $n's poison spreads through $S body.", ch, NULL, victim, TO_NOTVICT );
}
return rNONE;
}
ch_ret spell_remove_curse( int sn, int level, CHAR_DATA *ch, void *vo )
{
OBJ_DATA *obj;
CHAR_DATA *victim = (CHAR_DATA *) vo;
SKILLTYPE *skill = get_skilltype(sn);
if ( IS_SET( victim->immune, RIS_MAGIC ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( is_affected( victim, gsn_curse ) )
{
affect_strip( victim, gsn_curse );
set_char_color( AT_MAGIC, victim );
send_to_char( "The weight of your curse is lifted.\n\r", victim );
if ( ch != victim )
{
act( AT_MAGIC, "You dispel the curses afflicting $N.", ch, NULL, victim, TO_CHAR );
act( AT_MAGIC, "$n's dispels the curses afflicting $N.", ch, NULL, victim, TO_NOTVICT );
}
}
else
if ( victim->first_carrying )
{
for ( obj = victim->first_carrying; obj; obj = obj->next_content )
if ( !obj->in_obj
&& (IS_OBJ_STAT( obj, ITEM_NOREMOVE )
|| IS_OBJ_STAT( obj, ITEM_NODROP ) ) )
{
if ( IS_OBJ_STAT( obj, ITEM_NOREMOVE ) )
xREMOVE_BIT( obj->extra_flags, ITEM_NOREMOVE );
if ( IS_OBJ_STAT( obj, ITEM_NODROP ) )
xREMOVE_BIT( obj->extra_flags, ITEM_NODROP );
set_char_color( AT_MAGIC, victim );
send_to_char( "You feel a burden released.\n\r", victim );
if ( ch != victim )
{
act( AT_MAGIC, "You dispel the curses afflicting $N.", ch, NULL, victim, TO_CHAR );
act( AT_MAGIC, "$n's dispels the curses afflicting $N.", ch, NULL, victim, TO_NOTVICT );
}
return rNONE;
}
}
return rNONE;
}
ch_ret spell_remove_trap( int sn, int level, CHAR_DATA *ch, void *vo )
{
OBJ_DATA *obj;
OBJ_DATA *trap;
bool found;
int retcode;
SKILLTYPE *skill = get_skilltype(sn);
if ( !target_name || target_name[0] == '\0' )
{
send_to_char( "Remove trap on what?\n\r", ch );
return rSPELL_FAILED;
}
found = FALSE;
if ( !ch->in_room->first_content )
{
send_to_char( "You can't find that here.\n\r", ch );
return rNONE;
}
for ( obj = ch->in_room->first_content; obj; obj = obj->next_content )
if ( can_see_obj( ch, obj ) && nifty_is_name( target_name, obj->name ) )
{
found = TRUE;
break;
}
if ( !found )
{
send_to_char( "You can't find that here.\n\r", ch );
return rSPELL_FAILED;
}
if ( (trap = get_trap( obj )) == NULL )
{
failed_casting( skill, ch, NULL, NULL );
return rSPELL_FAILED;
}
if ( chance(ch, get_curr_wis(ch)) )
{
send_to_char( "Oops!\n\r", ch );
retcode = spring_trap(ch, trap);
if ( retcode == rNONE )
retcode = rSPELL_FAILED;
return retcode;
}
extract_obj( trap );
successful_casting( skill, ch, NULL, NULL );
return rNONE;
}
ch_ret spell_satiate(int sn, int level, CHAR_DATA *ch, void *vo)
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
if (IS_NPC(victim) || victim->pcdata->condition[COND_FULL] < 0) {
act( AT_MAGIC, "$N seems unaffected.", ch, NULL, victim, TO_CHAR);
return rSPELL_FAILED;
}
victim->pcdata->condition[COND_FULL] += 20;
act( AT_MAGIC, "You are satiated.", ch, NULL, victim, TO_VICT);
act( AT_MAGIC, "You fill $N's stomach.", ch, NULL, victim, TO_CHAR);
return rNONE;
}
/* room identify - keo */
ch_ret spell_sense_location( int sn, int level, CHAR_DATA *ch, void *vo )
{
char *sect;
int temp;
ch_printf(ch, "You are in %s in %s, on the world of %s.\n\r",
ch->in_room->name, ch->in_room->area->name, ch->in_room->area->resetmsg);
switch( ch->in_room->sector_type ) {
default: sect = "an indeterminate location";
break;
case SECT_INSIDE: sect = "inside";
break;
case SECT_CITY: sect = "a city street";
break;
case SECT_FIELD: sect = "a field";
break;
case SECT_FOREST: sect = "a forest";
break;
case SECT_HILLS: sect = "in the hills";
break;
case SECT_MOUNTAIN: sect = "in the mountains";
break;
case SECT_WATER_SWIM: sect = "swallow water";
break;
case SECT_WATER_NOSWIM: sect = "deep water";
break;
case SECT_UNDERWATER: sect = "underwater";
break;
case SECT_AIR: sect = "in the air";
break;
case SECT_DESERT: sect = "in the desert";
break;
case SECT_OCEANFLOOR: sect = "on the oceanfloor";
break;
case SECT_UNDERGROUND: sect = "underground";
break;
case SECT_LAVA: sect = "molten lava";
break;
case SECT_SWAMP: sect = "in the swamps";
break;
case SECT_BEACH: sect = "on a beach";
break;
case SECT_ICE: sect = "on ice";
break;
}
ch_printf(ch, "It appears to be %s.\n\r", sect);
if (ch->in_room->room_flags)
ch_printf(ch, "It seems to be %s.\n\r",
flag_string(ch->in_room->room_flags, r_flags) );
temp = ch->in_room->curr_water;
ch_printf(ch, "%s, and ", temp >= 100 ?
"The room is completely underwater" : temp > 50 ?
"The water is deep" : temp > 0 ? "The water is muddy and shallow"
: "The room is dry");
temp = ch->in_room->curr_vegetation;
ch_printf(ch, "%s vegetation grows.\n\r", temp >= 66 ?
"thick" : temp > 33 ? "some" : "no");
temp = ch->in_room->curr_resources;
ch_printf(ch, "There is a %s concentration of resources here.\n\r", temp >= 66 ?
"high" : temp > 33 ? "moderate" : "low");
if (ch->in_room->runes)
ch_printf( ch, "Runes of %s line the walls.\n\r",
flag_string(ch->in_room->runes, rune_flags) );
return rNONE;
}
ch_ret spell_shadowshroud( int sn, int level, CHAR_DATA *ch, void *vo )
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
if (IS_OBJ_STAT(obj, ITEM_DARK)) {
ch_printf(ch, "That item is already shrouded in shadows.\n\r");
return rSPELL_FAILED;
}
if (IS_OBJ_STAT(obj, ITEM_GLOW)) {
if (obj->value[4] == BRAND_RADIANT) {
send_to_char("You are unable to quench this radiant light.\n\r", ch);
return rSPELL_FAILED;
}
act( AT_MAGIC, "The $t grows dim and ceases to shine.", ch, myobj(obj), NULL, TO_CHAR);
act( AT_MAGIC, "$n's $t grows dim and ceases to shine.", ch, myobj(obj), NULL, TO_ROOM);
xREMOVE_BIT(obj->extra_flags, ITEM_GLOW);
return rNONE;
}
act( AT_MAGIC, "Swirling shadows shroud the $t.", ch, myobj(obj), NULL, TO_CHAR);
act( AT_MAGIC, "Swirling shadows shroud $n's $t.", ch, myobj(obj), NULL, TO_ROOM);
xSET_BIT(obj->extra_flags, ITEM_DARK);
return rNONE;
}
ch_ret spell_sleep( int sn, int level, CHAR_DATA *ch, void *vo )
{
AFFECT_DATA af;
int tmp;
CHAR_DATA *victim;
SKILLTYPE *skill = get_skilltype(sn);
if ( ( victim = get_char_room( ch, target_name ) ) == NULL )
{
send_to_char( "They aren't here.\n\r", ch );
return rSPELL_FAILED;
}
if ( !IS_NPC(victim) && IS_FIGHTING(victim) )
{
send_to_char( "You cannot sleep a fighting player.\n\r", ch );
return rSPELL_FAILED;
}
if ( IS_SET( victim->immune, RIS_SLEEP ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( SPELL_FLAG(skill, SF_PKSENSITIVE)
&& !IS_NPC(ch) && !IS_NPC(victim) )
tmp = get_curr_wil(ch)/2;
else
tmp = get_curr_wil(ch);
if ( number_range(1, get_curr_wil(ch))
< number_range(1, get_curr_wil(victim) + TALENT(victim, TAL_SECURITY)))
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if (ch == victim)
return rSPELL_FAILED;
if ( !IS_FIGHTING(victim) )
{
victim->last_hit = ch;
ch->last_hit = victim;
} else
return rSPELL_FAILED;
af.type = sn;
af.duration = get_curr_wil(ch)/5;
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = meb(AFF_SLEEP);
affect_join( victim, &af );
/* Added by Narn at the request of Dominus. */
if ( !IS_NPC( victim ) )
{
sprintf( log_buf, "%s has cast sleep on %s.", ch->name, victim->name );
to_channel( log_buf, "Spell", PERMIT_SECURITY );
}
if ( IS_AWAKE(victim) )
{
act( AT_MAGIC, "You feel very sleepy ..... zzzzzz.", victim, NULL, NULL, TO_CHAR );
act( AT_MAGIC, "$n goes to sleep.", victim, NULL, NULL, TO_ROOM );
victim->position = POS_SLEEPING;
}
if ( IS_NPC( victim ) )
start_hating( victim, ch );
return rNONE;
}
ch_ret spell_null( int sn, int level, CHAR_DATA *ch, void *vo )
{
send_to_char( "That's not a spell!\n\r", ch );
return rNONE;
}
/* don't remove, may look redundant, but is important */
ch_ret spell_notfound( int sn, int level, CHAR_DATA *ch, void *vo )
{
send_to_char( "That's not a spell!\n\r", ch );
return rNONE;
}
/*
* Haus' Spell Additions
*
*/
/*
* Syntax portal (mob/char)
* opens a 2-way EX_PORTAL from caster's room to room inhabited by
* mob or character won't mess with existing exits
*
* do_mp_open_passage, combined with spell_astral
*/
ch_ret spell_portal( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim;
ROOM_INDEX_DATA *targetRoom, *fromRoom;
int targetRoomVnum;
OBJ_DATA *portalObj;
EXIT_DATA *pexit;
char buf[MAX_STRING_LENGTH];
SKILLTYPE *skill = get_skilltype(sn);
/* No go if all kinds of things aren't just right, including the caster
and victim are not both pkill or both peaceful. -- Narn
*/
if ( ( victim = get_char_world( ch, target_name ) ) == NULL
|| victim == ch
|| !victim->in_room
|| IS_SET(victim->in_room->room_flags, ROOM_PRIVATE)
|| IS_SET(victim->in_room->room_flags, ROOM_SOLITARY)
|| IS_SET(victim->in_room->room_flags, ROOM_NO_ASTRAL)
|| IS_SET(victim->in_room->room_flags, ROOM_NO_RECALL)
|| IS_SET(victim->in_room->room_flags, ROOM_PROTOTYPE)
|| IS_SET(ch->in_room->room_flags, ROOM_NO_RECALL)
|| IS_SET(ch->in_room->room_flags, ROOM_NO_ASTRAL)
|| (IS_NPC(victim) && xIS_SET(victim->act, ACT_PROTOTYPE)))
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if (victim->in_room == ch->in_room)
{
send_to_char("They are right beside you!", ch);
return rSPELL_FAILED;
}
targetRoomVnum = victim->in_room->vnum;
fromRoom = ch->in_room;
targetRoom = victim->in_room;
/* Check if there already is a portal in either room. */
for ( pexit = fromRoom->first_exit; pexit; pexit = pexit->next )
{
if ( IS_SET( pexit->exit_info, EX_PORTAL ) )
{
send_to_char("There is already a portal in this room.\n\r",ch);
return rSPELL_FAILED;
}
if ( pexit->vdir == DIR_PORTAL )
{
send_to_char("You may not create a portal in this room.\n\r",ch);
return rSPELL_FAILED;
}
}
for ( pexit = targetRoom->first_exit; pexit; pexit = pexit->next )
if ( pexit->vdir == DIR_PORTAL )
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
pexit = make_exit( fromRoom, targetRoom, DIR_PORTAL );
pexit->keyword = STRALLOC( "portal" );
pexit->description = STRALLOC( "You gaze into the shimmering portal...\n\r" );
pexit->key = -1;
pexit->exit_info = EX_PORTAL | EX_xENTER | EX_HIDDEN | EX_xLOOK;
pexit->vnum = targetRoomVnum;
portalObj = create_object( get_obj_index( OBJ_VNUM_PORTAL ), 0 );
portalObj->timer = 5;
sprintf( buf, "a portal created by %s", ch->name );
STRFREE( portalObj->short_descr );
portalObj->short_descr = STRALLOC( buf );
/* support for new casting messages */
if ( !skill->hit_char || skill->hit_char[0] == '\0' )
{
set_char_color( AT_MAGIC, ch );
send_to_char("You utter an incantation, and a portal forms in front of you!\n\r", ch);
}
else
act( AT_MAGIC, skill->hit_char, ch, NULL, victim, TO_CHAR );
if ( !skill->hit_room || skill->hit_room[0] == '\0' )
act( AT_MAGIC, "$n utters an incantation, and a portal forms in front of you!", ch, NULL, NULL, TO_ROOM );
else
act( AT_MAGIC, skill->hit_room, ch, NULL, victim, TO_ROOM );
if ( !skill->hit_vict || skill->hit_vict[0] == '\0' ) {
act( AT_MAGIC, "A shimmering portal forms in front of you!", victim, NULL, NULL, TO_ROOM );
act( AT_MAGIC, "A shimmering portal forms in front of you!", victim, NULL, NULL, TO_CHAR );
} else
act( AT_MAGIC, skill->hit_vict, victim, NULL, victim, TO_ROOM );
portalObj = obj_to_room( portalObj, ch->in_room );
pexit = make_exit( targetRoom, fromRoom, DIR_PORTAL );
pexit->keyword = STRALLOC( "portal" );
pexit->description = STRALLOC( "You gaze into the shimmering portal...\n\r" );
pexit->key = -1;
pexit->exit_info = EX_PORTAL | EX_xENTER | EX_HIDDEN;
pexit->vnum = targetRoomVnum;
portalObj = create_object( get_obj_index( OBJ_VNUM_PORTAL ), 0 );
portalObj->timer = 5;
STRFREE( portalObj->short_descr );
portalObj->short_descr = STRALLOC( buf );
portalObj = obj_to_room( portalObj, targetRoom );
return rNONE;
}
ch_ret spell_recharge( int sn, int level, CHAR_DATA *ch, void *vo )
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
if ( obj->item_type == ITEM_STAFF
|| obj->item_type == ITEM_WAND)
{
separate_obj(obj);
if ( obj->value[2] == obj->value[1]
|| obj->value[1] > (obj->pIndexData->value[1] * 4) )
{
act( AT_FIRE, "$p bursts into flames, injuring you!", ch, obj, NULL, TO_CHAR );
act( AT_FIRE, "$p bursts into flames, charring $n!", ch, obj, NULL, TO_ROOM);
extract_obj(obj);
return rSPELL_FAILED;
}
if ( chance(ch, 2) )
{
act( AT_YELLOW, "$p glows with a blinding magical luminescence.",
ch, obj, NULL, TO_CHAR);
obj->value[1] *= 2;
obj->value[2] = obj->value[1];
return rNONE;
}
else
if ( chance(ch, 5) )
{
act( AT_YELLOW, "$p glows brightly for a few seconds...",
ch, obj, NULL, TO_CHAR);
obj->value[2] = obj->value[1];
return rNONE;
}
else
if ( chance(ch, 10) )
{
act( AT_WHITE, "$p disintegrates into a void.", ch, obj, NULL, TO_CHAR);
act( AT_WHITE, "$n's attempt at recharging fails, and $p disintegrates.",
ch, obj, NULL, TO_ROOM);
extract_obj(obj);
return rSPELL_FAILED;
}
else
if ( chance(ch, 20 ) )
{
send_to_char("Nothing happens.\n\r", ch);
return rSPELL_FAILED;
}
else
{
act( AT_MAGIC, "$p feels warm to the touch.", ch, obj, NULL, TO_CHAR);
--obj->value[1];
obj->value[2] = obj->value[1];
return rNONE;
}
}
else
{
send_to_char( "You can't recharge that!\n\r", ch);
return rSPELL_FAILED;
}
}
/* Ignores pickproofs, but can't unlock containers. -- Altrag 17/2/96 */
ch_ret spell_knock( int sn, int level, CHAR_DATA *ch, void *vo )
{
EXIT_DATA *pexit;
SKILLTYPE *skill = get_skilltype(sn);
set_char_color(AT_MAGIC, ch);
/*
* shouldn't know why it didn't work, and shouldn't work on pickproof
* exits. -Thoric
*/
if ( !(pexit=find_door(ch, target_name, FALSE))
|| !IS_SET(pexit->exit_info, EX_CLOSED)
|| !IS_SET(pexit->exit_info, EX_LOCKED)
|| IS_SET(pexit->exit_info, EX_PICKPROOF) )
{
failed_casting( skill, ch, NULL, NULL );
return rSPELL_FAILED;
}
REMOVE_BIT(pexit->exit_info, EX_LOCKED);
send_to_char( "*Click*\n\r", ch );
if ( pexit->rexit && pexit->rexit->to_room == ch->in_room )
REMOVE_BIT( pexit->rexit->exit_info, EX_LOCKED );
check_room_for_traps( ch, TRAP_UNLOCK | trap_door[pexit->vdir] );
return rNONE;
}
/* Tells to sleepers in are. -- Altrag 17/2/96 */
ch_ret spell_dream( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim;
char arg[MAX_INPUT_LENGTH];
target_name = one_argument(target_name, arg);
set_char_color(AT_MAGIC, ch);
if ( !(victim = get_char_world(ch, arg)) )
{
send_to_char("They aren't here.\n\r", ch);
return rSPELL_FAILED;
}
if ( victim->position != POS_SLEEPING )
{
send_to_char("They aren't asleep.\n\r", ch);
return rSPELL_FAILED;
}
if ( !target_name )
{
send_to_char("What do you want them to dream about?\n\r", ch );
return rSPELL_FAILED;
}
set_char_color(AT_TELL, victim);
ch_printf(victim, "You have dreams about %s telling you '%s'.\n\r",
PERS(ch, victim), target_name);
successful_casting( get_skilltype(sn), ch, victim, NULL );
/* send_to_char("Ok.\n\r", ch);*/
return rNONE;
}
/*******************************************************
* Everything after this point is part of SMAUG SPELLS *
*******************************************************/
/*
* Generic offensive spell damage attack -Thoric
*/
ch_ret spell_attack( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *victim = (CHAR_DATA *) vo;
SKILLTYPE *skill = get_skilltype(sn);
int dam;
ch_ret retcode = rNONE;
if (victim == NULL) {
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( skill->dice )
dam = UMAX( 0, dice_parse( ch, skill->dice ) );
else
dam = dice( 1, level/2 );
if ( retcode == rNONE && skill->affects
&& !char_died(ch) && !char_died(victim)
&& (!is_affected(victim, sn)
|| SPELL_FLAG(skill, SF_ACCUMULATIVE)
|| SPELL_FLAG(skill, SF_RECASTABLE)) )
retcode = spell_affectchar( sn, level, ch, victim );
return retcode;
}
/*
* Generic area attack -Thoric
*/
ch_ret spell_area_attack( int sn, int level, CHAR_DATA *ch, void *vo )
{
CHAR_DATA *vch, *vch_next;
SKILLTYPE *skill = get_skilltype(sn);
bool affects;
int dam;
bool ch_died = FALSE;
ch_ret retcode = rNONE;
if ( IS_SET( ch->in_room->room_flags, ROOM_SAFE ) )
{
failed_casting( skill, ch, NULL, NULL );
return rSPELL_FAILED;
}
affects = (skill->affects ? TRUE : FALSE);
if ( skill->hit_char && skill->hit_char[0] != '\0' )
act( AT_MAGIC, skill->hit_char, ch, NULL, NULL, TO_CHAR );
if ( skill->hit_room && skill->hit_room[0] != '\0' )
act( AT_MAGIC, skill->hit_room, ch, NULL, NULL, TO_ROOM );
for ( vch = ch->in_room->first_person; vch; vch = vch_next )
{
vch_next = vch->next_in_room;
if ( vch == ch )
continue;
if (!IS_SAME_PLANE(ch, vch))
continue;
if ( skill->dice )
dam = dice_parse(ch, skill->dice);
else
dam = dice( 1, level/2 );
if ( retcode == rNONE && affects && !char_died(ch) && !char_died(vch)
&& (!is_affected(vch, sn)
|| SPELL_FLAG(skill, SF_ACCUMULATIVE)
|| SPELL_FLAG(skill, SF_RECASTABLE)) )
retcode = spell_affectchar( sn, level, ch, vch );
if ( retcode == rCHAR_DIED || char_died(ch) )
{
ch_died = TRUE;
break;
}
}
return retcode;
}
ch_ret spell_affectchar( int sn, int level, CHAR_DATA *ch, void *vo )
{
AFFECT_DATA af;
SMAUG_AFF *saf;
SKILLTYPE *skill = get_skilltype(sn);
CHAR_DATA *victim = (CHAR_DATA *) vo;
int chance;
ch_ret retcode = rNONE;
if ( SPELL_FLAG( skill, SF_RECASTABLE ) )
affect_strip( victim, sn );
for ( saf = skill->affects; saf; saf = saf->next ) {
if ( saf->location >= REVERSE_APPLY )
victim = ch;
else
victim = (CHAR_DATA *) vo;
/* Check if char has this bitvector already */
af.bitvector = meb(saf->bitvector);
if ( saf->bitvector >= 0
&& xIS_SET(victim->affected_by, saf->bitvector)
&& !SPELL_FLAG(skill, SF_ACCUMULATIVE) )
continue;
/*
* necessary for affect_strip to work properly...
*/
switch ( saf->bitvector )
{
default: af.type = sn; break;
case AFF_POISON:
af.type = gsn_poison;
chance = ris_save( victim, level, RIS_POISON );
if ( chance == 1000 ) {
retcode = rVICT_IMMUNE;
if ( SPELL_FLAG(skill, SF_STOPONFAIL) )
return retcode;
continue;
}
victim->mental_state = URANGE( 30, victim->mental_state + 2, 100 );
break;
case AFF_BLIND: af.type = gsn_blindness; break;
case AFF_CURSE: af.type = gsn_curse; break;
case AFF_SLEEP: af.type = gsn_sleep;
chance = ris_save( victim, level, RIS_SLEEP );
if ( chance == 1000 )
{
retcode = rVICT_IMMUNE;
if ( SPELL_FLAG(skill, SF_STOPONFAIL) )
return retcode;
continue;
}
break;
case AFF_CHARM: af.type = gsn_charm_person;
chance = ris_save( victim, level, RIS_CHARM );
if ( chance == 1000 )
{
retcode = rVICT_IMMUNE;
if ( SPELL_FLAG(skill, SF_STOPONFAIL) )
return retcode;
continue;
}
break;
}
af.duration = dice_parse(ch, saf->duration);
af.modifier = dice_parse(ch, saf->modifier);
af.location = saf->location % REVERSE_APPLY;
if ( af.duration == 0 )
{
switch( af.location )
{
case APPLY_HIT:
victim->hit = URANGE( 0, victim->hit + af.modifier, victim->max_hit );
update_pos( victim );
break;
case APPLY_MANA:
victim->mana = URANGE( 0, victim->mana + af.modifier, victim->max_mana );
update_pos( victim );
break;
case APPLY_MOVE:
victim->move = URANGE( 0, victim->move + af.modifier, victim->max_move );
update_pos( victim );
break;
default:
affect_modify( victim, &af, TRUE );
break;
}
}
else
if ( SPELL_FLAG( skill, SF_ACCUMULATIVE ) )
affect_join( victim, &af );
else
affect_to_char( victim, &af );
}
update_pos( victim );
return retcode;
}
/*
* Generic spell affect -Thoric
*/
ch_ret spell_affect( int sn, int level, CHAR_DATA *ch, void *vo )
{
SMAUG_AFF *saf;
SKILLTYPE *skill = get_skilltype(sn);
CHAR_DATA *victim = (CHAR_DATA *) vo;
bool groupsp;
bool areasp;
bool hitchar = FALSE, hitroom = FALSE, hitvict = FALSE;
ch_ret retcode;
if ( !skill->affects )
{
bug( "spell_affect has no affects sn %d", sn );
return rNONE;
}
if ( SPELL_FLAG(skill, SF_GROUPSPELL) )
groupsp = TRUE;
else
groupsp = FALSE;
if ( SPELL_FLAG(skill, SF_AREA ) )
areasp = TRUE;
else
areasp = FALSE;
if ( !groupsp && !areasp )
{
/* Can't find a victim */
if ( !victim )
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( (skill->type != SKILL_HERB
&& IS_SET( victim->immune, RIS_MAGIC ))
|| is_immune( victim, SPELL_DAMAGE(skill) ) )
{
immune_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
/* Spell is already on this guy */
if ( is_affected( victim, sn )
&& !SPELL_FLAG( skill, SF_ACCUMULATIVE )
&& !SPELL_FLAG( skill, SF_RECASTABLE ) )
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
if ( (saf = skill->affects) && !saf->next
&& saf->location == APPLY_STRIPSN
&& !is_affected( victim, dice_parse(ch, saf->modifier) ) )
{
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
}
else
{
if ( skill->hit_char && skill->hit_char[0] != '\0' )
{
if ( strstr(skill->hit_char, "$N") )
hitchar = TRUE;
else
act( AT_MAGIC, skill->hit_char, ch, NULL, NULL, TO_CHAR );
}
if ( skill->hit_room && skill->hit_room[0] != '\0' )
{
if ( strstr(skill->hit_room, "$N") )
hitroom = TRUE;
else
act( AT_MAGIC, skill->hit_room, ch, NULL, NULL, TO_ROOM );
}
if ( skill->hit_vict && skill->hit_vict[0] != '\0' )
hitvict = TRUE;
if ( victim )
victim = victim->in_room->first_person;
else
victim = ch->in_room->first_person;
}
if ( !victim )
{
bug( "spell_affect: could not find victim: sn %d", sn );
failed_casting( skill, ch, victim, NULL );
return rSPELL_FAILED;
}
for ( ; victim; victim = victim->next_in_room )
{
if ( groupsp || areasp )
{
if ((groupsp && !is_same_group( victim, ch ))
|| IS_SET( victim->immune, RIS_MAGIC )
|| is_immune( victim, SPELL_DAMAGE(skill) )
|| (!SPELL_FLAG(skill, SF_RECASTABLE) && is_affected(victim, sn)))
continue;
if ( hitvict && ch != victim )
{
act( AT_MAGIC, skill->hit_vict, ch, NULL, victim, TO_VICT );
if ( hitroom )
{
act( AT_MAGIC, skill->hit_room, ch, NULL, victim, TO_NOTVICT );
act( AT_MAGIC, skill->hit_room, ch, NULL, victim, TO_CHAR );
}
}
else
if ( hitroom )
act( AT_MAGIC, skill->hit_room, ch, NULL, victim, TO_ROOM );
if ( ch == victim )
{
if ( hitvict )
act( AT_MAGIC, skill->hit_vict, ch, NULL, ch, TO_CHAR );
else
if ( hitchar )
act( AT_MAGIC, skill->hit_char, ch, NULL, ch, TO_CHAR );
}
else
if ( hitchar )
act( AT_MAGIC, skill->hit_char, ch, NULL, victim, TO_CHAR );
}
retcode = spell_affectchar( sn, level, ch, victim );
if ( !groupsp && !areasp )
{
if ( retcode == rVICT_IMMUNE )
immune_casting( skill, ch, victim, NULL );
else
successful_casting( skill, ch, victim, NULL );
break;
}
}
return rNONE;
}
/*
* Generic inventory object spell -Thoric
*/
ch_ret spell_obj_inv( int sn, int level, CHAR_DATA *ch, void *vo )
{
OBJ_DATA *obj = (OBJ_DATA *) vo;
SKILLTYPE *skill = get_skilltype(sn);
if ( !obj )
{
failed_casting( skill, ch, NULL, NULL );
return rNONE;
}
switch( SPELL_ACTION(skill) )
{
default:
case SA_NONE:
return rNONE;
case SA_CREATE:
if ( SPELL_FLAG(skill, SF_WATER) ) /* create water */
{
int water;
WEATHER_DATA *weath = ch->in_room->area->weather;
if ( obj->item_type != ITEM_DRINK_CON )
{
send_to_char( "It is unable to hold water.\n\r", ch );
return rSPELL_FAILED;
}
if ( obj->value[2] != LIQ_WATER && obj->value[1] != 0 )
{
send_to_char( "It contains some other liquid.\n\r", ch );
return rSPELL_FAILED;
}
water = UMIN( (skill->dice ? dice_parse(ch, skill->dice) : level)
* (weath->precip >= 0 ? 2 : 1),
obj->value[0] - obj->value[1] );
if ( water > 0 )
{
separate_obj(obj);
obj->value[2] = LIQ_WATER;
obj->value[1] += water;
if ( !is_name( "water", obj->name ) )
{
char buf[MAX_STRING_LENGTH];
sprintf( buf, "%s water", obj->name );
STRFREE( obj->name );
obj->name = STRALLOC( buf );
}
}
successful_casting( skill, ch, NULL, obj );
return rNONE;
}
if ( SPELL_DAMAGE(skill) == SD_FIRE ) /* burn object */
{
/* return rNONE; */
}
if ( SPELL_DAMAGE(skill) == SD_POISON /* poison object */
|| SPELL_CLASS(skill) == SC_DEATH )
{
switch( obj->item_type )
{
default:
failed_casting( skill, ch, NULL, obj );
break;
case ITEM_COOK:
case ITEM_FOOD:
case ITEM_DRINK_CON:
separate_obj(obj);
obj->value[3] = 1;
successful_casting( skill, ch, NULL, obj );
break;
}
return rNONE;
}
if ( SPELL_CLASS(skill) == SC_LIFE /* purify food/water */
&& (obj->item_type == ITEM_FOOD || obj->item_type == ITEM_DRINK_CON
|| obj->item_type == ITEM_COOK) )
{
switch( obj->item_type )
{
default:
failed_casting( skill, ch, NULL, obj );
break;
case ITEM_COOK:
case ITEM_FOOD:
case ITEM_DRINK_CON:
separate_obj(obj);
obj->value[3] = 0;
successful_casting( skill, ch, NULL, obj );
break;
}
return rNONE;
}
if ( SPELL_CLASS(skill) != SC_NONE )
{
failed_casting( skill, ch, NULL, obj );
return rNONE;
}
switch( SPELL_POWER(skill) ) /* clone object */
{
OBJ_DATA *clone;
default:
case SP_NONE:
if ( obj->cost > (get_curr_int(ch) * get_curr_wis(ch))/5 )
{
failed_casting( skill, ch, NULL, obj );
return rNONE;
}
break;
case SP_MINOR:
if ( obj->cost > (get_curr_int(ch) * get_curr_wis(ch))/3 )
{
failed_casting( skill, ch, NULL, obj );
return rNONE;
}
break;
case SP_GREATER:
if ( obj->cost > (get_curr_int(ch) * get_curr_wis(ch))/2 )
{
failed_casting( skill, ch, NULL, obj );
return rNONE;
}
break;
case SP_MAJOR:
if ( obj->cost > (get_curr_int(ch) * get_curr_wis(ch)) )
{
failed_casting( skill, ch, NULL, obj );
return rNONE;
}
break;
clone = clone_object(obj);
clone->timer = skill->dice ? dice_parse(ch, skill->dice) : 0;
obj_to_char( clone, ch );
successful_casting( skill, ch, NULL, obj );
}
return rNONE;
case SA_DESTROY:
case SA_RESIST:
case SA_SUSCEPT:
case SA_DIVINATE:
if ( SPELL_DAMAGE(skill) == SD_POISON ) /* detect poison */
{
if ( obj->item_type == ITEM_DRINK_CON
|| obj->item_type == ITEM_FOOD
|| obj->item_type == ITEM_COOK )
{
if ( obj->item_type == ITEM_COOK && obj->value[2] == 0)
send_to_char("It looks undercooked.\n\r", ch );
else if ( obj->value[3] != 0 )
send_to_char( "You smell poisonous fumes.\n\r", ch );
else
send_to_char( "It looks very delicious.\n\r", ch );
}
else
send_to_char( "It doesn't look poisoned.\n\r", ch );
return rNONE;
}
return rNONE;
case SA_OBSCURE: /* make obj invis */
if ( IS_OBJ_STAT(obj, ITEM_INVIS)
|| chance(ch, skill->dice ? dice_parse(ch, skill->dice) : 20))
{
failed_casting( skill, ch, NULL, NULL );
return rSPELL_FAILED;
}
successful_casting( skill, ch, NULL, obj );
xSET_BIT(obj->extra_flags, ITEM_INVIS);
return rNONE;
case SA_CHANGE:
return rNONE;
}
return rNONE;
}
/*
* Generic object creating spell -Thoric
*/
ch_ret spell_create_obj( int sn, int level, CHAR_DATA *ch, void *vo )
{
SKILLTYPE *skill = get_skilltype(sn);
int vnum = skill->value;
OBJ_DATA *obj;
OBJ_INDEX_DATA *oi;
/*
* Add predetermined objects here
*/
if ( (oi=get_obj_index(vnum)) == NULL
|| (obj=create_object(oi, 0)) == NULL )
{
failed_casting( skill, ch, NULL, NULL );
return rNONE;
}
obj->timer = skill->dice ? dice_parse( ch, skill->dice ) : 0;
successful_casting( skill, ch, NULL, obj );
if ( !IS_OBJ_STAT(obj, ITEM_NO_TAKE) )
obj_to_char( obj, ch );
else
obj_to_room( obj, ch->in_room );
return rNONE;
}
/*
* Generic mob creating spell -Thoric
*/
ch_ret spell_create_mob( int sn, int level, CHAR_DATA *ch, void *vo )
{
SKILLTYPE *skill = get_skilltype(sn);
int vnum = skill->value;
CHAR_DATA *mob;
MOB_INDEX_DATA *mi;
AFFECT_DATA af;
/*
* Add predetermined mobiles here
*/
if ( vnum == 0 )
{
if ( !str_cmp( target_name, "cityguard" ) )
vnum = MOB_VNUM_CITYGUARD;
if ( !str_cmp( target_name, "vampire" ) )
vnum = MOB_VNUM_VAMPIRE;
}
if ( (mi=get_mob_index(vnum)) == NULL
|| (mob=create_mobile(mi)) == NULL )
{
failed_casting( skill, ch, NULL, NULL );
return rNONE;
}
mob->max_hit = get_curr_con(mob)*25;
mob->hit = mob->max_hit;
mob->gold = 0;
successful_casting( skill, ch, mob, NULL );
char_to_room( mob, ch->in_room );
add_follower( mob, ch );
af.type = sn;
af.duration = (number_fuzzy( (level + 1) / 3 ) + 1) * DUR_CONV;
af.location = 0;
af.modifier = 0;
af.bitvector = meb(AFF_CHARM);
affect_to_char( mob, &af );
return rNONE;
}
/*
* Generic handler for new "SMAUG" spells -Thoric
*/
ch_ret spell_smaug( int sn, int level, CHAR_DATA *ch, void *vo )
{
char arg2[MAX_STRING_LENGTH];
struct skill_type *skill = get_skilltype(sn);
/* added bad skill check - shogar */
if(!skill || sn == -1)
{
if (ch && ch->name && ch->in_room && ch->in_room->vnum)
bug("bad skill %d passed to spell_smaug: ch->name %s, room %d",sn, ch->name, ch->in_room->vnum);
return rNONE;
}
if (ranged_target_name)
one_argument(ranged_target_name, arg2);
if (SPELL_FLAG(skill, SF_EARTH)
&& (ch->in_room->curr_vegetation < 33)) {
act( AT_GREEN, "There isn't enough vegetation here to cast this spell.", ch, NULL, NULL, TO_CHAR);
return rNONE;
}
if (SPELL_FLAG(skill, SF_AIR)
&& (ch->in_room->curr_water >= 100)) {
act( AT_CYAN, "There isn't enough air here to cast this spell.", ch, NULL, NULL, TO_CHAR);
act( AT_CYAN, "Some water bubbles float up toward the surface.", ch, NULL, NULL, TO_ROOM);
return rNONE;
}
if (SPELL_FLAG(skill, SF_WATER)
&& (ch->in_room->curr_water < 33)) {
act( AT_BLUE, "There isn't enough water here to cast this spell.", ch, NULL, NULL, TO_CHAR);
return rNONE;
}
if (SPELL_FLAG(skill, SF_ASTRAL)
&& (!IS_AFFECTED(ch, AFF_DREAMWORLD))) {
send_to_char("You must be in the dreamworld to cast this spell.\n\r", ch);
return rNONE;
}
if (SPELL_FLAG(skill, SF_GROUND)
&& (IS_SET(ch->in_room->room_flags, ROOM_NOFLOOR))) {
send_to_char("There needs to be ground nearby to cast this spell.\n\r", ch);
return rNONE;
}
switch( skill->target )
{
case TAR_IGNORE:
/* offensive area spell */
if ( SPELL_FLAG(skill, SF_AREA)
&& ((SPELL_ACTION(skill) == SA_DESTROY
&& SPELL_CLASS(skill) == SC_LIFE)
|| (SPELL_ACTION(skill) == SA_CREATE
&& SPELL_CLASS(skill) == SC_DEATH)) )
return spell_area_attack( sn, level, ch, vo );
if ( SPELL_ACTION(skill) == SA_CREATE )
{
if ( SPELL_FLAG(skill, SF_OBJECT) ) /* create object */
return spell_create_obj( sn, level, ch, vo );
if ( SPELL_CLASS(skill) == SC_LIFE ) /* create mob */
return spell_create_mob( sn, level, ch, vo );
}
/* affect a distant player */
if ( SPELL_FLAG(skill, SF_DISTANT)
&& SPELL_FLAG(skill, SF_CHARACTER))
return spell_affect(sn, level, ch, get_char_world( ch, target_name ));
/* affect a player in this room (should have been TAR_CHAR_XXX) */
if ( SPELL_FLAG(skill, SF_CHARACTER) )
return spell_affect(sn, level, ch, get_char_room( ch, target_name ));
if ( skill->range > 0 && find_door(ch, arg2, TRUE) != NULL && (
(SPELL_ACTION(skill) == SA_DESTROY
&& SPELL_CLASS(skill) == SC_LIFE)
|| (SPELL_ACTION(skill) == SA_CREATE
&& SPELL_CLASS(skill) == SC_DEATH) ) )
/* will fail, or be an area/group affect */
return spell_affect( sn, level, ch, vo );
case TAR_CHAR_OFFENSIVE:
if ((skill->range > 0)
&& ranged_target_name
&& find_door(ch, arg2, TRUE) != NULL
&& ((SPELL_ACTION(skill) == SA_DESTROY
&& SPELL_CLASS(skill) == SC_LIFE)
|| (SPELL_ACTION(skill) == SA_CREATE
&& SPELL_CLASS(skill) == SC_DEATH))
&& get_char_room(ch, ranged_target_name)==NULL)
/* a regular damage inflicting spell attack */
if ((SPELL_ACTION(skill) == SA_DESTROY && SPELL_CLASS(skill) == SC_LIFE)
|| (SPELL_ACTION(skill) == SA_CREATE && SPELL_CLASS(skill) == SC_DEATH))
return spell_attack( sn, level, ch, vo );
/* a nasty spell affect */
return spell_affect( sn, level, ch, vo );
case TAR_CHAR_DEFENSIVE:
case TAR_CHAR_SELF:
return spell_affect( sn, level, ch, vo );
case TAR_OBJ_INV:
return spell_obj_inv( sn, level, ch, vo );
}
return rNONE;
}
/* tap <amount> - draws <amount> mana from a zone into a char, plus or minus a random element */
void do_tap(CHAR_DATA *ch, char *argument) {
int amount;
char arg[MAX_INPUT_LENGTH];
argument = one_argument(argument, arg);
amount = atoi(arg);
if (amount < 1) {
send_to_char("You can't do that.\r\n", ch);
return;
}
if (amount > ch->max_mana) {
send_to_char("That might be harmful to your health.\r\n", ch);
return;
}
STRFREE(ch->last_taken);
ch->last_taken = STRALLOC("tapping mana");
ch->wait=UMAX(ch->wait, (amount/(100+TALENT(ch, TAL_CATALYSM))));
amount += number_range(-10,
(ch->in_room->area->weather->mana * 10) - amount);
if (amount < 0) {
act(AT_MAGIC, "There seems to be a powerful mana vaccuum here!", ch, NULL, NULL, TO_CHAR);
act(AT_MAGIC, "$n concentrates, then looks startled as $s body glows white for an instant.", ch, NULL, NULL, TO_ROOM);
} else {
act(AT_MAGIC, "You open yourself to the surroundings, letting extra mana flow into you.", ch, NULL, NULL, TO_CHAR);
act(AT_MAGIC, "$n concentrates, then gets a content look on $s face.", ch, NULL, NULL, TO_ROOM);
}
ch->mana += amount;
ch->in_room->area->weather->mana -= (int)amount / 100;
}
void do_link(CHAR_DATA *ch, char *argument) {
CHAR_DATA *victim;
if (ch->curr_talent[TAL_CATALYSM] < 100) {
huh(ch);
return;
}
victim = find_target(ch, argument, FALSE);
if (!victim) return;
if (IS_NPC(victim)) {
send_to_char("You can't link with a mensch!\n\r", ch);
return;
}
if (victim == ch) {
if (ch->pcdata->magiclink) {
act(AT_MAGIC, "$n breaks off the magic link.", ch, NULL, victim, TO_VICT);
act(AT_MAGIC, "You break off the magic link with $N.", ch, NULL, ch->pcdata->magiclink, TO_CHAR);
ch->pcdata->magiclink->pcdata->magiclink = NULL;
ch->pcdata->magiclink = NULL;
} else {
send_to_char("You are not linked to anyone.\n\r", ch);
}
return;
}
if (ch->pcdata->magiclink) {
send_to_char("You are already linked with somebody.\n\r", ch);
return;
}
if (victim->pcdata->magiclink) {
send_to_char("They are already linked with somebody.\n\r", ch);
return;
}
if (IS_NPC(victim)) {
send_to_char("They have no magic to link with.\n\r", ch);
return;
}
if (!IS_CONSENTING(victim, ch)) {
send_to_char("You cannot link your magic with them without their consent.\n\r", ch);
return;
}
act(AT_MAGIC, "You link your magic together with $N.", ch, NULL, victim, TO_CHAR);
act(AT_MAGIC, "$n links $s magic together with you.", ch, NULL, victim, TO_VICT);
ch->pcdata->magiclink = victim;
victim->pcdata->magiclink = ch;
}
/* Return TRUE if it stops you from casting the spell */
bool check_manastorm(CHAR_DATA *ch) {
WEATHER_DATA *weath;
int storm;
weath = ch->in_room->area->weather;
if (weath->mana < 200) return FALSE;
if (weath->wind < 200) return FALSE;
storm = weath->wind + weath->precip;
storm *= weath->mana;
if (storm < 4000) return FALSE;
switch(number_range(1,5)) {
case 1:
act(AT_CHAOS, "The errant mana surges through your being!",
ch, NULL, NULL, TO_CHAR);
ch->mana += 100 + (int)storm/4000;
return TRUE;
case 2:
act(AT_CHAOS, "White-violet lightning sears your being!",
ch, NULL, NULL, TO_CHAR);
act(AT_CHAOS, "White-violet lightning sears $n's being!",
ch, NULL, NULL, TO_ROOM);
lose_hp(ch, 100 + (int)storm/4000);
return TRUE;
default:
if (number_range(1, get_curr_wil(ch)) < 100) {
act(AT_CHAOS, "You are unable to control the mana flows.",
ch, NULL, NULL, TO_CHAR);
return TRUE;
} else {
act(AT_CHAOS, "With great concentration you control the mana through the storm.",
ch, NULL, NULL, TO_CHAR);
return FALSE;
}
}
return TRUE;
}