/****************************************************************************
* [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame | *
* -----------------------------------------------------------| \\._.// *
* SmaugWiz (C) 1998 by Russ Pillsbury (Windows NT version) | (0...0) *
* -----------------------------------------------------------| ).:.( *
* SMAUG (C) 1994, 1995, 1996 by Derek Snider | {o o} *
* -----------------------------------------------------------| / ' ' \ *
* SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus, |~'~.VxvxV.~'~*
* Scryn, Swordbearer, Rennard, Tricops, and Gorog. | *
* ------------------------------------------------------------------------ *
* 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 Staerfeldt, Tom Madsen, and Katja Nyboe. *
* ------------------------------------------------------------------------ *
* Spell handling module *
****************************************************************************/
#include "stdafx.h"
#include "smaug.h"
#include "SysData.h"
#include "skill.h"
#include "mobiles.h"
#include "objects.h"
#include "rooms.h"
#include "deity.h"
#include "area.h"
#include "races.h"
#include "class.h"
#include "Exits.h"
#include "SmaugWizDoc.h"
#include "descriptor.h"
#include "character.h"
// Local functions.
void say_spell (CCharacter *ch, int sn);
CCharacter *make_poly_mob (CCharacter *ch, int vnum);
ch_ret spell_affect (int sn, int level, CCharacter *ch, void *vo);
ch_ret spell_affectchar (int sn, int level, CCharacter *ch, void *vo);
BOOL CanMorph (CCharacter& Ch, const char* morph);
/*
* Is immune to a damage type
*/
BOOL is_immune (CCharacter *ch, short damtype)
{
switch (damtype) {
case SD_FIRE: if (ch->IsImmuneFire ()) return TRUE;
case SD_COLD: if (ch->IsImmuneCold ()) return TRUE;
case SD_ELECTRICITY: if (ch->IsImmuneElectricity ()) return TRUE;
case SD_ENERGY: if (ch->IsImmuneEnergy ()) return TRUE;
case SD_ACID: if (ch->IsImmuneAcid ()) return TRUE;
case SD_POISON: if (ch->IsImmunePoison ()) return TRUE;
case SD_DRAIN: if (ch->IsImmuneEnergyDrain ()) return TRUE;
}
return FALSE;
}
// Lookup a skill by name, only stopping at skills the player has.
int ch_slookup (CCharacter *ch, const char *name)
{
if (ch->IsNpc ())
return SkillTable.Lookup (name);
for (int sn=0; sn < SkillTable.GetCount (); sn++) {
if (! SkillTable.GetName (sn))
break;
if (ch->GetPcData ()->learned[sn] > 0
&& ch->GetLevel () >=
SkillTable.GetClassLevel (sn, ch->GetClass ())
&& LOWER (name [0]) == LOWER (SkillTable.GetName (sn) [0])
&& ! str_prefix (name, SkillTable.GetName (sn)))
return sn;
}
return -1;
}
// Lookup a personal skill
int personal_lookup (CCharacter *ch, const char *name)
{
return -1;
}
int find_spell (CCharacter *ch, const char *name, BOOL know)
{
if (ch->IsNpc () || ! know)
return SkillTable.Bsearch (name, gsn_first_spell, gsn_first_skill-1);
else
return SkillTable.ChBsearch (ch, name, gsn_first_spell,
gsn_first_skill - 1);
}
int find_skill (CCharacter *ch, const char *name, BOOL know)
{
if (ch->IsNpc () || ! know)
return SkillTable.Bsearch (name, gsn_first_skill, gsn_first_weapon-1);
else
return SkillTable.ChBsearch (ch, name, gsn_first_skill,
gsn_first_weapon-1);
}
int find_weapon (CCharacter *ch, const char *name, BOOL know)
{
if (ch->IsNpc () || ! know)
return SkillTable.Bsearch (name, gsn_first_weapon, gsn_first_tongue-1);
else
return SkillTable.ChBsearch (ch, name, gsn_first_weapon,
gsn_first_tongue-1);
}
int find_tongue (CCharacter *ch, const char *name, BOOL know)
{
if (ch->IsNpc () || ! know)
return SkillTable.Bsearch (name, gsn_first_tongue, gsn_top_sn-1);
else
return SkillTable.ChBsearch (ch, name, gsn_first_tongue,
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 < SkillTable.GetCount (); sn++)
if (slot == SkillTable.GetSlot (sn))
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 (CSkill *skill, CCharacter *ch,
CCharacter *victim, CObjData *obj)
{
short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT);
short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME);
if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) {
chit = chitroom;
chitme = chitroom;
}
if (ch && ch != victim) {
if (skill->ValidHitCharMsg ())
act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR);
else
if (skill->GetType () == SKILL_SPELL)
act (chit, "Ok.", ch, NULL, NULL, TO_CHAR);
}
if (ch && skill->ValidHitRoomMsg ())
act (chitroom, skill->GetHitRoomMsg (), ch, obj, victim, TO_NOTVICT);
if (ch && victim && skill->ValidHitVictMsg ()) {
if (ch != victim)
act (chitme, skill->GetHitVictMsg (), ch, obj, victim, TO_VICT);
else
act (chitme, skill->GetHitVictMsg (), ch, obj, victim, TO_CHAR);
}
else
if (ch && ch == victim && skill->GetType () == SKILL_SPELL)
act (chitme, "Ok.", ch, NULL, NULL, TO_CHAR);
}
// Fancy message handling for a failed casting -Thoric
void failed_casting (CSkill *skill, CCharacter *ch,
CCharacter *victim, CObjData *obj)
{
short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT);
short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME);
if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) {
chit = chitroom;
chitme = chitroom;
}
if (ch && ch != victim) {
if (skill->ValidMissCharMsg ())
act (chit, skill->GetMissCharMsg (), ch, obj, victim, TO_CHAR);
else
if (skill->GetType () == SKILL_SPELL)
act (chit, "You failed.", ch, NULL, NULL, TO_CHAR);
}
if (ch && skill->ValidMissRoomMsg ()
&& str_cmp (skill->GetMissRoomMsg (), "supress"))
act (chitroom, skill->GetMissRoomMsg (),
ch, obj, victim, TO_NOTVICT);
if (ch && victim && skill->ValidMissVictMsg ()) {
if (ch != victim)
act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_VICT);
else
act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_CHAR);
}
else if (ch && ch == victim) {
if (skill->ValidMissCharMsg ())
act (chitme, skill->GetMissCharMsg (), ch, obj, victim, TO_CHAR);
else
if (skill->GetType () == SKILL_SPELL)
act (chitme, "You failed.", ch, NULL, NULL, TO_CHAR);
}
}
// Fancy message handling for being immune to something -Thoric
void immune_casting (CSkill *skill, CCharacter *ch, CCharacter *victim,
CObjData *obj)
{
short chitroom = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_ACTION);
short chit = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HIT);
short chitme = (skill->GetType () == SKILL_SPELL ? AT_MAGIC : AT_HITME);
if (skill->GetTarget () != TAR_CHAR_OFFENSIVE) {
chit = chitroom;
chitme = chitroom;
}
if (ch && ch != victim) {
if (skill->ValidImmuneCharMsg ())
act (chit, skill->GetImmuneCharMsg (), ch, obj, victim, TO_CHAR);
else
if (skill->ValidHitCharMsg ())
act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR);
else
if (skill->GetType () == SKILL_SPELL || skill->GetType () == SKILL_SKILL)
act (chit, "That appears to have no effect.",
ch, NULL, NULL, TO_CHAR);
}
if (ch && skill->ValidImmuneRoomMsg ())
act (chitroom, skill->GetImmuneRoomMsg (), ch, obj, victim, TO_NOTVICT);
else
if (ch && skill->ValidMissRoomMsg ())
act (chitroom, skill->GetMissRoomMsg (), ch, obj, victim, TO_NOTVICT);
if (ch && victim && skill->ValidImmuneVictMsg ()) {
if (ch != victim)
act (chitme, skill->GetImmuneVictMsg (), ch, obj, victim, TO_VICT);
else
act (chitme, skill->GetImmuneVictMsg (), ch, obj, victim, TO_CHAR);
}
else if (ch && victim && skill->ValidMissVictMsg ()) {
if (ch != victim)
act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_VICT);
else
act (chitme, skill->GetMissVictMsg (), ch, obj, victim, TO_CHAR);
}
else if (ch && ch == victim) {
if (skill->ValidImmuneCharMsg ())
act (chit, skill->GetImmuneCharMsg (), ch, obj, victim, TO_CHAR);
else
if (skill->ValidMissCharMsg ())
act (chit, skill->GetHitCharMsg (), ch, obj, victim, TO_CHAR);
else
if (skill->GetType () == SKILL_SPELL || skill->GetType () == SKILL_SKILL)
act (chit, "That appears to have no affect.", ch, NULL, NULL, TO_CHAR);
}
}
// Utter mystical words for an sn.
void say_spell (CCharacter *ch, int sn)
{
char buf [MAX_STRING_LENGTH];
char buf2 [MAX_STRING_LENGTH];
CCharacter *rch;
char *pName;
int iSyl;
int length;
CSkill *skill = SkillTable.GetValidSkill (sn);
struct syl_type {
char *Old;
char *New;
};
static const struct syl_type syl_table [] = {
{ " ", " " },
{ "ar", "abra" },
{ "au", "kada" },
{ "bless", "fido" },
{ "blind", "nose" },
{ "bur", "mosa" },
{ "cu", "judi" },
{ "de", "oculo" },
{ "en", "unso" },
{ "light", "dies" },
{ "lo", "hi" },
{ "mor", "zak" },
{ "move", "sido" },
{ "ness", "lacri" },
{ "ning", "illa" },
{ "per", "duda" },
{ "polymorph", "iaddahs" },
{ "ra", "gru" },
{ "re", "candus" },
{ "son", "sabru" },
{ "tect", "infra" },
{ "tri", "cula" },
{ "ven", "nofo" },
{ "a", "a" }, { "b", "b" }, { "c", "q" }, { "d", "e" },
{ "e", "z" }, { "f", "y" }, { "g", "o" }, { "h", "p" },
{ "i", "u" }, { "j", "y" }, { "k", "t" }, { "l", "r" },
{ "m", "w" }, { "n", "i" }, { "o", "a" }, { "p", "s" },
{ "q", "d" }, { "r", "f" }, { "s", "g" }, { "t", "h" },
{ "u", "j" }, { "v", "z" }, { "w", "x" }, { "x", "n" },
{ "y", "l" }, { "z", "k" },
{ "", "" }
};
buf [0] = '\0';
for (pName = skill->GetName (); *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 utters the words, '%s'.", buf);
sprintf (buf, "$n utters the words, '%s'.", skill->GetName ());
rch = ch->GetInRoom ()->first_person;
for (; rch; rch = rch->GetNextInRoom ()) {
if (rch != ch)
act (AT_MAGIC, ch->GetClass () == rch->GetClass () ? buf : buf2,
ch, NULL, rch, TO_VICT);
}
}
// Make adjustments to saving throw based in RIS -Thoric
int ris_save (CCharacter *ch, int chance, int ris)
{
short modifier;
modifier = 10;
if (ch->IsImmune (ris))
modifier -= 10;
if (ch->CanResist (ris))
modifier -= 2;
if (ch->IsSusceptible (ris))
modifier += 2;
if (modifier <= 0)
return 1000;
if (modifier == 10)
return chance;
return (chance * modifier) / 10;
}
// Fancy dice expression parsing complete with order of operations,
// simple exponent support, dice support as well as a few extra
// variables: L = level, H = hp, M = mana, V = move, S = str, X = dex
// I = int, W = wis, C = con, A = cha, U = luck, A = age
//
// Used for spell dice parsing, ie: 3d8+L-6
int rd_parse (CCharacter *ch, int level, 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] == ')')
if ((*exp == '(') && !strchr (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 'L': case 'l': return level;
case 'H': case 'h': return ch->GetHp ();
case 'M': case 'm': return ch->GetMana ();
case 'V': case 'v': return ch->GetMove ();
case 'S': case 's': return ch->GetCurrentStrength ();
case 'I': case 'i': return ch->GetIntelligence ();
case 'W': case 'w': return ch->GetWisdom ();
case 'X': case 'x': return ch->GetDexterity ();
case 'C': case 'c': return ch->GetConstitution ();
case 'A': case 'a': return get_curr_cha (ch);
case 'U': case 'u': return get_curr_lck (ch);
case 'Y': case 'y': return ch->GetAge ();
}
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 < (int) 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) then x = lop;
else if (gop) then 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, level, sexp [0]);
switch (operation) {
case '-': total -= rd_parse (ch, level, sexp [1]); break;
case '+': total += rd_parse (ch, level, sexp [1]); break;
case '*': total *= rd_parse (ch, level, sexp [1]); break;
case '/': total /= rd_parse (ch, level, sexp [1]); break;
case '%': total %= rd_parse (ch, level, sexp [1]); break;
case 'd':
case 'D':
total = dice (total, rd_parse (ch, level, sexp [1]));
break;
case '<':
total = (total < rd_parse (ch, level, sexp [1]));
break;
case '>':
total = (total > rd_parse (ch, level, sexp [1]));
break;
case '=':
total = (total == rd_parse (ch, level, sexp [1]));
break;
case '{':
total = UMIN (total, rd_parse (ch, level, sexp [1]));
break;
case '}':
total = UMAX (total, rd_parse (ch, level, sexp [1]));
break;
case '^':
{
int y = rd_parse (ch, level, sexp[1]);
int 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 (CCharacter *ch, int level, char *exp)
{
char buf [MAX_INPUT_LENGTH];
strcpy (buf, exp);
return rd_parse (ch, level, buf);
}
// Compute a saving throw.
// Negative apply's make saving throw better.
BOOL saves_poison_death (int level, CCharacter *victim)
{
int save;
save = 50 + (victim->GetLevel () - level -
victim->saving_poison_death) * 5;
save = URANGE (5, save, 95);
return chance (victim, save);
}
BOOL saves_wands (int level, CCharacter *victim)
{
int save;
if (victim->IsImmuneMagic ())
return TRUE;
save = 50 + (victim->GetLevel () - level - victim->saving_wand) * 5;
save = URANGE (5, save, 95);
return chance (victim, save);
}
BOOL saves_para_petri (int level, CCharacter *victim)
{
int save;
save = 50 + (victim->GetLevel () - level -
victim->saving_para_petri) * 5;
save = URANGE (5, save, 95);
return chance (victim, save);
}
BOOL saves_breath (int level, CCharacter *victim)
{
int save;
save = 50 + (victim->GetLevel () - level - victim->saving_breath) * 5;
save = URANGE (5, save, 95);
return chance (victim, save);
}
BOOL saves_spell_staff (int level, CCharacter *victim)
{
int save;
if (victim->IsImmuneMagic ())
return TRUE;
if (victim->IsNpc () && level > 10)
level -= 5;
save = 50 + (victim->GetLevel () - level -
victim->saving_spell_staff) * 5;
save = URANGE (5, save, 95);
return chance (victim, save);
}
// 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 (CCharacter *ch, int sn)
{
CSkill *skill = SkillTable.GetValidSkill (sn);
char *comp = skill->GetComponents ();
char *check;
char arg [MAX_INPUT_LENGTH];
BOOL consume, fail, found;
int val, value;
CObjData *obj;
POSITION pos;
// 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;
}
value = atoi (check);
obj = NULL;
switch (UPPER (arg [0])) {
case 'T':
pos = ch->GetHeadCarryPos ();
while (obj = ch->GetNextCarrying (pos))
if (obj->item_type == value) {
if (fail) {
ch->SendText ("Something disrupts the casting of "
"this spell...\n\r");
return FALSE;
}
found = TRUE;
break;
}
break;
case 'V':
pos = ch->GetHeadCarryPos ();
while (obj = ch->GetNextCarrying (pos))
if (obj->pIndexData->vnum == value) {
if (fail) {
ch->SendText ("Something disrupts the casting of "
"this spell...\n\r");
return FALSE;
}
found = TRUE;
break;
}
break;
case 'K':
pos = ch->GetHeadCarryPos ();
while (obj = ch->GetNextCarrying (pos))
if (nifty_is_name (check, obj->GetName ())) {
if (fail) {
ch->SendText ("Something disrupts the casting of "
"this spell...\n\r");
return FALSE;
}
found = TRUE;
break;
}
break;
case 'G':
if (ch->GetGold () >= value)
if (fail) {
ch->SendText ("Something disrupts the casting of this "
"spell...\n\r");
return FALSE;
} else {
if (consume) {
set_char_color (AT_GOLD, ch);
ch->SendText ("You feel a little lighter...\n\r");
ch->AddGold (-value);
}
continue;
}
break;
case 'H':
if (ch->GetHp () >= value)
if (fail) {
ch->SendText ("Something disrupts the casting of this "
"spell...\n\r");
return FALSE;
} else {
if (consume) {
set_char_color (AT_BLOOD, ch);
ch->SendText ("You feel a little weaker...\n\r");
ch->AddHp (-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) {
ch->SendText ("Something is missing...\n\r");
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 (CCharacter *ch, char *arg, int sn, CCharacter **victim,
CObjData **obj)
{
CSkill *skill = SkillTable.GetValidSkill (sn);
void *vo = NULL;
*victim = NULL;
*obj = NULL;
switch (skill->GetTarget ()) {
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->GetFightWho ()) == NULL) {
ch->SendText ("Cast the spell on whom?\n\r");
return &pAbort;
}
} else {
if ((*victim = get_char_room (ch, arg)) == NULL) {
ch->SendText ("They aren't here.\n\r");
return &pAbort;
}
}
if (is_safe (ch, *victim))
return &pAbort;
if (ch == *victim) {
if (skill->IsNoSelf ()) {
ch->SendText ("You can't cast this on yourself!\n\r");
return &pAbort;
}
ch->SendText ("Cast this on yourself? Okay...\n\r");
}
if (! ch->IsNpc ()) {
if (! (*victim)->IsNpc ()) {
if (get_timer (ch, TIMER_PKILLED) > 0) {
ch->SendText ("You have been killed in the last 5 minutes.\n\r");
return &pAbort;
}
if (get_timer (*victim, TIMER_PKILLED) > 0) {
ch->SendText ("This player has been killed in the last 5 minutes.\n\r");
return &pAbort;
}
if (ch->IsNice () && ch != *victim) {
ch->SendText (
"You are too nice to attack another player.\n\r");
return &pAbort;
}
if (*victim != ch)
ch->SendText (
"You really shouldn't do this to another player...\n\r");
}
if (ch->IsCharmed () && ch->GetMaster () == *victim) {
ch->SendText ("You can't do that on your own follower.\n\r");
return &pAbort;
}
}
check_illegal_pk (ch, *victim);
if (ch != *victim) then adjust_favor (ch, 4, 1);
vo = (void *) *victim;
break;
case TAR_CHAR_DEFENSIVE:
if (arg [0] == '\0')
*victim = ch;
else {
if ((*victim = get_char_room (ch, arg)) == NULL) {
ch->SendText ("They aren't here.\n\r");
return &pAbort;
}
}
if (ch == *victim && SkillTable.GetValidSkill (sn)->IsNoSelf ()) {
ch->SendText ("You can't cast this on yourself!\n\r");
return &pAbort;
}
vo = (void *) *victim;
break;
case TAR_CHAR_SELF:
if (arg [0] != '\0' && ! nifty_is_name (arg, ch->GetName ())) {
ch->SendText ("You cannot cast this spell on another.\n\r");
return &pAbort;
}
vo = (void *) ch;
break;
case TAR_OBJ_INV:
if (arg [0] == '\0') {
ch->SendText ("What should the spell be cast upon?\n\r");
return &pAbort;
}
if ((*obj = get_obj_carry (ch, arg)) == NULL) {
ch->SendText ("You are not carrying that.\n\r");
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
void do_cast (CCharacter *ch, char *argument)
{
char arg1 [MAX_INPUT_LENGTH];
char arg2 [MAX_INPUT_LENGTH];
static char staticbuf [MAX_INPUT_LENGTH];
CCharacter *victim;
CObjData *obj;
void *vo = NULL;
int mana;
int blood;
int sn;
ch_ret retcode = rNONE;
BOOL bDontWait = FALSE;
CSkill *skill = NULL;
struct timeval time_used;
switch (ch->GetSubstate ()) {
default:
// no ordering charmed mobs to cast spells
if (ch->IsNpc () && (ch->IsCharmed () || ch->IsPossessed ())) {
ch->SendText ("You can't seem to do that right now...\n\r");
return;
}
if (ch->GetInRoom ()->IsNoMagic ()) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("You failed.\n\r");
return;
}
target_name = one_argument (argument, arg1);
one_argument (target_name, arg2);
delete ranged_target_name;
ranged_target_name = str_dup (target_name);
if (arg1 [0] == '\0') {
ch->SendText ("Cast which what where?\n\r");
return;
}
// Regular mortal spell casting
if (ch->GetTrustLevel () < LEVEL_GOD) {
if ((sn = find_spell (ch, arg1, TRUE)) < 0
|| (!ch->IsNpc () && ch->GetLevel () <
SkillTable.GetClassLevel (sn, ch->GetClass ()))) {
ch->SendText ("You can't do that.\n\r");
return;
}
if ((skill=SkillTable.GetValidSkill (sn)) == NULL) {
ch->SendText ("You can't do that right now...\n\r");
return;
}
} else {
// Godly "spell builder" spell casting with debugging messages
if ((sn=SkillTable.Lookup (arg1)) < 0) {
ch->SendText ("We didn't create that yet...\n\r");
return;
}
if (sn >= MAX_SKILL) {
ch->SendText ("Hmm... that might hurt.\n\r");
return;
}
if ((skill=SkillTable.GetValidSkill (sn)) == NULL) {
ch->SendText ("Somethis is severely wrong with that one...\n\r");
return;
}
if (skill->GetType () != SKILL_SPELL) {
ch->SendText ("That isn't a spell.\n\r");
return;
}
if (! skill->GetSpellFunction ()) {
ch->SendText ("We didn't finish that one yet...\n\r");
return;
}
}
// Something else removed by Merc -Thoric
// Band-aid alert! !IS_NPC check -- Blod
if (ch->GetPosition () < skill->GetMinimumPosition () && !ch->IsNpc ()) {
switch (ch->GetPosition ()) {
default:
ch->SendText ("You can't concentrate enough.\n\r");
break;
case POS_SITTING:
ch->SendText ("You can't summon enough energy sitting down.\n\r");
break;
case POS_RESTING:
ch->SendText ("You're too relaxed to cast that spell.\n\r");
break;
case POS_FIGHTING:
case POS_DEFENSIVE:
case POS_AGGRESSIVE:
case POS_BERSERK:
if (skill->GetMinimumPosition () <= POS_EVASIVE)
ch->SendText ("This fighting style is too demanding "
"for that!\n\r");
else
ch->SendText ("You can't concentrate enough while fighting!\n\r");
break;
case POS_EVASIVE:
ch->SendText ("You can't concentrate enough while fighting!\n\r");
break;
case POS_SLEEPING:
ch->SendText ("You dream about great feats of magic.\n\r");
break;
}
return;
}
if (skill->GetSpellFunction () == spell_null) {
ch->SendText ("That's not a spell!\n\r");
return;
}
if (!skill->GetSpellFunction ()) {
ch->SendText ("You cannot cast that... yet.\n\r");
return;
}
if (! ch->IsNpc () // fixed by Thoric
&& ! ch->IsImmortal ()
&& skill->GetGuild () != CLASS_NONE
&& (! ch->GetPcData ()->GetClan ()
|| skill->GetGuild () != ch->GetPcData ()->GetClan ()->m_Class)) {
ch->SendText ("That is only available to members of a certain guild.\n\r");
return;
}
mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (),
100 / (2 + ch->GetLevel ()
- skill->GetClassLevel (ch->GetClass ())));
// Locate targets.
vo = locate_targets (ch, arg2, sn, &victim, &obj);
if (vo == &pAbort)
return;
if (! ch->IsNpc () && victim && ! victim->IsNpc ()
&& victim->CanPkill () && ! ch->CanPkill ()
&& ! in_arena (ch) && ! in_arena (victim)) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("The gods will not permit you to cast spells on "
"that character.\n\r");
return;
}
// Vampire spell casting -Thoric
blood = UMAX (1, (mana+4) / 8); // NPCs don't have PCDatas. -- Altrag
if (ch->IsVampire ()) {
if (ch->GetPcData ()->condition [COND_BLOODTHIRST] < blood) {
ch->SendText ("You don't have enough blood power.\n\r");
return;
}
}
else if (! ch->IsNpc () && ch->GetMana () < mana) {
ch->SendText ("You don't have enough mana.\n\r");
return;
}
if (skill->GetParticipants () <= 1)
break;
// multi-participant spells -Thoric
add_timer (ch, TIMER_DO_FUN, UMIN (skill->GetBeats () / 10, 3),
do_cast, 1);
act (AT_MAGIC, "You begin to chant...", ch, NULL, NULL, TO_CHAR);
act (AT_MAGIC, "$n begins to chant...", ch, NULL, NULL, TO_ROOM);
sprintf (staticbuf, "%s %s", arg2, target_name);
ch->dest_buf = str_dup (staticbuf);
ch->tempnum = sn;
return;
case SUB_TIMER_DO_ABORT:
delete ch->alloc_ptr;
ch->alloc_ptr = NULL;
if (SkillTable.IsValid ((sn = ch->tempnum))) {
if ((skill = SkillTable.GetValidSkill (sn)) == NULL) {
ch->SendText ("Something went wrong...\n\r");
bug ("do_cast: SUB_TIMER_DO_ABORT: bad sn %d", sn);
return;
}
mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (),
100 / (2 + ch->GetLevel ()
- skill->GetClassLevel (ch->GetClass ())));
blood = UMAX (1, (mana+4) / 8);
if (ch->IsVampire ())
gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 3));
else
if (ch->IsMortal ()) // so imms dont lose mana
ch->AddMana (-mana / 3);
}
set_char_color (AT_MAGIC, ch);
ch->SendText ("You stop chanting...\n\r");
// should add chance of backfire here
return;
case 1:
sn = ch->tempnum;
if ((skill = SkillTable.GetValidSkill (sn)) == NULL) {
ch->SendText ("Something went wrong...\n\r");
bug ("do_cast: substate 1: bad sn %d", sn);
return;
}
if (! ch->alloc_ptr || ! SkillTable.IsValid (sn)
|| skill->GetType () != SKILL_SPELL) {
ch->SendText ("Something cancels out the spell!\n\r");
bug ("do_cast: ch->dest_buf NULL or bad sn (%d)", sn);
return;
}
mana = ch->IsNpc () ? 0 : UMAX (skill->GetMinMana (),
100 / (2 + ch->GetLevel ()
- skill->GetClassLevel (ch->GetClass ())));
blood = UMAX (1, (mana+4) / 8);
strcpy (staticbuf, (char*) ch->alloc_ptr);
target_name = one_argument (staticbuf, arg2);
delete ch->alloc_ptr;
ch->alloc_ptr = NULL;
ch->SetSubstate (SUB_NONE);
if (skill->GetParticipants () > 1) {
int cnt = 1;
CTimerData *t;
CCharacter *tmp = ch->GetInRoom ()->first_person;
for (; tmp; tmp = tmp->GetNextInRoom ())
if (tmp != ch
&& (t = get_timerptr (tmp, TIMER_DO_FUN)) != NULL
&& t->GetCount () >= 1 && t->GetDoFun () == do_cast
&& tmp->tempnum == sn && tmp->alloc_ptr
&& !str_cmp ((char*)tmp->alloc_ptr, staticbuf))
++cnt;
if (cnt >= skill->GetParticipants ()) {
tmp = ch->GetInRoom ()->first_person;
for (; tmp; tmp = tmp->GetNextInRoom ()) {
if (tmp != ch
&& (t = get_timerptr (tmp, TIMER_DO_FUN)) != NULL
&& t->GetCount () >= 1 && t->GetDoFun () == do_cast
&& tmp->tempnum == sn && tmp->alloc_ptr
&& !str_cmp ((char*) tmp->alloc_ptr, staticbuf)) {
extract_timer (tmp, t);
act (AT_MAGIC, "Channeling your energy into $n, you "
"help cast the spell!", ch, NULL, tmp, TO_VICT);
act (AT_MAGIC, "$N channels $S energy into you!",
ch, NULL, tmp, TO_CHAR);
act (AT_MAGIC, "$N channels $S energy into $n!",
ch, NULL, tmp, TO_NOTVICT);
learn_from_success (tmp, sn);
if (ch->IsVampire ())
gain_condition (tmp, COND_BLOODTHIRST, - blood);
else
tmp->AddMana (-mana);
tmp->SetSubstate (SUB_NONE);
tmp->tempnum = -1;
delete tmp->alloc_ptr;
tmp->alloc_ptr = NULL;
}
}
bDontWait = TRUE;
ch->SendText ("You concentrate all the energy into a burst "
"of mystical words!\n\r");
vo = locate_targets (ch, arg2, sn, &victim, &obj);
if (vo == &pAbort)
return;
} else {
set_char_color (AT_MAGIC, ch);
ch->SendText ("There was not enough power for the spell to "
"succeed...\n\r");
if (ch->IsVampire ())
gain_condition (ch,
COND_BLOODTHIRST, - UMAX (1, blood / 2));
else
if (ch->IsMortal ()) // so imms dont lose mana
ch->AddMana (-mana / 2);
learn_from_failure (ch, sn);
return;
}
}
}
// uttering those magic words unless casting "ventriloquate"
if (str_cmp (skill->GetName (), "ventriloquate"))
say_spell (ch, sn);
if (! bDontWait)
WAIT_STATE (ch, skill->GetBeats ());
// Getting ready to cast... check for spell components -Thoric
if (! process_spell_components (ch, sn)) {
if (ch->IsVampire ())
gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 2));
else
if (ch->IsMortal ()) // so imms dont lose mana
ch->AddMana (-mana / 2);
learn_from_failure (ch, sn);
return;
}
if (! ch->IsNpc () && (number_percent () + skill->GetDifficulty () *
5) > ch->GetPcData ()->learned [sn]) {
// Some more interesting loss of concentration messages -Thoric
switch (number_bits (2)) {
case 0: // too busy
if (ch->GetFightData ())
ch->SendText ("This round of battle is too hectic to "
"concentrate properly.\n\r");
else
ch->SendText ("You lost your concentration.\n\r");
break;
case 1: // irritation
if (number_bits (2) == 0) {
switch (number_bits (2)) {
case 0:
ch->SendText ("A tickle in your nose prevents you from keeping your concentration.\n\r");
break;
case 1:
ch->SendText ("An itch on your leg keeps you from properly casting your spell.\n\r");
break;
case 2:
ch->SendText ("Something in your throat prevents you from uttering the proper phrase.\n\r");
break;
case 3:
ch->SendText ("A twitch in your eye disrupts your concentration for a moment.\n\r");
break;
}
}
else
ch->SendText ("Something distracts you, and you lose your concentration.\n\r");
break;
case 2: // not enough time
if (ch->GetFightData ())
ch->SendText ("There wasn't enough time this round to "
"complete the casting.\n\r");
else
ch->SendText ("You lost your concentration.\n\r");
break;
case 3:
ch->SendText ("You get a mental block mid-way through the "
"casting.\n\r");
break;
}
if (ch->IsVampire ())
gain_condition (ch, COND_BLOODTHIRST, - UMAX (1, blood / 2));
else
if (ch->IsMortal ()) // so imms dont lose mana
ch->AddMana (-mana / 2);
learn_from_failure (ch, sn);
return;
} else {
if (ch->IsVampire ())
gain_condition (ch, COND_BLOODTHIRST, - blood);
else
ch->AddMana (-mana);
// 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->GetTarget () == TAR_CHAR_DEFENSIVE
|| skill->GetTarget () == TAR_CHAR_SELF)
&& victim && victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
retcode = rSPELL_FAILED;
} else {
start_timer (&time_used);
retcode = (*skill->GetSpellFunction ()) (sn, ch->GetLevel (),
ch, vo);
end_timer (&time_used);
update_userec (&time_used, &skill->GetUseRec ());
}
}
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);
// favor adjustments
if (victim && victim != ch && ! victim->IsNpc ()
&& skill->GetTarget () == TAR_CHAR_DEFENSIVE)
adjust_favor (ch, 7, 1);
if (victim && victim != ch && ! ch->IsNpc ()
&& skill->GetTarget () == TAR_CHAR_DEFENSIVE )
adjust_favor (victim, 13, 1);
if (victim && victim != ch && ! ch->IsNpc ()
&& skill->GetTarget () == TAR_CHAR_OFFENSIVE)
adjust_favor (ch, 4, 1);
// Fixed up a weird mess here, and added double safeguards -Thoric
if (skill->GetTarget () == TAR_CHAR_OFFENSIVE && victim
&& ! char_died (victim) && victim != ch) {
CCharacter *vch, *vch_next;
for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
vch_next = vch->GetNextInRoom ();
if (vch == victim) {
if (victim->GetMaster () != ch
&& ! victim->GetFightData ())
retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
break;
}
}
}
}
// Cast spells at targets using a magical object.
ch_ret obj_cast_spell (int sn, int level, CCharacter *ch, CCharacter *victim,
CObjData *obj)
{
void *vo;
ch_ret retcode = rNONE;
int levdiff = ch->GetLevel () - level;
CSkill *skill = SkillTable.GetValidSkill (sn);
struct timeval time_used;
if (sn == -1)
return retcode;
if (! skill || ! skill->GetSpellFunction ()) {
bug ("Obj_cast_spell: bad sn %d.", sn);
return rERROR;
}
if (ch->GetInRoom ()->IsNoMagic ()) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("Nothing seems to happen...\n\r");
return rNONE;
}
if (ch->GetInRoom ()->IsSafe ()
&& skill->GetTarget () == TAR_CHAR_OFFENSIVE) {
set_char_color( AT_MAGIC, ch );
ch->SendText ("Nothing seems to happen...\n\r");
return rNONE;
}
// Basically this was added to cut down on level 5 players using level
// 40 scrolls in battle too often ;) -Thoric
if ((skill->GetTarget () == TAR_CHAR_OFFENSIVE
|| number_bits (7) == 1) // 1/128 chance if non-offensive
&& skill->GetType () != SKILL_HERB
&& ! chance (ch, 95 + levdiff)) {
switch (number_bits (2)) {
case 0: failed_casting (skill, ch, victim, NULL); break;
case 1:
act (AT_MAGIC, "The $t spell backfires!", ch,
skill->GetName (), victim, TO_CHAR);
if (victim)
act (AT_MAGIC, "$n's $t spell backfires!", ch,
skill->GetName (), victim, TO_VICT);
act (AT_MAGIC, "$n's $t spell backfires!", ch,
skill->GetName (), victim, TO_NOTVICT);
return damage (ch, ch, number_range (1, level), TYPE_UNDEFINED);
case 2: failed_casting (skill, ch, victim, NULL); break;
case 3:
act (AT_MAGIC, "The $t spell backfires!", ch, skill->GetName (),
victim, TO_CHAR);
if (victim)
act (AT_MAGIC, "$n's $t spell backfires!", ch,
skill->GetName (), victim, TO_VICT);
act (AT_MAGIC, "$n's $t spell backfires!", ch,
skill->GetName (), victim, TO_NOTVICT);
return damage (ch, ch, number_range (1, level), TYPE_UNDEFINED);
}
return rNONE;
}
target_name = "";
switch (skill->GetTarget ()) {
default:
bug ("Obj_cast_spell: bad target for sn %d.", sn);
return rERROR;
case TAR_IGNORE:
vo = NULL;
if (victim)
target_name = NCCP victim->GetName ();
else if (obj)
target_name = NCCP obj->GetName ();
break;
case TAR_CHAR_OFFENSIVE:
if (victim != ch) {
if (! victim)
victim = ch->GetFightWho ();
if (! victim || ! victim->IsNpc () && !in_arena (victim)) {
ch->SendText ("You can't do that.\n\r");
return rNONE;
}
}
if (ch != victim && is_safe (ch, victim))
return rNONE;
vo = (void*) victim;
break;
case TAR_CHAR_DEFENSIVE:
if (victim == NULL)
victim = ch;
vo = (void*) victim;
if (skill->GetType () != SKILL_HERB && victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rNONE;
}
break;
case TAR_CHAR_SELF:
vo = (void*) ch;
if (skill->GetType () != SKILL_HERB && ch->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rNONE;
}
break;
case TAR_OBJ_INV:
if (obj == NULL) {
ch->SendText ("You can't do that.\n\r");
return rNONE;
}
vo = (void*) obj;
break;
}
delete ranged_target_name;
ranged_target_name = str_dup (target_name);
start_timer (&time_used);
retcode = (*skill->GetSpellFunction ()) (sn, level, ch, vo);
end_timer (&time_used);
update_userec (&time_used, &skill->GetUseRec ());
if (retcode == rSPELL_FAILED)
retcode = rNONE;
if (retcode == rCHAR_DIED || retcode == rERROR)
return retcode;
if (char_died (ch))
return rCHAR_DIED;
if (skill->GetTarget () == TAR_CHAR_OFFENSIVE && victim != ch
&& !char_died (victim)) {
CCharacter *vch;
CCharacter *vch_next;
for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
vch_next = vch->GetNextInRoom ();
if (victim == vch && !victim->GetFightData ()
&& victim->GetMaster () != ch) {
retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
break;
}
}
}
return retcode;
}
// Spell functions.
ch_ret spell_acid_blast (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
int dam = dice (level, 6);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_blindness (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CAffectData af;
int tmp;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (skill->IsPkSensitive () && ! ch->IsNpc () && ! victim->IsNpc ())
tmp = level/2;
else
tmp = level;
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->IsBlind () || saves_spell_staff (tmp, victim)) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
af.type = sn;
af.location = APPLY_HITROLL;
af.modifier = -4;
af.duration = (short) ((1 + (level / 3)) * DUR_CONV);
af.bitvector = AFF_BLIND;
affect_to_char (victim, &af);
set_char_color (AT_MAGIC, victim);
victim->SendText ("You are blinded!\n\r");
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_burning_hands (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const short dam_each [] = {
0,
0, 0, 0, 0, 14, 17, 20, 23, 26, 29,
29, 29, 30, 30, 31, 31, 32, 32, 33, 33,
34, 34, 35, 35, 36, 36, 37, 37, 38, 38,
39, 39, 40, 40, 41, 41, 42, 42, 43, 43,
44, 44, 45, 45, 46, 46, 47, 47, 48, 48,
49, 49, 50, 50, 51, 51, 52, 52, 53, 53,
54, 54, 55, 55, 56, 56, 57, 57, 58, 58
};
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_call_lightning (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *vch;
CCharacter *vch_next;
BOOL ch_died;
ch_ret retcode = rNONE;
if (ch->IsIndoors ()) {
ch->SendText ("You must be out of doors.\n\r");
return rSPELL_FAILED;
}
if (weather_info.sky < SKY_RAINING) {
ch->SendText ("You need bad weather.\n\r");
return rSPELL_FAILED;
}
int dam = dice (level/2, 8);
set_char_color (AT_MAGIC, ch);
ch->SendText ("God's lightning strikes your foes!\n\r");
act (AT_MAGIC, "$n calls God's lightning to strike $s foes!",
ch, NULL, NULL, TO_ROOM);
ch_died = FALSE;
for (vch = first_char; vch; vch = vch_next) {
vch_next = vch->GetNext ();
if (! vch->GetInRoom ())
continue;
if (vch->GetInRoom () == ch->GetInRoom ()) {
if (! vch->IsNpc () && vch->IsWizInvis ()
&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
continue;
if (vch != ch && (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()))
retcode = damage (ch, vch,
saves_spell_staff (level, vch) ? dam/2 : dam, sn);
if (retcode == rCHAR_DIED || char_died (ch))
ch_died = TRUE;
continue;
}
if (! ch_died
&& vch->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea ()
&& vch->IsOutside ()
&& vch->IsAwake ()) {
if (number_bits (3) == 0)
vch->SendColor ("&BLightning flashes in the sky.\n\r");
}
}
if (ch_died)
return rCHAR_DIED;
else
return rNONE;
}
ch_ret spell_cause_light (int sn, int level, CCharacter *ch, void *vo)
{
return damage (ch, (CCharacter *) vo, dice (1, 8) + level / 3, sn);
}
ch_ret spell_cause_critical (int sn, int level, CCharacter *ch, void *vo)
{
return damage (ch, (CCharacter *) vo, dice (3, 8) + level - 6, sn);
}
ch_ret spell_cause_serious (int sn, int level, CCharacter *ch, void *vo)
{
return damage (ch, (CCharacter *) vo, dice (2, 8) + level / 2, sn);
}
ch_ret spell_change_sex (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CAffectData af;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
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 = (short) (10 * level * DUR_CONV);
af.location = APPLY_SEX;
do {
af.modifier = number_range (0, 2) - victim->GetSex ();
} while (af.modifier == 0);
af.bitvector = -1;
affect_to_char (victim, &af);
set_char_color (AT_MAGIC, victim);
victim->SendText ("You feel different.\n\r");
if (ch != victim)
ch->SendText ("Ok.\n\r");
return rNONE;
}
ch_ret spell_charm_person (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CAffectData af;
int chance;
char buf [MAX_STRING_LENGTH];
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == ch) {
ch->SendText ("You like yourself even better!\n\r");
return rSPELL_FAILED;
}
if (victim->IsImmuneMagic () || victim->IsImmuneCharm ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (! victim->IsNpc () && !ch->IsNpc ()) {
ch->SendText ("I don't think so...\n\r");
victim->SendText ("You feel charmed...\n\r");
return rSPELL_FAILED;
}
chance = ris_save (victim, level, RIS_CHARM);
if (victim->IsCharmed ()
|| chance == 1000
|| ch->IsCharmed ()
|| level < victim->GetLevel ()
|| circle_follow (victim, ch)
|| saves_spell_staff (chance, victim)) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->GetMaster ())
stop_follower (victim);
add_follower (victim, ch);
af.type = sn;
af.duration = (short)((number_fuzzy ((level + 1) / 3) + 1) * DUR_CONV);
af.location = 0;
af.modifier = 0;
af.bitvector = AFF_CHARM;
affect_to_char (victim, &af);
act (AT_MAGIC, "Isn't $n just so nice?", ch, NULL, victim, TO_VICT);
act (AT_MAGIC, "$N's eyes glaze over...", ch, NULL, victim, TO_ROOM);
if (ch != victim)
ch->SendText ("Ok.\n\r");
sprintf (buf, "%s has charmed %s.", ch->GetName (), victim->GetName ());
gpDoc->LogString (buf, LOG_PLAYER, ch->GetLevel ());
if (victim->IsNpc ()) {
start_hating (victim, ch);
start_hunting (victim, ch);
}
return rNONE;
}
ch_ret spell_chill_touch (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const short dam_each [] = {
0,
0, 0, 6, 7, 8, 9, 12, 13, 13, 13,
14, 14, 14, 15, 15, 15, 16, 16, 16, 17,
17, 17, 18, 18, 18, 19, 19, 19, 20, 20,
20, 21, 21, 21, 22, 22, 22, 23, 23, 23,
24, 24, 24, 25, 25, 25, 26, 26, 26, 27,
27, 28, 28, 29, 29, 30, 30, 31, 31, 32,
32, 33, 34, 34, 35, 35, 36, 37, 37, 38
};
CAffectData af;
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
if (! saves_spell_staff (level, victim)) {
af.type = sn;
af.duration = 14;
af.location = APPLY_STR;
af.modifier = -1;
af.bitvector = -1;
affect_join (victim, &af);
}
else dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_colour_spray (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const short dam_each [] = {
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
30, 35, 40, 45, 50, 55, 55, 55, 56, 57,
58, 58, 59, 60, 61, 61, 62, 63, 64, 64,
65, 66, 67, 67, 68, 69, 70, 70, 71, 72,
73, 73, 74, 75, 76, 76, 77, 78, 79, 79,
80, 80, 81, 82, 82, 83, 83, 84, 85, 85,
86, 86, 87, 88, 88, 89, 89, 90, 91, 91
};
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_control_weather (int sn, int level, CCharacter *ch, void *vo)
{
CSkill *skill = SkillTable.GetValidSkill (sn);
if (! str_cmp (target_name, "better"))
weather_info.change += dice (level / 3, 4);
else if (!str_cmp (target_name, "worse"))
weather_info.change -= dice (level / 3, 4);
else {
ch->SendText ("Do you want it to get better or worse?\n\r");
return rSPELL_FAILED;
}
successful_casting (skill, ch, NULL, NULL);
return rNONE;
}
ch_ret spell_create_food (int sn, int level, CCharacter *ch, void *vo)
{
CObjData *mushroom;
mushroom = create_object (OIdxTable.GetObj (OBJ_VNUM_MUSHROOM), 0);
mushroom->value [0] = 5 + level;
act (AT_MAGIC, "$p suddenly appears.", ch, mushroom, NULL, TO_ROOM);
act (AT_MAGIC, "$p suddenly appears.", ch, mushroom, NULL, TO_CHAR);
mushroom = obj_to_room (mushroom, ch->GetInRoom ());
return rNONE;
}
ch_ret spell_create_water (int sn, int level, CCharacter *ch, void *vo)
{
CObjData *obj = (CObjData *) vo;
int water;
if (obj->item_type != ITEM_DRINK_CON) {
ch->SendText ("It is unable to hold water.\n\r");
return rSPELL_FAILED;
}
if (obj->value [2] != LIQ_WATER && obj->value [1] != 0) {
ch->SendText ("It contains some other liquid.\n\r");
return rSPELL_FAILED;
}
water = UMIN (level * (weather_info.sky >= SKY_RAINING ? 4 : 2),
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->GetName ())) {
char buf [MAX_STRING_LENGTH];
sprintf (buf, "%s water", obj->GetName ());
obj->SetName (buf);
}
act (AT_MAGIC, "$p is filled.", ch, obj, NULL, TO_CHAR);
}
return rNONE;
}
ch_ret spell_cure_blindness (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
set_char_color (AT_MAGIC, ch);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (! is_affected (victim, gsn_blindness)) {
if (ch != victim)
ch->SendText (
"You work your cure, but it has no apparent effect.\n\r");
else
ch->SendText ("You don't seem to be blind.\n\r");
return rSPELL_FAILED;
}
affect_strip (victim, gsn_blindness);
set_char_color (AT_MAGIC, victim);
victim->SendText ("Your vision returns!\n\r");
if (ch != victim)
ch->SendText ("You work your cure, restoring vision.\n\r");
return rNONE;
}
// Next 3 are Paladin spells, until we update smaugspells -- Blodkai, 4/97
ch_ret spell_sacral_divinity (int sn, int level, CCharacter *ch, void *vo)
{
CAffectData af;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (ch->GetAlignment () < 350) {
act (AT_MAGIC, "Your prayer goes unanswered.",
ch, NULL, NULL, TO_CHAR);
return rSPELL_FAILED;
}
if (ch->IsImmuneMagic ()) {
immune_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
if (ch->HasSanctuary ())
return rSPELL_FAILED;
af.type = sn;
af.duration = level * 3;
af.location = APPLY_AFFECT;
af.modifier = 0;
af.bitvector = AFF_SANCTUARY;
affect_to_char (ch, &af);
act (AT_MAGIC,
"A shroud of glittering light slowly wraps itself about $n.",
ch, NULL, NULL, TO_ROOM);
act (AT_MAGIC,
"A shroud of glittering light slowly wraps itself around you.",
ch, NULL, NULL, TO_CHAR );
return rNONE;
}
ch_ret spell_expurgation (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter*) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (! is_affected (victim, gsn_poison))
return rSPELL_FAILED;
affect_strip (victim, gsn_poison);
act (AT_MAGIC,
"You speak an ancient prayer, begging your god for purification.",
ch, NULL, NULL, TO_CHAR);
act (AT_MAGIC,
"$n speaks an ancient prayer begging $s god for purification.",
ch, NULL, NULL, TO_ROOM);
return rNONE;
}
ch_ret spell_bethsaidean_touch (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter*) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (! is_affected (victim, gsn_blindness))
return rSPELL_FAILED;
affect_strip (victim, gsn_blindness);
set_char_color (AT_MAGIC, victim);
victim->SendText ("Your sight is restored!\n\r");
if (ch != victim) {
act (AT_MAGIC, "$n lays $s hands over your eyes and concentrates...",
ch, NULL, victim, TO_VICT);
act (AT_MAGIC, "$n lays $s hands over $N's eyes and concentrates...",
ch, NULL, victim, TO_NOTVICT);
act (AT_MAGIC,
"Laying your hands on $N's eyes, you pray to life $S blindness.",
ch, NULL, victim, TO_CHAR);
}
return rNONE;
}
ch_ret spell_cure_poison (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
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);
victim->SendText ("A warm feeling runs through your body.\n\r");
victim->SetMentalState (URANGE (-100, victim->GetMentalState (), -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)
ch->SendText (
"You work your cure, but it has no apparent effect.\n\r");
else
ch->SendText ("You don't seem to be poisoned.\n\r");
}
return rSPELL_FAILED;
}
ch_ret spell_curse (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CAffectData af;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->IsCursed () || saves_spell_staff (level, victim)) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
af.type = sn;
af.duration = (short) ((4*level) * DUR_CONV);
af.location = APPLY_HITROLL;
af.modifier = -1;
af.bitvector = 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);
victim->SendText ("You feel unclean.\n\r");
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;
}
ch_ret spell_detect_poison (int sn, int level, CCharacter *ch, void *vo)
{
CObjData *obj = (CObjData *) vo;
set_char_color (AT_MAGIC, ch);
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)
ch->SendText ("It looks undercooked.\n\r");
else if (obj->value [3] != 0)
ch->SendText ("You smell poisonous fumes.\n\r");
else
ch->SendText ("It looks very delicious.\n\r");
}
else ch->SendText ("It doesn't look poisoned.\n\r");
return rNONE;
}
ch_ret spell_dispel_evil (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (! ch->IsNpc () && ch->IsEvil ())
victim = ch;
CString acts = SysData.GetSupremeEntity ();
acts += " protects $N.";
if (victim->IsGood ()) {
act (AT_MAGIC, acts, ch, NULL, victim, TO_ROOM);
return rSPELL_FAILED;
}
if (victim->IsNeutral ()) {
act (AT_MAGIC, "$N does not seem to be affected.", ch, NULL,
victim, TO_CHAR);
return rSPELL_FAILED;
}
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
int dam = dice (level, 4);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_dispel_magic (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
int affected_by, cnt;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (! victim->GetAffectFlags ().IsEmpty () && ch == victim) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("You pass your hands around your body...\n\r");
while (! victim->m_AffectList.IsEmpty ())
affect_remove (victim, victim->m_AffectList.RemoveTail ());
victim->SetAffectFlags (RaceTable.GetAffects (victim->GetRace ()));
return rNONE;
}
else if (victim->GetAffectFlags ()
== RaceTable.GetAffects (victim->GetRace ())
|| level < victim->GetLevel ()
|| saves_spell_staff (level, victim)) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (! victim->IsNpc ()) {
ch->SendText ("You can't do that... yet.\n\r");
return rSPELL_FAILED;
}
cnt = 0;
for (;;) {
affected_by = number_range (1, MAX_AFFECTED_BY);
if (victim->IsAffected (affected_by))
break;
if (cnt++ > 30) {
failed_casting (skill, ch, victim, NULL);
return rNONE;
}
}
victim->ClrAffBit (affected_by);
successful_casting (skill, ch, victim, NULL);
return rNONE;
}
ch_ret spell_earthquake (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *vch;
CCharacter *vch_next;
BOOL ch_died;
ch_ret retcode;
CSkill *skill = SkillTable.GetValidSkill (sn);
ch_died = FALSE;
retcode = rNONE;
if (ch->GetInRoom ()->IsSafe ()) {
failed_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
act (AT_MAGIC, "The earth trembles beneath your feet!", ch, NULL, NULL, TO_CHAR);
act (AT_MAGIC, "$n makes the earth tremble and shiver.", ch, NULL, NULL, TO_ROOM);
for (vch = first_char; vch; vch = vch_next) {
vch_next = vch->GetNext ();
if (! vch->GetInRoom ())
continue;
if (vch->GetInRoom () == ch->GetInRoom ()) {
if (! vch->IsNpc () && vch->IsWizInvis ()
&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
continue;
// Added victim immune if floating or flying. This change
// suggested by anrothan@locklin.org (ashenlands mud) - RCP
BOOL bDoDamage = vch != ch
&& (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ())
&& ! vch->IsFloating () && ! vch->IsFlying ();
if (bDoDamage) {
retcode = damage (ch, vch, level + dice (2, 8), sn);
if (retcode == rCHAR_DIED || char_died (ch)) {
ch_died = TRUE;
continue;
}
if (char_died (vch))
continue;
}
}
if (!ch_died &&
vch->GetInRoom ()->GetArea () == ch->GetInRoom ()->GetArea ()) {
if (number_bits (3) == 0)
vch->SendColor ("&BThe earth trembles and shivers.\n\r");
}
}
if (ch_died)
return rCHAR_DIED;
else
return rNONE;
}
ch_ret spell_enchant_weapon (int sn, int level, CCharacter *ch, void *vo)
{
CObjData *obj = (CObjData *) vo;
CAffectData *paf;
if (obj->item_type != ITEM_WEAPON || obj->IsMagic ()
|| obj->HasAffects ()) {
act (AT_MAGIC,
"Your magic twists and winds around $p but cannot take hold.",
ch, obj, NULL, TO_CHAR);
act (AT_MAGIC,
"$n's magic twists and winds around $p but cannot take hold.",
ch, obj, NULL, TO_NOTVICT );
return rSPELL_FAILED;
}
// Bug fix here. -- Alty
separate_obj (obj);
paf = new CAffectData;
paf->type = -1;
paf->duration = -1;
paf->location = APPLY_HITROLL;
paf->modifier = level / 15;
paf->bitvector = -1;
obj->AddAffect (paf);
paf = new CAffectData;
paf->type = -1;
paf->duration = -1;
paf->location = APPLY_DAMROLL;
paf->modifier = level / 15;
paf->bitvector = -1;
obj->AddAffect (paf);
if (ch->IsGood ()) {
obj->SetAntiEvil ();
act (AT_BLUE, "$p gleams with flecks of blue energy.",
ch, obj, NULL, TO_CHAR);
act (AT_BLUE, "$p gleams with flecks of blue energy.",
ch, obj, NULL, TO_CHAR);
}
else if (ch->IsEvil ()) {
obj->SetAntiGood ();
act (AT_BLOOD, "A crimson stain flows slowly over $p.",
ch, obj, NULL, TO_CHAR);
act (AT_BLOOD, "A crimson stain flows slowly over $p.",
ch, obj, NULL, TO_ROOM);
} else {
obj->SetAntiEvil ();
obj->SetAntiGood ();
act (AT_YELLOW, "$p glows with a disquieting light.",
ch, obj, NULL, TO_CHAR);
act (AT_YELLOW, "$p glows with a disquieting light.",
ch, obj, NULL, TO_ROOM);
}
return rNONE;
}
// Drain XP, MANA, HP.
// Caster gains HP.
ch_ret spell_energy_drain (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
int chance = ris_save (victim, victim->GetLevel (), RIS_DRAIN);
if (chance == 1000 || saves_spell_staff (chance, victim)) {
failed_casting (skill, ch, victim, NULL); // SB
return rSPELL_FAILED;
}
ch->SetAlignment (UMAX (-1000, ch->GetAlignment () - 200));
int dam;
if (victim->GetLevel () <= 2)
dam = ch->GetHp () + 1;
else {
gain_exp (victim, 0 - number_range (level / 2, 3 * level / 2));
victim->SetMana (victim->GetMana () / 2);
victim->SetMove (victim->GetMove () / 2);
dam = dice (1, level);
ch->AddHp (dam);
}
if (ch->GetHp () > ch->GetMaxHp ())
ch->SetHp (ch->GetMaxHp ());
return damage (ch, victim, dam, sn);
}
ch_ret spell_fireball (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const short dam_each [] = {
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 30, 35, 40, 45, 50, 55,
60, 65, 70, 75, 80, 82, 84, 86, 88, 90,
92, 94, 96, 98, 100, 102, 104, 106, 108, 110,
112, 114, 116, 118, 120, 122, 124, 126, 128, 130,
132, 134, 136, 138, 140, 142, 144, 146, 148, 150,
152, 154, 156, 158, 160, 162, 164, 166, 168, 170
};
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_flamestrike (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
int dam = dice (6, 8);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_faerie_fire (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CAffectData af;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->IsFaerieFire ()) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
af.type = sn;
af.duration = (short) (level * DUR_CONV);
af.location = APPLY_AC;
af.modifier = 2 * level;
af.bitvector = AFF_FAERIE_FIRE;
affect_to_char (victim, &af);
act (AT_PINK, "You are surrounded by a pink outline.",
victim, NULL, NULL, TO_CHAR);
act (AT_PINK, "$n is surrounded by a pink outline.",
victim, NULL, NULL, TO_ROOM);
return rNONE;
}
ch_ret spell_faerie_fog (int sn, int level, CCharacter *ch, void *vo)
{
act (AT_MAGIC, "$n conjures a cloud of purple smoke.",
ch, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "You conjure a cloud of purple smoke.",
ch, NULL, NULL, TO_CHAR);
CCharacter *ich = ch->GetInRoom ()->first_person;
for ( ; ich; ich = ich->GetNextInRoom ()) {
if (! ich->IsNpc () && ich->IsWizInvis ())
continue;
if (ich == ch || saves_spell_staff (level, ich))
continue;
affect_strip (ich, gsn_invis);
affect_strip (ich, gsn_mass_invis);
affect_strip (ich, gsn_sneak);
ich->ClrHide ();
ich->ClrInvis ();
ich->ClrSneak ();
act (AT_MAGIC, "$n is revealed!", ich, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "You are revealed!", ich, NULL, NULL, TO_CHAR);
}
return rNONE;
}
ch_ret spell_gate (int sn, int level, CCharacter *ch, void *vo)
{
CMobIndexData *pMob = MobTable.GetMob (MOB_VNUM_VAMPIRE);
if (! pMob) {
bug ("Spell_gate: Vampire vnum %d doesn't exist.", MOB_VNUM_VAMPIRE);
return rSPELL_FAILED;
}
create_mobile (pMob)->SendToRoom (ch->GetInRoom ());
return rNONE;
}
ch_ret spell_harm (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
int dam = UMAX (20, victim->GetHp () - dice (1,4));
if (saves_spell_staff (level, victim))
dam = UMIN (50, dam / 4);
dam = UMIN (100, dam);
return damage (ch, victim, dam, sn);
}
ch_ret spell_identify (int sn, int level, CCharacter *ch, void *vo)
{
// Modified by Scryn to work on mobs/players/objs
// Made it show short descrs instead of keywords, seeing as you need
// to know the keyword anyways, we may as well make it look nice -- Alty
CObjData *obj;
CCharacter *victim;
CAffectData *paf;
CSkill *sktmp;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (target_name [0] == '\0') {
ch->SendText ("What should the spell be cast upon?\n\r");
return rSPELL_FAILED;
}
if ((obj = get_obj_carry (ch, target_name))) {
set_char_color (AT_LBLUE, ch);
ch->SendColorf ("\n\rObject '%s' is %s", obj->GetShortDescr (),
aoran (item_type_name (obj)));
if (obj->item_type != ITEM_LIGHT && obj->wear_flags-1 > 0)
ch->SendTextf (", with wear location: %s\n\r",
flag_string (obj->wear_flags-1, w_flags));
else
ch->SendTextf (".\n\r");
ch->SendTextf ("Special properties:%s%s.\n\r"
"Its weight is %d, value is %d, and level is %d.\n\r",
obj->AntiClassNames (),
ExtraBitName (obj->m_ExtraFlags),
obj->weight, obj->cost, obj->level);
set_char_color (AT_MAGIC, ch);
switch (obj->item_type) {
case ITEM_CONTAINER:
ch->SendColorf ("%s appears to be %s.\n\r",
capitalize (obj->GetShortDescr ()),
obj->value [0] < 76 ? "of a small capacity" :
obj->value [0] < 150 ? "of a small to medium capacity" :
obj->value [0] < 300 ? "of a medium capacity" :
obj->value [0] < 550 ? "of a medium to large capacity" :
obj->value [0] < 751 ? "of a large capacity" :
"of a giant capacity");
break;
case ITEM_PILL:
case ITEM_SCROLL:
case ITEM_POTION:
ch->SendTextf ("Level %d spells of:", obj->value [0]);
if (obj->value [1] >= 0
&& (sktmp=SkillTable.GetValidSkill (obj->value [1])))
ch->SendTextf (" '%s'", sktmp->GetName ());
if (obj->value [2] >= 0
&& (sktmp=SkillTable.GetValidSkill (obj->value [2])))
ch->SendTextf (" '%s'", sktmp->GetName ());
if (obj->value [3] >= 0
&& (sktmp=SkillTable.GetValidSkill (obj->value [3])))
ch->SendTextf (" '%s'", sktmp->GetName ());
ch->SendText (".\n\r");
break;
case ITEM_SALVE:
ch->SendTextf ("Has %d(%d) applications of level %d",
obj->value [1], obj->value [2], obj->value [0]);
if (obj->value [4] >= 0
&& (sktmp = SkillTable.GetValidSkill (obj->value [4])))
ch->SendTextf (" '%s'", sktmp->GetName ());
if (obj->value [5] >= 0
&& (sktmp = SkillTable.GetValidSkill (obj->value [5])))
ch->SendTextf (" '%s'", sktmp->GetName ());
ch->SendText (".\n\r");
break;
case ITEM_WAND:
case ITEM_STAFF:
ch->SendTextf ("Has %d (%d) charges of level %d",
obj->value[1], obj->value[2], obj->value[0]);
if (obj->value[3] >= 0
&& (sktmp = SkillTable.GetValidSkill (obj->value[3])))
ch->SendTextf (" '%s'", sktmp->GetName ());
ch->SendText (".\n\r");
break;
case ITEM_WEAPON:
ch->SendTextf ("Damage is %d to %d (average %d)%s\n\r",
obj->value[1], obj->value[2],
(obj->value[1] + obj->value[2]) / 2,
obj->IsPoisoned () ? ", and is poisonous." : "." );
break;
case ITEM_ARMOR:
ch->SendTextf ("Armor class is %d.\n\r", obj->value [0]);
break;
}
CAffectList &IList = obj->pIndexData->AffList;
POSITION apos = IList.GetHeadPosition ();
while (apos)
IList.GetNext (apos)->ShowAffect (ch);
CAffectList &AList = obj->AffList;
apos = AList.GetHeadPosition ();
while (apos)
AList.GetNext (apos)->ShowAffect (ch);
return rNONE;
}
else if ((victim = get_char_room (ch, target_name)) != NULL) {
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->IsNpc ()) {
set_char_color (AT_BLOOD, ch);
ch->SendTextf ("%s appears to be between level %d and %d.\n\r",
victim->GetName (),
victim->GetLevel () - (victim->GetLevel () % 5),
victim->GetLevel () - (victim->GetLevel () % 5) + 5);
} else {
set_char_color (AT_GREEN, ch);
ch->SendColorf ("%s appears to be level %d.\n\r",
victim->GetName (), victim->GetLevel ());
}
ch->SendTextf ("%s looks like %s, and follows the ways of the %s.\n\r",
victim->GetName (), aoran (victim->GetRaceName ()),
victim->GetClassName ());
if ((chance (ch, 50) && ch->GetLevel () >= victim->GetLevel () + 10)
|| ch->IsImmortal ()) {
ch->SendColorf ("%s appears to be affected by: ",
victim->GetName ());
if (victim->m_AffectList.IsEmpty ()) {
ch->SendText ("nothing.\n\r");
return rNONE;
}
POSITION pos = victim->m_AffectList.GetHeadPosition ();
while (pos) {
paf = victim->m_AffectList.GetNext (pos);
if (sktmp = SkillTable.GetValidSkill (paf->type)) {
ch->SendTextf ("%s%s.", pos ? "and " : "",
sktmp->GetName ());
}
ch->SendText ("\n\r");
}
}
}
else {
ch->SendTextf ("&gYou can't find %s!\n\r", target_name);
return rSPELL_FAILED;
}
return rNONE;
}
ch_ret spell_invis (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim;
CSkill *skill = SkillTable.GetValidSkill (sn);
// Modifications on 1/2/96 to work on player/object - Scryn
if (target_name [0] == '\0')
victim = ch;
else
victim = get_char_room (ch, target_name);
if (victim) {
CAffectData af;
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->IsInvis ()) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
act (AT_MAGIC, "$n fades out of existence.",
victim, NULL, NULL, TO_ROOM);
af.type = sn;
af.duration = (short)(((level / 4) + 12) * DUR_CONV);
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_INVISIBLE;
affect_to_char (victim, &af);
act (AT_MAGIC, "You fade out of existence.",
victim, NULL, NULL, TO_CHAR);
return rNONE;
} else {
CObjData *obj;
obj = get_obj_carry (ch, target_name);
if (obj) {
separate_obj (obj); // Fix multi-invis bug --Blod
if (obj->IsInvisible () || chance (ch, 40 + level / 10)) {
failed_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
obj->SetInvisible ();
act (AT_MAGIC, "$p fades out of existence.",
ch, obj, NULL, TO_CHAR);
return rNONE;
}
}
ch->SendTextf ("You can't find %s!\n\r", target_name);
return rSPELL_FAILED;
}
ch_ret spell_know_alignment (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
char *msg;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (! victim) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
int ap = victim->GetAlignment ();
if (ap > 700) msg = "$N has an aura as white as the driven snow.";
else if (ap > 350) msg = "$N is of excellent moral character.";
else if (ap > 100) msg = "$N is often kind and thoughtful.";
else if (ap > -100) msg = "$N doesn't have a firm moral commitment.";
else if (ap > -350) msg = "$N lies to $S friends.";
else if (ap > -700) msg = "$N would just as soon kill you as look at you.";
else msg = "I'd rather just not say anything at all about $N.";
act (AT_MAGIC, msg, ch, NULL, victim, TO_CHAR);
return rNONE;
}
ch_ret spell_lightning_bolt (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const short dam_each [] = {
0,
0, 0, 0, 0, 0, 0, 0, 0, 25, 28,
31, 34, 37, 40, 40, 41, 42, 42, 43, 44,
44, 45, 46, 46, 47, 48, 48, 49, 50, 50,
51, 52, 52, 53, 54, 54, 55, 56, 56, 57,
58, 58, 59, 60, 60, 61, 62, 62, 63, 64,
64, 65, 65, 66, 66, 67, 68, 68, 69, 69,
70, 71, 71, 72, 72, 73, 73, 74, 75, 75
};
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_locate_object (int sn, int level, CCharacter *ch, void *vo)
{
char buf [MAX_INPUT_LENGTH];
CObjData *obj;
CObjData *in_obj;
int cnt;
BOOL bFound = FALSE;
CParseinfo Inf;
while (obj = Inf.ParseAreaLists (AllAreasList)) {
if (! can_see_obj (ch, *obj)
|| !nifty_is_name (target_name, obj->GetName ()))
continue;
if (obj->IsPrototype () && ch->IsMortal ())
continue;
for (cnt=0, in_obj=obj; in_obj->in_obj && cnt < 100;
in_obj = in_obj->in_obj, ++cnt);
if (cnt >= MAX_NEST) {
bug ("spell_locate_obj: object [%d] %s is nested more than "
"%d times!", obj->pIndexData->vnum,
obj->GetShortDescr (), MAX_NEST);
continue;
}
if (in_obj->carried_by) {
if (in_obj->carried_by->IsImmortal ()
&& ! in_obj->carried_by->IsNpc ()
&& (ch->GetTrustLevel () <
in_obj->carried_by->GetPcData ()->wizinvis)
&& in_obj->carried_by->IsWizInvis ())
continue;
sprintf (buf, "%s carried by %s.\n\r",
obj_short (obj), PERS (in_obj->carried_by, ch));
} else {
sprintf (buf, "%s in %s.\n\r",
obj_short (obj), in_obj->in_room == NULL
? "somewhere" : in_obj->in_room->GetName ());
}
buf [0] = UPPER (buf [0]);
set_char_color (AT_MAGIC, ch);
ch->SendColor (buf);
bFound = TRUE;
}
if (! bFound) {
ch->SendText ("Nothing like that exists.\n\r");
return rSPELL_FAILED;
}
return rNONE;
}
ch_ret spell_magic_missile (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const short dam_each [] = {
0,
3, 3, 4, 4, 5, 6, 6, 6, 6, 6,
7, 7, 7, 7, 7, 8, 8, 8, 8, 8,
9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
11, 11, 11, 11, 11, 12, 12, 12, 12, 12,
13, 13, 13, 13, 13, 14, 14, 14, 14, 14,
15, 15, 15, 15, 15, 16, 16, 16, 16, 16,
17, 17, 17, 17, 17, 18, 18, 18, 18, 18
};
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
/* What's this? You can't save vs. magic missile! -Thoric
if (saves_spell (level, victim))
dam /= 2;
*/
return damage (ch, victim, dam, sn);
}
ch_ret spell_pass_door (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CAffectData af;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->CanPass ()) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
af.type = sn;
af.duration = (short)(number_fuzzy (level / 4) * DUR_CONV);
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_PASS_DOOR;
affect_to_char (victim, &af);
act (AT_MAGIC, "$n turns translucent.", victim, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "You turn translucent.", victim, NULL, NULL, TO_CHAR);
return rNONE;
}
ch_ret spell_poison (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CAffectData af;
BOOL first = TRUE;
int chance = ris_save (victim, level, RIS_POISON);
if (chance == 1000 || saves_poison_death (chance, victim)) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("Your magic fails to take hold.\n\r");
return rSPELL_FAILED;
}
if (victim->IsPoisoned ())
first = FALSE;
af.type = sn;
af.duration = (short)(level * DUR_CONV);
af.location = APPLY_STR;
af.modifier = -2;
af.bitvector = AFF_POISON;
affect_join (victim, &af);
set_char_color (AT_GREEN, victim);
victim->SendText ("You feel very sick.\n\r");
victim->SetMentalState (URANGE (victim->IsPkiller () ? 10 : 20,
victim->GetMentalState () + (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, CCharacter *ch, void *vo)
{
CObjData *obj;
CCharacter *victim = (CCharacter *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim->IsImmuneMagic ()) {
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);
victim->SendText ("The weight of your curse is lifted.\n\r");
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->IsCarrying ()) {
POSITION pos = victim->GetHeadCarryPos ();
while (obj = victim->GetNextCarrying (pos))
if (! obj->in_obj && (obj->IsNoRemove () || obj->IsNoDrop ())) {
if (obj->IsNoRemove ())
obj->ClrNoRemove ();
if (obj->IsNoDrop ())
obj->ClrNoDrop ();
set_char_color (AT_MAGIC, victim);
victim->SendText ("You feel a burden released.\n\r");
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, CCharacter *ch, void *vo)
{
CObjData *trap;
int retcode;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (! target_name || target_name [0] == '\0') {
ch->SendText ("Remove trap on what?\n\r");
return rSPELL_FAILED;
}
if (ch->GetInRoom ()->IsEmpty ()) {
ch->SendText ("You can't find that here.\n\r");
return rNONE;
}
BOOL bFound = FALSE;
CObjData *obj = NULL;
POSITION pos = ch->GetInRoom ()->GetHeadContentPos ();
while (obj = ch->GetInRoom ()->GetNextContent (pos))
if (can_see_obj (ch, *obj)
&& nifty_is_name (target_name, obj->GetName ())) {
bFound = TRUE;
break;
}
if (! bFound) {
ch->SendText ("You can't find that here.\n\r");
return rSPELL_FAILED;
}
if ((trap = get_trap (obj)) == NULL) {
failed_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
if (chance (ch, 70 + ch->GetWisdom ())) {
ch->SendText ("Ooops!\n\r");
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_shocking_grasp (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const int dam_each [] = {
0,
0, 0, 0, 0, 0, 0, 20, 25, 29, 33,
36, 39, 39, 39, 40, 40, 41, 41, 42, 42,
43, 43, 44, 44, 45, 45, 46, 46, 47, 47,
48, 48, 49, 49, 50, 50, 51, 51, 52, 52,
53, 53, 54, 54, 55, 55, 56, 56, 57, 57,
58, 58, 59, 59, 60, 60, 61, 61, 62, 62,
63, 63, 64, 64, 65, 65, 66, 66, 67, 67
};
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
if (saves_spell_staff (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_sleep (int sn, int level, CCharacter *ch, void *vo)
{
CAffectData af;
int retcode;
int chance;
int tmp;
CCharacter *victim;
CSkill *skill = SkillTable.GetValidSkill (sn);
if ((victim = get_char_room (ch, target_name)) == NULL) {
ch->SendText ("They aren't here.\n\r");
return rSPELL_FAILED;
}
if (! victim->IsNpc () && victim->GetFightData ()) {
ch->SendText ("You cannot sleep a fighting player.\n\r");
return rSPELL_FAILED;
}
if (is_safe (ch, victim))
return rSPELL_FAILED;
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (skill->IsPkSensitive () && !ch->IsNpc () && ! victim->IsNpc ())
tmp = level / 2;
else
tmp = level;
if (victim->IsSleeping ()
|| (chance = ris_save (victim, tmp, RIS_SLEEP)) == 1000
|| level < victim->GetLevel ()
|| (victim != ch && victim->GetInRoom ()->IsSafe ())
|| saves_spell_staff (chance, victim)) {
failed_casting (skill, ch, victim, NULL);
if (ch == victim)
return rSPELL_FAILED;
if (! victim->GetFightData ()) {
retcode = multi_hit (victim, ch, TYPE_UNDEFINED);
if (retcode == rNONE)
retcode = rSPELL_FAILED;
return retcode;
}
}
af.type = sn;
af.duration = (short)((4 + level) * DUR_CONV);
af.location = APPLY_NONE;
af.modifier = 0;
af.bitvector = AFF_SLEEP;
affect_join (victim, &af);
// Added by Narn at the request of Dominus.
if (! victim->IsNpc ()) {
sprintf (log_buf, "%s has cast sleep on %s.", ch->GetName (),
victim->GetName ());
gpDoc->LogString (log_buf, LOG_PLAYER, ch->GetLevel ());
to_channel (log_buf, CHANNEL_MONITOR, "Monitor",
UMAX (LEVEL_IMMORTAL, ch->GetLevel ()));
}
if (victim->IsAwake ()) {
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->SetPosition (POS_SLEEPING);
}
if (victim->IsNpc ())
start_hating (victim, ch);
return rNONE;
}
ch_ret spell_summon (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
char buf [MAX_STRING_LENGTH];
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
if (CRoom.IsNoAstral ()
|| VRoom.IsSafe () || VRoom.IsPrivate () || VRoom.IsSolitary ()
|| VRoom.IsNoSummon () || VRoom.IsNoRecall ()
|| victim->GetLevel () >= level + 3
|| victim->GetFightData ()
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| !in_hard_range (victim, CRoom.GetArea ())
|| (!ch->IsNpc () && !ch->CanPkill () && victim->IsPkiller ())
|| ((CRoom.GetArea ()->IsNoPkill ()) && victim->IsPkiller ())
|| (!ch->IsNpc () && !victim->IsNpc () && victim->IsNoSummon ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (CRoom.GetArea () != victim->GetInRoom ()->GetArea ()) {
if (((ch->IsNpc () != victim->IsNpc ()) && chance (ch, 30))
|| ((ch->IsNpc () == victim->IsNpc ()) && chance (ch, 60))) {
failed_casting (skill, ch, victim, NULL);
set_char_color (AT_MAGIC, victim);
victim->SendText ("You feel a strange pulling sensation...\n\r");
return rSPELL_FAILED;
}
}
if (!ch->IsNpc ()) {
act (AT_MAGIC, "You feel a wave of nausea overcome you...", ch, NULL,
NULL, TO_CHAR);
act (AT_MAGIC, "$n collapses, stunned!", ch, NULL, NULL, TO_ROOM);
ch->SetPosition (POS_STUNNED);
sprintf (buf, "%s summoned %s to room %d.", ch->GetName (),
victim->GetName (), CRoom.vnum);
gpDoc->LogString (buf, LOG_PLAYER, ch->GetLevel ());
to_channel (buf, CHANNEL_MONITOR, "Monitor", UMAX (LEVEL_IMMORTAL,
ch->GetLevel ()));
}
act (AT_MAGIC, "$n disappears suddenly.", victim, NULL, NULL, TO_ROOM);
victim->RemoveFromRoom ();
victim->SendToRoom (&CRoom);
act (AT_MAGIC, "$n arrives suddenly.", victim, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "$N has summoned you!", victim, NULL, ch, TO_CHAR);
do_look (victim, "auto");
return rNONE;
}
// Travel via the astral plains to quickly travel to desired location
// -Thoric
ch_ret spell_astral_walk (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsPrototype ()
|| CRoom.IsNoRecall ()
|| victim->GetLevel () >= level + 15
|| (victim->CanPkill () && ! ch->IsNpc () && ! ch->CanPkill ())
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| !in_hard_range (ch, victim->GetInRoom ()->GetArea ())
|| (victim->GetInRoom ()->GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (skill->ValidHitCharMsg ())
act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR);
if (skill->ValidHitVictMsg ())
act (AT_MAGIC, skill->GetHitVictMsg (), ch, NULL, victim, TO_VICT);
if (skill->ValidHitRoomMsg ())
act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, victim, TO_ROOM);
else
act (AT_MAGIC, "$n disappears in a flash of light!", ch, NULL, NULL, TO_ROOM);
ch->RemoveFromRoom ();
ch->SendToRoom (victim->GetInRoom ());
if (skill->ValidHitDest ())
act (AT_MAGIC, skill->GetHitDest (), ch, NULL, victim, TO_NOTVICT);
else
act (AT_MAGIC, "$n appears in a flash of light!",
ch, NULL, NULL, TO_ROOM);
do_look (ch, "auto");
return rNONE;
}
ch_ret spell_teleport (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = (CCharacter*) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == NULL || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->GetInRoom ()->IsNoRecall ()
|| (!ch->IsNpc () && victim->GetFightData ())
|| (victim != ch && (saves_spell_staff (level, victim)
|| saves_wands (level, victim)))) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData *pRoomIndex;
do {
pRoomIndex = RoomTable.GetRoom (number_range (0, 2097152000));
if (pRoomIndex)
if (! pRoomIndex->IsPrivate ()
&& ! pRoomIndex->IsSolitary ()
&& ! pRoomIndex->IsNoAstral ()
&& ! pRoomIndex->GetArea ()->IsNoTeleport ()
&& ! pRoomIndex->IsPrototype ()
&& ! pRoomIndex->IsDeathRoom ()
&& in_hard_range (ch, pRoomIndex->GetArea ()))
break;
} while (1);
act (AT_MAGIC, "$n slowly fades out of view.", victim, NULL, NULL, TO_ROOM);
victim->RemoveFromRoom ();
victim->SendToRoom (pRoomIndex);
act (AT_MAGIC, "$n slowly fades into view.", victim, NULL, NULL, TO_ROOM);
do_look (victim, "auto");
return rNONE;
}
ch_ret spell_ventriloquate (int sn, int level, CCharacter *ch, void *vo)
{
char buf1 [MAX_STRING_LENGTH];
char buf2 [MAX_STRING_LENGTH];
char speaker [MAX_INPUT_LENGTH];
target_name = one_argument (target_name, speaker);
sprintf (buf1, "%s says '%s'.\n\r", speaker, target_name);
sprintf (buf2, "Someone makes %s say '%s'.\n\r", speaker, target_name);
buf1 [0] = UPPER (buf1 [0]);
CCharacter *vch = ch->GetInRoom ()->first_person;
for (; vch; vch = vch->GetNextInRoom ()) {
if (! is_name (speaker, vch->GetName ())) {
set_char_color (AT_SAY, vch);
vch->SendText (saves_spell_staff (level, vch) ? buf2 : buf1);
}
}
return rNONE;
}
ch_ret spell_weaken (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter*) vo;
CAffectData af;
CSkill *skill = SkillTable.GetValidSkill (sn);
set_char_color (AT_MAGIC, ch);
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (is_affected (victim, sn) || saves_wands (level, victim)) {
ch->SendText ("Your magic fails to take hold.\n\r");
return rSPELL_FAILED;
}
af.type = sn;
af.duration = (short)(level / 2 * DUR_CONV);
af.location = APPLY_STR;
af.modifier = -2;
af.bitvector = -1;
affect_to_char (victim, &af);
set_char_color (AT_MAGIC, victim);
victim->SendText ("Your muscles seem to atrophy!\n\r");
if (ch != victim) {
if ((((! victim->IsNpc ()
&& ClassTable.GetAttrPrime (victim->GetClass ()) == APPLY_STR)
|| victim->IsNpc ())
&& victim->GetCurrentStrength () < 25)
|| victim->GetCurrentStrength () < 20) {
act (AT_MAGIC,
"$N labors weakly as your spell atrophies $S muscles.",
ch, NULL, victim, TO_CHAR );
act (AT_MAGIC,
"$N labors weakly as $n's spell atrophies $S muscles.",
ch, NULL, victim, TO_NOTVICT );
} else {
act (AT_MAGIC, "You induce a mild atrophy in $N's muscles.",
ch, NULL, victim, TO_CHAR );
act (AT_MAGIC, "$n induces a mild atrophy in $N's muscles.",
ch, NULL, victim, TO_NOTVICT );
}
}
return rNONE;
}
// A spell as it should be -Thoric
ch_ret spell_word_of_recall (int sn, int level, CCharacter *ch, void *vo)
{
do_recall ((CCharacter *) vo, "");
return rNONE;
}
// NPC spells.
ch_ret spell_acid_breath (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CObjData *obj_lose;
int dam;
int hpch;
if (chance (ch, 2 * level) && ! saves_breath (level, victim)) {
POSITION pos = victim->GetHeadCarryPos ();
while (obj_lose = victim->GetNextCarrying (pos)) {
int iWear;
if (number_bits (2) != 0)
continue;
switch (obj_lose->item_type) {
case ITEM_ARMOR:
if (obj_lose->value [0] > 0) {
separate_obj (obj_lose);
act (AT_DAMAGE, "$p is pitted and etched!",
victim, obj_lose, NULL, TO_CHAR);
if ((iWear = obj_lose->wear_loc) != WEAR_NONE)
victim->AddArmor (-apply_ac (obj_lose, iWear));
obj_lose->value [0] -= 1;
obj_lose->cost = 0;
if (iWear != WEAR_NONE)
victim->AddArmor (apply_ac (obj_lose, iWear));
}
break;
case ITEM_CONTAINER:
separate_obj (obj_lose);
act (AT_DAMAGE, "$p fumes and dissolves!",
victim, obj_lose, NULL, TO_CHAR);
act (AT_OBJECT,
"The contents of $p spill out onto the ground.",
victim, obj_lose, NULL, TO_ROOM);
act (AT_OBJECT,
"The contents of $p spill out onto the ground.",
victim, obj_lose, NULL, TO_CHAR);
empty_obj (obj_lose, NULL, victim->GetInRoom ());
extract_obj (obj_lose);
break;
}
}
}
hpch = UMAX (10, ch->GetHp ());
dam = number_range (hpch/16+1, hpch/8);
if (saves_breath (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_fire_breath (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CObjData *obj_lose;
int dam;
int hpch;
if (chance (ch, 2 * level) && ! saves_breath (level, victim)) {
POSITION pos = victim->GetHeadCarryPos ();
while (obj_lose = victim->GetNextCarrying (pos)) {
char *msg;
if (number_bits (2) != 0)
continue;
switch (obj_lose->item_type) {
default: continue;
case ITEM_CONTAINER: msg = "$p ignites and burns!"; break;
case ITEM_POTION: msg = "$p bubbles and boils!"; break;
case ITEM_SCROLL: msg = "$p crackles and burns!"; break;
case ITEM_STAFF: msg = "$p smokes and chars!"; break;
case ITEM_WAND: msg = "$p sparks and sputters!"; break;
case ITEM_COOK:
case ITEM_FOOD: msg = "$p blackens and crisps!"; break;
case ITEM_PILL: msg = "$p melts and drips!"; break;
}
separate_obj (obj_lose);
act (AT_DAMAGE, msg, victim, obj_lose, NULL, TO_CHAR);
if (obj_lose->item_type == ITEM_CONTAINER) {
act (AT_OBJECT,
"The contents of $p held by $N spill onto the ground.",
victim, obj_lose, victim, TO_ROOM);
act (AT_OBJECT, "The contents of $p spill out onto the ground.",
victim, obj_lose, NULL, TO_CHAR);
empty_obj (obj_lose, NULL, victim->GetInRoom ());
}
extract_obj (obj_lose);
}
}
hpch = UMAX (10, ch->GetHp ());
dam = number_range (hpch/16+1, hpch/8);
if (saves_breath (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_frost_breath (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CObjData *obj_lose;
int dam;
int hpch;
if (chance (ch, 2 * level) && ! saves_breath (level, victim)) {
POSITION pos = victim->GetHeadCarryPos ();
while (obj_lose = victim->GetNextCarrying (pos)) {
char *msg;
if (number_bits (2) != 0)
continue;
switch (obj_lose->item_type) {
default:
continue;
case ITEM_CONTAINER:
case ITEM_DRINK_CON:
case ITEM_POTION:
msg = "$p freezes and shatters!";
break;
}
separate_obj (obj_lose);
act (AT_DAMAGE, msg, victim, obj_lose, NULL, TO_CHAR);
if (obj_lose->item_type == ITEM_CONTAINER) {
act (AT_OBJECT,
"The contents of $p held by $N spill onto the ground.",
victim, obj_lose, victim, TO_ROOM);
act (AT_OBJECT, "The contents of $p spill out onto the ground.",
victim, obj_lose, NULL, TO_CHAR);
empty_obj (obj_lose, NULL, victim->GetInRoom ());
}
extract_obj (obj_lose);
}
}
hpch = UMAX (10, ch->GetHp ());
dam = number_range (hpch/16+1, hpch/8);
if (saves_breath (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_gas_breath (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *vch;
CCharacter *vch_next;
int dam;
int hpch;
BOOL ch_died;
ch_died = FALSE;
if (ch->GetInRoom ()->IsSafe ()) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("You fail to breathe.\n\r");
return rNONE;
}
for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
vch_next = vch->GetNextInRoom ();
if (! vch->IsNpc () && vch->IsWizInvis ()
&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
continue;
if (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()) {
hpch = UMAX (10, ch->GetHp ());
dam = number_range (hpch/16+1, hpch/8);
if (saves_breath (level, vch))
dam /= 2;
if (damage (ch, vch, dam, sn) == rCHAR_DIED || char_died (ch))
ch_died = TRUE;
}
}
return ch_died ? rCHAR_DIED : rNONE;
}
ch_ret spell_lightning_breath (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
int hpch = UMAX (10, ch->GetHp ());
int dam = number_range (hpch/16+1, hpch/8);
if (saves_breath (level, victim))
dam /= 2;
return damage (ch, victim, dam, sn);
}
ch_ret spell_null (int sn, int level, CCharacter *ch, void *vo)
{
ch->SendText ("That's not a spell!\n\r");
return rNONE;
}
// don't remove, may look redundant, but is important
ch_ret spell_notfound (int sn, int level, CCharacter *ch, void *vo)
{
return spell_null (sn, level, ch, vo);
}
// Haus' Spell Additions
// to do: portal (like mpcreatepassage)
// enchant armour? (say -1/-2/-3 ac)
// sharpness (makes weapon of caster's level)
// repair (repairs armor)
// blood burn (offensive) * name: net book of spells *
// spirit scream (offensive) * name: net book of spells *
// something about saltpeter or brimstone
// Working on DM's transport eq suggestion - Scryn 8/13
ch_ret spell_transport (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CSkill *skill = SkillTable.GetValidSkill (sn);
char arg3 [MAX_STRING_LENGTH];
CObjData *obj;
target_name = one_argument (target_name, arg3);
CCharacter *victim = get_char_world (ch, target_name);
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsPrototype () || VRoom.IsNoRecall ()
|| victim->GetLevel () >= (level + 15)
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->GetInRoom () == &CRoom) {
ch->SendText ("They are right beside you!");
return rSPELL_FAILED;
}
if ((obj = get_obj_carry (ch, arg3)) == NULL
|| (victim->GetCarryWeight () + get_obj_weight (obj)) > can_carry_w (victim)
|| (victim->IsNpc () && victim->IsPrototype ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
separate_obj (obj); // altrag shoots, haus alley-oops!
if (obj->IsNoDrop ()) {
ch->SendText ("You can't seem to let go of it.\n\r");
return rSPELL_FAILED; // nice catch, caine
}
if (obj->IsPrototype ()
&& victim->GetTrustLevel () < LEVEL_IMMORTAL) {
ch->SendText ("That item is not for mortal hands to touch!\n\r");
return rSPELL_FAILED; // Thoric
}
act (AT_MAGIC, "$p slowly dematerializes...", ch, obj, NULL, TO_CHAR);
act (AT_MAGIC, "$p slowly dematerializes from $n's hands..", ch, obj, NULL, TO_ROOM);
obj_from_char (obj);
obj_to_char (obj, victim);
act (AT_MAGIC, "$p from $n appears in your hands!", ch, obj, victim, TO_VICT);
act (AT_MAGIC, "$p appears in $n's hands!", victim, obj, NULL, TO_ROOM);
if (SysData.IsSaveOnGive ())
save_char_obj (ch);
if (SysData.IsSaveOnReceive ())
save_char_obj (victim);
return rNONE;
}
// 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, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
// 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 (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsNoRecall () || VRoom.IsPrototype ()
|| CRoom.IsNoRecall () || CRoom.IsNoAstral ()
|| victim->GetLevel () >= level + 15
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| (!victim->IsNpc () && ch->CanPkill () != victim->CanPkill ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (&VRoom == &CRoom) {
ch->SendText ("They are right beside you!");
return rSPELL_FAILED;
}
CObjData *portalObj;
CExitData *pexit;
char buf [MAX_STRING_LENGTH];
// Check if there already is a portal in either room.
for (pexit = CRoom.first_exit; pexit; pexit = pexit->GetNext ()) {
if (pexit->IsPortal ()) {
ch->SendText ("There is already a portal in this room.\n\r");
return rSPELL_FAILED;
}
if (pexit->vdir == DIR_PORTAL) {
ch->SendText ("You may not create a portal in this room.\n\r");
return rSPELL_FAILED;
}
}
for (pexit = VRoom.first_exit; pexit; pexit = pexit->GetNext ())
if (pexit->vdir == DIR_PORTAL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
pexit = make_exit (&CRoom, &VRoom, DIR_PORTAL);
pexit->keyword = STRALLOC ("portal");
pexit->description = STRALLOC ("You gaze into the shimmering portal...\n\r");
pexit->key = -1;
pexit->SetPortal ();
pexit->SetCanEnter ();
pexit->SetHidden ();
pexit->SetCanLook ();
pexit->vnum = VRoom.vnum;
portalObj = create_object (OIdxTable.GetObj (OBJ_VNUM_PORTAL), 0);
portalObj->timer = 3;
sprintf (buf, "A portal created by %s", ch->GetName ());
portalObj->SetShortDescr (buf);
// support for new casting messages
if (! skill->ValidHitCharMsg ()) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("You utter an incantation, and a portal forms "
"in front of you!\n\r");
}
else act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, victim, TO_CHAR);
if (! skill->ValidHitRoomMsg ())
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->GetHitRoomMsg (), ch, NULL, victim, TO_ROOM);
if (! skill->ValidHitVictMsg ())
act (AT_MAGIC, "A shimmering portal forms in front of you!",
victim, NULL, NULL, TO_ROOM);
else act (AT_MAGIC, skill->GetHitVictMsg (), victim, NULL, victim, TO_ROOM);
portalObj = obj_to_room (portalObj, &CRoom);
pexit = make_exit (&VRoom, &CRoom, DIR_PORTAL);
pexit->keyword = STRALLOC ("portal");
pexit->description = STRALLOC ("You gaze into the shimmering portal...\n\r");
pexit->key = -1;
pexit->SetPortal ();
pexit->SetCanEnter ();
pexit->SetHidden ();
pexit->vnum = VRoom.vnum;
portalObj = create_object (OIdxTable.GetObj (OBJ_VNUM_PORTAL), 0);
portalObj->timer = 3;
portalObj->SetShortDescr (buf);
portalObj = obj_to_room (portalObj, &VRoom);
return rNONE;
}
ch_ret spell_farsight (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
// The spell fails if the victim isn't playing, the victim is the caster,
// the target room has private, solitary, noastral, death or proto flags,
// the caster's room is norecall, the victim is too high in level, the
// victim is a proto mob, the victim makes the saving throw or the pkill
// flag on the caster is not the same as on the victim. Got it?
if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsPrototype ()
|| CRoom.IsNoRecall ()
|| victim->GetLevel () >= level + 15
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| (!victim->IsNpc () && victim->CanPkill () && !ch->CanPkill ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
successful_casting (skill, ch, victim, NULL);
ch->RemoveFromRoom ();
ch->SendToRoom (victim->GetInRoom ());
do_look (ch, "auto");
ch->RemoveFromRoom ();
ch->SendToRoom (&CRoom);
return rNONE;
}
ch_ret spell_recharge (int sn, int level, CCharacter *ch, void *vo)
{
CObjData *obj = (CObjData *) 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);
if (damage (ch, ch, obj->level * 2, TYPE_UNDEFINED) == rCHAR_DIED
|| char_died (ch))
return rCHAR_DIED;
else
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, 50 - (ch->GetLevel () / 2))) {
ch->SendText ("Nothing happens.\n\r");
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 {
ch->SendText ("You can't recharge that!\n\r");
return rSPELL_FAILED;
}
}
// Idea from AD&D 2nd edition player's handbook (c)1989 TSR Hobbies Inc.
// -Thoric
ch_ret spell_plant_pass (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsPrototype ()
|| (VRoom.sector_type != SECT_FOREST && VRoom.sector_type != SECT_FIELD)
|| (CRoom.sector_type != SECT_FOREST && CRoom.sector_type != SECT_FIELD)
|| CRoom.IsNoRecall ()
|| victim->GetLevel () >= level + 15
|| (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| ! in_hard_range (ch, VRoom.GetArea ())
|| (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (CRoom.sector_type == SECT_FOREST)
act (AT_MAGIC, "$n melds into a nearby tree!", ch, NULL, NULL, TO_ROOM);
else
act (AT_MAGIC, "$n melds into the grass!", ch, NULL, NULL, TO_ROOM);
ch->RemoveFromRoom ();
ch->SendToRoom (victim->GetInRoom ());
if (CRoom.sector_type == SECT_FOREST)
act (AT_MAGIC, "$n appears from behind a nearby tree!", ch, NULL,
NULL, TO_ROOM);
else
act (AT_MAGIC, "$n grows up from the grass!", ch, NULL, NULL, TO_ROOM);
do_look (ch, "auto");
return rNONE;
}
// Vampire version of astral_walk -Thoric
ch_ret spell_mist_walk (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
BOOL allowday;
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (ch->IsPkiller ()
&& ch->GetPcData ()->condition[COND_BLOODTHIRST] > 22)
allowday = TRUE;
else
allowday = FALSE;
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
if ((time_info.hour < 21 && time_info.hour > 5 && !allowday)
|| VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsPrototype ()
|| CRoom.IsNoRecall ()
|| victim->GetLevel () >= level + 15
|| (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| !in_hard_range (ch, VRoom.GetArea ())
|| (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
failed_casting (skill, ch, victim, NULL);
ch->SendText ("You cannot sense your victim...");
return rSPELL_FAILED;
}
// Subtract 22 extra bp for mist walk from 0500 to 2100 SB
if (time_info.hour < 21 && time_info.hour > 5 && !ch->IsNpc ())
gain_condition (ch, COND_BLOODTHIRST, - 22);
act (AT_DGREEN, "$n dissolves into a cloud of glowing mist, then vanishes!",
ch, NULL, NULL, TO_ROOM);
ch->RemoveFromRoom ();
ch->SendToRoom (victim->GetInRoom ());
act (AT_DGREEN,
"A cloud of glowing mist engulfs you, then withdraws to unveil $n!",
ch, NULL, NULL, TO_ROOM);
do_look (ch, "auto");
return rNONE;
}
// Cleric version of astral_walk -Thoric
ch_ret spell_solar_flight (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
if ((time_info.hour > 18 || time_info.hour < 8)
|| ch->IsIndoors () || victim->IsIndoors ()
|| weather_info.sky == SKY_RAINING
|| weather_info.sky == SKY_LIGHTNING
|| VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsPrototype ()
|| CRoom.IsNoRecall ()
|| victim->GetLevel () >= level + 15
|| (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| !in_hard_range (ch, VRoom.GetArea ())
|| (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
act (AT_MAGIC, "$n disappears in a blinding flash of light!",
ch, NULL, NULL, TO_ROOM);
ch->RemoveFromRoom ();
ch->SendToRoom (victim->GetInRoom ());
act (AT_MAGIC, "$n appears in a blinding flash of light!",
ch, NULL, NULL, TO_ROOM);
do_look (ch, "auto");
return rNONE;
}
// Scryn 2/2/96
ch_ret spell_remove_invis (int sn, int level, CCharacter *ch, void *vo)
{
CObjData *obj;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (target_name [0] == '\0') {
ch->SendText ("What should the spell be cast upon?\n\r");
return rSPELL_FAILED;
}
obj = get_obj_carry (ch, target_name);
if (obj) {
if (! obj->IsInvisible ()) {
ch->SendText ("Its not invisible!\n\r");
return rSPELL_FAILED;
}
obj->ClrInvisible ();
act (AT_MAGIC, "$p becomes visible again.", ch, obj, NULL, TO_CHAR);
ch->SendText ("Ok.\n\r");
return rNONE;
} else {
CCharacter *victim = get_char_room (ch, target_name);
if (victim) {
if (! can_see (ch, victim)) {
ch->SendTextf ("You don't see %s!\n\r", target_name);
return rSPELL_FAILED;
}
if (! victim->IsInvis ()) {
ch->SendText ("They are not invisible!\n\r");
return rSPELL_FAILED;
}
if (is_safe (ch, victim)) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (victim->IsImmuneMagic ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (! victim->IsNpc ()) {
if (chance (ch, 50)
&& ch->GetLevel () + 10 < victim->GetLevel ()) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
else check_illegal_pk (ch, victim);
}
else {
if (chance (ch, 50)
&& ch->GetLevel () + 15 < victim->GetLevel ()) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
}
affect_strip (victim, gsn_invis);
affect_strip (victim, gsn_mass_invis);
victim->ClrInvis ();
successful_casting (skill, ch, victim, NULL);
return rNONE;
}
ch->SendTextf ("You can't find %s!\n\r", target_name);
return rSPELL_FAILED;
}
}
// Animate Dead: Scryn 3/2/96
// Modifications by Altrag 16/2/96
ch_ret spell_animate_dead (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *mob;
CObjData *corpse;
CObjData *obj;
CAffectData af;
char buf [MAX_STRING_LENGTH];
CSkill *skill = SkillTable.GetValidSkill (sn);
CMobIndexData *pMobIndex;
BOOL bFound = FALSE;
POSITION pos = ch->GetInRoom ()->GetHeadContentPos ();
while (corpse = ch->GetInRoom ()->GetNextContent (pos)) {
if (corpse->item_type == ITEM_CORPSE_NPC && corpse->cost != -5) {
bFound = TRUE;
break;
}
}
if (! bFound) {
ch->SendText ("You cannot find a suitable corpse here.\n\r");
return rSPELL_FAILED;
}
if (MobTable.GetMob (MOB_VNUM_ANIMATED_CORPSE) == NULL) {
bug ("Vnum 5 not found for spell_animate_dead!");
return rNONE;
}
if ((pMobIndex = MobTable.GetMob ((short) abs (corpse->cost))) == NULL) {
bug ("Can not find mob for cost of corpse, spell_animate_dead");
return rSPELL_FAILED;
}
if (! ch->IsNpc ()) {
if (ch->IsVampire ()) {
if (ch->IsMortal ()
&& ch->GetPcData ()->condition [COND_BLOODTHIRST] -
(pMobIndex->level/3) < 0) {
ch->SendText (
"You do not have enough blood power to reanimate this"
" corpse.\n\r");
return rSPELL_FAILED;
}
gain_condition (ch, COND_BLOODTHIRST, pMobIndex->level / 3);
}
else if (ch->GetMana () - (pMobIndex->level*4) < 0) {
ch->SendText ("You do not have enough mana to reanimate this "
"corpse.\n\r");
return rSPELL_FAILED;
}
else ch->AddMana (-(pMobIndex->level*4));
}
if (ch->IsImmortal ()
|| (chance (ch, 75) && pMobIndex->level - ch->GetLevel () < 10)) {
mob = create_mobile (MobTable.GetMob (MOB_VNUM_ANIMATED_CORPSE));
mob->SendToRoom (ch->GetInRoom ());
mob->SetLevel (UMIN (ch->GetLevel () / 2, pMobIndex->level));
mob->SetRace (pMobIndex->race); // should be undead
// Fix so mobs wont have 0 hps and crash mud - Scryn 2/20/96
if (! pMobIndex->hitnodice)
mob->SetMaxHp (pMobIndex->level * 8 + number_range (
pMobIndex->level * pMobIndex->level / 4,
pMobIndex->level * pMobIndex->level));
else
mob->SetMaxHp (dice (pMobIndex->hitnodice,
pMobIndex->hitsizedice) + pMobIndex->hitplus);
mob->SetMaxHp (UMAX (URANGE (mob->GetMaxHp () / 4,
(mob->GetMaxHp () * corpse->value[3]) / 100,
ch->GetLevel () * dice (20,10)), 1));
mob->SetHp (mob->GetMaxHp ());
mob->SetDamroll (ch->GetLevel () / 8);
mob->SetHitroll (ch->GetLevel () / 6);
mob->SetAlignment (ch->GetAlignment ());
act (AT_MAGIC, "$n makes $T rise from the grave!",
ch, NULL, pMobIndex->GetShortDescr (), TO_ROOM);
act (AT_MAGIC, "You make $T rise from the grave!",
ch, NULL, pMobIndex->GetShortDescr (), TO_CHAR);
sprintf (buf, "animated corpse %s", pMobIndex->GetPlayerName ());
mob->SetName (buf);
sprintf (buf, "The animated corpse of %s", pMobIndex->GetShortDescr ());
mob->SetShortDescr (buf);
sprintf (buf, "An animated corpse of %s struggles with the horror "
"of its undeath.\n\r", pMobIndex->GetShortDescr ());
mob->SetLongDescr (buf);
add_follower (mob, ch);
af.type = sn;
af.duration = (short)((number_fuzzy ((level + 1) / 4) + 1) * DUR_CONV);
af.location = 0;
af.modifier = 0;
af.bitvector = AFF_CHARM;
affect_to_char (mob, &af);
if (! corpse->IsEmpty ()) {
POSITION pos = corpse->GetHeadContentPos ();
while (obj = corpse->GetNextContent (pos)) {
obj_from_obj (obj);
obj_to_room (obj, corpse->in_room);
}
}
separate_obj (corpse);
extract_obj (corpse);
return rNONE;
} else {
failed_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
}
// Works now.. -- Altrag
ch_ret spell_possess (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim;
char buf [MAX_STRING_LENGTH];
CAffectData af;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (ch->GetDesc ()->m_pOriginal) {
ch->SendText ("You are not in your original state.\n\r");
return rSPELL_FAILED;
}
if ((victim = get_char_room (ch, target_name)) == NULL) {
ch->SendText ("They aren't here!\n\r");
return rSPELL_FAILED;
}
if (victim == ch) {
ch->SendText ("You can't possess yourself!\n\r");
return rSPELL_FAILED;
}
if (! victim->IsNpc ()) {
ch->SendText ("You can't possess another player!\n\r");
return rSPELL_FAILED;
}
if (victim->GetDesc ()) {
ch->SendTextf ("%s is already possessed.\n\r",
victim->GetShortDescr ());
return rSPELL_FAILED;
}
if (victim->IsImmuneMagic () || victim->IsCharmed ()) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
BOOL bTry = ris_save (victim, level, RIS_CHARM);
if (victim->IsPossessed () || level < (victim->GetLevel ())
|| ch->IsCharmed () || victim->GetDesc ()
|| saves_spell_staff (bTry, victim)
|| ! chance (ch, 25)) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
af.type = sn;
af.duration = 20 + (ch->GetLevel () - victim->GetLevel ()) / 2;
af.location = 0;
af.modifier = 0;
af.bitvector = AFF_POSSESS;
affect_to_char (victim, &af);
sprintf (buf, "You have possessed %s!\n\r", victim->GetShortDescr ());
ch->GetDesc ()->m_pCharacter = victim;
ch->GetDesc ()->m_pOriginal = ch;
victim->SetDesc (ch->GetDesc ());
ch->SetDesc (NULL);
ch->switched = victim;
victim->SendText (buf);
return rNONE;
}
// Ignores pickproofs, but can't unlock containers. -- Altrag 17/2/96
ch_ret spell_knock (int sn, int level, CCharacter *ch, void *vo)
{
CExitData *pexit;
CSkill *skill = SkillTable.GetValidSkill (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))
|| ! pexit->IsClosed () || ! pexit->IsLocked ()
|| pexit->IsPickProof ()) {
failed_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
pexit->ClrLocked ();
ch->SendText ("*Click*\n\r");
if (pexit->rexit && pexit->rexit->GetToRoom () == ch->GetInRoom ())
pexit->rexit->ClrLocked ();
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, CCharacter *ch, void *vo)
{
CCharacter *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)) ||
victim->GetInRoom ()->GetArea () != ch->GetInRoom ()->GetArea ()) {
ch->SendText ("They aren't here.\n\r");
return rSPELL_FAILED;
}
if (victim->GetPosition () != POS_SLEEPING) {
ch->SendText ("They aren't asleep.\n\r");
return rSPELL_FAILED;
}
if (! target_name) {
ch->SendText ("What do you want them to dream about?\n\r");
return rSPELL_FAILED;
}
set_char_color (AT_TELL, victim);
victim->SendTextf ("You have dreams about %s telling you '%s'.\n\r",
PERS (ch, victim), target_name);
successful_casting (SkillTable.GetValidSkill (sn), ch, victim, NULL);
return rNONE;
}
ch_ret spell_polymorph (int sn, int level, CCharacter *ch, void *vo)
{
int poly_vnum = -1;
CCharacter *poly_mob;
if (ch->IsNpc ()) {
ch->SendText ("Mobs can't polymorph!\n\r");
return rSPELL_FAILED;
}
if (ch->GetDesc ()->m_pOriginal) {
ch->SendText ("You are not in your original state.\n\r");
return rSPELL_FAILED;
}
if (! str_cmp (target_name, "wolf")) {
if (CanMorph (*ch, target_name))
poly_vnum = MOB_VNUM_POLY_WOLF;
}
else if (! str_cmp (target_name, "mist")) {
if (CanMorph (*ch, target_name))
poly_vnum = MOB_VNUM_POLY_MIST;
}
else if (! str_cmp (target_name, "bat")) {
if (CanMorph (*ch, target_name))
poly_vnum = MOB_VNUM_POLY_BAT;
}
else if (! str_cmp (target_name, "hawk")) {
if (CanMorph (*ch, target_name))
poly_vnum = MOB_VNUM_POLY_HAWK;
}
else if (! str_cmp (target_name, "cat")) {
if (CanMorph (*ch, target_name))
poly_vnum = MOB_VNUM_POLY_CAT;
}
else if (! str_cmp (target_name, "dove")) {
if (CanMorph (*ch, target_name))
poly_vnum = MOB_VNUM_POLY_DOVE;
}
else if (! str_cmp (target_name, "fish")) {
if (CanMorph (*ch, target_name))
poly_vnum = MOB_VNUM_POLY_FISH;
}
if (poly_vnum == -1) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("You can't polymorph into that!\n\r");
return rSPELL_FAILED;
}
poly_mob = make_poly_mob (ch, poly_vnum);
if (! poly_mob) {
bug ("Spell_polymorph: null polymob!");
return rSPELL_FAILED;
}
poly_mob->SendToRoom (ch->GetInRoom ());
ch->RemoveFromRoom ();
ch->SendToRoom (RoomTable.GetRoom (SysData.m_RoomPoly));
ch->GetDesc ()->m_pCharacter = poly_mob;
ch->GetDesc ()->m_pOriginal = ch;
poly_mob->SetDesc (ch->GetDesc ());
ch->SetDesc (NULL);
ch->switched = poly_mob;
return rNONE;
}
BOOL CanMorph (CCharacter& Ch, const char* morph)
{
// Let Immortals & NPC's morph to anything
if (Ch.IsImmortal () || Ch.IsNpc ()) return TRUE;
return ClassTable.CanMorph (Ch.GetClass (), morph);
}
CCharacter *make_poly_mob (CCharacter *ch, int vnum)
{
CCharacter *mob;
CMobIndexData *pMobIndex;
if (!ch)
{
bug ("Make_poly_mob: null ch!", 0);
return NULL;
}
if (vnum < 10 || vnum > 16)
{
bug ("Make_poly_mob: Vnum not in polymorphing mobs range", 0);
return NULL;
}
if ((pMobIndex = MobTable.GetMob (vnum)) == NULL)
{
bug ("Make_poly_mob: Can't find mob %d", vnum);
return NULL;
}
mob = create_mobile (pMobIndex);
mob->SetPolymorphed ();
return mob;
}
#ifdef XXXX // This is soo confusing that I rewrote it below - RCP
void do_revert (CCharacter *ch, char *argument)
{
CCharacter *mob;
if (! ch->IsNpc () || ! ch->IsPolymorphed ()) {
ch->SendText ("You are not polymorphed.\n\r");
return;
}
ch->ClrPolymorphed ();
ch->GetDesc ()->m_pOriginal->RemoveFromRoom ();
if (ch->GetDesc ()->m_pCharacter) {
mob = ch->GetDesc ()->m_pCharacter;
ch->GetDesc ()->m_pOriginal->SendToRoom (
ch->GetDesc ()->m_pCharacter->GetInRoom ()); // WORKS!!
ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal;
ch->GetDesc ()->m_pOriginal = NULL;
ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ());
ch->GetDesc ()->m_pCharacter->switched = NULL;
ch->SetDesc (NULL);
extract_char (mob, TRUE);
return;
}
// else {
// location = NULL;
// if (ch->GetDesc ()->m_pOriginal->GetPcData ()->clan)
// location = RoomTable.GetRoom (ch->GetDesc ()->m_pOriginal->GetPcData ()->clan->recall);
// if (!location)
// location = RoomTable.GetRoom (SysData.m_RoomTemple);
// ch->GetDesc ()->m_pOriginal->SendToRoom (location);
// }
ch->GetDesc ()->m_pCharacter = ch->GetDesc ()->m_pOriginal;
ch->GetDesc ()->m_pOriginal = NULL;
ch->GetDesc ()->m_pCharacter->SetDesc (ch->GetDesc ());
ch->GetDesc ()->m_pCharacter->switched = NULL;
ch->SetDesc (NULL);
}
#endif
void do_revert (CCharacter *ch, char *argument)
{
if (! ch->GetDesc ()) {
bug ("do_revert:Null Descriptor on poly mob");
return;
}
CDescriptor &Ds = *ch->GetDesc ();
CCharacter &Orig = *Ds.m_pOriginal;
CCharacter &Mob = *ch;
if (! Mob.IsNpc () || ! Mob.IsPolymorphed ()) {
ch->SendText ("You are not polymorphed.\n\r");
return;
}
if (Ds.m_pCharacter != ch)
bug ("do_revert:Poly Mob not in Descriptor");
Mob.ClrPolymorphed ();
Orig.RemoveFromRoom (); // remove char from poly save room
Orig.SendToRoom (Mob.GetInRoom ()); // WORKS!!
Ds.m_pCharacter = &Orig;
Ds.m_pOriginal = NULL;
Orig.SetDesc (&Ds);
Orig.switched = NULL;
Mob.SetDesc (NULL);
extract_char (&Mob, TRUE);
}
// Added spells spiral_blast, scorching surge,
// nostrum, and astral by SB for Augurer class 7/10/96
ch_ret spell_spiral_blast (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *vch;
CCharacter *vch_next;
int dam;
int hpch;
BOOL ch_died;
ch_died = FALSE;
if (ch->GetInRoom ()->IsSafe ()) {
set_char_color (AT_MAGIC, ch);
ch->SendText ("You fail to breathe.\n\r");
return rNONE;
}
for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
vch_next = vch->GetNextInRoom ();
if (! vch->IsNpc () && vch->IsWizInvis ()
&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
continue;
if (ch->IsNpc () ? ! vch->IsNpc () : vch->IsNpc ()) {
act (AT_MAGIC, "Swirling colours radiate from $n"
", encompassing $N.", ch, ch, vch, TO_ROOM);
act (AT_MAGIC, "Swirling colours radiate from you,"
" encompassing $N", ch, ch, vch , TO_CHAR);
hpch = UMAX (10, ch->GetHp ());
dam = number_range (hpch/14+1, hpch/7);
if (saves_breath (level, vch))
dam /= 2;
if (damage (ch, vch, dam, sn) == rCHAR_DIED ||
char_died (ch))
ch_died = TRUE;
}
}
return ch_died ? rCHAR_DIED : rNONE;
}
ch_ret spell_scorching_surge (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
static const short dam_each[] = {
0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 30, 35, 40, 45, 50, 55,
60, 65, 70, 75, 80, 82, 84, 86, 88, 90,
92, 94, 96, 98, 100, 102, 104, 106, 108, 110,
112, 114, 116, 118, 120, 122, 124, 126, 128, 130,
132, 134, 136, 138, 140, 142, 144, 146, 148, 150,
152, 154, 156, 158, 160, 162, 164, 166, 168, 170
};
level = UMIN (level, DIM (dam_each) - 1);
level = UMAX (0, level);
int dam = number_range (dam_each [level] / 2, dam_each [level] * 2);
if (saves_spell_staff (level, victim))
dam /= 2;
act (AT_MAGIC, "A fiery current lashes through $n's body!",
ch, NULL, NULL, TO_ROOM);
act (AT_MAGIC, "A fiery current lashes through your body!",
ch, NULL, NULL, TO_CHAR);
return damage (ch, victim, (int)(dam*1.4), sn);
}
ch_ret spell_helical_flow (int sn, int level, CCharacter *ch, void *vo)
{
ASSERT (ch);
CCharacter *victim = get_char_world (ch, target_name);
CSkill *skill = SkillTable.GetValidSkill (sn);
if (victim == NULL || victim == ch || victim->GetInRoom () == NULL) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
CRoomIndexData &CRoom = *ch->GetInRoom ();
CRoomIndexData &VRoom = *victim->GetInRoom ();
if (VRoom.IsPrivate () || VRoom.IsSolitary () || VRoom.IsNoAstral ()
|| VRoom.IsDeathRoom () || VRoom.IsPrototype ()
|| CRoom.IsNoRecall ()
|| victim->GetLevel () >= level + 15
|| (victim->CanPkill () && ! ch->IsNpc () && ! ch->IsPkiller ())
|| (victim->IsNpc () && victim->IsPrototype ())
|| (victim->IsNpc () && saves_spell_staff (level, victim))
|| !in_hard_range (ch, VRoom.GetArea ())
|| (VRoom.GetArea ()->IsNoPkill () && ch->IsPkiller ())) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
act (AT_MAGIC, "$n coils into an ascending column of colour,"
" vanishing into thin air.", ch, NULL, NULL, TO_ROOM);
ch->RemoveFromRoom ();
ch->SendToRoom (victim->GetInRoom ());
act (AT_MAGIC, "A coil of colours descends from above, "
"revealing $n as it dissipates.", ch, NULL, NULL, TO_ROOM);
do_look (ch, "auto");
return rNONE;
}
/*******************************************************
* Everything after this point is part of SMAUG SPELLS *
*******************************************************/
// saving throw check -Thoric
BOOL check_save (int sn, int level, CCharacter *ch, CCharacter *victim)
{
CSkill *skill = SkillTable.GetValidSkill (sn);
BOOL saved = FALSE;
if (skill->IsPkSensitive () && ! ch->IsNpc () && ! victim->IsNpc ())
level /= 2;
if (skill->GetSaves ())
switch (skill->GetSaves ()) {
case SS_POISON_DEATH:
saved = saves_poison_death (level, victim);
break;
case SS_ROD_WANDS:
saved = saves_wands (level, victim);
break;
case SS_PARA_PETRI:
saved = saves_para_petri (level, victim);
break;
case SS_BREATH:
saved = saves_breath (level, victim);
break;
case SS_SPELL_STAFF:
saved = saves_spell_staff (level, victim);
break;
}
return saved;
}
// Generic offensive spell damage attack -Thoric
ch_ret spell_attack (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
BOOL saved = check_save (sn, level, ch, victim);
int dam;
ch_ret retcode;
if (saved && skill->GetSaveEffect () == SE_NEGATE) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (skill->GetDiceRoll ())
dam = UMAX (0, dice_parse (ch, level, skill->GetDiceRoll ()));
else
dam = dice (1, level/2);
if (saved) {
switch (skill->GetSaveEffect ()) {
case SE_3QTRDAM: dam = (dam * 3) / 4; break;
case SE_HALFDAM: dam >>= 1; break;
case SE_QUARTERDAM: dam >>= 2; break;
case SE_EIGHTHDAM: dam >>= 3; break;
case SE_ABSORB: // victim absorbs spell for hp's
act (AT_MAGIC, "$N absorbs your $t!",
ch, skill->GetDamageMsg (), victim, TO_CHAR);
act (AT_MAGIC, "You absorb $N's $t!",
victim, skill->GetDamageMsg (), ch, TO_CHAR);
act (AT_MAGIC, "$N absorbs $n's $t!",
ch, skill->GetDamageMsg (), victim, TO_NOTVICT);
victim->SetHp (URANGE (0,
victim->GetHp () + dam, victim->GetMaxHp ()));
update_pos (victim);
if ((dam > 0 && ch->GetFightWho () == victim)
|| (dam > 0 && victim->GetFightWho () == ch)) {
int xp = ch->GetFightData () ?
ch->GetFightData ()->xp : victim->GetFightData ()->xp;
int xp_gain = (int) (xp * dam * 2) / victim->GetMaxHp ();
gain_exp (ch, 0 - xp_gain);
}
if (skill->GetAffects ())
retcode = spell_affectchar (sn, level, ch, victim);
return retcode;
case SE_REFLECT: // reflect the spell to the caster
return spell_attack (sn, level, victim, ch);
}
}
retcode = damage (ch, victim, dam, sn);
if (retcode == rNONE && skill->GetAffects ()
&& ! char_died (ch) && ! char_died (victim)
&& (! is_affected (victim, sn)
|| skill->IsAccumulative ()
|| skill->IsRecastable ()))
retcode = spell_affectchar (sn, level, ch, victim);
return retcode;
}
// Generic area attack -Thoric
ch_ret spell_area_attack (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *vch, *vch_next;
CSkill *skill = SkillTable.GetValidSkill (sn);
BOOL saved;
BOOL affects;
int dam;
BOOL ch_died = FALSE;
ch_ret retcode = rNONE;
if (ch->GetInRoom ()->IsSafe ()) {
failed_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
affects = (skill->GetAffects () ? TRUE : FALSE);
if (skill->ValidHitCharMsg ())
act (AT_MAGIC, skill->GetHitCharMsg (), ch, NULL, NULL, TO_CHAR);
if (skill->ValidHitRoomMsg ())
act (AT_MAGIC, skill->GetHitRoomMsg (), ch, NULL, NULL, TO_ROOM);
for (vch = ch->GetInRoom ()->first_person; vch; vch = vch_next) {
vch_next = vch->GetNextInRoom ();
if (!vch->IsNpc () && vch->IsWizInvis ()
&& vch->GetPcData ()->wizinvis >= LEVEL_IMMORTAL)
continue;
if (vch == ch)
continue;
if (ch->IsNpc () ? !vch->IsNpc () : vch->IsNpc ()) {
saved = check_save (sn, level, ch, vch);
if (saved && skill->GetSaveEffect () == SE_NEGATE) {
failed_casting (skill, ch, vch, NULL);
continue;
}
else if (skill->GetDiceRoll ())
dam = dice_parse (ch, level, skill->GetDiceRoll ());
else
dam = dice (1, level/2);
if (saved) {
switch (skill->GetSaveEffect ()) {
case SE_3QTRDAM: dam = (dam * 3) / 4; break;
case SE_HALFDAM: dam >>= 1; break;
case SE_QUARTERDAM: dam >>= 2; break;
case SE_EIGHTHDAM: dam >>= 3; break;
case SE_ABSORB: // victim absorbs spell for hp's
act (AT_MAGIC, "$N absorbs your $t!",
ch, skill->GetDamageMsg (), vch, TO_CHAR);
act (AT_MAGIC, "You absorb $N's $t!",
vch, skill->GetDamageMsg (), ch, TO_CHAR);
act (AT_MAGIC, "$N absorbs $n's $t!",
ch, skill->GetDamageMsg (), vch, TO_NOTVICT);
vch->SetHp (URANGE (0,
vch->GetHp () + dam, vch->GetMaxHp ()));
update_pos (vch);
if ((dam > 0 && ch->GetFightWho () == vch)
|| (dam > 0 && vch->GetFightWho () == ch)) {
int xp = ch->GetFightData () ?
ch->GetFightData ()->xp : vch->GetFightData ()->xp;
int xp_gain = (int) (xp * dam * 2) / vch->GetMaxHp ();
gain_exp (ch, 0 - xp_gain);
}
continue;
case SE_REFLECT: // reflect the spell to the caster
retcode = spell_attack (sn, level, vch, ch);
if (char_died (ch)) {
ch_died = TRUE;
break;
}
continue;
}
}
retcode = damage (ch, vch, dam, sn);
}
if (retcode == rNONE && affects && !char_died (ch) && !char_died (vch)
&& (! is_affected (vch, sn)
|| skill->IsAccumulative () || skill->IsRecastable ()))
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, CCharacter *ch, void *vo)
{
CAffectData af;
CSmaugAffect *saf;
CSkill *skill = SkillTable.GetValidSkill (sn);
CCharacter *victim = (CCharacter *) vo;
int chance;
ch_ret retcode = rNONE;
if (skill->IsRecastable ())
affect_strip (victim, sn);
for (saf = skill->GetAffects (); saf; saf = saf->GetNext ()) {
if (saf->location >= REVERSE_APPLY)
victim = ch;
else
victim = (CCharacter *) vo;
// Check if char has this bitvector already
if ((af.bitvector = saf->bitvector) >= 0
&& victim->IsAffected (af.bitvector)
&& ! skill->IsAccumulative ())
continue;
// necessary for affect_strip to work properly...
switch (af.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 (skill->IsStopOnFail ())
return retcode;
continue;
}
if (saves_poison_death (chance, victim)) {
if (skill->IsStopOnFail ())
return retcode;
continue;
}
victim->SetMentalState (URANGE (30,
victim->GetMentalState () + 2, 100));
break;
case AFF_BLIND:
af.type = gsn_blindness;
break;
case AFF_CURSE:
af.type = gsn_curse;
break;
case AFF_INVISIBLE:
af.type = gsn_invis;
break;
case AFF_SLEEP:
af.type = gsn_sleep;
chance = ris_save (victim, level, RIS_SLEEP);
if (chance == 1000) {
retcode = rVICT_IMMUNE;
if (skill->IsStopOnFail ())
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 (skill->IsStopOnFail ())
return retcode;
continue;
}
break;
case AFF_POSSESS:
af.type = gsn_possess;
break;
}
af.duration = dice_parse (ch, level, saf->duration);
af.modifier = dice_parse (ch, level, saf->modifier);
af.location = saf->location % REVERSE_APPLY;
if (af.duration == 0) {
int xp_gain;
switch (af.location) {
case APPLY_HIT:
victim->SetHp (URANGE (0, victim->GetHp () + af.modifier,
victim->GetMaxHp ()));
update_pos (victim);
if ((af.modifier > 0 && ch->GetFightData ()
&& ch->GetFightData ()->who == victim)
|| (af.modifier > 0 && victim->GetFightData ()
&& victim->GetFightData ()->who == ch)) {
int xp = ch->GetFightData () ? ch->GetFightData ()->xp
: victim->GetFightData ()->xp;
xp_gain = (int) (xp * af.modifier*2) / victim->GetMaxHp ();
gain_exp (ch, 0 - xp_gain);
}
break;
case APPLY_MANA:
victim->SetMana (URANGE (0, victim->GetMana () + af.modifier,
victim->GetMaxMana ()));
update_pos (victim);
break;
case APPLY_MOVE:
victim->SetMove (URANGE (0, victim->GetMove () + af.modifier,
victim->GetMaxMove ()));
update_pos (victim);
break;
default:
affect_modify (victim, &af, TRUE);
break;
}
}
else if (skill->IsAccumulative ())
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, CCharacter *ch, void *vo)
{
CSmaugAffect *saf;
CSkill *skill = SkillTable.GetValidSkill (sn);
CCharacter *victim = (CCharacter *) vo;
BOOL hitchar, hitroom, hitvict = FALSE;
ch_ret retcode;
if (! skill->GetAffects ()) {
bug ("spell_affect has no affects sn %d", sn);
return rNONE;
}
BOOL bGroupSp = skill->IsGroupSpell ();
BOOL bAreaSp = skill->IsAreaSpell ();
if (! bGroupSp && ! bAreaSp) {
// Can't find a victim
if (! victim) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if ((skill->GetType () != SKILL_HERB
&& victim->IsImmuneMagic ())
|| is_immune (victim, skill->GetSpellDamage ())) {
immune_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
// Spell is already on this guy
if (is_affected (victim, sn)
&& ! skill->IsAccumulative () && ! skill->IsRecastable ()) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if ((saf = skill->GetAffects ()) && !saf->GetNext ()
&& saf->location == APPLY_STRIPSN
&& !is_affected (victim, dice_parse (ch, level, saf->modifier))) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
if (check_save (sn, level, ch, victim)) {
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
} else {
if (skill->ValidHitCharMsg ()) {
if (strstr (skill->GetHitCharMsg (), "$N"))
hitchar = TRUE;
else
act (AT_MAGIC, skill->GetHitCharMsg (),
ch, NULL, NULL, TO_CHAR);
}
if (skill->ValidHitRoomMsg ()) {
if (strstr (skill->GetHitRoomMsg (), "$N"))
hitroom = TRUE;
else
act (AT_MAGIC, skill->GetHitRoomMsg (),
ch, NULL, NULL, TO_ROOM);
}
if (skill->ValidHitVictMsg ())
hitvict = TRUE;
if (victim)
victim = victim->GetInRoom ()->first_person;
else
victim = ch->GetInRoom ()->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->GetNextInRoom ()) {
if (bGroupSp || bAreaSp) {
if ((bGroupSp && ! is_same_group (victim, ch))
|| victim->IsImmuneMagic ()
|| is_immune (victim, skill->GetSpellDamage ())
|| check_save (sn, level, ch, victim)
|| (! skill->IsRecastable ()
&& is_affected (victim, sn)))
continue;
if (hitvict && ch != victim) {
act (AT_MAGIC, skill->GetHitVictMsg (),
ch, NULL, victim, TO_VICT);
if (hitroom) {
act (AT_MAGIC, skill->GetHitRoomMsg (),
ch, NULL, victim, TO_NOTVICT);
act (AT_MAGIC, skill->GetHitRoomMsg (),
ch, NULL, victim, TO_CHAR);
}
}
else if (hitroom)
act (AT_MAGIC, skill->GetHitRoomMsg (),
ch, NULL, victim, TO_ROOM);
if (ch == victim) {
if (hitvict)
act (AT_MAGIC, skill->GetHitVictMsg (),
ch, NULL, ch, TO_CHAR);
else if (hitchar)
act (AT_MAGIC, skill->GetHitCharMsg (),
ch, NULL, ch, TO_CHAR);
}
else if (hitchar)
act (AT_MAGIC, skill->GetHitCharMsg (),
ch, NULL, victim, TO_CHAR);
}
retcode = spell_affectchar (sn, level, ch, victim);
if (! bGroupSp && ! bAreaSp) {
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, CCharacter *ch, void *vo)
{
CObjData *obj = (CObjData *) vo;
CSkill *skill = SkillTable.GetValidSkill (sn);
if (! obj) {
failed_casting (skill, ch, NULL, NULL);
return rNONE;
}
switch (skill->GetSpellAction ()) {
default:
case SA_NONE:
return rNONE;
case SA_CREATE:
if (skill->IsWaterSpell ()) { // create water
if (obj->item_type != ITEM_DRINK_CON) {
ch->SendText ("It is unable to hold water.\n\r");
return rSPELL_FAILED;
}
if (obj->value [2] != LIQ_WATER && obj->value [1] != 0) {
ch->SendText ("It contains some other liquid.\n\r");
return rSPELL_FAILED;
}
int water = UMIN ((skill->GetDiceRoll () ? dice_parse (ch, level,
skill->GetDiceRoll ()) : level)
* (weather_info.sky >= SKY_RAINING ? 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->GetName ())) {
char buf [MAX_STRING_LENGTH];
sprintf (buf, "%s water", obj->GetName ());
obj->SetName (buf);
}
}
successful_casting (skill, ch, NULL, obj);
return rNONE;
}
if (skill->IsFireSpell ()) { // burn object
// return rNONE;
}
if (skill->IsPoisonSpell () // poison object
|| skill->IsDeathSpell ()) {
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 (skill->IsLifeSpell () // 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_FOOD:
case ITEM_DRINK_CON:
separate_obj (obj);
obj->value [3] = 0;
successful_casting (skill, ch, NULL, obj);
break;
}
return rNONE;
}
if (! skill->HasNoSpellClass ()) {
failed_casting (skill, ch, NULL, obj);
return rNONE;
}
switch (skill->GetSpellPower ()) { // clone object
CObjData *clone;
default:
case SP_NONE:
if (ch->GetLevel () - obj->level < 10
|| obj->cost > ch->GetLevel () * ch->GetIntelligence () *
ch->GetWisdom ()) {
failed_casting (skill, ch, NULL, obj);
return rNONE;
}
break;
case SP_MINOR:
if (ch->GetLevel () - obj->level < 20
|| obj->cost > ch->GetLevel () * ch->GetIntelligence () / 5) {
failed_casting (skill, ch, NULL, obj);
return rNONE;
}
break;
case SP_GREATER:
if (ch->GetLevel () - obj->level < 5
|| obj->cost > ch->GetLevel () * 10 * ch->GetIntelligence () *
ch->GetWisdom ()) {
failed_casting (skill, ch, NULL, obj);
return rNONE;
}
break;
case SP_MAJOR:
if (ch->GetLevel () - obj->level < 0
|| obj->cost > ch->GetLevel () * 50 * ch->GetIntelligence () *
ch->GetWisdom ()) {
failed_casting (skill, ch, NULL, obj);
return rNONE;
}
break;
clone = clone_object (obj);
clone->timer = skill->GetDiceRoll () ?
dice_parse (ch, level, skill->GetDiceRoll ()) : 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 (skill->IsPoisonSpell ()) { // 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)
ch->SendText ("It looks undercooked.\n\r");
else if (obj->value [3] != 0)
ch->SendText ("You smell poisonous fumes.\n\r");
else
ch->SendText ("It looks very delicious.\n\r");
}
else ch->SendText ("It doesn't look poisoned.\n\r");
return rNONE;
}
return rNONE;
case SA_OBSCURE: // make obj invis
if (obj->IsInvisible ()
|| chance (ch, skill->GetDiceRoll () ?
dice_parse (ch, level, skill->GetDiceRoll ()) : 20)) {
failed_casting (skill, ch, NULL, NULL);
return rSPELL_FAILED;
}
successful_casting (skill, ch, NULL, obj);
obj->SetInvisible ();
return rNONE;
case SA_CHANGE:
return rNONE;
}
return rNONE;
}
// Generic object creating spell -Thoric
ch_ret spell_create_obj (int sn, int level, CCharacter *ch, void *vo)
{
CSkill *skill = SkillTable.GetValidSkill (sn);
int lvl;
int vnum = skill->GetValue ();
CObjData *obj;
switch (skill->GetSpellPower ()) {
default:
case SP_NONE: lvl = 10; break;
case SP_MINOR: lvl = 0; break;
case SP_GREATER: lvl = level/2; break;
case SP_MAJOR: lvl = level; break;
}
// Add predetermined objects here
if (vnum == 0) {
if (! str_cmp (target_name, "sword"))
vnum = SysData.m_ObjSword;
if (! str_cmp (target_name, "shield"))
vnum = SysData.m_ObjShield;
}
CObjIndexData *oi = OIdxTable.GetObj (vnum);
if (oi == NULL || (obj = create_object (oi, lvl)) == NULL) {
failed_casting (skill, ch, NULL, NULL);
return rNONE;
}
obj->timer = skill->GetDiceRoll () ?
dice_parse (ch, level, skill->GetDiceRoll ()) : 0;
successful_casting (skill, ch, NULL, obj);
if (obj->CanWear (ITEM_TAKE))
obj_to_char (obj, ch);
else
obj_to_room (obj, ch->GetInRoom ());
return rNONE;
}
// Generic mob creating spell -Thoric
ch_ret spell_create_mob (int sn, int level, CCharacter *ch, void *vo)
{
CSkill *skill = SkillTable.GetValidSkill (sn);
int lvl;
int vnum = skill->GetValue ();
CCharacter *mob;
CAffectData af;
// set maximum mob level
switch (skill->GetSpellPower ()) {
default:
case SP_NONE: lvl = 20; break;
case SP_MINOR: lvl = 5; break;
case SP_GREATER: lvl = level/2; break;
case SP_MAJOR: lvl = level; break;
}
// 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;
}
CMobIndexData *mi = MobTable.GetMob (vnum);
if (mi == NULL || (mob = create_mobile (mi)) == NULL) {
failed_casting (skill, ch, NULL, NULL);
return rNONE;
}
mob->SetLevel (UMIN (lvl, skill->GetDiceRoll () ?
dice_parse (ch, level, skill->GetDiceRoll () ) : mob->GetLevel ()));
mob->SetArmor (interpolate (mob->GetLevel (), 100, -100));
mob->SetMaxHp (mob->GetLevel () * 8 + number_range (
mob->GetLevel () * mob->GetLevel () / 4,
mob->GetLevel () * mob->GetLevel ()));
mob->SetHp (mob->GetMaxHp ());
mob->SetGold (0);
successful_casting (skill, ch, mob, NULL);
mob->SendToRoom (ch->GetInRoom ());
add_follower (mob, ch);
af.type = sn;
af.duration = (short)((number_fuzzy ((level + 1) / 3) + 1) * DUR_CONV);
af.location = 0;
af.modifier = 0;
af.bitvector = AFF_CHARM;
affect_to_char (mob, &af);
return rNONE;
}
// Generic handler for new "SMAUG" spells -Thoric
ch_ret spell_smaug (int sn, int level, CCharacter *ch, void *vo)
{
CSkill *skill = SkillTable.GetValidSkill (sn);
switch (skill->GetTarget ()) {
case TAR_IGNORE:
// offensive area spell
if (skill->IsAreaSpell ()
&& (skill->IsDestroySpell () && skill->IsLifeSpell ())
|| (skill->IsCreateSpell () && skill->IsDeathSpell ()))
return spell_area_attack (sn, level, ch, vo);
if (skill->IsCreateSpell ()) {
if (skill->IsObjectSpell ()) // create object
return spell_create_obj (sn, level, ch, vo);
if (skill->IsLifeSpell ()) // create mob
return spell_create_mob (sn, level, ch, vo);
}
// affect a distant player
if (skill->IsDistantSpell () && skill->IsCharacterSpell ())
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 (skill->IsCharacterSpell ())
return spell_affect (sn, level, ch,
get_char_room (ch, target_name));
// 1.4add
if (skill->GetRange () > 0 && (skill->IsDestroySpell ()
&& skill->IsLifeSpell ())
|| (skill->IsCreateSpell () && skill->IsDeathSpell ()))
return ranged_attack (ch, ranged_target_name,
NULL, NULL, sn, skill->GetRange ());
// will fail, or be an area/group affect
return spell_affect (sn, level, ch, vo);
case TAR_CHAR_OFFENSIVE:
// a regular damage inflicting spell attack
if ((skill->IsDestroySpell () && skill->IsLifeSpell ())
|| (skill->IsCreateSpell () && skill->IsDeathSpell ()))
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:
// 1.4add
if (skill->IsNoFightSpell () && ch->IsFightPosition ()) {
ch->SendText ("You can't concentrate enough for that!\n\r");
return rNONE;
}
if (vo && skill->IsDestroySpell ()) {
CCharacter *victim = (CCharacter *) vo;
// cure poison
if (skill->IsPoisonSpell ()) {
if (is_affected (victim, gsn_poison)) {
affect_strip (victim, gsn_poison);
victim->SetMentalState (URANGE (-100,
victim->GetMentalState (), -10));
successful_casting (skill, ch, victim, NULL);
return rNONE;
}
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
// cure blindness
if (skill->IsIllusionSpell ()) {
if (is_affected (victim, gsn_blindness)) {
affect_strip (victim, gsn_blindness);
successful_casting (skill, ch, victim, NULL);
return rNONE;
}
failed_casting (skill, ch, victim, NULL);
return rSPELL_FAILED;
}
}
return spell_affect (sn, level, ch, vo);
case TAR_OBJ_INV:
return spell_obj_inv (sn, level, ch, vo);
}
return rNONE;
}
// Haus' new, new mage spells follow
// 4 Energy Spells
ch_ret spell_ethereal_fist (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (35, level);
int dam = (int) (1.3 * (level * number_range (1, 6))) - 31;
dam = UMAX (0,dam);
if (saves_spell_staff (level, victim))
dam /= 4;
act (AT_MAGIC, "A fist of black, otherworldly ether rams into $N, "
"leaving $M looking stunned!", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_spectral_furor (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (16, level);
int dam = (int) (1.3 * (level*number_range (1, 7))) + 7;
if (saves_spell_staff (level, victim))
dam /= 2;
act (AT_MAGIC, "The fabric of the cosmos strains in fury about $N!",
ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_hand_of_chaos (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (18, level);
int dam = (int) (1.3 * (level*number_range (1, 7))) + 9;
if (saves_spell_staff (level, victim))
dam /= 4;
act (AT_MAGIC, "$N is grasped by an incomprehensible hand of chaos!",
ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_disruption (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (14, level);
int dam = (int) (1.3 * (level*number_range (1, 6))) + 8;
if (saves_spell_staff (level, victim))
dam = 0;
act (AT_MAGIC, "A weird energy encompasses $N, causing you to "
"question $S continued existence.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_sonic_resonance (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (23, level);
int dam = (int) (1.3 * (level*number_range (1, 8)));
if (saves_spell_staff (level, victim))
dam = dam * 3/4;
act (AT_MAGIC, "A cylinder of kinetic energy enshrouds $N causing $S to"
" resonate.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
// 3 Mentalstate spells
ch_ret spell_mind_wrack (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
// decrement mentalstate by up to 50
level = UMAX (0, level);
int dam = number_range (0, 0);
if (saves_spell_staff (level, victim))
dam /= 2;
act (AT_MAGIC, "$n stares intently at $N, causing $N to seem very "
"lethargic.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_mind_wrench (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
// increment mentalstate by up to 50
level = UMAX (0, level);
int dam = number_range (0, 0);
if (saves_spell_staff (level, victim))
dam /= 2;
act (AT_MAGIC, "$n stares intently at $N, causing $N to seem very "
"hyperactive.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
// Non-offensive spell!
ch_ret spell_revive (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
// set mentalstate to mentalstate/2
level = UMAX (0, level);
int dam = number_range (0, 0);
if (saves_spell_staff (level, victim))
dam /= 2;
act (AT_MAGIC, "$n concentrates intently, and begins looking more "
"centered.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
// n Acid Spells
ch_ret spell_sulfurous_spray (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (19, level);
int dam = (int) (1.3 * (2 * level*number_range (1, 7))) + 11;
if (saves_spell_staff (level, victim))
dam /= 4;
act (AT_MAGIC, "A stinking spray of sulfurous liquid rains down on $N.",
ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_caustic_fount (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (42, level);
int dam = (int) (1.3 * (2*level*number_range (1, 6))) - 31;
dam = UMAX (0,dam);
if (saves_spell_staff (level, victim))
dam = dam * 1/2;
act (AT_MAGIC, "A fountain of caustic liquid forms below $N. The smell"
" of $S degenerating tissues is revolting! ",
ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_acetum_primus (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
int dam = (int) (1.3 * (2*level*number_range (1, 4))) + 7;
if (saves_spell_staff (level, victim))
dam = 3 * dam/4;
act (AT_MAGIC, "A cloak of primal acid enshrouds $N, sparks form as "
"it consumes all it touches. ", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
// Electrical
ch_ret spell_galvanic_whip (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (10, level);
int dam = (int) (1.3 * (level*number_range (1, 6))) + 5;
if (saves_spell_staff (level, victim))
dam /= 2;
act (AT_MAGIC, "$n conjures a whip of ionized particles, which lashes "
"ferociously at $N.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_magnetic_thrust (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (29, level);
int dam = (int) (.65 * (5*level*number_range (1, 6))) + 16;
if (saves_spell_staff (level, victim))
dam /= 3;
act (AT_MAGIC, "An unseen energy moves nearby, causing your hair to "
"stand on end!", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_quantum_spike (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
int l = 2 * level/10;
int dam = (int) (1.3 * (l * number_range (1,40))) + 145;
if (saves_spell_staff (level, victim))
dam /= 2;
act (AT_MAGIC, "$N seems to dissolve into tiny unconnected particles, "
"then is painfully reassembled.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
// Black-magicish guys
// L2 Mage Spell
ch_ret spell_black_hand (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (5, level);
int dam = (int) (1.3 * (level*number_range (1, 6))) + 3;
if (saves_poison_death (level, victim))
dam /= 4;
act (AT_MAGIC, "$n conjures a mystical hand, which swoops menacingly "
"at $N.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_black_fist (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
level = UMIN (30, level);
int dam = (int) (1.3 * (level*number_range (1, 9))) + 4;
if (saves_poison_death (level, victim))
dam /= 4;
act (AT_MAGIC, "$n conjures a mystical fist, which swoops menacingly "
"at $N.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_black_lightning (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim = (CCharacter *) vo;
level = UMAX (0, level);
int l = 2 * level/10;
int dam = (int) (1.3 * (l*number_range (1,50))) + 135;
if (saves_poison_death (level, victim))
dam /= 4;
act (AT_MAGIC, "$n conjures a mystical black thundercloud directly "
"over $N's head.", ch, NULL, victim, TO_NOTVICT);
return damage (ch, victim, dam, sn);
}
ch_ret spell_midas_touch (int sn, int level, CCharacter *ch, void *vo)
{
CCharacter *victim;
int val;
CObjData *obj = (CObjData *) vo;
separate_obj (obj); // nice, alty :)
if (obj->IsNoDrop ()) {
ch->SendText ("You can't seem to let go of it.\n\r");
return rSPELL_FAILED;
}
if (obj->IsPrototype ()
&& ch->GetTrustLevel () < LEVEL_IMMORTAL) {
ch->SendText ("That item is not for mortal hands to touch!\n\r");
return rSPELL_FAILED; // Thoric
}
if (! obj->CanWear (ITEM_TAKE)
|| (obj->item_type == ITEM_CORPSE_NPC)
|| (obj->item_type == ITEM_CORPSE_PC)) {
ch->SendText ("Your cannot seem to turn this item to gold!");
return rNONE;
}
val = obj->cost/2;
val = UMAX (0, val);
if (obj->item_type == ITEM_WEAPON) {
switch (number_bits (2)) {
case 0: victim = get_char_world (ch, "shmalnoth"); break;
case 1: victim = get_char_world (ch, "midas001"); break;
case 2: victim = get_char_world (ch, "midas003"); break;
case 3: victim = get_char_world (ch, "shmalnak"); break;
}
}
else if (obj->item_type == ITEM_ARMOR) {
switch (number_bits (3)) {
case 0: victim = get_char_world (ch, "shmalnoth"); break;
case 1: victim = get_char_world (ch, "shmalnoth"); break;
case 2: victim = get_char_world (ch, "midas001"); break;
case 3: victim = get_char_world (ch, "midas001"); break;
case 4: victim = get_char_world (ch, "midas003"); break;
case 5: victim = get_char_world (ch, "midas005"); break;
case 6: victim = get_char_world (ch, "crafter"); break;
case 7: victim = get_char_world (ch, "crafter"); break;
}
}
else if (obj->item_type == ITEM_SCROLL) {
switch( number_bits (2)) {
case 0: victim = get_char_world (ch, "tatorious"); break;
case 1: victim = get_char_world (ch, "midas002"); break;
case 2: victim = get_char_world (ch, "midas002"); break;
case 3: victim = get_char_world (ch, "midas007"); break;
}
}
else if (obj->item_type == ITEM_STAFF) {
switch (number_bits (2)) {
case 0: victim = get_char_world (ch, "tatorious"); break;
case 1: victim = get_char_world (ch, "midas007"); break;
case 2: victim = get_char_world (ch, "midas007"); break;
case 3: victim = get_char_world (ch, "midas008"); break;
}
}
else if (obj->item_type == ITEM_WAND) {
switch (number_bits (2)) {
case 0: victim = get_char_world (ch, "tatorious"); break;
case 1: victim = get_char_world (ch, "midas006"); break;
case 2: victim = get_char_world (ch, "midas007"); break;
case 3: victim = get_char_world (ch, "midas008"); break;
}
} else {
victim = get_char_world (ch, "terror");
}
if (victim == NULL) {
ch->AddGold (val);
if (obj_extracted (obj))
return rNONE;
if (cur_obj == obj->serial)
global_objcode = rOBJ_SACCED;
extract_obj (obj);
ch->SendText ("O.K.");
return rNONE;
}
if ((victim->GetCarryWeight () + get_obj_weight (obj)) > can_carry_w (victim)
|| (victim->IsNpc () && victim->IsPrototype ())) {
ch->AddGold (val);
if (obj_extracted (obj))
return rNONE;
if (cur_obj == obj->serial)
global_objcode = rOBJ_SACCED;
extract_obj (obj);
ch->SendText ("Ok\n\r");
return rNONE;
}
ch->AddGold (val);
obj_from_char (obj);
obj_to_char (obj, victim);
ch->SendText ("You transmogrify the item to gold!\n\r");
return rNONE;
}