nexus/spell.c

4135 lines
107 KiB
C

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attack.h"
#include "defs.h"
#include "flag.h"
#include "io.h"
#include "lf.h"
#include "map.h"
#include "move.h"
#include "nexus.h"
#include "objects.h"
#include "spell.h"
#include "text.h"
extern lifeform_t *player;
extern skill_t *firstskill, *lastskill;
extern int needredraw;
extern prompt_t prompt;
extern WINDOW *msgwin;
extern job_t *firstjob;
int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifeform_t *target, flag_t *cwflag) {
char username[BUFLEN];
char killername[BUFLEN];
char targetname[BUFLEN];
char buf[BUFLEN];
int power = 0,needgrab = B_FALSE;
char damstr[BUFLEN];
objecttype_t *ot;
flag_t *f;
getlfname(user, username);
real_getlfname(user,killername, B_FALSE);
// defaults
strcpy(damstr,"");
power = 0;
needgrab = B_FALSE;
if (cwflag && strlen(cwflag->text)) {
texttospellopts(cwflag->text, &power, damstr, &needgrab);
}
// get more options from ablity itself...
ot = findot(abilid);
if (ot) {
if (hasflag(ot->flags, F_NEEDSGRAB)) {
needgrab = B_TRUE;
}
}
if (needgrab) {
// target is whoever we are grabbing
f = lfhasflag(user, F_GRABBING);
if (f) {
target = findlf(NULL, f->val[0]);
} else {
if (isplayer(user)) msg("You need to grab someone first!");
return B_TRUE;
}
}
if (target) {
getlfname(target, targetname);
}
if (abilid == OT_A_SWOOP) {
cell_t *adjcell = NULL,*origcell;
char targetname[BUFLEN];
int i;
cell_t *retcell[MAXRETCELLS];
int nretcell;
int srange = 5;
flag_t *srflag;
// get max range
srflag = lfhasflag(user, F_SWOOPRANGE);
if (srflag) {
srange = srflag->val[0];
}
if (isimmobile(user)) {
if (isplayer(user)) msg("You can't move!");
return B_TRUE;
} else if (!lfhasflag(user, F_FLYING)) {
if (isplayer(user)) msg("You are not flying, therefore cannot swoop.");
return B_TRUE;
}
if (!targcell) {
if (isplayer(user)) {
sprintf(buf, "Flyby who (max range %d)?",srange);
// TODO: ask for direction
targcell = askcoords("Flyby who?", TT_MONSTER);
if (!targcell) {
msg("Cancelled.");
return TRUE;
}
} else {
return B_FALSE;
}
}
if (getcelldist(user->cell, targcell) > srange) {
if (isplayer(user)) msg("You can't swoop that far!");
return B_TRUE;
}
// make sure we have LOF to there
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there!");
return TRUE;
} else if (!haslof(user->cell, targcell, LOF_NEED, NULL)) {
if (isplayer(user)) msg("Your path there is blocked!");
return TRUE;
}
getlfname(target, targetname);
// find cell on the way...
calcbresnham(user->cell->map, user->cell->x, user->cell->y, targcell->x, targcell->y, retcell, &nretcell);
for (i = 1; i < nretcell; i++) {
if (retcell[i] == targcell) {
adjcell = retcell[i-1];
break;
}
}
if (!adjcell) {
if (isplayer(user)) {
msg("There is no space nearby for you to attack!");
}
return B_TRUE;
}
// take some time
taketime(user, getactspeed(user));
// remember orig cell
origcell = user->cell;
// teleport next to them
movelf(user, adjcell);
if (haslos(player, adjcell)) {
msg("%s swoop%s towards %s!",username,isplayer(user) ? "" : "s",targetname);
needredraw = B_TRUE;
drawlevelfor(player);
redraw();
//if (!isplayer(user)) {
more();
//}
}
// attack
attackcell(user, targcell);
// teleport back to initial pos
movelf(user, origcell);
if (haslos(player, origcell)) {
redraw();
}
} else if (abilid == OT_A_GRAB) {
char dirch;
flag_t *f;
f = lfhasflag(user, F_GRABBING);
if (f) {
if (isplayer(user)) msg("You are already holding someone!");
return B_TRUE;
}
f = lfhasflag(user, F_GRABBEDBY);
if (f) {
if (isplayer(user)) msg("You are already being held by someone!");
return B_TRUE;
}
// ask for direction
if (!targcell) {
dirch = askchar("Grab in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE);
if (dirch == '.') {
if (isplayer(user)) msg("You can't grab yourself!");
return B_TRUE;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to grab!");
return B_TRUE;
}
getlfname(target, targetname);
// victim gets a skilcheck to avoid being grabbed
if (skillcheck(target, SC_DODGE, getattr(user, A_DEX)+11, 0)) {
msg("%s evade%s %s%s grasp.", targetname, isplayer(target) ? "" : "s",
username, getpossessive(username));
} else {
addflag(user->flags, F_GRABBING, target->id, NA, NA, NULL);
addflag(target->flags, F_GRABBEDBY, user->id, NA, NA, NULL);
// damage?
if (strlen(damstr)) {
losehp(target, roll(damstr), DT_CRUSH, user, killername);
}
}
taketime(user, getactspeed(user));
} else if (abilid == OT_A_CRUSH) {
int dam = 0;
if (isimmobile(user)) {
if (isplayer(user)) msg("You cannot move!");
return B_TRUE;
}
// shouldn't be able to happen!
/*
f = lfhasflag(user, F_GRABBING);
if (!f) {
if (isplayer(user)) msg("You need to hold someone before using this ability.");
return B_TRUE;
}
*/
// announce
if (cansee(player, target)) {
msg("%s squeeze%s %s tightly!", username, isplayer(user) ? "" : "s", targetname);
}
// victim gets a skilcheck to avoid damage...
if (skillcheckvs(target, SC_STR, 0, user, SC_STR, 1)) {
if (cansee(player, target)) {
// broke free
killflagsofid(target->flags, F_GRABBEDBY);
killflagsofid(user->flags, F_GRABBING);
}
} else {
int str;
// determine damage baesd on crusher's strength
str = getattr(user, A_STR);
dam = modifybystat((str/2), user, A_STR);
// announce
if (cansee(player, target)) {
msg("%s %s being crushed!", targetname, isplayer(target) ? "are" : "is");
}
losehp(target, dam, DT_CRUSH, user, killername);
}
taketime(user, getactspeed(user));
} else if (abilid == OT_A_JUMP) {
lifeform_t *victim = NULL;
char victimname[BUFLEN];
int dodged = B_FALSE;
cell_t *origcell;
if (!targcell) {
sprintf(buf, "Jump where (max distance 2)?");
while (!targcell) {
// ask where
targcell = askcoords(buf, TT_NONE);
if (!targcell) {
return B_TRUE;
} else if (getcelldist(user->cell, targcell) > 2) {
targcell = NULL;
if (isplayer(user)) {
sprintf(buf, "You can't jump that far! Jump where (max distance 2)?");
}
} else if (!haslos(user, targcell)) {
targcell = NULL;
if (isplayer(user)) {
sprintf(buf, "You can't see where to land! Jump where (max distance 2)?");
}
}
}
}
if (isburdened(user)) {
if (isplayer(user)) {
msg("Your load is too heavy to jump!");
}
return B_TRUE;
} else if (lfhasflag(user, F_GRAVBOOSTED)) {
if (isplayer(user)) {
msg("Gravity around you is too strong to jump in!");
}
return B_TRUE;
}
// we now have a cell - go there!
taketime(user, getactspeed(user));
origcell = user->cell;
// did you land on anyone?
victim = haslf(targcell);
if (victim) {
cell_t *c;
int acc;
getlfname(victim,victimname);
// TODO: if jumper is smaller, move them out of the way instead
// move them out of the way
c = getrandomadjcell(victim->cell, WE_EMPTY, B_NOEXPAND);
// nowhere to move? move to player's cell!
if (!c) {
c = user->cell;
}
movelf(victim, c);
// see if you actually landed on them or they dodge...
acc = 100 - getevasion(victim);
if (rnd(1,100) > acc) {
// dodged!
dodged = B_TRUE;
}
}
movelf(user, targcell);
// announce
if (isplayer(user)) {
if (victim && !dodged) {
msg("You leap high in the air and land on %s!",victimname);
} else {
msg("You leap high in the air!");
}
} else if (haslos(player, origcell)) {
if (cansee(player, user)) {
if (victim && !dodged) {
msg("%s leaps high in the air and lands on %s!", username,victimname);
} else {
msg("%s leaps high in the air and lands nearby!", username);
}
} else {
msg("%s leaps high in the air!", username);
}
} else if (cansee(player, user)) {
if (victim && !dodged) {
msg("%s drops from the air and lands nearby!", username);
} else {
msg("%s drops from the air and lands on %s!", username,victimname);
}
}
if (victim) {
if (dodged) {
if (cansee(player, user)) {
msg("%s dodges out of the way!", victimname);
}
} else {
int dam;
// they take damage based on the jumper's weight
// 1hp per 15 kg
// TODO: make divisor be dependant on difference in lf size
dam = getlfweight(user, B_WITHOBS) / 15;
if (dam > 0) {
if (lfhasflag(user, F_EXTRAINFO) || lfhasflag(user, F_OMNIPOTENT)) {
msg("[%s takes %d damage]",victimname,dam);
}
sprintf(buf, "a falling %s", user->race->name);
losehp(victim, dam, DT_BASH, user, buf);
}
}
}
} else if (abilid == OT_A_SPRINT) {
int howlong;
int slev;
flag_t *f;
f = lfhasflag(user, F_SPRINTING);
if (f) {
if (f->val[0]) {
if (isplayer(user)) {
msg("You are already sprinting!");
}
} else {
if (isplayer(user)) {
msg("You are too tired to sprint right now.");
}
}
return B_TRUE;
}
if (lfhasflag(user, F_TIRED)) {
if (isplayer(user)) {
msg("You are too tired to sprint right now.");
}
return B_TRUE;
}
howlong = 2;
// +2 for each athletics skill level
slev = getskill(user, SK_ATHLETICS);
if (slev > PR_INEPT) {
howlong += (2*slev);
}
// modify for constitution
howlong = modifybystat(howlong, user, A_CON);
if (howlong <= 0) {
if (isplayer(user)) {
msg("You are too unfit to sprint.");
}
return B_TRUE;
}
addtempflag(user->flags, F_SPRINTING, B_TRUE, NA, NA, NULL, howlong);
} else if (abilid == OT_A_STINGACID) {
validateabillf(user, abilid, &target);
if (!target) return B_TRUE;
getlfname(target, targetname);
if (cansee(player, target)) {
msg("%s inject%s acid into %s!", username,
isplayer(user) ? "" : "s",
targetname);
}
losehp(target, roll(damstr), DT_ACID, user, killername);
// cause ongoing pain for 2 turns
addtempflag(target->flags, F_PAIN, DT_ACID, NA, NA, damstr, 2);
taketime(user, getactspeed(user));
} else if (abilid == OT_A_POLYREVERT) {
flag_t *f;
if (!target) target = user;
// take time
taketime(user, getactspeed(user));
f = lfhasflag(target, F_ORIGRACE);
if (!f) {
if (isplayer(user)) nothinghappens();
return B_FALSE;
}
// this call will also remove this ability...
setrace(user, f->val[0]);
} else if (abilid == OT_A_LEARN) {
skill_t *sk;
char ch = 'a';
initprompt(&prompt, "Which skill will you learn?");
ch = 'a';
for (sk = firstskill ; sk ; sk = sk->next) {
sprintf(buf, "%s (%s)",getskillname(sk->id), getskilldesc(sk->id));
addchoice(&prompt, ch++, getskillname(sk->id), buf, sk);
}
getchoicestr(&prompt, B_FALSE, B_TRUE);
sk = (skill_t *)prompt.result;
if (sk) {
int i;
ch = 'a';
initprompt(&prompt, "How much will you learn this skill?");
for (i = getskill(user, sk->id) + 1 ; i <= PR_MASTER; i++) {
sprintf(buf, "%s",getskilllevelname(i));
addchoice(&prompt, ch++, buf, buf, NULL);
}
if (prompt.nchoices <= 0) {
msg("You have already mastered this skill!");
return B_TRUE;
}
getchoice(&prompt);
while (strcmp(getskilllevelname(getskill(user, sk->id)), prompt.choice[prompt.selection].text)) {
giveskill(user, sk->id);
}
} else {
msg("Cancelled.");
}
return B_FALSE;
} else if (abilid == OT_A_LEVELUP) {
char buf[BUFLEN];
int lev;
askstring("Which xp level will you attain", '?', buf, BUFLEN, NULL);
lev = atoi(buf);
if (lev <= user->level) {
msg("Cancelled.");
} else {
while (user->level < lev) {
gainlevel(user);
}
}
return B_FALSE;
} else if (abilid == OT_A_DEBUG) {
cell_t *where;
where = askcoords("Debug who?", TT_MONSTER);
if (where) {
lifeform_t *victim;
victim = where->lf;
if (victim) {
char lfname[BUFLEN];
flag_t *f;
getlfname(victim, lfname);
f = hasflag(victim->flags, F_DEBUG);
if (f) {
killflag(f);
msg("%s - debugging is DISABLED.", lfname);
} else {
addflag(victim->flags, F_DEBUG, B_TRUE, NA, NA, NULL);
msg("%s - debugging is ON.", lfname);
}
}
}
} else if (abilid == OT_A_EMPLOY) {
cell_t *where;
where = askcoords("Assign job to who?", TT_MONSTER);
if (where && where->lf) {
char question[BUFLEN];
char lfname[BUFLEN];
char buf[BUFLEN];
job_t *j;
lifeform_t *target;
target = where->lf;
getlfname(target, lfname);
sprintf(question, "What job will you assign to %s",lfname);
askstring(question, '?', buf, BUFLEN, NULL);
j = findjobbyname(buf);
if (j) {
givejob(target, j->id);
msg("%s is now a %s.", lfname, j->name);
} else {
fizzle(user);
}
} else {
msg("There is nobody there!");
}
} else if (abilid == OT_A_ENHANCE) {
cell_t *where;
where = askcoords("Enhance stats of who?", TT_MONSTER);
if (where && where->lf) {
char ch;
enum ATTRIB att;
ch = askchar("Enhance which stat (n for none)?", "sdcin",NULL, B_TRUE);
switch (ch) {
case 's': att = A_STR; break;
case 'd': att = A_DEX; break;
case 'c': att = A_CON; break;
case 'i': att = A_IQ; break;
default: att = A_NONE; break;
}
if (att != A_NONE) {
char buf[BUFLEN];
int val;
askstring("Set stat to what", '?', buf, BUFLEN, NULL);
val = atoi(buf);
if ((val <= 0) || (val > 18)) {
msg("Invalid value.");
} else {
setattr(where->lf, att, val);
getlfname(where->lf, buf);
msg("%s%s %s set to %d.", buf, getpossessive(buf), getattrname(att), val);
}
//
}
}
} else if (abilid == OT_A_HEAVYBLOW) {
object_t *wep;
char dirch;
char targetname[BUFLEN];
flag_t *f;
wep = getweapon(user);
if (!wep) {
if (isplayer(user)) msg("You need a bashing weapon to perform a heavy blow!");
return B_TRUE;
} else if (getdamtype(wep) != DT_BASH) {
if (isplayer(user)) msg("You need a bashing weapon to perform a heavy blow!");
return B_TRUE;
}
// ask for direction
if (!targcell) {
dirch = askchar("Attack in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE);
if (dirch == '.') {
// yourself!
targcell = user->cell;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to attack!");
return B_TRUE;
}
getlfname(target, targetname);
f = addflag(wep->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL);
attackcell(user, targcell);
killflag(f);
} else if (abilid == OT_A_INSPECT) {
object_t *o;
int difficulty;
int rarity;
int mod;
enum SKILLLEVEL slev;
flag_t *f;
// only players can do this
if (!isplayer(user)) {
return B_TRUE;
}
if (!haslos(user, user->cell)) {
msg("You can't inspect anything, since you can't see!");
return B_TRUE;
}
// ask what to inspect
o = askobject(user->pack, "Inspect which object", NULL, AO_NOTKNOWN);
if (!o) {
msg("Cancelled");
return B_TRUE;
}
if (isknown(o)) {
msg("You already know what that is!");
return B_TRUE;
}
// can only inspect certain types of things
switch (o->type->obclass->id) {
case OC_SCROLL:
case OC_BOOK:
case OC_WAND:
case OC_RING:
break;
default:
msg("This doesn't seem to be a standard kind of item.");
return B_TRUE;
}
// failed this already?
if (lfhasflagval(user, F_FAILEDINSPECT, o->type->id, NA, NA, NULL)) {
msg("You won't be able to recognise this until you are more experienced.");
return B_TRUE;
}
// skillcheck...
f = hasflag(o->flags, F_RARITY);
if (f) {
rarity = f->val[1];
} else {
rarity = 0;
}
difficulty = 25 + ((100 - rarity) / 5);
switch (o->type->obclass->id) {
case OC_SCROLL:
difficulty += 2; break;
case OC_BOOK:
difficulty += 4; break;
case OC_WAND:
difficulty += 6; break;
case OC_RING:
difficulty += 8; break;
default:
break;
}
slev = getskill(user, SK_RESEARCH);
switch (slev) {
case PR_INEPT:
mod = -5;
break;
default:
mod = slev*4;
break;
}
if (skillcheck(user, SC_IQ, difficulty, mod)) {
char obname[BUFLEN];
// passed!
makeknown(o->type->id);
getobname(o, obname, o->amt);
msgnocap("This seems to be %s!", obname);
} else {
msg("You have no idea what this is.");
addflag(user->flags, F_FAILEDINSPECT, o->type->id, NA, NA, NULL);
}
taketime(user, getactspeed(user));
}
// expire ability
f = lfhasflagval(user, F_CANWILL, abilid, NA, NA, NULL);
if (f) {
if (f->val[2] != NA) {
// ie. it will go to 0 next turn.
f->val[1] = -1;
}
}
return B_FALSE;
}
// returns TRUE on error
int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_t *target, object_t *targob, cell_t *targcell, int blessed, int *seenbyplayer) {
char buf[BUFLEN];
char castername[BUFLEN];
int rv = B_FALSE;
objecttype_t *sp;
sp = findot(spellid);
getlfname(caster, castername);
// default to unseen
if (seenbyplayer) *seenbyplayer = B_FALSE;
// reverse some spells if cursed
if (hasflag(sp->flags, F_ONGOING)) {
if (lfhasflagval(caster, F_BOOSTSPELL, spellid, NA, NA, NULL)) {
// cancel it.
stopspell(caster, spellid);
return B_FALSE;
} else {
addflag(caster->flags, F_BOOSTSPELL, spellid, NA, NA, NULL);
}
}
// switch based on spell effects...
if (spellid == OT_S_ABSORBMETAL) {
int i;
float totalmass = 0;
object_t *o;
int howmuch;
// works on all metal in sight
if (isplayer(caster)) {
if (seenbyplayer) *seenbyplayer = B_FALSE;
msg("You draw power from nearby metal!");
} else if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_FALSE;
msg("%s draws power from nearby metal!",castername);
}
totalmass = 0;
// destroy WORN metal objects by the caster (not CARRIED ones)
for (o = caster->pack->first ; o ; o = o->next) {
if (isequipped(o) && ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(caster->pack);
// handle cell under the caster
for (o = caster->cell->obpile->first ; o ; o = o->next) {
// destroy metal items on the ground
if (ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(caster->cell->obpile);
for (i = 0; i < caster->nlos; i++) {
targcell = caster->los[i];
for (o = targcell->obpile->first ; o ; o = o->next) {
// destroy metal items on the ground
if (ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(targcell->obpile);
if (targcell->lf && !skillcheck(targcell->lf, SC_RESISTMAG, 20 + power, 0)) {
// destroy only WORN metal objects, not CARRIED ones
for (o = targcell->lf->pack->first ; o ; o = o->next) {
if (isequipped(o) && ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(targcell->lf->pack);
}
}
if (totalmass > 0) {
float max;
// heal 1 mp per kilo
howmuch = floor(totalmass);
// maximum based on power
max = 40 + (power*10);
if (howmuch > max) howmuch = max;
gainmp(caster, howmuch);
} else {
fizzle(caster);
}
} else if (spellid == OT_S_AIRBLAST) {
int dir;
object_t *o,*nexto;
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_TRUE, LOF_NEED, spellid, power)) return B_TRUE;
target = targcell->lf;
if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
msg("%s emit%s a powerful blast of air!", castername, isplayer(caster) ? "" : "s");
}
dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS);
// lfs
if (target) {
knockback(target, dir, power, caster);
}
// objects
for (o = targcell->obpile->first ;o ; o = nexto) {
nexto = o->next;
if (!hasflag(o->flags, F_NOPICKUP)) knockbackob(o, dir, power, power, caster);
}
} else if (spellid == OT_S_ANIMATEDEAD) {
int i;
object_t *o,*nexto;
int donesomething = B_FALSE;
// animate corpses within los of caster
for (i = 0; i < caster->nlos; i++) {
targcell = caster->los[i];
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (o->type->id == OT_CORPSE) {
lifeform_t *newlf;
newlf = makezombie(o);
if (newlf) {
if (isplayer(caster) && skillcheck(caster, A_IQ, 20, power)) {
makefriendly(newlf, PERMENANT);
}
donesomething = B_TRUE;
}
}
}
}
if (donesomething) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
} else if (spellid == OT_S_ANIMATEMETAL) {
object_t *o;
o = getweapon(caster);
if (o) {
char obname[BUFLEN];
lifeform_t *newlf;
getobname(o, obname, 1);
if (!ismetal(o->material->id)) {
if (isplayer(caster)) {
msg("Your %s is not metal, therefore unaffected.", noprefix(obname));
} else {
fizzle(caster);
}
return B_FALSE;
} else if (o->amt != 1) {
if (isplayer(caster)) {
msg("Your %s wobbles a little.", obname);
} else {
fizzle(caster);
}
return B_FALSE;
}
newlf = makeanimated(caster, o, caster->level);
if (newlf) {
if (isplayer(caster)) {
msg("Your %s leaps into the air!", noprefix(obname));
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
msg("%s%s %s leaps into the air!",castername, getpossessive(castername),
noprefix(obname));
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
fizzle(caster);
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_BLINDNESS) {
int failed = B_FALSE;
// ask for target
if (!validatespelllf(caster, &target)) return B_TRUE;
if (isblind(target)) {
fizzle(caster);
return B_FALSE;
}
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
failed = B_TRUE;
}
if (target) {
if (failed) {
if (isplayer(target)) {
msg("Your vision darkens for a moment.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
char targetname[BUFLEN];
getlfname(target, targetname);
msg("%s seems to lose its vision for a moment.", targetname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
} else {
int howlong = 7;
howlong = getspellduration(5,10,blessed) + power;
addtempflag(target->flags, F_BLIND, B_TRUE, NA, NA, NULL, howlong);
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_BLINK) {
if (!caster->nlos) {
// can't see anywhere
fizzle(caster);
} else {
int tries = 0,maxtries = 10;
// pick a random location
targcell = NULL;
while (!targcell || !cellwalkable(caster, targcell, NULL) || celldangerous(caster, targcell, B_FALSE, NULL)) {
int i;
i = rnd(0,caster->nlos-1);
targcell = caster->los[i];
tries++;
if (tries >= maxtries) {
fizzle(caster);
return B_FALSE;
}
}
teleportto(caster, targcell, B_TRUE);
}
} else if (spellid == OT_S_BURNINGWAVE) {
cell_t *retcell[MAXRETCELLS];
int nretcell;
int i;
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_TRUE, LOF_WALLSTOP, spellid, power)) return B_TRUE;
if (cansee(player, caster)) {
msg("%s shoot%s a wave of fire!",castername, isplayer(caster) ? "" : "s");
}
// create a line of fire towards the target cell
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell);
// don't set caster's cell on fire!
for (i = 1; i < nretcell; i++) {
cell_t *c;
object_t *o;
c = retcell[i];
// set on fire
o = addob(c->obpile, "medium fire");
if (o) {
setobcreatedby(o, caster);
if (c->lf) {
char buf[BUFLEN];
char damstring[BUFLEN];
char realcname[BUFLEN];
getlfname(c->lf,buf);
if (!isimmuneto(c->lf->flags, DT_FIRE)) {
msg("%s burn%s!",buf,isplayer(c->lf) ? "" : "s");
}
real_getlfname(caster, realcname, B_FALSE);
sprintf(damstring, "%s%s wave of fire", realcname, getpossessive(realcname));
losehp(c->lf, rolldie(2,10), DT_FIRE, caster, damstring);
}
}
}
if (cansee(player, caster)) {
needredraw = B_TRUE;
}
} else if (spellid == OT_S_CLOUDKILL) {
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
if (targcell->type->solid) {
fizzle(caster);
return B_FALSE;
}
addobburst(targcell, (power/3), DT_COMPASS, "cloud of gas", caster);
if (haslos(player, targcell)) {
msg("A cloud of poison gas appears!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_CHARM) {
char targetname[BUFLEN];
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, targetname);
if (getallegiance(caster) == AL_PEACEFUL) {
fizzle(caster);
return B_FALSE;
}
if (getallegiance(target) == getallegiance(caster)) {
if (isplayer(caster)) {
msg("%s is already allied with you!",targetname);
}
return B_FALSE;
}
if (skillcheck(targcell->lf, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(caster) || cansee(player, target)) {
msg("%s resists.",targetname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
int howlong;
if (isplayer(caster) || cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
howlong = getspellduration(20,30,blessed) + (power*10);
addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong);
}
} else if (spellid == OT_S_COLDBURST) {
int range = 1;
int x,y;
range = 1 + (power / 5);
// announce
if (isplayer(caster) || cansee(player, caster)) {
msg("%s emit%s a blast of icy cold!",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
animradialorth(caster->cell, range, '}', C_GREY);
for (y = caster->cell->y - range ; y <= caster->cell->y + range; y++) {
for (x = caster->cell->x - range ; x <= caster->cell->x + range; x++) {
targcell = getcellat(caster->cell->map, x,y);
if (targcell && (getcelldistorth(caster->cell, targcell) <= range)) {
if (targcell->lf && (targcell->lf != caster) && haslof(caster->cell, targcell, B_FALSE, NULL)) {
char lfname[BUFLEN];
// automatic hit
getlfname(targcell->lf, lfname);
if (haslos(caster, targcell)) {
msg("%s %s chilled!",lfname,isplayer(targcell->lf) ? "are" : "is");
}
losehp(targcell->lf, rolldie(1,8)+3, DT_COLD, caster, "a burst of coldness");
}
}
}
}
} else if (spellid == OT_S_CONECOLD) {
char lfname[BUFLEN];
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
// animation
anim(caster->cell, targcell, '}', C_GREY);
if (isplayer(caster) || cansee(player, caster)) {
msg("%s shoot%s a blast of coldness.",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
// target takes magical damage
if (skillcheck(target, SC_DODGE, 20 + (power*2), 0)) {
// miss
if (cansee(caster, target)) {
msg("A blast of coldness misses %s.",lfname);
}
} else {
// hit
if (cansee(caster, target)) {
msg("A blast of coldness ray hits %s.",lfname);
}
losehp(target, rnd(2,6), DT_COLD, caster, "a blast of coldness");
}
} else {
object_t *o, *nexto;
for (o = targcell->obpile->first; o ; o = nexto) {
nexto = o->next;
takedamage(o, 0, DT_COLD);
}
}
} else if (spellid == OT_S_CREATEMONSTER) {
lifeform_t *newlf;
job_t *forcejob = NULL;
race_t *r = NULL;
int randomjobsok = B_TRUE;
if (!targcell) {
if (power >= 5) {
// control location
if (!validatespellcell(caster, &targcell, TT_NONE, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
// make sure it's empty
if (!cellwalkable(NULL, targcell, NULL)) {
targcell = NULL;
}
} else {
// get random adjacent cell
targcell = getrandomadjcell(caster->cell, WE_EMPTY, B_ALLOWEXPAND);
}
}
if (!targcell) {
fizzle(caster);
return B_TRUE;
}
// determine type of mosnter
if (power >= 7) {
// ask what kind of monster
askstring("Create what kind of monster", '?', buf, BUFLEN, NULL);
r = findracebyname(buf);
// not found - are we asking for a job with the monster?
if (!r) {
job_t *j;
// try removing suffixes
for (j = firstjob ; j ; j = j->next) {
char *p;
char jobname[BUFLEN];
strcpy(jobname, j->name);
jobname[0] = tolower(jobname[0]);
p = strstr(buf, jobname);
if (p) {
char newbuf[BUFLEN];
strncpy(newbuf, buf, (p - buf) - 1);
r = findracebyname(newbuf);
if (r) {
forcejob = j;
break;
}
}
}
}
if (!r) {
fizzle(caster);
return B_TRUE;
}
} else {
// random one
r = getreallyrandomrace();
}
if (forcejob) {
randomjobsok = B_FALSE;
} else {
randomjobsok = B_TRUE;
}
// add the monster
newlf = addmonster(targcell, r->id, randomjobsok, 1);
if (newlf) {
// assign job if required
if (forcejob) {
givejob(newlf, forcejob->id);
}
if (haslos(player, targcell)) {
char *newbuf;
getlfname(newlf, buf);
newbuf = strdup(buf);
// newbuf will be "the xxx"
if (isvowel(newbuf[4])) {
newbuf = strrep(newbuf, "the ", "an ", NULL);
} else {
newbuf = strrep(newbuf, "the ", "a ", NULL);
}
capitalise(newbuf);
msg("%s appears!", newbuf);
free(newbuf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// not worth any xp
addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL);
rv = B_FALSE;
} else {
// didn't work for some reason
if (caster->controller == C_PLAYER) {
nothinghappens();
}
return B_TRUE;
}
} else if (spellid == OT_S_DARKNESS) {
if (!targcell) targcell = caster->cell;
// centre on the caster
if (haslos(player, targcell)) {
msg("A cloud of darkness descends!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (blessed) {
// permenant darkness
makelitradius(targcell, power*2, L_PERMDARK, -1);
} else {
// temporary darkness
makelitradius(targcell, power*2, L_PERMDARK, rnd(5,10) + power );
}
} else if (spellid == OT_S_DETECTAURA) {
if (isplayer(caster)) {
object_t *o;
char obname[BUFLEN];
int somethinghappened = B_FALSE;
for (o = caster->pack->first ; o ; o = o->next) {
if (o->blessed == B_BLESSED) {
getobname(o, obname, o->amt);
msg("Your %s flash%s white!", noprefix(obname), (o->amt == 1) ? "es" : "");
somethinghappened = B_TRUE;
} else if (o->blessed == B_CURSED) {
getobname(o, obname, o->amt);
msg("Your %s flash%s black!", noprefix(obname), (o->amt == 1) ? "es" : "");
somethinghappened = B_TRUE;
}
}
// now check objects on the ground...
for (o = caster->cell->obpile->first ; o ; o = o->next) {
if (o->blessed == B_BLESSED) {
getobname(o, obname, o->amt);
msg("%s flash%s white!", obname, (o->amt == 1) ? "es" : "");
somethinghappened = B_TRUE;
} else if (o->blessed == B_CURSED) {
getobname(o, obname, o->amt);
msg("%s flash%s black!", obname, (o->amt == 1) ? "es" : "");
somethinghappened = B_TRUE;
}
}
if (somethinghappened) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
// we know that everything else is uncursed
for (o = caster->pack->first ; o ; o = o->next) {
o->blessknown = B_TRUE;
}
for (o = caster->cell->obpile->first ; o ; o = o->next) {
o->blessknown = B_TRUE;
}
} else {
nothinghappens();
}
} else {
// monsters can't use this
}
} else if (spellid == OT_S_DETECTLIFE) {
target = caster;
if (isplayer(caster)) {
int howlong,radius;
howlong = getspellduration(10,20,blessed) + (power*2);
radius = power * 10;
addtempflag(target->flags, F_DETECTLIFE, 10, (power >= 5) ? B_TRUE : NA, NA, NULL, howlong);
} else {
// monsters can't use this
}
} else if (spellid == OT_S_DETECTMAGIC) {
target = caster;
if (isplayer(caster)) {
int howlong;
howlong = getspellduration(40,90,blessed)+(power*2);
addtempflag(target->flags, F_DETECTMAGIC, B_TRUE, NA, NA, NULL, howlong);
} else {
// monsters can't use this
}
/*
} else if (spellid == OT_S_DETECTMETAL) {
target = caster;
if (isplayer(caster)) {
int howlong = 15;
howlong = getspellduration(10,20,blessed);
addtempflag(target->flags, F_DETECTMETAL, 10, NA, NA, NULL, howlong);
} else {
// monsters can't use this
}
*/
} else if (spellid == OT_S_DETECTOBS) {
target = caster;
if (isplayer(caster)) {
int howlong,radius;
howlong = getspellduration(25,35,blessed) + (power*10);
radius = power*10;
addtempflag(target->flags, F_DETECTOBS, radius, NA, NA, NULL, howlong);
} else {
// monsters can't use this
}
} else if (spellid == OT_S_DETONATE) {
// don't need line of fire!
if (!validatespellcell(caster, &targcell, TT_MONSTER|TT_DOOR, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
explodecells(targcell, 20, B_TRUE, NULL, power / 4, B_TRUE);
if (haslos(player, targcell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_EXPLODEMETAL) {
float totalmass = 0;
object_t *o, *nexto;
if (!validatespellcell(caster, &targcell, TT_OBJECT, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
// how much metal is there?
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
// destroy metal items on the ground
if (ismetal(o->material->id)) {
totalmass += getobweight(o);
removeob(o, o->amt);
}
}
// destroy objects right away
removedeadobs(targcell->lf->pack);
// explosion, based on size...
if (totalmass > 0) {
// announce
if (totalmass < 10) {
// little one
explodecells(targcell, totalmass*5, B_FALSE, NULL, 0, B_TRUE);
} else {
explodecells(targcell, totalmass*5, B_TRUE, NULL, 1, B_TRUE);
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_DISPERSAL) {
cell_t *c = NULL;
object_t *o, *nexto;
int donesomething = B_FALSE;
if (!targcell) {
if (isplayer(caster)) {
sprintf(buf, "Where will you target your dispersal?");
targcell = askcoords(buf, TT_MONSTER|TT_OBJECT);
if (!targcell) { // no cell
fizzle(caster);
return B_TRUE;
}
} else {
fizzle(caster);
return B_TRUE;
}
}
target = targcell->lf;
if (targcell->lf) {
int resisted = B_FALSE;
// if you cast this at yourself, only check for magic resistance if you have the
// flag.
// other people get a check anyway.
if (targcell->lf == caster) {
if (getmr(targcell->lf) && skillcheck(targcell->lf, SC_RESISTMAG, 20 + power, 0)) {
resisted = B_TRUE;
}
} else {
if (skillcheck(targcell->lf, SC_RESISTMAG, 20 + power, 0)) {
resisted = B_TRUE;
}
}
if (resisted) {
if (isplayer(targcell->lf)) {
msg("You flicker.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
} else if (haslos(player, targcell)) {
getlfname(targcell->lf, buf);
msg("%s flickers.",buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
}
} else {
c = NULL;
while (!c || c->type->solid || haslf(c)) {
c = getrandomcell(targcell->map);
}
if (cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
}
teleportto(target, c, B_FALSE);
}
}
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
getobname(o, buf, o->amt);
c = NULL;
while (!c || c->type->solid || haslf(c) || hasobwithflag(c->obpile, F_CLIMBABLE)) {
c = getrandomcell(targcell->map);
}
if (haslos(player, targcell)) {
msg("%s disappear%s!", buf, (o->amt == 1) ? "s" : "");
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
}
moveob(o, c->obpile, o->amt);
if (haslos(player, c)) {
msg("%s appear%s nearby!", buf, (o->amt == 1) ? "s" : "");
}
}
if (isplayer(caster) && !donesomething) {
nothinghappens();
}
rv = B_FALSE;
} else if (spellid == OT_S_DRAINLIFE) {
char lfname[BUFLEN];
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
} else {
fizzle(caster);
return B_FALSE;
}
if (!isimmuneto(target->flags, DT_NECROTIC)) {
// animation (opposite dir)
anim(targcell, caster->cell, '%', C_MAGENTA);
}
if (isplayer(caster) || cansee(player, caster)) {
if (isimmuneto(target->flags, DT_NECROTIC)) {
msg("%s suck%s death from %s!",castername,isplayer(caster) ? "" : "s", lfname);
} else {
msg("%s suck%s life from %s!",castername,isplayer(caster) ? "" : "s", lfname);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (target) {
int amt;
if (isimmuneto(target->flags, DT_NECROTIC)) {
// target gains hp
amt = rnd(1,6) + power;
gainhp(target, amt);
// caster loses it
amt = losehp(caster, rnd(1,6) + power, DT_NECROTIC, caster, "lifeforce drain");
} else {
// target loses hp
amt = losehp(target, rnd(1,6) + power, DT_NECROTIC, caster, "lifeforce drain");
// caster gains it
gainhp(caster, amt);
}
}
} else if (spellid == OT_S_ENERGYBLAST) {
int range;
int x,y;
// announce
if (isplayer(caster) || cansee(player, caster)) {
msg("%s emit%s a radial blast of energy!",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
range = 2 + (power / 4);
animradial(caster->cell, range, '}', C_CYAN);
for (y = caster->cell->y - range ; y <= caster->cell->y + range; y++) {
for (x = caster->cell->x - range ; x <= caster->cell->x + range; x++) {
targcell = getcellat(caster->cell->map, x,y);
if (targcell && (getcelldist(caster->cell, targcell) <= range)) {
if (targcell->lf && (targcell->lf != caster) && haslof(caster->cell, targcell, B_FALSE, NULL)) {
char lfname[BUFLEN];
// automatic hit
getlfname(targcell->lf, lfname);
if (haslos(caster, targcell)) {
msg("A blast of energy hits %s.",lfname);
}
losehp(targcell->lf, rnd(2,6), DT_MAGIC, caster, "an energy blast");
}
}
}
}
} else if (spellid == OT_S_FLASH) {
if (isplayer(caster) || cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
brightflash(caster->cell, 2 + (power/4), player);
} else if (spellid == OT_S_ENERGYBOLT) {
char lfname[BUFLEN];
char numbuf[BUFLEN];
numtotext(power, numbuf);
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
// animation
anim(caster->cell, targcell, '}', C_CYAN);
if (isplayer(caster) || cansee(player, caster)) {
if (power == 1) {
msg("%s fire%s a bolt of energy.",castername,isplayer(caster) ? "" : "s");
} else {
msg("%s fire%s %s bolts of energy.",castername,isplayer(caster) ? "" : "s", numbuf);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
// target takes magical damage
// ALWAYS hits.
if (cansee(caster, target)) {
if (power == 1) {
msg("A bolt of energy hits %s.",lfname);
} else {
msg("%s bolts of energy hit %s.",numbuf, lfname);
}
}
losehp(target, rolldie(power,4), DT_MAGIC, caster, "an energy bolt");
}
} else if (spellid == OT_S_FIREBALL) {
int failed = B_FALSE;
// ask for a target cell
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
if (targcell) {
if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) {
int dir;
cell_t *c;
object_t *o;
// centre fireball here...
if (isplayer(caster)) {
msg("You launch an enormous ball of fire!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
msg("%s launches an enormous ball of fire!",castername);
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, targcell)) {
msg("An enormous ball of fire explodes!");
}
anim(caster->cell, targcell, '^', C_RED);
// add fires as follows (3 = large, 2 = medium, 1 = smell)
//
// 1
// 232
// 13331
// 232
// 1
// add large fires to surrounding cells
addob(targcell->obpile, "large fire");
for (dir = D_N; dir <= D_W; dir++) {
c = getcellindir(targcell, dir);
if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) {
o = addob(c->obpile, "large fire");
if (o) {
setobcreatedby(o, caster);
}
if (c->lf) {
losehp(c->lf, rolldie(power,4), DT_FIRE, caster, "a fireball");
}
}
}
for (dir = DC_NE; dir <= DC_NW; dir += 2) {
cell_t *c;
c = getcellindir(targcell, dir);
if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) {
o = addob(c->obpile, "medium fire");
if (o) {
setobcreatedby(o, caster);
}
if (c->lf) {
int ndice;
ndice = power / 2; if (ndice < 1) ndice = 1;
losehp(c->lf, rolldie(ndice,4), DT_FIRE, caster, "a fireball");
}
}
}
for (dir = D_N; dir <= D_W; dir++) {
cell_t *c;
c = getcellindir(targcell, dir);
if (c) {
c = getcellindir(c, dir);
if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) {
o = addob(c->obpile, "small fire");
if (o) {
setobcreatedby(o, caster);
}
if (c->lf) {
int ndice;
ndice = power / 2; if (ndice < 1) ndice = 1;
losehp(c->lf, rolldie(ndice,2), DT_FIRE, caster, "a fireball");
}
}
}
}
} else {
failed = B_TRUE;
}
} else {
if (caster->controller == C_PLAYER) {
msg("You have no line of fire to there!");
}
failed = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_FIREDART) {
char lfname[BUFLEN];
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
// animation
anim(caster->cell, targcell, '}', C_RED);
if (isplayer(caster) || cansee(player, caster)) {
msg("%s shoot%s a dart of flame.",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
// target takes magical damage
// check if it hits
if (skillcheck(target, SC_DODGE, 20 + (power*2), 0)) {
// miss
msg("A dart of flame misses %s.",lfname);
} else {
// hit
if (cansee(caster, target)) {
msg("A dart of flame hits %s.",lfname);
}
losehp(target, rnd(2,6) + power, DT_FIRE, caster, "a dart of flame");
}
} else {
object_t *o, *nexto;
for (o = targcell->obpile->first; o ; o = nexto) {
nexto = o->next;
takedamage(o, 0, DT_FIRE);
}
}
} else if (spellid == OT_S_FLAMEBURST) {
int range = 1;
int x,y;
range = 1 + (power / 5);
// announce
if (isplayer(caster) || cansee(player, caster)) {
msg("%s emit%s a %sblast of fire!",castername,isplayer(caster) ? "" : "s",
(power >= 5) ? "huge " : "");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
animradialorth(caster->cell, range, '}', C_RED);
for (y = caster->cell->y - range ; y <= caster->cell->y + range; y++) {
for (x = caster->cell->x - range ; x <= caster->cell->x + range; x++) {
targcell = getcellat(caster->cell->map, x,y);
if (targcell && (getcelldistorth(caster->cell, targcell) <= range)) {
if (targcell->lf && (targcell->lf != caster) && haslof(caster->cell, targcell, B_FALSE, NULL)) {
char lfname[BUFLEN];
// automatic hit
getlfname(targcell->lf, lfname);
if (haslos(caster, targcell)) {
msg("%s burn%s!",lfname,isplayer(targcell->lf) ? "" : "s");
}
losehp(targcell->lf, rolldie(2,8)+3, DT_FIRE, caster, "a burst of fire");
}
}
}
}
} else if (spellid == OT_S_FLAMEPILLAR) {
int failed = B_FALSE;
// ask for a target cell
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_TRUE, LOF_WALLSTOP, spellid, power)) return B_TRUE;
if (targcell && haslos(caster, targcell)) {
if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) {
flag_t *f;
object_t *o;
// create flame there
if (haslos(player, targcell)) {
msg("A raging pillar of flame appears!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
o = addob(targcell->obpile, "large fire");
// magically boost hp based on power
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] += power;
f->val[1] += power;
}
setobcreatedby(o, caster);
} else {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_ENCHANT) {
object_t *o;
if (targob) {
o = targob;
} else {
// ask for an object
o = askobjectwithflag(caster->pack, "Enchant which object", NULL, AO_NONE, F_ENCHANTABLE);
}
if (!o) {
fizzle(caster);
return B_TRUE;
}
if (isplayer(caster)) {
if (!hasflag(o->flags, F_ENCHANTABLE)) {
nothinghappens();
return B_TRUE;
} else {
int amt;
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("Your %s glows %s for a moment.", noprefix(obname), (blessed == B_CURSED) ? "black" : "green");
if (seenbyplayer) *seenbyplayer = B_TRUE;
if (blessed == B_CURSED) {
amt = -1;
} else {
amt = 1;
}
modbonus(o, amt);
}
} else {
// monsters can't id things!
}
} else if (spellid == OT_S_FLIGHT) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_FLYING, B_TRUE, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_FREEZEOB) {
object_t *o;
if (targob) {
o = targob;
} else {
// rings?
o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_RIGHTHAND, NA, NA, NULL);
if (!o) o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_LEFTHAND, NA, NA, NULL);
// gloves?
if (!o) o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_HANDS, NA, NA, NULL);
// weapon?
if (!o) o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_WEAPON, NA, NA, NULL);
if (o) {
// at high power, warn...
if (isplayer(caster) && (power > 5)) {
char obname[BUFLEN];
char ch;
getobname(o, obname, o->amt);
msg("Your %s start%s glowing bright blue!", obname, (o->amt == 1) ? "s" : "");
more();
ch = askchar("Abort your spell?", "yn","y", B_TRUE);
if (ch == 'y') {
msg("You release your spell into the air.");
return B_FALSE;
}
}
} else {
// next thing touched
addflag(caster->flags, F_FREEZINGTOUCH, 1, NA, NA, NULL);
if (caster->controller == C_PLAYER) {
msg("Your hands begin to glow blue!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
getlfname(caster, buf);
msg("%s's hands begin to glow blue!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
}
if (!o) {
fizzle(caster);
return B_TRUE;
}
// object will turn to ice immediately...
if (isplayer(caster)) {
msg("Your hands feel freezing cold!");
}
if (o->material->id == MT_ICE) { // already made of ice?
if (caster->controller == C_PLAYER) {
nothinghappens();
}
return B_TRUE;
} else {
int rv;
getobname(o, buf, o->amt);
rv = changemat(o, MT_ICE);
if (rv) {
if (rv == E_NOEFFECT) {
if (o->pile->owner) {
if (o->pile->owner->controller == C_PLAYER) {
msg("Your %s glows blue for a moment.",noprefix(buf));
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, o->pile->owner->cell)) {
char buf2[BUFLEN];
getlfname(o->pile->owner, buf2);
msg("%s%s %s glows blue for a moment.", noprefix(buf2),getpossessive(buf2), buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (haslos(player, o->pile->where)) {
msg("%s glows blue for a moment.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
if (caster->controller == C_PLAYER) {
nothinghappens();
}
}
return B_TRUE;
} else {
if (o->pile->owner) {
if (o->pile->owner->controller == C_PLAYER) {
msg("Your %s turn%s to ice!",noprefix(buf), (o->amt == 1) ? "s" : "");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, o->pile->owner->cell)) {
getlfname(o->pile->owner, buf);
msg("A small cloud of vapour rises up from %s.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (haslos(player, o->pile->where)) {
msg("%s turns to ice!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
}
}
} else if (spellid == OT_S_GASEOUSFORM) {
target = caster;
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target)) {
msg("You feel momentarily insubstantial.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
if (isplayer(caster) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// remember the original race
addflag(target->flags, F_ORIGRACE, target->race->id, NA, NA, NULL);
// polymorph is always will be temporary
addtempflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, 10);
setrace(target, R_GASCLOUD);
}
} else if (spellid == OT_S_HASTE) {
int howlong = 15;
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
if (haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (getmr(target) && skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target) || haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s blur%s for a moment.",buf, isplayer(target) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
howlong = getspellduration(5,25,blessed) + power;
addtempflag(target->flags, F_FASTACT, 5, NA, NA, NULL, howlong);
addtempflag(target->flags, F_FASTMOVE, 5, NA, NA, NULL, howlong);
}
} else if (spellid == OT_S_HEALING) {
int donesomething = B_FALSE;
flag_t *f;
target = caster;
// cure certain bad effects
if (killflagsofid(target->flags, F_PAIN)) {
donesomething = B_TRUE;
}
if (target->hp < target->maxhp) {
switch (blessed) {
case B_BLESSED:
gainhp(target, 20 + (power*2));
break;
case B_UNCURSED:
gainhp(target, rnd(10,20) + (power*2));
break;
case B_CURSED:
gainhp(target, 10);
break;
}
if (isplayer(target)) {
if (target->hp >= target->maxhp) {
msg("Your wounds close themselves!");
} else {
msg("Some of your wounds close themselves!");
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s looks healthier!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
donesomething = B_TRUE;
}
f = lfhasflag(target, F_POISONED);
if (f) {
killflag(f);
donesomething = B_TRUE;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (!donesomething) {
if (isplayer(target)) {
nothinghappens();
}
}
} else if (spellid == OT_S_HEALINGMIN) {
int donesomething = B_FALSE;
flag_t *f;
target = caster;
// cure bad effects instead of healing
if (killflagsofid(target->flags, F_PAIN)) {
return B_FALSE;
}
if (target->hp < target->maxhp) {
switch (blessed) {
case B_BLESSED:
gainhp(target, 8 + power);
break;
case B_UNCURSED:
gainhp(target, rnd(1,8) + power);
break;
case B_CURSED:
gainhp(target, 1);
break;
}
if (isplayer(target)) {
msg("%s of your scrapes and bruises are healed!",
(target->hp >= target->maxhp) ? "All" : "Some");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s looks a little healthier!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
donesomething = B_TRUE;
}
f = lfhasflag(target, F_POISONED);
if (f) {
killflag(f);
donesomething = B_TRUE;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (!donesomething) {
if (isplayer(target)) {
nothinghappens();
}
}
} else if (spellid == OT_S_IDENTIFY) {
object_t *o;
if (targob) {
o = targob;
} else {
// ask for an object
o = askobject(caster->pack, "Identify which object", NULL, AO_NOTIDENTIFIED);
}
if (!o) {
fizzle(caster);
return B_TRUE;
}
if (caster->controller == C_PLAYER) {
if (isidentified(o)) { // already identified?
nothinghappens();
return B_TRUE;
} else {
int charges;
identify(o);
getobname(o, buf, o->amt);
// charges
charges = getcharges(o);
if (charges == -1) {
msgnocap("%c - %s.",o->letter, buf);
} else {
msgnocap("%c - %s (%d charges left).",o->letter, buf, charges);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
// monsters can't id things!
}
} else if (spellid == OT_S_INFINITEDEATH) {
lifeform_t *l;
if (caster->controller == C_PLAYER) {
msg("A wave of incalculable evil blasts outwards from you!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
getlfname(caster, buf);
msg("A wave of incalculable evil blasts outwards from %s!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (player->cell->map == caster->cell->map) { // should always be true
msg("Oh no! A wave of incalculable evil washes over you!");
}
for (l = caster->cell->map->lf ; l ; l = l->next) {
if (l != caster) {
if (isimmuneto(l->flags, DT_NECROTIC) || skillcheck(l, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(l)) {
msg("Luckily, the evil doesn't seem to harm you.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
char dambuf[BUFLEN];
char cname[BUFLEN];
real_getlfname(caster, cname, B_FALSE);
sprintf(dambuf, "%s%s infinite death spell", cname, getpossessive(cname));
losehp(l, 500, DT_NECROTIC, NULL, dambuf);
}
}
}
// now hit the caster!
if (isimmuneto(caster->flags, DT_NECROTIC) || skillcheck(caster, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(caster)) {
msg("Luckily, the evil doesn't seem to harm you.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
char dambuf[BUFLEN];
char cname[BUFLEN];
real_getlfname(caster, cname, B_FALSE);
sprintf(dambuf, "%s%s infinite death spell", cname, getpossessive(cname));
losehp(caster, 500, DT_NECROTIC, NULL, dambuf);
}
} else if (spellid == OT_S_MANASPIKE) {
char lfname[BUFLEN];
char numbuf[BUFLEN];
numtotext(power, numbuf);
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
// animation
anim(caster->cell, targcell, '}', C_CYAN);
if (isplayer(caster) || cansee(player, caster)) {
if (power == 1) {
msg("%s fire%s a spike of mana.",castername,isplayer(caster) ? "" : "s");
} else {
msg("%s fire%s %s spikes of mana.",castername,isplayer(caster) ? "" : "s", numbuf);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
// target takes magical damage
// always hit
if (cansee(caster, target)) {
if (power == 1) {
msg("A spike of mana hits %s.",lfname);
} else {
msg("%s spikes of mana hit %s.",numbuf, lfname);
}
}
losehp(target, rolldie(power,2), DT_MAGIC, caster, "a mana spike");
}
} else if (spellid == OT_S_MAGSHIELD) {
object_t *o,*nexto;
flag_t *f;
// wearing metal?
for (o = caster->pack->first ; o ; o = nexto) {
nexto = o->next;
if (ismetal(o->material->id)) {
f = isequipped(o);
if (f) {
if ((f->val[0] == BP_WEAPON) || (f->val[0] == BP_SECWEAPON)) {
} else if ((f->val[0] == BP_RIGHTHAND) || (f->val[0] == BP_LEFTHAND)) {
if (isplayer(caster)) {
getobname(o, buf, 1);
msg("Your %s slides off your %s!", buf, getbodypartname(f->val[0]));
} else if (cansee(player, caster)) {
getobname(o, buf, 1);
msg("%s%s %s slides off its %s!", castername,
getpossessive(castername), buf, getbodypartname(f->val[0]));
}
moveob(o, caster->cell->obpile, o->amt);
} else {
if (isplayer(caster)) {
getobname(o, buf, 1);
msg("Your %s pulls away from you a little.", noprefix(buf));
}
fizzle(caster);
return B_FALSE;
}
}
}
}
// add the magnetic field!
addtempflag(caster->flags, F_MAGSHIELD, B_TRUE, NA, NA, NULL, 25 + (power*2));
} else if (spellid == OT_S_MAPPING) {
int x,y;
int range;
map_t *m;
m = caster->cell->map;
if (power == 10) {
range = UNLIMITED;
} else {
range = power * 6;
}
// reveal map
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
if (c) {
if (range == UNLIMITED) {
c->known = B_TRUE;
} else if (getcelldist(caster->cell, c) <= range) {
c->known = B_TRUE;
}
}
}
}
if (caster->controller == C_PLAYER) {
msg("An image of your surroundings appears in your mind!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
rv = B_FALSE;
} else if (spellid == OT_S_METALHEAL) {
int i;
float totalmass = 0;
object_t *o;
int howmuch;
// works on all metal in sight
if (isplayer(caster)) {
if (seenbyplayer) *seenbyplayer = B_FALSE;
msg("You draw health from nearby metal!");
} else if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_FALSE;
msg("%s draws health from nearby metal!",castername);
}
totalmass = 0;
// destroy WORN metal objects by the caster (not CARRIED ones)
for (o = caster->pack->first ; o ; o = o->next) {
if (isequipped(o) && ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(caster->pack);
// handle cell under the caster
for (o = caster->cell->obpile->first ; o ; o = o->next) {
// destroy metal items on the ground
if (ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(caster->cell->obpile);
for (i = 0; i < caster->nlos; i++) {
targcell = caster->los[i];
for (o = targcell->obpile->first ; o ; o = o->next) {
// destroy metal items on the ground
if (ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(targcell->obpile);
if (targcell->lf && !skillcheck(targcell->lf, SC_RESISTMAG, 20 + power, 0)) {
// destroy only WORN metal objects, not CARRIED ones
for (o = targcell->lf->pack->first ; o ; o = o->next) {
if (isequipped(o) && ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(targcell->lf->pack);
}
}
if (totalmass > 0) {
// heal 2 hp per kilo
howmuch = floor(totalmass) * 2;
howmuch *= (power/3); // modify for power
gainhp(caster, howmuch);
} else {
fizzle(caster);
}
} else if (spellid == OT_S_MINDSCAN) {
cell_t *where;
int failed = B_FALSE;
if (caster->controller == C_PLAYER) {
// ask for a target cell
sprintf(buf, "Whose mind will you scan?");
where = askcoords(buf, TT_MONSTER);
if (where && haslos(caster, where) && haslf(where)) {
char targname[BUFLEN];
lifeform_t *oldplayer;
// temporarily change player pointer...
oldplayer = player;
player = where->lf;
//
getlfname(where->lf, targname);
sprintf(buf, "Mindscanning %s, ESC to quit.", targname);
doexplain(buf);
// restore player pointer
player = oldplayer;
//showlfstats(where->lf, B_TRUE);
} else {
failed = B_TRUE;
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_ACCELMETAL) {
if (!targob) {
// ask for an object
targob = askobject(caster->pack, "Fire which object", NULL, AO_NONE);
}
if (!targob || !ismetal(targob->material->id) ) {
fizzle(caster);
return B_TRUE;
}
if (isequipped(targob) && iscursed(targob)) {
char obname[BUFLEN];
getobname(targob, obname, 1);
msg("Your %s appears to be stuck to you!", noprefix(obname));
targob->blessknown = B_TRUE;
return B_TRUE;
}
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_FALSE, LOF_WALLSTOP, spellid, power)) return B_TRUE;
// 4 is the same as ST_TITANIC strength
// 10 = gun speed
fireat(caster, targob, 1, targcell, 8 + (power / 2) , NULL);
} else if (spellid == OT_S_PARALYZE) {
int howlong;
int saved = B_FALSE;
if (!validatespelllf(caster, &target)) return B_TRUE;
if (lfhasflag(target, F_PARALYZED)) {
fizzle(caster);
return B_TRUE;
}
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
saved = B_TRUE;
} else if (skillcheck(target, SC_STR, 20 + power, 0)) {
saved = B_TRUE;
}
if (saved) {
if (isplayer(target)) {
msg("You stiffen momentarily.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s stiffens momentarily.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
howlong = power*2;
addtempflag(target->flags, F_PARALYZED, B_TRUE, NA, NA, NULL, howlong);
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_PAIN) {
int failed = B_FALSE;
// ask for target
if (!validatespelllf(caster, &target)) return B_TRUE;
if (lfhasflag(target, F_PAIN)) {
fizzle(caster);
return B_FALSE;
}
// make this a constitution skill check ?
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
failed = B_TRUE;
} else if (skillcheck(target, SC_CON, 20 + power, 0)) {
failed = B_TRUE;
}
if (target) {
if (failed) {
if (isplayer(target)) {
msg("Your skin hurts for a moment.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
char targetname[BUFLEN];
getlfname(target, targetname);
msg("%s winces in pain, then recovers.", targetname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
} else {
int howlong = 7;
howlong = getspellduration(3,5,blessed) + (power/2);
addtempflag(target->flags, F_PAIN, DT_MAGIC, NA, NA, "2d4+2", howlong);
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_PETRIFY) {
if (!validatespelllf(caster, &target)) return B_TRUE;
// some thigns can't be stoned
if (!lfcanbestoned(target)) {
fizzle(caster);
return B_FALSE;
} else if (lfhasflag(target, F_BEINGSTONED)) {
fizzle(caster);
return B_FALSE;
}
// savingthrow
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0) || skillcheck(target, SC_CON, 30 + power, 0)) {
if (haslos(player, target->cell)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("%s glow%s grey momentarily.", lfname, isplayer(target) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_TRUE;
}
// successful
if (haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
addflag(target->flags, F_BEINGSTONED, 2, NA, NA, NULL);
} else if (spellid == OT_S_POISONBOLT) {
char lfname[BUFLEN];
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_FALSE, LOF_NEED, spellid, power)) return B_TRUE;
// animation
anim(caster->cell, targcell, '}', C_GREEN);
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
// target gets saving throw to avoid...
if (skillcheck(target, SC_DODGE, 20 + (power*2), 0)) {
// miss
if (cansee(caster, target)) {
msg("A glob of venom misses %s.",lfname);
}
} else {
// hit
if (cansee(caster, target)) {
msg("A glob of venom hits %s.",lfname);
}
losehp(target, rnd(1,4), DT_POISON, caster, "a glob of poison");
if (!isimmuneto(target->flags, DT_POISON)) {
addtempflag(target->flags, F_POISONED, NA, NA, NA, castername, power*3);
}
}
} else {
object_t *o, *nexto;
for (o = targcell->obpile->first; o ; o = nexto) {
nexto = o->next;
takedamage(o, 0, DT_FIRE);
}
}
} else if (spellid == OT_S_POSSESSION) {
char targname[BUFLEN];
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, targname);
// warn if it can't do magic
if (lfhasflag(target, F_NOSPELLS)) {
int cancel = B_FALSE;
if (isplayer(caster)) {
char buf[BUFLEN];
char ch;
sprintf(buf, "You may be stuck in %s%s body - proceed?", targname,
getpossessive(targname));
ch = askchar(buf, "yn", "n", B_TRUE);
if (ch != 'y') {
cancel = B_TRUE;
}
} else {
cancel = B_TRUE;
}
if (cancel) {
if (isplayer(caster)) msg("Cancelled.");
return B_TRUE;
}
}
// saving throw....
if (skillcheck(target, SC_RESISTMAG, 20 + power*2, 0)) {
if (isplayer(caster) && cansee(player, target)) {
msg("%s%s mind fights off your intrusion!", targname, getpossessive(targname));
}
return B_FALSE;
}
// announce
if (cansee(player,target)) {
if (isplayer(caster)) {
msg("You take possession of %s%s mind!", targname, getpossessive(targname));
} else {
msg("%s takes possession of %s%s mind!",cansee(player, caster) ? castername : "Something",
targname, getpossessive(targname));
}
}
// possess!
if (isplayer(caster)) {
// player name
copyflag(target->flags, caster->flags, F_NAME);
// level
target->level = player->level;
player->controller = C_AI;
player = target;
target->controller = C_PLAYER;
} else {
switch (getallegiance(caster)) {
case AL_HOSTILE:
killflagsofid(target->flags, F_FRIENDLY);
addflag(target->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
break;
case AL_PEACEFUL:
killflagsofid(target->flags, F_FRIENDLY);
killflagsofid(target->flags, F_HOSTILE);
break;
case AL_FRIENDLY:
makefriendly(target, PERMENANT);
break;
}
}
// now kill the caster!
die(caster);
} else if (spellid == OT_S_PSYARMOUR) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_MAGICARMOUR, power*4, NA, NA, "psychic barrier", FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_PULL) {
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_TRUE, LOF_WALLSTOP, spellid, power)) return B_TRUE;
target = targcell->lf;
if (target) {
int failed = B_FALSE;
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
failed = B_TRUE;
} else {
// they get pulled towards caster
failed = pullnextto(target, caster->cell);
}
if (isplayer(target) || cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (failed) {
if (isplayer(target) || cansee(player, target)) {
char buf[BUFLEN];
getlfname(target, buf);
msg("%s %s pulled forward slightly.", buf, isplayer(target) ? "are" : "is");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_PULLMETAL) {
int donesomething = B_FALSE;
if (!validatespellcell(caster, &targcell,TT_OBJECT, B_TRUE, LOF_WALLSTOP, spellid, power)) return B_TRUE;
if (targcell->lf) {
object_t *o;
int failed = B_FALSE;
int gotmetal = B_FALSE;
float metalweight = 0;
object_t *wep;
donesomething = B_FALSE;
// if they are weilding a metal weapon...
wep = getweapon(targcell->lf);
if (wep && ismetal(wep->material->id) && !lfhasflag(caster, F_NOPACK)) {
// pull their weapon from them
pullobto(wep, caster);
donesomething = B_TRUE;
}
// if they're wearing metal...
for (o = targcell->lf->pack->first ; o ; o = o->next) {
if (ismetal(o->material->id)) {
flag_t *f;
f = isequipped(o);
if ((f->val[0] != BP_WEAPON) && (f->val[0] != BP_SECWEAPON)) {
gotmetal = B_TRUE;
metalweight += getobweight(o);
break;
}
}
}
if (gotmetal) {
donesomething = B_TRUE;
// include the target lf's weight since we will be pulling on them
target = targcell->lf;
metalweight += getlfweight(target, B_WITHOBS);
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
failed = B_TRUE;
} else if (getobpileweight(caster->pack) + metalweight <= getmaxcarryweight(caster)) {
// they get pulled towards caster
failed = pullnextto(target, caster->cell);
} else {
// caster gets pulled towards them
failed = pullnextto(caster, target->cell);
}
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (failed) {
if (isplayer(target) || haslos(player, target->cell)) {
char buf[BUFLEN];
getlfname(target, buf);
msg("%s %s pulled forward slightly.", buf, isplayer(target) ? "are" : "is");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
}
if (!donesomething) {
fizzle(caster);
}
} else if (targcell->obpile->first) { // no lifeform there
targob = NULL;
// select object from cell...
if (countobs(targcell->obpile) == 1) {
targob = targcell->obpile->first;
} else {
targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE);
}
if (targob) {
if (ismetal(targob->material->id)) {
if (isplayer(caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (getobpileweight(caster->pack) + getobweight(targob) <= getmaxcarryweight(caster)) {
// move it to the player
pullobto(targob, caster);
} else {
// move player to it
pullnextto(caster, targcell);
}
} else {
fizzle(caster);
}
} else {
fizzle(caster);
}
}
} else if (spellid == OT_S_GATE) {
int newdepth;
int min,max;
char query[BUFLEN];
// calculate range
min = caster->cell->map->depth - (power*2);
max = caster->cell->map->depth + (power*2);
if (min < 1) min = 1;
// ask which level
sprintf(query, "Create a portal to which level (%d-%d)",min,max);
askstring(query, '?', buf, BUFLEN, NULL);
newdepth = atoi(buf);
if ((newdepth < min) || (newdepth > max)) {
fizzle(caster);
} else {
map_t *newmap;
cell_t *newcell,*srccell;
object_t *srcportal,*dstportal;
// find adjacent cell for portal
srccell = getrandomadjcell(caster->cell, WE_WALKABLE, B_ALLOWEXPAND);
if (!srccell) {
fizzle(caster);
return B_FALSE;
}
// create the source portal
srcportal = addob(srccell->obpile, "magic portal");
setobcreatedby(srcportal, caster);
// announce, because we might have a delay creating the level...
if (isplayer(caster)) {
msg("There is a blinding flash of light...");
wrefresh(msgwin);
}
// find new map !
newmap = findmapofdepth(newdepth);
if (!newmap) {
// create new map
newmap = addmap();
createmap(newmap, newdepth, caster->cell->map->habitat, NULL, findot(OT_STAIRSUP));
}
// find a random cell there
newcell = getrandomcell(newmap);
while (!cellwalkable(caster, newcell, NULL) || hasenterableobject(newcell)) {
newcell = getrandomcell(newmap);
}
// add the dst portal
dstportal = addob(newcell->obpile, "magic portal");
setobcreatedby(dstportal, caster);
// link the dst portal
addflag_real(dstportal->flags, F_MAPLINK, caster->cell->map->id, srccell->x, srccell->y, NULL, PERMENANT, B_FALSE, -1);
// link the source portal
addflag_real(srcportal->flags, F_MAPLINK, newmap->id, newcell->x, newcell->y, NULL, PERMENANT, B_FALSE, -1);
// make both gates temporary
addflag(srcportal->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(srcportal->flags, F_OBHP, 6, 6, NA, NULL);
addflag(srcportal->flags, F_OBHPDRAIN, 1, DT_DIRECT, NA, NULL);
addflag(srcportal->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(srcportal->flags, F_DIETEXT, B_TRUE, NA, NA, "vanishes");
addflag(dstportal->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(dstportal->flags, F_OBHP, 6, 6, NA, NULL);
addflag(dstportal->flags, F_OBHPDRAIN, 1, DT_DIRECT, NA, NULL);
addflag(dstportal->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(dstportal->flags, F_DIETEXT, B_TRUE, NA, NA, "vanishes");
if (haslos(player, srccell)) {
char obname[BUFLEN];
getobname(srcportal, obname, 1);
msg("%s appears!",obname);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_INSCRIBE) {
char buf[BUFLEN];
if (isplayer(caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
// ask what to inscribe
askstring("What will you inscribe here", '?', buf, BUFLEN, NULL);
if (strlen(buf) > 0) {
char *p;
if (isblind(caster)) {
// if blind, garble text somewhat
for (p = buf ; *p; p++) {
if (*p != ' ') {
if (rnd(1,4) == 1) {
if (rnd(1,2) == 1) {
*p = rnd('a','z');
} else {
*p = rnd('A','Z');
}
}
}
}
msg("You feel something stirring in the air around you.");
} else {
msg("Magical writing appears before you!");
}
if (caster->cell->writing) {
free(caster->cell->writing);
}
caster->cell->writing = strdup(buf);
} else {
fizzle(caster);
}
} else {
// monsters can't cast
}
} else if (spellid == OT_S_INVISIBILITY) {
int howlong = 30;
int willannounce = B_FALSE;
char targname[BUFLEN];
if (!validatespellcell(caster, &targcell,TT_MONSTER, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
} else if ( lfhasflag(target, F_INVISIBLE) ) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, targname);
howlong = getspellduration(20,40,blessed) + (power*2);
if (!isplayer(target) && cansee(player, target) && !lfhasflag(player, F_SEEINVIS)) {
willannounce = B_TRUE;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
addtempflag(target->flags, F_INVISIBLE, B_TRUE, NA, NA, NULL, howlong);
if (willannounce) {
msg("%s flicker%s then vanishes!",targname, isplayer(target) ? "" : "s");
}
} else if (spellid == OT_S_KNOCK) {
object_t *o;
if (!validatespellcell(caster, &targcell,TT_DOOR, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
target = targcell->lf;
// high powered knock can knock back monsters
if (target && (power >= 7)) {
int dir;
dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS);
knockback(target, dir, 2, caster);
} else {
o = hasobwithflag(targcell->obpile, F_DOOR);
if (o) {
int dooropen;
isdoor(o, &dooropen); // just check whether it's open, we know it's a door
if (dooropen) {
fizzle(caster);
} else {
if (haslos(player, targcell)) {
getobname(o, buf, o->amt);
msg("%s %s!",isplayer(caster) ? "You blast" : "Something blasts", buf);
}
takedamage(o, 999, DT_DIRECT);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
fizzle(caster);
}
}
} else if (spellid == OT_S_LEVITATION) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_LEVITATING, B_TRUE, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_LIGHT) {
lifeform_t *l;
// at power 3, you can control where the light appears
// at power 8, the light is permenant
if (power >= 3) {
// TODO: this actually means we can cast it through walls!!!
if (!validatespellcell(caster, &targcell,TT_NONE, B_FALSE, LOF_DONTNEED, spellid, power)) return B_TRUE;
} else {
targcell = caster->cell;
}
if (haslos(player, targcell)) {
msg("The area is lit by a magical light!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (blessed || (power >= 8)) {
// permenant light
makelitradius(targcell, power*2, L_PERMLIGHT, -1);
} else {
// temporary light
makelitradius(targcell, power*2, L_PERMLIGHT, rnd(5,10)+(power*2) );
}
// blind anyone with nightvis who sees it
// (player last)
for (l = caster->cell->map->lf ; l ; l = l->next) {
if (!isplayer(l) && haslos(l, caster->cell)) {
if (lfhasflag(l, F_SEEINDARK)) {
// if you don't have eyes, your'e safe!
if (!lfhasflagval(l, F_NOBODYPART, BP_EYES, NA, NA, NULL)) {
// blind for 1-3 turns
addtempflag(l->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(1,3));
}
}
// undead will flee from light
if (lfhasflag(l, F_UNDEAD)) {
// runs away from caster
addtempflag(l->flags, F_FLEEFROM, caster->id, NA, NA, NULL, 20);
}
}
}
if (isplayer(caster) || cansee(player, caster)) {
if (lfhasflag(player, F_SEEINDARK)) {
msg("The light burns your eyes!");
// blind for 5-10 turns
addtempflag(player->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(5,10));
}
}
// anyone there glows if the spell was controlled
if (targcell->lf && (power >= 3)) {
if (power >= 8) {
// permenant!
addflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL);
} else {
addtempflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, rnd(10,20)+(power*2) );
}
}
} else if (spellid == OT_S_GRAVBOOST) {
// ask for target
if (!target) {
if (isplayer(caster)) {
cell_t *where;
sprintf(buf, "Where will you target your spell?");
where = askcoords(buf, TT_MONSTER);
if (where && haslos(caster, where) && haslf(where)) {
target = haslf(where);
} else {
fizzle(caster);
}
}
}
if (target) {
int howlong = 15;
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target)) {
msg("You feel momentarily heavier.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
howlong = getspellduration(2,10,blessed) + (power/2);
addtempflag(target->flags, F_GRAVBOOSTED, B_TRUE, NA, NA, NULL, howlong);
} else {
fizzle(caster);
}
} else if (spellid == OT_S_GRAVLOWER) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_DTIMMUNE, DT_FALL, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_PACIFY) {
char targetname[BUFLEN];
if (!validatespellcell(caster, &targcell, TT_MONSTER, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, targetname);
if (skillcheck(targcell->lf, SC_RESISTMAG, 30 + power, 0)) {
if (isplayer(caster) || cansee(player, target)) {
msg("%s resists.",targetname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
int donesomething = B_FALSE;
// stop targetting anybody
if (killflagsofid(target->flags, F_TARGET)) {
donesomething = B_TRUE;
}
if (killflagsofid(target->flags, F_TARGETCELL)) {
donesomething = B_TRUE;
}
if (killflagsofid(target->flags, F_HOSTILE)) {
donesomething = B_TRUE;
}
if (donesomething) {
if (cansee(player, target)) {
msg("%s calms down.",targetname);
}
} else {
if (isplayer(caster)) {
msg("%s already seems calm enough.",targetname);
}
}
return B_FALSE;
}
} else if (spellid == OT_S_PASSWALL) {
int howlong = 7;
target = caster;
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target)) {
msg("You feel momentarily insubstantial.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
howlong = getspellduration(5,10,blessed) + (power/2);
addtempflag(target->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL, howlong);
} else if (spellid == OT_S_POLYMORPH) {
race_t *r;
// ask for target if required
if (!target && isplayer(caster)) {
cell_t *where;
where = askcoords("Who will you polymorph?", TT_MONSTER);
if (haslos(caster, where)) {
target = where->lf;
} else {
target = NULL;
}
}
if (!target) {
fizzle(caster);
return B_TRUE;
}
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target)) {
msg("You feel momentarily different.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s looks momentarily different.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
if (lfhasflag(caster, F_CONTROL)) {
if (power < 5) {
power = 5;
}
}
if (isplayer(caster) && (power >= 5)) {
if (isplayer(target)) {
askstring("What will you become", '?', buf, BUFLEN, NULL);
} else {
char buf2[BUFLEN];
char targname[BUFLEN];
getlfname(target, targname);
sprintf(buf2, "What will you transform %s into", targname);
askstring(buf2, '?', buf, BUFLEN, NULL);
}
r = findracebyname(buf);
// make sure race is valid:
// - can't turn into monsters which aren't randomly generated.
// - can't turn into unique monsters
if (r && !appearsrandomly(r->id)) {
r = NULL;
}
} else {
// random race, but not the same!
r = target->race;
while (r == target->race) {
r = getrandomrace(NULL, 0);
}
}
if (r == target->race) {
fizzle(caster);
return B_TRUE;
}
if (r) {
//int rememberorig = B_FALSE;
getlfname(target, buf);
// if this is the player, remember the original race and job...
if (!hasflag(target->flags, F_ORIGRACE)) {
// remember the original race
addflag(target->flags, F_ORIGRACE, target->race->id, NA, NA, NULL);
}
if (isplayer(target)) {
int howlong;
// polymorph will be temporary...
howlong = rnd(20,50);
addtempflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, howlong);
} else {
// permenant
addflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL);
}
// become the new race!
setrace(target, r->id);
// if someone cast the spell at themself, they can change back at will.
if (target == caster) {
addflag(target->flags, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL);
}
if (haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
fizzle(caster);
return B_TRUE;
}
} else if (spellid == OT_S_SLEEP) {
int howlong;
if (!validatespelllf(caster, &target)) return B_TRUE;
if (lfhasflag(target, F_ASLEEP)) {
fizzle(caster);
return B_TRUE;
}
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target)) {
msg("You yawn.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s yawns.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
howlong = getspellduration(5,10,blessed) + (power/2);
addtempflag(target->flags, F_ASLEEP, B_TRUE, NA, NA, NULL, howlong);
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_SLOW) {
int howlong = 15;
if (!validatespelllf(caster, &target)) return B_TRUE;
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target)) {
msg("You feel momentarily slower.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s looks momentarily slower.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
howlong = rnd(5,25) + power;
addtempflag(target->flags, F_SLOWACT, 5, NA, NA, NULL, howlong);
addtempflag(target->flags, F_SLOWMOVE, 5, NA, NA, NULL, howlong);
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_SPARK) {
object_t *o,*nexto;
int donesomething = B_FALSE;
if (!validatespellcell(caster, &targcell,TT_OBJECT | TT_MONSTER, B_TRUE, LOF_DONTNEED, spellid, power)) return B_TRUE;
if (haslos(player, targcell)) {
msg("A small spark of flame appears.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (isflammable(o)) {
takedamage(o, 0, DT_FIRE);
donesomething = B_TRUE;
}
}
if (targcell->lf) {
losehp(targcell->lf, 0, DT_FIRE, caster, "a spark");
}
} else if (spellid == OT_S_TELEPORT) {
cell_t *c = NULL;
// target is always the caster
target = caster;
if (getmr(target) && skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (isplayer(target)) {
msg("You flicker.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s flickers.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
if (lfhasflag(caster, F_CONTROL)) {
if (power < 5) {
power = 5;
} else if (power < 8) {
power = 8;
}
}
if ((power < 5) || !isplayer(caster)) {
// random
while (!c || c->type->solid || haslf(c)) {
c = getrandomcell(target->cell->map);
}
} else if (power >= 8) {
// controlled
sprintf(buf, "Where will you teleport to?");
while (!c) {
int ch;
c = askcoords(buf, TT_NONE);
if (!c->known) {
// confirm
ch = askchar("Are you sure to want to teleport into the unknown?", "yn", "n", B_TRUE);
if (ch != 'y') c = NULL;
} else if (c->type->solid) {
// confirm
ch = askchar("Are you sure to want to teleport into solid rock?", "yn", "n", B_TRUE);
if (ch != 'y') c = NULL;
}
}
} else { // ie. if (power >= 5)
int dir;
char dirch;
cell_t *poss[MAX_MAPW * MAX_MAPH];
int nposs;
int x,y;
int xmin,ymin;
int xmax,ymax;
// semicontrolled
// ask for dir
dirch = askchar("Teleport in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE);
if ((dirch == '.') || (dir == '-')) {
fizzle(caster);
return B_TRUE;
} else {
dir = chartodir(dirch);
}
// make a list of all valid cells in that dir
nposs = 0;
switch (dir) {
case D_N:
case DC_N:
ymin = 0;
ymax = target->cell->y - 1;
xmin = target->cell->x - 5;
xmax = target->cell->x + 5;
break;
case DC_NE:
xmin = target->cell->x + 1;
ymin = 0;
xmax = target->cell->map->w - 1;
ymax = target->cell->y - 1;
break;
case D_E:
case DC_E:
xmin = target->cell->x + 1;
xmax = target->cell->map->w - 1;
ymin = target->cell->y - 5;;
ymax = target->cell->y + 5;;
break;
case DC_SE:
xmin = target->cell->x + 1;
ymin = target->cell->y + 1;
xmax = target->cell->map->w - 1;
ymax = target->cell->map->h - 1;
break;
case D_S:
case DC_S:
ymin = target->cell->y + 1;
ymax = target->cell->map->h - 1;
xmin = target->cell->x - 5;
xmax = target->cell->x + 5;
break;
case DC_SW:
xmin = 0;
ymin = target->cell->y + 1;
xmax = target->cell->x - 1;
ymax = target->cell->map->h - 1;
break;
case D_W:
case DC_W:
xmin = 0;
xmax = target->cell->x - 1;
ymin = target->cell->y - 5;
ymax = target->cell->y + 5;
break;
case DC_NW:
xmin = 0;
ymin = 0;
xmax = target->cell->x - 1;
ymax = target->cell->y - 1;
break;
}
if (xmin < 0) xmin = 0;
if (ymin < 0) ymin = 0;
if (xmax > target->cell->map->w-1) xmax = target->cell->map->w-1;
if (ymax > target->cell->map->h-1) ymax = target->cell->map->h-1;
for (y = ymin; y <= ymax ; y++) {
for (x = xmin; x <= xmax ; x++) {
c = getcellat(target->cell->map, x,y);
if (c && cellwalkable(target, c, NULL)) {
poss[nposs] = c;
nposs++;
}
}
}
if (nposs <= 0) {
fizzle(caster);
return B_FALSE;
}
c = poss[rnd(0,nposs-1)];
} // end if (semicontrolled | controlled | random )
targcell = c;
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
teleportto(target, targcell, B_TRUE);
rv = B_FALSE;
} else if (spellid == OT_S_TELEKINESIS) {
cell_t *where;
int failed = B_FALSE;
float maxweight;
// if no target object...
if (!targob) {
// ask for a target cell (to take objects from)
sprintf(buf, "Where will you focus your telekinetic power?");
where = askcoords(buf, TT_OBJECT | TT_DOOR);
if (where && haslos(caster, where)) {
if (where->obpile->first) {
// select object from cell...
if (countobs(where->obpile) == 1) {
targob = where->obpile->first;
} else {
targob = askobject(where->obpile, "Target which object", NULL, AO_NONE);
if (!targob) {
failed = B_TRUE;
}
}
// TODO: check object weight!
} else {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
}
if (!failed) {
// is this a door?
if (hasflag(targob->flags, F_DOOR)) {
if (hasflag(targob->flags, F_OPEN)) {
closedoorat(caster, where);
} else {
opendoorat(caster, where);
}
return B_FALSE; // don't do rest of telekenesis code
}
// if no target cell, ask where to throw object
if (!targcell) {
char obname[BUFLEN];
getobname(targob, obname, 1);
sprintf(buf, "Where will you move %s to?", obname);
targcell = askcoords(buf, TT_MONSTER | TT_PLAYER);
}
// not liftable?
if (hasflag(targob->flags, F_NOPICKUP)) {
if (isplayer(caster)) {
nothinghappens();
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
// too heavy? max weight is now based on our race's weight and intelligence
maxweight = getlfweight(caster, B_NOOBS) +
(getlfweight(caster, B_NOOBS) * (getstatmod(caster, A_IQ) / 100));
// modify by power
maxweight += (10*power);
if (getobweight(targob) > maxweight) {
cell_t *obloc;
char obname[BUFLEN];
obloc = getoblocation(targob);
getobname(targob, obname, targob->amt);
if (haslos(player, obloc)) {
msg("%s lifts slightly, then drops again.",obname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
if (targcell && haslos(caster, targcell)) {
// if it is on us, then take theobject
if (targcell == caster->cell) {
pullobto(targob, caster);
} else {
// otherwise throw it there - but speed is based on
// caster's INTELLIGENCE, not strength like normal.
fireat(caster, targob, targob->amt, targcell, power, NULL);
// note that we use fireat() rather than throwat() to avoid
// calling taketime() twice.
}
} else {
failed = B_TRUE;
}
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_TURNUNDEAD) {
int i;
// works on all undead in sight
for (i = 0; i < caster->nlos; i++) {
targcell = caster->los[i];
target = targcell->lf;
if (target && hasflag(target->flags, F_UNDEAD)) {
int howlong = 10;
int worked = B_TRUE;
// TODO: does it work? depends on caster level & intelligence & power
worked = B_TRUE;
// TODO: how long for? depends on caster level & intelligence & power
howlong = rnd(10,20);
if (worked) {
// don't use scare() since it will fail due to them being undead.
addtempflag(target->flags, F_FLEEFROM, caster->id, NA, NA, NULL,howlong);
}
}
}
} else if (spellid == OT_S_WEAKEN) {
// ask for target
if (!validatespelllf(caster, &target)) return B_TRUE;
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
if (cansee(player, target)) {
getlfname(target, buf);
msg("%s %s momentarily weaker.", buf, isplayer(target) ? "feel" : "looks");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
if (target) {
int howlong = 15;
flag_t *f;
// already weakaned?
for (f = target->flags->first; f ; f = f->next) {
if ((f->id == F_ATTRMOD) && (f->val[0] == A_STR) && (f->obfrom == OT_S_WEAKEN)) {
if (cansee(player, target)) {
getlfname(target, buf);
msg("%s %s momentarily weaker.", buf, isplayer(target) ? "feel" : "looks");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
}
howlong = getspellduration(5,10,blessed) + power;
f = addtempflag(target->flags, F_ATTRMOD, A_STR, -power, NA, NULL, howlong);
f->obfrom = OT_S_WEAKEN;
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
} else if (spellid == OT_S_WINDSHIELD) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_WINDSHIELD, power, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if ((spellid == OT_S_WISH) || (spellid == OT_S_GIFT)) {
object_t *o;
if (isplayer(caster)) {
char lfname[BUFLEN];
char question[BUFLEN];
if (seenbyplayer) *seenbyplayer = B_TRUE;
// ask for target
if (spellid == OT_S_GIFT) {
if (!validatespelllf(caster, &target)) return B_TRUE;
} else {
target = caster;
}
getlfname(target, lfname);
// ask for an object
if (spellid == OT_S_GIFT) {
sprintf(question, "What gift will %s receive",lfname);
} else {
sprintf(question, "For what do you wish");
}
askstring(question, '?', buf, BUFLEN, NULL);
o = addob(target->cell->obpile, buf);
if (o) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
if (!hasflag(o->flags, F_IMPASSABLE) && canpickup(target, o, ALL)) {
relinkob(o, target->pack); // move to pack
if (isplayer(target)) {
msgnocap("%c - %s.", o->letter, obname);
} else {
msg("%s is gifted with %s.", lfname, obname);
}
} else {
// couldn't pick it up - try to place on ground!
// impassable?
if (hasflag(o->flags, F_IMPASSABLE)) {
cell_t *newloc;
// if so, don't put it where the player is!
newloc = getrandomadjcell(target->cell, WE_WALKABLE, B_ALLOWEXPAND);
o = relinkob(o, newloc->obpile);
}
if (o) {
noise(caster->cell, NULL, "something hitting the ground.", NULL);
if (!isblind(caster)) {
msg("%s appear%s on the ground!", obname, (o->amt == 1) ? "s" : "");
}
} else {
// couldn't make it appear
msg("The air in front of %s seems to ripple for a moment.", lfname);
}
}
} else {
// couldn't make it appear - ob doesn't exist
msg("The air in front of %s seems to ripple for a while.", lfname);
}
} else {
// monsters can't wish
}
}
return rv;
}
void fizzle(lifeform_t *caster) {
if (isplayer(caster)) {
if (lfhasflag(caster, F_CASTINGSPELL)) {
msg("Your spell fizzles.");
} else {
nothinghappens();
}
/*
} else if (cansee(player, caster)) {
getlfname(caster, buf);
capitalise(buf);
msg("%s's spell fizzles.", buf);
*/
}
}
enum SKILL getschoolskill(enum SPELLSCHOOL ss) {
switch (ss) {
case SS_ALLOMANCY:
return SK_SS_ALLOMANCY;
case SS_AIR:
return SK_SS_AIR;
case SS_DEATH:
return SK_SS_DEATH;
case SS_DIVINATION:
return SK_SS_DIVINATION;
case SS_FIRE:
return SK_SS_FIRE;
case SS_ICE:
return SK_SS_ICE;
case SS_GRAVITY:
return SK_SS_GRAVITY;
case SS_LIFE:
return SK_SS_LIFE;
case SS_MODIFICATION:
return SK_SS_MODIFICATION;
case SS_MENTAL:
return SK_SS_MENTAL;
case SS_SUMMONING:
return SK_SS_SUMMONING;
case SS_TRANSLOCATION:
return SK_SS_TRANSLOCATION;
case SS_WILD:
return SK_SS_WILD;
default:
break;
}
return SK_NONE;
}
// returns a number between min & max
// if blessed, always the max
// if cursed, always the min
int getspellduration(int min,int max,int blessed) {
int howlong;
switch (blessed) {
case B_BLESSED:
howlong = max; break;
case B_CURSED:
howlong = min; break;
default:
case B_UNCURSED:
howlong = rnd(min,max); break;
}
return howlong;
}
int getspelllevel(enum OBTYPE spellid) {
flag_t *f;
objecttype_t *ot;
int level = 0;
ot = findot(spellid);
if (!ot) {
return 0;
}
f = hasflag(ot->flags, F_SPELLLEVEL);
if (f) {
level = f->val[0];
}
return level;
}
int getspellmaxpower(enum OBTYPE spellid) {
flag_t *f;
objecttype_t *ot;
int max = 10;
ot = findot(spellid);
if (!ot) {
return 10;
}
f = hasflag(ot->flags, F_MAXPOWER);
if (f) {
max = f->val[0];
}
return max;
}
char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf) {
int power;
enum SPELLSCHOOL ss;
objecttype_t *ot;
ot = findot(spellid);
strcpy(buf, ot->name);
capitalise(buf);
power = getspellpower(lf, spellid);
ss = getspellschool(spellid);
if ((power > 1) && (ss != SS_ABILITY)) {
strcat(buf, " ");
strcat(buf, roman(power));
}
if (spellid == OT_S_DETECTLIFE) {
if (power >= 5) {
strcat(buf, "(enhanced)");
}
} else if (spellid == OT_S_CREATEMONSTER) {
if (power >= 7) {
strcat(buf, "(fullctrl)");
} else if (power >= 5) {
strcat(buf, "(semictrl)");
}
} else if (spellid == OT_S_KNOCK) {
if (power >= 7) {
strcat(buf, "(enhanced)");
}
} else if (spellid == OT_S_LIGHT) {
if (power >= 8) {
strcat(buf, "(perm,ctrl)");
} else if (power >= 3) {
strcat(buf, "(ctrl)");
}
} else if (spellid == OT_S_POLYMORPH) {
if (power >= 5) {
strcat(buf, "(ctrl)");
}
} else if (spellid == OT_S_TELEPORT) {
if (power >= 8) {
strcat(buf, "(fullctrl)");
} else if (power >= 5) {
strcat(buf, "(semictrl)");
}
}
return buf;
}
int getspellpower(lifeform_t *lf, enum OBTYPE spellid) {
int power = 0;
int statmod;
int spelllev;
enum SKILLLEVEL spellskill,schoolskill;
enum SPELLSCHOOL school;
int max = 10;
flag_t *f;
// first: can we WILL this to occur? if so, we might have a set
// spellpower
f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL);
if (f && strlen(f->text)) {
texttospellopts(f->text, &power, NULL, NULL);
if (power > 0) {
return power;
}
}
// statmod is -1 to 1
statmod = getstatmod(lf, A_IQ);
if (statmod >= 40) {
statmod = 1;
} else if (statmod <= -40) {
statmod = -1;
} else {
statmod = 0;
}
school = getspellschool(spellid);
spellskill = getskill(lf, SK_SPELLCASTING);
// dont need spellcasting skill for mental/allomancy
switch (school) {
case SS_ALLOMANCY:
case SS_MENTAL:
break;
default:
if (spellskill == PR_INEPT) {
return 0;
}
break;
}
// every 6 levels you get 1 more power
// ie. at level 30 you get +5 power
power = (lf->level/6) + statmod;
switch (school) {
case SS_ALLOMANCY:
case SS_MENTAL:
break;
default:
power += spellskill;
break;
}
spelllev = getspelllevel(spellid);
if (spelllev > 0) {
int divamt;
if ((school == SS_ALLOMANCY) || (school == SS_MENTAL)) {
divamt = (spelllev/2);
} else {
divamt = spelllev;
}
if (divamt > 0) {
power /= divamt;
}
}
// specialised school skill - apply this AFTER dividing by spell level
schoolskill = getskill(lf, getschoolskill(getspellschool(spellid)));
if (schoolskill != PR_INEPT) {
power += ((float)schoolskill * 1.5);
}
// enforce maximum
max = getspellmaxpower(spellid);
if (power > max) power = max;
return power;
}
enum SPELLSCHOOL getspellschool(enum OBTYPE spellid) {
flag_t *f;
objecttype_t *ot;
ot = findot(spellid);
if (!ot) {
return SS_NONE;
}
f = hasflag(ot->flags, F_SPELLSCHOOL);
if (f) {
return f->val[0];
}
return SS_NONE;
}
int getspellrange(enum OBTYPE spellid, int power) {
objecttype_t *st;
int range = UNLIMITED;
st = findot(spellid);
if (st) {
flag_t *f;
f = hasflag(st->flags, F_RANGE);
if (f) {
range = f->val[0];
}
}
switch (spellid) {
case OT_S_BURNINGWAVE:
// base range for this spell is 3
range += (power/3);
default:
break;
}
return range;
}
// magically propel an object into lf's hands.
// if it's too big it'll hit them!
void pullobto(object_t *o, lifeform_t *lf) {
char obname[BUFLEN];
char lfname[BUFLEN];
glyph_t *gl;
gl = getglyph(o);
anim(getoblocation(o), lf->cell, gl->ch, gl->colour);
getobname(o, obname, o->amt);
getlfname(lf, lfname);
if (isplayer(lf) || haslos(player, lf->cell)) {
msg("%s %s towards %s!", obname, (o->amt == 1) ? "flies" : "fly",
lfname);
}
// can we pick up the object?
if (hasflag(o->flags, F_NOPICKUP) || hasflag(o->flags, F_IMPASSABLE) || !canpickup(lf, o, ALL)) {
char buf[BUFLEN];
int dir;
cell_t *obloc,*newcell;
sprintf(buf, "a flying %s", noprefix(obname));
if (isplayer(lf) || haslos(player, lf->cell)) {
msg("%s %s into %s!", obname, (o->amt == 1) ? "slams" : "slam", lfname);
}
// where does it end up?
obloc = getoblocation(o);
dir = getdirtowards(lf->cell, obloc, NULL, B_FALSE, DT_COMPASS);
newcell = getcellindir(lf->cell, dir);
if (newcell) {
// move next to player
o = moveob(o, newcell->obpile, o->amt);
// lf takes damage
losehp(lf, getthrowdam(o), DT_PROJECTILE, NULL, buf);
} else {
o = moveob(o, lf->cell->obpile, o->amt);
// kill lf straight away!
lf->lastdamtype = DT_PROJECTILE;
setlastdam(lf, buf);
lf->hp = 0;
}
} else {
if (isplayer(lf) || haslos(player, lf->cell)) {
msg("%s %s %s.", lfname, isplayer(lf) ? "catch" : "catches", obname);
}
pickup(lf, o, o->amt, B_FALSE);
}
}
void stopallspells(lifeform_t *lf) {
flag_t *f,*nextf;
for (f = lf->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->id == F_BOOSTSPELL) {
stopspell(lf, f->val[0]);
}
}
}
void stopspell(lifeform_t *caster, enum OBTYPE spellid) {
flag_t *f,*nextf;
for (f = caster->flags->first ; f ; f = nextf) {
nextf = f->next;
if ((f->id == F_BOOSTSPELL) && (f->val[0] == spellid)) {
killflag(f);
} else if ((f->lifetime == FROMSPELL) && (f->obfrom == spellid)) {
killflag(f);
}
}
// remove any other specific effects based on spell type.
}
lifeform_t *validateabillf(lifeform_t *user, enum OBTYPE aid, lifeform_t **target) {
objecttype_t *ot;
int maxrange;
flag_t *f;
if (*target) {
return *target;
}
ot = findot(aid);
f = hasflag(ot->flags, F_RANGE);
if (f) {
maxrange = f->val[0];
} else {
maxrange = UNLIMITED;
}
// ask for a target lifeform
if (isplayer(user)) {
cell_t *where;
char buf[BUFLEN];
sprintf(buf, "Where will you target your %s?",ot->name);
where = askcoords(buf, TT_MONSTER);
if (where) {
if (!haslf(where)) {
msg("There is nobody there!");
return NULL;
}
if (maxrange != UNLIMITED) {
if (getcelldist(user->cell,where) > maxrange) {
msg("That location is out of range!");
return NULL;
}
}
*target = haslf(where);
} else {
msg("Cancelled.");
return NULL;
}
} else {
// TODO: fill in monster code?
return NULL;
}
return *target;
}
cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, int needlos, enum LOFTYPE needlof, enum OBTYPE spellid, int power) {
int maxrange = UNLIMITED;
int done = B_FALSE;
cell_t *where = NULL;
maxrange = getspellrange(spellid, power);
if (*targcell) where = *targcell;
while (!done) {
if (where) {
cell_t *newwhere = NULL;
// validate it
if (where && needlos && !haslos(caster, where) && (where != caster->cell)) {
msg("You cannot see there!"); more();
where = NULL;
}
// line of fire interrupted?
if (where && needlof && !haslof(caster->cell, where, needlof, &newwhere)) {
if (newwhere) {
if (isplayer(caster)) {
// warn!
int ch;
ch = askchar("Your have no clear line of fire - really target here?","yn","n", B_TRUE);
if (ch == 'y') {
where = newwhere;
} else {
where = NULL;
}
}
} else {
where = NULL;
}
}
if (where && (maxrange != UNLIMITED) && (getcelldist(caster->cell, where) > maxrange)) {
// out of range
msg("Too far away - max range is %d.",maxrange); more();
where = NULL;
}
if (where && isplayer(caster) && (where == caster->cell)) {
// warn before targetting yourself!
if (getiqname(getattr(caster, A_IQ), NULL) >= IQ_AVERAGE) {
objecttype_t *sp;
sp = findot(spellid);
if (sp) {
if (hasflagval(sp->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL) ||
hasflagval(sp->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL)) {
int ch;
ch = askchar("Really target yourself","yn","n", B_TRUE);
if (ch != 'y') {
where = NULL;
}
}
}
}
}
// still got a target?
if (where) {
*targcell = where;
done = B_TRUE;
}
} else {
// ask for a target cell
if (isplayer(caster)) {
char buf[BUFLEN];
if (maxrange == UNLIMITED) {
objecttype_t *ot;
ot = findot(spellid);
sprintf(buf, "Where will you target your %s?", ot->name);
} else {
sprintf(buf, "Where will you target your spell [max range %d]?",maxrange);
}
where = askcoords(buf, targtype);
if (!where) {
int ch;
ch = askchar("Abandon your spell?","yn","n", B_TRUE);
if (ch == 'y') {
return NULL;
}
}
} else {
// TODO: fill in monster code?
fizzle(caster);
return NULL;
}
}
}
if (*targcell && isplayer(caster)) {
flag_t *f;
// update last cmd
f = hasflag(caster->flags, F_LASTCMD);
if (f && (f->text[0] == 'm')) {
f->val[0] = (*targcell)->x;
f->val[1] = (*targcell)->y;
}
}
return *targcell;
}
/*
// returns the amount if IQ needed to learn this spell
// required intelligence is 9 + spelllevel
// ie. spelllev1 needs 9+ intelligence
// ie. spelllev7 needs 16+ intelligence
int getiqreq(enum OBTYPE oid) {
flag_t *f;
objecttype_t *ot;
int iqreq = 0;
ot = findot(oid);
if (!ot) {
return 0;
}
f = hasflag(ot->flags, F_SPELLLEVEL);
if (f) {
iqreq = 9 + f->val[0];
}
return iqreq;
}
*/
int getmpcost(lifeform_t *lf, enum OBTYPE oid) {
flag_t *f;
objecttype_t *ot;
int cost = 0;
ot = findot(oid);
if (!ot) {
return 0;
}
if (lf) {
if (lfhasflagval(lf, F_BOOSTSPELL, oid, NA, NA, NULL)) {
return 0; // ie. deactivating it
}
if (lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL)) {
return 0; // ie. deactivating it
}
}
f = hasflag(ot->flags, F_MPCOST);
if (f) {
cost = f->val[0];
} else {
f = hasflag(ot->flags, F_SPELLLEVEL);
if (f) {
cost = f->val[0] * f->val[0];
}
}
return cost;
}
lifeform_t *validatespelllf(lifeform_t *caster, lifeform_t **target) {
if (*target) {
return *target;
}
// ask for a target lifeform
if (isplayer(caster)) {
cell_t *where;
char buf[BUFLEN];
sprintf(buf, "Where will you target your spell?");
where = askcoords(buf, TT_MONSTER);
if (where && haslos(caster, where) && haslf(where)) {
*target = haslf(where);
} else {
fizzle(caster);
return NULL;
}
} else {
// TODO: fill in monster code?
fizzle(caster);
return NULL;
}
return *target;
}