7159 lines
184 KiB
C
7159 lines
184 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 race_t *firstrace, *lastrace;
|
|
|
|
extern int needredraw;
|
|
|
|
extern prompt_t prompt;
|
|
|
|
extern WINDOW *msgwin;
|
|
|
|
extern job_t *firstjob;
|
|
extern objecttype_t *objecttype;
|
|
|
|
extern map_t *firstmap;
|
|
|
|
extern object_t *retobs[MAXPILEOBS+1];
|
|
extern int retobscount[MAXPILEOBS+1];
|
|
extern int nretobs;
|
|
|
|
extern enum ERROR reason;
|
|
|
|
extern int needredraw, statdirty;
|
|
|
|
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, range = 0;
|
|
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, &range);
|
|
}
|
|
|
|
// 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_CHARGE) {
|
|
cell_t *adjcell = NULL,*origcell;
|
|
char targetname[BUFLEN];
|
|
int i;
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell;
|
|
|
|
if (isimmobile(user)) {
|
|
if (isplayer(user)) msg("You can't move!");
|
|
return B_TRUE;
|
|
}
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't charge while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!range) {
|
|
// get max range - based on speed.
|
|
range = (int) (((float)SP_NORMAL / (float)getmovespeed(user)) * 2.5);
|
|
if (range <= 1) {
|
|
if (isplayer(user)) msg("You are too slow to charge!");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
if (!targcell) {
|
|
if (isplayer(user)) {
|
|
sprintf(buf, "Charge who (max range %d)?",range);
|
|
// TODO: ask for direction
|
|
targcell = askcoords(buf, TT_MONSTER, user, range, LOF_NEED, B_TRUE);
|
|
if (!targcell) {
|
|
msg("Cancelled.");
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
return B_FALSE;
|
|
}
|
|
|
|
}
|
|
if (getcelldist(user->cell, targcell) > range) {
|
|
if (isplayer(user)) msg("You can't charge 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)) {
|
|
char verb[BUFLEN];
|
|
// special case: sometimes we call this a 'leap'
|
|
switch (user->race->id) {
|
|
case R_CREEPINGCLAW:
|
|
case R_LEECH:
|
|
case R_SNAKETREE:
|
|
case R_PIRANHAKING:
|
|
strcpy(verb, "leap");
|
|
break;
|
|
default:
|
|
strcpy(verb, "charge");
|
|
break;
|
|
}
|
|
msg("%s %s%s towards %s!",username,verb, isplayer(user) ? "" : "s",targetname);
|
|
needredraw = B_TRUE;
|
|
drawlevelfor(player);
|
|
redraw();
|
|
|
|
//if (!isplayer(user)) {
|
|
more();
|
|
//}
|
|
}
|
|
|
|
// special cases....
|
|
if (user->race->id == R_CREEPINGCLAW) {
|
|
// creeping claw automatically grabs target
|
|
addflag(user->flags, F_GRABBING, target->id, NA, NA, NULL);
|
|
addflag(target->flags, F_GRABBEDBY, user->id, NA, NA, NULL);
|
|
} else {
|
|
// attack
|
|
attackcell(user, targcell);
|
|
}
|
|
} else if (abilid == OT_A_COOK) {
|
|
object_t *water,*o;
|
|
race_t *r;
|
|
char buf[BUFLEN];
|
|
if (!isplayer(user)) {
|
|
return B_TRUE;
|
|
}
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't cook while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
water = hasob(user->pack, OT_POT_WATER);
|
|
if (!water || !isknown(water)) {
|
|
msg("You need some water before you can cook.");
|
|
return B_TRUE;
|
|
|
|
}
|
|
o = doaskobject(user->pack, "What will you cook?", NULL, B_FALSE, B_FALSE, AO_EDIBLE, F_CORPSEOF);
|
|
if (!o) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = hasflag(o->flags, F_CORPSEOF);
|
|
if (f) {
|
|
r = findrace(f->val[0]);
|
|
}
|
|
|
|
taketime(user, getactspeed(user));
|
|
|
|
// remove object and water
|
|
removeob(o,ALL);
|
|
removeob(water,ALL);
|
|
// give food
|
|
if (getskill(user, SK_COOKING >= PR_EXPERT)) {
|
|
o = addob(user->pack, "jerky");
|
|
} else {
|
|
o = addob(user->pack, "stew");
|
|
}
|
|
|
|
if (o) {
|
|
if (r) {
|
|
addflag(o->flags, F_LINKRACE, r->id, NA, NA, NULL);
|
|
}
|
|
f = hasflag(o->flags, F_EDIBLE);
|
|
if (f) {
|
|
f->val[1] = getskill(user, SK_COOKING)*20;
|
|
}
|
|
|
|
getobname(o, buf, o->amt);
|
|
msgnocap("%c - %s", o->letter, buf);
|
|
} else {
|
|
msg("Your cooking attempt fails (maybe your pack was too full?).");
|
|
}
|
|
} else if (abilid == OT_A_DARKWALK) {
|
|
if (range <= 0) range = UNLIMITED;
|
|
|
|
if (islit(user->cell)) {
|
|
if (isplayer(user)) msg("It is too light to darkwalk!");
|
|
return TRUE;
|
|
}
|
|
|
|
if (!targcell) {
|
|
if (isplayer(user)) {
|
|
if (range != UNLIMITED) {
|
|
sprintf(buf, "Darkwalk to where (max range %d)?", range);
|
|
} else {
|
|
sprintf(buf, "Darkwalk to where?");
|
|
}
|
|
targcell = askcoords(buf, TT_NONE, user, range, LOF_DONTNEED, B_FALSE);
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!targcell) {
|
|
msg("Cancelled.");
|
|
return TRUE;
|
|
} else if (!cellwalkable(user, targcell, NULL)) {
|
|
msg("There is something in your way.");
|
|
return TRUE;
|
|
} else if (!haslos(user, targcell)) {
|
|
msg("You cannot see there!");
|
|
return TRUE;
|
|
} else if (islit(targcell)) {
|
|
msg("It is too light to darkwalk there!");
|
|
return TRUE;
|
|
} else if ((range != UNLIMITED) && (getcelldist(user->cell, targcell) > range)) {
|
|
msg("You cannot darkwalk that far!");
|
|
return TRUE;
|
|
}
|
|
|
|
taketime(user, getactspeed(user));
|
|
|
|
if (isplayer(user)) {
|
|
msg("You step through the shadows.");
|
|
} else if (cansee(player, user)) {
|
|
msg("%s steps through the shadows.",username);
|
|
}
|
|
moveto(user, targcell, B_TRUE, B_TRUE);
|
|
} else if (abilid == OT_A_DISARM) {
|
|
int dir;
|
|
object_t *o,*trapob = NULL;
|
|
flag_t *trapflag = NULL;
|
|
char buf[BUFLEN];
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't disarm traps while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dirch;
|
|
dirch = askchar("Disarm trap in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE);
|
|
if (dirch == '-') {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE ;
|
|
}
|
|
dir = chartodir(dirch);
|
|
if (dir == D_NONE) {
|
|
targcell = user->cell;
|
|
} else {
|
|
targcell = getcellindir(user->cell, dir);
|
|
}
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
if (isplayer(user)) {
|
|
char inwayname[BUFLEN];
|
|
getlfname(targcell->lf, inwayname);
|
|
msg("%s is in your way!",inwayname);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// trap there?
|
|
for (o = targcell->obpile->first ; o ; o = o->next) {
|
|
if (hasflag(o->flags, F_TRAP) && !hasflag(o->flags, F_SECRET)) {
|
|
trapob = o;
|
|
trapflag = hasflag(trapob->flags, F_TRAP);
|
|
break;
|
|
}
|
|
}
|
|
if (!trapob) {
|
|
if (isplayer(user)) msg("You can't see any traps there!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
getobname(trapob, buf, 1);
|
|
|
|
if (isplayer(user) || cansee(player, user)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
|
|
// taketime
|
|
taketime(user, getactspeed(user));
|
|
|
|
// move player there...
|
|
movelf(user, targcell);
|
|
|
|
// try to disarm it
|
|
if (skillcheck(user, SC_DISARM, trapflag->val[0], 0)) {
|
|
if (isplayer(user)) {
|
|
msg("You disarm %s.",buf);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s disarms %s.",username, buf);
|
|
}
|
|
removeob(trapob, trapob->amt);
|
|
|
|
if (isplayer(user) && hasjob(user, J_ROGUE)) {
|
|
gainxp(user, trapflag->val[0]);
|
|
}
|
|
} else {
|
|
// failed. another check to see if it goes off
|
|
if ((trapflag->val[1] == B_TRUE) && !skillcheck(user, SC_DISARM, trapflag->val[0], 0)) {
|
|
if (isplayer(user)) {
|
|
msg("Oops - you trigger %s!",buf);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s triggers %s!",username, buf);
|
|
}
|
|
trapeffects(trapob, trapob->type->id, user);
|
|
} else {
|
|
if (isplayer(user)) {
|
|
msg("You fail to disarm %s.",buf);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s fails to disarm %s.",username, buf);
|
|
}
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_FLURRY) {
|
|
int dir;
|
|
int dirch;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't make an attack flurry traps while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!isdualweilding(user)) {
|
|
if (isplayer(user)) msg("You need two be dual-weilding to perform an attack flurry!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for direction
|
|
if (targcell) {
|
|
dir = getdirtowards(user->cell, targcell, NULL, B_FALSE, DT_ORTH);
|
|
} else {
|
|
dirch = askchar("Flurry in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE);
|
|
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);
|
|
|
|
// announce
|
|
if (!isplayer(user) && cansee(player, user)) {
|
|
msg("%s performs a flurry of fast attacks!", username);
|
|
}
|
|
|
|
// push them back
|
|
knockback(target, dir, 1, user, 5);
|
|
|
|
// if we succeeded in pushing them...
|
|
if (target->cell != targcell) {
|
|
movelf(user, targcell);
|
|
}
|
|
|
|
// now attack them
|
|
attackcell(user, target->cell);
|
|
} 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;
|
|
}
|
|
|
|
taketime(user, getactspeed(user));
|
|
|
|
getlfname(target, targetname);
|
|
|
|
if (lfhasflag(target, F_NONCORPOREAL)) {
|
|
if (isplayer(user)) {
|
|
msg("You grab at %s%s insubstantial body.", targetname, getpossessive(targetname));
|
|
} else if (cansee(player, user)) {
|
|
msg("%s grabs at %s%s insubstantial body.",
|
|
username, targetname, getpossessive(targetname));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// victim gets a skilcheck to avoid being grabbed
|
|
if (skillcheck(target, SC_DODGE, getattr(user, A_DEX)+11, 0)) {
|
|
if (cansee(player, user)) {
|
|
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);
|
|
}
|
|
}
|
|
} 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;
|
|
|
|
// fixed damage?
|
|
if (strlen(damstr)) {
|
|
dam = roll(damstr);
|
|
} else {
|
|
// 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, is(target));
|
|
}
|
|
|
|
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 (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't jump while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!targcell) {
|
|
sprintf(buf, "Jump where (max distance 2)?");
|
|
while (!targcell) {
|
|
// ask where
|
|
targcell = askcoords(buf, TT_NONE, user, 2, LOF_DONTNEED, B_TRUE);
|
|
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_RAGE) {
|
|
int howlong;
|
|
f = lfhasflag(user, F_RAGE);
|
|
if (f) {
|
|
if (isplayer(user)) {
|
|
msg("You are already enraged!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
if (lfhasflag(user, F_TIRED)) {
|
|
if (isplayer(user)) {
|
|
msg("You are too tired to enter a rage right now.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
howlong = 10;
|
|
addtempflag(user->flags, F_RAGE, B_TRUE, NA, NA, NULL, howlong);
|
|
} else if (abilid == OT_A_SPRINT) {
|
|
int howlong;
|
|
int slev;
|
|
flag_t *f;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't sprint while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
if (isburdened(user)) {
|
|
if (isplayer(user)) {
|
|
msg("You cannot sprint while burdened.");
|
|
}
|
|
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_SUCKBLOOD) {
|
|
int dam = 0;
|
|
|
|
f = lfhasflag(user, F_ATTACHEDTO);
|
|
if (!f) {
|
|
if (isplayer(user)) msg("You need to attach to someone before using this ability.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// announce
|
|
if (cansee(player, target)) {
|
|
msg("%s suck%s blood from %s!", username, isplayer(user) ? "" : "s", targetname);
|
|
}
|
|
|
|
// fixed damage?
|
|
if (strlen(damstr)) {
|
|
dam = roll(damstr);
|
|
} else {
|
|
dam = 1;
|
|
}
|
|
losehp(target, dam, DT_DIRECT, user, killername);
|
|
|
|
taketime(user, getactspeed(user));
|
|
|
|
// special case
|
|
if (!isplayer(user)) {
|
|
if ((user->race->id == R_STIRGE) || (user->race->id == R_LEECH)) {
|
|
int satedat = 8;
|
|
int num;
|
|
num = modcounter(user->flags, dam);
|
|
|
|
switch (user->race->id) {
|
|
default:
|
|
case R_STIRGE:
|
|
satedat = 8;
|
|
break;
|
|
case R_LEECH:
|
|
satedat = 12;
|
|
break;
|
|
}
|
|
|
|
if (num >= satedat) {
|
|
// sated.
|
|
killflagsofid(user->flags, F_ATTACHEDTO);
|
|
makepeaceful(user);
|
|
addflag(user->flags, F_FLEEONDAM, B_TRUE, NA, NA, NULL);
|
|
addflag(user->flags, F_DIESPLATTER, 3, NA, NA, "splash of blood");
|
|
// counter will keep ticking up.
|
|
// once it gets past next threshold, monster will go to sleep.
|
|
modcounter(user->flags, rnd(1,4));
|
|
}
|
|
}
|
|
}
|
|
} else 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, "Swoop who (max range %d)?",srange);
|
|
// TODO: ask for direction
|
|
targcell = askcoords(buf, TT_MONSTER, user, srange, LOF_NEED, B_TRUE);
|
|
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_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], B_TRUE);
|
|
|
|
} 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, user, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (where && where->lf) {
|
|
debug(where->lf);
|
|
}
|
|
} else if (abilid == OT_A_EMPLOY) {
|
|
cell_t *where;
|
|
where = askcoords("Assign job to who?", TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
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, user, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
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;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You lack the stability for a heavy blow while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
wep = getweapon(user);
|
|
if (!wep || !ismeleeweapon(wep) || (getobunitweight(wep) < 3)) {
|
|
if (isplayer(user)) msg("You need a heavy 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_WARCRY) {
|
|
// announce
|
|
if (isplayer(user)) {
|
|
msg("You shout a blood-curdling war cry!");
|
|
}
|
|
makenoise(user, N_WARCRY);
|
|
|
|
// take a lot of time, so that there is a danger in just
|
|
// using it all the time.
|
|
taketime(user, getactspeed(user)*2);
|
|
|
|
// all in range must pass a morale check or flee
|
|
for (target = user->cell->map->lf ; target ; target = target->next) {
|
|
if ((target != user) && cansee(target, user) && areenemies(target, user)) {
|
|
if (canhear(target, user->cell)) {
|
|
scare(target, user, rnd(5,10), 0);
|
|
}
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_HURRICANESTRIKE) {
|
|
int dir;
|
|
cell_t *c;
|
|
flag_t *f,*f2;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You cannot do that while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// take time
|
|
// - NOTE: purposely using action speed, not weapon speed.
|
|
taketime(user, getactspeed(user));
|
|
|
|
// now don't take any more time for the actual attacks.
|
|
f = addflag(user->flags, F_NOTIME, B_TRUE, NA, NA, NULL);
|
|
// lower accuracy
|
|
f2 = addflag(user->flags, F_ACCURACYMOD, -20, NA, NA, NULL);
|
|
// attack all adjacent enemies
|
|
for (dir = DC_N; dir <= DC_NW; dir++) {
|
|
c = getcellindir(user->cell, dir);
|
|
if (c) {
|
|
attackcell(user, c);
|
|
}
|
|
}
|
|
// remove temporary flags
|
|
killflag(f);
|
|
killflag(f2);
|
|
} else if (abilid == OT_A_HIDE) {
|
|
int penalty = 0;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't hide while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (lfhasflag(user, F_HIDING)) {
|
|
if (isplayer(user)) msg("You are already hiding!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
penalty = gethidemodifier(user);
|
|
if (reason == E_IMPOSSIBLE) {
|
|
if (isplayer(user)) msg("You can't hide - there are monsters around!");
|
|
return B_TRUE;
|
|
}
|
|
// start hiding
|
|
addflag(user->flags, F_HIDING, penalty, NA, NA, NULL);
|
|
|
|
taketime(user, getactspeed(user));
|
|
} 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 (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("That wouldn't be a good idea while swimming.");
|
|
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_LORE_ARCANA);
|
|
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);
|
|
|
|
if (caster) {
|
|
getlfname(caster, castername);
|
|
} else {
|
|
strcpy(castername, "something");
|
|
}
|
|
|
|
// default to unseen
|
|
if (seenbyplayer) *seenbyplayer = B_FALSE;
|
|
|
|
|
|
if (caster) {
|
|
if (hasflag(sp->flags, F_ONGOING)) {
|
|
if (lfhasflagval(caster, F_BOOSTSPELL, spellid, NA, NA, NULL)) {
|
|
// cancel it.
|
|
stopspell(caster, spellid);
|
|
return B_FALSE;
|
|
} else {
|
|
objecttype_t *sp;
|
|
int cost;
|
|
cost = getmpcost(caster, spellid);
|
|
sp = findot(spellid);
|
|
|
|
if (sp && hasflag(sp->flags, F_VARPOWER)) {
|
|
cost *= power;
|
|
}
|
|
addflag(caster->flags, F_BOOSTSPELL, spellid, cost, power, 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;
|
|
cell_t *firstobcell = NULL;
|
|
cell_t *nextc;
|
|
|
|
if (targcell) {
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS);
|
|
} else {
|
|
int dirch;
|
|
dirch = askchar("Airblast in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE);
|
|
if ((dirch == '.') || (dir == '-')) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
dir = chartodir(dirch);
|
|
}
|
|
|
|
// keep going that dir until the next cell is a wall/lf/impassable ob
|
|
targcell = caster->cell;
|
|
nextc = getcellindir(targcell, dir);
|
|
while (nextc && cellwalkable(NULL, nextc, NULL)) {
|
|
targcell = nextc;
|
|
if (!firstobcell) {
|
|
// obs here?
|
|
for (o = targcell->obpile->first ;o ; o = o->next) {
|
|
if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) {
|
|
firstobcell = targcell;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
nextc = getcellindir(targcell, dir);
|
|
}
|
|
|
|
if (cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("%s emit%s a powerful blast of air!", castername, isplayer(caster) ? "" : "s");
|
|
}
|
|
// if it was a lifeform which stopped us...
|
|
if (nextc->lf) {
|
|
targcell = nextc;
|
|
target = targcell->lf;
|
|
knockback(target, dir, power, caster, 0);
|
|
} else if (firstobcell) {
|
|
// objects
|
|
for (o = firstobcell->obpile->first ;o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) {
|
|
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_BARKSKIN) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
f = addtempflag(caster->flags, F_MAGICARMOUR, power*2, NA, NA, "skin of bark", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
f = addtempflag(caster->flags, F_DTVULN, DT_FIRE, NA, NA, "2d4", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_BLADEBURN) {
|
|
object_t *wep;
|
|
enum DAMTYPE dt;
|
|
if (!validatespellcell(caster, &targcell, TT_PLAYER | TT_ALLY, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// does caster have a bladed weapon?
|
|
wep = getweapon(target);
|
|
if (!wep) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
dt = getdamtype(wep);
|
|
if ((dt != DT_PIERCE) && (dt != DT_SLASH) && (dt != DT_CHOP)) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
addtempflag(wep->flags, F_ONFIRE, B_TRUE, NA, NA, "1d6", 10 + power*3);
|
|
} else if (spellid == OT_S_BLINDNESS) {
|
|
int failed = B_FALSE;
|
|
// ask for target
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
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, 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);
|
|
|
|
redrawpause();
|
|
// 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);
|
|
}
|
|
}
|
|
redrawresume();
|
|
// now burn things
|
|
for (i = 1; i < nretcell; i++) {
|
|
cell_t *c;
|
|
c = retcell[i];
|
|
|
|
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_CALLLIGHTNING) {
|
|
int failed = B_FALSE;
|
|
// ask for a target cell
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
target = targcell->lf;
|
|
if (target) {
|
|
char targname[BUFLEN];
|
|
|
|
getlfname(target, targname);
|
|
|
|
if (haslos(player, targcell)) {
|
|
animsky(targcell, '}', C_WHITE);
|
|
msg("%s %s struck by a bolt of lightning!",targname,is(target));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// outdoors?
|
|
if (isoutdoors(target->cell->map)) {
|
|
losehp(target, rolldie(4,6), DT_ELECTRIC, caster, "a bolt of lightning");
|
|
} else {
|
|
losehp(target, rolldie(3,6), DT_ELECTRIC, caster, "a bolt of lightning");
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_CALLWIND) {
|
|
int failed = B_FALSE;
|
|
float maxweight;
|
|
|
|
// if no target object...
|
|
if (!targob) {
|
|
// ask for a target cell (to take objects from)
|
|
if (!validatespellcell(caster, &targcell, TT_OBJECT, spellid, power)) return B_TRUE;
|
|
|
|
if (targcell->obpile->first) {
|
|
// select object from cell...
|
|
targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE);
|
|
if (!targob) {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!failed) {
|
|
targcell = caster->cell;
|
|
|
|
// 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;
|
|
}
|
|
|
|
pullobto(targob, caster);
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_CALMANIMALS) {
|
|
int i;
|
|
int powerleft;
|
|
int donesomething = B_FALSE;
|
|
cell_t *c;
|
|
powerleft = rolldie(power+1, 4);
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
c = caster->los[i];
|
|
if (c->lf && (c->lf->race->raceclass->id == RC_ANIMAL) && (gethitdice(c->lf) <= powerleft)) {
|
|
if (lfhasflag(c->lf, F_HOSTILE)) {
|
|
powerleft -= gethitdice(c->lf);
|
|
makepeaceful(c->lf);
|
|
|
|
// druids get 25% of the monster's XP value for calming it.
|
|
if (hasjob(caster, J_DRUID)) {
|
|
if (getallegiance(caster) == AL_FRIENDLY) {
|
|
awardxpfor(c->lf, 25);
|
|
}
|
|
}
|
|
|
|
if (cansee(player, c->lf)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
donesomething++;
|
|
}
|
|
}
|
|
if (powerleft <= 0) break;
|
|
}
|
|
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_CALMINGSCENT) {
|
|
// just announce
|
|
if (isplayer(caster)) {
|
|
msg("A relaxing aroma surrounds you.");
|
|
}
|
|
} else if (spellid == OT_S_CHAINLIGHTNING) {
|
|
cell_t *hitcell[MAXRETCELLS]; // all cells which have been hit in total
|
|
cell_t *arccell[MAXRETCELLS]; // cells hit this time
|
|
cell_t *arccell2[MAXRETCELLS]; // cells hit this time
|
|
int nhitcells = 0, narccells,narccells2;
|
|
int i;
|
|
int range;
|
|
int nsides = 6;
|
|
|
|
range = getspellrange(spellid, power);
|
|
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
// create a line of fire towards the target cell
|
|
animline(caster->cell, targcell, B_FALSE, '/', '\\', C_WHITE);
|
|
|
|
if (cansee(player, caster)) {
|
|
msg("%s shoot%s a bolt of electricity!",castername, isplayer(caster) ? "" : "s");
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
arccell[0] = targcell;
|
|
narccells = 1;
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
|
|
nhitcells = 0;
|
|
while (narccells) {
|
|
int stillseen = B_FALSE;
|
|
// everyone takes damage, and mark cells as hit
|
|
for (i = 0; i < narccells; i++) {
|
|
losehp(arccell[i]->lf, rolldie(2,nsides), DT_ELECTRIC, caster, "an electricity bolt");
|
|
hitcell[nhitcells++] = arccell[i];
|
|
|
|
if (haslos(player, arccell[i])) {
|
|
stillseen = B_TRUE;
|
|
needredraw = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
|
|
// now range gets halved and damage lowered
|
|
range /= 2;
|
|
if (range <= 0) break;
|
|
if (--nsides <= 0) break; // ie. max of 5 arcs
|
|
|
|
// now arc to other cells with lfs still within range
|
|
narccells2 = 0;
|
|
for (i = 0; i < narccells; i++) {
|
|
int x,y;
|
|
cell_t *c;
|
|
for (y = arccell[i]->y - range ; y <= arccell[i]->y + range; y++) {
|
|
for (x = arccell[i]->x - range ; x <= arccell[i]->x + range; x++) {
|
|
c = getcellat(targcell->map, x, y);
|
|
if (c && c->lf && (c->lf != caster)
|
|
&& haslof(arccell[i], c, LOF_NEED, NULL)) {
|
|
int n,found = B_FALSE;
|
|
// if this cell hasn't been hit...
|
|
for (n = 0; n < nhitcells; n++) {
|
|
if (hitcell[n] == c) {
|
|
found = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
// will arc to here
|
|
arccell2[narccells2++] = c;
|
|
animline(arccell[i], c, B_FALSE, '/', '\\', C_WHITE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// replace arccells
|
|
for (i = 0; i < narccells2; i++) {
|
|
arccell[i] = arccell2[i];
|
|
}
|
|
narccells = narccells2;
|
|
|
|
if (narccells && stillseen) {
|
|
msg("The electricity arcs!");
|
|
}
|
|
} // end while narccells
|
|
} else if (spellid == OT_S_CLOUDKILL) {
|
|
int radius;
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
if (targcell->type->solid) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
radius = power/3;
|
|
if (radius < 1) radius = 1;
|
|
|
|
addobburst(targcell, radius, DT_COMPASS, "cloud of poison gas", caster, LOF_WALLSTOP);
|
|
|
|
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, spellid, power)) return B_TRUE;
|
|
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
// only animals/humanoids
|
|
if (getraceclass(target) != getraceclass(caster)) {
|
|
if (isplayer(caster)) {
|
|
msg("%s%s mind is too alien for you to charm.",targetname,getpossessive(targetname));
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (!ischarmable(target)) {
|
|
if (isplayer(caster)) {
|
|
switch (reason) {
|
|
case E_DRUNK:
|
|
msg("%s%s mind is too alcohol-impaired for you to charm.",targetname,getpossessive(targetname));
|
|
break;
|
|
case E_LOWIQ:
|
|
msg("%s%s intellect is too simple for you to charm.",targetname,getpossessive(targetname));
|
|
break;
|
|
case E_UNDEAD:
|
|
msg("The undead are immune to charming.");
|
|
break;
|
|
default:
|
|
msg("You cannot charm %s.", targetname);
|
|
break;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (getallegiance(caster) == AL_PEACEFUL) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
if (ispetof(target, 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;
|
|
}
|
|
// they get angry!
|
|
if (!isplayer(target) && cansee(target, caster)) {
|
|
fightback(target, caster);
|
|
}
|
|
} 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);
|
|
addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong);
|
|
}
|
|
} else if (spellid == OT_S_CHARMANIMAL) {
|
|
char targetname[BUFLEN];
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
// only animals
|
|
if (getraceclass(target) != RC_ANIMAL) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (!ischarmable(target)) {
|
|
if (isplayer(caster)) {
|
|
switch (reason) {
|
|
case E_DRUNK:
|
|
msg("%s%s mind is too alcohol-impaired for you to charm.",targetname,getpossessive(targetname));
|
|
break;
|
|
case E_LOWIQ:
|
|
msg("%s%s intellect is too simple for you to charm.",targetname,getpossessive(targetname));
|
|
break;
|
|
case E_UNDEAD:
|
|
msg("The undead are immune to charming.");
|
|
break;
|
|
default:
|
|
msg("You cannot charm %s.", targetname);
|
|
break;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (getallegiance(caster) == AL_PEACEFUL) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
if (ispetof(target, caster)) {
|
|
if (isplayer(caster)) {
|
|
msg("%s is already allied with you!",targetname);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// no saving throw, just depends on hit dice.
|
|
// too powerful?
|
|
if (gethitdice(target) <= power) {
|
|
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);
|
|
addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong);
|
|
} else {
|
|
if (isplayer(caster) || cansee(player, target)) {
|
|
msg("%s resists.",targetname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
// they get angry!
|
|
if (!isplayer(target) && cansee(target, caster)) {
|
|
fightback(target, caster);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_CHILL) {
|
|
char lfname[BUFLEN];
|
|
int exposedlimbs,dam;
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
getlfname(target, lfname);
|
|
|
|
// how many body parts are impacted?
|
|
exposedlimbs = 0;
|
|
if (!getouterequippedob(target, BP_HEAD)) exposedlimbs += 1;
|
|
if (!getouterequippedob(target, BP_SHOULDERS)) exposedlimbs += 1;
|
|
if (!getouterequippedob(target, BP_BODY)) exposedlimbs += 2;
|
|
if (!getouterequippedob(target, BP_HANDS)) exposedlimbs += 1;
|
|
if (!getouterequippedob(target, BP_LEGS)) exposedlimbs += 2;
|
|
if (!getouterequippedob(target, BP_FEET)) exposedlimbs += 1;
|
|
|
|
dam = rolldie(exposedlimbs, 3);
|
|
|
|
if (isplayer(target)) {
|
|
if (isimmuneto(target->flags, DT_COLD)) {
|
|
msg("You feel mildly chilly.");
|
|
} else {
|
|
msg("You feel extremely cold!");
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
if (isimmuneto(target->flags, DT_COLD)) {
|
|
msg("%s looks mildly chilly.", lfname);
|
|
} else {
|
|
msg("%s looks extremely cold!", lfname);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// target takes magical damage
|
|
// always hit
|
|
if (!isimmuneto(target->flags, DT_COLD)) {
|
|
losehp(target, dam, DT_COLD, caster, "a chill spell");
|
|
if (!skillcheck(target, SC_CON, 20+(exposedlimbs*3), 0)) {
|
|
poison(target, 20+(power*3), P_COLD, 0, "a chill spell");
|
|
}
|
|
}
|
|
} 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) &&
|
|
haslof(caster->cell, targcell, B_FALSE, NULL)) {
|
|
if (targcell->lf) {
|
|
if (targcell->lf != caster) {
|
|
char lfname[BUFLEN];
|
|
// automatic hit
|
|
getlfname(targcell->lf, lfname);
|
|
if (haslos(caster, targcell)) {
|
|
msg("%s %s chilled!",lfname,is(targcell->lf));
|
|
}
|
|
losehp(targcell->lf, rolldie(1,8)+3, DT_COLD, caster, "a burst of coldness");
|
|
}
|
|
} else {
|
|
// noone there, hit objects.
|
|
damageallobs(NULL, targcell->obpile, 0, DT_COLD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_COLDRAY) {
|
|
char lfname[BUFLEN];
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell,i;
|
|
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
// animation
|
|
anim(caster->cell, targcell, '}', C_GREY);
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
msg("%s shoot%s a ray of coldness.",castername,isplayer(caster) ? "" : "s");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y,
|
|
targcell->x, targcell->y, retcell, &nretcell);
|
|
|
|
for (i = 1; i < nretcell; i++) {
|
|
target = haslf(retcell[i]);
|
|
if (target) {
|
|
getlfname(target, lfname);
|
|
// target takes magical damage
|
|
if (skillcheck(target, SC_DODGE, 20 + (power*2), 0)) {
|
|
// miss
|
|
if (cansee(player, target)) {
|
|
msg("A ray of coldness misses %s.",lfname);
|
|
}
|
|
} else {
|
|
// hit
|
|
if (cansee(player, target)) {
|
|
msg("A ray of coldness ray hits %s.",lfname);
|
|
}
|
|
losehp(target, rnd(2,5), DT_COLD, caster, "a blast of coldness");
|
|
// ray stops here.
|
|
break;
|
|
}
|
|
} else {
|
|
damageallobs(NULL, retcell[i]->obpile, 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) && isplayer(caster)) {
|
|
// control location
|
|
if (!validatespellcell(caster, &targcell, TT_NONE, 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) && isplayer(caster)) {
|
|
// 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, B_FALSE, NULL);
|
|
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
|
|
killflagsofid(newlf->flags, F_XPVAL);
|
|
addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
rv = B_FALSE;
|
|
} else {
|
|
// didn't work for some reason
|
|
if (isplayer(caster)) {
|
|
nothinghappens();
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_CUREPOISON) {
|
|
if (!validatespellcell(caster, &targcell,TT_ALLY, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
killflagsofid(target->flags, F_POISONED);
|
|
|
|
if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = 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 );
|
|
}
|
|
calclight(targcell->map);
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
} 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_DIG) {
|
|
int numseen = 0;
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell,i;
|
|
int ndigs;
|
|
|
|
// don't need line of fire OR sight!
|
|
if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power)) return B_TRUE;
|
|
|
|
// calculate a line from caster to the target cell
|
|
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y,
|
|
targcell->x, targcell->y, retcell, &nretcell);
|
|
|
|
ndigs = 0;
|
|
|
|
// get rid of rock in the cells...
|
|
for (i = 0; i < nretcell && (ndigs <= power) ; i++) {
|
|
int seenthiscell = B_FALSE;
|
|
if (haslos(player, retcell[i])) seenthiscell = B_TRUE;
|
|
if (retcell[i]->type->solid) {
|
|
// can dig through stone, but nothing else.
|
|
if (retcell[i]->type->material->id == MT_STONE) {
|
|
setcelltype(retcell[i], getemptycelltype(retcell[i]->map->habitat));
|
|
if (seenthiscell) {
|
|
ndigs++;
|
|
numseen++;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
// stop.
|
|
break;
|
|
}
|
|
} else {
|
|
object_t *o;
|
|
for (o = retcell[i]->obpile->first ; o ; o = o->next) {
|
|
if (hasflag(o->flags, F_IMPASSABLE)) {
|
|
char obname[BUFLEN];
|
|
// destroy this object then stop.
|
|
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
|
|
if (seenthiscell) {
|
|
if (!hasflag(o->flags, F_OBDIETEXT)) {
|
|
getobname(o, obname, o->amt);
|
|
msg("%s crumble%s to dust!", obname, (o->amt == 1) ? "s" : "");
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
ndigs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// announce destruction of any walls seen
|
|
if (numseen) {
|
|
msg("The wall%s crumble%s to dust!",
|
|
(numseen == 1) ? "" : "s",
|
|
(numseen == 1) ? "s" : ""
|
|
);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (ndigs == 0) {
|
|
if (isplayer(caster)) nothinghappens();
|
|
}
|
|
} 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_DETECTPOISON) {
|
|
if (isplayer(caster)) {
|
|
int npoisoned = 0;
|
|
int n;
|
|
for (n = 0; n < caster->nlos; n++) {
|
|
object_t *o;
|
|
for (o = caster->los[n]->obpile->first ; o ; o = o->next) {
|
|
flag_t *f;
|
|
int ispoisoned = B_FALSE, isdecayed = B_FALSE;
|
|
if (isrotting(o)) {
|
|
isdecayed = B_TRUE;
|
|
} else if (o->type->id == OT_POT_POISON) {
|
|
ispoisoned = B_TRUE;
|
|
} else {
|
|
for (f = o->flags->first ; f ; f = f->next) {
|
|
if (f->id == F_POISONED) {
|
|
ispoisoned = B_TRUE;
|
|
f->known = B_TRUE;
|
|
break;
|
|
} else if ((f->id == F_HITCONFER) && (f->val[0] == F_POISONED)) {
|
|
ispoisoned = B_TRUE;
|
|
f->known = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (ispoisoned || isdecayed) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s %s %s!", obname, (o->amt == 1) ? "is" : "are",
|
|
ispoisoned ? "poisoned" : "rotted");
|
|
npoisoned++;
|
|
}
|
|
|
|
if (o->type->id == OT_POT_POISON) {
|
|
makeknown(o->type->id);
|
|
}
|
|
}
|
|
}
|
|
if (!npoisoned) {
|
|
msg("You can't detect any poison nearby.");
|
|
}
|
|
} // end if isplayer
|
|
} else if (spellid == OT_S_DETONATE) {
|
|
// don't need line of fire!
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER|TT_DOOR, spellid, power)) return B_TRUE;
|
|
|
|
explodecells(targcell, 20, B_TRUE, NULL, power / 4, DT_ORTH, 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, 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, DT_COMPASS, B_TRUE);
|
|
} else {
|
|
explodecells(targcell, totalmass*5, B_TRUE, NULL, 1, DT_COMPASS, B_TRUE);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_DISPERSAL) {
|
|
cell_t *c = NULL;
|
|
object_t *o, *nexto;
|
|
int donesomething = B_FALSE;
|
|
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER|TT_OBJECT, spellid, power)) return B_TRUE;
|
|
if (!targcell) {
|
|
if (isplayer(caster)) {
|
|
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, 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_FEAR) {
|
|
char targname[BUFLEN];
|
|
// ask for target
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targname);
|
|
|
|
if (isplayer(caster)) {
|
|
msg("You emit an aura of fear!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s emits an aura of fear!",castername);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (lfhasflag(target, F_ASLEEP)) {
|
|
if (isplayer(target)) {
|
|
msg("You suffer terrifying nightmares!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s thrashes about in its sleep!",targname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
losehp(target, roll("2d6"), DT_DIRECT, caster, "terrifying nightmares");
|
|
} else {
|
|
scare(target, caster, 5+rnd(1,power), 5+power);
|
|
}
|
|
} else if (spellid == OT_S_FEEBLEMIND) {
|
|
// ask for target
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
if ((getattr(target, A_IQ) <= 3) || skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
|
|
if (cansee(player, target)) {
|
|
getlfname(target, buf);
|
|
msg("%s %s momentarily foolish.", buf, isplayer(target) ? "feel" : "looks");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (target) {
|
|
int howlong = 15;
|
|
flag_t *f;
|
|
|
|
// already feebleminded?
|
|
for (f = target->flags->first; f ; f = f->next) {
|
|
if ((f->id == F_ATTRMOD) && (f->val[0] == A_IQ) && (f->obfrom == OT_S_FEEBLEMIND)) {
|
|
if (cansee(player, target)) {
|
|
getlfname(target, buf);
|
|
msg("%s %s momentarily more foolish.", buf, isplayer(target) ? "feel" : "looks");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
howlong = getspellduration(5,10,blessed) + power;
|
|
// always set to 3 (ie. animal)
|
|
f = addtempflag(target->flags, F_ATTRSET, A_IQ, 3, NA, NULL, howlong);
|
|
f->obfrom = OT_S_FEEBLEMIND;
|
|
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} 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_FLOOD) {
|
|
int failed = B_FALSE;
|
|
// ask for a target cell
|
|
if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power)) return B_TRUE;
|
|
if (targcell) {
|
|
if (!targcell->type->solid) {
|
|
// create water there
|
|
object_t *o;
|
|
o = addob(targcell->obpile, "deep water");
|
|
if (o) {
|
|
enum OBTYPE badoid[2];
|
|
int i,amt;
|
|
amt = ((power+1) * (power+1)) - 1;
|
|
badoid[0] = OT_WATERDEEP;
|
|
badoid[1] = OT_NONE;
|
|
for (i = 0; i < amt; i++) {
|
|
cell_t *c;
|
|
c = real_getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND, LOF_WALLSTOP, badoid);
|
|
if (c) {
|
|
addob(c->obpile, "deep water");
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (haslos(player, targcell)) {
|
|
msg("A huge pool of water appears!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_ENERGYBOLT) {
|
|
char lfname[BUFLEN];
|
|
char numbuf[BUFLEN];
|
|
|
|
numtotext(power, numbuf);
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, 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(player, 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, 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);
|
|
|
|
redrawpause();
|
|
// add fires as follows (3 = medium, 2 = medium, 1 = smell)
|
|
//
|
|
// 1
|
|
// 232
|
|
// 13331
|
|
// 232
|
|
// 1
|
|
// add large fires to surrounding cells
|
|
addob(targcell->obpile, "medium 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, "medium 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");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
redrawresume();
|
|
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
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, 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(player, target)) {
|
|
msg("A dart of flame hits %s.",lfname);
|
|
}
|
|
losehp(target, rnd(1,6) + power, DT_FIRE, caster, "a dart of flame");
|
|
}
|
|
} else {
|
|
damageallobs(NULL, targcell->obpile, 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,6), 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, spellid, power)) return B_TRUE;
|
|
if (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_ENDUREELEMENTS) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
if (!validatespellcell(caster, &targcell,TT_ALLY, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
f = addtempflag(caster->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
f = addtempflag(caster->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_ENTANGLE) {
|
|
char targname[BUFLEN];
|
|
flag_t *f;
|
|
object_t *o;
|
|
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targname);
|
|
if (cansee(player, target)) {
|
|
msg("Magical vines grasp %s!",targname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// create vine
|
|
o = addob(targcell->obpile, "entangling vine");
|
|
// set power
|
|
f = hasflag(o->flags, F_RESTRICTMOVEMENT);
|
|
if (f) {
|
|
f->val[0] = 30 + (power/2);
|
|
}
|
|
/// remmeber creator. if they don't have los to us, spell
|
|
// is broken and vines will vanish.
|
|
setobcreatedby(o, caster);
|
|
|
|
} 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("%s %s start%s glowing bright blue!",
|
|
(o->pile->owner == caster) ? "Your" : "",
|
|
(o->pile->owner == caster) ? noprefix(obname) : 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 (isplayer(caster)) {
|
|
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) {
|
|
if (isplayer(caster)) {
|
|
msg("Your hands feel freezing cold for a moment.");
|
|
}
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// object will turn to ice immediately...
|
|
if (o->material->id == MT_ICE) { // already made of ice?
|
|
cell_t *loc;
|
|
flag_t *f;
|
|
loc = getoblocation(o);
|
|
// announce
|
|
if (haslos(caster, loc)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s %s freeze%s some more.",
|
|
(o->pile->owner == caster) ? "Your" : "",
|
|
(o->pile->owner == caster) ? noprefix(obname) : obname,
|
|
(o->amt == 1) ? "s" : "");
|
|
}
|
|
// restore it
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] = f->val[1];
|
|
}
|
|
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 (isplayer(o->pile->owner)) {
|
|
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 (isplayer(caster)) {
|
|
nothinghappens();
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
} else {
|
|
if (o->pile->owner) {
|
|
if (isplayer(o->pile->owner)) {
|
|
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_FROSTBITE) {
|
|
char lfname[BUFLEN];
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
getlfname(target, lfname);
|
|
|
|
if (isplayer(target)) {
|
|
if (isimmuneto(target->flags, DT_COLD)) {
|
|
msg("You feel mildly chilly.");
|
|
} else {
|
|
msg("You feel very cold!");
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
if (isimmuneto(target->flags, DT_COLD)) {
|
|
msg("%s looks mildly chilly.", lfname);
|
|
} else {
|
|
msg("%s looks very cold!", lfname);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// target takes magical damage
|
|
// always hit
|
|
if (!isimmuneto(target->flags, DT_COLD)) {
|
|
losehp(target, roll("1d3"), DT_COLD, caster, "a frostbite spell");
|
|
}
|
|
} 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, B_TRUE);
|
|
}
|
|
} else if (spellid == OT_S_GREASE) {
|
|
int radius;
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
if (targcell->type->solid) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
radius = power/2;
|
|
if (radius < 1) radius = 1;
|
|
|
|
addobburst(targcell, radius, DT_ORTH, "puddle of oil", caster, LOF_WALLSTOP);
|
|
|
|
if (haslos(player, targcell)) {
|
|
char underbuf[BUFLEN];
|
|
if (targcell->lf && cansee(player, targcell->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
sprintf(underbuf, " under %s",lfname);
|
|
} else {
|
|
strcpy(underbuf, "");
|
|
}
|
|
msg("A %spool of oil appears%s!", (radius == 1) ? "" : "huge ", underbuf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_HAILSTORM) {
|
|
int failed = B_FALSE;
|
|
if (isoutdoors(caster->cell->map)) {
|
|
power += 3;
|
|
limit(&power, NA, 10);
|
|
}
|
|
// ask for a target cell
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
if (targcell) {
|
|
if (!targcell->type->solid) {
|
|
object_t *o;
|
|
int x,y;
|
|
int radius;
|
|
char *dambuf;
|
|
// centre storm here...
|
|
if (haslos(player, targcell)) {
|
|
msg("An intense storm of hail appears!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
radius = power / 2;
|
|
if (radius < 1) radius = 1;
|
|
addobsinradius(targcell, radius, DT_ORTH, "hail storm", B_FALSE);
|
|
|
|
// replace damage per sec with power d4
|
|
asprintf(&dambuf, "%dd4", power);
|
|
for (y = targcell->y - radius; y <= targcell->y + radius; y++) {
|
|
for (x = targcell->x - radius; x <= targcell->x + radius; x++) {
|
|
cell_t *c;
|
|
c = getcellat(targcell->map, x, y);
|
|
if (c && (getcelldist(targcell, c) <= radius)) {
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (o->type->id == OT_HAILSTORM) {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_WALKDAM);
|
|
if (f) {
|
|
free(f->text);
|
|
f->text = strdup(dambuf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end forx
|
|
} // end fory
|
|
|
|
free(dambuf);
|
|
} else { // end if targcell not solid
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_HASTE) {
|
|
int howlong = 15;
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, 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_FASTACTMOVE, 10, NA, NA, NULL, howlong);
|
|
}
|
|
} else if ((spellid == OT_S_HEALING) || (spellid == OT_S_HEALINGMIN) || (spellid == OT_S_HEALINGMAJ)) {
|
|
int donesomething = B_FALSE;
|
|
if (!validatespellcell(caster, &targcell,TT_ALLY, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// cure certain bad effects
|
|
if (killflagsofid(target->flags, F_PAIN)) {
|
|
donesomething = B_TRUE;
|
|
}
|
|
if (killflagsofid(target->flags, F_POISONED)) {
|
|
donesomething = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if ((spellid == OT_S_HEALINGMIN) && donesomething) {
|
|
// minor healing will stop here
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (target->hp < target->maxhp) {
|
|
int min,max;
|
|
switch (spellid) {
|
|
case OT_S_HEALINGMIN:
|
|
default:
|
|
min = 1; max = 10;
|
|
break;
|
|
case OT_S_HEALING:
|
|
min = 10; max = 20;
|
|
break;
|
|
case OT_S_HEALINGMAJ:
|
|
min = 20; max = 30;
|
|
break;
|
|
}
|
|
gainhp(target, getspellduration(min,max,blessed) + (power*2));
|
|
|
|
if (isplayer(target)) {
|
|
if (target->hp >= target->maxhp) {
|
|
switch (spellid) {
|
|
case OT_S_HEALINGMIN:
|
|
msg("All of your scrapes and bruises are healed!");
|
|
break;
|
|
case OT_S_HEALING:
|
|
default:
|
|
msg("Your wounds close themselves!");
|
|
break;
|
|
case OT_S_HEALINGMAJ:
|
|
msg("Your injuries are healed!");
|
|
break;
|
|
}
|
|
} else {
|
|
switch (spellid) {
|
|
case OT_S_HEALINGMIN:
|
|
msg("Some of your scrapes and bruises are healed!");
|
|
break;
|
|
case OT_S_HEALING:
|
|
default:
|
|
msg("Some of your wounds close themselves!");
|
|
break;
|
|
case OT_S_HEALINGMAJ:
|
|
msg("Your injuries are partially healed!");
|
|
break;
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
if (!donesomething) {
|
|
if (isplayer(target)) {
|
|
nothinghappens();
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_HOLDPORTAL) {
|
|
object_t *o;
|
|
if (!validatespellcell(caster, &targcell,TT_DOOR, spellid, power)) return B_TRUE;
|
|
o = hasobwithflag(targcell->obpile, F_DOOR);
|
|
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
closedoor(NULL, o);
|
|
addflag(o->flags, F_JAMMED, 100, NA, NA, NULL);
|
|
|
|
if (haslos(player, targcell)) {
|
|
char buf[BUFLEN];
|
|
getobname(o, buf, 1);
|
|
msg("%s glows for a moment.",buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_ICEEDGE) {
|
|
object_t *wep;
|
|
enum DAMTYPE dt;
|
|
char obname[BUFLEN];
|
|
if (!validatespellcell(caster, &targcell, TT_PLAYER | TT_ALLY, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// does caster have a bladed weapon?
|
|
wep = getweapon(target);
|
|
if (!wep) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
dt = getdamtype(wep);
|
|
if ((dt != DT_PIERCE) && (dt != DT_SLASH) && (dt != DT_CHOP)) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
getobname(wep, obname, wep->amt);
|
|
|
|
if (isplayer(target)) {
|
|
msg("Your %s%s blade is covered with ice!", obname, getpossessive(obname));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("%s%s %s is covered with ice!", lfname, getpossessive(lfname), lfname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
addtempflag(wep->flags, F_EXTRADAM, DT_COLD, NA, NA, "1d6", 10 + power*3);
|
|
|
|
} else if (spellid == OT_S_ICICLE) {
|
|
object_t *o;
|
|
int donesomething = B_FALSE;
|
|
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
// create icicle
|
|
o = addob(targcell->obpile, "huge icicle");
|
|
if (o) {
|
|
flag_t *f;
|
|
if (haslos(player, targcell)) {
|
|
drawscreen();
|
|
msg("A massive icicle rises from the ground!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] = 3+power;
|
|
f->val[1] = 3+power;
|
|
}
|
|
addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL);
|
|
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
// knock lfs away
|
|
if (targcell->lf) {
|
|
knockback(targcell->lf, getdiraway(targcell, targcell, NULL, B_FALSE, DT_COMPASS), 1, NULL, 25+power);
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
} 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 (isplayer(caster)) {
|
|
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 (isplayer(caster)) {
|
|
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, 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(player, 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) {
|
|
setcellknown(c, PR_ADEPT);
|
|
} else if (getcelldist(caster->cell, c) <= range) {
|
|
setcellknown(c, PR_ADEPT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isplayer(caster)) {
|
|
msg("An image of your surroundings appears in your mind!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
needredraw = 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) {
|
|
int failed = B_FALSE;
|
|
if (isplayer(caster)) {
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
if (targcell && haslos(caster, targcell) && haslf(targcell)) {
|
|
char targname[BUFLEN];
|
|
lifeform_t *oldplayer;
|
|
// temporarily change player pointer...
|
|
oldplayer = player;
|
|
player = targcell->lf;
|
|
|
|
//
|
|
getlfname(targcell->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, 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 (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
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 (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
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 (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
// 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, 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(player, target)) {
|
|
msg("A glob of venom misses %s.",lfname);
|
|
}
|
|
} else {
|
|
// hit
|
|
if (cansee(player, target)) {
|
|
msg("A glob of venom hits %s.",lfname);
|
|
}
|
|
if (!isimmuneto(target->flags, DT_POISON)) {
|
|
poison(target, power*3, P_VENOM, power, "a glob of venom");
|
|
}
|
|
}
|
|
} else {
|
|
damageallobs(NULL, targcell->obpile, 0, DT_FIRE);
|
|
}
|
|
} else if (spellid == OT_S_POSSESSION) {
|
|
char targname[BUFLEN];
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, 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;
|
|
}
|
|
}
|
|
|
|
// charmable?
|
|
if (!ischarmable(target)) {
|
|
if (isplayer(caster)) {
|
|
switch (reason) {
|
|
case E_DRUNK:
|
|
msg("%s%s mind is too alcohol-impaired for you to possess.",targname,getpossessive(targname));
|
|
break;
|
|
case E_LOWIQ:
|
|
msg("%s%s intellect is too simple for you to possess.",targname,getpossessive(targname));
|
|
break;
|
|
case E_UNDEAD:
|
|
msg("The undead are immune to possession.");
|
|
break;
|
|
default:
|
|
msg("You cannot possesss %s.", targname);
|
|
break;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// saving throw....
|
|
if (skillcheck(target, SC_RESISTMAG, 16 + 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));
|
|
}
|
|
}
|
|
|
|
|
|
// remove 'become a ghost' flag fro mcaster
|
|
killflagsofid(caster->flags, F_RISEASGHOST);
|
|
|
|
// 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;
|
|
// now kill the caster!
|
|
die(caster);
|
|
// if they survived somehow...
|
|
if (!isdead(caster)) {
|
|
// ... they will be hostile!
|
|
fightback(caster, 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, 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, is(target));
|
|
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, 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, is(target));
|
|
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...
|
|
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->region, AUTO, NULL, D_NONE);
|
|
}
|
|
|
|
|
|
// 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_OBDIETEXT, 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_OBDIETEXT, 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 (onein(4)) {
|
|
if (onein(2)) {
|
|
*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];
|
|
|
|
// special case for ghosts...
|
|
if (caster->race->id == R_GHOST) {
|
|
targcell = caster->cell;
|
|
} else {
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, 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) ) {
|
|
willannounce = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
addtempflag(target->flags, F_INVISIBLE, B_TRUE, NA, NA, NULL, howlong);
|
|
if (willannounce && !lfhasflag(player, F_SEEINVIS)) {
|
|
msg("%s flicker%s then vanishes!",targname, isplayer(target) ? "" : "s");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_JOLT) {
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
target = haslf(targcell);
|
|
if (target) {
|
|
int dam;
|
|
// hit
|
|
if (isplayer(target)) {
|
|
msg("A pulse of electricity shocks you!");
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("A pulse of electricity shocks %s!",lfname);
|
|
}
|
|
dam = rolldie(1, power);
|
|
losehp(target, dam, DT_ELECTRIC, caster, "a jolt of electricity");
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_KNOCK) {
|
|
object_t *o;
|
|
if (!validatespellcell(caster, &targcell,TT_DOOR, 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, 0);
|
|
} else {
|
|
int donesomething = B_FALSE;
|
|
object_t *nexto;
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (hasflag(o->flags, F_IMPASSABLE)) {
|
|
int gotit = B_FALSE;
|
|
int dooropen;
|
|
if (isdoor(o, &dooropen)) {
|
|
if (!dooropen) {
|
|
gotit = B_TRUE;
|
|
}
|
|
} else {
|
|
gotit = B_TRUE;
|
|
}
|
|
|
|
if (gotit) {
|
|
if (haslos(player, targcell)) {
|
|
getobname(o, buf, o->amt);
|
|
msg("%s %s!",isplayer(caster) ? "You blast" : "Something blasts", buf);
|
|
}
|
|
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!donesomething) {
|
|
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;
|
|
|
|
if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} 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, 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 (isundead(l)) {
|
|
// 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) );
|
|
}
|
|
}
|
|
calclight(targcell->map);
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
} else if (spellid == OT_S_LIGHTNINGBOLT) {
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcells;
|
|
int i;
|
|
int nhits = power;
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
// create a line of fire towards the target cell
|
|
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcells);
|
|
animcells(caster->cell, &retcell[1], nretcells-1, B_FALSE, '/', '\\', C_WHITE);
|
|
if (cansee(player, caster)) {
|
|
msg("%s shoot%s a bolt of electricity!",castername, isplayer(caster) ? "" : "s");
|
|
}
|
|
|
|
// don't hit the caster cell on fire!
|
|
for (i = 1; (i < nretcells) && (nhits > 0); i++) {
|
|
cell_t *c;
|
|
c = retcell[i];
|
|
if (c->lf) {
|
|
// hit with lightning
|
|
losehp(c->lf, roll("2d6"), DT_ELECTRIC, caster, "an electricity bolt");
|
|
nhits--;
|
|
}
|
|
if (haslos(player, c)) {
|
|
needredraw = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_LIGHTNINGSTORM) {
|
|
char targname[BUFLEN];
|
|
lifeform_t *poss[MAXCANDIDATES], *targ[MAXCANDIDATES];
|
|
int nposs = 0,ntarg = 0;
|
|
int seen = B_FALSE;
|
|
int i;
|
|
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
target = caster->los[i]->lf;
|
|
if (target && areenemies(caster, target)) {
|
|
poss[nposs++] = target;
|
|
}
|
|
}
|
|
|
|
if (nposs) {
|
|
int nhits;
|
|
nhits = power*3;
|
|
for (i = 0; (i < nhits) && (nposs > 0); i++) {
|
|
int sel,n;
|
|
// select a random target
|
|
sel = rnd(0,nposs-1);
|
|
targ[ntarg++] = poss[sel];
|
|
// remove selection from list
|
|
for (n = sel; n < nposs-1; n++) {
|
|
poss[n] = poss[n+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
|
|
for (i = 0; i < ntarg; i++) {
|
|
if (haslos(player, targ[i]->cell)) {
|
|
// hit this target with lightning
|
|
animsky(targ[i]->cell, '}', C_CYAN);
|
|
seen = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (seen) {
|
|
drawlevelfor(player);
|
|
}
|
|
|
|
for (i = 0; i < ntarg; i++) {
|
|
if (cansee(player, targ[i])) {
|
|
getlfname(targ[i],targname);
|
|
msg("%s %s struck by a bolt of lightning!",targname, is(targ[i]));
|
|
}
|
|
if (isoutdoors(targ[i]->cell->map)) {
|
|
losehp(targ[i], rolldie(4,6), DT_ELECTRIC, caster, "a bolt of lightning");
|
|
} else {
|
|
losehp(targ[i], rolldie(3,6), DT_ELECTRIC, caster, "a bolt of lightning");
|
|
}
|
|
}
|
|
|
|
if (seen) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_LOCATEOBJECT) {
|
|
char wantname[BUFLEN];
|
|
char buf[BUFLEN];
|
|
map_t *m;
|
|
object_t *o;
|
|
|
|
if (!isplayer(caster)) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
// ask for object
|
|
askstring("Locate what kind of object", '?', wantname, BUFLEN, NULL);
|
|
// find it in all maps!
|
|
sprintf(buf, "Locations of '%s':", wantname);
|
|
initprompt(&prompt, buf);
|
|
prompt.maycancel = B_TRUE;
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
int x,y;
|
|
char ch = 'b';
|
|
// on the ground?
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
cell_t *c;
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
char obname[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE);
|
|
if (strcasestr(obname, wantname)) {
|
|
addchoice(&prompt, ch++, obname, NULL, o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// carried by someone?
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
cell_t *c;
|
|
c = getcellat(m, x, y);
|
|
if (c && c->lf) {
|
|
for (o = c->lf->pack->first ; o ; o = o->next) {
|
|
char obname[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE);
|
|
if (strcasestr(obname, wantname)) {
|
|
char lfname[BUFLEN];
|
|
real_getlfnamea(c->lf, lfname, B_FALSE);
|
|
strcat(obname, " (held by ");
|
|
strcat(obname, lfname);
|
|
strcat(obname, ")");
|
|
|
|
addchoice(&prompt, ch++, obname, NULL, o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
getchoice(&prompt);
|
|
o = (object_t *)prompt.result;
|
|
while (o) {
|
|
// describe it
|
|
describeob(o);
|
|
// ask for another one
|
|
getchoice(&prompt);
|
|
o = (object_t *)prompt.result;
|
|
}
|
|
|
|
// restore screen
|
|
needredraw = B_TRUE;
|
|
statdirty = B_TRUE;
|
|
drawscreen();
|
|
msg("Your spell finishes.");
|
|
} else if (spellid == OT_S_LORE) {
|
|
enum SKILL skid;
|
|
skill_t *sk;
|
|
char ch = 'a';
|
|
|
|
if (!isplayer(caster)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
// ask for which knowledge skill
|
|
initprompt(&prompt, "Which knowledge do you desire?");
|
|
prompt.maycancel = B_TRUE;
|
|
for (skid = 0; skid < MAXSKILLS; skid++) {
|
|
sk = findskill(skid);
|
|
if (sk && isloreskill(skid)) {
|
|
if (!getskill(caster, skid)) {
|
|
addchoice(&prompt, ch++, sk->name, NULL, sk);
|
|
}
|
|
}
|
|
}
|
|
if (prompt.nchoices == 0) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
getchoice(&prompt);
|
|
sk = (skill_t *)prompt.result;
|
|
if (sk) {
|
|
flag_t *f;
|
|
f = addtempflag(caster->flags, F_HASSKILL, sk->id, (power/2)+1, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
|
|
msg("New knowledge floods into your mind!");
|
|
} else {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
} else if (spellid == OT_S_GRAVBOOST) {
|
|
// ask for target
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
if (target) {
|
|
int howlong = 15;
|
|
|
|
if (lfhasflag(target, F_GRAVBOOSTED) || skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
|
|
if (isplayer(target)) {
|
|
msg("You feel momentarily heavier.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
msg("%s looks momentarily heavier.", targname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
howlong = getspellduration(3,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_GUSTOFWIND) {
|
|
obpile_t *op;
|
|
object_t *o, *nexto;
|
|
object_t *poss[MAXPILEOBS],*blowob[MAXPILEOBS];
|
|
int nposs = 0,nblowobs = 0;
|
|
int i;
|
|
char targname[BUFLEN];
|
|
|
|
if (isoutdoors(caster->cell->map)) {
|
|
power += 5;
|
|
limit(&power, NA, 10);
|
|
}
|
|
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER|TT_OBJECT, spellid, power)) return B_TRUE;
|
|
|
|
target = targcell->lf;
|
|
|
|
if (target) {
|
|
getlfname(target, targname);
|
|
// objects held by target
|
|
op = target->pack;
|
|
} else {
|
|
// objects on ground
|
|
op = targcell->obpile;
|
|
}
|
|
|
|
for (o = op->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (getobunitweight(o) <= 5) {
|
|
poss[nposs++] = o;
|
|
}
|
|
}
|
|
|
|
for (i = 0; ((i < power) && (nposs > 0)); i++) {
|
|
int sel,n;
|
|
sel = rnd(0,nposs-1);
|
|
blowob[nblowobs++] = poss[sel];
|
|
// remove ob from possibility list
|
|
for (n = sel; n < (nposs-1); n++) {
|
|
poss[sel] = poss[sel+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
|
|
if (target && cansee(player, target)) {
|
|
msg("A gust of wind whips up around %s!", targname);
|
|
} else if (haslos(player, targcell)) {
|
|
msg("A gust of wind whips up!");
|
|
}
|
|
|
|
// chance of blowing each ob away
|
|
for (i = 0; i < nblowobs; i++) {
|
|
char obname[BUFLEN];
|
|
cell_t *c;
|
|
|
|
getobname(blowob[i], obname, 1);
|
|
c = getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND);
|
|
if (c && ((rnd(1,100)+power) <= 33)) {
|
|
// move it
|
|
fireat(NULL, blowob[i], 1, c, 4, NULL);
|
|
}
|
|
}
|
|
|
|
|
|
// easyish save to avoid falling
|
|
if (target && !skillcheck(target, SC_FALL, 12 + (power*2), 0)) {
|
|
fall(target, NULL, B_TRUE);
|
|
}
|
|
needredraw = B_TRUE;
|
|
} else if (spellid == OT_S_MIST) {
|
|
object_t *o;
|
|
targcell = caster->cell;
|
|
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
if (targcell->lf) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
msg("A thick veil of mist surrounds %s!", lfname);
|
|
} else {
|
|
msg("A thick veil of mist appears!");
|
|
}
|
|
}
|
|
o = addob(targcell->obpile, "thick mist");
|
|
if (o) {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] += power;
|
|
f->val[1] += power;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_MENDING) {
|
|
object_t *o;
|
|
|
|
if (targob) {
|
|
o = targob;
|
|
} else {
|
|
// ask for an object
|
|
o = askobject(caster->pack, "Mend which object", NULL, AO_DAMAGED);
|
|
}
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(caster)) {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (!f) {
|
|
nothinghappens();
|
|
return B_TRUE;
|
|
} else {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
|
|
f->val[0] += (rnd(1,6) + power);
|
|
|
|
if (f->val[0] >= f->val[1]) {
|
|
msg("Your %s is completely repaired!", noprefix(obname));
|
|
f->val[0] = f->val[1];
|
|
} else {
|
|
msg("Your %s is repaired a little!", noprefix(obname));
|
|
}
|
|
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
// monsters can't repair things!
|
|
}
|
|
} else if (spellid == OT_S_PACIFY) {
|
|
char targetname[BUFLEN];
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, 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_TARGETLF)) {
|
|
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;
|
|
flag_t *f;
|
|
target = caster;
|
|
|
|
howlong = getspellduration(5,10,blessed) + (power/2);
|
|
f = addtempflag(target->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL, howlong);
|
|
f->obfrom = OT_S_PASSWALL;
|
|
|
|
breakallgrabs(caster);
|
|
} else if (spellid == OT_S_POLYMORPH) {
|
|
race_t *r = NULL;
|
|
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
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)) { // ie. polymorphing yourself
|
|
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
|
|
// - can't turn into undead monsters
|
|
if (r && !canpolymorphto(r->id)) r = NULL;
|
|
} else { // random
|
|
if (isplayer(target) && lfhasflag(target, F_CONTROL)) {
|
|
askstring("What will you become", '?', buf, BUFLEN, NULL);
|
|
r = findracebyname(buf);
|
|
|
|
// make sure race is valid:
|
|
if (r && !canpolymorphto(r->id)) r = NULL;
|
|
}
|
|
|
|
if (!r) {
|
|
// random race, but not the same!
|
|
r = target->race;
|
|
while ((r == target->race) || !canpolymorphto(r->id)) {
|
|
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, B_TRUE);
|
|
|
|
// 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_PURIFYFOOD) {
|
|
object_t *o;
|
|
|
|
if (targob) {
|
|
o = targob;
|
|
} else {
|
|
// ask for an object
|
|
o = askobject(caster->pack, "Purify what?", NULL, AO_EDIBLE | AO_DRINKABLE);
|
|
}
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(caster)) {
|
|
char obname[BUFLEN];
|
|
flag_t *f;
|
|
int donesomething = B_FALSE;
|
|
|
|
getobname(o, obname, o->amt);
|
|
|
|
if (o->type->id == OT_POT_POISON) {
|
|
msg("Your %s sparkles for a while.", noprefix(obname));
|
|
makeknown(o->type->id); // you now know that it was poison.
|
|
o->type = findot(OT_POT_WATER);
|
|
makeknown(o->type->id); // you now know what water is too.
|
|
donesomething = B_TRUE;
|
|
} else {
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] = f->val[1];
|
|
|
|
f = hasflagval(o->flags, F_OBHPDRAIN, NA, DT_DECAY, NA, NULL);
|
|
if (f) {
|
|
killflag(f);
|
|
}
|
|
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
f = hasflag(o->flags, F_TAINTED);
|
|
if (f) {
|
|
killflag(f);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (donesomething) {
|
|
if (isdrinkable(o)) {
|
|
msg("Your %s looks more clean now.", noprefix(obname));
|
|
} else {
|
|
msg("Your %s looks more fresh now.", noprefix(obname));
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
nothinghappens();
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
// monsters can't purify things!
|
|
}
|
|
} else if (spellid == OT_S_QUENCH) {
|
|
object_t *o,*nexto;
|
|
int ndone = 0;
|
|
|
|
if (!validatespellcell(caster, &targcell,TT_OBJECT, spellid, power)) return B_TRUE;
|
|
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
// all objects.
|
|
for (o = targcell->lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->type->material->id == MT_FIRE) {
|
|
if (haslos(player, targcell)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s vanishes!",obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
killob(o);
|
|
ndone++;
|
|
} else {
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
killflagsofid(o->flags, F_ONFIRE);
|
|
ndone++;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->type->material->id == MT_FIRE) {
|
|
if (haslos(player, targcell)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s vanishes!",obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
killob(o);
|
|
ndone++;
|
|
} else {
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
killflagsofid(o->flags, F_ONFIRE);
|
|
ndone++;
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_LESSENPOISON) {
|
|
flag_t *f;
|
|
int ndone = 0;
|
|
if (!validatespellcell(caster, &targcell,TT_ALLY, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
for (f = target->flags->first ; f ; f = f->next) {
|
|
if (f->id == F_POISONED) {
|
|
// slightly lower time
|
|
if (f->lifetime > 1) {
|
|
f->lifetime--;
|
|
}
|
|
// cut power in half
|
|
f->val[1] /= 2; if (f->val[1] < 1) f->val[1] = 1;
|
|
if (isplayer(target)) {
|
|
msg("Your %s seems less intense.",getpoisonname(f->val[0]));
|
|
}
|
|
ndone++;
|
|
}
|
|
}
|
|
if (ndone) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_REPELINSECTS) {
|
|
// just announce
|
|
if (isplayer(caster)) {
|
|
msg("A strange odour surrounds you...");
|
|
}
|
|
} else if (spellid == OT_S_REVEALHIDDEN) {
|
|
int i;
|
|
int seen = B_FALSE;
|
|
for (i = 0 ; i < caster->nlos; i++ ){
|
|
targcell = caster->los[i];
|
|
if (targcell) {
|
|
object_t *o;
|
|
if (isplayer(caster)) {
|
|
// reveal secret doors/obs/etc
|
|
for (o = targcell->obpile->first ; o ; o = o->next) {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_SECRET);
|
|
if (f && (f->val[0] != NA)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s is magically revealed!", obname);
|
|
killflag(f);
|
|
seen = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove invisibility/hiding
|
|
if (targcell->lf) {
|
|
flag_t *f, *nextf;
|
|
for (f = targcell->lf->flags->first ; f ; f = nextf) {
|
|
nextf = f->next;
|
|
if (f->id == F_INVISIBLE) {
|
|
if ((f->lifetime > 0) || (f->lifetime == PERMENANT)) {
|
|
killflag(f);
|
|
// player can see whoever just appeared, and the caster?
|
|
if (cansee(player, targcell->lf) && cansee(player, caster)) {
|
|
char tname[BUFLEN];
|
|
seen = B_TRUE;
|
|
getlfname(targcell->lf, tname);
|
|
msg("%s becomes visible!", tname);
|
|
}
|
|
}
|
|
} else if ( (f->id == F_HIDING) &&
|
|
!lfhasflagval(caster, F_SPOTTED, targcell->lf->id, NA, NA, NULL)) {
|
|
addflag(caster->flags, F_SPOTTED, targcell->lf->id, NA, NA, NULL);
|
|
// player can see whoever just appeared, and the caster?
|
|
if (cansee(player, targcell->lf) && cansee(player, caster)) {
|
|
char tname[BUFLEN];
|
|
seen = B_TRUE;
|
|
getlfname(targcell->lf, tname);
|
|
msg("A hiding %s is revealed!", noprefix(tname));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (seen) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
nothinghappens();
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_SEEINVIS) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
f = addtempflag(caster->flags, F_SEEINVIS, B_TRUE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_SLEEP) {
|
|
int howlong;
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
if (lfhasflag(target, F_ASLEEP) || !cansleep(target)) {
|
|
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);
|
|
fallasleep(target, F_ASLEEP);
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SLEETSTORM) {
|
|
int failed = B_FALSE;
|
|
if (isoutdoors(caster->cell->map)) {
|
|
power += 3;
|
|
limit(&power, NA, 10);
|
|
}
|
|
// ask for a target cell
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
if (targcell) {
|
|
if (!targcell->type->solid) {
|
|
int radius;
|
|
// centre storm here...
|
|
if (haslos(player, targcell)) {
|
|
msg("An raging storm of sleet appears!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
radius = power / 2;
|
|
if (radius < 1) radius = 1;
|
|
addobsinradius(targcell, radius, DT_ORTH, "storm of sleet", B_FALSE);
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_SLOW) {
|
|
int howlong = 15;
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
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_SLOWACTMOVE, 10, NA, NA, NULL, howlong);
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SOFTENEARTH) {
|
|
int seen = B_FALSE;
|
|
int ndone = 0;
|
|
int powerleft = power;
|
|
// ask for a target cell
|
|
if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power)) return B_TRUE;
|
|
if (targcell) {
|
|
object_t *o;
|
|
enum OBTYPE badoid[2];
|
|
int i;
|
|
cell_t *c;
|
|
// do first cell
|
|
// TODO: is it actually earth here, not stone,metal etc?
|
|
o = addob(targcell->obpile, "pool of mud");
|
|
if (o) {
|
|
ndone++;
|
|
powerleft--;
|
|
if (haslos(player, targcell)) seen = B_TRUE;
|
|
// do the rest
|
|
badoid[0] = OT_MUDPOOL;
|
|
badoid[1] = OT_NONE;
|
|
for (i = 0; i < powerleft; i++) {
|
|
c = real_getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND, LOF_DONTNEED, badoid);
|
|
if (c) {
|
|
o = addob(c->obpile, "pool of mud");
|
|
if (o) {
|
|
ndone++;
|
|
if (haslos(player, targcell)) seen = B_TRUE;
|
|
// do the rest
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ndone) {
|
|
if (seen) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("The ground nearby softens into mud.");
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_SPARK) {
|
|
object_t *o,*nexto;
|
|
int donesomething = B_FALSE;
|
|
if (!validatespellcell(caster, &targcell,TT_OBJECT | TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
if (haslos(player, targcell)) {
|
|
msg("A small spark of flame appears.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
losehp(targcell->lf, rnd(1,3), DT_FIRE, caster, "a spark");
|
|
} else {
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (isflammable(o)) {
|
|
takedamage(o, rnd(1,3), DT_FIRE);
|
|
}
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_STENCH) {
|
|
int howlong;
|
|
if (!validatespellcell(caster, &targcell,TT_OBJECT | TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (skillcheck(target, SC_RESISTMAG, 20 + power, 0)) {
|
|
if (isplayer(target)) {
|
|
msg("You feel momentarily nauseated.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char buf[BUFLEN];
|
|
getlfname(target, buf);
|
|
msg("%s looks momentarily unwell.", buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
howlong = getspellduration(5,10,blessed) + power;
|
|
if (makenauseated(target, power, howlong)) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_STICKTOSNAKE) {
|
|
int i;
|
|
cell_t *poss[MAXCANDIDATES];
|
|
int nposs;
|
|
race_t *raceposs[MAXCANDIDATES],*r;
|
|
int nraceposs = 0;
|
|
cell_t *c = NULL,*newcell = NULL;
|
|
object_t *o,*nexto;
|
|
int ndone = 0;
|
|
|
|
// power is always at lesat 2 for this spell
|
|
if (power < 2) power = 2;
|
|
|
|
// get list of possible snakes
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
if (strstr(r->name, " snake")) {
|
|
flag_t *f;
|
|
f = hasflag(r->flags, F_HITDICE);
|
|
// ie. 1hd needs power 2
|
|
// ie. 2hd needs power 4
|
|
// ie. 3hd needs power 6
|
|
// ie. 4hd needs power 8
|
|
if (f && (power >= (f->val[0] * 2))) {
|
|
raceposs[nraceposs++] = r;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
poss[0] = caster->cell;
|
|
nposs = 1;
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
poss[nposs++] = caster->los[i];
|
|
}
|
|
// for each cell we can see...
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
c = poss[i];
|
|
if (c->lf) {
|
|
if (cansee(caster, c->lf)) {
|
|
for (o = c->lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if ((o->material->id == MT_WOOD) &&
|
|
hasflag(o->flags, F_RODSHAPED) &&
|
|
isequipped(o) &&
|
|
(o->blessed == B_UNCURSED) &&
|
|
!hasflag(o->flags, F_HASBRAND)) {
|
|
char obname[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
getlfname(c->lf, lfname);
|
|
newcell = getrandomadjcell(c, WE_WALKABLE, B_NOEXPAND);
|
|
if (newcell) {
|
|
lifeform_t *snake;
|
|
// add a snake there.
|
|
snake = addlf(newcell, raceposs[rnd(0,nraceposs-1)]->id, 1);
|
|
if (snake) {
|
|
msg("%s%s %s transforms into a snake!",lfname,
|
|
getpossessive(lfname),
|
|
noprefix(obname));
|
|
killflagsofid(snake->flags, F_XPVAL);
|
|
addflag(snake->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
makefriendly(snake, PERMENANT);
|
|
if (c->lf == caster) {
|
|
char fullobname[BUFLEN];
|
|
getobnametrue(o, fullobname, 1);
|
|
killflagsofid(snake->flags, F_CORPSETYPE);
|
|
addflag(snake->flags, F_CORPSETYPE, NA, NA, NA, fullobname);
|
|
}
|
|
killob(o);
|
|
ndone++;
|
|
}
|
|
} else {
|
|
msg("%s%s %s quivers for a moment.",lfname, getpossessive(lfname),
|
|
noprefix(obname));
|
|
ndone++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else { // ie. no lf there
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if ((o->material->id == MT_WOOD) && hasflag(o->flags, F_RODSHAPED) && !isblessed(o)) {
|
|
cell_t *newcell = NULL;
|
|
lifeform_t *snake;
|
|
char obname[BUFLEN];
|
|
|
|
getobname(o, obname, o->amt);
|
|
|
|
// add to current cell or new one?
|
|
if (c->lf) { // ie. if a previous object made a snake here
|
|
newcell = getrandomadjcell(c, WE_WALKABLE, B_NOEXPAND);
|
|
} else {
|
|
newcell = c;
|
|
}
|
|
if (newcell) {
|
|
// add a snake there.
|
|
snake = addlf(newcell, raceposs[rnd(0,nraceposs-1)]->id, 1);
|
|
if (snake) {
|
|
msg("%s transforms into a snake!",obname);
|
|
killob(o);
|
|
killflagsofid(snake->flags, F_XPVAL);
|
|
addflag(snake->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
makefriendly(snake, PERMENANT);
|
|
ndone++;
|
|
}
|
|
} else {
|
|
msg("%s quivers for a moment.",obname);
|
|
ndone++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ndone) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_TELEPORT) {
|
|
cell_t *c = NULL;
|
|
lifeform_t *ally[8];
|
|
int nallies = 0;
|
|
|
|
// 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, caster, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (!c) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
} else 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 (c->type->solid) {
|
|
// ok, but you'll die!
|
|
} else if (!cellwalkable(caster, c, NULL)) {
|
|
// go somewhere nearby...
|
|
c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND);
|
|
if (!c) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// we can take up to 'power-1' allies with us.
|
|
if (caster == target) {
|
|
int dir;
|
|
for (dir = DC_N; (dir <= DC_NW) && (nallies < power); dir++) {
|
|
c = getcellindir(target->cell, dir);
|
|
if (c && c->lf && areallies(c->lf, caster)) {
|
|
ally[nallies] = c->lf;
|
|
nallies++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
teleportto(target, targcell, B_TRUE);
|
|
|
|
// now do allies
|
|
if (nallies) {
|
|
int i;
|
|
for (i = 0; i < nallies; i++) {
|
|
c = getrandomadjcell(targcell, WE_WALKABLE, B_ALLOWEXPAND);
|
|
if (c) {
|
|
teleportto(ally[i], c, 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, caster, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (where && haslos(caster, where)) {
|
|
if (where->obpile->first) {
|
|
// select object from cell...
|
|
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);
|
|
// TODO: start trail from the object
|
|
targcell = askcoords(buf, TT_MONSTER | TT_PLAYER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
}
|
|
|
|
// 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_THORNS) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
f = addtempflag(caster->flags, F_RETALIATE, 1, 4, DT_PIERCE, "sharp thorns", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} 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 && isundead(target)) {
|
|
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_TWIDDLE) {
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
if (!target || (target == caster)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(caster)) {
|
|
msg("You feel a wrenching sensation!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (isplayer(target)) {
|
|
msg("You feel a wrenching sensation!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
swapplaces(caster, target, B_FALSE);
|
|
} else if ((spellid == OT_S_SUMMONANIMALSSM) ||
|
|
(spellid == OT_S_SUMMONANIMALSMD) ||
|
|
(spellid == OT_S_SUMMONANIMALSLG)) {
|
|
int lifetime, nwant,ngot;
|
|
enum LFSIZE wantsize;
|
|
enum RACECLASS wantrc;
|
|
lifetime = (power * 9) + rnd(1,power*2);
|
|
nwant = rnd(2,3);
|
|
switch (spellid) {
|
|
case OT_S_SUMMONANIMALSSM:
|
|
wantrc = RC_ANIMAL;
|
|
wantsize = SZ_SMALL;
|
|
break;
|
|
case OT_S_SUMMONANIMALSMD:
|
|
wantrc = RC_ANIMAL;
|
|
wantsize = SZ_MEDIUM;
|
|
break;
|
|
case OT_S_SUMMONANIMALSLG:
|
|
wantrc = RC_ANIMAL;
|
|
wantsize = SZ_LARGE;
|
|
break;
|
|
default:
|
|
wantsize = SZ_ANY;
|
|
break;
|
|
}
|
|
ngot = summonlfs(caster, wantrc, wantsize, nwant, lifetime);
|
|
if (!ngot) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else {
|
|
if (haslos(player, caster->cell)) { // intentionally not using cansee
|
|
raceclass_t *rc;
|
|
rc = findraceclass(wantrc);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("%s appear around %s!", makeplural(rc->name), castername);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_SUMMONWEAPON) {
|
|
object_t *o;
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
if (getequippedob(target->pack, BP_WEAPON)) {
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_FALSE;
|
|
}
|
|
o = addob(target->pack, "energy blade");
|
|
if (o) {
|
|
if (canweild(target, o)) {
|
|
flag_t *f;
|
|
// announce
|
|
if (isplayer(target)) {
|
|
msg("A blade of pure energy forms in your hands!");
|
|
} else if (cansee(player, target)) {
|
|
msg("A blade of pure energy forms in %s%s hands!",castername,
|
|
getpossessive(castername));
|
|
}
|
|
weild(target, o);
|
|
// set its damage value
|
|
f = hasflag(o->flags, F_DAM);
|
|
if (f) {
|
|
char buf[BUFLEN];
|
|
free(f->text);
|
|
sprintf(buf, "2d%d",power);
|
|
f->text = strdup(buf);
|
|
}
|
|
} else {
|
|
killob(o);
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_FALSE;
|
|
}
|
|
} else if (spellid == OT_S_WALLOFICE) {
|
|
object_t *o;
|
|
int donesomething = B_FALSE;
|
|
cell_t *c;
|
|
int vdist = 0,hdist = 0;
|
|
int walldir;
|
|
int seen = B_FALSE;
|
|
flag_t *f;
|
|
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
|
|
|
|
// get vert distance
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) {
|
|
vdist++;
|
|
}
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) {
|
|
vdist++;
|
|
}
|
|
// get horz dist
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) {
|
|
hdist++;
|
|
}
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) {
|
|
hdist++;
|
|
}
|
|
|
|
if ((vdist == 0) && (hdist == 0)) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// select direction
|
|
if (vdist < hdist) {
|
|
walldir = D_N;
|
|
} else if (hdist < vdist) {
|
|
walldir = D_E;
|
|
} else {
|
|
if (onein(2)) {
|
|
walldir = D_N;
|
|
} else {
|
|
walldir = D_E;
|
|
}
|
|
}
|
|
|
|
if (walldir == D_N) {
|
|
// vert wall
|
|
for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_N)) {
|
|
if (!seen && haslos(player, c)) seen = B_TRUE;
|
|
o = addob(c->obpile, "wall of ice");
|
|
if (o) {
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) { f->val[0] = power*10; f->val[1] = power*10; }
|
|
addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL);
|
|
}
|
|
// knock lfs away
|
|
if (c->lf) {
|
|
knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
for (c = getcellindir(targcell, D_S) ;
|
|
(c == targcell) || c->lf || cellwalkable(NULL, c, NULL);
|
|
c = getcellindir(c, D_S)) {
|
|
if (!seen && haslos(player, c)) seen = B_TRUE;
|
|
o = addob(c->obpile, "wall of ice");
|
|
if (o) {
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) { f->val[0] = power*10; f->val[1] = power*10; }
|
|
addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL);
|
|
}
|
|
// knock lfs away
|
|
if (c->lf) {
|
|
knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
// horz wall
|
|
for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_E)) {
|
|
if (!seen && haslos(player, c)) seen = B_TRUE;
|
|
o = addob(c->obpile, "wall of ice");
|
|
if (o) {
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) { f->val[0] = power*10; f->val[1] = power*10; }
|
|
addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL);
|
|
}
|
|
// knock lfs away
|
|
if (c->lf) {
|
|
knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
for (c = getcellindir(targcell, D_W) ;
|
|
(c == targcell) || c->lf || cellwalkable(NULL, c, NULL);
|
|
c = getcellindir(c, D_W)) {
|
|
if (!seen && haslos(player, c)) seen = B_TRUE;
|
|
o = addob(c->obpile, "wall of ice");
|
|
if (o) {
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) { f->val[0] = power*10; f->val[1] = power*10; }
|
|
addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL);
|
|
}
|
|
// knock lfs away
|
|
if (c->lf) {
|
|
knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 30+power);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (seen) {
|
|
drawscreen();
|
|
msg("A wall of ice appears!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
|
|
} else if (spellid == OT_S_WATERJET) {
|
|
char lfname[BUFLEN];
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell,i;
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
// animation
|
|
//anim(caster->cell, targcell, '}', C_BLUE);
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
msg("%s fire%s a jet of high-pressure water.",castername,isplayer(caster) ? "" : "s");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
|
|
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell);
|
|
for (i = 1; i < nretcell; i++) {
|
|
target = haslf(retcell[i]);
|
|
if (target) {
|
|
int dir,amt, i;
|
|
object_t *arm[MAXBODYPARTS];
|
|
int narm = 0;
|
|
getlfname(target, lfname);
|
|
// hit
|
|
if (cansee(player, target)) {
|
|
msg("%s %s hit by a jet of water!",lfname, is(target));
|
|
}
|
|
// water damage will generally be turn to zero unless people are specifically
|
|
// vulnerable to water, so do bashing damage too.
|
|
losehp(target, roll("3d4"), DT_WATER, caster, "a high-pressure jet of water");
|
|
losehp(target, roll("3d4"), DT_BASH, caster, "a high-pressure jet of water");
|
|
// knock backwards
|
|
dir = getdirtowards(caster->cell, target->cell, target, B_FALSE, DT_COMPASS);
|
|
amt = (power/3); if (amt < 2) amt = 2;
|
|
knockback(target, dir, amt, caster, 0);
|
|
// rust
|
|
getallouterarmour(target, arm, &narm);
|
|
for (i = 0; i < narm; i++) {
|
|
takedamage(arm[i], R_TRUSTY, DT_WATER);
|
|
}
|
|
// add water object
|
|
addob(retcell[i]->obpile, "large puddle of water");
|
|
break;
|
|
} else {
|
|
damageallobs(NULL, retcell[i]->obpile, 0, DT_WATER);
|
|
// add water object
|
|
addob(retcell[i]->obpile, "large puddle of water");
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_WARPWOOD) {
|
|
object_t *o,*nexto;
|
|
flag_t *f;
|
|
int ndone = 0;
|
|
|
|
if (!validatespellcell(caster, &targcell,TT_OBJECT, spellid, power)) return B_TRUE;
|
|
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
// weilded weapons/armour
|
|
for (o = targcell->lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->type->material->id == MT_WOOD) {
|
|
if (isequipped(o)) {
|
|
int dam;
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
dam = rnd(f->val[0]/2,f->val[0]);
|
|
} else {
|
|
dam = roll("1d6");
|
|
}
|
|
takedamage(o, dam, DT_DIRECT);
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
ndone++;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->type->material->id == MT_WOOD) {
|
|
int dam;
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
dam = rnd(1,f->val[0]);
|
|
} else {
|
|
dam = roll("1d6");
|
|
}
|
|
takedamage(o, dam, DT_DIRECT);
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
ndone++;
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_WEAKEN) {
|
|
// ask for target
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
|
|
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_WEB) {
|
|
char numbuf[BUFLEN];
|
|
int i;
|
|
|
|
numtotext(power, numbuf);
|
|
if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power)) return B_TRUE;
|
|
// animation
|
|
anim(caster->cell, targcell, '^', C_GREY);
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
if (power == 1) {
|
|
msg("%s fire%s a burst of webbing.",castername,isplayer(caster) ? "" : "s");
|
|
} else {
|
|
msg("%s fire%s %s bursts of webbing.",castername,isplayer(caster) ? "" : "s", numbuf);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// create webs
|
|
for (i = 0; i < power; i++) {
|
|
cell_t *c;
|
|
if (i == 0) {
|
|
c = targcell;
|
|
} else {
|
|
c = getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND);
|
|
}
|
|
if (c) {
|
|
addob(c->obpile, "web");
|
|
if (c->lf && (c->lf->race->baseid != R_SPIDER)) {
|
|
if (isplayer(c->lf)) {
|
|
msg("You are stuck in a web!");
|
|
} else if (cansee(player, c->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("%s is stuck in a web!", lfname);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
} 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];
|
|
int i;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
// ask for target
|
|
if (spellid == OT_S_GIFT) {
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power)) return B_TRUE;
|
|
target = targcell->lf;
|
|
} 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);
|
|
addob(target->cell->obpile, buf);
|
|
if (nretobs) {
|
|
for (i = 0; i < nretobs; i++) {
|
|
char obname[BUFLEN];
|
|
o = retobs[i];
|
|
getobname(o, obname, o->amt);
|
|
if (!hasflag(o->flags, F_IMPASSABLE) && canpickup(target, o, ALL)) {
|
|
// you gain it.
|
|
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 {
|
|
// can't pick this up...
|
|
|
|
// impassable? goes in a nearby cell instead of at your feet.
|
|
if (hasflag(o->flags, F_IMPASSABLE)) {
|
|
cell_t *newloc;
|
|
// if so, don't put it where the player is!
|
|
newloc = real_getrandomadjcell(target->cell, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL);
|
|
o = relinkob(o, newloc->obpile);
|
|
}
|
|
if (o) {
|
|
noise(caster->cell, NULL, 1, "something hitting the ground.", NULL);
|
|
if (!isblind(caster)) {
|
|
msg("%s appear%s on the ground!", obname, (o->amt == 1) ? "s" : "");
|
|
}
|
|
} else {
|
|
// ob exists but couldn't make it appear
|
|
msg("The air in front of %s seems to ripple for a moment.", lfname);
|
|
break; // don't process any more.
|
|
}
|
|
}
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
objecttype_t *findspelln(char *buf) {
|
|
objecttype_t *ot;
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if (ot->obclass->id == OC_SPELL) {
|
|
if (!strcasecmp(ot->name, buf)) {
|
|
return ot;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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 OBTYPE getrandomspell(void) {
|
|
int wantlev;
|
|
objecttype_t *ot,*poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
|
|
wantlev = 1;
|
|
while (rnd(1,2) == 1) {
|
|
wantlev++;
|
|
}
|
|
|
|
// get list of all spells of this level
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if ((ot->obclass->id == OC_SPELL) && (getspelllevel(ot->id) == wantlev) ) {
|
|
poss[nposs++] = ot;
|
|
}
|
|
}
|
|
|
|
if (nposs <= 0) {
|
|
return OT_NONE;
|
|
}
|
|
ot = poss[rnd(0,nposs-1)];
|
|
return ot->id;
|
|
}
|
|
|
|
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_NATURE:
|
|
return SK_SS_NATURE;
|
|
case SS_FIRE:
|
|
return SK_SS_FIRE;
|
|
case SS_COLD:
|
|
return SK_SS_COLD;
|
|
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 "x MP" or "x-y MP", or "x-y MP, ongoing" ect
|
|
char *getspellcosttext(lifeform_t *lf, enum OBTYPE spellid, int power, char *buf) {
|
|
objecttype_t *ot;
|
|
int ongoing = B_FALSE;
|
|
int cost;
|
|
|
|
ot = findot(spellid);
|
|
if (!ot) {
|
|
strcpy(buf, "?invalidspellid?");
|
|
return buf;
|
|
}
|
|
|
|
cost = getmpcost(lf, spellid);
|
|
|
|
if (hasflag(ot->flags, F_ONGOING)) ongoing = B_TRUE;
|
|
|
|
if (hasflag(ot->flags, F_VARPOWER)) {
|
|
sprintf(buf, "%d-%d MP%s", cost,
|
|
cost * power, ongoing ? ", ongoing" : "");
|
|
} else {
|
|
sprintf(buf, "%d MP%s", cost, ongoing ? ", ongoing" : "");
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// abilities count as level 0
|
|
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, NULL);
|
|
if (power > 0) {
|
|
return power;
|
|
}
|
|
}
|
|
|
|
// player can only ever cast spells up to your level.
|
|
if (isplayer(lf) && !hasjob(lf, J_GOD)) {
|
|
if (getspelllevel(spellid) > lf->level) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// 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 = getspellschoolknown(lf, spellid);
|
|
spellskill = getskill(lf, SK_SPELLCASTING);
|
|
if (hasjob(lf, J_DRUID) && (school == SS_NATURE)) {
|
|
// druid doesn't use spellcasting skill for nature spells
|
|
} else {
|
|
// dont need spellcasting skill for mental/allomancy
|
|
switch (school) {
|
|
case SS_ALLOMANCY:
|
|
case SS_MENTAL:
|
|
break;
|
|
default:
|
|
if (spellskill == PR_INEPT) {
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasjob(lf, J_DRUID)) {
|
|
power = (lf->level/3) + statmod;
|
|
} else {
|
|
// 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(school));
|
|
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;
|
|
}
|
|
if (ot->obclass->id == OC_ABILITY) {
|
|
return SS_ABILITY;
|
|
}
|
|
f = hasflag(ot->flags, F_SPELLSCHOOL);
|
|
if (f) {
|
|
return f->val[0];
|
|
}
|
|
return SS_NONE;
|
|
}
|
|
|
|
enum SPELLSCHOOL getspellschoolknown(lifeform_t *lf, enum OBTYPE spellid) {
|
|
flag_t *f;
|
|
enum SPELLSCHOOL thisschool;
|
|
objecttype_t *ot;
|
|
|
|
ot = findot(spellid);
|
|
if (!ot) {
|
|
return SS_NONE;
|
|
}
|
|
|
|
if (ot->obclass->id == OC_ABILITY) {
|
|
return SS_ABILITY;
|
|
}
|
|
|
|
|
|
// find a school which we know about!
|
|
thisschool = SS_NONE;
|
|
for (f = ot->flags->first ; f ; f = f->next) {
|
|
if ((f->id == F_SPELLSCHOOL) && getskill(lf, getschoolskill(f->val[0]))) {
|
|
thisschool = f->val[0];
|
|
break;
|
|
}
|
|
}
|
|
// if we don't know any of the schools...
|
|
if (thisschool == SS_NONE) {
|
|
// just pick the first one.
|
|
f = hasflag(ot->flags, F_SPELLSCHOOL);
|
|
assert(f);
|
|
thisschool = f->val[0];
|
|
}
|
|
|
|
|
|
return thisschool;
|
|
}
|
|
|
|
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);
|
|
break;
|
|
case OT_S_CALLWIND:
|
|
range = (power*2);
|
|
break;
|
|
case OT_S_DIG:
|
|
range = power;
|
|
break;
|
|
case OT_S_LIGHTNINGBOLT:
|
|
range = (power*3);
|
|
break;
|
|
case OT_S_CHAINLIGHTNING:
|
|
range = (power*3);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return range;
|
|
}
|
|
|
|
char *getvarpowerspelldesc(enum OBTYPE spellid, int power, char *buf) {
|
|
// default
|
|
strcpy(buf, "");
|
|
switch (spellid) {
|
|
case OT_S_BARKSKIN:
|
|
sprintf(buf, "+%d Armour Rating, Fire Vulnerability", power*2);
|
|
break;
|
|
case OT_S_PSYARMOUR:
|
|
sprintf(buf, "+%d Armour Rating", power*4);
|
|
break;
|
|
case OT_S_SUMMONWEAPON:
|
|
sprintf(buf, "Create a 2d%d damage magical weapon",power);
|
|
break;
|
|
case OT_S_WINDSHIELD:
|
|
sprintf(buf, "Protection from missiles <= %d km/h",speedtokph(power));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// 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]);
|
|
}
|
|
}
|
|
}
|
|
|
|
int schoolappearsinbooks(enum SPELLSCHOOL ss) {
|
|
switch (ss) {
|
|
case SS_DIVINE:
|
|
case SS_ABILITY:
|
|
case SS_ALLOMANCY:
|
|
case SS_MENTAL:
|
|
return B_FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
void spellcloud(cell_t *srcloc, int radius, char ch, enum COLOUR col, enum OBTYPE sid, int power) {
|
|
int x,y;
|
|
if (ch != '\0') {
|
|
if (haslos(player, srcloc)) {
|
|
animradialorth(srcloc, radius, ch, col);
|
|
drawscreen();
|
|
}
|
|
}
|
|
for (y = srcloc->y - radius ; y <= srcloc->y + radius; y++) {
|
|
for (x = srcloc->x - radius ; x <= srcloc->x + radius; x++) {
|
|
cell_t *c;
|
|
c = getcellat(srcloc->map, x, y);
|
|
if (c && c->lf && (c != srcloc) && !c->type->solid &&
|
|
haslof(srcloc, c, LOF_WALLSTOP, NULL)) {
|
|
if (getcelldistorth(srcloc, c) <= radius) {
|
|
// fall asleep
|
|
dospelleffects(NULL, sid, power, c->lf, NULL, c, B_UNCURSED, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void stopspell(lifeform_t *caster, enum OBTYPE spellid) {
|
|
flag_t *f,*nextf;
|
|
object_t *o, *nexto;
|
|
|
|
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.
|
|
for (o = caster->pack->first; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->type->id == OT_ENERGYBLADE) {
|
|
if (cansee(player, caster)) {
|
|
char obname[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
getobname(o, obname, 1);
|
|
getlfname(caster, lfname);
|
|
msg("%s%s %s vanishes.", lfname, getpossessive(lfname),
|
|
obname);
|
|
}
|
|
killob(o);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// returns # created
|
|
int summonlfs(lifeform_t *caster, enum RACECLASS wantrc, enum LFSIZE wantsize, int howmany, int lifetime) {
|
|
lifeform_t *newlf;
|
|
race_t *r = NULL;
|
|
enum RACE poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
int i;
|
|
int ncreated = 0;
|
|
cell_t *c;
|
|
|
|
// determine possible types of race
|
|
for (r = firstrace ; r; r = r->next) {
|
|
int ok = B_FALSE;
|
|
if ((wantrc == RC_ANY) || (r->raceclass->id == wantrc)) {
|
|
flag_t *f;
|
|
if (wantsize == SZ_ANY) {
|
|
ok = B_TRUE;
|
|
} else {
|
|
f = hasflag(r->flags, F_SIZE);
|
|
if (f && (f->val[0] == wantsize)) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (ok) {
|
|
poss[nposs++] = r->id;
|
|
}
|
|
}
|
|
|
|
if (nposs == 0) {
|
|
return 0;
|
|
}
|
|
|
|
ncreated = 0;
|
|
for (i = 0; i < howmany; i++) {
|
|
// get random adjacent cell
|
|
c = getrandomadjcell(caster->cell, WE_EMPTY, B_ALLOWEXPAND);
|
|
if (!c) {
|
|
return ncreated;
|
|
}
|
|
// determine exact type of monster
|
|
r = findrace(poss[rnd(0,nposs-1)]);
|
|
if (r) {
|
|
// add it!
|
|
newlf = addmonster(c, r->id, B_FALSE, 1, B_FALSE, NULL);
|
|
// not worth any xp
|
|
killflagsofid(newlf->flags, F_XPVAL);
|
|
addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
// summoned
|
|
addflag(newlf->flags, F_SUMMONEDBY, caster->id, lifetime, NA, NULL);
|
|
addflag(newlf->flags, F_PETOF, caster->id, NA, NA, NULL);
|
|
if (areallies(player, caster)) {
|
|
makefriendly(newlf, PERMENANT);
|
|
}
|
|
ncreated++;
|
|
}
|
|
}
|
|
return ncreated;
|
|
}
|
|
|
|
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, user, maxrange, LOF_DONTNEED, B_FALSE);
|
|
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, enum OBTYPE spellid, int power) {
|
|
int maxrange = UNLIMITED;
|
|
int done = B_FALSE;
|
|
cell_t *where = NULL;
|
|
objecttype_t *sp;
|
|
int needlos = B_TRUE;
|
|
enum LOFTYPE needlof = LOF_NEED;
|
|
|
|
if (!caster) {
|
|
return *targcell;
|
|
}
|
|
|
|
sp = findot(spellid);
|
|
if (sp) {
|
|
flag_t *f;
|
|
f = hasflag(sp->flags, F_LOSLOF);
|
|
if (f) {
|
|
needlos = f->val[0];
|
|
needlof = f->val[1];
|
|
}
|
|
}
|
|
|
|
|
|
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)) {
|
|
if (isplayer(caster)) {
|
|
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 = newwhere;
|
|
}
|
|
} else {
|
|
where = NULL;
|
|
}
|
|
}
|
|
if (where && (maxrange != UNLIMITED) && (getcelldist(caster->cell, where) > maxrange)) {
|
|
// out of range
|
|
if (isplayer(caster)) {
|
|
msg("Too far away - max range is %d.",maxrange); more();
|
|
}
|
|
where = NULL;
|
|
}
|
|
|
|
if (where && where->lf && haslos(caster, where) && isplayer(caster) && areallies(caster, where->lf)) {
|
|
// 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)) {
|
|
char ques[BUFLEN];
|
|
int ch;
|
|
if (isplayer(where->lf)) {
|
|
sprintf(ques,"Really target yourself");
|
|
} else {
|
|
char lfname[BUFLEN];
|
|
getlfname(where->lf, lfname);
|
|
sprintf(ques, "Really target %s", lfname);
|
|
}
|
|
ch = askchar(ques,"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, caster, maxrange, needlof, needlof ? B_TRUE : B_FALSE);
|
|
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 (!caster) {
|
|
return *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, caster, UNLIMITED);
|
|
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;
|
|
}
|
|
*/
|