16106 lines
432 KiB
C
16106 lines
432 KiB
C
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "ai.h"
|
|
#include "attack.h"
|
|
#include "defs.h"
|
|
#include "flag.h"
|
|
#include "god.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 recipe_t *firstrecipe, *lastrecipe;
|
|
extern map_t *heaven;
|
|
extern region_t *firstregion;
|
|
extern knowledge_t *knowledge;
|
|
|
|
extern int needredraw;
|
|
|
|
extern prompt_t prompt;
|
|
|
|
extern WINDOW *msgwin;
|
|
extern WINDOW *gamewin;
|
|
|
|
extern void *rdata;
|
|
|
|
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;
|
|
|
|
extern condset_t ccwalkable;
|
|
extern condset_t ccwalkableroom;
|
|
|
|
// return value of FALSE means that stamina costs should be charged.
|
|
// TRUE is a cancellation - don't change stamina
|
|
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],racestr[BUFLEN];
|
|
objecttype_t *ot;
|
|
object_t *fromob = NULL;
|
|
char fromobname[BUFLEN];
|
|
flag_t *f;
|
|
|
|
strcpy(fromobname, "");
|
|
if (user && cwflag && (cwflag->obfrom != -1)) {
|
|
fromob = findobbyid(user->pack, cwflag->obfrom);
|
|
if (fromob) {
|
|
real_getobname(fromob, fromobname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
}
|
|
}
|
|
|
|
getlfname(user, username);
|
|
real_getlfname(user,killername, NULL, B_NOSHOWALL, B_REALRACE);
|
|
|
|
// defaults
|
|
strcpy(damstr,"");
|
|
power = 0;
|
|
needgrab = B_FALSE;
|
|
|
|
if (cwflag && strlen(cwflag->text)) {
|
|
texttospellopts(cwflag->text, "pw:", &power, "dam:", damstr, "needgrab:", &needgrab, "range:", &range, "range:", racestr, NULL);
|
|
}
|
|
|
|
// 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_BUILD) {
|
|
objecttype_t *tempot = NULL;
|
|
int dir;
|
|
char ch = 'a';
|
|
cell_t *c;
|
|
object_t *o;
|
|
int numtobuild = 1;
|
|
enum SKILLLEVEL slev;
|
|
enum OBTYPE needob = OT_NONE;
|
|
int numneed = 0;
|
|
if (!isplayer(user)) return B_TRUE;
|
|
|
|
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 build anything while swimming!");
|
|
return B_TRUE;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't build anything while stuck!");
|
|
return B_TRUE;
|
|
} else if (isairborne(user, NULL)) {
|
|
if (isplayer(user)) msg("You can't build anything while airborne!");
|
|
return B_TRUE;
|
|
}
|
|
slev = getskill(user, SK_ENGINEERING);
|
|
|
|
dir = askdir("Build something in which direction (. for self, - to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_MYSELF) {
|
|
c = NULL;
|
|
initprompt(&prompt, "What will you build?");
|
|
if (slev >= PR_NOVICE) {
|
|
addbuildchoice(&prompt, user, OT_ARROW, &ch);
|
|
addbuildchoice(&prompt, user, OT_BOLT, &ch);
|
|
}
|
|
} else if (dir < 0) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else {
|
|
c = getcellindir(user->cell, dir);
|
|
|
|
if (c->lf || c->type->solid) {
|
|
msg("There is no space to build anything there!");
|
|
return B_TRUE;
|
|
}
|
|
// what can we build?
|
|
initprompt(&prompt, "What will you build?");
|
|
if (hasdoor(c)) {
|
|
if (slev >= PR_BEGINNER) {
|
|
// collapsing door
|
|
addbuildchoice(&prompt, user, OT_TRAPDOORFALL, &ch);
|
|
}
|
|
|
|
if (slev >= PR_EXPERT) {
|
|
// other door traps
|
|
addbuildchoice(&prompt, user, OT_TRAPNEEDLEP, &ch);
|
|
addbuildchoice(&prompt, user, OT_TRAPFIRE, &ch);
|
|
addbuildchoice(&prompt, user, OT_TRAPMINE, &ch);
|
|
}
|
|
}
|
|
// floor traps?
|
|
if (cellwalkable(NULL, c, NULL)) {
|
|
if (slev >= PR_BEGINNER) {
|
|
addbuildchoice(&prompt, user, OT_TRAPTRIP, &ch);
|
|
addbuildchoice(&prompt, user, OT_BARRICADE, &ch);
|
|
addbuildchoice(&prompt, user, OT_RUBBLE, &ch);
|
|
addbuildchoice(&prompt, user, OT_FENCEWOOD, &ch);
|
|
}
|
|
if (slev >= PR_ADEPT) {
|
|
addbuildchoice(&prompt, user, OT_TRAPARROW, &ch);
|
|
}
|
|
if (slev >= PR_EXPERT) {
|
|
addbuildchoice(&prompt, user, OT_TRAPROCK, &ch);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prompt.nchoices <= 0) {
|
|
if (c) {
|
|
msg("There is nothing that you can build there.");
|
|
} else {
|
|
msg("There is nothing that you can currently build.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
prompt.maycancel = B_TRUE;
|
|
ch = getchoice(&prompt);
|
|
tempot = (objecttype_t *)prompt.result;
|
|
if (!tempot || (ch == '\0')) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
// remove ingredients
|
|
canbuild(user, tempot, NULL, &needob, &numneed); // just using this to get ingredients and count
|
|
if (needob != OT_NONE) {
|
|
o = hasob(user->pack, needob);
|
|
if (o && (o->amt >= numneed)) {
|
|
if (c) {
|
|
// just build it
|
|
removeob(o, numneed);
|
|
} else {
|
|
int npossible;
|
|
npossible = (o->amt / numneed);
|
|
// ask how many
|
|
if (npossible >= 2) {
|
|
char ptext[BUFLEN],buf[BUFLEN];
|
|
char *p;
|
|
p = strdup(tempot->name);
|
|
makeplural(&p);
|
|
sprintf(ptext, "How many %s will you build (max %d)", p, npossible);
|
|
free(p);
|
|
askstring(ptext, '?', buf, BUFLEN, "1");
|
|
numtobuild = atoi(buf);
|
|
if (numtobuild <= 0) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
removeob(o, numneed*numtobuild);
|
|
}
|
|
} else {
|
|
msg("You don't have the required components to build that.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
if (c) {
|
|
// create the trap/obstacle/whatever
|
|
o = hasdoor(c);
|
|
if (o) {
|
|
addflag(o->flags, F_TRAPPED, tempot->id, NA, B_TRUE, NULL);
|
|
o = NULL;
|
|
} else {
|
|
o = addobfast(c->obpile, tempot->id);
|
|
if (o) {
|
|
killflagsofid(o->flags, F_SECRET);
|
|
} else {
|
|
msg("For some reason, you can't build there.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
char buf[BUFLEN];
|
|
sprintf(buf, "%d original %s", numtobuild, tempot->name);
|
|
o = addob(user->pack, buf);
|
|
}
|
|
|
|
taketime(user, getactspeed(user)*2);
|
|
if (c) {
|
|
setlosdirty(user);
|
|
msg("You build %s %s.", needan(tempot->name) ? "an" : "a", tempot->name);
|
|
} else {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msgnocap("%c - %s", o->letter, obname);
|
|
}
|
|
|
|
} else if (abilid == OT_A_CHARGE) {
|
|
cell_t *adjcell = NULL,*origcell;
|
|
char targetname[BUFLEN];
|
|
|
|
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;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't charge while stuck!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!range) {
|
|
int movespeed;
|
|
// get max range - based on speed.
|
|
movespeed = getmovespeed(user);
|
|
if (movespeed > SP_NORMAL) {
|
|
if (isplayer(user)) msg("You are too slow to charge!");
|
|
return B_TRUE;
|
|
}
|
|
range = 3 + ((SP_NORMAL - movespeed)/5);
|
|
}
|
|
|
|
if (!targcell) {
|
|
if (isplayer(user)) {
|
|
snprintf(buf, BUFLEN, "Charge who (max range %d)?",range);
|
|
// TODO: ask for direction
|
|
targcell = askcoords(buf, "Charge->", 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;
|
|
} else if (getcelldist(user->cell, targcell) < 2) {
|
|
if (isplayer(user)) msg("You don't have enough space to charge there!");
|
|
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_real(user->cell, targcell, LOF_NEED, NULL, user, B_FALSE)) {
|
|
if (isplayer(user)) msg("Your path there is blocked!");
|
|
return TRUE;
|
|
}
|
|
|
|
getlfname(target, targetname);
|
|
|
|
// find cell on the way...
|
|
adjcell = get_closest_adjcell(user->cell, targcell);
|
|
if (!adjcell) {
|
|
if (isplayer(user)) {
|
|
msg("There is no space nearby for you to attack!");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
if (adjcell->lf) {
|
|
if (isplayer(user)) msg("Your path there is blocked!");
|
|
return TRUE;
|
|
}
|
|
|
|
// take some time
|
|
taketime(user, getactspeed(user));
|
|
|
|
// remember orig cell
|
|
origcell = user->cell;
|
|
|
|
// teleport next to them
|
|
movelf(user, adjcell, B_FALSE);
|
|
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:
|
|
case R_CATTIGER:
|
|
case R_CATPANTHER:
|
|
strcpy(verb, "leap");
|
|
break;
|
|
default:
|
|
strcpy(verb, "charge");
|
|
break;
|
|
}
|
|
// check baseid too
|
|
if (user->race->baseid == R_LEECH) {
|
|
strcpy(verb, "leap");
|
|
}
|
|
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, B_TRUE);
|
|
}
|
|
} else if (abilid == OT_A_CHECKSTAIRS) {
|
|
char obname[BUFLEN], buf[BUFLEN],ch;
|
|
int madenewmap = B_TRUE;
|
|
cell_t *origcell = NULL;
|
|
lifeform_t *inway = NULL, *movedlf = NULL;
|
|
cell_t *c;
|
|
object_t *stairs;
|
|
int stairdir, needsclimb = B_FALSE;
|
|
char climbprompt[BUFLEN];
|
|
|
|
if (!isplayer(user)) {
|
|
return B_FALSE;
|
|
}
|
|
// are there stairs here?
|
|
stairs = hasobwithflag(user->cell->obpile, F_CLIMBABLE);
|
|
if (!stairs) {
|
|
msg("There are no stairs here!");
|
|
return B_TRUE;
|
|
}
|
|
stairdir = getstairdirection(stairs);
|
|
|
|
if ((stairdir != D_UP) && (stairdir != D_DOWN)) {
|
|
// slightly different message from above, for debugging
|
|
msg("There are no stairs here to check!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
// are stairs on the roof?
|
|
if (isinroof(stairs) && !canreachroof(user)) {
|
|
needsclimb = B_TRUE;
|
|
snprintf(climbprompt, BUFLEN, "Climb to check %%s");
|
|
}
|
|
|
|
// how can we check the stairs?
|
|
getobname(stairs, obname, 1);
|
|
sprintf(buf, "How will you check %s", obname);
|
|
initprompt(&prompt, buf);
|
|
if (lfhasflag(user, F_DETECTLIFE)) {
|
|
addchoice(&prompt, 'd', "Detect lifeforms", NULL, NULL, NULL);
|
|
}
|
|
if ( ((getskill(user, SK_LISTEN) >= PR_ADEPT) && !isdeaf(user)) ||
|
|
hasequippedobid(user->pack, OT_AMU_THIEF)) {
|
|
addchoice(&prompt, 'l', "Listen for sounds", NULL, NULL, NULL);
|
|
}
|
|
if ((getskill(user, SK_PERCEPTION)) && !isblind(user)) {
|
|
if (needsclimb) {
|
|
addchoice(&prompt, 'f', "Check for footprints (requires climbing)", NULL, NULL, NULL);
|
|
snprintf(climbprompt, BUFLEN, "Climb to check %%s for footprints");
|
|
} else {
|
|
addchoice(&prompt, 'f', "Check for footprints", NULL, NULL, NULL);
|
|
}
|
|
|
|
}
|
|
if (getskill(user, SK_STEALTH) >= PR_SKILLED) {
|
|
if (needsclimb) {
|
|
addchoice(&prompt, 'p', "Peek at the other end (requires climbing)", NULL, NULL, NULL);
|
|
snprintf(climbprompt, BUFLEN, "Climb to peek through %%s");
|
|
} else {
|
|
addchoice(&prompt, 'p', "Peek at the other end", NULL, NULL, NULL);
|
|
}
|
|
}
|
|
if (lfhasflag(user, F_ENHANCESMELL)) {
|
|
addchoice(&prompt, 's', "Sniff for scents", NULL, NULL, NULL);
|
|
}
|
|
addchoice(&prompt, '-', "(cancel)", NULL, NULL, NULL);
|
|
prompt.maycancel = B_TRUE;
|
|
if (prompt.nchoices == 1) {
|
|
msg("You have no way to check %s.", obname);
|
|
return B_TRUE;
|
|
}
|
|
ch = getchoice(&prompt);
|
|
|
|
if ((ch == '-') || (ch == '\0')) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (needsclimb) {
|
|
// must try to climb first.
|
|
if (asktoclimb(stairs, climbprompt)) {
|
|
// failed
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// announce the check, so that the player has feedback
|
|
// if map generation takes a while.
|
|
if (ch == 'd') {
|
|
msg("You concentrate on %s...",obname);
|
|
} else if (ch == 'l') {
|
|
msg("You listen at %s...",obname);
|
|
} else if (ch == 's') { // smell
|
|
msg("You sniff %s...",obname);
|
|
} else if (ch == 'f') { // footprints
|
|
msg("You inspect %s...",obname);
|
|
} else if (ch == 'p') { // peek
|
|
msg("You peek %s the stairs...", getdirname(stairdir));
|
|
}
|
|
|
|
// find out where the other end goes...
|
|
user->changinglev = B_TRUE;
|
|
c = getstairdestination(stairs, &madenewmap);
|
|
|
|
// show --more-- after we have the destination
|
|
more();
|
|
|
|
if (!c) {
|
|
msg("These stairs don't seem to go anywhere!");
|
|
user->changinglev = B_FALSE;
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
redrawpause();
|
|
|
|
// move any lfs at the other end out of the way.
|
|
if (c->lf) {
|
|
if (movelfsoutofway(c)) {
|
|
// can't move them? ie. no adj cells.
|
|
inway = c->lf;
|
|
} else {
|
|
movedlf = c->lf;
|
|
}
|
|
}
|
|
|
|
if (!inway) {
|
|
// temporarily put the player at the other end
|
|
// purposely not using movelf() because we don't
|
|
// want to trigger map enter effects.
|
|
origcell = user->cell;
|
|
origcell->lf = NULL;
|
|
user->cell = c;
|
|
c->lf = user;
|
|
}
|
|
|
|
// now actually do the check
|
|
if (ch == 'd') {
|
|
lifeform_t *lf;
|
|
char *dname[MAXCANDIDATES],thisdname[BUFLEN];
|
|
int nposs = 0,n,dcount[MAXCANDIDATES];
|
|
int maxdist,showreal = B_FALSE;
|
|
f = lfhasflag(user, F_DETECTLIFE);
|
|
assert(f);
|
|
maxdist = f->val[0];
|
|
if (f->val[1] == B_TRUE) showreal = B_TRUE;
|
|
|
|
// get all lfs which are within flag v0 of the other end
|
|
for (lf = c->map->lf ; lf ; lf = lf->next) {
|
|
if (lf == user) continue;
|
|
// within maxdist of stairs?
|
|
if (getcelldist(lf->cell, c) <= maxdist) {
|
|
int dirtolf;
|
|
dirtolf = getdirtowards(c, lf->cell, NULL, B_FALSE, DT_ORTH);
|
|
int found = B_FALSE;
|
|
if (showreal) {
|
|
getlfnamea(lf, thisdname);
|
|
} else {
|
|
// detected size - "large monster" etc
|
|
snprintf(thisdname, BUFLEN, "%s monster", getsizetext(getlfsize(lf)));
|
|
}
|
|
|
|
// already a name like this?
|
|
for (n = 0; n < nposs; n++) {
|
|
if (streq(dname[n], thisdname)) {
|
|
dcount[n]++;
|
|
found = B_TRUE;
|
|
}
|
|
}
|
|
if (!found) {
|
|
if (showreal) {
|
|
dname[nposs] = strdup(noprefix(thisdname));
|
|
} else {
|
|
dname[nposs] = strdup(thisdname);
|
|
}
|
|
dcount[nposs] = 1;
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
user->changinglev = B_FALSE;
|
|
// announce
|
|
if (nposs) {
|
|
for (n = 0; n < nposs; n++) {
|
|
char amttext[BUFLEN];
|
|
if (dcount[n] == 1) {
|
|
strcpy(amttext, "");
|
|
} else if (dcount[n] <= 3) {
|
|
strcpy(amttext, "several ");
|
|
} else { // 4+
|
|
strcpy(amttext, "lots of ");
|
|
}
|
|
msg("You detect %s%s.", amttext, dname[n]);
|
|
}
|
|
} else {
|
|
msg("You don't detect any living creatures.");
|
|
}
|
|
} else if (ch == 'l') {
|
|
lifeform_t *lf;
|
|
enum SKILLLEVEL slev;
|
|
char *movetext[MAXCANDIDATES],thismovetext[BUFLEN];
|
|
int nposs = 0,n,movetextcount[MAXCANDIDATES],found,vol;
|
|
|
|
slev = getskill(user, SK_LISTEN);
|
|
|
|
if (inway) {
|
|
// just get the lf in the way
|
|
if (getnoisedetails(inway, N_WALK, NULL, thismovetext, NULL, &vol, NULL)) {
|
|
// doesn't make noise
|
|
nposs = 0;
|
|
} else {
|
|
if (slev >= PR_EXPERT) {
|
|
// overwrite name
|
|
real_getlfnamea(inway, thismovetext, NULL, B_NOSHOWALL, B_CURRACE);
|
|
}
|
|
movetext[0] = strdup(thismovetext);
|
|
movetextcount[0] = 1;
|
|
nposs = 1;
|
|
}
|
|
} else {
|
|
int nwalls;
|
|
// get all lfs within direct hearing (not through walls) of the other end
|
|
for (lf = c->map->lf ; lf ; lf = lf->next) {
|
|
if (lf == user) continue;
|
|
// get movement text
|
|
if (getnoisedetails(lf, N_WALK, NULL, thismovetext, NULL, &vol, NULL)) continue;
|
|
if (slev >= PR_EXPERT) {
|
|
// overwrite name
|
|
real_getlfnamea(lf, thismovetext, NULL, B_NOSHOWALL, B_CURRACE);
|
|
}
|
|
if (canhear(user, lf->cell, vol, &nwalls) && (nwalls == 0)) {
|
|
// already have this text?
|
|
found = B_FALSE;
|
|
for (n = 0; n < nposs; n++) {
|
|
if (streq(movetext[n], thismovetext)) {
|
|
movetextcount[n]++;
|
|
found = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
movetext[nposs] = strdup(thismovetext);
|
|
movetextcount[nposs] = 1;
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// announce
|
|
if (nposs) {
|
|
for (n = 0; n < nposs; n++) {
|
|
char amttext[BUFLEN];
|
|
if (slev >= PR_EXPERT) { // we're hearing monster names
|
|
if (movetextcount[n] == 1) {
|
|
strcpy(amttext, "");
|
|
} else if (movetextcount[n] <= 3) {
|
|
strcpy(amttext, "some ");
|
|
} else {
|
|
strcpy(amttext, "lots of ");
|
|
}
|
|
} else { // we're hearing monster sounds
|
|
strcpy(amttext, "");
|
|
}
|
|
|
|
if (slev >= PR_EXPERT) {
|
|
char *newtext;
|
|
if (movetextcount[n] > 1) {
|
|
newtext = strdup(movetext[n]);
|
|
makeplural(&newtext);
|
|
msg("You can hear %s%s.", amttext, noprefix(newtext));
|
|
free(newtext);
|
|
} else {
|
|
msg("You can hear %s.", movetext[n]);
|
|
}
|
|
} else {
|
|
msg("You can hear %s%s", amttext, movetext[n]);
|
|
}
|
|
// free this mem
|
|
free(movetext[n]);
|
|
}
|
|
|
|
} else {
|
|
msg("You don't hear anything unusual.");
|
|
}
|
|
} else if (ch == 's') { // smell
|
|
lifeform_t *lf;
|
|
race_t *smellrace[MAXCANDIDATES];
|
|
int nposs = 0,n,smellcount[MAXCANDIDATES], range = 0;
|
|
|
|
f = lfhasflag(user, F_ENHANCESMELL);
|
|
if (f) {
|
|
range = f->val[0];
|
|
}
|
|
|
|
if (range) {
|
|
// list everyone you can smell.
|
|
if (inway) {
|
|
// just smell the lf in the way
|
|
if (issmellablelf(inway)) {
|
|
smellrace[0] = inway->race;
|
|
smellcount[0] = 1;
|
|
nposs = 1;
|
|
} else {
|
|
nposs = 0;
|
|
}
|
|
} else {
|
|
// get all lfs you can smell from the other end
|
|
for (lf = c->map->lf ; lf ; lf = lf->next) {
|
|
int found;
|
|
if (lf == user) continue;
|
|
if (getcelldist(lf->cell, c) <= range) {
|
|
// already have this one?
|
|
found = B_FALSE;
|
|
for (n = 0; n < nposs; n++) {
|
|
if (smellrace[n] == lf->race) {
|
|
smellcount[n]++;
|
|
found = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
smellrace[nposs] = lf->race;
|
|
smellcount[nposs] = 1;
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// announce
|
|
if (nposs) {
|
|
for (n = 0; n < nposs; n++) {
|
|
char amttext[BUFLEN];
|
|
char *newtext;
|
|
if (smellcount[n] == 1) {
|
|
strcpy(amttext, "");
|
|
} else if (smellcount[n] <= 3) {
|
|
strcpy(amttext, "some ");
|
|
} else {
|
|
strcpy(amttext, "lots of ");
|
|
}
|
|
|
|
if (smellcount[n] > 1) {
|
|
newtext = strdup(smellrace[n]->name);
|
|
makeplural(&newtext);
|
|
msg("You can smell %s%s.", amttext, newtext);
|
|
free(newtext);
|
|
} else {
|
|
msg("You can smell %s %s.", needan(smellrace[n]->name) ? "an" : "a", smellrace[n]->name);
|
|
}
|
|
}
|
|
} else {
|
|
msg("You don't smell anything unusual.");
|
|
}
|
|
} else if (ch == 'f') { // footprints
|
|
lifeform_t *lf;
|
|
object_t *trailob;
|
|
char *fpname[MAXCANDIDATES],thisfpname[BUFLEN];
|
|
int nposs = 0,n,fpcount[MAXCANDIDATES];
|
|
|
|
if (inway) {
|
|
// just do footprints for the lf in the way
|
|
trailob = addtrail(inway, c, inway->facing, B_TRUE, B_FALSE);
|
|
if (trailob) {
|
|
getobname(trailob, thisfpname, 1);
|
|
fpname[0] = strdup(thisfpname);
|
|
fpcount[0] = 1;
|
|
nposs = 1;
|
|
killob(trailob);
|
|
} else {
|
|
nposs = 0;
|
|
}
|
|
} else {
|
|
// get all lfs which make trails from the other end
|
|
for (lf = c->map->lf ; lf ; lf = lf->next) {
|
|
if (lf == user) continue;
|
|
// within lof of stairs?
|
|
if (haslof(lf->cell, c, LOF_WALLSTOP, NULL)) {
|
|
int dirtolf;
|
|
dirtolf = getdirtowards(c, lf->cell, NULL, B_FALSE, DT_ORTH);
|
|
trailob = addtrail(lf, c, dirtolf, B_TRUE, B_FALSE);
|
|
if (trailob) {
|
|
if (canseeob(user, trailob)) {
|
|
int found = B_FALSE;
|
|
// already a trail like this?
|
|
getobname(trailob, thisfpname, 1);
|
|
for (n = 0; n < nposs; n++) {
|
|
if (streq(fpname[n], thisfpname)) {
|
|
fpcount[n]++;
|
|
found = B_TRUE;
|
|
}
|
|
}
|
|
if (!found) {
|
|
fpname[nposs] = strdup(thisfpname);
|
|
fpcount[nposs] = 1;
|
|
nposs++;
|
|
}
|
|
}
|
|
killob(trailob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
user->changinglev = B_FALSE;
|
|
// announce
|
|
if (nposs) {
|
|
for (n = 0; n < nposs; n++) {
|
|
char amttext[BUFLEN];
|
|
if (fpcount[n] == 1) {
|
|
strcpy(amttext, "");
|
|
} else if (fpcount[n] <= 3) {
|
|
strcpy(amttext, "several ");
|
|
} else { // 4+
|
|
strcpy(amttext, "lots of ");
|
|
}
|
|
msg("You find %s%s.", amttext, fpname[n]);
|
|
}
|
|
} else {
|
|
msg("You don't find any unusual tracks.");
|
|
}
|
|
} else if (ch == 'p') { // peek
|
|
flag_t *awareness;
|
|
// process light sources for the other end (otherwise new
|
|
// maps will be dark by default and you might not see anything)
|
|
//calclight(c->map);
|
|
// prevent announcement of the flag we're about to give
|
|
user->born = B_FALSE;
|
|
// temporarily give the player 360degree sight
|
|
awareness = addflag(user->flags, F_AWARENESS, B_TRUE, NA, NA, NULL);
|
|
// we now need born to be true so that precalclos() works.
|
|
user->born = B_TRUE;
|
|
|
|
// allow redraws
|
|
redrawresume();
|
|
setlosdirty(user); // this will redraw the screen
|
|
askcoords("Peek (ESC when done)->", "Peek (ESC when done)->", TT_NONE, user, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
user->born = B_FALSE;
|
|
killflag(awareness);
|
|
user->born = B_TRUE;
|
|
} else {
|
|
msg("Not implemented yet.");
|
|
} // end ways of checking
|
|
|
|
if (!inway) {
|
|
// move user back
|
|
user->cell->lf = NULL;
|
|
user->cell = origcell;
|
|
origcell->lf = user;
|
|
}
|
|
|
|
if (movedlf) {
|
|
// move lf back
|
|
movelf(movedlf, c, B_FALSE);
|
|
}
|
|
// resume redrawing capability.
|
|
// might have already been done if we used the 'p'eek commaned (see above).
|
|
redrawresume();
|
|
|
|
if (ch == 'p') {
|
|
// if we peeked, then we have to redraw now.
|
|
setlosdirty(user);
|
|
if (needsclimb) {
|
|
msg("You drop to the ground.");
|
|
} else {
|
|
msg("You return to your original position.");
|
|
}
|
|
} else if (needsclimb) {
|
|
msg("You drop to the ground.");
|
|
}
|
|
|
|
taketime(user, getactspeed(user)*2);
|
|
} else if (abilid == OT_A_CLIMB) {
|
|
enum ERROR why;
|
|
int origdir = D_NONE;
|
|
|
|
// for ai only: turn to face target cell first.
|
|
if (!isplayer(user) && targcell) {
|
|
turntoface(user, targcell);
|
|
}
|
|
if (isplayer(user) && !isorthogonal(user->facing)) {
|
|
int dir;
|
|
dir = askdir("Climb in which direction (orthogonal only, - to cancel)", B_TRUE, B_FALSE);
|
|
if ((dir == D_NONE) || (dir == D_MYSELF) || !isorthogonal(dir)) {
|
|
msg("You can only climb in orthogonal directions.");
|
|
return B_TRUE;
|
|
} else {
|
|
origdir = user->facing;
|
|
setfacing(user, dir);
|
|
targcell = getcellindir(user->cell, user->facing);
|
|
}
|
|
}
|
|
|
|
if (!canclimb(user, &why)) {
|
|
if (isplayer(user)){
|
|
switch (why) {
|
|
case E_CLIMBING: msg("You are already climbing!"); break;
|
|
case E_CANTMOVE: msg("You can't move!"); break;
|
|
case E_BADCLIMBDIR: msg("There is no wall to climb in front of you!"); break;
|
|
case E_BADCLIMBDIR2: msg("You can only climb in orthogonal directions."); break;
|
|
case E_LFINWAY: msg("Something is in the way!"); break;
|
|
case E_SWIMMING: msg("You can't climb while swimming!"); break;
|
|
case E_TOOHEAVY: msg("Your load is too heavy to climb with!"); break;
|
|
case E_NOABIL: msg("You cannot climb!"); break;
|
|
default: msg("For some reason, you can't climb."); break;
|
|
}
|
|
}
|
|
if (origdir != D_NONE) {
|
|
setfacing(user, origdir);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
startclimbing(user);
|
|
} else if (abilid == OT_A_COOK) {
|
|
object_t *o;
|
|
recipe_t *rec;
|
|
char *longdesc,ch;
|
|
int i;
|
|
cell_t fakecell;
|
|
map_t fakemap;
|
|
char obname[BUFLEN];
|
|
int donesomething = B_TRUE;
|
|
int ncooked = 0;
|
|
int cooktime = 0;
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
while (donesomething) {
|
|
object_t *corpse = NULL;
|
|
donesomething = B_FALSE;
|
|
// anything here to cook?
|
|
for (o = user->cell->obpile->first ; o ; o = o->next) {
|
|
if (iscorpse(o) && !hasflag(o->flags, F_PREPARED)) {
|
|
char yn;
|
|
char ques[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
sprintf(ques, "There %s %s here. Cook it?", OB1(o,"is","are"),obname);
|
|
yn = askchar(ques, "yn","n", B_TRUE, B_FALSE);
|
|
if (yn == 'y') {
|
|
corpse = o;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (corpse) {
|
|
getobname(corpse, obname, o->amt);
|
|
if (getobsize(corpse) > (getskill(user, SK_COOKING)+1)) {
|
|
msg("%s is too large for you to cook (Cooking skill too low).", obname);
|
|
ncooked++;
|
|
break;
|
|
} else if (isimmuneto(corpse->flags, DT_FIRE, B_FALSE)) {
|
|
msg("^wYou attempt to cook %s, but it won't heat up.^n", obname);
|
|
cooktime += getactspeed(user);
|
|
ncooked++;
|
|
break;
|
|
} else {
|
|
preparecorpse(user, corpse);
|
|
if (isresistantto(corpse->flags, DT_FIRE, B_FALSE)) {
|
|
// takes longer
|
|
cooktime += (getactspeed(user)*2);
|
|
} else {
|
|
cooktime += getactspeed(user);
|
|
}
|
|
practice(user, SK_COOKING, 1);
|
|
ncooked++;
|
|
if (isplayer(user)) pleasegodmaybe(R_GODNATURE, 1);
|
|
}
|
|
// set donesomething even if the actual cooking failed.
|
|
donesomething = B_TRUE;
|
|
corpse = NULL;
|
|
}
|
|
} // end while donesomething
|
|
|
|
if (ncooked) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
// didn't cook anything from the ground?
|
|
// make recipes.
|
|
createfakes(&fakemap, &fakecell);
|
|
|
|
longdesc = malloc(HUGEBUFLEN * sizeof(char));
|
|
initprompt(&prompt, "What will you cook (ESC to cancel)?");
|
|
for (rec = firstrecipe ; rec ; rec = rec->next ) {
|
|
if (cancook(user, rec, NULL)) {
|
|
object_t *o;
|
|
o = addobfast(fakecell.obpile, rec->result);
|
|
makedesc_ob(o, longdesc);
|
|
addchoice(&prompt, 'a', o->type->name, NULL, rec, longdesc);
|
|
killob(o);
|
|
}
|
|
}
|
|
free(longdesc);
|
|
killfakes(&fakemap, &fakecell);
|
|
|
|
addchoice(&prompt, '\0', "nothing", NULL, NULL, NULL);
|
|
prompt.maycancel = B_TRUE;
|
|
ch = getchoicestr(&prompt, B_FALSE, B_TRUE);
|
|
rec = (recipe_t *)prompt.result;
|
|
if (!rec) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else {
|
|
object_t *retob[MAXRECIPEINGREDIENTS];
|
|
int retcount[MAXRECIPEINGREDIENTS];
|
|
int retconsume[MAXRECIPEINGREDIENTS];
|
|
int nretobs;
|
|
char obname[BUFLEN];
|
|
enum RACE corpsetype = R_NONE;
|
|
// try to cook this...
|
|
// give the new object
|
|
o = addobfast(user->pack, rec->result);
|
|
if (o) {
|
|
// now remove the ingredients...
|
|
getingredients(user->pack, rec, retob, retcount, retconsume, &nretobs, B_TRUE);
|
|
for (i = 0; i < nretobs; i++) {
|
|
if (retconsume[i]) {
|
|
// remember corpse types
|
|
if (retob[i]->type->id == OT_CORPSE) {
|
|
f = hasflag(retob[i]->flags, F_CORPSEOF);
|
|
if (f) corpsetype = f->val[0];
|
|
}
|
|
removeob(retob[i], retcount[i]);
|
|
}
|
|
}
|
|
if (corpsetype != R_NONE) {
|
|
addflag(o->flags, F_LINKRACE, corpsetype, NA, NA, NULL);
|
|
}
|
|
getobname(o, obname, o->amt);
|
|
msgnocap("%c - %s", o->letter, obname);
|
|
// take some time
|
|
taketime(user, getactspeed(user));
|
|
practice(user, SK_COOKING, 1);
|
|
if (isplayer(user)) pleasegodmaybe(R_GODNATURE, 5);
|
|
} else {
|
|
// pack full?
|
|
msg("You have no space to cook!");
|
|
}
|
|
}
|
|
} 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) {
|
|
snprintf(buf, BUFLEN, "Darkwalk to where (max range %d)?", range);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "Darkwalk to where?");
|
|
}
|
|
targcell = askcoords(buf, "Darkwalk->", 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];
|
|
int bonus = 0;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't disable traps while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
dir = askdir("Disable trap in which direction (- to cancel)", B_TRUE, B_FALSE);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE ;
|
|
} else if (dir == D_MYSELF) {
|
|
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)) {
|
|
// ie. a known trapped cell
|
|
trapob = o;
|
|
trapflag = hasflag(trapob->flags, F_TRAP);
|
|
break;
|
|
} else if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) {
|
|
// ie. a known trapped object
|
|
trapob = o;
|
|
trapflag = hasflag(trapob->flags, F_TRAPPED);
|
|
break;
|
|
}
|
|
}
|
|
if (!trapob) {
|
|
if (isplayer(user)) msg("You can't see any traps there!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(user) || cansee(player, user)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
|
|
// taketime
|
|
taketime(user, getactspeed(user));
|
|
|
|
// if this was a cell trap, move player there.
|
|
if (trapflag->id == F_TRAP) {
|
|
movelf(user, targcell, B_TRUE);
|
|
}
|
|
|
|
// have any objects which will help?
|
|
bonus = 0;
|
|
for (o = user->pack->first ; o ; o = o->next) {
|
|
f = hasflag(o->flags, F_HELPSDISARM);
|
|
if (f && (f->val[0] > bonus)) {
|
|
bonus = f->val[0];
|
|
}
|
|
}
|
|
|
|
// try to disarm it
|
|
if (skillcheck(user, SC_DISARM, trapflag->val[0], bonus)) {
|
|
if (trapflag->id == F_TRAP) {
|
|
// ie. trapped cell
|
|
getobname(trapob, buf, 1);
|
|
removeob(trapob, trapob->amt);
|
|
} else {
|
|
objecttype_t *ot;
|
|
ot = findot(trapflag->val[0]);
|
|
sprintf(buf, "%s %s", needan(ot->name) ? "an" : "a", ot->name);
|
|
// ie. trapped object
|
|
killflag(trapflag);
|
|
}
|
|
if (isplayer(user)) {
|
|
msg("You disable %s.",buf);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s disables %s.",username, buf);
|
|
}
|
|
|
|
if (isplayer(user) && hasjob(user, J_ROGUE)) {
|
|
gainxp(user, trapflag->val[0]);
|
|
}
|
|
practice(user, SK_ENGINEERING, 1);
|
|
} else {
|
|
int trapgoesoff = B_FALSE;
|
|
// failed. another check to see if it goes off
|
|
if (trapflag->id == F_TRAP) {
|
|
getobname(trapob, buf, 1);
|
|
if ((trapflag->val[1] == B_TRUE) && !skillcheck(user, SC_DISARM, trapflag->val[0], 0)) {
|
|
trapgoesoff = B_TRUE;
|
|
}
|
|
} else {
|
|
strcpy(buf, "the trap");
|
|
if (!skillcheck(user, SC_DISARM, trapflag->val[0], 0)) {
|
|
trapgoesoff = B_TRUE;
|
|
}
|
|
}
|
|
if (trapgoesoff) {
|
|
if (isplayer(user)) {
|
|
msg("Oops - you trigger %s!",buf);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s triggers %s!",username, buf);
|
|
}
|
|
|
|
if (trapflag->id == F_TRAP) { // an actual trap
|
|
trapeffects(trapob, trapob->type->id, user->cell,
|
|
(trapflag->id == F_TRAPPED) ? trapob : NULL);
|
|
} else { // a trap on an object
|
|
triggerattachedtraps(trapob, user, B_NOANNOUNCE);
|
|
|
|
//trapeffects(NULL, trapflag->val[0], user->cell,
|
|
//(trapflag->id == F_TRAPPED) ? trapob : NULL);
|
|
}
|
|
} else {
|
|
getobname(trapob, buf, 1);
|
|
if (isplayer(user)) {
|
|
msg("You fail to disable %s.",buf);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s fails to disable %s.",username, buf);
|
|
}
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_DISARMLF) {
|
|
object_t *wep,*targetwep;
|
|
int skillmod = 0,targetskillmod = 0;
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Disarm in which direction (- to cancel)", B_TRUE, B_FALSE);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else if (dir == D_MYSELF) {
|
|
if (isplayer(user)) msg("You can't disarm yourself!");
|
|
return B_TRUE;
|
|
} else {
|
|
targcell = getcellindir(user->cell, dir);
|
|
}
|
|
}
|
|
|
|
if (!haslos(user, targcell)) {
|
|
if (isplayer(user)) msg("You can't see there!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
if (isplayer(user)) msg("There is nobody there to disarm!");
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targetname);
|
|
targetwep = getweapon(target);
|
|
if (!targetwep) {
|
|
targetwep = getequippedob(target->pack, BP_SECWEAPON);
|
|
if (!targetwep) {
|
|
if (isplayer(user)) msg("%s has no weapon!", targetname);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
wep = getweapon(user);
|
|
|
|
// try to disarm them...
|
|
taketime(user, getactspeed(user));
|
|
|
|
if (wep) {
|
|
skillmod = getweaponskill(user, wep) * 10;
|
|
if (isplayer(user) && (skillmod == 0)) skillmod = -25;
|
|
} else {
|
|
skillmod = getskill(user, SK_UNARMED) * 10;
|
|
}
|
|
|
|
targetskillmod = getweaponskill(target, targetwep) * 10;
|
|
if (isplayer(target) && (targetskillmod == 0)) targetskillmod = -25;
|
|
|
|
if (skillcheckvs(user, SC_DEX, skillmod, target, SC_DEX, targetskillmod)) {
|
|
if (cansee(player, user)) {
|
|
msg("^w%s disarm%s %s!",username, isplayer(user) ? "" : "s", targetname);
|
|
}
|
|
// calling drop() will cause the target to take some time - this will normally give
|
|
// free hit in before they can pick it back up.
|
|
drop(targetwep, ALL);
|
|
} else {
|
|
if (isplayer(user)) {
|
|
msg("You try to disarm %s, but fail.",targetname);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s tries to disarm %s, but fail.",username, targetname);
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_DISMANTLE) {
|
|
objecttype_t *newot = NULL;
|
|
int numnew = 0;
|
|
int dir;
|
|
cell_t *c;
|
|
object_t *o;
|
|
char obname[BUFLEN];
|
|
enum SKILLLEVEL slev;
|
|
if (!isplayer(user)) return B_TRUE;
|
|
|
|
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 dismantle anything while swimming!");
|
|
return B_TRUE;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't dismantle anything while stuck!");
|
|
return B_TRUE;
|
|
}
|
|
slev = getskill(user, SK_ENGINEERING);
|
|
|
|
|
|
dir = askdir("Dismantle obstacle in which direction (- to cancel)", B_TRUE, B_FALSE);
|
|
if (dir == D_NONE) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else if (dir == D_MYSELF) {
|
|
c = user->cell;
|
|
}
|
|
c = getcellindir(user->cell, dir);
|
|
|
|
// antything there?
|
|
o = hasobwithflag(c->obpile, F_CLIMBOBSTACLE);
|
|
if (c->type->solid) {
|
|
if (slev >= PR_ADEPT) {
|
|
// you can try to dismantle walls. digcell() will
|
|
// check whether it is actually diggable.
|
|
} else {
|
|
msg("There is nothing you can dismantle there.");
|
|
return B_TRUE;
|
|
}
|
|
} else if (!o) {
|
|
if (slev >= PR_ADEPT) {
|
|
// you can dismantle doors too...
|
|
o = hasdoor(c);
|
|
}
|
|
if (!o) {
|
|
msg("There is nothing you can dismantle there.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!o) {
|
|
// ie trying to dismantle a wall
|
|
return digcell(user, c, NULL, B_TRUE);
|
|
}
|
|
|
|
// figrue out what to replace it with
|
|
switch (o->material->id) {
|
|
case MT_STONE: newot = findot(OT_STONE); break;
|
|
case MT_METAL: newot = findot(OT_IRONSTAFF); break;
|
|
case MT_WOOD: newot = findot(OT_WOODPLANK); break;
|
|
default: break;
|
|
}
|
|
|
|
if (newot) {
|
|
int origsize,junksize;
|
|
// figure out how many
|
|
origsize = getobsize(o);
|
|
junksize = newot->size;
|
|
numnew = origsize / junksize;
|
|
limit(&numnew, 1, 10);
|
|
}
|
|
getobname(o, obname, 1);
|
|
removeob(o, 1);
|
|
if (numnew && newot) {
|
|
char newobname[BUFLEN];
|
|
sprintf(newobname, "%d %s", numnew, newot->name);
|
|
addob(c->obpile, newobname);
|
|
}
|
|
taketime(user, getactspeed(user)*2);
|
|
msg("You dismantle %s.", obname);
|
|
} else if (abilid == OT_A_DRAGUNDERGROUND) {
|
|
// announce
|
|
if (cansee(player, target)) {
|
|
msg("%s drags %s underground.", username, targetname);
|
|
}
|
|
addflag(target->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
|
|
addflag(target->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
|
|
target->hp = 0;
|
|
killallobs(target->pack);
|
|
} else if (abilid == OT_A_FLY) {
|
|
flag_t *f;
|
|
|
|
// already flying? stop.
|
|
f = isflyingwithwings(user);
|
|
if (f) {
|
|
killflag(f);
|
|
taketime(user, getactspeed(user));
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (isimmobile(user)) {
|
|
if (isplayer(user)) msg("You can't move!");
|
|
return B_TRUE;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't start flying while stuck!");
|
|
return B_TRUE;
|
|
} else if (isburdened(user)) {
|
|
if (isplayer(user)) msg("You can't flying while burdened!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// start flying
|
|
addtempflag(user->flags, F_FLYING, getnaturalflightheight(user), NA, NA, NULL, FROMABIL);
|
|
taketime(user, getactspeed(user));
|
|
|
|
} else if (abilid == OT_A_FEIGNDEATH) {
|
|
lifeform_t *lf;
|
|
if (hasflag(user->flags, F_FEIGNINGDEATH)) {
|
|
if (isplayer(user)) msg("You are already feigning death!");
|
|
return B_TRUE;
|
|
}
|
|
taketime(user, getactspeed(user));
|
|
|
|
if (isplayer(user)) {
|
|
msg("You drop to the ground.");
|
|
} else if (cansee(player, user)) {
|
|
if (target) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
if (isplayer(target)) {
|
|
msg("^gYou kill %s!", username);
|
|
} else {
|
|
msg("%s kills %s.", targname, username);
|
|
}
|
|
} else {
|
|
msg("%s dies.", username);
|
|
}
|
|
}
|
|
if (!isprone(user)) {
|
|
addflag(user->flags, F_PRONE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
addflag(user->flags, F_FEIGNINGDEATH, B_TRUE, NA, NA, NULL);
|
|
// anyone attacking you stops
|
|
for (lf = user->cell->map->lf ; lf ; lf = lf->next) {
|
|
flag_t *f;
|
|
char buf[BUFLENTINY];
|
|
f = hasflagval(lf->flags, F_TARGETLF, user->id, NA, NA, NULL);
|
|
if (f) killflag(f);
|
|
f = hasflagval(lf->flags, F_TARGETCELL, user->cell->x, user->cell->y, NA, NULL);
|
|
if (f) killflag(f);
|
|
snprintf(buf, BUFLENTINY, "%d\n",user->id);
|
|
f = hasflagval(lf->flags, F_TARGETCELL, NA, NA, MR_LF, buf);
|
|
if (f) killflag(f);
|
|
}
|
|
needredraw = B_TRUE;
|
|
statdirty = B_TRUE;
|
|
} else if (abilid == OT_A_FLIP) {
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Flip from which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_MYSELF) {
|
|
if (isplayer(user)) msg("You can't flip yourself!");
|
|
return B_TRUE;
|
|
} else 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 flip!");
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
if (getlfsize(target) > getlfsize(user)) {
|
|
if (isplayer(user)) msg("%s is too large for you to flip.", targetname);
|
|
return B_TRUE;
|
|
}
|
|
|
|
taketime(user, getactspeed(user));
|
|
|
|
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_FALSE;
|
|
}
|
|
|
|
// if not already held, victim gets a skillcheck to avoid being grabbed - hard.
|
|
if (!lfhasflagval(user, F_GRABBING, target->id, NA, NA, NULL) &&
|
|
skillcheck(target, SC_DODGE, getattr(user, A_AGI)+50, 0)) {
|
|
if (cansee(player, user)) {
|
|
msg("%s evade%s %s%s grasp.", targetname, isplayer(target) ? "" : "s",
|
|
username, getpossessive(username));
|
|
}
|
|
} else {
|
|
int dir;
|
|
cell_t *c;
|
|
// free space behind the flipper?
|
|
dir = diropposite(user->facing);
|
|
c = getcellindir(user->cell, dir);
|
|
|
|
if (c && cellwalkable(target, c, NULL)) {
|
|
int dist;
|
|
// break grabs, but don't annouce "you break free"
|
|
breakgrabs(target, B_TRUE, B_TRUE, B_FALSE);
|
|
|
|
// throw!
|
|
// - announce BEFORE moving the target.
|
|
if (isplayer(user)) {
|
|
msg("^%cYou flip %s over your head!", getlfcol(target, CC_BAD), targetname);
|
|
} else if (cansee(player, user)) {
|
|
if (hasbp(user, BP_HEAD)) {
|
|
char yr[BUFLEN];
|
|
strcpy(yr, your(user));
|
|
yr[0] = tolower(yr[0]);
|
|
msg("^%c%s flips %s over %s %s!", getlfcol(target, CC_BAD), username, targetname, yr, getbodypartname(user, BP_HEAD));
|
|
} else {
|
|
msg("^%c%s flips %s!", getlfcol(target, CC_BAD), username, targetname);
|
|
}
|
|
}
|
|
setfacing(user, dir); // turn player to face them
|
|
movelf(target, c, B_FALSE); // move target behind player
|
|
dist = 2 + (getskill(user, SK_ATHLETICS)/3);
|
|
setfacing(target, getrandomdir(DT_COMPASS));
|
|
knockback(target, dir, dist, user, 125, B_NOANNOUNCE, B_DODAM);
|
|
} else {
|
|
if (isplayer(user)) {
|
|
msg("There is no room to flip %s!", targetname);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s tries to flip %s but fails.", username, targetname);
|
|
}
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_FLURRY) {
|
|
int dir;
|
|
|
|
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 (hasjob(user, J_MONK)) {
|
|
if (getweapon(user)) {
|
|
if (isplayer(user)) msg("You need be unarmed to perform an attack flurry!");
|
|
return B_TRUE;
|
|
}
|
|
} else if (!isdualweilding(user)) {
|
|
if (isplayer(user)) msg("You need to 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 {
|
|
dir = askdir("Flurry in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if ((dir == D_NONE) || (dir == D_MYSELF)) {
|
|
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, 25, B_DOANNOUNCE, B_NODAM);
|
|
|
|
// if we succeeded in pushing them...
|
|
if (target->cell != targcell) {
|
|
movelf(user, targcell, B_FALSE);
|
|
}
|
|
|
|
// now attack them
|
|
attackcell(user, target->cell, B_TRUE);
|
|
} else if (abilid == OT_A_FULLSHIELD) {
|
|
object_t *shield[MAXPILEOBS],*selshield = NULL;
|
|
int checkmod[MAXPILEOBS];
|
|
int nshields,i;
|
|
char shtext[BUFLEN];
|
|
if (isimmobile(user)) {
|
|
if (isplayer(user)) msg("You can't do that while stuck!");
|
|
return B_TRUE;
|
|
}
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't do that while swimming!");
|
|
return B_TRUE;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't do that while stuck!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// stop fullshielding.
|
|
if (lfhasflag(user, F_FULLSHIELD)) {
|
|
killflagsofid(user->flags, F_FULLSHIELD);
|
|
taketime(user, getactspeed(user));
|
|
return B_FALSE;
|
|
}
|
|
|
|
getallshields(user, DT_BASH, shield, checkmod, &nshields);
|
|
for (i = 0; i < nshields; i++) {
|
|
if (isshield(shield[i])) {
|
|
selshield = shield[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!selshield) {
|
|
if (isplayer(user)) msg("You need to equip a shield first!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
sprintf(shtext, "%ld", selshield->id);
|
|
addflag(user->flags, F_FULLSHIELD, NA, NA, NA, shtext);
|
|
taketime(user, getactspeed(user));
|
|
} else if (abilid == OT_A_GRAB) {
|
|
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) {
|
|
int dir;
|
|
dir = askdir("Grab in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else if (dir == D_MYSELF) {
|
|
if (isplayer(user)) msg("You can't grab yourself!");
|
|
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_FALSE;
|
|
}
|
|
|
|
// victim gets a skilcheck to avoid being grabbed
|
|
if (skillcheck(target, SC_DODGE, (getattr(user, A_AGI))+50, 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;
|
|
}
|
|
|
|
// 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, 5)) {
|
|
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)/10;
|
|
dam = modifybystat(str, 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;
|
|
int maxrange = 2;
|
|
object_t *o;
|
|
int swapped = B_FALSE;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't jump while swimming!");
|
|
return B_TRUE;
|
|
} else if (isairborne(user, NULL)) {
|
|
if (isplayer(user)) msg("You can't jump while airbourne!");
|
|
return B_TRUE;
|
|
} else if (hasflag(user->flags, F_GRAVBOOSTED)) {
|
|
if (isplayer(user)) msg("You can't jump with gravity boosted around you!");
|
|
return B_TRUE;
|
|
} else if (lfhasflag(user, F_GRABBING)) {
|
|
if (isplayer(user)) msg("You can't jump while holding someone!");
|
|
return B_TRUE;
|
|
} else if (lfhasflag(user, F_GRABBEDBY)) {
|
|
if (isplayer(user)) msg("You can't jump while being held!");
|
|
return B_TRUE;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't jump while stuck!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (hasflag(user->flags, F_GRAVLESSENED)) {
|
|
maxrange++;
|
|
}
|
|
|
|
if (!targcell) {
|
|
snprintf(buf, BUFLEN, "Jump where (max distance %d)?", maxrange);
|
|
while (!targcell) {
|
|
// ask where
|
|
targcell = askcoords(buf, "Jump->", TT_NONE, user, maxrange, LOF_DONTNEED, B_TRUE);
|
|
if (!targcell) {
|
|
return B_TRUE;
|
|
} else if (getcelldist(user->cell, targcell) > maxrange) {
|
|
targcell = NULL;
|
|
if (isplayer(user)) {
|
|
snprintf(buf, BUFLEN, "You can't jump that far! Jump where (max distance %d)?", maxrange);
|
|
}
|
|
} else if (!haslos(user, targcell)) {
|
|
targcell = NULL;
|
|
if (isplayer(user)) {
|
|
snprintf(buf, BUFLEN, "You can't see where to land! Jump where (max distance %d)?", maxrange);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (iswoozy(user)) {
|
|
cell_t *retcell[MAXCANDIDATES];
|
|
int nretcells;
|
|
// change destination.
|
|
getradiuscells(user->cell, maxrange, DT_COMPASS, B_FALSE, LOF_NEED, B_FALSE, retcell, &nretcells, B_FALSE);
|
|
targcell = retcell[rnd(0,nretcells-1)];
|
|
}
|
|
|
|
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;
|
|
}
|
|
// did you land on something impassable?
|
|
for (o = targcell->obpile->first ; o ; o = o->next) {
|
|
if (isimpassableob(o, user, getlfsize(user))) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
if (isplayer(user)) {
|
|
msg("There %s %s in the way!", (o->amt == 1) ? "is" : "are", obname);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// we now have a cell - go there!
|
|
taketime(user, getactspeed(user));
|
|
|
|
if (!targcell || (targcell == user->cell)) {
|
|
if (isplayer(user)) {
|
|
msg("You jump on the spot.");
|
|
} else if (cansee(player, user)) {
|
|
msg("%s jumps on the spot.", username);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
origcell = user->cell;
|
|
|
|
// did you land on anyone?
|
|
victim = haslf(targcell);
|
|
if (victim) {
|
|
int acc;
|
|
getlfname(victim,victimname);
|
|
// see if you actually landed on them or they dodge...
|
|
acc = 100 - (getevasion(victim)*2);
|
|
if (rnd(1,100) > acc) {
|
|
// dodged!
|
|
dodged = B_TRUE;
|
|
}
|
|
}
|
|
|
|
// announce
|
|
if (isplayer(user)) {
|
|
if (victim && !dodged) {
|
|
msg("You leap high in the air onto %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 onto %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 onto %s!", username,victimname);
|
|
} else {
|
|
msg("%s drops from the air and lands nearby!", username);
|
|
}
|
|
}
|
|
|
|
if (victim) {
|
|
cell_t *c;
|
|
|
|
getlfname(victim,victimname);
|
|
|
|
// move them out of the way
|
|
c = getrandomadjcell(victim->cell, &ccwalkable, B_NOEXPAND);
|
|
// nowhere to move? move to where the lf jumped from!
|
|
if (c) {
|
|
movelf(victim, c, B_FALSE);
|
|
} else {
|
|
swapplaces(victim, user, B_CHANGEDIR, B_CHANGEDIR, B_NOTONPURPOSE);
|
|
swapped = B_TRUE;
|
|
}
|
|
|
|
}
|
|
if (!swapped) {
|
|
movelf(user, targcell, B_FALSE);
|
|
}
|
|
|
|
// splash?
|
|
for (o = targcell->obpile->first ; o ; o = o->next) {
|
|
if ((o->material->id == MT_WATER) && (o->type->id != OT_PUDDLEWATER)) {
|
|
addobburst(targcell, 1, DT_COMPASS, "small puddle of water", NULL, LOF_NEED);
|
|
noise(targcell, NULL, NC_OTHER, SV_TALK, "a splash.", "Splash!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (victim) {
|
|
if (dodged) {
|
|
if (cansee(player, user)) {
|
|
msg("%s dodges out of the way!", victimname);
|
|
}
|
|
} else {
|
|
// both victim and attacker fall
|
|
if (!isdead(victim)) fall(victim, NULL, B_TRUE);
|
|
fall(user, NULL, B_TRUE);
|
|
}
|
|
}
|
|
|
|
practice(user, SK_ATHLETICS, 1);
|
|
} else if (abilid == OT_A_PICKLOCK) {
|
|
lockpick(user, NULL, NULL, NULL);
|
|
} else if (abilid == OT_A_REFLEXDODGE) {
|
|
// stopping?
|
|
if (killflagsofid(user->flags, F_DODGES)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
addflag(user->flags, F_DODGES, B_TRUE, NA, NA, NULL);
|
|
} 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;
|
|
}
|
|
howlong = DEF_RAGETIME;
|
|
enrage(user, howlong);
|
|
} else if (abilid == OT_A_REPAIR) {
|
|
object_t *o;
|
|
enum MATERIAL repairablemats[MAXCANDIDATES];
|
|
int cutoffpct[MAXCANDIDATES];
|
|
int nmats = 0;
|
|
int i;
|
|
|
|
// get list of repairable materials
|
|
getworkablematerials(user, SK_METALWORK, repairablemats, cutoffpct, &nmats);
|
|
getworkablematerials(user, SK_SEWING, repairablemats, cutoffpct, &nmats);
|
|
// 1.compile a list of repairable objects
|
|
// sk_armour lets you repair armour up to xx% (depends on skill)
|
|
initprompt(&prompt, "Repair which object?");
|
|
addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL);
|
|
addchoice(&prompt, ',', "All", "All", NULL, NULL);
|
|
for (o = user->pack->first ; o ; o = o->next) {
|
|
int ok = B_FALSE;
|
|
int cutoff = 0;
|
|
for (i = 0; i < nmats; i++) {
|
|
if (o->material->id == repairablemats[i]) {
|
|
ok = B_TRUE;
|
|
cutoff = cutoffpct[i];
|
|
break;
|
|
}
|
|
}
|
|
if (ok && isdamaged(o)) {
|
|
float pct;
|
|
f = hasflag(o->flags, F_OBHP);
|
|
pct = ((float)f->val[0] /(float) f->val[1]) * 100;
|
|
if (pct >= cutoff) {
|
|
char buf[BUFLEN],desc[BUFLEN];
|
|
getobname(o, buf, o->amt);
|
|
sprintf(desc, "%s",buf);
|
|
// we can repair this object
|
|
addchoice(&prompt, o->letter, desc, desc, o, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prompt.nchoices <= 2) {
|
|
if (isplayer(user)) {
|
|
msg("You don't have anything which you are able to repair.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// 2. ask which ones to repair (or ALL)
|
|
if (isplayer(user)) {
|
|
char ch;
|
|
ch = getchoice(&prompt);
|
|
if (ch == ',') { // all
|
|
int n;
|
|
for (n = 0 ; n < prompt.nchoices; n++) {
|
|
o = (object_t *)prompt.choice[n].data;
|
|
if (o) {
|
|
char buf[BUFLEN];
|
|
sprintf(buf, "%ld", o->id);
|
|
addflag(user->flags, F_REPAIRING, NA, NA, NA, buf);
|
|
}
|
|
}
|
|
o = NULL;
|
|
} else {
|
|
o = (object_t *) prompt.result;
|
|
if (!o) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
// pick a random one (not 'cancel' or 'all')
|
|
o = (object_t *) prompt.choice[rnd(2,prompt.nchoices-1)].data;
|
|
if (!o) return B_TRUE;
|
|
}
|
|
|
|
if (o) {
|
|
char buf[BUFLEN];
|
|
sprintf(buf, "%ld", o->id);
|
|
addflag(user->flags, F_REPAIRING, NA, NA, NA, buf);
|
|
getobname(o, buf, o->amt);
|
|
if (isplayer(user)) {
|
|
msg("You start repairing %s...", buf);
|
|
} else if (cansee(player, user)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(user, lfname);
|
|
msg("%s starts repairing %s...", lfname, buf);
|
|
}
|
|
} else {
|
|
// repairing all...
|
|
if (isplayer(user)) {
|
|
msg("You start repairing your equipment...");
|
|
} else if (cansee(player, user)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(user, lfname);
|
|
msg("%s starts repairing its equipment...", lfname);
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_RESIZE) {
|
|
object_t *o;
|
|
enum MATERIAL repairablemats[MAXCANDIDATES];
|
|
enum LFSIZE wantsize,origsize;
|
|
int cutoffpct[MAXCANDIDATES];
|
|
int i,nmats = 0;
|
|
char ch,obname[BUFLEN];
|
|
|
|
if (!isplayer(user)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
// get list of resizable materials
|
|
if (getskill(user, SK_METALWORK) >= PR_BEGINNER) {
|
|
getworkablematerials(user, SK_METALWORK, repairablemats, cutoffpct, &nmats);
|
|
}
|
|
if (getskill(user, SK_SEWING) >= PR_BEGINNER) {
|
|
getworkablematerials(user, SK_SEWING, repairablemats, cutoffpct, &nmats);
|
|
}
|
|
|
|
// 1.compile a list of resizable objects
|
|
initprompt(&prompt, "Resize which object?");
|
|
addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL);
|
|
for (o = user->pack->first ; o ; o = o->next) {
|
|
int ok = B_FALSE;
|
|
for (i = 0; i < nmats; i++) {
|
|
if (o->material->id == repairablemats[i]) {
|
|
ok = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (ok && hasflag(o->flags, F_MULTISIZE)) {
|
|
getobname(o, obname, o->amt);
|
|
// we can resize this object
|
|
addchoice(&prompt, o->letter, obname, NULL, o, NULL);
|
|
}
|
|
}
|
|
prompt.maycancel = B_TRUE;
|
|
|
|
if (prompt.nchoices <= 1) {
|
|
msg("You don't have anything which you are able to resize.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// 2. ask which ones to resize (or ALL)
|
|
getchoice(&prompt);
|
|
o = (object_t *) prompt.result;
|
|
if (!o) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
getobname(o, obname, 1);
|
|
origsize = getarmoursize(o);
|
|
|
|
|
|
// ask what size we want.
|
|
sprintf(buf, "Resize %s to what size?", obname);
|
|
initprompt(&prompt, buf);
|
|
for (i = SZ_MEDIUM; i <= SZ_LARGE; i++) {
|
|
if (i != origsize) {
|
|
switch (i) {
|
|
case SZ_MEDIUM:
|
|
addchoice(&prompt, 's', "half-sized", NULL, NULL, NULL);
|
|
break;
|
|
case SZ_HUMAN:
|
|
addchoice(&prompt, 'm', "human-sized", NULL, NULL, NULL);
|
|
break;
|
|
case SZ_LARGE:
|
|
addchoice(&prompt, 'l', "giant-sized", NULL, NULL, NULL);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL);
|
|
|
|
ch = getchoice(&prompt);
|
|
switch (ch) {
|
|
case 's':
|
|
wantsize = SZ_MEDIUM;
|
|
break;
|
|
case 'm':
|
|
wantsize = SZ_HUMAN;
|
|
break;
|
|
case 'l':
|
|
wantsize = SZ_LARGE;
|
|
break;
|
|
default:
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// in case it's on fire, etc
|
|
if (touch(user, o)) {
|
|
taketime(user, getactspeed(user));
|
|
return B_FALSE;
|
|
}
|
|
|
|
// resize it!
|
|
resizeobject(o, wantsize);
|
|
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[1] /= 2;
|
|
if (f->val[1] < 1) f->val[1] = 1;
|
|
|
|
f->val[0] = f->val[1];
|
|
}
|
|
|
|
msg("You %s the size of your %s.", (wantsize > origsize) ? "increase" : "reduce", noprefix(obname));
|
|
getobname(o, obname, 1);
|
|
more();
|
|
msgnocap("%c - %s", o->letter, obname);
|
|
|
|
practice(user, SK_METALWORK, 1);
|
|
practice(user, SK_SEWING, 1);
|
|
// TODO: make this like eating/resting/etc ?
|
|
taketime(user, getactspeed(user));
|
|
} else if (abilid == OT_A_SCREAM) {
|
|
lifeform_t *lf;
|
|
if (lfhasflag(user, F_SILENCED)) {
|
|
if (isplayer(user)) msg("You are unable to make a sound!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!validatespellcell(user, &targcell,TT_MONSTER, abilid, power, B_FALSE)) return B_TRUE;
|
|
|
|
makenoise(user, N_DEAFENSCREAM);
|
|
|
|
// adjacent lfs must pass a magic resistance check or die
|
|
for (lf = user->cell->map->lf ; lf ; lf = lf->next) {
|
|
if (lf != user) {
|
|
int nwalls;
|
|
if (canhear(lf, user->cell, SV_PLANE, &nwalls) && isadjacent(lf->cell, user->cell)) {
|
|
if ((nwalls == 0) && !lfhasflag(lf, F_DEAF)) {
|
|
addtempflag(lf->flags, F_DEAF, B_TRUE, NA, NA, NULL, rnd(30,50));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} else if (abilid == OT_A_SHIELDBASH) {
|
|
object_t *shield = NULL;
|
|
flag_t *f;
|
|
int badshield = B_FALSE;
|
|
int shpenalty,mod;
|
|
object_t *shieldlist[MAXPILEOBS];
|
|
int nshields,i;
|
|
char shname[BUFLEN], tname[BUFLEN];
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You lack the stability for a shield bash while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
getallshields(user, DT_ALL, shieldlist, NULL, &nshields);
|
|
for (i = 0; i < nshields; i++) {
|
|
if (isshield(shieldlist[i])) {
|
|
shield = shieldlist[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!shield) {
|
|
badshield = B_TRUE;
|
|
}
|
|
|
|
if (badshield) {
|
|
if (isplayer(user)) msg("You need a shield equipped to perform a shield bash!");
|
|
return B_TRUE;
|
|
}
|
|
getobname(shield, shname, 1);
|
|
|
|
|
|
f = hasflagval(shield->flags, F_EQUIPCONFER, F_SHIELDPENALTY, NA, NA, NULL);
|
|
if (f) {
|
|
shpenalty = adjustshieldpenalty(user, f->val[1]);
|
|
} else {
|
|
shpenalty = 0;
|
|
}
|
|
if (shpenalty) {
|
|
if (isplayer(user)) msg("Your %s is too cumbersome to bash with.",shname);
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Shield bash in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_MYSELF) {
|
|
// yourself!
|
|
targcell = user->cell;
|
|
} else 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, tname);
|
|
|
|
if (isplayer(user)) {
|
|
msg("You bash %s with your %s!",tname, noprefix(shname));
|
|
} else if (cansee(player, user)) {
|
|
msg("%s bashes %s with %s!",username, tname, shname);
|
|
}
|
|
|
|
|
|
// only works if shield is hard
|
|
if (gethardness(shield->material->id)) {
|
|
int damamt;
|
|
// victim takes a little bit of damage
|
|
damamt = getstrdammod(user);
|
|
if (damamt > 0) {
|
|
char damstr[BUFLEN];
|
|
sprintf(damstr, "%s%s shield bash", username, getpossessive(username));
|
|
losehp(target, damamt, DT_BASH, user, damstr);
|
|
}
|
|
if (!isdead(target)) {
|
|
// stun success depends on shield skill, relative lf size, target's evasion
|
|
mod = 0;
|
|
modifyforsize(&mod, user, target, 10, M_VAL);
|
|
if (mod > 0) mod = 0;
|
|
if (skillcheck(user, SC_SHIELDBLOCK, 25 + getevasion(target), mod)) {
|
|
stun(target, 3);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!touch(target, shield)) {
|
|
// if the victim touching the shield didn't destroy it, the shield gets damaged
|
|
takedamage(shield, roll("1d3"), DT_DIRECT, user);
|
|
}
|
|
|
|
} else if (abilid == OT_A_SNATCH) {
|
|
object_t *o = NULL;
|
|
flag_t *f;
|
|
char obname[BUFLEN];
|
|
|
|
if (!targcell) {
|
|
int dir;
|
|
// ask which dir
|
|
dir = askdir("Snatch from which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else {
|
|
targcell = getcellindir(user->cell, dir);
|
|
}
|
|
}
|
|
|
|
if (targcell->obpile->first) {
|
|
if (isplayer(user)) {
|
|
// select object from cell...
|
|
o = askobject(targcell->obpile, "Snatch which object", NULL, NULL, '\0', NULL, B_FALSE);
|
|
} else {
|
|
object_t *oo;
|
|
for (oo = targcell->obpile->first; oo ; oo = oo->next) {
|
|
if (canpickup(user, oo, 1) && aiwants(user, oo, NULL)) {
|
|
o = oo;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (isplayer(user)) msg("There is nothing there to snatch!");
|
|
return B_TRUE;
|
|
}
|
|
if (!o) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
getobname(o, obname, 1);
|
|
|
|
// spell doesn't take any time, using an object does.
|
|
if (fromob) {
|
|
if (cansee(player, user)) {
|
|
msg("%s%s %s wraps around %s.", username, getpossessive(username),
|
|
noprefix(fromobname), obname);
|
|
}
|
|
} else {
|
|
// setting v0 to spellid just in case pickup() changes flags
|
|
addflag(user->flags, F_NOTIME, OT_A_SNATCH, NA, NA, NULL);
|
|
}
|
|
|
|
if (!pickup(user, o, 1, B_TRUE, B_FALSE)) {
|
|
if (isplayer(user)) {
|
|
msg("You snatch %s from the ground!",obname);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s snatches %s from the ground!",username, obname);
|
|
}
|
|
}
|
|
f = lfhasflagval(user, F_NOTIME, OT_A_SNATCH, NA, NA, NULL);
|
|
if (f) killflag(f);
|
|
} else if (abilid == OT_A_SONICBOLT) {
|
|
int volume,nwalls;
|
|
if (lfhasflag(user, F_SILENCED)) {
|
|
if (isplayer(user)) msg("You are unable to make a sound!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!validatespellcell(user, &targcell,TT_MONSTER, abilid, power, B_FALSE)) return B_TRUE;
|
|
target = targcell->lf;
|
|
makenoise(user, N_SONICBOLT);
|
|
|
|
// if target can hear, they take damage
|
|
f = hasflagval(user->flags, F_NOISETEXT, N_SONICBOLT, NA, NA, NULL);
|
|
if (f) {
|
|
volume = f->val[1];
|
|
} else {
|
|
volume = power;
|
|
}
|
|
if (target && canhear(target, user->cell, volume, &nwalls)) {
|
|
if (nwalls == 0) {
|
|
if (isplayer(target)) {
|
|
msg("Pain shoots through your eardrums!");
|
|
}
|
|
losehp(target, roll(damstr), DT_SONIC, user, "a bolt of sound");
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_SPRINT) {
|
|
flag_t *f;
|
|
f = lfhasflag(user, F_SPRINTING);
|
|
if (f) {
|
|
// stop sprinting.
|
|
killflag(f);
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (lfhasflagval(user, F_INJURY, IJ_WINDPIPECRUSHED, NA, NA, NULL)) {
|
|
if (isplayer(user)) msg("You can't sprint with a crushed windpipe.");
|
|
return B_TRUE;
|
|
}
|
|
if (hasinjuredbp(user, BP_LEGS)) {
|
|
if (isplayer(user)) msg("You can't sprint with injured legs.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't sprint while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isairborne(user, NULL)) {
|
|
if (isplayer(user)) msg("You can't sprint while airborne!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isburdened(user)) {
|
|
if (isplayer(user)) {
|
|
msg("You cannot sprint while burdened.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(user)) {
|
|
int dir;
|
|
dir = askdir("Sprint in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if ((dir == D_NONE) || (dir == D_MYSELF)) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
setfacing(user, dir);
|
|
}
|
|
|
|
addflag(user->flags, F_SPRINTING, B_TRUE, NA, NA, NULL);
|
|
practice(user, SK_ATHLETICS, 1);
|
|
killflagsofid(user->flags, F_HIDING);
|
|
} 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);
|
|
if (!lfhasflagval(target, F_PAIN, DT_ACID, NA,NA, NULL) && !isundead(target)) {
|
|
int howlong = 2;
|
|
if (power > 0) {
|
|
howlong = power * 2;
|
|
}
|
|
// cause ongoing pain for power*2 turns
|
|
addtempflag(target->flags, F_PAIN, DT_ACID, NA, NA, damstr, howlong);
|
|
}
|
|
taketime(user, getactspeed(user));
|
|
} else if (abilid == OT_A_STUDYSCROLL) {
|
|
object_t *o;
|
|
int difficulty, mod;
|
|
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 study anything, since you can't see!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask what to inspect
|
|
initprompt(&prompt, "Study which scroll?");
|
|
prompt.maycancel = B_TRUE;
|
|
for (o = user->pack->first ; o ; o = o->next) {
|
|
if ((o->type->obclass->id == OC_SCROLL) && isknown(o)) {
|
|
f = hasflag(o->flags, F_LINKSPELL);
|
|
if (f && !cancast(user, f->val[0], NULL)) {
|
|
char buf2[BUFLEN];
|
|
getobname(o, buf, o->amt);
|
|
difficulty = 100 + (getspelllevel(f->val[0])*10);
|
|
mod = getspellskill(user, f->val[0]) * 10;
|
|
//pct = getskillcheckchance(user, SC_LEARNMAGIC, difficulty, mod);
|
|
sprintf(buf2, "%s (%d%% success chance)", buf, difficulty-mod);
|
|
addchoice(&prompt, o->letter, buf2, NULL, f, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!prompt.nchoices) {
|
|
msg("You have no scrolls which you need to study.");
|
|
return B_TRUE;
|
|
}
|
|
getchoice(&prompt);
|
|
f = (flag_t *)prompt.result;
|
|
if (!f) {
|
|
msg("Cancelled");
|
|
return B_TRUE;
|
|
}
|
|
o = f->pile->ob;
|
|
|
|
// try to transcribe it...
|
|
difficulty = 100 + (getspelllevel(f->val[0])*10);
|
|
mod = getspellskill(user, f->val[0]) * 10;
|
|
|
|
if (skillcheck(user, SC_LEARNMAGIC, difficulty, mod)) {
|
|
addflag(user->flags, F_CANCAST, f->val[0], NA, NA, NULL);
|
|
} else {
|
|
// failed!
|
|
msg("You fail to comprehend this spell.");
|
|
}
|
|
taketime(user, getactspeed(user));
|
|
|
|
// now kill the scroll
|
|
msg("The scroll crumbles to dust.");
|
|
removeob(o, 1);
|
|
} else if (abilid == OT_A_STRIKETOKO) {
|
|
object_t *wep;
|
|
wep = getweapon(user);
|
|
if (wep) {
|
|
skill_t *sk;
|
|
sk = getobskill(wep->flags);
|
|
if (!sk || (sk->id != SK_CLUBS)) {
|
|
if (isplayer(user)) msg("You can only use bashing weapons to fight mercifully.");
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (lfhasflag(user, F_STRIKETOKO)) {
|
|
killflagsofid(user->flags, F_STRIKETOKO);
|
|
} else {
|
|
addflag(user->flags, F_STRIKETOKO, B_TRUE, NA, NA, NULL);
|
|
}
|
|
// note: takes no time.
|
|
} 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("^%c%s suck%s blood from %s!", getlfcol(target, CC_BAD), 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 = 6;
|
|
break;
|
|
case R_LEECH:
|
|
satedat = 8;
|
|
break;
|
|
}
|
|
|
|
if (num >= satedat) {
|
|
// sated.
|
|
killflagsofid(user->flags, F_ATTACHEDTO);
|
|
makepeaceful(user, NULL);
|
|
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_SWALLOW) {
|
|
object_t *o = NULL,*nexto;
|
|
int nswallowed = 0,dodged = B_FALSE;
|
|
|
|
if (!targcell) {
|
|
int dir;
|
|
// ask which dir
|
|
dir = askdir("Swallow in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else {
|
|
targcell = getcellindir(user->cell, dir);
|
|
}
|
|
}
|
|
|
|
// take some time
|
|
taketime(user, getactspeed(user));
|
|
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (!hasflag(o->flags, F_NOPICKUP)) {
|
|
// TODO: move to new map instead?
|
|
killob(o);
|
|
nswallowed++;
|
|
}
|
|
}
|
|
|
|
target = targcell->lf;
|
|
|
|
if (target) {
|
|
if (skillcheck(target, SC_DODGE, 100, 0)) {
|
|
dodged = B_TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
if (target) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
if (dodged) {
|
|
if (isplayer(user)) {
|
|
msg("^WYou try to swallow %s, but miss.", targname);
|
|
} else if (cansee(player, user) || isplayer(target)) {
|
|
msg("^W%s tries to swallow %s, but misses!", username, targname);
|
|
}
|
|
} else {
|
|
if (isplayer(user)) {
|
|
msg("^WYou swallow %s!", targname);
|
|
} else if (cansee(player, user) || isplayer(target)) {
|
|
msg("^W%s swallows %s!", username, targname);
|
|
}
|
|
}
|
|
} else {
|
|
if (isplayer(user)) {
|
|
msg("You swallow some %s!", (nswallowed || target) ? "objects" : "air");
|
|
} else if (cansee(player, user)) {
|
|
msg("%s swallows some %s!", username, (nswallowed || target) ? "objects" : "air");
|
|
}
|
|
}
|
|
|
|
if (target) {
|
|
if (isplayer(user)) {
|
|
// just kill the target
|
|
killlf(target);
|
|
} else {
|
|
// move the target into the swallower's stomach
|
|
map_t *newmap;
|
|
cell_t *c,*entry;
|
|
flag_t *lflinkflag;
|
|
int x,y;
|
|
condset_t cs;
|
|
|
|
|
|
lflinkflag = hasflag(user->flags, F_MAPLINK);
|
|
if (lflinkflag) {
|
|
newmap = findmap(lflinkflag->val[0]);
|
|
} else {
|
|
region_t *r;
|
|
// create and move to new "worm" map.
|
|
// this map will be destroyed when you leave it.
|
|
r = findregionbytype(BH_STOMACH);
|
|
if (!r) {
|
|
r = addregion(BH_STOMACH, NULL, -1, 0, user->cell->map->id);
|
|
}
|
|
// create stomach map
|
|
newmap = addmap();
|
|
createmap(newmap, 1, r, targcell->map, D_NONE, NULL);
|
|
addflag(newmap->flags, F_STOMACHOF, user->id, NA, NA, username);
|
|
}
|
|
|
|
// find a random empty cell here
|
|
//entry = getrandomroomcell(newmap, ANYROOM, WE_WALKABLE);
|
|
initcondv(&cs, CC_ISROOM, B_TRUE, NA,
|
|
CC_WALKABLEFOR, B_TRUE, target->id, CC_NONE);
|
|
entry = getcell_cond(newmap, &cs);
|
|
|
|
/*
|
|
while (!cellwalkable(target, entry, NULL)) {
|
|
//entry = getrandomroomcell(newmap, ANYROOM, WE_WALKABLE);
|
|
entry = getcell_cond(newmap, &ccwalkableroom);
|
|
}
|
|
*/
|
|
|
|
// link the map to this lf
|
|
if (!lflinkflag) {
|
|
addflag(user->flags, F_MAPLINK, newmap->id, NA, NA, NULL);
|
|
}
|
|
|
|
// udpate the map's exits to go to where we were swallowed
|
|
for (y = 0; y < newmap->h; y++) {
|
|
for (x = 0; x < newmap->w; x++) {
|
|
c = getcellat(newmap, x, y);
|
|
o = hasob(c->obpile, OT_STOMACHEXIT);
|
|
if (o) {
|
|
killflagsofid(o->flags, F_MAPLINK);
|
|
addflag(o->flags, F_MAPLINK, targcell->map->id, targcell->x, targcell->y, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
// pause before moving...
|
|
more();
|
|
}
|
|
|
|
// move target close to entrypoint
|
|
movelf(target, entry, B_FALSE);
|
|
// announce
|
|
announcearrival(target, target->cell->map);
|
|
}
|
|
}
|
|
} 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;
|
|
} else if (!lfhasflagval(user, F_HASATTACK, OT_CLAWS, NA, NA, NULL)) {
|
|
if (isplayer(user)) msg("You need claws to perform a swoop attack.");
|
|
return B_TRUE;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't swoop while stuck!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!targcell) {
|
|
if (isplayer(user)) {
|
|
snprintf(buf, BUFLEN, "Swoop who (max range %d)?",srange);
|
|
// TODO: ask for direction
|
|
targcell = askcoords(buf, "Swoop->", 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;
|
|
} else if (getcelldist(user->cell, targcell) < 2) {
|
|
if (isplayer(user)) msg("You don't have enough space to charge there!");
|
|
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, B_FALSE);
|
|
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
|
|
addflag(user->flags, F_FORCEATTACK, OT_CLAWS, NA, NA, "fromuseability");
|
|
attackcell(user, targcell, B_TRUE);
|
|
f = lfhasflagval(user, F_FORCEATTACK, OT_CLAWS, NA, NA, "fromuseability");
|
|
if (f) killflag(f);
|
|
|
|
// teleport back to initial pos
|
|
if (hasfreeaction(user)) {
|
|
movelf(user, origcell, B_FALSE);
|
|
if (haslos(player, adjcell)) {
|
|
msg("%s swoop%s away!",username,isplayer(user) ? "" : "s");
|
|
needredraw = B_TRUE;
|
|
drawlevelfor(player);
|
|
redraw();
|
|
//if (!isplayer(user)) {
|
|
//}
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_THRUST) {
|
|
object_t *wep;
|
|
flag_t *f;
|
|
char targetname[BUFLEN];
|
|
int badweapon = B_FALSE;
|
|
flag_t *damflag = NULL;
|
|
obpile_t *op = NULL;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You lack the stability for a thrust while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
wep = getweapon(user);
|
|
if (!wep) {
|
|
object_t *weplist[MAXPILEOBS];
|
|
int nweps;
|
|
// look for innate attack
|
|
getweapons(user, B_MELEEONLY, weplist, NULL, NULL, &op, &nweps);
|
|
if (nweps) {
|
|
wep = weplist[0];
|
|
} else {
|
|
badweapon = B_TRUE;
|
|
}
|
|
}
|
|
if (wep) {
|
|
if (getdamtype(wep) != DT_PIERCE) {
|
|
badweapon = B_TRUE;
|
|
}
|
|
damflag = hasflag(wep->flags, F_DAM);
|
|
}
|
|
if (!damflag) {
|
|
badweapon = B_TRUE;
|
|
}
|
|
|
|
if (badweapon) {
|
|
if (isplayer(user)) msg("You need a long piercing weapon to perform a thrust!" );
|
|
if (op) killobpile(op);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
snprintf(buf, BUFLEN, "Thrust at who (max range 2)?");
|
|
targcell = askcoords(buf, "Thrust->", TT_MONSTER, user, 2, LOF_NEED, B_TRUE);
|
|
if (!targcell) {
|
|
msg("Cancelled.");
|
|
if (op) killobpile(op);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
if (isplayer(user)) msg("There is nobody there to attack!");
|
|
if (op) killobpile(op);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
f = addflag(user->flags, F_ACCURACYMOD, -10, NA, NA, NULL);
|
|
attacklf(user, target, wep, damflag);
|
|
killflag(f);
|
|
taketime(user, getattackspeed(user));
|
|
if (op) killobpile(op);
|
|
} else if (abilid == OT_A_TRAIN) {
|
|
// can we train?
|
|
if (!user->skillpoints && !lfhasflag(user, F_HASNEWLEVEL)) {
|
|
if(isplayer(user)) {
|
|
msg("You are not ready for any training right now.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// safe to train?
|
|
if (check_rest_ok(user)) return B_TRUE;
|
|
|
|
// warn if we're a mage training without a spellbook
|
|
/*
|
|
f = lfhasflag(user, F_CANSTUDY);
|
|
if (f) {
|
|
if (!hasobwithflagval(user->pack, F_LINKSCHOOL, f->val[0], NA, NA, NULL) &&
|
|
!hasob(user->pack, OT_GRIMOIRE)) {
|
|
char buf[BUFLEN];
|
|
snprintf(buf, BUFLEN, "You cannot study %s without a spellbook. Continue", getschoolname(f->val[0]));
|
|
// warn
|
|
if (!real_warnabout(buf, DEF_WARNINGTIME, B_TRUE)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
|
|
// start training!
|
|
if (!startresting(user, B_TRUE)) {
|
|
// do the first one right away
|
|
rest(user, B_TRUE);
|
|
}
|
|
} else if (abilid == OT_A_TIPTOE) {
|
|
if (isswimming(user)) {
|
|
if (isplayer(user)) msg("You can't tiptoe while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
if (isairborne(user, NULL)) {
|
|
if (isplayer(user)) msg("You can't tiptoe while airborne!");
|
|
return B_TRUE;
|
|
}
|
|
if (isclimbing(user)) {
|
|
if (isplayer(user)) msg("You can't tiptoe while climbing!");
|
|
return B_TRUE;
|
|
}
|
|
trysneak(user, D_NONE);
|
|
} else if (abilid == OT_A_TRIPLF) {
|
|
object_t *wep;
|
|
int skillmod = 0;
|
|
int legs;
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Trip in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_MYSELF) {
|
|
if (isplayer(user)) msg("You can't trip yourself!");
|
|
return B_TRUE;
|
|
} else if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else {
|
|
targcell = getcellindir(user->cell, dir);
|
|
}
|
|
}
|
|
|
|
if (!haslos(user, targcell)) {
|
|
if (isplayer(user)) msg("You can't see there!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
if (isplayer(user)) msg("There is nobody there to trip!");
|
|
return B_TRUE;
|
|
}
|
|
if (isairborne(target, NULL)) {
|
|
if (isplayer(user)) msg("You can't trip someone in the air!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
legs = countlegs(target);
|
|
|
|
if (legs == 0) {
|
|
if (isplayer(user)) msg("You can't trip something which has no legs!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
getlfname(target, targetname);
|
|
|
|
wep = getweapon(user);
|
|
|
|
// try to trip them...
|
|
taketime(user, getactspeed(user));
|
|
|
|
if (wep) {
|
|
skillmod = getweaponskill(user, wep)*10;
|
|
if (isplayer(user) && (skillmod == 0)) skillmod = -5;
|
|
} else {
|
|
skillmod = getskill(user, SK_UNARMED)*10;
|
|
}
|
|
|
|
if (skillcheckvs(user, SC_DEX, skillmod, target, SC_SLIP, (legs > 2) ? 25 : 0)) {
|
|
if (cansee(player, user)) {
|
|
msg("^w%s trip%s %s.",username, isplayer(user) ? "" : "s", targetname);
|
|
}
|
|
fall(target, NULL, B_TRUE);
|
|
} else {
|
|
if (isplayer(user)) {
|
|
msg("You try to trip %s, but fail.",targetname);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s tries to trip %s, but fails.",username, targetname);
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_TUMBLE) {
|
|
cell_t *origcell,*c;
|
|
cell_t *retcell[MAXRETCELLS];
|
|
char stopthing[BUFLEN],verb[BUFLEN], stopverb[BUFLEN];
|
|
int i,nretcell = 0,dir = D_NONE;
|
|
enum RELATIVEDIR reldir;
|
|
object_t *stopob = NULL;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't tumble while swimming!");
|
|
return B_TRUE;
|
|
} else if (isairborne(user, NULL)) {
|
|
if (isplayer(user)) msg("You can't tumble while airbourne!");
|
|
return B_TRUE;
|
|
} else if (lfhasflag(user, F_GRABBING)) {
|
|
if (isplayer(user)) msg("You can't tumble while holding someone!");
|
|
return B_TRUE;
|
|
} else if (lfhasflag(user, F_GRABBEDBY)) {
|
|
if (isplayer(user)) msg("You can't tumble while being held!");
|
|
return B_TRUE;
|
|
} else if (isstuck(user)) {
|
|
if (isplayer(user)) msg("You can't tumble while stuck!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (targcell) {
|
|
dir = getdirtowards(user->cell, targcell, NULL, B_FALSE, DT_ORTH);
|
|
} else {
|
|
dir = askdir("Tumble in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if ((dir == D_NONE) || (dir == D_MYSELF)) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else {
|
|
cell_t *cell1 = NULL,*cell2 = NULL;
|
|
|
|
cell1 = getcellindir(user->cell, dir);
|
|
if (cell1) {
|
|
cell2 = getcellindir(cell1, dir);
|
|
} else {
|
|
if (isplayer(user)) msg("There is no room to tumble that way!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (cell2) targcell = cell2;
|
|
else targcell = cell1;
|
|
|
|
if (!targcell) {
|
|
if (isplayer(user)) msg("There is no room to tumble that way!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
reldir = getrelativedir(user, dir);
|
|
switch (reldir) {
|
|
case RD_FORWARDS:
|
|
strcpy(verb, "tumble"); break;
|
|
case RD_BACKWARDS:
|
|
strcpy(verb, "backflip"); break;
|
|
case RD_SIDEWAYS:
|
|
strcpy(verb, "cartwheel"); break;
|
|
}
|
|
|
|
if (isburdened(user)) {
|
|
if (isplayer(user)) {
|
|
msg("Your load is too heavy to %s with!", verb);
|
|
}
|
|
return B_TRUE;
|
|
} else if (lfhasflag(user, F_GRAVBOOSTED)) {
|
|
if (isplayer(user)) {
|
|
msg("Gravity around you is too strong to %s!", verb);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
origcell = user->cell;
|
|
|
|
taketime(user, getactspeed(user));
|
|
|
|
|
|
// will you be interrupted on the way?
|
|
strcpy(stopthing, "");
|
|
strcpy(stopverb, "");
|
|
calcbresnham(origcell->map, origcell->x, origcell->y, targcell->x, targcell->y, retcell, &nretcell);
|
|
for (i = 0; i < nretcell; i++) {
|
|
c = retcell[i];
|
|
if (getcellwaterdepth(c, user)) {
|
|
// stop here.
|
|
targcell = c;
|
|
stopob = hasobwithflag(c->obpile, F_DEEPWATER);
|
|
strcpy(stopverb, "fall");
|
|
getobname(stopob, stopthing, stopob->amt);
|
|
break;
|
|
} else if (hasobwithflagval(c->obpile, F_PIT, D_DOWN, NA, NA, NULL)) {
|
|
// stop here.
|
|
targcell = c;
|
|
stopob = hasobwithflagval(c->obpile, F_PIT, D_DOWN, NA, NA, NULL);
|
|
strcpy(stopverb, "fall");
|
|
getobname(stopob, stopthing, stopob->amt);
|
|
break;
|
|
}
|
|
|
|
// run into a person, wall or impassable object?
|
|
if (i >= 1) {
|
|
enum ERROR why;
|
|
if (!cellwalkable(user, c, &why)) { // this will set "rdata"
|
|
// stop in previous cell
|
|
strcpy(stopverb, "bump");
|
|
targcell = retcell[i-1];
|
|
if (haslos(user, c)) {
|
|
switch (why) {
|
|
case E_WALLINWAY:
|
|
strcpy(stopthing, c->type->name);
|
|
break;
|
|
case E_OBINWAY:
|
|
if (canseeob(user, (object_t *)rdata)) {
|
|
getobname((object_t *)rdata, stopthing, ((object_t *)rdata)->amt);
|
|
} else {
|
|
strcpy(stopthing, "something");
|
|
}
|
|
break;
|
|
case E_LFINWAY:
|
|
if (cansee(user, (lifeform_t *)rdata)) {
|
|
getlfname((lifeform_t *)rdata, stopthing);
|
|
} else {
|
|
strcpy(stopthing, "someone");
|
|
}
|
|
break;
|
|
default:
|
|
strcpy(stopthing, "something");
|
|
break;
|
|
}
|
|
} else {
|
|
strcpy(stopthing, "something");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// skillcheck...
|
|
if (!skillcheck(user, SC_TUMBLE, 50, 0)) {
|
|
// fail!
|
|
if (isplayer(user)) {
|
|
msg("You fumble and fall.");
|
|
} else if (cansee(player, user)) {
|
|
msg("%s fumbles and falls.", username);
|
|
}
|
|
fall(user, NULL, B_FALSE);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// announce
|
|
if (isplayer(user)) {
|
|
msg("You %s across the ground!", verb);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s %ss across the ground!", username, verb);
|
|
}
|
|
|
|
// rolling around will put out fires
|
|
if (reldir == RD_FORWARDS) {
|
|
extinguishlf(user);
|
|
}
|
|
|
|
// go there!
|
|
movelf(user, targcell, B_FALSE);
|
|
|
|
// pits/water?
|
|
if (strlen(stopthing)) {
|
|
if (isplayer(user)) {
|
|
msg("You %s into %s!", stopverb, stopthing);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s %ss into %s!",username, stopverb, stopthing);
|
|
}
|
|
|
|
// pass another (harder) skill check or fall
|
|
if (!skillcheck(user, SC_TUMBLE, 100, 0)) {
|
|
// fail!
|
|
fall(user, NULL, B_TRUE);
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
practice(user, SK_ATHLETICS, 1);
|
|
} 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_TRUE;
|
|
}
|
|
|
|
// this call will also remove this ability...
|
|
setrace(user, f->val[0], B_TRUE);
|
|
} else if (abilid == OT_A_PRAY) {
|
|
lifeform_t *god;
|
|
if (!isplayer(user)) return B_FALSE;
|
|
|
|
if (lfhasflag(player, F_NOPRAY)) {
|
|
if (isplayer(user)) {
|
|
msg("Your pride prevents you from worshipping another being.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for which god
|
|
god = askgod("To whom will you pray?", B_FALSE, B_TRUE);
|
|
if (!god) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
prayto(user, god);
|
|
} 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) {
|
|
char buf2[HUGEBUFLEN];
|
|
enum SKILLLEVEL curlev;
|
|
curlev = getskill(user, sk->id);
|
|
if (curlev != PR_MASTER) {
|
|
snprintf(buf, BUFLEN, "%s (%s)",getskillname(sk->id), getskilldesc(sk->id));
|
|
makedesc_skill(sk->id, buf2, curlev+1);
|
|
addchoice(&prompt, ch++, getskillname(sk->id), buf, sk, buf2);
|
|
}
|
|
}
|
|
getchoicestr(&prompt, B_FALSE, B_TRUE);
|
|
sk = (skill_t *)prompt.result;
|
|
if (sk) {
|
|
enum SKILLLEVEL firstlev,wantlev;
|
|
int i;
|
|
ch = 'a';
|
|
firstlev = getskill(user, sk->id) + 1;
|
|
initprompt(&prompt, "How much will you learn this skill?");
|
|
for (i = firstlev ; i <= PR_MASTER; i++) {
|
|
snprintf(buf, BUFLEN, "%s",getskilllevelname(i));
|
|
addchoice(&prompt, ch++, buf, buf, NULL, NULL);
|
|
}
|
|
if (prompt.nchoices <= 0) {
|
|
msg("You have already mastered this skill!");
|
|
return B_TRUE;
|
|
}
|
|
ch = getchoice(&prompt);
|
|
wantlev = firstlev + (ch - 'a');
|
|
|
|
//while (strcmp(getskilllevelname(getskill(user, sk->id)), prompt.choice[prompt.selection].text)) {
|
|
giveskilllev(user, sk->id, wantlev);
|
|
//}
|
|
} else {
|
|
msg("Cancelled.");
|
|
}
|
|
} 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, B_TRUE);
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_BLINDALL) {
|
|
lifeform_t *lf;
|
|
for (lf = user->cell->map->lf ; lf ; lf = lf->next) {
|
|
if (!isplayer(lf)) {
|
|
addflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL);
|
|
killflagsofid(lf->flags, F_WANTS);
|
|
killflagsofid(lf->flags, F_WANTSOBFLAG);
|
|
}
|
|
}
|
|
msg("all blinded!");
|
|
} else if (abilid == OT_A_AIMEDSTRIKE) {
|
|
object_t *wep;
|
|
char targetname[BUFLEN];
|
|
flag_t *f;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You lack the control for an aimed strike while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
wep = getweapon(user);
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Aimed strike in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE;
|
|
} else if (dir == D_MYSELF) {
|
|
// yourself!
|
|
targcell = user->cell;
|
|
} 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(user->flags, F_AIMEDSTRIKE, B_TRUE, NA, NA, NULL);
|
|
attackcell(user, targcell, B_TRUE);
|
|
killflag(f);
|
|
} else if (abilid == OT_A_ALTERATTACK) {
|
|
object_t *wep;
|
|
enum SKILLLEVEL slev = PR_INEPT;
|
|
char obname[BUFLEN],buf[BUFLEN];
|
|
flag_t *retflag[MAXCANDIDATES],*damflag;
|
|
int nretflags,i;
|
|
char ch = 'a';
|
|
enum DAMTYPE curdt = DT_NONE;
|
|
wep = getweapon(user);
|
|
if (wep) {
|
|
slev = getweaponskill(user, wep);
|
|
curdt = getdamtype(wep);
|
|
} else {
|
|
msg("You need to weild a weapon first!");
|
|
return B_TRUE;
|
|
}
|
|
if (slev < PR_ADEPT) {
|
|
msg("You are not skilled enough with your weapon to do this!");
|
|
return B_TRUE;
|
|
}
|
|
damflag = hasflag(wep->flags, F_DAM);
|
|
if (damflag) {
|
|
getflags(wep->flags, retflag, &nretflags, F_ALTDAM, F_NONE);
|
|
} else {
|
|
nretflags = 0;
|
|
}
|
|
if (!nretflags) {
|
|
msg("Your weapon is not capable of dealing any other damage type.");
|
|
return B_TRUE;
|
|
}
|
|
getobname(wep, obname, 1);
|
|
sprintf(buf, "Deal what kind of damage with your %s?", noprefix(obname));
|
|
initprompt(&prompt, buf);
|
|
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
sprintf(buf, "%s, DR %d", getdamname(f->val[0]), f->val[1]);
|
|
if (strlen(f->text)) {
|
|
strcat(buf, "(");
|
|
strcat(buf, f->text);
|
|
strcat(buf, ")");
|
|
}
|
|
if (f->val[0] == curdt) {
|
|
strcat(buf, " (current)");
|
|
}
|
|
addchoice(&prompt, ch++, buf, NULL, f, NULL);
|
|
}
|
|
ch = getchoice(&prompt);
|
|
f = (flag_t *)prompt.result;
|
|
if (f->val[0] == curdt) {
|
|
msg("Cancelled.");
|
|
} else {
|
|
// change dam type and amt
|
|
damflag->val[0] = f->val[0];
|
|
damflag->val[1] = f->val[1];
|
|
msg("Your %s will now deal %s damage.", noprefix(obname), getdamname(damflag->val[0]));
|
|
}
|
|
} else if (abilid == OT_A_COMBOSTRIKE) {
|
|
object_t *wep;
|
|
skill_t *wepsk = NULL;
|
|
int keepgoing = B_TRUE,nhits = 0;
|
|
enum SKILLLEVEL slev;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You cannot perform a combination attack while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
wep = getweapon(user);
|
|
if (wep) {
|
|
wepsk = getobskill(wep->flags);
|
|
} else {
|
|
wepsk = findskill(SK_UNARMED);
|
|
}
|
|
if (wepsk) {
|
|
slev = getskill(user, wepsk->id);
|
|
} else {
|
|
slev = PR_INEPT;
|
|
}
|
|
if (slev < PR_MASTER) {
|
|
if (isplayer(user)) msg("You must have Mastered your weapon to perform a combination attack.");
|
|
return B_TRUE;
|
|
}
|
|
if (!getadjenemies(user, NULL, NULL)) {
|
|
if (isplayer(user)) msg("You cannot see anyone nearby to attack!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// take time
|
|
taketime(user, getattackspeed(user));
|
|
// remember that we are doing a combo. this will stop taketime() from being
|
|
// called during our attacks.
|
|
addflag(user->flags, F_COMBOSTRIKE, B_TRUE, NA, NA, NULL);
|
|
while (keepgoing) {
|
|
cell_t *c;
|
|
keepgoing = B_FALSE;
|
|
|
|
// ask direction
|
|
c = NULL;
|
|
while (!c) {
|
|
int dir;
|
|
char ques[BUFLEN];
|
|
snprintf(ques, BUFLEN,"%s combination in which direction (- to cancel)", nhits ? "Continue" : "Start");
|
|
dir = askdir(ques, B_TRUE, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
break;
|
|
} else {
|
|
c = getcellindir(user->cell, dir);
|
|
}
|
|
}
|
|
|
|
// attack in that dir
|
|
if (c && c->lf) {
|
|
attackcell(user, c, B_TRUE);
|
|
|
|
// if the lf there died, keep going.
|
|
if (!c->lf || isdead(c->lf)) {
|
|
keepgoing = B_TRUE;
|
|
}
|
|
|
|
// other reasons to stop the combo now?
|
|
if (keepgoing) {
|
|
if (isdead(user) || (getweapon(user) != wep)) { // dead or lost our weapon?
|
|
keepgoing = B_FALSE;
|
|
} else if (!getadjenemies(user, NULL, NULL)) { // noone left to attack
|
|
keepgoing = B_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
msg("Combination ended.");
|
|
keepgoing = B_FALSE;
|
|
}
|
|
// process lf deaths, then redraw the screen
|
|
checkdeath();
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
more(); // clear msgs
|
|
nhits++;
|
|
}
|
|
killflagsofid(user->flags, F_COMBOSTRIKE);
|
|
} else if (abilid == OT_A_DEBUG) {
|
|
cell_t *where;
|
|
where = askcoords("Debug who?", "Debug->",TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (where && where->lf) {
|
|
debug(where->lf);
|
|
}
|
|
} else if (abilid == OT_A_DUMPMON) {
|
|
msg("Dumping monsters...");
|
|
dumpmonsters(H_DUNGEON);
|
|
msg("Monster list dumped to monsters.html.");
|
|
} else if (abilid == OT_A_PATHFIND) {
|
|
cell_t *where;
|
|
where = askcoords("Pathfind to where?", "Pathfind->",TT_NONE, user, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (where) {
|
|
if (ai_createpathto(user, where)) {
|
|
msg("Success!"); more();
|
|
showpath(user);
|
|
killflagsofid(user->flags, F_AIPATH);
|
|
} else {
|
|
msg("PATHFIND FAILED.");
|
|
}
|
|
|
|
}
|
|
} else if (abilid == OT_A_PETIFY) {
|
|
cell_t *where;
|
|
where = askcoords("Petify who?", "Petify->",TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (where && where->lf) {
|
|
petify(where->lf, user);
|
|
msg("Petified %s.", where->lf->race->name);
|
|
}
|
|
} else if (abilid == OT_A_EMPLOY) {
|
|
cell_t *where;
|
|
where = askcoords("Assign job to who?", "Assignjob->",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);
|
|
snprintf(question, BUFLEN, "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);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
msg("There is nobody there!");
|
|
}
|
|
} else if (abilid == OT_A_ENHANCE) {
|
|
cell_t *where;
|
|
where = askcoords("Enhance stats of who?", "Enhancestats->",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)?", "sacin",NULL, B_TRUE, B_FALSE);
|
|
switch (ch) {
|
|
case 's': att = A_STR; break;
|
|
case 'a': att = A_AGI; 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_ENHANCEOB) {
|
|
object_t *o,*o2;
|
|
enum MATERIAL repairablemats[MAXCANDIDATES];
|
|
int cutoffpct[MAXCANDIDATES];
|
|
int i,nmats = 0;
|
|
char ch,obname[BUFLEN];
|
|
|
|
if (!isplayer(user)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
// get list of resizable materials
|
|
getworkablematerials(user, SK_METALWORK, repairablemats, cutoffpct, &nmats);
|
|
getworkablematerials(user, SK_SEWING, repairablemats, cutoffpct, &nmats);
|
|
|
|
// 1.compile a list of enhancable objects
|
|
initprompt(&prompt, "Enhance which object?");
|
|
addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL);
|
|
for (o = user->pack->first ; o ; o = o->next) {
|
|
int ok = B_FALSE;
|
|
for (i = 0; i < nmats; i++) {
|
|
if (o->material->id == repairablemats[i]) {
|
|
ok = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (ok && (isweapon(o) || isarmour(o)) && !hasflag(o->flags, F_NOQUALITY) &&
|
|
!hasflag(o->flags, F_MASTERWORK)) {
|
|
int gotdupe = B_FALSE;
|
|
// do we have another?
|
|
for (o2 = user->pack->first ; o2 ; o2 = o2->next) {
|
|
if ((o2 != o) && (o2->type->id == o->type->id)) {
|
|
gotdupe = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (gotdupe) {
|
|
// we can enhance this object
|
|
getobname(o, obname, o->amt);
|
|
addchoice(&prompt, o->letter, obname, NULL, o, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prompt.nchoices <= 1) {
|
|
msg("You don't have anything which you are able to enhance.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// 2. ask which ones to enhance
|
|
getchoice(&prompt);
|
|
o = (object_t *) prompt.result;
|
|
getobname(o, obname, 1);
|
|
|
|
// ask what size we want.
|
|
sprintf(buf, "Destroy which object to enahnce %s?", obname);
|
|
initprompt(&prompt, buf);
|
|
for (o2 = user->pack->first ; o2 ; o2 = o2->next) {
|
|
if ((o2 != o) && (o2->type->id == o->type->id)) {
|
|
getobname(o2, obname, o2->amt);
|
|
addchoice(&prompt, o2->letter, obname, NULL, o2, NULL);
|
|
}
|
|
}
|
|
addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL);
|
|
prompt.maycancel = B_TRUE;
|
|
|
|
ch = getchoice(&prompt);
|
|
if ((ch == '-') || (ch == '\0')) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
o2 = (object_t *)prompt.result;
|
|
|
|
// in case it's on fire, etc
|
|
if (touch(user, o)) {
|
|
taketime(user, getactspeed(user));
|
|
return B_FALSE;
|
|
}
|
|
if (touch(user, o2)) {
|
|
taketime(user, getactspeed(user));
|
|
return B_FALSE;
|
|
}
|
|
|
|
// destroy second ob
|
|
removeob(o2, ALL);
|
|
// fully repair first ob
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) f->val[0] = f->val[1];
|
|
// enhance first ob
|
|
addflag(o->flags, F_MASTERWORK, B_TRUE, NA, NA, NULL);
|
|
|
|
getobname(o, obname, o->amt);
|
|
msgnocap("%c - %s", o->letter, obname);
|
|
|
|
practice(user, SK_METALWORK, 1);
|
|
practice(user, SK_SEWING, 1);
|
|
|
|
taketime(user, getactspeed(user));
|
|
} else if (abilid == OT_A_EXPLODESELF) {
|
|
// special case to avoid abuse
|
|
if (isplayer(user) && isimmuneto(user->flags, DT_EXPLOSIVE, B_FALSE)) {
|
|
msg("Your resistance to explosive damage prevents the use of this ability.");
|
|
return B_TRUE;
|
|
}
|
|
msg("^%c%s explode%s!", getlfcol(user, CC_VBAD), username, isplayer(user) ? "" : "s");
|
|
explodecells(user->cell, 9999, B_TRUE, NULL, 2, DT_COMPASS, B_FALSE, user);
|
|
} else if (abilid == OT_A_EXPOSEDSTRIKE) {
|
|
flag_t *f;
|
|
if (getweaponskill(user, getweapon(user)) < PR_BEGINNER) {
|
|
if (isplayer(user)) msg("You are not skilled enough to perform a wild strike.", HEAVYWEPKG);
|
|
return B_TRUE;
|
|
}
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Wild strike in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
if (isplayer(user)) msg("Cancelled.");
|
|
return B_TRUE ;
|
|
} else {
|
|
targcell = getcellindir(user->cell, dir);
|
|
}
|
|
}
|
|
|
|
f = addflag(user->flags, F_ACCURACYMOD, 200, NA, NA, NULL);
|
|
attackcell(user, targcell, B_FALSE);
|
|
taketime(user, getattackspeed(user)*2);
|
|
killflag(f);
|
|
} else if (abilid == OT_A_HEAVYBLOW) {
|
|
object_t *wep;
|
|
char targetname[BUFLEN];
|
|
flag_t *f,*damflag;
|
|
int badweapon = B_FALSE;
|
|
|
|
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) {
|
|
if (!hasjob(user, J_MONK)) {
|
|
badweapon = B_TRUE;
|
|
}
|
|
} else if (!ismeleeweapon(wep) || !isheavyweapon(wep)) {
|
|
badweapon = B_TRUE;
|
|
}
|
|
damflag = hasflag(wep->flags, F_DAM);
|
|
if (!damflag) {
|
|
badweapon = B_TRUE;
|
|
}
|
|
|
|
if (badweapon) {
|
|
if (isplayer(user)) msg("You need a heavy weapon (%dkg or more) to perform a heavy blow!", HEAVYWEPKG);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Heavy blow in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
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(user->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL);
|
|
attacklf(user, target, wep, damflag);
|
|
taketime(user, getattackspeed(user));
|
|
killflag(f);
|
|
} else if (abilid == OT_A_QUIVERINGPALM) {
|
|
object_t *wep;
|
|
char targetname[BUFLEN];
|
|
flag_t *f;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You lack the stability to use this ability while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
wep = getweapon(user);
|
|
if (wep) {
|
|
if (isplayer(user)) msg("You must be unarmed to make a quivering palm strike.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Quivering Palm in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
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(user->flags, F_QUIVERINGPALM, B_TRUE, NA, NA, NULL);
|
|
attackcell(user, targcell, B_TRUE);
|
|
killflag(f);
|
|
} else if (abilid == OT_A_STEAL) {
|
|
enum SKILLLEVEL slev;
|
|
object_t *wep;
|
|
char targetname[BUFLEN];
|
|
flag_t *penalty = NULL;
|
|
int failed = B_TRUE;
|
|
|
|
slev = getskill(user, SK_THIEVERY);
|
|
if (slev == PR_INEPT) {
|
|
if (isplayer(user)) msg("You are too unskilled to steal anything!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// stealing from a lifeform
|
|
// ask for direction
|
|
if (!targcell) {
|
|
int dir;
|
|
dir = askdir("Steal in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
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 steal from!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
taketime(user, getactspeed(user));
|
|
|
|
getlfname(target, targetname);
|
|
|
|
if (slev == PR_NOVICE) {
|
|
penalty = addflag(user->flags, F_ACCURACYMOD, -14, NA, NA, NULL);
|
|
} else if (slev == PR_BEGINNER) {
|
|
penalty = addflag(user->flags, F_ACCURACYMOD, -7, NA, NA, NULL);
|
|
}
|
|
|
|
// use empty handed attack accuracy
|
|
wep = getweapon(user);
|
|
if (rolltohit(user, target, wep, NULL, NULL, NULL)) {
|
|
// success!
|
|
failed = B_FALSE;
|
|
if (steal(user, target->pack, F_NONE)) {
|
|
if (isplayer(user)) {
|
|
msg("%s has nothing for you to steal!", targetname);
|
|
}
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (penalty) {
|
|
killflag(penalty);
|
|
}
|
|
|
|
if (failed) {
|
|
if (isplayer(user)) {
|
|
msg("You try to steal from %s, but fail.", targetname);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s tries to steal from %s, but fails.", username, targetname);
|
|
}
|
|
// ai will get angry!
|
|
if (cansee(target, user) && !isplayer(target)) {
|
|
fightback(target, user);
|
|
}
|
|
} else {
|
|
practice(user, SK_THIEVERY, 1);
|
|
}
|
|
} else if (abilid == OT_A_WARCRY) {
|
|
if (lfhasflag(user, F_SILENCED)) {
|
|
if (isplayer(user)) msg("You are unable to make a sound!");
|
|
return B_TRUE;
|
|
}
|
|
// 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)) {
|
|
int ok = B_FALSE;
|
|
switch (target->race->raceclass->id) {
|
|
case RC_ANIMAL:
|
|
case RC_HUMANOID:
|
|
ok = B_TRUE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (ok && canhear(target, user->cell, 4, NULL)) {
|
|
scare(target, user, rnd(5,10), 0);
|
|
}
|
|
}
|
|
}
|
|
} else if (abilid == OT_A_HURRICANESTRIKE) {
|
|
int dir;
|
|
cell_t *c;
|
|
flag_t *f,*f2,*f3;
|
|
object_t *wep;
|
|
|
|
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);
|
|
// remember we are doing a hurricane attack to avoid lots of
|
|
// "there is nothing to attack there" messages.
|
|
f3 = addflag(user->flags, F_HURRICANESTRIKE, B_TRUE, NA, NA, NULL);
|
|
|
|
wep = getweapon(user);
|
|
if (wep) {
|
|
char buf[BUFLEN],wepname[BUFLEN];
|
|
getobname(wep, buf, 1);
|
|
sprintf(wepname, "%s", noprefix(buf));
|
|
if (isplayer(user)) {
|
|
msg("You swing your %s in a wide arc!", wepname);
|
|
} else if (cansee(player, user)) {
|
|
msg("%s swings its %s in a wide arc!", username, wepname);
|
|
}
|
|
} else {
|
|
if (isplayer(user)) {
|
|
msg("You attack in a wide arc!");
|
|
} else if (cansee(player, user)) {
|
|
msg("%s attacks in a wide arc!", username);
|
|
}
|
|
}
|
|
// attack all adjacent enemies
|
|
for (dir = DC_N; dir <= DC_NW; dir++) {
|
|
c = getcellindir(user->cell, dir);
|
|
if (c) {
|
|
attackcell(user, c, B_TRUE);
|
|
}
|
|
}
|
|
// remove temporary flags
|
|
killflag(f);
|
|
killflag(f2);
|
|
killflag(f3);
|
|
} else if (abilid == OT_A_HIDE) {
|
|
lifeform_t *lf;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You can't hide while swimming!");
|
|
return B_TRUE;
|
|
}
|
|
if (lfproduceslight(user, NULL)) {
|
|
if (isplayer(user)) msg("You can't hide while producing light!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (lfhasflag(user, F_HIDING)) {
|
|
// stop hiding.
|
|
killflagsofid(user->flags, F_HIDING);
|
|
taketime(user, getactspeed(user));
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(user)) {
|
|
if (!safetorest(user, NULL)) {
|
|
if (getattrbracket(getattr(user, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) {
|
|
if (askchar("Really try to hide while in view of enemies?", "yn", "n", B_TRUE, B_FALSE) != 'y') {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (getskill(user, SK_STEALTH) < PR_EXPERT) {
|
|
// anyone who saw you start hiding can still see you
|
|
for (lf = user->cell->map->lf ; lf ; lf = lf->next) {
|
|
if (lf == user) continue;
|
|
if (cansee(lf, user)) {
|
|
addflag(lf->flags, F_SPOTTED, user->id, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
stopsprinting(user);
|
|
// start hiding
|
|
addflag(user->flags, F_HIDING, 0, NA, NA, NULL);
|
|
|
|
// even though it's untrainable - this will
|
|
// still mark it as used.
|
|
practice(user, SK_STEALTH, 1);
|
|
|
|
taketime(user, getactspeed(user));
|
|
} else if (abilid == OT_A_INSPECT) {
|
|
object_t *o;
|
|
int difficulty;
|
|
enum RARITY rarity;
|
|
int mod;
|
|
enum SKILLLEVEL slev;
|
|
flag_t *f;
|
|
|
|
// only players can do this
|
|
if (!isplayer(user)) {
|
|
return B_FALSE;
|
|
}
|
|
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
|
|
initprompt(&prompt, "Inspect which object");
|
|
for (o = user->pack->first ; o ; o = o->next) {
|
|
int classok = B_FALSE;
|
|
enum SKILL whichsk;
|
|
whichsk = getclassloreskill(o->type->obclass->id);
|
|
// can we inspect this?
|
|
if ((whichsk != SK_NONE) && getskill(user, whichsk)) classok = B_TRUE;
|
|
if (classok && !isknown(o)) {
|
|
char buf[BUFLEN];
|
|
getobname(o, buf, o->amt);
|
|
addchoice(&prompt, o->letter, buf, NULL, o, NULL);
|
|
}
|
|
}
|
|
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
|
|
prompt.maycancel = B_TRUE;
|
|
|
|
if (prompt.nchoices == 1) {
|
|
msg("You don't have anything which you can inspect.");
|
|
return B_TRUE;
|
|
}
|
|
getchoice(&prompt);
|
|
o = (object_t *)prompt.result;
|
|
if (!o) {
|
|
msg("Cancelled");
|
|
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) {
|
|
if (f->val[2] == NA) {
|
|
rarity = RR_COMMON;
|
|
} else {
|
|
rarity = f->val[2];
|
|
}
|
|
} else {
|
|
rarity = 0;
|
|
}
|
|
difficulty = 120 + (rarity*10);
|
|
/*
|
|
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, getclassloreskill(o->type->obclass->id));
|
|
switch (slev) {
|
|
case PR_INEPT:
|
|
mod = -5; // should never happen
|
|
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));
|
|
} else if (abilid == OT_A_IRONFIST) {
|
|
char tname[BUFLEN];
|
|
int dam,dir = D_NONE;
|
|
|
|
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
|
|
if (isplayer(user)) msg("You lack the stability to invoke the iron fist manouver while swimming.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (getweapon(user)) {
|
|
if (isplayer(user)) msg("You must be unarmed to invoke the iron fist.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// ask for direction
|
|
if (!targcell) {
|
|
dir = askdir("Iron fist in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
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, tname);
|
|
|
|
if (isplayer(user)) {
|
|
msg("^%cYou strike %s with THE IRON FIST!",getlfcol(user, CC_GOOD),tname);
|
|
} else if (cansee(player, user)) {
|
|
msg("^%c%s strikes %s with THE IRON FIST!",getlfcol(user, CC_GOOD),username, tname);
|
|
}
|
|
|
|
// convert all remaining stamina into damage.
|
|
dam = getstamina(user);
|
|
|
|
setstamina(user, 0);
|
|
sprintf(damstr, "%s%s invocation of the iron fist",username,getpossessive(username));
|
|
losehp(target, dam, DT_DIRECT, user, damstr);
|
|
|
|
if (dir != D_NONE) {
|
|
// knockback too
|
|
knockback(target, dir, dam, user, 0, B_DOANNOUNCE, B_DODAM);
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
void addbuildchoice(prompt_t *p, lifeform_t *lf, enum OBTYPE oid, char *ch) {
|
|
objecttype_t *tempot;
|
|
char tempname[BUFLEN];
|
|
tempot = findot(oid);
|
|
if (!haschoicedata(p, tempot) && canbuild(lf, tempot, tempname, NULL, NULL)) {
|
|
addchoice(p, *ch, tempname, NULL, tempot, NULL);
|
|
(*ch)++;
|
|
}
|
|
}
|
|
|
|
|
|
// returns true if you can't charm them.
|
|
int checkcharm(lifeform_t *caster, lifeform_t *target) {
|
|
char targetname[BUFLEN];
|
|
getlfname(target, targetname);
|
|
// if the target is of a different raceclass, you usually
|
|
// can't charm them.
|
|
if (getraceclass(target) != getraceclass(caster)) {
|
|
int willfail = B_FALSE;
|
|
switch (getraceclass(target)) {
|
|
case RC_DEMON:
|
|
case RC_DRAGON:
|
|
case RC_GOD:
|
|
case RC_SLIME:
|
|
case RC_MAGIC:
|
|
case RC_PLANT:
|
|
willfail = B_TRUE;
|
|
break;
|
|
default: break;
|
|
}
|
|
if (willfail) {
|
|
if (isplayer(caster)) {
|
|
msg("%s%s mind is too alien for you to charm.",targetname,getpossessive(targetname));
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!ischarmable(target)) {
|
|
if (isplayer(caster)) {
|
|
err_nocharm(reason, targetname);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
// ok
|
|
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, int frompot, object_t *fromob) {
|
|
char buf[BUFLEN];
|
|
char castername[BUFLEN];
|
|
int rv = B_FALSE;
|
|
objecttype_t *sp;
|
|
flag_t *casttype = NULL;
|
|
|
|
// sometimes change spellid based on blessed status
|
|
if (blessed == B_CURSED) {
|
|
switch (spellid) {
|
|
case OT_S_LIGHT: spellid = OT_S_DARKNESS;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
sp = findot(spellid);
|
|
|
|
if (caster) {
|
|
getlfname(caster, castername);
|
|
} else {
|
|
strcpy(castername, "something");
|
|
}
|
|
|
|
// default to unseen
|
|
if (seenbyplayer) *seenbyplayer = B_FALSE;
|
|
|
|
// adjust destination/range for wands, since you might inadvertantly
|
|
// target an invalid cell if you don't know what it is yet.
|
|
if (fromob && caster) {
|
|
enum LOFTYPE loft;
|
|
int maxr;
|
|
|
|
// reduce range if required
|
|
maxr = getspellrange(caster, spellid, power, NULL);
|
|
if (targcell && (maxr != UNLIMITED) && (getcelldist(caster->cell, targcell) > maxr)) {
|
|
cell_t *retcell[MAXCANDIDATES];
|
|
int nretcells;
|
|
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcells);
|
|
if (maxr > (nretcells-1)) {
|
|
maxr = nretcells-1;
|
|
}
|
|
targcell = retcell[maxr];
|
|
}
|
|
|
|
// adjust for LOF - we already validated this during castspell()'s call to validatespellcell(),
|
|
// but check again in case, for example, there was an invisible lf in the way which we couldn't
|
|
// see.
|
|
loft = getspellloftype(spellid);
|
|
if (targcell && (loft != LOF_DONTNEED)) {
|
|
cell_t *newtarg;
|
|
haslof(caster->cell, targcell, loft, &newtarg);
|
|
targcell = newtarg;
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
// druids gain power from nearby plants
|
|
if (hasjob(caster, J_DRUID)) {
|
|
cell_t *c;
|
|
int d;
|
|
float totweight = 0;
|
|
int powerinc = 0;
|
|
object_t *o;
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
c = getcellindir(caster->cell, d);
|
|
if (c) {
|
|
for (o = c->obpile->first ; o ; o = o->next){
|
|
if (o->type->obclass->id == OC_FLORA) totweight += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
for (o = caster->cell->obpile->first ; o ; o = o->next){
|
|
if (o->type->obclass->id == OC_FLORA) totweight += getobmass(o);
|
|
}
|
|
for (o = caster->pack->first ; o ; o = o->next){
|
|
if (o->type->obclass->id == OC_FLORA) totweight += getobmass(o);
|
|
}
|
|
powerinc = totweight / 50;
|
|
if (powerinc > 0) {
|
|
power += powerinc;
|
|
}
|
|
}
|
|
|
|
// worshipers of ekrub gain power from piety
|
|
if (isplayer(caster) && godprayedto(R_GODNATURE) &&
|
|
(hasflagval(sp->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL))) {
|
|
enum PIETYLEV pl;
|
|
pl = getpietylev(R_GODNATURE, NULL, NULL);
|
|
if (pl >= PL_PLEASED) {
|
|
power += pl;
|
|
limit(&power, NA, 10);
|
|
}
|
|
}
|
|
|
|
// special: spit attacks need an animation
|
|
casttype = lfhasflag(caster, F_CASTTYPE);
|
|
if (casttype && (casttype->val[1] == CT_EYESPIT)) {
|
|
enum COLOUR col;
|
|
if (casttype->val[2] == NA) {
|
|
col = C_GREEN;
|
|
} else {
|
|
col = casttype->val[2];
|
|
}
|
|
anim(caster->cell, targcell, '}', col);
|
|
}
|
|
}
|
|
|
|
|
|
// special case: summoning spell on a pentagram
|
|
if (caster && hasob(caster->cell->obpile, OT_PENTAGRAM) && hasflagval(sp->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL) &&
|
|
(sp->id != OT_S_SUMMONDEMON)) {
|
|
if (haslos(player, caster->cell)) {
|
|
msg("The pentagram pulses red.");
|
|
}
|
|
if (summonlfs(caster, caster->cell, R_NONE, RC_DEMON, SZ_ANY, AL_NONE, 1, PERMENANT, B_MAYBE)) {
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
msg("An other-worldly demon appears!");
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// switch based on spell effects...
|
|
if (spellid == OT_S_ABSOLUTEZERO) {
|
|
cell_t *retcell[MAXCANDIDATES];
|
|
int nretcells,i;
|
|
if (isplayer(caster)) {
|
|
msg("You unleash a freezing blast!");
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s unleashes a freezing blast!", castername, getpossessive(castername));
|
|
}
|
|
// freeze lfs, obs in most cells
|
|
getradiuscells(caster->cell, 7, DT_ORTH, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 90);
|
|
for (i = 0; i < nretcells; i++) {
|
|
object_t *o, *nexto;
|
|
if (retcell[i]->lf) {
|
|
for (o = retcell[i]->lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
changemat(o, MT_ICE);
|
|
}
|
|
freezelf(retcell[i]->lf, caster, PERMENANT);
|
|
}
|
|
for (o = retcell[i]->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
changemat(o, MT_ICE);
|
|
}
|
|
o = addobfast(retcell[i]->obpile, OT_ICESHEET);
|
|
if (o) killflagsofid(o->flags, F_OBHPDRAIN);
|
|
}
|
|
|
|
} else 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, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
removedeadobs(caster->pack);
|
|
|
|
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, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
removedeadobs(targcell->obpile);
|
|
|
|
|
|
if (targcell->lf && !spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
// 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, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
removedeadobs(targcell->lf->pack);
|
|
}
|
|
}
|
|
|
|
if (totalmass > 0) {
|
|
float max;
|
|
int mptoheal;
|
|
// heal 1 mp per kilo
|
|
howmuch = floor(totalmass);
|
|
|
|
// maximum based on power
|
|
max = 40 + (power*10);
|
|
|
|
if (howmuch > max) howmuch = max;
|
|
|
|
mptoheal = getmaxmp(caster) - caster->mp;
|
|
|
|
gainmp(caster, howmuch);
|
|
|
|
// any left? heal hp with half the remaining amt now.
|
|
howmuch -= mptoheal;
|
|
howmuch /= 2;
|
|
if (howmuch > 0) {
|
|
gainhp(caster, howmuch);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_ABSORBWOOD) {
|
|
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 wood!");
|
|
} else if (cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_FALSE;
|
|
msg("%s draws power from nearby wood!",castername);
|
|
}
|
|
totalmass = 0;
|
|
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
targcell = caster->los[i];
|
|
// destroy wood items on the ground
|
|
for (o = targcell->obpile->first ; o ; o = o->next) {
|
|
if (o->material->id == MT_WOOD) {
|
|
takedamage(o, 9999, DT_DIRECT, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
removedeadobs(targcell->obpile);
|
|
|
|
|
|
// carried wood?
|
|
if (targcell->lf && (targcell->lf != caster) &&
|
|
!spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
// destroy only WORN wood objects, not CARRIED ones
|
|
for (o = targcell->lf->pack->first ; o ; o = o->next) {
|
|
if (isequipped(o) && (o->material->id == MT_WOOD)) {
|
|
takedamage(o, 9999, DT_DIRECT, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
removedeadobs(targcell->lf->pack);
|
|
}
|
|
}
|
|
|
|
if (totalmass > 0) {
|
|
float max;
|
|
int mptoheal;
|
|
// heal 1 mp per kilo
|
|
howmuch = floor(totalmass);
|
|
|
|
// maximum based on power
|
|
max = 40 + (power*10);
|
|
|
|
if (howmuch > max) howmuch = max;
|
|
|
|
mptoheal = getmaxmp(caster) - caster->mp;
|
|
|
|
gainmp(caster, howmuch);
|
|
|
|
// any left? heal hp with half the remaining amt now.
|
|
howmuch -= mptoheal;
|
|
howmuch /= 2;
|
|
if (howmuch > 0) {
|
|
gainhp(caster, howmuch);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_AIRBLAST) {
|
|
int dir;
|
|
object_t *o,*nexto;
|
|
cell_t *firstobcell = NULL;
|
|
cell_t *nextc;
|
|
|
|
if (targcell) {
|
|
dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS);
|
|
} else {
|
|
dir = askdir("Airblast in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if (dir == D_NONE) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
// knock them back as well
|
|
targcell = nextc;
|
|
target = nextc->lf;
|
|
knockback(target, dir, power, caster, 0, B_DOANNOUNCE, B_DODAM);
|
|
real_fall_from_air(target, SZ_HUMAN + (power/4));
|
|
} 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*5, caster);
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_ALARM) {
|
|
if (isplayer(caster)) {
|
|
msg("You create a alarm field around yourself.");
|
|
}
|
|
} else if (spellid == OT_S_ALCHEMY) {
|
|
char obname[BUFLEN];
|
|
char obtocreate[BUFLEN];
|
|
obpile_t *op;
|
|
object_t *newob = NULL;
|
|
int obamt;
|
|
float obweight;
|
|
float convrate, powerpct;
|
|
float obvalue;
|
|
// needs:
|
|
// object = which object to convert
|
|
if (!targob && isplayer(caster)) {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_STONE,
|
|
CC_NONE);
|
|
|
|
targob = doaskobject(caster->pack, "Convert which object to gold", NULL, NULL, B_FALSE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, &cs, B_FALSE);
|
|
}
|
|
if (!targob) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
op = targob->pile;
|
|
obamt = targob->amt;
|
|
getobname(targob, obname, targob->amt);
|
|
obweight = getobmass(targob);
|
|
// get conversion rate
|
|
powerpct = ((double)power / getspellmaxpower(spellid)) ;
|
|
convrate = (powerpct * (4.0 - 0.5)) + 0.5;
|
|
// determine value of gold
|
|
obvalue = convrate * obweight;
|
|
limitf(&obvalue, 0, NA);
|
|
// original object vanishes.
|
|
killob(targob);
|
|
// create new object
|
|
if (obvalue > 0) {
|
|
if (op->owner) {
|
|
newob = hasob(op, OT_GOLD);
|
|
if (newob) {
|
|
newob->amt += (int)obvalue;
|
|
} else {
|
|
sprintf(obtocreate, "%d gold dollars", (int)obvalue);
|
|
newob = addob(op, obtocreate);
|
|
}
|
|
} else {
|
|
sprintf(obtocreate, "%d gold dollars", (int)obvalue);
|
|
newob = addob(op, obtocreate);
|
|
}
|
|
}
|
|
|
|
// announce
|
|
if (isplayer(op->owner) || (op->where && haslos(player, op->where)) ) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
if (newob) {
|
|
char newobname[BUFLEN];
|
|
msg("%s change%s into %d gold dollar%s!", obname, (obamt == 1) ? "s" : "", (int)obvalue, ((int)obvalue == 1) ? "" : "s");
|
|
if (newob->pile->owner && isplayer(newob->pile->owner)) {
|
|
getobname(newob, newobname, newob->amt);
|
|
msgnocap("%c - %s", newob->letter, newobname);
|
|
}
|
|
} else {
|
|
msg("%s vanishes!", obname);
|
|
if (isplayer(caster)) angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_ANIMATEDEAD) {
|
|
int i,nposs = 0;
|
|
object_t *o,*nexto;
|
|
int donesomething = B_FALSE;
|
|
object_t **poss;
|
|
if (!target) {
|
|
target = caster;
|
|
}
|
|
poss = malloc(target->nlos * sizeof(object_t *));
|
|
// animate corpses within lof of caster
|
|
for (i = 0; i < target->nlos; i++) {
|
|
targcell = target->los[i];
|
|
//if (targcell != target->cell) {
|
|
// DO include the cell the target is standing on,
|
|
// since makezombie() will look for adjacent cells
|
|
// to place the zombie.
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->type->id == OT_CORPSE) {
|
|
poss[nposs++] = o;
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
|
|
for (i = 0 ; (i < power) && (nposs > 0); i++) {
|
|
lifeform_t *newlf;
|
|
int sel,n;
|
|
sel = rnd(0,nposs-1);
|
|
o = poss[sel];
|
|
newlf = makezombie(o, power, "rises from the dead", target);
|
|
if (newlf) {
|
|
donesomething = B_TRUE;
|
|
}
|
|
// remove from list
|
|
for (n = sel; n < nposs - 1; n++) {
|
|
poss[n] = poss[n+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
free(poss);
|
|
|
|
if (donesomething) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} 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_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
} else if (o->amt != 1) {
|
|
if (isplayer(caster)) {
|
|
msg("Your %s wobbles a little.", obname);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
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);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_ANIMATESTATUE) {
|
|
object_t *o;
|
|
target = targcell->lf;
|
|
if (target) {
|
|
if (lfhasflag(target, F_BEINGSTONED)) {
|
|
killflagsofid(target->flags, F_BEINGSTONED);
|
|
if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// otherwise find first statue
|
|
o = hasob(targcell->obpile, OT_STATUE);
|
|
if (o) {
|
|
lifeform_t *lf = NULL;
|
|
char obname[BUFLEN];
|
|
flag_t *f;
|
|
// what is it a statue of?
|
|
f = hasflag(o->flags, F_CORPSEOF);
|
|
if (!f) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
// add it!
|
|
getobname(o, obname, 1);
|
|
removeob(o, ALL);
|
|
lf = addmonster(targcell, f->val[0], NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL);
|
|
setlfmaterial(lf, MT_STONE, B_FALSE);
|
|
if (cansee(player, lf)) {
|
|
msg("%s comes to life!",obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
if (caster) {
|
|
petify(lf, caster);
|
|
addflag(lf->flags, F_SUMMONEDBY, caster->id, rnd(50,100), NA, NULL);
|
|
}
|
|
// no corpse after death (so you can't keep reanimating it)
|
|
addflag(lf->flags, F_NOCORPSE, NA, NA, NA, NULL);
|
|
if (isplayer(caster)) {
|
|
angergodmaybe(R_GODFIRE, 50, GA_HERESY);
|
|
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_ANIMATETREE) {
|
|
object_t *o;
|
|
|
|
o = hasob(targcell->obpile, OT_TREE);
|
|
if (o) {
|
|
lifeform_t *lf = NULL;
|
|
char obname[BUFLEN];
|
|
enum RACE rid;
|
|
// determine new race
|
|
switch (rnd(1,6)) {
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
rid = R_TREANTYOUNG;
|
|
break;
|
|
case 4:
|
|
case 5:
|
|
rid = R_TREANT;
|
|
break;
|
|
case 6:
|
|
rid = R_TREANTOLD;
|
|
break;
|
|
}
|
|
getobname(o, obname, 1);
|
|
removeob(o, ALL);
|
|
lf = addmonster(targcell, rid, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL);
|
|
if (cansee(player, lf)) {
|
|
msg("%s comes to life!",obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
if (caster) {
|
|
petify(lf, caster);
|
|
addflag(lf->flags, F_SUMMONEDBY, caster->id, rnd(50,100), NA, NULL);
|
|
}
|
|
if (isplayer(caster)) {
|
|
angergodmaybe(R_GODFIRE, 50, GA_HERESY);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_ANTICIPATE) {
|
|
if (!target) target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
addflag(caster->flags, F_ANTICIPATE, target->id, power, NA, NULL);
|
|
} else if (spellid == OT_S_APPORTATION) {
|
|
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, frompot)) return B_TRUE;
|
|
|
|
if (targcell->obpile->first) {
|
|
// more than object?
|
|
if (targcell->obpile->first->next) {
|
|
// select object from cell...
|
|
targob = askobject(targcell->obpile, "Target which object", NULL, NULL, '\0', NULL, B_FALSE);
|
|
} else {
|
|
targob = targcell->obpile->first;
|
|
}
|
|
if (!targob) {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!failed) {
|
|
char obname[BUFLEN];
|
|
cell_t *obloc;
|
|
obloc = getoblocation(targob);
|
|
getobname(targob, obname, 1);
|
|
|
|
targcell = caster->cell;
|
|
|
|
// not liftable?
|
|
if (hasflag(targob->flags, F_NOPICKUP)) {
|
|
if (isplayer(caster)) {
|
|
nothinghappens();
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// modify by power
|
|
maxweight = 10;
|
|
|
|
if (getobmass(targob) > maxweight) {
|
|
if (haslos(player, obloc)) {
|
|
msg("%s fades out of view slightly, then reappears.",obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (moveob(targob, caster->pack, 1)) {
|
|
if (isplayer(caster)) {
|
|
msg("%s appears in your pack!", obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (haslos(player, obloc)) {
|
|
msg("%s vanishes!", obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_AWARENESS) {
|
|
flag_t *f;
|
|
f = addtempflag(caster->flags, F_AWARENESS, B_TRUE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_BAFFLE) {
|
|
target = targcell->lf;
|
|
|
|
if (lfhasflag(target, F_ASLEEP) || !ischarmable(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
return B_FALSE;
|
|
} else {
|
|
confuse(target, power*4);
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_BARKSKIN) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
if (!target) {
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
}
|
|
|
|
//f = addtempflag(caster->flags, F_MAGICARMOUR, power*2, NA, NA, "skin of bark", FROMSPELL);
|
|
//f->obfrom = spellid;
|
|
|
|
setlfmaterial(target, MT_WOOD, B_TRUE);
|
|
f = addtempflag(caster->flags, F_DTVULN, DT_FIRE, NA, NA, "2d4", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_BLADEBURN) {
|
|
object_t *wep;
|
|
if (!targcell) {
|
|
targcell = caster->cell;
|
|
}
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// does caster have a weapon?
|
|
wep = getweapon(target);
|
|
if (!wep) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
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, NULL, 10 + power*3);
|
|
} else if (spellid == OT_S_BLIGHT) {
|
|
char obname[BUFLEN];
|
|
if (targob) {
|
|
if (isedible(targob)) {
|
|
// just affect one item
|
|
targcell = targob->pile->where;
|
|
target = targob->pile->owner;
|
|
getobname(targob, obname, targob->amt);
|
|
if (target && cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%cA green miasma surrounds %s%s %s.", getlfcol(target, CC_BAD), lfname,
|
|
getpossessive(lfname), obname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (targcell && haslos(player, targcell)) {
|
|
msg("A green miasma surrounds %s.", obname);
|
|
}
|
|
if (!hasflag(targob->flags, F_TAINTED)) {
|
|
addflag(targob->flags, F_TAINTED, B_TRUE, NA, NA, NULL);
|
|
}
|
|
return B_FALSE;
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
obpile_t *op;
|
|
object_t *o,*nexto;
|
|
int ndone = 0;
|
|
if (targcell->lf) {
|
|
op = targcell->lf->pack;
|
|
if (cansee(player, targcell->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
msg("^%cA green miasma surrounds %s.", getlfcol(targcell->lf, CC_BAD), lfname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (isliving(targcell->lf)) {
|
|
int dam;
|
|
char damstr[BUFLEN];
|
|
dam = rnd(1,power);
|
|
if (caster) {
|
|
char cname[BUFLEN];
|
|
real_getlfnamea(caster, cname, NULL, B_SHOWALL, B_REALRACE);
|
|
sprintf(damstr, "%s%s blight spell.", cname, getpossessive(cname));
|
|
} else {
|
|
strcpy(damstr, "a blight spell.");
|
|
}
|
|
if (isplayer(targcell->lf)) {
|
|
msg("^%cA blight courses through your veins!", getlfcol(targcell->lf, CC_BAD));
|
|
} else if (cansee(player, targcell->lf)) {
|
|
char tname[BUFLEN];
|
|
getlfname(targcell->lf, tname);
|
|
msg("%s looks unwell.", tname);
|
|
}
|
|
losehp(targcell->lf, dam, DT_DECAY, caster, damstr);
|
|
}
|
|
} else {
|
|
op = targcell->obpile;
|
|
}
|
|
// taint all objects here
|
|
for (o = op->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (isedible(o)) {
|
|
if (!targcell->lf && haslos(player, targcell)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("A green miasma surrounds %s.", obname);
|
|
}
|
|
if (!hasflag(o->flags, F_TAINTED)) {
|
|
addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL);
|
|
}
|
|
if (++ndone >= power) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_BLINDNESS) {
|
|
int failed = B_FALSE;
|
|
target = targcell->lf;
|
|
|
|
if (isblind(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_BLINK) {
|
|
cell_t *poss[MAXCANDIDATES];
|
|
int nposs = 0,x,y;
|
|
|
|
if (lfhasflag(caster, F_CONTROL) && (power < 6)) {
|
|
power = 6;
|
|
}
|
|
if (power >= 6) {
|
|
// controlled
|
|
// must be within line of sight.
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE;
|
|
if (!targcell || !cellwalkable(caster, targcell, NULL)) {
|
|
char ch;
|
|
ch = askchar("Teleport randomly?", "yn","n", B_TRUE, B_FALSE);
|
|
if (ch != 'y') {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (!targcell) {
|
|
// pick a random location
|
|
// only needs to be in line-of-FIRE, not neccesarily sight.
|
|
for (y = 0 ; y <= caster->cell->map->h; y++) {
|
|
for (x = 0 ; x <= caster->cell->map->w; x++) {
|
|
cell_t *c;
|
|
c = getcellat(caster->cell->map, x, y);
|
|
if (c && haslof(caster->cell, c, LOF_WALLSTOP, NULL)) {
|
|
if (cellwalkable(caster, c, NULL) &&
|
|
!celldangerous(caster, c, B_FALSE, NULL)) {
|
|
poss[nposs++] = c;
|
|
}
|
|
}
|
|
if (nposs >= MAXCANDIDATES) break;
|
|
}
|
|
if (nposs >= MAXCANDIDATES) break;
|
|
}
|
|
|
|
if (!nposs) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
targcell = poss[rnd(0,nposs-1)];
|
|
}
|
|
|
|
teleportto(caster, targcell, B_TRUE);
|
|
} else if (spellid == OT_S_BLINKASS) {
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// find cell behind caster
|
|
targcell = getcellindir(targcell, diropposite(target->facing));
|
|
if (!cellwalkable(caster, targcell, NULL)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
teleportto(caster, targcell, B_TRUE);
|
|
setfacing(caster, target->facing);
|
|
} else if (spellid == OT_S_BLOODBOIL) {
|
|
if (!target) {
|
|
target = targcell->lf;
|
|
}
|
|
if (!target || lfhasflag(target, F_BLOODBOIL)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
addflag(target->flags, F_BLOODBOIL, B_TRUE, NA, NA, NULL);
|
|
} else if (spellid == OT_S_LOWERMETAB) {
|
|
flag_t *f;
|
|
// ie. 2 - 4
|
|
f = addtempflag(caster->flags, F_SLOWMETAB, 2+(power/4), NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
// you move more slowly too.
|
|
if (power < 3) {
|
|
f = addtempflag(caster->flags, F_SLOWMOVE, 10, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (power < 5) {
|
|
f = addtempflag(caster->flags, F_SLOWMOVE, 5, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
}
|
|
} else if (spellid == OT_S_BOOSTCONFIDENCE) {
|
|
flag_t *f;
|
|
if (!target) {
|
|
target = targcell->lf;
|
|
}
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(target)){
|
|
msg("You suddenly feel supremely confident!");
|
|
} else if (cansee(player, target)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
msg("%s looks more confident!", targname);
|
|
}
|
|
killflagsofid(target->flags, F_FLEEONDAM);
|
|
killflagsofid(target->flags, F_FLEEONHPPCT);
|
|
killflagsofid(target->flags, F_FLEEFROM);
|
|
killflagsofid(target->flags, F_TIMID);
|
|
killflagsofid(target->flags, F_ATTACKRANGE);
|
|
addflag(target->flags, F_NOFLEE, B_TRUE, NA, NA, NULL);
|
|
f = lfhasflag(target, F_MORALE); if (f) f->val[0] = 30;
|
|
} else if (spellid == OT_S_BURNINGFEET) {
|
|
char killertext[BUFLEN];
|
|
if (!target) {
|
|
target = targcell->lf;
|
|
}
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (!hasbp(target, BP_FEET)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
sprintf(killertext, "%s%s hotfoot spell", castername, getpossessive(castername));
|
|
addflag(target->flags, F_HOTFEET, 1, DT_FIRE, NA, killertext);
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_BURNINGWAVE) {
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell;
|
|
int i;
|
|
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, B_FALSE) && cansee(player, c->lf)) {
|
|
msg("^%c%s burn%s!",getlfcol(c->lf, CC_BAD), buf,isplayer(c->lf) ? "" : "s");
|
|
}
|
|
real_getlfname(caster, realcname, NULL, B_SHOWALL, B_REALRACE);
|
|
snprintf(damstring, BUFLEN, "%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_CANINETRACKING) {
|
|
int howlong;
|
|
if (hasflag(caster->flags, F_NOSMELL)) {
|
|
if (isplayer(caster)) {
|
|
nothinghappens();
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
howlong = getspellduration(20,30,blessed) + (power*10);
|
|
addtempflag(caster->flags, F_ENHANCESMELL, 4, NA, NA, NULL, howlong);
|
|
}
|
|
} else if ((spellid == OT_S_COLDSNAP) || (spellid == OT_S_HEATWAVE)) {
|
|
object_t *retob[MAXCANDIDATES];
|
|
int nretobs,radius,tempmod;
|
|
char obname[BUFLEN];
|
|
|
|
if (spellid == OT_S_COLDSNAP) {
|
|
sprintf(obname, "unnatural coldness");
|
|
tempmod = -30;
|
|
radius = power+1;
|
|
} else {
|
|
sprintf(obname, "unnatural heat");
|
|
tempmod = 30;
|
|
radius = power-1;
|
|
}
|
|
|
|
addobsinradius(targcell, radius, DT_ORTH, obname, B_FALSE, B_TRUE, caster, retob, NULL, &nretobs);
|
|
|
|
if (nretobs) {
|
|
int i,donemsg = B_FALSE;
|
|
for (i = 0; i < nretobs; i++) {
|
|
flag_t *f;
|
|
object_t *o;
|
|
if (!donemsg && haslos(player, targcell)) {
|
|
if (spellid == OT_S_COLDSNAP) {
|
|
msg("A large cloud of vapour appears.");
|
|
} else {
|
|
msg("A shimmering haze of heat appears.");
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
donemsg = B_TRUE;
|
|
}
|
|
o = retob[i];
|
|
|
|
// undo temperature effects from adding the ob
|
|
affect_temperature(o, B_REMOVE);
|
|
// replace the TEMPMOD flag.
|
|
killflagsofid(o->flags, F_TEMPMOD);
|
|
addflag(o->flags, F_TEMPMOD, tempmod, NA, NA, NULL);
|
|
// now re-apply temperature effects
|
|
affect_temperature(o, B_ADD);
|
|
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[1] = 40;
|
|
f->val[0] = f->val[1];
|
|
}
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_CONFISCATE) {
|
|
char ch = 'a';
|
|
char obname[BUFLEN];
|
|
if (targob) {
|
|
target = targob->pile->owner;
|
|
} else {
|
|
object_t *o;
|
|
// ask for a target cell
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE;
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// which object to take?
|
|
initprompt(&prompt, "Confiscate which object?");
|
|
addchoice(&prompt, '-', "(Cancel)", NULL, NULL, NULL);
|
|
for (o = target->pack->first ; o ; o = o->next) {
|
|
if (o->type->obclass->id == OC_GODSTONE) continue;
|
|
getobname(o, obname, o->amt);
|
|
addchoice(&prompt, ch, obname, NULL, o, NULL);
|
|
if (ch == 'z') {
|
|
ch = 'A';
|
|
} else {
|
|
ch++;
|
|
}
|
|
}
|
|
if (isplayer(caster) && (power > 1)) {
|
|
// select one
|
|
getchoice(&prompt);
|
|
targob = (object_t *)prompt.result;
|
|
} else {
|
|
// random one
|
|
targob = (object_t *) prompt.choice[rnd(0,prompt.nchoices-1)].data;
|
|
}
|
|
}
|
|
if (targob) {
|
|
if (target) {
|
|
if (isplayer(target)) {
|
|
getobname(targob, obname, targob->amt);
|
|
msg("^%cYour %s vanish%s!", getlfcol(target, CC_VBAD), noprefix(obname), OB1(targob,"es",""));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (isplayer(caster)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
getobname(targob, obname, targob->amt);
|
|
msg("%s%s %s appear%s in your pack!", targname, getpossessive(targname),
|
|
noprefix(obname), OB1(targob,"es",""));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
getobname(targob, obname, targob->amt);
|
|
msg("^%c%s%s %s vanish%s!", getlfcol(target, CC_VBAD), targname, getpossessive(targname), noprefix(obname),
|
|
OB1(targob,"es",""));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
moveob(targob, caster->pack, ALL);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_CALLLIGHTNING) {
|
|
int failed = B_FALSE;
|
|
target = targcell->lf;
|
|
if (target) {
|
|
char targname[BUFLEN];
|
|
|
|
getlfname(target, targname);
|
|
|
|
if (haslos(player, targcell)) {
|
|
animsky(targcell, '}', C_WHITE);
|
|
msg("^%c%s %s struck by a bolt of lightning!",getlfcol(target, CC_BAD), targname,is(target));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// outdoors?
|
|
if (caster && (getraceclass(caster) == RC_GOD)) {
|
|
losehp(target, rolldie(5,6), DT_ELECTRIC, caster, "a heavenly bolt of lightning");
|
|
} else if (target->cell->map->habitat->id == H_FOREST) {
|
|
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);
|
|
return B_TRUE;
|
|
}
|
|
} 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, frompot)) return B_TRUE;
|
|
|
|
if (targcell->obpile->first) {
|
|
// select object from cell...
|
|
targob = askobject(targcell->obpile, "Target which object", NULL, NULL, '\0', NULL, B_FALSE);
|
|
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 (getobmass(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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_CALMANIMALS) {
|
|
int i,ncalmed = 0;
|
|
int powerleft;
|
|
int donesomething = B_FALSE;
|
|
cell_t *c;
|
|
//powerleft = rolldie(power+1, 4);
|
|
powerleft = power+2;
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
c = caster->los[i];
|
|
if (c->lf && (c->lf != caster) && (c->lf->race->raceclass->id == RC_ANIMAL) && lfhasflag(c->lf, F_HOSTILE)) {
|
|
if (gettr(c->lf) <= powerleft) {
|
|
powerleft -= gettr(c->lf);
|
|
makepeaceful(c->lf, caster);
|
|
|
|
ncalmed++;
|
|
|
|
// druids get 50% of the monster's XP value for calming it.
|
|
// everyone else gets 25%.
|
|
if (getallegiance(caster) == AL_FRIENDLY) {
|
|
if (hasjob(caster, J_DRUID)) {
|
|
awardxpfor(c->lf, 50);
|
|
} else {
|
|
awardxpfor(c->lf, 25);
|
|
}
|
|
// prevent attacking it and getting xp again.
|
|
addflag(c->lf->flags, F_XPVAL, 0, NA, NA, NULL);
|
|
}
|
|
|
|
if (cansee(player, c->lf)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
donesomething++;
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("%s is not affected.", lfname);
|
|
}
|
|
}
|
|
}
|
|
if (powerleft <= 0) break;
|
|
}
|
|
|
|
if (ncalmed && isplayer(caster)) {
|
|
pleasegodmaybe(R_GODMERCY, ncalmed);
|
|
}
|
|
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} 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*2]; // 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(caster, spellid, power, NULL);
|
|
|
|
if (caster) {
|
|
// 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");
|
|
}
|
|
} else {
|
|
if (haslos(player, targcell)) {
|
|
msg("A bolt of electricity arcs out of the air!");
|
|
}
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
arccell[0] = targcell;
|
|
narccells = 1;
|
|
} else {
|
|
narccells = 0;
|
|
if (haslos(player, targcell)) {
|
|
msg("A bolt of electricity fizzles out.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
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(3,nsides), DT_ELECTRIC, caster, "an electricity bolt");
|
|
hitcell[nhitcells++] = arccell[i];
|
|
assert(nhitcells < MAXRETCELLS*2);
|
|
|
|
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;
|
|
assert(narccells2 < MAXRETCELLS);
|
|
animline(arccell[i], c, B_FALSE, '/', '\\', C_WHITE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// replace arccells
|
|
for (i = 0; i < narccells2; i++) {
|
|
arccell[i] = arccell2[i];
|
|
}
|
|
narccells = narccells2;
|
|
assert(narccells < MAXRETCELLS);
|
|
|
|
if (narccells && stillseen) {
|
|
msg("The electricity arcs!");
|
|
}
|
|
} // end while narccells
|
|
} else if (spellid == OT_S_CLEANSINGFIRE) {
|
|
int i;
|
|
int pct = 0;
|
|
int ndone = 0;
|
|
object_t *o,*nexto;
|
|
if (!target) target = caster;
|
|
// find all fires within los
|
|
for (i = 0; i < target->nlos; i++) {
|
|
for (o = target->los[i]->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (ndone >= power) break;
|
|
if (o->material->id == MT_FIRE) {
|
|
char obname[BUFLEN];
|
|
int oamt;
|
|
getobname(o, obname, o->amt);
|
|
oamt = o->amt;
|
|
killob(o);
|
|
pct += 30;
|
|
if (haslos(player, target->los[i])) {
|
|
msg("%s %s out!", obname, (oamt == 1) ? "goes" : "go");
|
|
}
|
|
ndone++;
|
|
continue;
|
|
}
|
|
if (killflagsofid(o->flags, F_ONFIRE)) {
|
|
pct += 20;
|
|
ndone++;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
limit(&pct, 0, 100);
|
|
if (pct) {
|
|
if (isplayer(target)) {
|
|
msg("^%cYour wounds are healed!", getlfcol(target, CC_GOOD));
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%c%s%s wounds are healed!", getlfcol(target, CC_GOOD), lfname, getpossessive(lfname));
|
|
}
|
|
gainhp(target, pctof(pct, target->maxhp));
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_CLONE) {
|
|
// duplicate the caster
|
|
targcell = getrandomadjcell(caster->cell, &ccwalkable, B_NOEXPAND);
|
|
if (!targcell) {
|
|
if (isplayer(caster)) fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(caster) || haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
clonelf(caster, targcell);
|
|
} else if (spellid == OT_S_CLOUDKILL) {
|
|
int radius;
|
|
|
|
if (targcell->type->solid) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
radius = (power/3);
|
|
|
|
addobburst(targcell, radius, DT_COMPASS, "puff 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];
|
|
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
if (checkcharm(caster, target)) return B_FALSE;
|
|
|
|
if ((getallegiance(target) == AL_PEACEFUL) || ispetof(target, caster)) {
|
|
if (isplayer(caster)) {
|
|
msg("%s is already allied with you!",targetname);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, 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;
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
howlong = 2+(power/5); // ie. 2-4
|
|
} else {
|
|
howlong = getspellduration(2,5,blessed) + power;
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong);
|
|
} else {
|
|
// becaomes allied to caster
|
|
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];
|
|
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
// only animals
|
|
if (getraceclass(target) != RC_ANIMAL) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (checkcharm(caster, target)) return B_FALSE;
|
|
|
|
if (getallegiance(caster) == AL_PEACEFUL) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (ispetof(target, caster)) {
|
|
if (isplayer(caster)) {
|
|
msg("%s is already allied with you!",targetname);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
// no saving throw, just depends on hit dice.
|
|
// too powerful?
|
|
if (gettr(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_CHIBOLT) {
|
|
char lfname[BUFLEN];
|
|
char numbuf[BUFLEN];
|
|
|
|
numtotext(power, numbuf);
|
|
// animation
|
|
anim(caster->cell, targcell, '}', C_LIGHTGREEN);
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
msg("%s fire%s a bolt of chi energy.",castername,isplayer(caster) ? "" : "s");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
target = haslf(targcell);
|
|
if (target) {
|
|
int dam;
|
|
char attackname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
dam = rnd(1,4);
|
|
sprintf(attackname, "a bolt of chi energy");
|
|
// target takes magical damage
|
|
// always hit
|
|
if (check_for_block(caster, target, dam, DT_EXPLOSIVE, 999, attackname, B_RANGED)) {
|
|
} else {
|
|
if (cansee(player, target)) {
|
|
msg("^%cA bolt of chi energy hits %s.",getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
losehp(target, dam, DT_MAGIC, caster, attackname);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_CHISTRIKE) {
|
|
flag_t *f;
|
|
if (isplayer(caster)) {
|
|
msg("An aura of chi energy forms around your %s!",getbodypartname(caster, BP_HANDS));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
msg("An aura of chi energy forms around %s%s %s!",castername, getpossessive(castername),
|
|
getbodypartname(caster, BP_HANDS));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
f = addtempflag(caster->flags, F_AWARENESS, DT_EXPLOSIVE, NA, NA, "1d4", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_CHILL) {
|
|
char lfname[BUFLEN];
|
|
int exposedlimbs,dam;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, lfname);
|
|
|
|
// how many body parts are impacted?
|
|
exposedlimbs = getexposedlimbs(target);
|
|
|
|
dam = rnd(1,exposedlimbs/2);
|
|
|
|
if (isplayer(target)) {
|
|
msg("^%cThe air around you feels icy cold!", getlfcol(target, CC_BAD));
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
if (isimmuneto(target->flags, DT_COLD, B_FALSE)) {
|
|
msg("You feel mildly chilly.");
|
|
} else {
|
|
msg("^%cYou feel very cold!", getlfcol(target, CC_BAD));
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
if (isimmuneto(target->flags, DT_COLD, B_FALSE)) {
|
|
msg("%s doesn't seem to mind the cold.", lfname);
|
|
} else {
|
|
msg("^%c%s looks very cold!", getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// target takes magical damage
|
|
// always hit
|
|
if (!isimmuneto(target->flags, DT_COLD, B_FALSE)) {
|
|
// partially avoidable
|
|
if (check_for_block(caster, target, dam, DT_COLD, 999, "the icy air", B_RANGED)) {
|
|
dam /= 2;
|
|
}
|
|
losehp(target, dam, DT_COLD, caster, "a chill spell");
|
|
}
|
|
} else if (spellid == OT_S_COLDBURST) {
|
|
int range = 1;
|
|
int x,y;
|
|
char buf[BUFLEN];
|
|
|
|
range = 1 + (power / 5);
|
|
|
|
// announce
|
|
sprintf(buf, "%s emit%s a blast of icy cold!",castername,isplayer(caster) ? "" : "s");
|
|
animradial(caster->cell, range, '}', C_GREY, DT_ORTH, buf, "Something emits a blast of icy cold!");
|
|
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
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];
|
|
int dam;
|
|
// automatic hit
|
|
getlfname(targcell->lf, lfname);
|
|
dam = rolldie(1,8)+3;
|
|
// partially avoidable
|
|
if (check_for_block(caster, target, dam, DT_COLD, 999, "a burst of coldness", B_RANGED)) {
|
|
dam /= 2;
|
|
} else {
|
|
if (haslos(player, targcell)) {
|
|
msg("^%c%s %s chilled!",getlfcol(targcell->lf, CC_BAD),
|
|
lfname,is(targcell->lf));
|
|
}
|
|
}
|
|
losehp(targcell->lf, dam, DT_COLD, caster, "a burst of coldness");
|
|
}
|
|
} else {
|
|
// noone there, hit objects.
|
|
damageallobs(NULL, targcell->obpile, 0, DT_COLD, caster);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_COLDRAY) {
|
|
char lfname[BUFLEN];
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell,i;
|
|
// animation
|
|
anim(caster->cell, targcell, '}', C_GREY);
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
msg("%s shoot%s a blast of ice-cold air.",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, 90 + (power*10), 0)) {
|
|
// miss
|
|
if (cansee(player, target)) {
|
|
msg("A blast of icy air misses %s.",lfname);
|
|
}
|
|
} else {
|
|
int dam;
|
|
// hit
|
|
dam = roll("3d6");
|
|
if (power > 1) { // overcast for dragons
|
|
dam += (power-1);
|
|
}
|
|
if (check_for_block(caster, target, dam, DT_COLD, 999, "a blast of icy air", B_RANGED)) {
|
|
dam /= 2;
|
|
} else {
|
|
if (cansee(player, target)) {
|
|
msg("^%cA blast of icy air assails %s.",getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
}
|
|
losehp(target, dam, DT_COLD, caster, "a blast of ice-cold air");
|
|
// ray stops here.
|
|
break;
|
|
}
|
|
} else {
|
|
damageallobs(NULL, retcell[i]->obpile, 0, DT_COLD, caster);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_COMMANDUNDEAD) {
|
|
// mosnters won't cast this.
|
|
if (!isplayer(caster)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
target = targcell->lf;
|
|
if (!target || !isundead(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// saving throw
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
// they get angry!
|
|
if (!isplayer(target) && cansee(target, caster)) {
|
|
fightback(target, caster);
|
|
}
|
|
} else {
|
|
flag_t *f;
|
|
if (isplayer(caster) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
// it worked. temporarily make them a pet so that you
|
|
// can command them.
|
|
f = addflag(target->flags, F_PETOF, caster->id, NA, NA, NULL);
|
|
docomms(target);
|
|
killflag(f);
|
|
|
|
}
|
|
} else if (spellid == OT_S_CREATEFOOD) {
|
|
objecttype_t *ot;
|
|
object_t *o;
|
|
char obname[BUFLEN];
|
|
int i,amt = 1;
|
|
if (!targcell || targcell->type->solid) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
for (i = 0; i < power; i++) {
|
|
int pickagain = B_TRUE;
|
|
flag_t *f;
|
|
while (pickagain) {
|
|
pickagain = B_FALSE;
|
|
ot = getrandomobofclass(OC_FOOD, NA, NA, NA, NULL, NULL);
|
|
if (ot) {
|
|
if (hasflag(ot->flags, F_VENOMSAC)) {
|
|
pickagain = B_TRUE;
|
|
continue;
|
|
}
|
|
f = hasflag(ot->flags, F_EDIBLE);
|
|
if (!f || (f->val[1] <= 5)) {
|
|
pickagain = B_TRUE;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
o = addobfast(targcell->obpile, ot->id);
|
|
if (i == 0) {
|
|
getobname(o, obname, o->amt);
|
|
amt = o->amt;
|
|
}
|
|
}
|
|
if (haslos(player, targcell)) {
|
|
if (power >= 8) {
|
|
msg("A grand feast appears!");
|
|
} else if (power >= 5) {
|
|
msg("A hearty meal appears!");
|
|
} else if (power >= 2) {
|
|
msg("Some light refreshments appear!");
|
|
} else { // ie. power == 1
|
|
msg("%s appear%s!", obname, (amt == 1) ? "s" : "");
|
|
}
|
|
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (isplayer(caster)) {
|
|
angergodmaybe(R_GODFIRE, 50, GA_HERESY);
|
|
}
|
|
} else if (spellid == OT_S_CREATEMONSTER) {
|
|
lifeform_t *newlf;
|
|
race_t *r = NULL;
|
|
|
|
if (!targcell) {
|
|
if ((power >= 5) && isplayer(caster)) {
|
|
// control location
|
|
if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power, frompot)) return B_TRUE;
|
|
// make sure it's empty
|
|
if (!cellwalkable(NULL, targcell, NULL)) {
|
|
targcell = NULL;
|
|
}
|
|
} else {
|
|
// get random adjacent cell
|
|
targcell = getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND);
|
|
}
|
|
}
|
|
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// determine type of mosnter
|
|
if (getforcedspellrace(caster, spellid, buf, NULL)) {
|
|
} else if ((power >= 7) && isplayer(caster)) {
|
|
// ask what kind of monster
|
|
askstring("Create what kind of monster", '?', buf, BUFLEN, NULL);
|
|
} else {
|
|
r = getreallyrandomrace(RC_ANY, NULL);
|
|
snprintf(buf, BUFLEN, "%s", r->name);
|
|
}
|
|
|
|
// add the monster
|
|
newlf = summonmonster(caster, targcell, R_SPECIFIED, buf, PERMENANT, B_FALSE);
|
|
if (newlf) {
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
rv = B_FALSE;
|
|
} else {
|
|
// didn't work for some reason
|
|
if (isplayer(caster)) {
|
|
nothinghappens();
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_CLEARLEVEL) {
|
|
int x,y;
|
|
cell_t *c;
|
|
for (y = 0; y < caster->cell->map->h ; y++) {
|
|
for (x = 0; x < caster->cell->map->w ; x++) {
|
|
c = getcellat(caster->cell->map, x, y);
|
|
if (c) {
|
|
if (c->lf && isplayer(c->lf)) {
|
|
} else {
|
|
clearcell(c);
|
|
setcelltype(c, c->map->habitat->solidcelltype);
|
|
c->locked = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
needredraw = B_TRUE;
|
|
} else if (spellid == OT_S_CREATEVAULT) {
|
|
vault_t *v;
|
|
int vx,vy;
|
|
int vw,vh;
|
|
int newroomid;
|
|
// ask for a vaulttype
|
|
v = askvault("Create which vault?");
|
|
|
|
newroomid = caster->cell->map->nrooms;
|
|
if (createvault(caster->cell->map, newroomid, v, &vw, &vh, &vx, &vy)) {
|
|
msg("Couldn't create a vault.");
|
|
} else {
|
|
char ch;
|
|
cell_t *c;
|
|
|
|
// link the new vault to the rest of the map
|
|
//c = getcellat(caster->cell->map, vx, vy);
|
|
linkexits(caster->cell->map, newroomid);
|
|
|
|
msg("BAM! A vault has appeared nearby."); more();
|
|
needredraw = B_TRUE;
|
|
ch = askchar("Teleport to the new vault", "yn","y", B_TRUE, B_FALSE);
|
|
if (ch == 'y') {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_WALKABLEFOR, B_TRUE, caster->id,
|
|
CC_HASROOMID, B_TRUE, caster->cell->map->nrooms-1,
|
|
CC_NONE);
|
|
//c = getrandomroomcell(caster->cell->map, caster->cell->map->nrooms-1, WE_WALKABLE);
|
|
c = getcell_cond(caster->cell->map, &cs);
|
|
if (!c) {
|
|
msg("Oops, couldn't find a free space.");
|
|
return B_FALSE;
|
|
}
|
|
teleportto(caster, c, B_TRUE);
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
} else if (spellid == OT_S_CRYSTALARM) {
|
|
object_t *o;
|
|
enum BODYPART bp[4];
|
|
int nbp, donesomething = B_FALSE,i;
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
nbp = (power/3)+1;
|
|
bp[0] = BP_BODY;
|
|
bp[1] = BP_HEAD;
|
|
bp[2] = BP_HANDS;
|
|
bp[3] = BP_FEET;
|
|
for (i = 0; (i < 4) && (i < nbp); i++) {
|
|
if (hasbp(target, bp[i]) && !getequippedob(target->pack, bp[i])) {
|
|
switch (bp[i]) {
|
|
case BP_BODY: default:
|
|
o = addob(target->pack, "ice crystal armour"); break;
|
|
case BP_HEAD:
|
|
o = addob(target->pack, "ice crystal helmet"); break;
|
|
case BP_HANDS:
|
|
o = addob(target->pack, "ice crystal gauntlets"); break;
|
|
case BP_FEET:
|
|
o = addob(target->pack, "ice crystal boots"); break;
|
|
}
|
|
if (o) {
|
|
if (canwear(target, o, BP_NONE)) {
|
|
char obname[BUFLEN];
|
|
flag_t *f;
|
|
// announce
|
|
getobname(o, obname, 1);
|
|
if (isplayer(target)) {
|
|
msg("^%c%s forms %s your %s!", getlfcol(target, CC_VGOOD), obname, getbodypartequipname(bp[i]), getbodypartname(target, bp[i]));
|
|
} else if (cansee(player, target)) {
|
|
msg("^%c%s forms %s %s%s %s!", getlfcol(target, CC_VGOOD),obname, getbodypartequipname(bp[i]),
|
|
castername, getpossessive(castername), getbodypartname(target, bp[i]));
|
|
}
|
|
// don't use "wear" because we don't want it being announced.
|
|
addflag(o->flags, F_EQUIPPED, bp[i], -1, -1, NULL);
|
|
// set its values
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] = power*5;
|
|
f->val[1] = power*5;
|
|
}
|
|
addflag(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL);
|
|
donesomething = B_TRUE;
|
|
} else {
|
|
killob(o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_TRUE;
|
|
}
|
|
|
|
} else if (spellid == OT_S_CRYSTALSHIELD) {
|
|
object_t *o;
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
o = getequippedob(target->pack, BP_SECWEAPON);
|
|
if (o) {
|
|
if (isplayer(caster)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("Your %s shimmers like a crystal for a moment.", noprefix(obname));
|
|
}
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_TRUE;
|
|
}
|
|
o = addob(target->pack, "ice crystal shield");
|
|
if (o) {
|
|
if (canwear(target, o, BP_SECWEAPON)) {
|
|
flag_t *f;
|
|
enum LFSIZE sz;
|
|
if (power <= 2) {
|
|
sz = SZ_SMALL;
|
|
} else if (power <= 4) {
|
|
sz = SZ_MEDIUM;
|
|
} else {
|
|
sz = SZ_LARGE;
|
|
}
|
|
// announce
|
|
if (isplayer(target)) {
|
|
msg("^%cA %s shield of shimmering ice forms in your hand!", getlfcol(target, CC_GOOD),
|
|
getsizetext(sz));
|
|
} else if (cansee(player, target)) {
|
|
msg("^%cA %s shield of shimmering ice forms in %s%s hand!",getlfcol(target, CC_GOOD),
|
|
getsizetext(sz), castername,
|
|
getpossessive(castername));
|
|
}
|
|
// don't use "wear" because we don't want it being announced.
|
|
addflag(o->flags, F_EQUIPPED, BP_SECWEAPON, -1, -1, NULL);
|
|
// set its values
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] = power*5;
|
|
f->val[1] = power*5;
|
|
}
|
|
addflag(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL);
|
|
} else {
|
|
killob(o);
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_CUREPOISON) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags,i,donesomething = B_FALSE;
|
|
if (!target) {
|
|
target = targcell->lf;
|
|
}
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getflags(target->flags, retflag, &nretflags, F_INCUBATING, F_POISONED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
poisontype_t *pt;
|
|
pt = findpoisontype(retflag[i]->val[0]);
|
|
if (pt->severity == PS_CURSE) {
|
|
} else {
|
|
killflag(retflag[i]);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (donesomething && cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_CURSE) {
|
|
int ndone = 0,i;
|
|
object_t *o,*poss[MAXPILEOBS];
|
|
int nposs = 0;
|
|
if (!target) target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
for (o = target->pack->first ; o ; o = o->next) {
|
|
if (isequipped(o) && !iscursed(o) && !hasflag(o->flags, F_NOBLESS)) {
|
|
poss[nposs++] = o;
|
|
}
|
|
}
|
|
|
|
for (i = 0 ; nposs && (i < power); i++) {
|
|
int n;
|
|
// pick a random one
|
|
o = poss[rnd(0,nposs-1)];
|
|
curseob(o);
|
|
if (o->blessed == B_CURSED) {
|
|
// also apply a penalty
|
|
if (isweapon(o) || isarmour(o)) {
|
|
modbonus(o, -1);
|
|
}
|
|
}
|
|
// remove this from the list
|
|
for (n = i; n < nposs-1; n++) {
|
|
poss[n] = poss[n+1];
|
|
}
|
|
nposs--;
|
|
ndone++;
|
|
}
|
|
if (ndone) {
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_DANCINGFLAME) {
|
|
int i;
|
|
int ndone = 0;
|
|
object_t *o;
|
|
if (!target) target = caster;
|
|
// find all fires within los
|
|
for (i = 0; i < target->nlos; i++) {
|
|
enum OBTYPE fireid = OT_NONE;
|
|
o = hasobofmaterial(target->los[i]->obpile, MT_FIRE);
|
|
if (o) {
|
|
fireid = o->type->id;
|
|
} else {
|
|
o = hasobwithflag(target->los[i]->obpile, F_ONFIRE);
|
|
if (o) {
|
|
fireid = OT_FIRESMALL;
|
|
}
|
|
}
|
|
|
|
if (fireid != OT_NONE) {
|
|
int dir;
|
|
cell_t *c;
|
|
// any nearby lfs?
|
|
for (dir = DC_N; dir <= DC_NW; dir++) {
|
|
c = getcellindir(target->los[i], dir);
|
|
if (c && c->lf) {
|
|
object_t *newob;
|
|
newob = addobfast(c->obpile, OT_FIRESMALL);
|
|
if (newob) {
|
|
ndone++;
|
|
if (haslos(player, c)) {
|
|
char obname[BUFLEN];
|
|
getobname(newob, obname, 1);
|
|
msg("%s spreads!", obname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_DARKNESS) {
|
|
int radius;
|
|
if (!targcell) targcell = caster->cell;
|
|
|
|
// create light objects
|
|
radius = power / 3;
|
|
limit(&radius, 1, NA);
|
|
makelitradius(targcell, radius, OT_MAGICDARK, rnd(5,10)+(power*2), power );
|
|
|
|
if (blessed || (power >= 5)) {
|
|
modillumination(targcell->map, D_DARKER);
|
|
}
|
|
|
|
if (haslos(player, targcell)) {
|
|
msg("A cloud of darkness descends nearby!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (getcelldist(player->cell, targcell) <= radius) {
|
|
msg("A cloud of darkness descends on you!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_DECAYFIELD) {
|
|
obpile_t *op;
|
|
object_t *o,*nexto;
|
|
int range = 0, ncells,i,lfdam,obdam,walldam;
|
|
cell_t *cell[MAXCANDIDATES];
|
|
|
|
// default to radiating out from the caster's cell
|
|
if (!targcell) {
|
|
if (caster) {
|
|
targcell = caster->cell;
|
|
} else {
|
|
// fail
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// determine damage (will do double this to object)
|
|
lfdam = (power*3)/2;
|
|
obdam = power*3;
|
|
walldam = power*5;
|
|
|
|
// determine radius
|
|
range = (power/2)+1;
|
|
|
|
// announce
|
|
if (caster && (targcell == caster->cell)) {
|
|
sprintf(buf, "%s radiate%s a field of decay!",castername,isplayer(caster) ? "" : "s");
|
|
} else {
|
|
sprintf(buf, "A field of decay radiates outwards!");
|
|
}
|
|
animradial(targcell, range, '}', C_INDIGO, DT_ORTH, buf, "Something radiates a field of decay!");
|
|
|
|
// don't affect centre cell
|
|
getradiuscells(targcell, range, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_FALSE, cell, &ncells, B_FALSE);
|
|
for (i = 0; i < ncells; i++) {
|
|
if (seenbyplayer) {
|
|
if (haslos(player, targcell)) {
|
|
*seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (cell[i]->lf && !isundead(cell[i]->lf) && isorganicmat(getlfmaterial(cell[i]->lf))) {
|
|
char damstr[BUFLEN];
|
|
if (caster) {
|
|
char cname[BUFLEN];
|
|
real_getlfnamea(caster, cname, NULL, B_SHOWALL, B_REALRACE);
|
|
sprintf(damstr, "%s%s field of decay spell.", cname, getpossessive(cname));
|
|
} else {
|
|
strcpy(damstr, "a field of decay spell.");
|
|
}
|
|
if (isplayer(cell[i]->lf)) {
|
|
msg("^%cPieces of your body decay away!", getlfcol(cell[i]->lf, CC_BAD));
|
|
} else if (cansee(player, cell[i]->lf)) {
|
|
char tname[BUFLEN];
|
|
getlfname(targcell->lf, tname);
|
|
msg("Pieces of %s decay away!", tname);
|
|
}
|
|
losehp(cell[i]->lf, lfdam, DT_DECAY, caster, damstr);
|
|
}
|
|
op = cell[i]->obpile;
|
|
// damage all organic objects here
|
|
for (o = op->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (isorganicmat(o->material->id)) {
|
|
if (!cell[i]->lf && haslos(player, cell[i])) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s decays!", obname);
|
|
}
|
|
takedamage(o, obdam, DT_DECAY, caster);
|
|
}
|
|
}
|
|
// damage all organic walls here
|
|
if (isorganicmat(cell[i]->type->material->id)) {
|
|
damagecell(cell[i], walldam, DT_DECAY, caster);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_DEATHKEEN) {
|
|
if (!isnighttime()) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(caster)) {
|
|
msg("You wail with the power of death!");
|
|
}
|
|
noise(caster->cell, NULL, NC_OTHER, SV_TALK, "the dread wail of a banshee", NULL);
|
|
makenoise(caster, N_DEATHKEEN);
|
|
|
|
// all in range must pass a magic resistance check or die
|
|
for (target = caster->cell->map->lf ; target ; target = target->next) {
|
|
if (target != caster) {
|
|
int nwalls;
|
|
if (canhear(target, caster->cell, SV_TALK, &nwalls)) {
|
|
if (nwalls == 0) {
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
} else {
|
|
losehp(target, target->hp, DT_SONIC, caster, "a banshee's wail");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_DELAYDEATH) {
|
|
if (!target) {
|
|
target = caster;
|
|
}
|
|
if (isplayer(target)) {
|
|
msg("^gYou bend your psionic will towards defying death!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
statdirty = B_TRUE;
|
|
}
|
|
} 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("You sense a holy aura from your %s!", noprefix(obname));
|
|
somethinghappened = B_TRUE;
|
|
} else if (o->blessed == B_CURSED) {
|
|
getobname(o, obname, o->amt);
|
|
msg("Your sense an evil aura from your %s!", noprefix(obname));
|
|
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("You sense a holy aura from %s!", obname);
|
|
somethinghappened = B_TRUE;
|
|
} else if (o->blessed == B_CURSED) {
|
|
getobname(o, obname, o->amt);
|
|
msg("You sense an evil aura from %s!", obname);
|
|
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 {
|
|
if (isplayer(caster)) nothinghappens();
|
|
}
|
|
} else {
|
|
// monsters can't use this
|
|
}
|
|
} else if (spellid == OT_S_DIG) {
|
|
int numseen = 0;
|
|
cell_t *c;
|
|
int ndigs;
|
|
char ch;
|
|
int dir;
|
|
|
|
if (targcell) {
|
|
if (targcell == caster->cell) {
|
|
dir = D_DOWN;
|
|
} else {
|
|
dir = getdirtowards(caster->cell, targcell, NULL, B_FALSE, DT_COMPASS);
|
|
}
|
|
} else {
|
|
// don't need line of fire OR sight!
|
|
//if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power, frompot)) return B_TRUE;
|
|
ch = askchar("Dig in which direction (- to cancel)", "yuhjklbn.-<>","-", B_FALSE, B_TRUE);
|
|
if ((ch == '.') || (ch == '-') || !ch) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else if (ch == '<') {
|
|
if (seenbyplayer && haslos(player, caster->cell)) *seenbyplayer = B_TRUE;
|
|
dir = D_UP;
|
|
} else if (ch == '>') {
|
|
if (seenbyplayer && haslos(player, caster->cell)) *seenbyplayer = B_TRUE;
|
|
dir = D_DOWN;
|
|
} else {
|
|
dir = chartodir(ch);
|
|
}
|
|
}
|
|
|
|
if (dir == D_UP) {
|
|
return digup(caster, NULL);
|
|
} else if (dir == D_DOWN) {
|
|
return digdown(caster, NULL);
|
|
}
|
|
|
|
if (dir == DT_NONE) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
ndigs = 0;
|
|
|
|
// get rid of rock/flesh in the cells...
|
|
c = getcellindir(caster->cell, dir);
|
|
while (c && (ndigs <= power)) {
|
|
int seenthiscell = B_FALSE;
|
|
|
|
if (haslos(player, c)) seenthiscell = B_TRUE;
|
|
|
|
if (c->type->solid) {
|
|
if (isdiggable(c, OT_S_DIG)) {
|
|
int d2;
|
|
if (seenthiscell) {
|
|
msg("%s %s collapses!", needan(c->type->name) ? "An" : "A",
|
|
c->type->name);
|
|
numseen++;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
setcelltype(c, c->map->habitat->emptycelltype);
|
|
ndigs++;
|
|
if (c->type->material->id == MT_STONE) {
|
|
// debris shower
|
|
for (d2 = DC_N; d2 <= DC_NW; d2++) {
|
|
cell_t *c2;
|
|
c2 = getcellindir(c, d2);
|
|
if (c2 && !c2->type->solid && c2->lf) {
|
|
if (!isimmuneto(c2->lf->flags, DT_PROJECTILE, B_FALSE)) {
|
|
int dam;
|
|
dam = roll("2d6");
|
|
if (check_for_block(caster, c2->lf, dam, DT_PROJECTILE, 999, "flying debris", B_RANGED)) {
|
|
} else {
|
|
if (cansee(player, c2->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c2->lf, lfname);
|
|
msg("^%c%s %s showered with debris!",
|
|
getlfcol(c2->lf, CC_BAD),
|
|
lfname, isplayer(c2->lf) ? "are" : "is");
|
|
}
|
|
losehp(c2->lf, dam, DT_PROJECTILE, NULL, "flying debris");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
addobsinradius(c, 1, DT_COMPASS, "5-10 stones", B_TRUE, B_NOCENTRE, NULL, NULL, NULL, NULL);
|
|
addob(c->obpile, "5-10 stones");
|
|
} else if (c->type->material->id == MT_BLOOD) {
|
|
addobsinradius(c, 1, DT_COMPASS, "pool of blood", B_TRUE, B_NOCENTRE, NULL, NULL, NULL, NULL);
|
|
addob(c->obpile, "pool of blood");
|
|
}
|
|
|
|
} else {
|
|
// stop.
|
|
break;
|
|
}
|
|
} else {
|
|
object_t *o;
|
|
for (o = c->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, OBS1(o));
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
ndigs++;
|
|
}
|
|
}
|
|
}
|
|
ndigs++;
|
|
// go to next cell
|
|
c = getcellindir(c, dir);
|
|
}
|
|
|
|
|
|
// 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();
|
|
}
|
|
*/
|
|
if (ndigs == 0) {
|
|
if (isplayer(caster)) nothinghappens();
|
|
}
|
|
} else if (spellid == OT_S_DISRUPTUNDEAD) {
|
|
target = targcell->lf;
|
|
if (!target || !isundead(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(target)) {
|
|
msg("^%cYou feel your body's essence unraveling!", getlfcol(target, CC_BAD));
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("%s convulses.", lfname);
|
|
}
|
|
// use direct damage rather than holy, because otherwise it might be increased
|
|
// due to vulnerabilities
|
|
losehp(target, roll("3d6") + power, DT_DIRECT, caster, "disruption");
|
|
} else if (spellid == OT_S_DISORIENT) {
|
|
target = targcell->lf;
|
|
|
|
if (lfhasflag(target, F_ASLEEP) || !ischarmable(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
return B_FALSE;
|
|
} else {
|
|
int newdir;
|
|
// make them face a random (different) direction
|
|
newdir = getdiraway(targcell, caster->cell, NULL, B_FALSE, DT_ORTH, B_FALSE);
|
|
|
|
// announce - do this before turning in case it makes the
|
|
// caster be out of sight.
|
|
if (isplayer(target)) {
|
|
msg("You are twisted around to face %s!", getdirname(target->facing));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char tname[BUFLEN];
|
|
getlfname(target, tname);
|
|
msg("%s spins around to face %s!", tname, getdirname(target->facing));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
setfacing(target, newdir);
|
|
// ai loses target info
|
|
loseaitargets(target);
|
|
|
|
}
|
|
} else if (spellid == OT_S_DETECTLIFE) {
|
|
if (!target) {
|
|
if (targcell) {
|
|
target = targcell->lf;
|
|
} else {
|
|
target = caster;
|
|
}
|
|
}
|
|
if (isplayer(target)) {
|
|
int howlong,radius;
|
|
howlong = getspellduration(40,80,blessed) + (power*2);
|
|
radius = power * 10;
|
|
addtempflag(target->flags, F_DETECTLIFE, 10, (power >= 8) ? 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 selfpoisoned = B_FALSE;
|
|
int n,i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
getflags(caster->flags, retflag, &nretflags, F_INCUBATING, F_POISONED, F_NONE);
|
|
for (i = 0; i < nretflags ; i++) {
|
|
flag_t *f;
|
|
poisontype_t *pt;
|
|
f = retflag[i];
|
|
pt = findpoisontype(f->val[0]);
|
|
msg("You detect %s in your body.", pt->name);
|
|
f->known = B_TRUE;
|
|
selfpoisoned = B_TRUE;
|
|
}
|
|
|
|
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) || (hasflag(o->flags, F_PURIFIESTO))) {
|
|
ispoisoned = B_TRUE;
|
|
} else if ((o->type->id == OT_TRAPARROWP) ||
|
|
(o->type->id == OT_TRAPNEEDLEP) ||
|
|
(o->type->id == OT_TRAPGAS)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s %s revealed!", obname, OB1(o,"is","are"));
|
|
npoisoned++;
|
|
killflagsofid(o->flags, F_SECRET);
|
|
} 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;
|
|
}
|
|
}
|
|
f = hasflag(o->flags, F_TRAPPED);
|
|
if (f) {
|
|
if ((f->val[0] == OT_TRAPARROWP) ||
|
|
(f->val[0] == OT_TRAPNEEDLEP) ||
|
|
(f->val[0] == OT_TRAPGAS)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("A poison trap on %s is revealed!", obname);
|
|
npoisoned++;
|
|
f->val[2] = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (ispoisoned || isdecayed) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s %s %s!", obname, OB1(o,"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 %spoison nearby.", selfpoisoned ? "other " : "");
|
|
}
|
|
} // end if isplayer
|
|
} else if (spellid == OT_S_DETONATE) {
|
|
// don't need line of fire!
|
|
explodecells(targcell, 20+(power*2), B_TRUE, NULL, power / 4, DT_ORTH, B_TRUE, caster);
|
|
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_DETONATEDELAY) {
|
|
addobfast(targcell->obpile, OT_VIBCLOUD);
|
|
if (haslos(player, targcell)) {
|
|
msg("^%cThe air nearby begins to vibrate violently...", getlfcol(player, CC_VBAD)); more();
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_EQANDOP) {
|
|
flag_t *f;
|
|
f = addtempflag(caster->flags, F_REFLECTION, B_TRUE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (spellid == OT_S_ETHEREALSTEED) {
|
|
int howlong;
|
|
howlong = power*5;
|
|
addtempflag(caster->flags, F_AUTOCREATEOB, 0, NA, NA, "whirlwind", howlong);
|
|
if (isplayer(caster)) {
|
|
msg("^%cA spinning whirlwind lifts you off the ground!", getlfcol(caster, CC_GOOD));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
msg("^%cA spinning whirlwind lifts %s off the ground!", getlfcol(caster, CC_GOOD), castername);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
addtempflag(caster->flags, F_LEVITATING, B_TRUE, NA, NA, NULL, howlong);
|
|
addtempflag(caster->flags, F_DTIMMUNE, DT_PROJECTILE, NA, NA, NULL, howlong);
|
|
|
|
} else if (spellid == OT_S_EVAPORATE) {
|
|
cell_t *cell[MAXCANDIDATES];
|
|
int i,ncells;
|
|
int nseen = 0;
|
|
int nsteamseen = 0;
|
|
// don't need line of fire!
|
|
getradiuscells(targcell, power, DT_ORTH, B_FALSE, LOF_NEED, B_TRUE, cell, &ncells, B_FALSE);
|
|
for (i = 0; i < ncells; i++) {
|
|
int cellseen = B_FALSE;
|
|
object_t *o,*nexto;
|
|
if (haslos(player, cell[i])) {
|
|
cellseen = B_TRUE;
|
|
}
|
|
for (o = cell[i]->obpile->first ; o ; o = nexto) {
|
|
int dosteam = B_FALSE;
|
|
nexto = o->next;
|
|
if (getmaterialstate(o->material->id) == MS_LIQUID) {
|
|
// if 20kilos or more, make steam
|
|
if (getobmass(o) >= 25) {
|
|
dosteam = B_TRUE;
|
|
if (cellseen) nsteamseen++;
|
|
}
|
|
nseen++;
|
|
removeob(o, ALL);
|
|
} else if ((o->material->id == MT_ICE) || (o->type->obclass->id == OC_POTION)) {
|
|
char obname[BUFLEN];
|
|
dosteam = B_TRUE;
|
|
if (cellseen) nsteamseen++;
|
|
nseen++;
|
|
getobname(o, obname, o->amt);
|
|
msg("%s boil%s and explode%s!", obname, OBS1(o), OBS1(o));
|
|
removeob(o, ALL);
|
|
}
|
|
|
|
if (dosteam) {
|
|
addob(cell[i]->obpile, "cloud of steam");
|
|
}
|
|
}
|
|
if (cell[i]->lf) {
|
|
for (o = cell[i]->lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if ((o->material->id == MT_ICE) || (o->type->obclass->id == OC_POTION)) {
|
|
char obname[BUFLEN];
|
|
if (cellseen) nsteamseen++;
|
|
nseen++;
|
|
getobname(o, obname, o->amt);
|
|
msg("%s boil%s and explode%s!", obname, OBS1(o), OBS1(o));
|
|
removeob(o, ALL);
|
|
addob(cell[i]->obpile, "cloud of steam");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nsteamseen) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("A huge cloud of steam appears!");
|
|
} else if (nseen) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("Some nearby liquid evaporates!");
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
} else if (spellid == OT_S_EXORCISE) {
|
|
if (!target) target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (lfhasflag(target, F_SUMMONEDBY) || (getraceclass(target) == RC_DEMON)) {
|
|
if (canexorcise(caster, target, power)) {
|
|
unsummon(target, B_TRUE);
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%c%s is too powerful for you to exorcise!", getlfcol(caster, CC_BAD), lfname);
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("%s resists the effects of an exorcism.", lfname);
|
|
}
|
|
}
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("%s seems unaffected.", lfname);
|
|
}
|
|
// this counts as a failure since you targetted the
|
|
// wrong kind of creature
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_EXORCISEMASS) {
|
|
int i,ndone = 0;
|
|
for (i = 1; i < caster->nlos; i++) {
|
|
cell_t *c;
|
|
c = caster->los[i];
|
|
if (c->lf &&
|
|
(lfhasflag(c->lf, F_SUMMONEDBY) || (getraceclass(c->lf) == RC_DEMON)) ) {
|
|
if (canexorcise(caster, c->lf, power)) {
|
|
unsummon(c->lf, B_TRUE);
|
|
ndone++;
|
|
} else {
|
|
if (!isplayer(caster) && cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("%s resists the effects of an exorcism.", lfname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (ndone == 0) {
|
|
if (isplayer(caster)) {
|
|
msg("Your exorcism fails.");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_EXPLODEMETAL) {
|
|
float totalmass = 0;
|
|
object_t *o, *nexto;
|
|
|
|
// 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 += getobmass(o);
|
|
removeob(o, o->amt);
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
if (removedeadobs(targcell->obpile)) {
|
|
if (isplayer(caster)) angergodmaybe(R_GODNATURE, 20, GA_ATTACKOBJECT);
|
|
}
|
|
|
|
// 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, caster);
|
|
} else {
|
|
explodecells(targcell, totalmass*5, B_TRUE, NULL, 1, DT_COMPASS, B_TRUE, caster);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_DISPERSAL) {
|
|
cell_t *c = NULL;
|
|
object_t *o, *nexto;
|
|
int donesomething = B_FALSE;
|
|
|
|
if (!targcell) {
|
|
if (isplayer(caster)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
target = targcell->lf;
|
|
|
|
if (target) {
|
|
int resisted = B_FALSE, failed = 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 (target == caster) {
|
|
if (getmr(target) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
resisted = B_TRUE;
|
|
}
|
|
} else {
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
resisted = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!resisted) {
|
|
c = getrandomcell_forteleport(targcell->map, target);
|
|
if (c) {
|
|
if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
donesomething = B_TRUE;
|
|
}
|
|
teleportto(target, c, B_FALSE);
|
|
} else {
|
|
// nothing happens.
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (resisted || failed) {
|
|
if (isplayer(target)) {
|
|
msg("You flicker.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
donesomething = B_TRUE;
|
|
} else if (haslos(player, targcell)) {
|
|
getlfname(target, buf);
|
|
msg("%s flickers.",buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
|
|
c = getrandomcell_forteleport(targcell->map, NULL);
|
|
if (c) {
|
|
getobname(o, buf, o->amt);
|
|
if (haslos(player, targcell) && canseeob(player, o)) {
|
|
msg("%s disappear%s!", buf, OBS1(o));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
donesomething = B_TRUE;
|
|
}
|
|
moveob(o, c->obpile, o->amt);
|
|
if (haslos(player, c) && canseeob(player, o)) {
|
|
msg("%s appear%s nearby!", buf, OBS1(o));
|
|
}
|
|
}
|
|
}
|
|
if (isplayer(caster) && !donesomething) {
|
|
nothinghappens();
|
|
}
|
|
rv = B_FALSE;
|
|
} else if (spellid == OT_S_DRAINIQ) {
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
msg("^%cYour brain cells are blasted!",getlfcol(target, CC_VBAD));
|
|
} else if (isplayer(caster)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%cYou blast %s%s brain cells!", getlfcol(target, CC_VBAD),
|
|
lfname, getpossessive(lfname));
|
|
}
|
|
if (!ischarmable(target) && (reason != E_ALREADYUSING) && (reason != E_LOWIQ)) {
|
|
if (isplayer(caster)) {
|
|
char tname[BUFLEN];
|
|
getlfname(target, tname);
|
|
msg("%s %s unaffected.", tname, isplayer(target) ? "are" : "is");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
statdrain(target, A_IQ, roll("2d6") + rolldie(power,6),SC_NONE,-1,caster);
|
|
} else if (spellid == OT_S_DRAINLIFE) {
|
|
char lfname[BUFLEN];
|
|
target = haslf(targcell);
|
|
if (target) {
|
|
getlfname(target, lfname);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!isimmuneto(target->flags, DT_NECROTIC, B_FALSE)) {
|
|
// animation (opposite dir)
|
|
anim(targcell, caster->cell, '%', C_MAGENTA);
|
|
}
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
if (isimmuneto(target->flags, DT_NECROTIC, B_FALSE)) {
|
|
msg("^%c%s suck%s death from %s!",getlfcol(target, CC_BAD),
|
|
castername,isplayer(caster) ? "" : "s", lfname);
|
|
} else {
|
|
msg("^%c%s suck%s life from %s!",getlfcol(target, CC_BAD),
|
|
castername,isplayer(caster) ? "" : "s", lfname);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (isplayer(target)) {
|
|
msg("^%cYou feel your life force draining away!", getlfcol(target, CC_BAD));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (target) {
|
|
int amt;
|
|
if (isimmuneto(target->flags, DT_NECROTIC, B_FALSE)) {
|
|
// target gains hp
|
|
amt = rnd(1,4) + power;
|
|
gainhp(target, amt);
|
|
// caster loses it
|
|
amt = losehp(caster, amt, DT_NECROTIC, caster, "lifeforce drain");
|
|
} else {
|
|
// target loses hp
|
|
amt = losehp(target, rnd(1,4) + power, DT_NECROTIC, caster, "lifeforce drain");
|
|
// caster gains it
|
|
gainhp(caster, amt);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_ENERGYBLAST) {
|
|
int range;
|
|
int x,y;
|
|
char buf[BUFLEN];
|
|
if (caster && !targcell) targcell = caster->cell;
|
|
range = 2 + (power / 4);
|
|
|
|
sprintf(buf, "%s emit%s a radial blast of energy!",castername,isplayer(caster) ? "" : "s");
|
|
animradial(targcell, range, '}', C_CYAN, DT_COMPASS, buf, "Something emits a radial blast of energy!");
|
|
|
|
if (isplayer(caster) || haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
for (y = targcell->y - range ; y <= targcell->y + range; y++) {
|
|
for (x = targcell->x - range ; x <= targcell->x + range; x++) {
|
|
cell_t *c;
|
|
c = getcellat(targcell->map, x,y);
|
|
if (c && (getcelldist(targcell, c) <= range)) {
|
|
if (c->lf && (c->lf != caster) && haslof(targcell, c, B_FALSE, NULL)) {
|
|
int dam;
|
|
dam = roll("2d6");
|
|
// automatic hit
|
|
if (check_for_block(caster, c->lf, dam, DT_MAGIC, 999, "an energy blast", B_RANGED)) {
|
|
} else {
|
|
if (isplayer(c->lf)) {
|
|
msg("^%cA blast of energy hits you!", getlfcol(c->lf, CC_BAD));
|
|
} else if (cansee(player, c->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("^%cA blast of energy hits %s.", getlfcol(c->lf, CC_BAD), lfname);
|
|
}
|
|
losehp(c->lf, dam, DT_MAGIC, caster, "an energy blast");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_FEAR) {
|
|
char targname[BUFLEN];
|
|
// ask for target
|
|
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("^%cYou suffer terrifying nightmares!", getlfcol(target, CC_BAD));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
msg("^%c%s thrashes about in its sleep!",getlfcol(target, CC_BAD), targname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
losehp(target, roll("2d6"), DT_DIRECT, caster, "terrifying nightmares");
|
|
} else {
|
|
scare(target, caster, 5+rnd(1,power), 6+power);
|
|
}
|
|
} else if (spellid == OT_S_FEEBLEMIND) {
|
|
target = targcell->lf;
|
|
|
|
if ((getattr(target, A_IQ) <= 15) || spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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, 15, NA, NULL, howlong);
|
|
f->obfrom = OT_S_FEEBLEMIND;
|
|
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_FIREWORKS) {
|
|
lifeform_t *poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
if (isplayer(caster)) {
|
|
msg("^wA burst of fireworks explodes around you!");
|
|
} else if (cansee(player, caster)) {
|
|
msg("^wA burst of fireworks explodes around %s!", castername);
|
|
}
|
|
for (target = caster->cell->map->lf ; target ; target = target->next) {
|
|
if (target == caster) continue;
|
|
if (haslos(target, caster->cell)) {
|
|
poss[nposs] = target;
|
|
nposs++;
|
|
if (nposs >= MAXCANDIDATES) break;
|
|
}
|
|
}
|
|
if (nposs) {
|
|
int i,n,idx;
|
|
for (i = 0; (i < power) && nposs; i++) {
|
|
idx = rnd(0,nposs-1);
|
|
stun(poss[idx], 2);
|
|
for (n = idx; n < nposs-1; n++) {
|
|
poss[n] = poss[n+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_FLASH) {
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
brightflash(caster->cell, 2 + (power/4), caster);
|
|
} else if (spellid == OT_S_FLAYFLESH) {
|
|
char targetname[BUFLEN];
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (target->race->material->id != MT_FLESH) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("^%cUnseen forces rip into your flesh!", getlfcol(target, CC_VBAD));
|
|
} else if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
getlfname(target, targetname);
|
|
msg("^%cUnseen forces rip into %s%s flesh!", getlfcol(target, CC_VBAD), targetname, getpossessive(targetname));
|
|
}
|
|
criticalhit(caster, target, getrandomcorebp(target, NULL), NULL, rnd(1,6), DT_SLASH);
|
|
} else if (spellid == OT_S_GLYPHWARDING) {
|
|
char buf[BUFLEN];
|
|
int gpower;
|
|
gpower = 2 + (power / 2);
|
|
limit(&gpower, 1, NA);
|
|
sprintf(buf, "^g*WARD%d*^n", gpower);
|
|
writetextonground(caster, caster->cell, buf, power*5);
|
|
} else if (spellid == OT_S_FLOATINGDISC) {
|
|
lifeform_t *newlf;
|
|
// get random adjacent cell
|
|
targcell = getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND);
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
// add it
|
|
newlf = summonmonster(caster, targcell, R_FLOATINGDISC, NULL, PERMENANT, B_TRUE);
|
|
if (newlf) {
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
// set carrying capacity
|
|
setattr(newlf, A_STR, 8 + power);
|
|
} else {
|
|
// didn't work for some reason
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_FLOOD) {
|
|
int failed = B_FALSE;
|
|
if (targcell) {
|
|
if (!targcell->type->solid) {
|
|
// create water there
|
|
object_t *o;
|
|
o = addob(targcell->obpile, "water");
|
|
if (o) {
|
|
enum OBTYPE badoid;
|
|
int i,amt;
|
|
int depth;
|
|
condset_t cs;
|
|
depth = DP_SHOULDERS + power;
|
|
|
|
//amt = ((power+1) * (power+1)) - 1;
|
|
//amt = power;
|
|
amt = power*2;
|
|
badoid = OT_WATERDEEP;
|
|
for (i = 0; i < amt; i++) {
|
|
cell_t *c;
|
|
|
|
initcondv(&cs, CC_SOLID, B_FALSE, NA,
|
|
CC_HASOBTYPE, B_FALSE, badoid,
|
|
CC_NONE);
|
|
|
|
c = real_getrandomadjcell(targcell, &cs, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, NULL);
|
|
if (c) {
|
|
object_t *water;
|
|
water = addob(c->obpile, "water");
|
|
if (water) {
|
|
flag_t *f;
|
|
f = hasflag(water->flags, F_DEEPWATER);
|
|
if (f) f->val[0] = depth;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (haslos(player, targcell)) {
|
|
msg("A huge pool of water appears!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (isplayer(caster)) {
|
|
angergodmaybe(R_GODFIRE, 75, GA_HERESY);
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_FORCESPHERE) {
|
|
cell_t *retcell[MAXRETCELLS];
|
|
char buf[BUFLEN];
|
|
int radius,nretcells,n,i;
|
|
if (!target) target = caster;
|
|
targcell = target->cell;
|
|
radius = power/3;
|
|
limit(&radius, 1, NA);
|
|
if (isplayer(target)) {
|
|
sprintf(buf, "You unleash a mighty shockwave!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
animradial(target->cell, radius, '}', C_RED, DT_COMPASS, buf, buf);
|
|
} else if (cansee(player, target)) {
|
|
char tname[BUFLEN];
|
|
getlfname(target, tname);
|
|
sprintf(buf, "%s unleashes a mighty shockwave!", tname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
animradial(target->cell, radius, '}', C_RED, DT_COMPASS, buf, buf);
|
|
}
|
|
// start at outside.
|
|
for (n = radius; n >= 1; n--) {
|
|
getradiuscells(targcell, n, DT_COMPASS, B_TRUE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0);
|
|
for (i = 0; i < nretcells; i++) {
|
|
if (retcell[i]->lf) {
|
|
knockback(retcell[i]->lf,
|
|
getdiraway(retcell[i], targcell, NULL, B_FALSE, DT_COMPASS, B_FALSE),
|
|
2, target, 100+(power*10), B_DOANNOUNCE, B_DODAM);
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_ENERGYBOLT) {
|
|
char lfname[BUFLEN];
|
|
char numbuf[BUFLEN];
|
|
|
|
numtotext(power, numbuf);
|
|
// 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) {
|
|
int dam;
|
|
getlfname(target, lfname);
|
|
// target takes magical damage
|
|
// ALWAYS hits.
|
|
dam = rolldie(power*2,4);
|
|
if (check_for_block(caster, target, dam, DT_MAGIC, 999, "an energy bolt", B_RANGED)) {
|
|
} else {
|
|
if (cansee(player, target)) {
|
|
if (power == 1) {
|
|
msg("^%cA bolt of energy hits %s.",getlfcol(target, CC_BAD), lfname);
|
|
} else {
|
|
msg("^%c%s bolts of energy hit %s.",getlfcol(target, CC_BAD), numbuf, lfname);
|
|
}
|
|
}
|
|
}
|
|
losehp(target, dam, DT_MAGIC, caster, "an energy bolt");
|
|
}
|
|
} else if ((spellid == OT_S_FIREBALL) || (spellid == OT_S_METEOR)) {
|
|
int failed = B_FALSE;
|
|
char fbname[BUFLEN];
|
|
enum OBTYPE fire1, fire2,fire3;
|
|
if (spellid == OT_S_FIREBALL) {
|
|
strcpy(fbname, "a huge ball of fire");
|
|
fire1 = OT_FIREMED;
|
|
fire2 = OT_FIREMED;
|
|
fire3 = OT_FIRESMALL;
|
|
} else {
|
|
strcpy(fbname, "a flaming meteorite");
|
|
fire1 = OT_FIRELARGE;
|
|
fire2 = OT_FIRELARGE;
|
|
fire3 = OT_FIREMED;
|
|
}
|
|
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 %s!", fbname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s launches %s!",castername, fbname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (haslos(player, targcell)) {
|
|
msg("%s explodes!", fbname);
|
|
}
|
|
|
|
anim(caster->cell, targcell, '^', C_RED);
|
|
|
|
redrawpause();
|
|
// add fires as follows (3 = medium, 2 = medium, 1 = small)
|
|
//
|
|
// 1
|
|
// 232
|
|
// 13331
|
|
// 232
|
|
// 1
|
|
// add large fires to surrounding cells
|
|
addobfast(targcell->obpile, fire1);
|
|
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 = addobfast(c->obpile, fire1);
|
|
if (o) {
|
|
setobcreatedby(o, caster);
|
|
}
|
|
if (c->lf) {
|
|
int dam;
|
|
char attackname[BUFLEN];
|
|
if (spellid == OT_S_FIREBALL) {
|
|
dam = rolldie(power,3);
|
|
strcpy(attackname, "a fireball");
|
|
} else {
|
|
dam = 30+rolldie(power,6);
|
|
strcpy(attackname, "a meteorite");
|
|
}
|
|
|
|
if (check_for_block(caster, c->lf, dam, DT_FIRE, 999, attackname, B_RANGED)) {
|
|
// partial damage
|
|
dam /= 2;
|
|
}
|
|
losehp(c->lf, dam, DT_FIRE, caster, attackname);
|
|
}
|
|
}
|
|
}
|
|
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 = addobfast(c->obpile, fire2);
|
|
if (o) {
|
|
setobcreatedby(o, caster);
|
|
}
|
|
if (c->lf) {
|
|
int ndice;
|
|
ndice = power / 2; if (ndice < 1) ndice = 1;
|
|
|
|
if (spellid == OT_S_FIREBALL) {
|
|
losehp(c->lf, rolldie(ndice,3), DT_FIRE, caster, "a fireball");
|
|
} else {
|
|
losehp(c->lf, 15+rolldie(ndice,5), DT_FIRE, caster, "a meteorite");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
if ((spellid = OT_S_METEOR) || (power > 5)) {
|
|
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 = addobfast(c->obpile, fire3);
|
|
if (o) {
|
|
setobcreatedby(o, caster);
|
|
}
|
|
if (c->lf) {
|
|
int ndice;
|
|
ndice = power / 2; if (ndice < 1) ndice = 1;
|
|
if (spellid == OT_S_FIREBALL) {
|
|
losehp(c->lf, rolldie(ndice,2), DT_FIRE, caster, "a fireball");
|
|
} else {
|
|
losehp(c->lf, 7+rolldie(ndice,4), DT_FIRE, caster, "a meteorite");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_FIREDART) {
|
|
char lfname[BUFLEN];
|
|
// 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, 90 + (power*10), 0)) {
|
|
// miss
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
msg("A dart of flame misses %s.",lfname);
|
|
}
|
|
} else {
|
|
int dam;
|
|
// hit
|
|
dam = rnd(1,6) + power;
|
|
if (check_for_block(caster, target, dam, DT_FIRE, 999, "a dart of flame", B_RANGED)) {
|
|
} else {
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
msg("^%cA dart of flame hits %s.",getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
losehp(target, dam, DT_FIRE, caster, "a dart of flame");
|
|
}
|
|
}
|
|
} else {
|
|
damageallobs(NULL, targcell->obpile, 0, DT_FIRE, caster);
|
|
}
|
|
} else if (spellid == OT_S_FLAMEBURST) {
|
|
int range = 1;
|
|
int x,y;
|
|
char buf[BUFLEN],buf2[BUFLEN];
|
|
|
|
range = 1 + (power / 5);
|
|
|
|
// announce
|
|
sprintf(buf, "%s emit%s a %sblast of fire!",castername,isplayer(caster) ? "" : "s",
|
|
(power >= 5) ? "huge " : "");
|
|
sprintf(buf2, "Something emit%s a %sblast of fire!",isplayer(caster) ? "" : "s",
|
|
(power >= 5) ? "huge " : "");
|
|
|
|
animradial(caster->cell, range, '}', C_RED, DT_ORTH, buf, buf2);
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
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];
|
|
int dam;
|
|
dam = rolldie(2,6);
|
|
// automatic hit
|
|
getlfname(targcell->lf, lfname);
|
|
if (check_for_block(caster, targcell->lf, dam, DT_FIRE, 999, "a burst of fire", B_RANGED)) {
|
|
} else {
|
|
if (haslos(caster, targcell)) {
|
|
msg("^%c%s burn%s!",getlfcol(targcell->lf, CC_BAD),
|
|
lfname,isplayer(targcell->lf) ? "" : "s");
|
|
}
|
|
losehp(targcell->lf, dam, DT_FIRE, caster, "a burst of fire");
|
|
}
|
|
}
|
|
damageallobs(NULL, targcell->obpile, rolldie(2,6), DT_FIRE, caster);
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_FLAMEPILLAR) {
|
|
int failed = B_FALSE;
|
|
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;
|
|
}
|
|
if (power < 5) {
|
|
o = addob(targcell->obpile, "small fire");
|
|
} else if (power < 8) {
|
|
o = addob(targcell->obpile, "medium fire");
|
|
} else {
|
|
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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_EARTHQUAKE) {
|
|
cell_t *retcell[MAXRETCELLS],*c;
|
|
int nretcells,i,radius,seenwalls = 0, seenpits = 0;
|
|
radius = 3 + power;
|
|
getradiuscells(caster->cell, radius, DT_ORTH, B_FALSE, LOF_DONTNEED, B_FALSE, retcell, &nretcells, 80);
|
|
for (i = 0; i < nretcells; i++) {
|
|
c = retcell[i];
|
|
if (c->type->solid) {
|
|
if (haslos(player, c)) seenwalls++;
|
|
setcelltype(c, c->map->habitat->emptycelltype);
|
|
addob(c->obpile, "50-100 stones");
|
|
} else {
|
|
// it collapses
|
|
addobfast(c->obpile, OT_HOLEINGROUND);
|
|
if (haslos(player, c)) seenpits++;
|
|
}
|
|
}
|
|
getradiuscells(caster->cell, radius, DT_ORTH, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 80);
|
|
for (i = 0; i < nretcells; i++) {
|
|
c = retcell[i];
|
|
if (!c->type->solid) {
|
|
addobfast(c->obpile, OT_DUSTCLOUD);
|
|
}
|
|
}
|
|
if (seenpits || seenwalls || cansee(player, caster)) {
|
|
msg("The earth below you shudders and shakes violently!");
|
|
setlosdirty(player);
|
|
}
|
|
if (seenpits) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
if (seenwalls == 1) {
|
|
msg("A huge rent opens up in the ground!");
|
|
} else {
|
|
msg("Huge rents open up in the ground!");
|
|
}
|
|
}
|
|
if (seenwalls) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
if (seenwalls == 1) {
|
|
msg("A nearby wall collapses into the ground!");
|
|
} else {
|
|
msg("Nearby walls collapse into the ground!");
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_ENCHANT) {
|
|
object_t *o;
|
|
|
|
if (targob) {
|
|
o = targob;
|
|
} else {
|
|
// ask for an object
|
|
condset_t cs;
|
|
initcondv(&cs, CC_HASFLAG, B_TRUE, F_ENCHANTABLE,
|
|
CC_NONE);
|
|
o = askobject(caster->pack, "Enchant which object", NULL, NULL, '\0', &cs, B_FALSE);
|
|
}
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(caster)) {
|
|
if (!hasflag(o->flags, F_ENCHANTABLE)) {
|
|
if (isplayer(caster)) nothinghappens();
|
|
return B_TRUE;
|
|
} else {
|
|
int amt;
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("Your %s glow%s %s for a moment.", noprefix(obname), (o->amt == 1) ? "s" : "",
|
|
(blessed == B_CURSED) ? "black" : "green");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
if (blessed == B_CURSED) {
|
|
amt = -1;
|
|
} else {
|
|
amt = 1;
|
|
}
|
|
modbonus(o, amt);
|
|
if (iscursed(o)) {
|
|
setblessed(o, B_UNCURSED);
|
|
}
|
|
killflagsofid(o->flags, F_RUSTED);
|
|
}
|
|
} else {
|
|
// monsters can't id things!
|
|
}
|
|
} else if (spellid == OT_S_ENDURECOLD) {
|
|
flag_t *f;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = addtempflag(caster->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_ENDUREELEMENTS) {
|
|
flag_t *f;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
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_ENDUREFIRE) {
|
|
flag_t *f;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = addtempflag(caster->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_ENTANGLE) {
|
|
char targname[BUFLEN];
|
|
flag_t *f;
|
|
object_t *o;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targname);
|
|
if (cansee(player, target)) {
|
|
msg("^%cEntangling vines rise up and grasp %s!",getlfcol(target, CC_BAD), targname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// create vine
|
|
o = addobfast(targcell->obpile, OT_VINE);
|
|
|
|
// set power
|
|
f = hasflag(o->flags, F_RESTRICTMOVEMENT);
|
|
if (f) {
|
|
f->val[0] = 100 + (power*10);
|
|
//f->val[1] = B_FALSE; // struggling doesn't damage the vine
|
|
f->val[1] = B_TRUE; // struggling doesn't damage the vine
|
|
}
|
|
// boost hp based on power, up 300%
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[1] = pctof(100 + (power*20), f->val[1]);
|
|
f->val[0] = f->val[1];
|
|
}
|
|
if (caster && (caster->cell->map == targcell->map)) {
|
|
// if caster is on the same map (ie. not being cast by a god)
|
|
/// remember creator. if they don't have los to us, spell
|
|
// is broken and vines will vanish.
|
|
setobcreatedby(o, caster);
|
|
}
|
|
} else if (spellid == OT_S_EXCAVATE) {
|
|
cell_t *retcell[MAXRETCELLS],*c;
|
|
int nretcells,i,radius,seenwalls = 0, seenobs = 0;
|
|
int killedobs = 0;
|
|
radius = power;
|
|
limit(&radius, 3, NA);
|
|
getradiuscells(caster->cell, radius, DT_ORTH, B_FALSE, LOF_DONTNEED, B_FALSE, retcell, &nretcells, 0);
|
|
for (i = 0; i < nretcells; i++) {
|
|
object_t *o, *nexto;
|
|
c = retcell[i];
|
|
if (c->type->solid) {
|
|
if (haslos(player, c)) seenwalls++;
|
|
setcelltype(c, c->map->habitat->emptycelltype);
|
|
addob(c->obpile, "10-20 piles of ash");
|
|
}
|
|
// impassable objects here are destroyed
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if ((o->type->id != OT_ASH) && (getmaterialstate(o->material->id) == MS_SOLID)) {
|
|
killob(o);
|
|
addobfast(c->obpile, OT_ASH);
|
|
if (haslos(player, c)) seenobs++;
|
|
killedobs++;
|
|
}
|
|
}
|
|
}
|
|
if (seenobs || seenwalls || cansee(player, caster)) {
|
|
msg("A shockwave of destructive force rips through the air!");
|
|
setlosdirty(player);
|
|
}
|
|
if (isplayer(caster) && killedobs) {
|
|
angergodmaybe(R_GODNATURE, 10*killedobs, GA_ATTACKOBJECT);
|
|
}
|
|
} else if (spellid == OT_S_FLIGHT) {
|
|
flag_t *f;
|
|
int height;
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
height = (power/2)+1;
|
|
limit(&height, SZ_MEDIUM, NA);
|
|
|
|
f = addtempflag(caster->flags, F_FLYING, height, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_FREEZEOB) {
|
|
if (lfhasflag(caster, F_FREEZINGTOUCH)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
// next thing touched
|
|
addflag(caster->flags, F_FREEZINGTOUCH, 1, power, 10+power, NULL);
|
|
return B_FALSE;
|
|
} else if (spellid == OT_S_FROSTBITE) {
|
|
char lfname[BUFLEN];
|
|
int exposedlimbs,dam;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, lfname);
|
|
|
|
// how many body parts are impacted?
|
|
exposedlimbs = getexposedlimbs(target);
|
|
|
|
dam = rolldie(exposedlimbs, 4);
|
|
|
|
if (isplayer(target)) {
|
|
if (isimmuneto(target->flags, DT_COLD, B_FALSE)) {
|
|
msg("You feel mildly chilly.");
|
|
} else {
|
|
msg("^%cYou feel extremely cold!", getlfcol(target, CC_BAD) );
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
if (isimmuneto(target->flags, DT_COLD, B_FALSE)) {
|
|
msg("%s looks mildly chilly.", lfname);
|
|
} else {
|
|
msg("^%c%s looks extremely cold!", getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// target takes magical damage
|
|
// always hit
|
|
if (!isimmuneto(target->flags, DT_COLD, B_FALSE)) {
|
|
losehp(target, dam, DT_COLD, caster, "a frostbite spell");
|
|
}
|
|
} else if (spellid == OT_S_GASEOUSFORM) {
|
|
if (!target) target = caster;
|
|
if (getmr(target) && (target->race->id != R_VAMPIRE) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
if (isplayer(target)) {
|
|
msg("You feel momentarily insubstantial.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
int howlong;
|
|
if (isplayer(caster) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (target->race->id == R_VAMPIRE) {
|
|
howlong = PERMENANT;
|
|
} else {
|
|
howlong = 10;
|
|
}
|
|
polymorphto(target, R_GASCLOUD, howlong);
|
|
}
|
|
} else if (spellid == OT_S_GATHERFLAME) {
|
|
cell_t *c;
|
|
int amt = 0,i;
|
|
if (!target) target = caster;
|
|
// override castername
|
|
getlfname(target, castername);
|
|
// all flame in sight
|
|
for (i = 0; i < target->nlos; i++) {
|
|
c = target->los[i];
|
|
if (c->lf && (c->lf->material->id == MT_FIRE)) {
|
|
if (gettr(c->lf) <= power) {
|
|
char buf[BUFLEN];
|
|
// instadeath
|
|
sprintf(buf, "being sucked into %s", castername);
|
|
losehp(c->lf, c->lf->maxhp, DT_DIRECT, target, buf);
|
|
if (isplayer(c->lf)) {
|
|
msg("^%cYour essence is sucked into %s!", getlfcol(c->lf, CC_VBAD), castername);
|
|
} else if (cansee(player, c->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("^%c%s%s essence is sucked into %s!",getlfcol(c->lf, CC_VBAD), lfname, getpossessive(lfname), castername);
|
|
}
|
|
amt++;
|
|
}
|
|
}
|
|
}
|
|
if (isdead(target)) { // consumed yourself?
|
|
return B_FALSE;
|
|
}
|
|
// now gather flame from cells in los
|
|
for (i = 0; (i < target->nlos) && (amt < power); i++) {
|
|
object_t *o, *nexto;
|
|
for (o = target->los[i]->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->material->id == MT_FIRE) {
|
|
removeob(o, ALL);
|
|
amt++;
|
|
} else {
|
|
int nfound;
|
|
nfound = killflagsofid(o->flags, F_ONFIRE);
|
|
if (nfound) {
|
|
amt += nfound;
|
|
// kill temperature mods too.
|
|
amt += killflagsofid(o->flags, F_TEMPMOD);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (amt) {
|
|
// boost spells...
|
|
addflag(target->flags, F_TEMPMAGICBOOST, amt, NA, NA, NULL);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_GLACIATE) {
|
|
object_t *o;
|
|
|
|
o = addobfast(targcell->obpile, OT_COLDNESS);
|
|
if (o) {
|
|
flag_t *f;
|
|
if (haslos(player, targcell)) {
|
|
msg("A puff of vapour appears.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
addflag(o->flags, F_TEMPMOD, (power == 2) ? -25 : -15, NA, NA, NULL);
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[1] = power*20;
|
|
f->val[0] = f->val[1];
|
|
}
|
|
affect_temperature(o, B_ADD);
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if ((spellid == OT_S_GREASE) || (spellid == OT_S_CREATEWATER)) {
|
|
int radius;
|
|
char createname[BUFLEN];
|
|
if (targcell->type->solid) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
radius = power/2;
|
|
if (radius < 1) radius = 1;
|
|
|
|
switch (spellid) {
|
|
case OT_S_CREATEWATER:
|
|
strcpy(createname, "large puddle of water");
|
|
break;
|
|
case OT_S_GREASE:
|
|
strcpy(createname, "puddle of oil");
|
|
break;
|
|
default:
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
addobburst(targcell, radius, DT_ORTH, createname, caster, LOF_WALLSTOP);
|
|
|
|
if (haslos(player, targcell)) {
|
|
char underbuf[BUFLEN];
|
|
if (targcell->lf && cansee(player, targcell->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
snprintf(underbuf, BUFLEN, " under %s",lfname);
|
|
} else {
|
|
strcpy(underbuf, "");
|
|
}
|
|
msg("A %s%s appears%s!", (radius == 1) ? "" : "huge ", createname, underbuf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_HAILSTORM) {
|
|
int failed = B_FALSE;
|
|
if (caster && (caster->cell->map->habitat->id == H_FOREST)) {
|
|
power += 3;
|
|
limit(&power, NA, 10);
|
|
}
|
|
// ask for a target cell
|
|
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, B_INCLUDECENTRE,
|
|
caster, NULL, NULL, NULL);
|
|
|
|
// 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 = hasflagval(o->flags, F_WALKDAM, DT_PROJECTILE, NA, NA, NULL);
|
|
if (f) {
|
|
changeflagtext(f, dambuf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end forx
|
|
} // end fory
|
|
|
|
free(dambuf);
|
|
} else { // end if targcell not solid
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_HASTE) {
|
|
int howlong = 15;
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (getmr(target) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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,i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
int undead = B_FALSE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
undead = isundead(target);
|
|
|
|
// cure certain bad effects
|
|
if (!undead) {
|
|
if (killflagsofid(target->flags, F_PAIN)) {
|
|
donesomething = B_TRUE;
|
|
}
|
|
getflags(target->flags, retflag, &nretflags, F_INCUBATING, F_POISONED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
poisontype_t *pt;
|
|
pt = findpoisontype(retflag[i]->val[0]);
|
|
if (pt->severity == PS_CURSE) {
|
|
} else {
|
|
killflag(retflag[i]);
|
|
donesomething = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!lfhasflagval(target, F_POISONED, P_ROT, NA, NA, NULL)) {
|
|
// severed body parts
|
|
getflags(target->flags, retflag, &nretflags, F_INJURY, F_NOBODYPART, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
enum BODYPART bpgone = BP_NONE;
|
|
if ((retflag[i]->id == F_NOBODYPART) && (retflag[i]->val[1] == B_FROMINJURY)) {
|
|
bpgone = retflag[i]->val[0];
|
|
} else if ((retflag[i]->id == F_INJURY) && (retflag[i]->lifetime == PERMENANT)) { // permenant injury
|
|
bpgone = retflag[i]->val[1];
|
|
}
|
|
if (bpgone != BP_NONE) {
|
|
if (isplayer(target)) {
|
|
msg("^%cYour %s grows back!", getlfcol(target, CC_GOOD), getbodypartname(target, bpgone));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
msg("^%c%s%s %s grows back!", getlfcol(target, CC_GOOD), targname, getpossessive(targname),
|
|
getbodypartname(target, bpgone));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
killflag(retflag[i]);
|
|
donesomething = B_TRUE;
|
|
} else if ((retflag[i]->id == F_INJURY) && (retflag[i]->lifetime > 0)) {
|
|
killflag(retflag[i]);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((spellid == OT_S_HEALINGMIN) && donesomething) {
|
|
// minor healing will stop here
|
|
} else {
|
|
int min,max,amt;
|
|
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;
|
|
}
|
|
amt = getspellduration(min,max,blessed);
|
|
limit(&amt, min+(power*2)-1, max);
|
|
|
|
if (undead) {
|
|
losehp(target, amt, DT_HOLY, caster, "the power of healing");
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
getlfname(target, buf);
|
|
msg("^%c%s writhe%s in agony!", getlfcol(target, CC_BAD), buf, isplayer(target) ? "" : "s");
|
|
}
|
|
} else if (target->hp < target->maxhp) {
|
|
if (!undead && lfhasflagval(target, F_POISONED, P_ROT, NA, NA, NULL)) {
|
|
amt /= 10;
|
|
}
|
|
if (amt > 0) {
|
|
gainhp(target, amt);
|
|
if (isplayer(target)) {
|
|
if (target->hp >= target->maxhp) {
|
|
switch (spellid) {
|
|
case OT_S_HEALINGMIN:
|
|
msg("^%cAll of your scrapes and bruises are healed!", getlfcol(target, CC_GOOD));
|
|
break;
|
|
case OT_S_HEALING:
|
|
default:
|
|
msg("^%cYour wounds close themselves!", getlfcol(target, CC_GOOD));
|
|
break;
|
|
case OT_S_HEALINGMAJ:
|
|
msg("^%cYour injuries are healed!", getlfcol(target, CC_GOOD));
|
|
break;
|
|
}
|
|
} else {
|
|
switch (spellid) {
|
|
case OT_S_HEALINGMIN:
|
|
msg("^%cSome of your scrapes and bruises are healed!", getlfcol(target, CC_GOOD));
|
|
break;
|
|
case OT_S_HEALING:
|
|
default:
|
|
msg("^%cSome of your wounds close themselves!", getlfcol(target, CC_GOOD));
|
|
break;
|
|
case OT_S_HEALINGMAJ:
|
|
msg("^%cYour injuries are partially healed!", getlfcol(target, CC_GOOD));
|
|
break;
|
|
}
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (haslos(player, target->cell)) {
|
|
getlfname(target, buf);
|
|
msg("^%c%s looks healthier!", getlfcol(target, CC_GOOD), buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
if (!donesomething) {
|
|
nothinghappens();
|
|
}
|
|
}
|
|
if (donesomething && isplayer(caster)) {
|
|
// god of death REALLY doesn't like healing.
|
|
angergodmaybe(R_GODDEATH, 40, GA_HERESY);
|
|
}
|
|
// hostile monsters might calm down
|
|
if (!undead && !frompot && donesomething && isplayer(caster) && !isplayer(target) && (getallegiance(target) == AL_HOSTILE)) {
|
|
enum ATTRBRACKET iqb;
|
|
iqb = getattrbracket(getattr(target, A_IQ), A_IQ, NULL);
|
|
if ((iqb >= IQ_ANIMAL) && cansee(target, caster)) {
|
|
if (skillcheckvs(caster, SC_MORALE, 15, target, SC_MORALE, 0)) {
|
|
makepeaceful(target, caster);
|
|
}
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_HEATMETAL) {
|
|
int donesomething = B_FALSE;
|
|
object_t *o,*nexto;
|
|
target = targcell->lf;
|
|
if (target) {
|
|
if (ismetal(target->race->material->id)) {
|
|
if (!isimmuneto(target->flags, DT_HEAT, B_FALSE)) {
|
|
if (isplayer(target)) {
|
|
msg("^%cYou suffer massive burns!", getlfcol(target, CC_BAD) );
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%c%s glows red hot!", getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
losehp(target, roll("6d4"), DT_HEAT, caster, "massive burns");
|
|
donesomething = B_TRUE;
|
|
}
|
|
} else {
|
|
int nburn = 0,ndropobs = 0;
|
|
object_t *dropob[MAXPILEOBS];
|
|
// affect equipped objects
|
|
for (o = target->pack->first ; o ; o = nexto) {
|
|
int amt;
|
|
nexto = o->next;
|
|
amt = isheatable(o);
|
|
|
|
if (ismetal(o->material->id) && amt) {
|
|
int dodam = B_FALSE;
|
|
if (isequippedon(o, BP_WEAPON) || isequippedon(o, BP_SECWEAPON)) {
|
|
nburn++;
|
|
dodam++;
|
|
if (!getequippedob(target->pack, BP_HANDS)) {
|
|
dropob[ndropobs++] = o;
|
|
}
|
|
donesomething = B_TRUE;
|
|
} else if (isequipped(o)) {
|
|
nburn++;
|
|
donesomething = B_TRUE;
|
|
dodam++;
|
|
}
|
|
if (dodam) {
|
|
if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
makehot(o, amt, 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ndropobs && !isimmuneto(target->flags, DT_FIRE, B_FALSE)) {
|
|
int i;
|
|
for (i = 0; i < ndropobs; i++) {
|
|
if (isplayer(target)) {
|
|
char obname[BUFLEN];
|
|
getobname(dropob[i], obname, dropob[i]->amt);
|
|
msg("^BYour %s %s too hot to hold!", obname,
|
|
(dropob[i]->amt == 1) ? "is" : "are");
|
|
}
|
|
drop(dropob[i], dropob[i]->amt);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// affect objects on ground
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
int amt;
|
|
nexto = o->next;
|
|
// damage metal items on the ground
|
|
amt = isheatable(o);
|
|
if (ismetal(o->material->id) && amt) {
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
donesomething = B_TRUE;
|
|
}
|
|
makehot(o, amt, 2);
|
|
if (isplayer(caster)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s glow%s red hot!", obname, OBS1(o));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!donesomething) {
|
|
if (isplayer(caster)) {
|
|
nothinghappens();
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_HEAVENARM) {
|
|
flag_t *f;
|
|
if (!target) target = caster;
|
|
if (lfhasflag(target, F_HEAVENARM)) {
|
|
fizzle(target);
|
|
return B_TRUE;
|
|
}
|
|
f = addtempflag(target->flags, F_HEAVENARM, power*10, NA, NA, "shell of divine armour", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_HOLDPORTAL) {
|
|
object_t *o,*oo;
|
|
cell_t *c;
|
|
condset_t cs;
|
|
int jamamt = 1;
|
|
o = hasobwithflag(targcell->obpile, F_DOOR);
|
|
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// move any objects which are in the way (but not lfs)
|
|
initcondv(&cs, CC_SOLID, B_FALSE, NA,
|
|
CC_NONE);
|
|
for (oo = targcell->obpile->first ; oo ; oo = oo->next) {
|
|
if ((oo != o) && (getmaterialstate(oo->material->id) == MS_SOLID)) {
|
|
c = getrandomadjcell(targcell, &cs, B_ALLOWEXPAND);
|
|
if (c) moveob(oo, c->obpile, ALL);
|
|
}
|
|
}
|
|
|
|
closedoor(NULL, o);
|
|
jamamt = power/2;
|
|
limit(&jamamt, 1, NA);
|
|
addflag(o->flags, F_JAMMED, jamamt, haslos(player, targcell) ? B_TRUE : B_FALSE, 150 + (power*10), 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_HONEMETAL) {
|
|
object_t *o;
|
|
int donesomething = B_FALSE;
|
|
flag_t *f;
|
|
char fullobname[BUFLEN];
|
|
char obname[BUFLEN];
|
|
|
|
if (targob) {
|
|
o = targob;
|
|
} else {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_METAL,
|
|
CC_HASFLAG, B_TRUE, F_DAM,
|
|
CC_NONE);
|
|
// ask for an object
|
|
o = doaskobject(caster->pack, "Hone which metal weapon", NULL, NULL, B_FALSE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, &cs, B_FALSE);
|
|
}
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!wepdullable(o)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
getobname(o, obname, o->amt);
|
|
|
|
if (isplayer(caster)) {
|
|
snprintf(fullobname, BUFLEN, "Your %s", noprefix(obname));
|
|
} else if (cansee(player, caster)) {
|
|
snprintf(fullobname, BUFLEN, "%s%s %s", castername, getpossessive(castername), noprefix(obname));
|
|
} else {
|
|
strcpy(fullobname, "");
|
|
}
|
|
|
|
f = hasflag(o->flags, F_IMMUTABLE);
|
|
if (!f) {
|
|
f = hasflag(o->flags, F_ARMOURPIERCE);
|
|
}
|
|
if (f) {
|
|
if (isplayer(caster)) {
|
|
char obname[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
msg("For some reason, %s is unaffected!", fullobname);
|
|
}
|
|
f->known = B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
f = addtempflag(o->flags, F_ARMOURPIERCE, 5+power, NA, NA, NULL, (power == 3) ? PERMENANT : 20*power);
|
|
if (f) {
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
// fix rust and dulled weapons
|
|
if (isweapon(o)) {
|
|
f = hasflag(o->flags, F_BONUS);
|
|
if (f && (f->val[0] < 0)) {
|
|
killflag(f);
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s seems more effective!", fullobname);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
if (killflagsofid(o->flags, F_RUSTED)) {
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s is no longer rusted!", fullobname);
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
if (donesomething) {
|
|
if (strlen(fullobname)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
if (isplayer(caster)) nothinghappens();
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_HUNGER) {
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
msg("^%cA sudden hunger comes over you!", getlfcol(target, CC_BAD));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%c%s looks ravenous!", getlfcol(target, CC_BAD), lfname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (lfhasflag(target, F_HUNGER)) {
|
|
modhunger(target, HUNGERCONST); // make more hungry
|
|
} else {
|
|
addflag(target->flags, F_HUNGER, HUNGERCONST*2, NA, NA, NULL);
|
|
}
|
|
} else if (spellid == OT_S_HOLYAURA) {
|
|
flag_t *f;
|
|
if (!target) target = caster;
|
|
|
|
f = addtempflag(target->flags, F_HOLYAURA, B_TRUE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
f = addtempflag(target->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_ICECRUST) {
|
|
object_t *wep;
|
|
//enum DAMTYPE dt;
|
|
char obname[BUFLEN];
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// does caster have a weapon?
|
|
wep = getweapon(target);
|
|
if (!wep) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
getobname(wep, obname, wep->amt);
|
|
|
|
if (isplayer(target)) {
|
|
msg("^%cYour %s is covered with ice!", getlfcol(target, CC_GOOD), noprefix(obname));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%c%s%s %s is covered with ice!", getlfcol(target, CC_GOOD), lfname, getpossessive(lfname), noprefix(obname));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
addtempflag(wep->flags, F_FROZEN, B_TRUE, NA, NA, NULL, 10 + power*3);
|
|
|
|
} else if (spellid == OT_S_ICICLE) {
|
|
object_t *o;
|
|
int donesomething = B_FALSE;
|
|
|
|
if (haslos(player, targcell)) {
|
|
msg("A massive icicle rises from the ground!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
// note: we don't actually create the icicle yet, because "getrandomadjcell" will fail
|
|
// for cells which contain impassable objects (ie. the icicle).
|
|
|
|
|
|
// knock lfs away
|
|
if (targcell->lf) {
|
|
cell_t *c;
|
|
c = getrandomadjcell(targcell, &ccwalkable, B_NOEXPAND);
|
|
if (c) {
|
|
knockback(targcell->lf, getdirtowards(targcell, c, NULL, B_FALSE, DT_COMPASS), 1, NULL,
|
|
100+(power*10), B_DOANNOUNCE, B_DODAM);
|
|
} else {
|
|
if (isplayer(targcell->lf)) {
|
|
msg("^%cYou are impaled by an icicle!", getlfcol(targcell->lf, CC_BAD) );
|
|
} else if (cansee(player, targcell->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
msg("^%c%s is impaled by an icicle!", getlfcol(targcell->lf, CC_BAD), lfname);
|
|
}
|
|
losehp(targcell->lf, rolldie(3,power), DT_PIERCE, caster, "impalement on an icicle");
|
|
}
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
// NOW create the icicle
|
|
o = addob(targcell->obpile, "huge icicle");
|
|
if (o) {
|
|
flag_t *f;
|
|
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;
|
|
} else {
|
|
if (haslos(player, targcell)) {
|
|
msg("The icicle vanishes.");
|
|
}
|
|
}
|
|
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
} else if (spellid == OT_S_IDENTIFY) {
|
|
object_t *o;
|
|
if (!target) target = caster;
|
|
|
|
if (targob) {
|
|
o = targob;
|
|
} else {
|
|
// ask for an object
|
|
condset_t cs;
|
|
initcondv(&cs, CC_IDENTIFIED, B_FALSE, NA,
|
|
CC_NONE);
|
|
o = askobject(target->pack, "Identify which object", "You have nothing which needs identifying.", NULL, '\0', &cs, B_FALSE);
|
|
}
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (o->pile->owner && isplayer(o->pile->owner)) {
|
|
if (isidentified(o)) { // already identified?
|
|
nothinghappens();
|
|
return B_TRUE;
|
|
} else {
|
|
char extrabuf[BUFLEN];
|
|
identify(o);
|
|
//getobname(o, buf, o->amt);
|
|
getobnametrue(o, buf, o->amt);
|
|
getobextrainfo(o, extrabuf);
|
|
if (strlen(extrabuf)) strcat(buf, extrabuf);
|
|
|
|
// charges
|
|
msgnocap("%c - %s.",o->letter, buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
// monsters can't id things!
|
|
}
|
|
} else if (spellid == OT_S_IMMOLATE) {
|
|
char targname[BUFLEN],buf[BUFLEN];
|
|
if (!target) {
|
|
target = targcell->lf;
|
|
}
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (hasobofmaterial(targcell->obpile, MT_FIRE)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targname);
|
|
if (rolltohit(caster, target, NULL, NULL, NULL, NULL)) {
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
construct_hit_string(caster, target, castername, targname, targname, NULL, DT_TOUCH, 0, target->maxhp,
|
|
0, B_FALSE, B_FALSE, B_FALSE, B_TRUE, buf);
|
|
msg("%s", buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("^%c%s is engulfed in roaring flames!", getlfcol(target, CC_BAD), targname);
|
|
addobfast(target->cell->obpile, OT_FIREMED);
|
|
}
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
msg("You try to touch %s, but fail.", targname);
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s tries to touch %s, but fails.", castername, targname);
|
|
}
|
|
}
|
|
} 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("^%cOh no! A wave of incalculable evil washes over you!", getlfcol(player, CC_VBAD));
|
|
}
|
|
for (l = caster->cell->map->lf ; l ; l = l->next) {
|
|
if (l != caster) {
|
|
/*
|
|
if (isimmuneto(l->flags, DT_NECROTIC, B_FALSE) ||
|
|
spellresisted(l, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
*/
|
|
if (spellresisted(l, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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, NULL, B_SHOWALL, B_REALRACE);
|
|
snprintf(dambuf, BUFLEN, "%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, B_FALSE) || spellresisted(caster, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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, NULL, B_SHOWALL, B_REALRACE);
|
|
snprintf(dambuf, BUFLEN, "%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);
|
|
// 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) {
|
|
int dam;
|
|
char attackname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
dam = rolldie(power, 4);
|
|
if (power == 1) {
|
|
sprintf(attackname, "a spike of mana");
|
|
} else {
|
|
sprintf(attackname, "%s spikes of mana", numbuf);
|
|
}
|
|
// target takes magical damage
|
|
// always hit
|
|
if (check_for_block(caster, target, dam, DT_MAGIC, 999, attackname, B_RANGED)) {
|
|
} else {
|
|
if (cansee(player, target)) {
|
|
if (power == 1) {
|
|
msg("^%cA spike of mana hits %s.",getlfcol(target, CC_BAD), lfname);
|
|
} else {
|
|
msg("^%c%s spikes of mana hit %s.",getlfcol(target, CC_BAD), numbuf, lfname);
|
|
}
|
|
}
|
|
losehp(target, dam, DT_MAGIC, caster, attackname);
|
|
}
|
|
}
|
|
} 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 = hasflag(o->flags, F_EQUIPPED);
|
|
if (f) {
|
|
if ((f->val[0] == BP_RIGHTFINGER) || (f->val[0] == BP_LEFTFINGER)) {
|
|
if (isplayer(caster)) {
|
|
getobname(o, buf, 1);
|
|
msg("Your %s slides off your %s!", buf, getbodypartname(caster, 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(caster, 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_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
if (!target) target = caster;
|
|
m = target->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) || (getcelldist(target->cell, c) <= range)) {
|
|
if (blessed == B_CURSED) {
|
|
if (!c->known) {
|
|
// set unknown cells to a random glyph!
|
|
if (onein(3)) { // 33% chance of the correct thing
|
|
setcellknown(c, MAXOF(PR_ADEPT, getskill(target, SK_CARTOGRAPHY)));
|
|
} else if (onein(2)) { // otherwise 50/50 .
|
|
setcellknown_fake(c, getcellempty(c));
|
|
} else { // otherwise solid
|
|
setcellknown_fake(c, getcellsolid(c));
|
|
}
|
|
}
|
|
} else {
|
|
setcellknown(c, MAXOF(PR_ADEPT, getskill(target, SK_CARTOGRAPHY)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
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("^%cYou draw health from nearby metal!", getlfcol(caster, CC_GOOD));
|
|
} else if (cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_FALSE;
|
|
msg("^%c%s draws health from nearby metal!", getlfcol(caster, CC_GOOD),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, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
removedeadobs(caster->pack);
|
|
|
|
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, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(o);
|
|
}
|
|
}
|
|
}
|
|
// destroy objects right away
|
|
removedeadobs(targcell->obpile);
|
|
if (targcell->lf && !spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
// 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, caster);
|
|
if (hasflag(o->flags, F_DEAD)) {
|
|
totalmass += getobmass(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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_MINDSCAN) {
|
|
int failed = B_FALSE;
|
|
if (isplayer(caster)) {
|
|
if (targcell && haslos(caster, targcell) && haslf(targcell)) {
|
|
char targname[BUFLEN];
|
|
lifeform_t *oldplayer;
|
|
|
|
// calc view
|
|
setlosdirty(targcell->lf);
|
|
//precalclos(targcell->lf);
|
|
|
|
// temporarily change player pointer...
|
|
oldplayer = player;
|
|
player = targcell->lf;
|
|
|
|
//
|
|
getlfname(targcell->lf, targname);
|
|
snprintf(buf, BUFLEN, "Mindscanning %s, 'v' to view info, ESC to quit.", targname);
|
|
doexplain(buf);
|
|
|
|
// restore player pointer
|
|
player = oldplayer;
|
|
//showlfstats(where->lf, B_TRUE);
|
|
|
|
needredraw = B_TRUE;
|
|
statdirty = B_TRUE;
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_MINDSHIELD) {
|
|
flag_t *f;
|
|
f = addtempflag(caster->flags, F_MINDSHIELD, B_TRUE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_MINDWHIP) {
|
|
int fail = B_FALSE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(target)) {
|
|
msg("^%cA lash of psychic power assaults your mind!",getlfcol(target, CC_BAD));
|
|
} else if (isplayer(caster)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%cYou psycically lash %s%s mind!", getlfcol(target, CC_BAD),
|
|
lfname, getpossessive(lfname));
|
|
}
|
|
if (getattrbracket(getattr(target, A_IQ), A_IQ, NULL) <= AT_VLOW) {
|
|
fail = B_TRUE;
|
|
} else if (!ischarmable(target) && (reason != E_ALREADYUSING)) {
|
|
fail = B_TRUE;
|
|
}
|
|
if (fail) {
|
|
if (isplayer(caster)) {
|
|
char tname[BUFLEN];
|
|
getlfname(target, tname);
|
|
msg("%s %s unaffected.", tname, isplayer(target) ? "are" : "is");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
losehp(target, rnd(2,6), DT_DIRECT, caster, "a psychic lash");
|
|
} else if (spellid == OT_S_MIRRORIMAGE) {
|
|
int i,ndone = 0;
|
|
int seen = B_FALSE;
|
|
for (i = 0; i < power; i++) {
|
|
lifeform_t *lf;
|
|
job_t *j;
|
|
object_t *o;
|
|
// create a mirror image
|
|
targcell = real_getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND, LOF_NEED, NULL, caster);
|
|
if (!targcell) break;
|
|
lf = clonelf(caster, targcell);
|
|
if (!lf) break;
|
|
if (isplayer(caster) || cansee(player, lf)) {
|
|
seen = B_TRUE;
|
|
}
|
|
killflagsofid(lf->flags, F_CORPSETYPE);
|
|
killflagsofid(lf->flags, F_EXTRACORPSE);
|
|
addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL);
|
|
addflag(lf->flags, F_PHANTASM, caster->hp, NA, NA, NULL);
|
|
|
|
// copy caster's job
|
|
j = getjob(caster);
|
|
if (j) {
|
|
givejob(lf, j->id);
|
|
}
|
|
addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL);
|
|
lf->created = B_FALSE; // to avoid "the xxx puts on armour" messages
|
|
// copy caster's weapons & armour
|
|
for (o = caster->pack->first ; o ; o = o->next) {
|
|
flag_t *equipflag;
|
|
equipflag = isequipped(o);
|
|
if (equipflag) {
|
|
object_t *newob;
|
|
newob = addobfast(lf->pack,o->type->id);
|
|
if (newob) {
|
|
if (isweapon(newob) && canweild(lf, newob)) {
|
|
weild(lf, newob);
|
|
} else if (canwear(lf, newob, equipflag->val[0])) {
|
|
wear(lf, newob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
killflagsofid(lf->flags, F_NOTIME);
|
|
lf->created = B_TRUE;
|
|
|
|
addflag(lf->flags, F_NODEATHSPEECH, B_TRUE, NA, NA, NULL);
|
|
petify(lf, caster);
|
|
setfollowdistance(lf, 1, 2); // stay close
|
|
|
|
addflag(lf->flags, F_SUMMONEDBY, caster->id, PERMENANT, NA, NULL);
|
|
|
|
ndone++;
|
|
}
|
|
|
|
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else if (seen) {
|
|
char copytext[BUFLEN];
|
|
if (ndone == 1) {
|
|
sprintf(copytext, "A duplicate");
|
|
} else {
|
|
sprintf(copytext, "%d duplicates", ndone);
|
|
}
|
|
msg("%s of %s appear%s!", copytext, castername, (ndone == 1) ? "s" : "");
|
|
}
|
|
|
|
} else if (spellid == OT_S_NEGATECOLD) {
|
|
flag_t *f;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = addtempflag(caster->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_NEGATEFIRE) {
|
|
flag_t *f;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = addtempflag(caster->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_SLOWMISSILES) {
|
|
if (!target) {
|
|
target = caster;
|
|
}
|
|
if (isplayer(target)) {
|
|
msg("^gYou attune your mind to deflect incoming projectiles.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_NULLIFY) {
|
|
flag_t *retflag[MAXCANDIDATES],*poss[MAXCANDIDATES],*f;
|
|
int nretflags,i,ndone = 0,nposs;
|
|
char announcebuf[BUFLEN];
|
|
int seen;
|
|
char lfname[BUFLEN];
|
|
strcpy(announcebuf, "");
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, lfname);
|
|
|
|
seen = cansee(player, target);
|
|
|
|
if (isplayer(target)) {
|
|
msg("^BYou are engulfed in an anti-magic field!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (seen) {
|
|
msg("^B%s is engulfed in an anti-magic field!", lfname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
// resist ?
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
if (isplayer(target)) {
|
|
msg("Luckily, you shrug off its effects.");
|
|
} else if (seen) {
|
|
msg("%s seems unaffected.", lfname);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// lose some mana
|
|
if (target->mp > 0) {
|
|
int howmuch;
|
|
howmuch = pctof(power*10, target->maxmp);
|
|
losemp(target, howmuch);
|
|
if (isplayer(target)) msg("^wYour mana drains away!");
|
|
ndone++;
|
|
}
|
|
|
|
if (killflagsofid(target->flags, F_REVIVETIMER)) {
|
|
ndone++;
|
|
if (seen && !isplayer(target)) {
|
|
msg("^%c%s%s ability to regenerate is nullified!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname));
|
|
}
|
|
}
|
|
|
|
// now stop active spells or abilities
|
|
while (ndone < power) {
|
|
nposs = 0;
|
|
getflags(target->flags, retflag, &nretflags, F_BOOSTSPELL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
int ok = B_TRUE;
|
|
// exception: don't stop animals etc from flying
|
|
if ((retflag[i]->val[0] == OT_S_FLIGHT) && lfhasflag(target, F_NATURALFLIGHT)) {
|
|
ok = B_FALSE;
|
|
}
|
|
if (ok) {
|
|
poss[nposs++] = retflag[i];
|
|
}
|
|
}
|
|
if (nposs) {
|
|
enum OBTYPE sid;
|
|
objecttype_t *ot;
|
|
sid = (poss[rnd(0,nposs-1)])->val[0];
|
|
ot = findot(sid);
|
|
stopspell(target, sid);
|
|
ndone++;
|
|
if (seen && !isplayer(target)) {
|
|
msg("^%c%s%s active '%s' spell is nullified!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname), ot->name);
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// now remove the ability to cast them!
|
|
while (ndone < power) {
|
|
// for player:
|
|
// remove memorised spells
|
|
// for monsters:
|
|
// remove racial spells AND abilities
|
|
//
|
|
// abilities/spells conferred by objects are not affected.
|
|
nposs = 0;
|
|
getflags(target->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
int ok = B_TRUE;
|
|
if (isplayer(target)) {
|
|
if (retflag[i]->id == F_CANWILL) {
|
|
ok = B_FALSE;
|
|
} else if (retflag[i]->lifetime != PERMENANT) {
|
|
ok = B_FALSE;
|
|
}
|
|
} else {
|
|
if ((retflag[i]->lifetime != FROMRACE) && (retflag[i]->lifetime != PERMENANT)) {
|
|
ok = B_FALSE;
|
|
}
|
|
}
|
|
// for player, only CANCAST SPELLs can be nullified.
|
|
if (ok && isplayer(target)) {
|
|
if (retflag[i]->id == F_CANCAST) {
|
|
objecttype_t *ot;
|
|
ot = findot(retflag[i]->val[0]);
|
|
if (ot->obclass->id != OC_SPELL) {
|
|
ok = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
poss[nposs++] = retflag[i];
|
|
}
|
|
}
|
|
|
|
if (nposs) {
|
|
enum OBTYPE sid;
|
|
objecttype_t *ot;
|
|
f = poss[rnd(0,nposs-1)];
|
|
sid = f->val[0];
|
|
ot = findot(sid);
|
|
if (seen && !isplayer(target)) {
|
|
msg("^%c%s can no longer %s '%s'!", getlfcol(target, CC_BAD), lfname,
|
|
(f->id == F_CANCAST) ? "cast" : "use the ability", ot->name);
|
|
}
|
|
killflag(f);
|
|
ndone++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
if (isplayer(target)) {
|
|
msg("You are unaffected.");
|
|
} else if (seen) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("%s is unaffected.", lfname);
|
|
}
|
|
}
|
|
if (isplayer(caster)) angergodmaybe(R_GODMAGIC, 10, GA_HERESY);
|
|
} else if (spellid == OT_S_OBJECTGROWTH) {
|
|
enum OBTYPE newoid = OT_NONE;
|
|
enum LFSIZE newsize = SZ_ANY;
|
|
enum CELLTYPE newcelltype = CT_NONE;
|
|
skill_t *obsk = NULL;
|
|
char obname[BUFLEN],newobname[BUFLEN];
|
|
int seen;
|
|
|
|
if (targcell->obpile->first) {
|
|
targob = doaskobject(targcell->obpile, "Target which object", NULL, NULL, B_TRUE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, NULL, B_FALSE);
|
|
}
|
|
|
|
if (!targob) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
targcell = getoblocation(targob);
|
|
if (haslos(player, targcell)) {
|
|
seen = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
getobname(targob, obname, 1);
|
|
obsk = getobskill(targob->flags);
|
|
if (isweapon(targob) && (obsk->id == SK_LONGBLADES)) {
|
|
newoid = OT_GREATSWORD;
|
|
} else if (isweapon(targob) && (obsk->id == SK_SHORTBLADES)) {
|
|
newoid = OT_GREATSWORD;
|
|
} else if (isweapon(targob) && (obsk->id == SK_CLUBS)) {
|
|
newoid = OT_GREATCLUB;
|
|
} else if (isweapon(targob) && (obsk->id == SK_AXES)) {
|
|
newoid = OT_GREATAXE;
|
|
} else if (targob->type->obclass->id == OC_ROCK) {
|
|
newoid = OT_BOULDER;
|
|
} else if ((targob->type->obclass->id == OC_ARMOUR) &&
|
|
hasflag(targob->flags, F_MULTISIZE) &&
|
|
(getarmoursize(targob) < SZ_LARGE)) {
|
|
newsize = getarmoursize(targob) + 1;
|
|
} else if (targob->type->obclass->id == OC_POTION) {
|
|
newcelltype = CT_WALLGLASS;
|
|
} else {
|
|
flag_t *f;
|
|
f = hasflag(targob->flags, F_GROWSTO);
|
|
if (f) {
|
|
if (f->val[1] == VT_OB) {
|
|
newoid = f->val[0];
|
|
} else { // ie. cell
|
|
newcelltype = f->val[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newsize != SZ_ANY) {
|
|
resizeobject(targob, newsize);
|
|
getobname(targob, newobname, 1);
|
|
getobname(targob, newobname, 1);
|
|
if (seen) msg("%s grows into %s!", obname, newobname);
|
|
} else if (newcelltype != CT_NONE) {
|
|
cell_t *where;
|
|
if (seen) {
|
|
celltype_t *ct;
|
|
ct = findcelltype(newcelltype);
|
|
msg("%s grows into %s %s!", obname, needan(ct->name) ? "an" : "a", ct->name);
|
|
}
|
|
where = getoblocation(targob);
|
|
killob(targob);
|
|
setcelltype(where, newcelltype);
|
|
} else if (newoid == targob->id) {
|
|
if (seen) msg("%s shudders for a moment.", obname);
|
|
} else if (newoid != OT_NONE) {
|
|
obpile_t *op;
|
|
op = targob->pile;
|
|
killob(targob);
|
|
targob = addobfast(op, newoid);
|
|
if (targob) {
|
|
getobname(targob, newobname, 1);
|
|
if (seen) msg("%s grows into %s!", obname, newobname);
|
|
if (targob->type->id == OT_WATERDEEP) {
|
|
cell_t *where;
|
|
where = getoblocation(targob);
|
|
setcelltype(where, CT_LOWFLOOR); // lower floor so water doesn't spread
|
|
}
|
|
} else {
|
|
if (seen) msg("%s shudders then explodes!", obname);
|
|
}
|
|
} else {
|
|
if (seen) msg("%s shudders for a moment.", obname);
|
|
}
|
|
} else if (spellid == OT_S_OBJECTSHRINK) {
|
|
enum OBTYPE newoid = OT_NONE;
|
|
char obtocreate[BUFLEN];
|
|
enum LFSIZE newsize = SZ_ANY;
|
|
enum CELLTYPE newcelltype = CT_NONE;
|
|
skill_t *obsk = NULL;
|
|
char obname[BUFLEN],newobname[BUFLEN];
|
|
int seen;
|
|
|
|
if (targcell->obpile->first) {
|
|
targob = doaskobject(targcell->obpile, "Target which object", NULL, NULL, B_TRUE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, NULL, B_FALSE);
|
|
}
|
|
strcpy(obtocreate, "");
|
|
|
|
if (!targob) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
targcell = getoblocation(targob);
|
|
if (haslos(player, targcell)) {
|
|
seen = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
getobname(targob, obname, 1);
|
|
obsk = getobskill(targob->flags);
|
|
if (isweapon(targob) && (obsk->id == SK_LONGBLADES)) {
|
|
newoid = OT_DAGGER;
|
|
} else if (isweapon(targob) && (obsk->id == SK_SHORTBLADES)) {
|
|
newoid = OT_KNIFE;
|
|
} else if (isweapon(targob) && (obsk->id == SK_CLUBS)) {
|
|
newoid = OT_STICK;
|
|
} else if (isweapon(targob) && (obsk->id == SK_AXES)) {
|
|
newoid = OT_KNIFE;
|
|
} else if ((targob->type->obclass->id == OC_ARMOUR) &&
|
|
hasflag(targob->flags, F_MULTISIZE) &&
|
|
(getarmoursize(targob) > SZ_TINY)) {
|
|
newsize = getarmoursize(targob) - 1;
|
|
} else if (targob->type->obclass->id == OC_POTION) {
|
|
newoid = OT_BROKENGLASS;
|
|
} else {
|
|
flag_t *f;
|
|
f = hasflag(targob->flags, F_SHRINKSTO);
|
|
if (f) {
|
|
if (f->val[1] == VT_OB) {
|
|
if (strlen(f->text)) {
|
|
strcpy(obtocreate, f->text);
|
|
} else {
|
|
newoid = f->val[0];
|
|
}
|
|
} else { // ie. cell
|
|
newcelltype = f->val[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (newsize != SZ_ANY) {
|
|
resizeobject(targob, newsize);
|
|
getobname(targob, newobname, 1);
|
|
getobname(targob, newobname, 1);
|
|
if (seen) msg("%s shrinks into %s!", obname, newobname);
|
|
} else if (newcelltype != CT_NONE) {
|
|
cell_t *where;
|
|
if (seen) {
|
|
celltype_t *ct;
|
|
ct = findcelltype(newcelltype);
|
|
msg("%s shrinks into %s %s!", obname, needan(ct->name) ? "an" : "a", ct->name);
|
|
}
|
|
where = getoblocation(targob);
|
|
killob(targob);
|
|
setcelltype(where, newcelltype);
|
|
} else if (newoid == targob->id) {
|
|
if (seen) msg("%s shudders for a moment.", obname);
|
|
} else if (strlen(obtocreate) || (newoid != OT_NONE)) {
|
|
obpile_t *op;
|
|
op = targob->pile;
|
|
killob(targob);
|
|
if (strlen(obtocreate)) {
|
|
targob = addob(op, obtocreate);
|
|
} else {
|
|
targob = addobfast(op, newoid);
|
|
}
|
|
if (targob) {
|
|
getobname(targob, newobname, 1);
|
|
if (seen) msg("%s shrinks into %s!", obname, newobname);
|
|
if (targob->type->id == OT_WATERDEEP) {
|
|
cell_t *where;
|
|
where = getoblocation(targob);
|
|
setcelltype(where, CT_LOWFLOOR); // lower floor so water doesn't spread
|
|
}
|
|
} else {
|
|
if (seen) msg("%s shudders then implodes!", obname);
|
|
}
|
|
} else {
|
|
if (seen) msg("%s shudders for a moment.", obname);
|
|
}
|
|
} else if ((spellid == OT_S_ACCELMETAL) || (spellid == OT_S_PROPELMISSILE)) {
|
|
char oidbuf[BUFLEN];
|
|
char obname[BUFLEN];
|
|
flag_t *f;
|
|
if (!targob) {
|
|
// ask for an object
|
|
if (spellid == OT_S_ACCELMETAL) {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_METAL,
|
|
CC_NONE);
|
|
// TODO: handle ai casting
|
|
targob = askobject(caster->pack, "Accelerate which metal object", NULL, NULL, 't', &cs, B_FALSE);
|
|
} else { // ie. propel
|
|
if (isplayer(caster)) {
|
|
targob = askobject(caster->pack, "Propel which object", NULL, NULL, 't', NULL, B_FALSE);
|
|
} else {
|
|
lifeform_t *target;
|
|
target = gettargetlf(caster);
|
|
if (target) {
|
|
targob = getbestthrowmissile(caster, target);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!targob || ((spellid == OT_S_ACCELMETAL) && !ismetal(targob->material->id)) ) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getobname(targob, obname, 1);
|
|
|
|
if ((spellid == OT_S_PROPELMISSILE) && (getmaxthrowrange(caster, targob) <= 0)) {
|
|
if (isplayer(caster)) {
|
|
msg("%s is too heavy for you to throw!");
|
|
}
|
|
return B_FALSE; // don't cost mana
|
|
}
|
|
|
|
|
|
if (isequipped(targob) && iscursed(targob)) {
|
|
msg("Your %s appears to be stuck to you!", noprefix(obname));
|
|
targob->blessknown = B_TRUE;
|
|
return B_TRUE;
|
|
}
|
|
|
|
sprintf(oidbuf, "%ld", targob->id);
|
|
f = addflag(caster->flags, F_THROWING, B_TRUE, NA, NA, oidbuf);
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE;
|
|
killflagsofid(caster->flags, F_THROWING);
|
|
|
|
// 5 is the same as AT_VHIGH strength
|
|
addflag(targob->flags, F_MISSILEALWAYSDIES, B_TRUE, NA, NA, NULL);
|
|
addflag(caster->flags, F_TKTHROW, A_IQ, SK_SS_AIR, NA, NULL);
|
|
f = addflag(caster->flags, F_ACCURACYMOD, 50, NA, NA, NULL);
|
|
real_fireat(caster, targob, 1, targcell, 4+power, NULL, B_TRUE, spellid, NULL);
|
|
killflag(f);
|
|
killflagsofid(caster->flags, F_TKTHROW);
|
|
} else if (spellid == OT_S_TRAVEL) {
|
|
regionthing_t *poss[MAXCANDIDATES],*rt;
|
|
region_t *srcregion = NULL;
|
|
int nposs,i,depth;
|
|
char ch = 'a';
|
|
cell_t *dstcell = NULL;
|
|
if (!isplayer(caster)) return B_TRUE;
|
|
// ask which region to go to
|
|
getbranchlinks(poss, &nposs, RT_BRANCHLINK, RT_HABITAT, RT_NONE);
|
|
if (!nposs) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
initprompt(&prompt, "Where do you wish to travel?");
|
|
prompt.maycancel = B_TRUE;
|
|
|
|
for (i = 0; i < nposs; i++) {
|
|
char choicetext[BUFLEN];
|
|
branch_t *destbranch;
|
|
if (poss[i]->whatkind == RT_BRANCHLINK) {
|
|
destbranch = findbranch(poss[i]->value);
|
|
strcpy(choicetext, destbranch->name);
|
|
} else if (poss[i]->whatkind == RT_HABITAT) {
|
|
habitat_t *h;
|
|
h = findhabitat(poss[i]->value);
|
|
strcpy(choicetext, h->name);
|
|
}
|
|
capitalise(choicetext);
|
|
addchoice(&prompt, ch++, choicetext, NULL, poss[i], NULL);
|
|
}
|
|
|
|
ch = getchoice(&prompt);
|
|
rt = (regionthing_t *)prompt.result;
|
|
if (!rt) {
|
|
msg("Cancelled.");
|
|
return B_TRUE;
|
|
}
|
|
depth = rt->depth;
|
|
|
|
// find region containing this link.
|
|
findregionthing(rt->id, &srcregion);
|
|
|
|
if (srcregion) {
|
|
map_t *srcmap;
|
|
|
|
if (isplayer(caster)) {
|
|
msg("There is a blinding flash of light...");
|
|
wrefresh(msgwin);
|
|
}
|
|
|
|
// does the map contining the link exist?
|
|
srcmap = findregionmap(srcregion->id, depth);
|
|
if (!srcmap) {
|
|
// we'll need to create the map.
|
|
srcmap = addmap();
|
|
createmap(srcmap, depth, srcregion, NULL, D_NONE, NULL);
|
|
}
|
|
|
|
if (rt->whatkind == RT_BRANCHLINK) {
|
|
object_t *dstob = NULL;
|
|
region_t *destregion = NULL;
|
|
// find the branchlink object. ie. the stairs/portal which goes to
|
|
// the given region.
|
|
destregion = findregionbytype(rt->value);
|
|
dstob = findmapobwithflagval(srcmap, F_CLIMBABLE, NA, destregion->id, NA, NULL);
|
|
assert(dstob);
|
|
dstcell = getoblocation(dstob);
|
|
} else if (rt->whatkind == RT_HABITAT) {
|
|
// on top of the 'up' staircase
|
|
dstcell = findobinmap(srcmap, srcmap->habitat->upstairtype);
|
|
if (!dstcell) {
|
|
dstcell = getrandomcell(srcmap);
|
|
}
|
|
}
|
|
} else {
|
|
msg("srcregion doesnt exist!");
|
|
}
|
|
|
|
if (dstcell && !cellwalkable(caster, dstcell, NULL)) {
|
|
dstcell = real_getrandomadjcell(dstcell, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL);
|
|
}
|
|
|
|
if (dstcell) {
|
|
// make sure it's walkable
|
|
teleportto(caster, dstcell, B_FALSE);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_PLANESHIFT) {
|
|
map_t *m;
|
|
target = caster;
|
|
if (isplayer(target)) {
|
|
region_t *r;
|
|
char ch = 'a';
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
// ask for region
|
|
initprompt(&prompt, "Where do you wish to travel?");
|
|
prompt.maycancel = B_TRUE;
|
|
|
|
for (r = firstregion ; r ; r = r->next) {
|
|
branch_t *rt;
|
|
rt = r->rtype;
|
|
addchoice(&prompt, ch++, rt->name, NULL, r, NULL);
|
|
}
|
|
|
|
ch = getchoice(&prompt);
|
|
r = (region_t *)prompt.result;
|
|
// find first map in region
|
|
m = findregionmap(r->id, 1);
|
|
} else {
|
|
if (cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
// alwyas go realm of gods
|
|
m = heaven;
|
|
}
|
|
if (m) {
|
|
condset_t cs;
|
|
// find random free cell in map
|
|
//targcell = getrandomcell(m);
|
|
//targcell = getrandomroomcell(m, ANYROOM, WE_WALKABLE);
|
|
initcondv(&cs, CC_ISROOM, B_TRUE, NA,
|
|
CC_WALKABLEFOR, B_TRUE, target->id,
|
|
CC_DANGEROUSFOR, B_FALSE, target->id,
|
|
CC_NONE);
|
|
targcell = getcell_cond(m, &ccwalkableroom);
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
teleportto(target, targcell, B_TRUE);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_PLANTWALK) {
|
|
int x,y;
|
|
cell_t *poss[MAX_MAPW*MAX_MAPH];
|
|
object_t *srcob;
|
|
int nposs = 0;
|
|
srcob = hasobofclass(targcell->obpile, OC_FLORA);
|
|
if (!srcob) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// after this point the spell always counts as successful
|
|
// (ie. costs mana)
|
|
if (isplayer(caster)) {
|
|
char obname[BUFLEN];
|
|
getobname(srcob, obname, 1);
|
|
msg("You touch %s.", obname);
|
|
} else if (cansee(player, caster)) {
|
|
char obname[BUFLEN];
|
|
getobname(srcob, obname, 1);
|
|
msg("%s touches %s.", castername, obname);
|
|
}
|
|
|
|
for (y = 0; y < caster->cell->map->h; y++) {
|
|
for (x = 0; x < caster->cell->map->w; x++) {
|
|
cell_t *c;
|
|
c = getcellat(caster->cell->map, x, y);
|
|
if (c && (c != targcell)) {
|
|
if ((power == 1) && hasob(c->obpile, srcob->type->id)) {
|
|
poss[nposs++] = c;
|
|
} else if ((power == 2) && hasobofclass(c->obpile, OC_FLORA)) {
|
|
poss[nposs++] = c;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// no possible destinations?
|
|
if (!nposs) {
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
nothinghappens();
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (isplayer(caster)) {
|
|
int i;
|
|
// reveal all potential locations
|
|
for (i = 0; i < nposs; i++) {
|
|
setcellknown(poss[i], PR_EXPERT);
|
|
}
|
|
// then ask to select one
|
|
targcell = real_askcoords("Travel where?", "Plantwalk->", TT_SPECIFIED, player, 0, UNLIMITED, LOF_DONTNEED, B_FALSE, poss, nposs);
|
|
if (!hasobofclass(targcell->obpile, OC_FLORA)) {
|
|
targcell = NULL;
|
|
}
|
|
} else {
|
|
targcell = poss[rnd(0,nposs-1)];
|
|
}
|
|
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
// purposely returning false here, since we've already revealed
|
|
// some information to the caster.
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (!cellwalkable(caster, targcell, NULL)) {
|
|
targcell = real_getrandomadjcell(targcell, &ccwalkable, B_NOEXPAND, LOF_DONTNEED, NULL, NULL);
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// teleport to destination.
|
|
teleportto(caster, targcell, B_FALSE);
|
|
} else if (spellid == OT_S_PARALYZE) {
|
|
int howlong;
|
|
int saved = B_FALSE;
|
|
target = targcell->lf;
|
|
|
|
if (lfhasflag(target, F_PARALYZED)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isimmuneto(target->flags, DT_PETRIFY, B_FALSE)) {
|
|
saved = B_TRUE;
|
|
} else if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
saved = B_TRUE;
|
|
} else if (skillcheck(target, SC_STR, 100 + (power*5), 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;
|
|
target = targcell->lf;
|
|
|
|
if (lfhasflag(target, F_PAIN) || isundead(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
failed = B_TRUE;
|
|
} else if (skillcheck(target, SC_CON, 100 + (power*5), 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/4);
|
|
addtempflag(target->flags, F_PAIN, DT_MAGIC, NA, NA, "1d3", howlong);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_PETRIFY) {
|
|
target = targcell->lf;
|
|
|
|
// some thigns can't be stoned
|
|
if (!lfcanbestoned(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else if (lfhasflag(target, F_BEINGSTONED)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// savingthrow
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE) || skillcheck(target, SC_CON, 100 + power*5, 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;
|
|
}
|
|
// power determines how long before stoning.
|
|
// power 1 = 6 turns
|
|
// power 10 = 3 turns
|
|
addflag(target->flags, F_BEINGSTONED, 8-(power/2), NA, NA, NULL);
|
|
} else if (spellid == OT_S_POISONBOLT) {
|
|
char lfname[BUFLEN];
|
|
// 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, 90 + (power*10), 0)) {
|
|
// miss
|
|
if (cansee(player, target)) {
|
|
msg("A glob of venom misses %s.",lfname);
|
|
}
|
|
} else {
|
|
// hit
|
|
if (cansee(player, target)) {
|
|
msg("^%cA glob of venom hits %s.",getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
if (!isimmuneto(target->flags, DT_POISON, B_FALSE)) {
|
|
poison(target, power*3, P_VENOM, (power/4)+1, "a glob of venom",
|
|
caster ? caster->race->id : R_NONE, B_TRUE);
|
|
}
|
|
}
|
|
}
|
|
if (isplayer(caster)) god_usepoison_response();
|
|
} else if (spellid == OT_S_POSSESSION) {
|
|
char targname[BUFLEN];
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
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;
|
|
snprintf(buf, BUFLEN, "You may be stuck in %s%s body - proceed?", targname,
|
|
getpossessive(targname));
|
|
ch = askchar(buf, "yn", "n", B_TRUE, B_FALSE);
|
|
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;
|
|
case E_ROBOT:
|
|
msg("Robots are immune to charming.");
|
|
break;
|
|
case E_ALREADYUSING:
|
|
msg("%s is already charmed by another!", targname);
|
|
break;
|
|
default:
|
|
msg("You cannot possesss %s.", targname);
|
|
break;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// saving throw....
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
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_PRECOGNITION) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
f = addtempflag(caster->flags, F_EVASION, 5, NA, NA, "precognition", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_MFEEDBACK) {
|
|
int dam,iq;
|
|
enum ATTRBRACKET iqb;
|
|
char targetname[BUFLEN];
|
|
int fail = B_FALSE;
|
|
if (!target) {
|
|
target = targcell->lf;
|
|
}
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
iq = getattr(target, A_IQ);
|
|
iqb = getattrbracket(iq, A_IQ, NULL);
|
|
// not smart enough
|
|
if (iqb <= AT_EXLOW) {
|
|
fail = B_TRUE;
|
|
} else if (!ischarmable(target) && (reason != E_ALREADYUSING)) {
|
|
fail = B_TRUE;
|
|
}
|
|
|
|
if (fail) {
|
|
if (isplayer(caster)) {
|
|
char tname[BUFLEN];
|
|
getlfname(target, tname);
|
|
msg("%s %s unaffected.", tname, isplayer(target) ? "are" : "is");
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
dam = rnd((iq/5),(iq/4));
|
|
losehp(target, dam, DT_DIRECT, caster, "a mental feedback loop");
|
|
getlfname(target, targetname);
|
|
if (isplayer(target)) {
|
|
msg("^%cYour brain is blasted!",getlfcol(target, CC_BAD) );
|
|
} else if (cansee(player, target)) {
|
|
msg("^%c%s%s brain is blasted!",getlfcol(target, CC_BAD), targetname, getpossessive(targetname));
|
|
}
|
|
} else if (spellid == OT_S_PSIBLAST) {
|
|
int range;
|
|
int x,y;
|
|
char buf[BUFLEN];
|
|
if (caster && !targcell) targcell = caster->cell;
|
|
range = 3;
|
|
|
|
sprintf(buf, "%s emit%s a blast of psionic power!",castername,isplayer(caster) ? "" : "s");
|
|
animradial(targcell, range, '}', C_CYAN, DT_COMPASS, buf, "Something emits a radial blast of psionic power!");
|
|
|
|
if (isplayer(caster) || haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
for (y = targcell->y - range ; y <= targcell->y + range; y++) {
|
|
for (x = targcell->x - range ; x <= targcell->x + range; x++) {
|
|
cell_t *c;
|
|
c = getcellat(targcell->map, x,y);
|
|
if (c && (getcelldist(targcell, c) <= range)) {
|
|
if (c->lf && (c->lf != caster) && haslof(targcell, c, B_FALSE, NULL)) {
|
|
stun(c->lf, 4+power);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} 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_PSYSHOVE) {
|
|
if (targcell->lf) {
|
|
float targweight = 0,lfweight = 0;
|
|
char tname[BUFLEN];
|
|
int pushpower, dirtowards, diraway;
|
|
int pushcaster = B_FALSE;
|
|
cell_t *cellaway;
|
|
|
|
target = targcell->lf;
|
|
|
|
pushpower = power+1;
|
|
|
|
getlfname(target, tname);
|
|
targweight = getlfweight(target, B_WITHOBS);
|
|
lfweight = getlfweight(caster, B_WITHOBS);
|
|
|
|
if (isplayer(caster)) {
|
|
msg("You psychically push against %s!", tname);
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s psychically pushes against %s!", castername, tname);
|
|
}
|
|
|
|
dirtowards = whichwayto(caster->cell, targcell, NULL, B_FALSE);
|
|
diraway = diropposite(dirtowards);
|
|
cellaway = getcellindir(target->cell, dirtowards);
|
|
|
|
if (lfweight < targweight) {
|
|
pushcaster = B_TRUE;
|
|
} else if (!cellaway) {
|
|
pushcaster = B_TRUE;
|
|
} else if (issolid(cellaway) || hascloseddoor(cellaway)) {
|
|
pushcaster = B_TRUE;
|
|
}
|
|
|
|
if (pushcaster) {
|
|
// push caster back
|
|
msg("%s %s backwards through the air!", castername,
|
|
isplayer(caster) ? "sail" : "sails");
|
|
knockback(caster, diraway, pushpower, caster, NA, B_NOANNOUNCE, B_NODAM); // never fall
|
|
} else {
|
|
// push them away, but airborne people don't fall
|
|
knockback(target, dirtowards, pushpower, caster, 15*power, B_DOANNOUNCE, B_NODAM); // ie. 45 max falldiff
|
|
}
|
|
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else if (spellid == OT_S_PULLMETAL) {
|
|
int donesomething = B_FALSE;
|
|
|
|
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) && isequipped(o)) {
|
|
if (!isequippedon(o, BP_WEAPON) && !isequippedon(o, BP_SECWEAPON)) {
|
|
gotmetal = B_TRUE;
|
|
metalweight += getobmass(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 (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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (targcell->obpile->first) { // no lifeform there
|
|
targob = NULL;
|
|
// select object from cell...
|
|
targob = askobject(targcell->obpile, "Target which object", NULL, NULL, '\0', NULL, B_FALSE);
|
|
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) + getobmass(targob) <= getmaxcarryweight(caster)) {
|
|
// move it to the player
|
|
pullobto(targob, caster);
|
|
} else {
|
|
// move player to it
|
|
pullnextto(caster, targcell);
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
} 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);
|
|
|
|
limit(&min, 1, caster->cell->map->region->rtype->maxdepth);
|
|
limit(&max, 1, caster->cell->map->region->rtype->maxdepth);
|
|
if (min < 1) min = 1;
|
|
|
|
// ask which level
|
|
snprintf(query, BUFLEN, "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);
|
|
return B_TRUE;
|
|
} else {
|
|
cell_t *srccell;
|
|
object_t *srcportal,*dstportal;
|
|
|
|
// find adjacent cell for portal
|
|
srccell = real_getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND, LOF_NEED, NULL, caster);
|
|
if (!srccell) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
// create the source portal
|
|
srcportal = addobfast(srccell->obpile, OT_PORTAL);
|
|
assert(srcportal);
|
|
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);
|
|
}
|
|
|
|
|
|
dstportal = linkportaltodepth(srcportal, newdepth);
|
|
|
|
setobcreatedby(dstportal, caster);
|
|
|
|
//newmap = findmapofdepth(newdepth);
|
|
// make both gates temporary
|
|
maketemporary(srcportal, 6, "vanishes");
|
|
maketemporary(dstportal, 6, "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 (strchr(buf, '*') || strchr(buf, '^')) {
|
|
// prevent glyphs / colours
|
|
fizzle(caster);
|
|
} else if (strlen(buf) > 0) {
|
|
writetextonground(caster, caster->cell, buf, power*50);
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
} else {
|
|
// monsters can't cast
|
|
}
|
|
*/
|
|
} else if (spellid == OT_S_INSTANTDISROBE) {
|
|
cell_t *newcell = NULL;
|
|
int howmany = (power / 4)+1,ndone = 0;
|
|
int i,announcethump = B_FALSE;
|
|
object_t *o;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// find cell next to caster
|
|
newcell = getrandomadjcell(targcell, &ccwalkable, B_NOEXPAND);
|
|
if (!newcell) {
|
|
newcell = targcell;
|
|
}
|
|
for (i = 0; i < howmany; i++) {
|
|
// pick armour
|
|
o = getrandomarmour(target, NULL);
|
|
if (o) {
|
|
char obname[BUFLEN];
|
|
// move it
|
|
ndone++;
|
|
moveob(o, newcell->obpile, o->amt);
|
|
if (isplayer(target)) {
|
|
getobname(o, obname, o->amt);
|
|
if (haslos(player, newcell)) {
|
|
msg("^%cYour %s suddenly appears next to you!", getlfcol(target, CC_BAD), noprefix(obname));
|
|
} else {
|
|
msg("^%cYour %s suddenly vanishes!", getlfcol(target, CC_BAD), noprefix(obname));
|
|
|
|
announcethump = B_TRUE;
|
|
}
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
getobname(o, obname, o->amt);
|
|
if (haslos(player, newcell)) {
|
|
msg("^%c%s%s %s suddenly appears next to it!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname),
|
|
noprefix(obname));
|
|
} else {
|
|
msg("^%c%s%s %s suddenly vanishes!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname),
|
|
noprefix(obname));
|
|
}
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (announcethump && !isdeaf(player)) {
|
|
msg("You hear something hitting the ground next to you.");
|
|
}
|
|
} 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_PLAYER, spellid, power, frompot)) return B_TRUE;
|
|
}
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else if ( lfhasflag(target, F_INVISIBLE) ) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targname);
|
|
|
|
// time is based on spellpower
|
|
howlong = getspellduration(5,10,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;
|
|
}
|
|
breakaitargets(target, B_TRUE);
|
|
} else if (spellid == OT_S_JOLT) {
|
|
target = haslf(targcell);
|
|
if (target) {
|
|
int dam;
|
|
// hit
|
|
if (isplayer(target)) {
|
|
msg("^%cA pulse of electricity shocks you!",getlfcol(target, CC_BAD));
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%cA pulse of electricity shocks %s!",getlfcol(target, CC_BAD),lfname);
|
|
}
|
|
dam = power*2;
|
|
losehp(target, dam, DT_ELECTRIC, caster, "a jolt of electricity");
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_KNOCK) {
|
|
object_t *o;
|
|
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, B_DOANNOUNCE, B_DODAM);
|
|
} 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)) {
|
|
// closed door?
|
|
if (!dooropen) {
|
|
gotit = B_TRUE;
|
|
}
|
|
} else {
|
|
// something else impassable
|
|
gotit = B_TRUE;
|
|
}
|
|
if (gotit) {
|
|
if (haslos(player, targcell)) {
|
|
getobname(o, buf, o->amt);
|
|
msg("%s %s!",isplayer(caster) ? "You blast" : "Something blasts", buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
|
|
donesomething = B_TRUE;
|
|
}
|
|
} else {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_LOCKED);
|
|
if (f) {
|
|
if (f->val[2] == NA) {
|
|
// unlock it
|
|
if (haslos(player, targcell)) {
|
|
getobname(o, buf, 1);
|
|
msg("%s unlocks.", buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
killflagsofid(o->flags, F_LOCKED);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_KNOWWEAKNESS) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
if (isplayer(caster)) {
|
|
msg("^gYour mind becomes attuned to your foes' weaknesses!");
|
|
}
|
|
f = addtempflag(caster->flags, F_EXTRADAM, NA, NA, 2, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} 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) {
|
|
int radius;
|
|
|
|
// at power 3, you can control where the light appears
|
|
// at power 5, also increase illumination of entire map too!
|
|
if (power >= 3) {
|
|
// TODO: this actually means we can cast it through walls!!!
|
|
if (!validatespellcell(caster, &targcell,TT_NONE, spellid, power, frompot)) return B_TRUE;
|
|
} else {
|
|
targcell = caster->cell;
|
|
}
|
|
|
|
// create light objects
|
|
radius = power / 3;
|
|
limit(&radius, 1, NA);
|
|
makelitradius(targcell, radius, OT_MAGICLIGHT, rnd(5,10)+(power*2), power );
|
|
|
|
if (blessed || (power >= 5)) {
|
|
// permenant light
|
|
modillumination(targcell->map, D_LIGHTER);
|
|
}
|
|
|
|
if (haslos(player, targcell)) {
|
|
if (power < 5) {
|
|
msg("A magical light appears!");
|
|
} else {
|
|
msg("The area is lit by a magical light!");
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
// undead will flee from light
|
|
if (isundead(targcell->lf) && !skillcheck(targcell->lf, SC_WILL, 100, 0)) {
|
|
// runs away from this cell
|
|
addtempflag(targcell->lf->flags, F_FLEEFROM, caster->id, NA, NA, NULL, 15+power);
|
|
}
|
|
}
|
|
|
|
//calclight(targcell->map);
|
|
/*
|
|
if (targcell->map == player->cell->map) {
|
|
needredraw = B_TRUE;
|
|
drawscreen();
|
|
}
|
|
*/
|
|
} else if (spellid == OT_S_LIGHTNINGBOLT) {
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcells;
|
|
int i;
|
|
int nhits = power;
|
|
// 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)) {
|
|
// if fromob is set, this was from a lightning javelin. instead of announcing
|
|
// the bolt firing, announce it hitting people (since we've already said
|
|
// "the javelin turns into lightning").
|
|
if (!fromob) {
|
|
msg("%s shoot%s a bolt of lightning!",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) {
|
|
int dam;
|
|
// hit with lightning
|
|
dam = roll("2d6");
|
|
if (power > 1) {
|
|
dam += power; // maxpower is 1, but blue dragons can exceed this
|
|
}
|
|
if (check_for_block(caster, c->lf, dam, DT_ELECTRIC, 999, "a lightning bolt", B_RANGED)) {
|
|
} else {
|
|
losehp(c->lf, dam, DT_ELECTRIC, caster, "a lightning bolt");
|
|
if (haslos(player, c)) {
|
|
if (fromob) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("^%cThe bolt of lightning strikes %s!", getlfcol(c->lf, CC_BAD), lfname);
|
|
}
|
|
needredraw = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
nhits--;
|
|
}
|
|
}
|
|
} 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++) {
|
|
if (caster->los[i] != caster->cell) {
|
|
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++) {
|
|
int dam;
|
|
if (cansee(player, targ[i])) {
|
|
getlfname(targ[i],targname);
|
|
msg("^%c%s %s struck by a bolt of lightning!",getlfcol(targ[i], CC_BAD),
|
|
targname, is(targ[i]));
|
|
}
|
|
if (caster && (targ[i]->cell->map->habitat->id == H_FOREST)) {
|
|
dam = rolldie(4,6);
|
|
} else {
|
|
dam = rolldie(3,6);
|
|
}
|
|
|
|
if (check_for_block(caster, targ[i], dam, DT_ELECTRIC, 999, "a bolt of lightning", B_RANGED)) {
|
|
} else {
|
|
losehp(targ[i], dam, DT_ELECTRIC, caster, "a bolt of lightning");
|
|
}
|
|
}
|
|
|
|
if (seen) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} 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_TRUE;
|
|
}
|
|
// ask for object
|
|
askstring("Locate what kind of object", '?', wantname, BUFLEN, NULL);
|
|
// find it in all maps!
|
|
snprintf(buf, BUFLEN, "Locations of '%s':", wantname);
|
|
initprompt(&prompt, buf);
|
|
prompt.maycancel = B_TRUE;
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
int x,y;
|
|
char ch = 'a';
|
|
char mapname[BUFLEN];
|
|
|
|
getregionname(mapname, m, NULL, RF_WITHLEVEL);
|
|
capitalise(mapname);
|
|
|
|
if (m->habitat->id == H_HEAVEN) continue;
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
cell_t *c;
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
// on the ground?
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
char obname[BUFLEN];
|
|
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_CONDITION, B_NOBLINDADJUST,
|
|
B_NOBLESSINGS, B_USED, B_NOSHOWALL);
|
|
if (!hasflag(o->flags, F_TRAIL) && strcasestr(obname, wantname)) {
|
|
char ptext[BUFLEN];
|
|
char distbuf[BUFLEN],dirbuf[BUFLEN];
|
|
getdisttext(caster->cell, c, distbuf, NULL, dirbuf);
|
|
sprintf(ptext, "%s: %s (%s to the %s)", mapname, obname, distbuf, dirbuf);
|
|
addchoice(&prompt, ch++, ptext, NULL, o, NULL);
|
|
}
|
|
}
|
|
|
|
// carried by someone?
|
|
if (c->lf) {
|
|
for (o = c->lf->pack->first ; o ; o = o->next) {
|
|
char obname[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_CONDITION,
|
|
B_NOBLINDADJUST, B_NOBLESSINGS, B_USED, B_NOSHOWALL);
|
|
if (strcasestr(obname, wantname)) {
|
|
char ptext[BUFLEN];
|
|
char distbuf[BUFLEN],dirbuf[BUFLEN];
|
|
char lfname[BUFLEN];
|
|
|
|
real_getlfnamea(c->lf, lfname, NULL, B_SHOWALL, B_REALRACE);
|
|
getdisttext(caster->cell, c, distbuf, NULL, dirbuf);
|
|
sprintf(ptext, "%s: %s (%s to the %s, held by %s)",
|
|
mapname, obname, distbuf, dirbuf, lfname);
|
|
|
|
addchoice(&prompt, ch++, ptext, NULL, o, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prompt.nchoices == 0) {
|
|
msg("Your spell does not detect anything nearby.");
|
|
} else {
|
|
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, NULL);
|
|
}
|
|
}
|
|
}
|
|
if (prompt.nchoices == 0) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
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_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_GRAVBOOST) {
|
|
target = targcell->lf;
|
|
if (target) {
|
|
int howlong = 15;
|
|
flag_t *f;
|
|
int i;
|
|
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
f = hasactivespell(target, OT_S_GRAVLOWER);
|
|
if (f) {
|
|
stopspell(target, OT_S_GRAVLOWER);
|
|
return B_FALSE;
|
|
}
|
|
|
|
i = 0;
|
|
i += killtransitoryflags(target->flags, F_GRAVLESSENED);
|
|
i += killtransitoryflagvals(target->flags, F_DTIMMUNE, DT_FALL, NA, NA, NULL);
|
|
if (i) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (lfhasflag(target, F_GRAVBOOSTED) || spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
if (isplayer(target)) {
|
|
msg("You feel momentarily heavier.");
|
|
} else if (cansee(player, target)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
msg("%s looks momentarily heavier.", targname);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
howlong = getspellduration(3,10,blessed) + (power/2);
|
|
addtempflag(target->flags, F_GRAVBOOSTED, B_TRUE, NA, NA, NULL, howlong);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_GRAVLOWER) {
|
|
flag_t *f;
|
|
// always targetted at caster
|
|
targcell = caster->cell;
|
|
target = caster;
|
|
|
|
f = lfhasflag(target, F_GRAVBOOSTED);
|
|
if (f) {
|
|
killflag(f);
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
f = addtempflag(caster->flags, F_DTIMMUNE, DT_FALL, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
f = addtempflag(caster->flags, F_GRAVLESSENED, power, 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];
|
|
|
|
target = targcell->lf;
|
|
|
|
if (targcell->map->habitat->id == H_FOREST) {
|
|
power += 5;
|
|
limit(&power, NA, 10);
|
|
}
|
|
|
|
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 (!hasflag(o->flags, F_NOSTEAL) && !hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) {
|
|
if ((getobweight(o) <= 5) ||
|
|
((isweapon(o)) && (isequipped(o))) ) {
|
|
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 from nowhere!");
|
|
}
|
|
|
|
// chance of blowing each ob away
|
|
for (i = 0; i < nblowobs; i++) {
|
|
char obname[BUFLEN];
|
|
cell_t *c;
|
|
condset_t cs;
|
|
initcondv(&cs, CC_SOLID, B_FALSE, NA, CC_NONE);
|
|
|
|
getobname(blowob[i], obname, 1);
|
|
c = getrandomadjcell(targcell, &cs, B_ALLOWEXPAND);
|
|
if (c && ((rnd(1,100)-power) <= 33)) {
|
|
// move it
|
|
fireat(NULL, blowob[i], 1, c, 4, NULL);
|
|
}
|
|
}
|
|
|
|
if (target) {
|
|
if (!real_fall_from_air(target, SZ_SMALL + (power/4))) {
|
|
int pen = 0;
|
|
// easyish save to avoid falling
|
|
switch (getlfsize(target)) {
|
|
case SZ_MEDIUM:
|
|
pen = -15; break;
|
|
case SZ_SMALL:
|
|
pen = -25; break;
|
|
case SZ_TINY:
|
|
pen = -35; break;
|
|
case SZ_MINI:
|
|
pen = -45; break;
|
|
break;
|
|
default: pen = 0; break;
|
|
}
|
|
if (!skillcheck(target, SC_FALL, 60 + (power*5), pen)) {
|
|
fall(target, NULL, B_TRUE);
|
|
}
|
|
}
|
|
}
|
|
needredraw = B_TRUE;
|
|
} else if (spellid == OT_S_MIST) {
|
|
object_t *o;
|
|
|
|
if (!targcell || targcell->type->solid) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
if (targcell->lf) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
msg("^%cA thick veil of mist surrounds %s!", getlfcol(targcell->lf, CC_GOOD), 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;
|
|
}
|
|
}
|
|
// hack - make all monsters who can see you stop targetting you.
|
|
// otherwise they will just try to go to your last known location.
|
|
breakaitargets(caster, B_TRUE);
|
|
} else if (spellid == OT_S_MENDING) {
|
|
object_t *o;
|
|
int donesomething = B_FALSE;
|
|
flag_t *f;
|
|
char fullobname[BUFLEN];
|
|
char obname[BUFLEN];
|
|
|
|
if (targob) {
|
|
o = targob;
|
|
} else {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_DAMAGED, B_TRUE, NA,
|
|
CC_NONE);
|
|
// ask for an object
|
|
o = askobject(caster->pack, "Mend which object", NULL, NULL, '\0', &cs, B_FALSE);
|
|
}
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
getobname(o, obname, o->amt);
|
|
|
|
if (isplayer(caster)) {
|
|
snprintf(fullobname, BUFLEN, "Your %s", noprefix(obname));
|
|
} else if (cansee(player, caster)) {
|
|
snprintf(fullobname, BUFLEN, "%s%s %s", castername, getpossessive(castername), noprefix(obname));
|
|
} else {
|
|
strcpy(fullobname, "");
|
|
}
|
|
|
|
f = hasflag(o->flags, F_IMMUTABLE);
|
|
if (f) {
|
|
if (isplayer(caster)) {
|
|
char obname[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
msg("For some reason, %s is unaffected!", fullobname);
|
|
}
|
|
f->known = B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
if (blessed == B_CURSED) {
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s deteriorates!", fullobname);
|
|
takedamage(o, rnd(1,6) + power, DT_DIRECT, caster);
|
|
donesomething = B_TRUE;
|
|
} else if (isdamaged(o)) {
|
|
f->val[0] += (rnd(1,6) + power);
|
|
|
|
if (f->val[0] >= f->val[1]) {
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s is completely repaired!", fullobname);
|
|
f->val[0] = f->val[1];
|
|
} else {
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s is repaired a little!", fullobname);
|
|
}
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
// fix rust and dulled weapons
|
|
if (!iscursed(o)) {
|
|
if (isweapon(o)) {
|
|
f = hasflag(o->flags, F_BONUS);
|
|
if (f && (f->val[0] < 0)) {
|
|
killflag(f);
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s seems more effective!", fullobname);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
if (killflagsofid(o->flags, F_RUSTED)) {
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s is no longer rusted!", fullobname);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (donesomething) {
|
|
if (strlen(fullobname)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
if (isplayer(caster)) nothinghappens();
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_PACIFY) {
|
|
char targetname[BUFLEN];
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
|
|
if (spellresisted(target, caster, spellid, power, 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;
|
|
|
|
if (lfhasflag(target, F_NONCORPOREAL)) {
|
|
if (frompot) {
|
|
if (isplayer(caster)) nothinghappens();
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
howlong = getspellduration(SP_NORMAL,SP_NORMAL*2,blessed) + (power*SP_VERYFAST);
|
|
// minimum of two moves.
|
|
limit(&howlong, getmovespeed(target)*2, NA);
|
|
|
|
f = addtempflag(target->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL, howlong);
|
|
f->obfrom = OT_S_PASSWALL;
|
|
|
|
if (isplayer(target) && (howlong < getmovespeed(target)*3)) {
|
|
msg("This won't last for long...");
|
|
}
|
|
|
|
breakgrabs(target, B_TRUE, B_TRUE, B_TRUE);
|
|
|
|
|
|
|
|
} else if ((spellid == OT_S_POLYMORPH) || (spellid == OT_S_SHAPESHIFT)) {
|
|
race_t *r = NULL;
|
|
flag_t *f;
|
|
if (caster && (frompot || (spellid == OT_S_SHAPESHIFT))) {
|
|
target = caster;
|
|
} else {
|
|
target = targcell->lf;
|
|
}
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellid != OT_S_SHAPESHIFT) {
|
|
if ((target != caster) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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 target has been resized, just revert to original size
|
|
f = lfhasflag(target, F_SIZETIMER);
|
|
if (f) {
|
|
f->val[1] = 0;
|
|
if (isplayer(target)) {
|
|
msg("Your body seems to be changing shape!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
char tname[BUFLEN];
|
|
getlfname(target, tname);
|
|
msg("%s seems to be changing shape!", tname);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
//if ((caster == target) && getforcedspellrace(caster, spellid, buf)) {
|
|
if (getforcedspellrace(target, spellid, buf, NULL)) {
|
|
r = findracebyname(buf, NULL);
|
|
} else {
|
|
if (spellid == OT_S_POLYMORPH) {
|
|
int dorandom = B_FALSE;
|
|
if (caster && lfhasflag(caster, F_CONTROL)) {
|
|
if (power < 5) {
|
|
power = 5;
|
|
}
|
|
}
|
|
|
|
if (power >= 5) { // controlled
|
|
if (isplayer(caster)) {
|
|
if (isplayer(target)) { // ie. polymorphing yourself
|
|
askstring("What will you become", '?', buf, BUFLEN, NULL);
|
|
} else {
|
|
char buf2[BUFLEN];
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
snprintf(buf2, BUFLEN, "What will you transform %s into", targname);
|
|
askstring(buf2, '?', buf, BUFLEN, NULL);
|
|
}
|
|
r = findracebyname(buf, NULL);
|
|
} else {
|
|
// TODO: select based on player damage, etc
|
|
dorandom = B_TRUE;
|
|
}
|
|
} else { // random
|
|
if (isplayer(target) && lfhasflag(target, F_CONTROL)) {
|
|
askstring("What will you become", '?', buf, BUFLEN, NULL);
|
|
r = findracebyname(buf, NULL);
|
|
|
|
// make sure race is valid:
|
|
if (r && !canpolymorphto(r->id)) r = NULL;
|
|
} else {
|
|
dorandom = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (!r || dorandom) {
|
|
int forcedepth;
|
|
// want a random race with similar Threat Rating to the
|
|
// target's current one.
|
|
//
|
|
// normally tr range is (depth/2) +/- RARITYVARIANCELF
|
|
//
|
|
// so for a given tr, ask for:
|
|
//
|
|
// depth = tr*2
|
|
forcedepth = gettr(target)*2;
|
|
r = target->race;
|
|
while ((r == target->race) || !canpolymorphto(r->id)) {
|
|
r = getrandomrace(NULL, forcedepth, NULL);
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_SHAPESHIFT) {
|
|
int i,ch = 'a';
|
|
// get list of all lfs in sight
|
|
initprompt(&prompt, "What will you become?");
|
|
for (i = 0; i < target->nlos; i++) {
|
|
if (target->los[i]->lf && cansee(target, target->los[i]->lf)) {
|
|
race_t *potrace;
|
|
int n;
|
|
potrace = target->los[i]->lf->race;
|
|
// same race?
|
|
if (potrace == target->race) continue;
|
|
// too powerful?
|
|
if (gettrrace(potrace) > power) {
|
|
continue;
|
|
}
|
|
// already in the list?
|
|
for (n = 0; n < prompt.nchoices; n++) {
|
|
if (prompt.choice[n].data == potrace) {
|
|
continue;
|
|
}
|
|
}
|
|
addchoice(&prompt, ch++, potrace->name, NULL, potrace, NULL);
|
|
}
|
|
}
|
|
addchoice(&prompt, '-', "(cancel)", NULL, NULL, NULL);
|
|
prompt.maycancel = B_TRUE;
|
|
if (prompt.nchoices == 1) {
|
|
if (isplayer(caster)) {
|
|
msg("You cannot see any forms to copy!");
|
|
} else {
|
|
fizzle(caster);
|
|
}
|
|
return B_TRUE;
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
getchoice(&prompt);
|
|
r = (race_t *)prompt.result;
|
|
} else {
|
|
r = (race_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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) && !hasjob(caster, J_GOD)) {
|
|
if (isplayer(caster)) msg("As you think of a %s, magic energy dampens around you.", r->name);
|
|
r = NULL;
|
|
}
|
|
} // end if forcepoly
|
|
|
|
if (r == target->race) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (r) {
|
|
int howlong;
|
|
//int rememberorig = B_FALSE;
|
|
|
|
if (isplayer(target)) {
|
|
// polymorph will be temporary...
|
|
howlong = rnd(20,50);
|
|
} else {
|
|
howlong = PERMENANT;
|
|
}
|
|
polymorphto(target, r->id, howlong);
|
|
if (isplayer(caster) && !isplayer(target) && !lfhasflag(target, F_UNIQUE)) {
|
|
// permenant
|
|
killflagsofid(target->flags, F_POLYMORPHED);
|
|
killflagsofid(target->flags, F_ORIGRACE);
|
|
}
|
|
|
|
// if someone cast the spell at themself and it's controlled, they can change back at will.
|
|
if (target == caster) {
|
|
int canrevert = B_FALSE;
|
|
if ((spellid == OT_S_POLYMORPH) && (power >= 5)) {
|
|
canrevert = B_TRUE;
|
|
} else if (spellid == OT_S_SHAPESHIFT) {
|
|
canrevert = B_TRUE;
|
|
}
|
|
if (canrevert) {
|
|
addflag(target->flags, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL);
|
|
}
|
|
}
|
|
|
|
if (haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
breakgrabs(target, B_TRUE, B_TRUE, B_TRUE);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if ((spellid == OT_S_PROTGOOD) || (spellid == OT_S_PROTEVIL)) {
|
|
flag_t *f;
|
|
if (!targcell) targcell = caster->cell;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
f = addtempflag(target->flags, F_PROTALIGN, 5+(power*2), (spellid == OT_S_PROTGOOD) ? AL_GOOD : AL_EVIL, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_PURIFYFOOD) {
|
|
object_t *o,*nexto;
|
|
obpile_t *op;
|
|
int ndone = 0;
|
|
|
|
if (power < 3) {
|
|
targcell = caster->cell;
|
|
}
|
|
if (targcell->lf) {
|
|
target = targcell->lf;
|
|
op = targcell->lf->pack;
|
|
} else {
|
|
target = NULL;
|
|
op = targcell->obpile;
|
|
}
|
|
for (o = op->first ; o ; o = nexto) {
|
|
char obname[BUFLEN];
|
|
int donesomething = B_FALSE;
|
|
flag_t *f;
|
|
nexto = o->next;
|
|
|
|
getobname(o, obname, o->amt);
|
|
|
|
if ((f = hasflag(o->flags, F_PURIFIESTO)) != NULL) {
|
|
if (target && isplayer(target)) {
|
|
msg("Your %s sparkles for a while.", noprefix(obname));
|
|
} else if (haslos(player, targcell)) {
|
|
msg("%s sparkles for a while.", obname);
|
|
}
|
|
o->type = findot(f->val[0]);
|
|
if (isplayer(target)) {
|
|
// you now know what water is.
|
|
// you DONT know what the original type was, since it could have been a number
|
|
// of different things.
|
|
makeknown(o->type->id);
|
|
}
|
|
donesomething = B_TRUE;
|
|
ndone++;
|
|
} else {
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] = f->val[1];
|
|
f = hasflag(o->flags, F_DECAY);
|
|
if (f) {
|
|
f->val[2] = 0;
|
|
killflag(f);
|
|
donesomething = B_TRUE;
|
|
ndone++;
|
|
}
|
|
}
|
|
|
|
if (killflagsofid(o->flags, F_TAINTED)) {
|
|
donesomething = B_TRUE;
|
|
ndone++;
|
|
}
|
|
}
|
|
|
|
if (donesomething) {
|
|
if (target && isplayer(target)) {
|
|
if (isdrinkable(o)) {
|
|
msg("Your %s looks more clean now.", noprefix(obname));
|
|
} else if (isedible(o)) {
|
|
msg("Your %s looks more fresh now.", noprefix(obname));
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (haslos(player, targcell)) {
|
|
if (isdrinkable(o)) {
|
|
msg("%s looks more clean now.", obname);
|
|
} else if (isedible(o)) {
|
|
msg("%s looks more fresh now.", obname);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_PYROMANIA) {
|
|
int i,donesomething = B_FALSE;
|
|
object_t *retob[MAXRECIPEINGREDIENTS];
|
|
int nretobs;
|
|
if (!target) target = caster;
|
|
for (i = 0; i < target->nlos; i++) {
|
|
int n;
|
|
flag_t *f;
|
|
targcell = target->los[i];
|
|
getflamingobs(targcell->obpile, retob, &nretobs);
|
|
for (n = 0 ; n < nretobs; n++) {
|
|
donesomething = B_TRUE;
|
|
if (retob[n]->material->id == MT_FIRE) {
|
|
// large fires get surrounded with small ones
|
|
f = hasflag(retob[n]->flags, F_DIECONVERT);
|
|
if (f) {
|
|
cell_t *retcell[MAXCANDIDATES];
|
|
int nretcells,nn;
|
|
getradiuscells(targcell, 1, DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 90);
|
|
for (nn = 0; nn < nretcells; nn++) {
|
|
addob(retcell[nn]->obpile, f->text);
|
|
}
|
|
} else {
|
|
// small fires last longer
|
|
f = hasflag(retob[n]->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[1] = pctof(150, f->val[1]);
|
|
f->val[0] = f->val[1];
|
|
}
|
|
}
|
|
} else {
|
|
f = hasflag(retob[n]->flags, F_ONFIRE);
|
|
if (f) {
|
|
addob(targcell->obpile, "small fire");
|
|
}
|
|
}
|
|
if (haslos(player, targcell)) {
|
|
char obname[BUFLEN];
|
|
getobname(retob[n], obname, retob[n]->amt);
|
|
msg("%s flare%s up!", obname, OBS1(retob[n]));
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} // end foreach flaming ob
|
|
}
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_QUENCH) {
|
|
object_t *o,*nexto;
|
|
int ndone = 0;
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_QUICKENFIRE) {
|
|
int howmany,i,n,sel,nposs = 0,nseen = 0;
|
|
cell_t *c;
|
|
object_t *o,*poss[MAXCANDIDATES];
|
|
howmany = (power / 2) + 1;
|
|
// get a list of all fire cells near caster
|
|
for (i = DC_N; i <= DC_NW; i++) {
|
|
c = getcellindir(caster->cell, i);
|
|
if (c) {
|
|
o = hasobofmaterial(c->obpile, MT_FIRE);
|
|
if (o) {
|
|
poss[nposs++] = o;
|
|
}
|
|
}
|
|
}
|
|
limit(&howmany, NA, nposs);
|
|
if (!howmany) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
// now change them
|
|
for (i = 0; i < howmany; i++) {
|
|
lifeform_t *lf;
|
|
enum RACE rid;
|
|
// pick a random one
|
|
sel = rnd(0,nposs-1);
|
|
o = poss[sel];
|
|
// turn it into a golem
|
|
c = getoblocation(o);
|
|
killob(o);
|
|
if (power < 6) {
|
|
rid = R_PRIMALFIREL;
|
|
} else {
|
|
rid = R_PRIMALFIRE;
|
|
}
|
|
lf = summonmonster(caster, c, rid, NULL, 30, B_TRUE);
|
|
if (haslos(player, c)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
nseen++;
|
|
}
|
|
// remove it from the list
|
|
for (n = sel ; n < (nposs-1); n++) {
|
|
poss[n] = poss[n+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
// set dirty line of sight for caster, as walls have vanished
|
|
caster->losdirty = B_TRUE;
|
|
} else if (spellid == OT_S_QUICKENSTONE) {
|
|
int howmany,i,n,sel,nposs = 0,nseen = 0;
|
|
cell_t *c,*poss[MAXCANDIDATES];
|
|
howmany = (power / 2) + 1;
|
|
// get a list of all stone cells near caster
|
|
for (i = DC_N; i <= DC_NW; i++) {
|
|
c = getcellindir(caster->cell, i);
|
|
if (c && c->type->solid && (c->type->material->id == MT_STONE)) {
|
|
poss[nposs++] = c;
|
|
}
|
|
}
|
|
limit(&howmany, NA, nposs);
|
|
if (!howmany) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
// now change them
|
|
for (i = 0; i < howmany; i++) {
|
|
lifeform_t *lf;
|
|
enum RACE rid;
|
|
// pick a random one
|
|
sel = rnd(0,nposs-1);
|
|
c = poss[sel];
|
|
// turn it into a golem
|
|
setcelltype(c, c->map->habitat->emptycelltype);
|
|
if (power < 6) {
|
|
rid = R_PRIMALSTONEL;
|
|
} else {
|
|
rid = R_PRIMALSTONE;
|
|
}
|
|
lf = summonmonster(caster, c, rid, NULL, 30, B_TRUE);
|
|
if (haslos(player, c)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
nseen++;
|
|
}
|
|
// remove it from the list
|
|
for (n = sel ; n < (nposs-1); n++) {
|
|
poss[n] = poss[n+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
// set dirty line of sight for caster, as walls have vanished
|
|
caster->losdirty = B_TRUE;
|
|
} else if (spellid == OT_S_LESSENPOISON) {
|
|
flag_t *f;
|
|
int ndone = 0;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
for (f = target->flags->first ; f ; f = f->next) {
|
|
if (f->id == F_POISONED) {
|
|
poisontype_t *pt;
|
|
pt = findpoisontype(f->val[0]);
|
|
if (pt->severity != PS_CURSE) {
|
|
// slightly lower time
|
|
if (f->lifetime > 1) {
|
|
f->lifetime--;
|
|
}
|
|
// reduce power to minimum
|
|
f->val[1] = 1;
|
|
if (isplayer(target)) {
|
|
msg("^%cYour %s seems less intense.",getlfcol(target, CC_GOOD),pt->name);
|
|
}
|
|
ndone++;
|
|
}
|
|
} else if (f->id == F_INCUBATING) {
|
|
f->val[1] *= 2; // take longer to incubate
|
|
}
|
|
}
|
|
if (ndone) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (!ndone) {
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_LETHARGY) {
|
|
int amttolose;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
amttolose = roll("1d6") + power;
|
|
if (isplayer(target)) {
|
|
msg("^%cYou suddenly feel very lethargic!", getlfcol(target, CC_BAD));
|
|
} else if (cansee(player, target)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
msg("^%c%s looks very lethargic!", getlfcol(target, CC_BAD), targname);
|
|
}
|
|
// cancels rage
|
|
killflagsofid(target->flags, F_RAGE);
|
|
|
|
modstamina(target, -amttolose);
|
|
} else if (spellid == OT_S_REFRACTION) {
|
|
flag_t *f;
|
|
f = addtempflag(caster->flags, F_EVASION, (power*10), NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
if (isplayer(caster)) {
|
|
msg("^%cYour body seems to shimmer and bend!", getlfcol(caster, CC_GOOD));
|
|
} else if (cansee(player, caster)) {
|
|
msg("^%c%s%s body seems to shimmer and bend!", getlfcol(caster, CC_GOOD),
|
|
castername, getpossessive(castername));
|
|
}
|
|
} else if (spellid == OT_S_REMOTEKO) {
|
|
char targetname[BUFLEN];
|
|
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
getlfname(target, targetname);
|
|
|
|
if (checkcharm(caster, target)) return B_FALSE;
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
// they get angry!
|
|
if (!isplayer(target) && cansee(target, caster)) {
|
|
fightback(target, caster);
|
|
}
|
|
} else {
|
|
// ko !
|
|
loseconsciousness(target, rnd(50,100), caster);
|
|
}
|
|
} else if (spellid == OT_S_REPELINSECTS) {
|
|
// just announce
|
|
if (isplayer(caster)) {
|
|
msg("A strange odour surrounds you...");
|
|
}
|
|
} else if (spellid == OT_S_REPLENISH) {
|
|
int incamt,newamt,maxed = B_FALSE;
|
|
char obname[BUFLEN];
|
|
flag_t *f;
|
|
|
|
if (!targob) {
|
|
if (isplayer(caster)) {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_HASFLAG, B_TRUE, F_REPLENISHABLE,
|
|
CC_NONE);
|
|
// ask for an object
|
|
targob = askobject(caster->pack, "Replenish which object", NULL, NULL, '\0', &cs, B_FALSE);
|
|
}
|
|
}
|
|
if (!targob) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = hasflag(targob->flags, F_CHARGES);
|
|
if (!f) {
|
|
if (isplayer(caster)) nothinghappens();
|
|
return B_TRUE;
|
|
}
|
|
|
|
// restores power% of maximum
|
|
incamt = pctof(power*10, f->val[1]);
|
|
limit(&incamt, 1, NA); // at least 1!
|
|
|
|
newamt = f->val[0] + incamt;
|
|
limit(&newamt, NA, f->val[1]);
|
|
|
|
if (f->val[0] == newamt) {
|
|
// already maxed
|
|
maxed = B_TRUE;
|
|
}
|
|
f->val[0] = newamt;
|
|
|
|
targcell = getoblocation(targob);
|
|
|
|
if (maxed) {
|
|
if (haslos(player, targcell)) {
|
|
nothinghappens();
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
getobname(targob, obname, targob->amt);
|
|
|
|
if (targob->pile->owner) {
|
|
if (isplayer(targob->pile->owner)) {
|
|
msg("Your %s %s %s.", noprefix(obname), (targob->amt == 1) ? "pulses" : "pulse", (power < 5) ? "briefly" : "for a while");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player,targob->pile->owner)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targob->pile->owner, lfname);
|
|
msg("%s%s %s %s %s.", lfname, getpossessive(lfname),
|
|
noprefix(obname), (targob->amt == 1) ? "pulses" : "pulse", (power < 5) ? "briefly" : "for a while");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
if (haslos(player, targcell)) {
|
|
msg("%s %s %s.", noprefix(obname), (targob->amt == 1) ? "pulses" : "pulse", (power < 5) ? "briefly" : "for a while");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_RESSURECTION) {
|
|
lifeform_t *newlf;
|
|
if (!targob) {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_HASFLAG, B_TRUE, F_CORPSEOF,
|
|
CC_NONE);
|
|
// select object from cell...
|
|
targob = askobject(targcell->obpile, "Revive which corpse", NULL, NULL, '\0', &cs, B_FALSE);
|
|
if (!targob) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
newlf = ressurect(targob);
|
|
|
|
if (newlf) {
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_RESTORATION) {
|
|
int seen = B_FALSE;
|
|
int i,hpheal,mpheal;
|
|
int failed = B_TRUE;
|
|
char lfname[BUFLEN];
|
|
if (!target) target = caster;
|
|
|
|
getlfname(target, lfname);
|
|
|
|
if (frompot) {
|
|
if (blessed == B_BLESSED) {
|
|
power = 3;
|
|
} else if (blessed == B_CURSED) {
|
|
power = 1;
|
|
} else {
|
|
power = 2;
|
|
}
|
|
}
|
|
|
|
if (!isplayer(target) && cansee(player, target)) {
|
|
seen = B_TRUE;
|
|
}
|
|
|
|
if (isundead(target)) {
|
|
if (seen) {
|
|
msg("^%c%s writhe%s in agony!",getlfcol(target, CC_BAD), lfname, isplayer(target) ? "" : "s");
|
|
}
|
|
losehp(target, power*10, DT_HOLY, caster, "the power of restoration");
|
|
return B_FALSE;
|
|
}
|
|
|
|
for (i = 0; i < MAXATTS; i++ ){
|
|
if (getattr(target,i) < target->baseatt[i]) {
|
|
setattr(target, i, target->baseatt[i]);
|
|
failed = B_FALSE;
|
|
}
|
|
}
|
|
// fix diseases (even magical ones)
|
|
if (killflagsofid(target->flags, F_POISONED)) {
|
|
failed = B_FALSE;
|
|
}
|
|
// wake up from sleep/ko
|
|
if (killflagsofid(target->flags, F_ASLEEP)) {
|
|
failed = B_FALSE;
|
|
}
|
|
// fix pain
|
|
if (killflagsofid(target->flags, F_PAIN)) {
|
|
failed = B_FALSE;
|
|
}
|
|
// fix injuries
|
|
if (killflagsofid(target->flags, F_INJURY)) {
|
|
failed = B_FALSE;
|
|
}
|
|
// blessed restores hp/mp to full
|
|
if (power == 3) { // heal ALL hp & mp
|
|
hpheal = target->maxhp;
|
|
mpheal = getmaxmp(target);
|
|
} else if (power == 2) { // heal 3/4 hp & mp
|
|
hpheal = pctof(target->maxhp, 75);
|
|
mpheal = pctof(getmaxmp(target), 75);
|
|
} else { // heal half hp & mp
|
|
hpheal = target->maxhp / 2;
|
|
mpheal = getmaxmp(target) / 2;
|
|
}
|
|
|
|
if (target->hp < hpheal) {
|
|
gainhp(target, hpheal - target->hp);
|
|
failed = B_FALSE;
|
|
if (!isplayer(target) && cansee(player, target)) {
|
|
msg("^%c%s%s health has been restored!", getlfcol(target, CC_GOOD),lfname, getpossessive(lfname));
|
|
}
|
|
}
|
|
if (target->mp < mpheal) {
|
|
gainmp(target, mpheal - target->mp);
|
|
failed = B_FALSE;
|
|
}
|
|
|
|
if (failed) {
|
|
if (isplayer(target)) msg("You feel momentarily restored.");
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else {
|
|
if (isplayer(target)) msg("You feel restored!");
|
|
}
|
|
} else if (spellid == OT_S_REVEALHIDDEN) {
|
|
int i;
|
|
int seen = B_FALSE;
|
|
|
|
if (!isplayer(caster)) return B_TRUE;
|
|
|
|
if (!target) target = caster;
|
|
for (i = 0 ; i < target->nlos; i++ ){
|
|
targcell = target->los[i];
|
|
if (targcell) {
|
|
object_t *o;
|
|
if (isplayer(target)) {
|
|
// 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];
|
|
killflag(f);
|
|
getobname(o, obname, o->amt);
|
|
msg("^G%s is magically revealed!", obname);
|
|
seen = B_TRUE;
|
|
}
|
|
if (hasflag(o->flags, F_ISMONSTER)) {
|
|
lifeform_t *newlf;
|
|
newlf = reveal_pretendob(o);
|
|
if (newlf) {
|
|
char lfname[BUFLEN];
|
|
getlfnamea(newlf, lfname);
|
|
msg("^G%s is magically revealed!", lfname);
|
|
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, target)) {
|
|
char tname[BUFLEN];
|
|
seen = B_TRUE;
|
|
getlfname(targcell->lf, tname);
|
|
msg("%s becomes visible!", tname);
|
|
}
|
|
}
|
|
} else if ( (f->id == F_HIDING) &&
|
|
!lfhasflagval(target, F_SPOTTED, targcell->lf->id, NA, NA, NULL)) {
|
|
addflag(target->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, target)) {
|
|
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;
|
|
if (isplayer(caster)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
} else {
|
|
if (isplayer(caster)) {
|
|
nothinghappens();
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_SATEHUNGER) {
|
|
int hunger;
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
hunger = gethungerval(target);
|
|
if (hunger > 0) {
|
|
int modamt;
|
|
modamt = hunger; // take away all current hunger
|
|
modamt += HUNGERCONST * 2; // ... then make them stuffed
|
|
modhunger(target, -modamt);
|
|
if (isplayer(target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} 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_SHAPEMETAL) {
|
|
object_t *o;
|
|
int donesomething = B_FALSE;
|
|
flag_t *f;
|
|
char fullobname[BUFLEN];
|
|
char obname[BUFLEN];
|
|
|
|
if (targob) {
|
|
o = targob;
|
|
} else {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_METAL,
|
|
CC_NONE);
|
|
// ask for an object
|
|
o = doaskobject(caster->pack, "Repair which metal object", NULL, NULL, B_FALSE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, &cs, B_FALSE);
|
|
}
|
|
if (!o) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
getobname(o, obname, o->amt);
|
|
|
|
if (isplayer(caster)) {
|
|
snprintf(fullobname, BUFLEN, "Your %s", noprefix(obname));
|
|
} else if (cansee(player, caster)) {
|
|
snprintf(fullobname, BUFLEN, "%s%s %s", castername, getpossessive(castername), noprefix(obname));
|
|
} else {
|
|
strcpy(fullobname, "");
|
|
}
|
|
|
|
f = hasflag(o->flags, F_IMMUTABLE);
|
|
if (f) {
|
|
if (isplayer(caster)) {
|
|
char obname[BUFLEN];
|
|
real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
msg("For some reason, %s is unaffected!", fullobname);
|
|
}
|
|
f->known = B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
if (isdamaged(o)) {
|
|
f->val[0] = f->val[1];
|
|
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s is completely repaired!", fullobname);
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
// fix rust and dulled weapons
|
|
if (isweapon(o)) {
|
|
f = hasflag(o->flags, F_BONUS);
|
|
if (f && (f->val[0] < 0)) {
|
|
killflag(f);
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s seems more effective!", fullobname);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
if (killflagsofid(o->flags, F_RUSTED)) {
|
|
if (isplayer(caster) || cansee(player, caster)) msg("%s is no longer rusted!", fullobname);
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
if (donesomething) {
|
|
if (strlen(fullobname)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
if (isplayer(caster)) nothinghappens();
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SHARDSHOT) {
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcells;
|
|
int i;
|
|
int nhits = power;
|
|
// 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, '*', '\0', C_WHITE);
|
|
if (cansee(player, caster)) {
|
|
msg("%s fire%s a burst of ice shards!",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) {
|
|
int dam;
|
|
dam = rolldie(nhits, 6);
|
|
// hit with ice
|
|
if (check_for_block(caster, c->lf, dam, DT_COLD, 999, "a burst of ice shards", B_RANGED)) {
|
|
} else {
|
|
if (isplayer(c->lf) || cansee(player, c->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("^%cA burst of ice shards hits %s.",getlfcol(target, CC_BAD), lfname);
|
|
}
|
|
losehp(c->lf, dam, DT_COLD, caster, "a burst of ice shards");
|
|
}
|
|
nhits--;
|
|
}
|
|
if (haslos(player, c)) {
|
|
needredraw = B_TRUE;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_SHATTER) {
|
|
char buf[BUFLEN];
|
|
if (targcell->lf && isplayer(targcell->lf)) {
|
|
msg("Ultra-sonic vibrations ring through your body!");
|
|
}
|
|
snprintf(buf, BUFLEN, "%s%s shatter spell", castername, getpossessive(castername));
|
|
if (!shattercell(targcell, caster, buf)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SILENCE) {
|
|
int howlong = 30;
|
|
target = targcell->lf;
|
|
|
|
if (!target || hasflag(target->flags, F_SILENCED)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
if (!isdeaf(player)) {
|
|
if (isplayer(target)) {
|
|
msg("Noise around you sound softer for a moment.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (haslos(player, target->cell)) {
|
|
getlfname(target, buf);
|
|
msg("Noises around %s sound softer for a moment.", buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
howlong = rnd(20,30) + power*2;
|
|
|
|
addtempflag(target->flags, F_SILENCED, NA, NA, NA, NULL, howlong);
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SIXTHSENSE) {
|
|
flag_t *f;
|
|
if (!target) target = caster;
|
|
if (lfhasflag(target, F_SIXTHSENSE)) {
|
|
fizzle(target);
|
|
return B_TRUE;
|
|
}
|
|
f = addtempflag(caster->flags, F_SIXTHSENSE, power, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if ((spellid == OT_S_SIZEUP) || (spellid == OT_S_SIZEDOWN)) {
|
|
enum LFSIZE origsize,newsize;
|
|
char origstats[BUFLEN];
|
|
int reverting = B_FALSE;
|
|
int doobs = B_FALSE;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
origsize = getlfsize(target);
|
|
|
|
if ((power >= 5) && areallies(target,caster)) {
|
|
doobs = B_TRUE;
|
|
}
|
|
|
|
if (spellid == OT_S_SIZEUP) {
|
|
if (origsize >= SZ_ENORMOUS) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
newsize = origsize + 1;
|
|
} else { // ie. sizedown
|
|
if (origsize <= SZ_MINI) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
newsize = origsize - 1;
|
|
}
|
|
|
|
if (lfhasflag(target, F_SIZETIMER)) {
|
|
reverting = B_TRUE;
|
|
}
|
|
|
|
if (resizelf(target, newsize, doobs)) {
|
|
// failed
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (!reverting) {
|
|
int newstr;
|
|
sprintf(origstats, "%d,%d", target->att[A_STR], target->maxhp);
|
|
|
|
// affect str / hp
|
|
newstr = target->att[A_STR] + 50;
|
|
target->att[A_STR] = newstr;
|
|
target->maxhp *= 2;
|
|
if (isplayer(target)) statdirty = B_TRUE;
|
|
|
|
// revert in a little while...
|
|
addflag(target->flags, F_SIZETIMER, origsize, power*10, doobs, origstats);
|
|
}
|
|
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SLEEP) {
|
|
int howlong;
|
|
if (!target) target = targcell->lf;
|
|
|
|
if (lfhasflag(target, F_ASLEEP) || !cansleep(target)) {
|
|
if (!frompot) fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
msg("A sudden feeling of drowsiness washes over you!");
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
// 1st skill check passed. make another one.
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
// 2nd one passed - no effect..
|
|
if (isplayer(target)) {
|
|
msg("You yawn.");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, target)) {
|
|
getlfname(target, buf);
|
|
msg("%s yawns.", buf);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
// 2nd one failed - lose all stamina instead
|
|
modstamina(target, -(getstamina(target)));
|
|
if (isplayer(target)) {
|
|
msg("^%cYou suddenly feel very lethargic!",getlfcol(target, CC_BAD));
|
|
} else if (cansee(player, target)) {
|
|
getlfname(target, buf);
|
|
msg("^%c%s looks very lethargic!", getlfcol(target, CC_BAD),buf);
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
howlong = getspellduration(5,10,blessed) + (power/2);
|
|
fallasleep(target, ST_ASLEEP, howlong);
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SLEETSTORM) {
|
|
int failed = B_FALSE;
|
|
if (caster && caster->cell->map->habitat->id == H_FOREST) {
|
|
power += 3;
|
|
limit(&power, NA, 10);
|
|
}
|
|
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, B_INCLUDECENTRE,
|
|
caster, NULL, NULL, NULL);
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SLIDE) {
|
|
flag_t *f;
|
|
target = caster;
|
|
if (lfhasflag(target, F_ICESLIDE)) {
|
|
fizzle(target);
|
|
return B_TRUE;
|
|
}
|
|
f = addtempflag(target->flags, F_ICESLIDE, power, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
f = addtempflag(target->flags, F_AUTOCREATEOB, 0, NA, NA, "sheet of ice", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
f = addtempflag(target->flags, F_FASTMOVE, 10, NA, NA, "sheet of ice", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
if (cansee(player, target) && seenbyplayer) if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
|
|
} else if (spellid == OT_S_SLOW) {
|
|
int howlong = 15;
|
|
target = targcell->lf;
|
|
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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_SMITEEVIL) || (spellid == OT_S_SMITEGOOD)) {
|
|
enum ALIGNMENT wantalign;
|
|
if (spellid == OT_S_SMITEEVIL) {
|
|
wantalign = AL_EVIL;
|
|
} else {
|
|
wantalign = AL_GOOD;
|
|
}
|
|
target = targcell->lf;
|
|
if (!target || (getalignment(target) != wantalign)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (isplayer(target)) {
|
|
msg("^%cThe power of %s smites you!",getlfcol(target, CC_BAD),
|
|
(spellid == OT_S_SMITEEVIL) ? "good" : "evil");
|
|
} else if (cansee(player, target)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(target, lfname);
|
|
msg("^%cThe power of %s smites %s!", getlfcol(target, CC_BAD),
|
|
(spellid == OT_S_SMITEEVIL) ? "good" : "evil", lfname);
|
|
}
|
|
// use direct damage rather than holy, because otherwise it might be increased
|
|
// due to vulnerabilities
|
|
losehp(target, rolldie(1+power,4), DT_DIRECT, caster, "a smiting");
|
|
} else if (spellid == OT_S_SNAPFREEZE) {
|
|
target = haslf(targcell);
|
|
if (target) {
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
// they get angry!
|
|
if (!isplayer(target) && cansee(target, caster)) {
|
|
fightback(target, caster);
|
|
}
|
|
} else {
|
|
if (seenbyplayer && (isplayer(caster) || cansee(player, target))) *seenbyplayer = B_TRUE;
|
|
freezelf(target, caster, rnd(20,40));
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SNOWBALL) {
|
|
int failed = B_FALSE;
|
|
// ask for a target cell
|
|
if (targcell) {
|
|
if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) {
|
|
int dir;
|
|
char buf[BUFLEN];
|
|
cell_t *c;
|
|
// centre snowball here...
|
|
if (isplayer(caster)) {
|
|
msg("You launch a huge snowball!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s launches a huge snowball!",castername);
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
anim(caster->cell, targcell, '^', C_WHITE);
|
|
|
|
sprintf(buf, "A huge snowball explodes! ");
|
|
animradial(targcell, 1, '}', C_WHITE, DT_ORTH, buf, buf);
|
|
|
|
redrawpause();
|
|
// add snow as follows (3 = medium, 2 = medium, 1 = smell)
|
|
// *
|
|
// ***
|
|
// *
|
|
if (targcell->lf) {
|
|
if (check_for_block(caster, targcell->lf, 1, DT_COLD, 999, "a snowball", B_RANGED)) {
|
|
} else {
|
|
losehp(targcell->lf, 1, DT_COLD, caster, "a snowball");
|
|
}
|
|
} else {
|
|
addob(targcell->obpile, "small puddle of water");
|
|
}
|
|
for (dir = D_N; dir <= D_W; dir++) {
|
|
c = getcellindir(targcell, dir);
|
|
if (c && !c->type->solid ) {
|
|
if (c->lf) {
|
|
if (check_for_block(caster, c->lf, 1, DT_COLD, 999, "a burst of snow", B_RANGED)) {
|
|
} else {
|
|
losehp(c->lf, 1, DT_COLD, caster, "a burst of snow");
|
|
}
|
|
} else {
|
|
addob(c->obpile, "small puddle of water");
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SOFTENEARTH) {
|
|
int seenground = B_FALSE;
|
|
int seenwall = B_FALSE;
|
|
int seenboulder = B_FALSE;
|
|
int ndone = 0;
|
|
int powerleft = power;
|
|
// ask for a target cell
|
|
if (targcell) {
|
|
object_t *o,*oo;
|
|
int i;
|
|
|
|
oo = hasob(targcell->obpile, OT_BOULDER);
|
|
while (oo && powerleft) {
|
|
removeob(oo, 1);
|
|
o = addobfast(targcell->obpile, OT_RUBBLE);
|
|
ndone++;
|
|
powerleft--;
|
|
if (haslos(player, targcell)) {
|
|
seenboulder++;
|
|
msg("A boulder crumbles into rubble!");
|
|
}
|
|
oo = hasob(targcell->obpile, OT_BOULDER);
|
|
}
|
|
|
|
if (powerleft) {
|
|
// do first cell
|
|
// is it actually earth here?
|
|
if (targcell->type->material->id == MT_STONE) {
|
|
if (targcell->type->solid) {
|
|
if (power >= 2) {
|
|
// soften into dirt
|
|
setcelltype(targcell, CT_WALLDIRT);
|
|
if (haslos(player, targcell)) seenwall = B_TRUE;
|
|
ndone++;
|
|
powerleft = 0;
|
|
}
|
|
} else {
|
|
if (targcell->type->id != CT_DIRT) setcelltype(targcell, CT_DIRT);
|
|
o = addob(targcell->obpile, "pool of mud");
|
|
if (o) {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_SOLID, B_FALSE, NA,
|
|
CC_HASOBTYPE, B_FALSE, OT_MUDPOOL,
|
|
CC_HASMATERIAL, B_TRUE, MT_STONE,
|
|
CC_NONE);
|
|
ndone++;
|
|
powerleft--;
|
|
if (haslos(player, targcell)) seenground = B_TRUE;
|
|
// do the rest
|
|
for (i = 0; i < powerleft; i++) {
|
|
cell_t *c;
|
|
c = real_getrandomadjcell(targcell, &cs, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL);
|
|
if (c) {
|
|
if (targcell->type->id != CT_DIRT) setcelltype(targcell, CT_DIRT);
|
|
o = addob(c->obpile, "pool of mud");
|
|
if (o) {
|
|
ndone++;
|
|
if (haslos(player, targcell)) seenground = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end if solid
|
|
} // end if material is stone
|
|
} // end if powerleft
|
|
} // end if targcell
|
|
|
|
if (ndone) {
|
|
if (seenground) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("The ground nearby softens into mud.");
|
|
}
|
|
if (seenwall) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
msg("The walls nearby soften into dirt.");
|
|
}
|
|
if (seenboulder) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SOULLINK) {
|
|
lifeform_t *targ2 = NULL;
|
|
char buf[BUFLEN],tname[BUFLEN],t2name[BUFLEN];
|
|
if (caster && !isplayer(caster)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
if (!target) {
|
|
target = targcell->lf;
|
|
}
|
|
if (!target || (target == caster)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
real_getlfnamea(target, tname, NULL, B_SHOWALL, B_REALRACE);
|
|
|
|
if (power < 3) {
|
|
// link caster to target
|
|
targ2 = caster;
|
|
} else {
|
|
cell_t *where;
|
|
char smallprompt[BUFLEN];
|
|
// link target to target2
|
|
// ask for a target cell
|
|
snprintf(buf, BUFLEN, "Who will you soul-link %s to?", tname);
|
|
snprintf(smallprompt, BUFLEN, "soul link:%s->", tname);
|
|
where = askcoords(buf, smallprompt, TT_MONSTER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (where && where->lf && cansee(caster, where->lf)) {
|
|
targ2 = where->lf;
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (targ2 == target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
real_getlfnamea(targ2, t2name, NULL, B_SHOWALL, B_REALRACE);
|
|
|
|
if (hasflag(target->flags, F_SOULLINK) || hasflag(targ2->flags, F_SOULLINK)) {
|
|
msg("^bAn existing soul link prevents your spell from working!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// link caster to target
|
|
sprintf(buf, "%s%s soul link spell", castername, getpossessive(castername));
|
|
addflag(target->flags, F_SOULLINK, targ2->id, NA, NA, buf);
|
|
addflag(targ2->flags, F_SOULLINK, target->id, NA, NA, buf);
|
|
|
|
// soullink flag is only announce for the player, so oif we linked to someone else,
|
|
// provide some feedback.
|
|
if (targ2 != player) {
|
|
msg("^gYou establish a soul link between %s and %s!",tname,t2name);
|
|
}
|
|
} else if (spellid == OT_S_SPARK) {
|
|
object_t *o,*nexto;
|
|
int donesomething = B_FALSE;
|
|
int nburnt = 0;
|
|
|
|
if (haslos(player, targcell)) {
|
|
if (targcell->lf && cansee(player, targcell->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
msg("^%cA small burst of flame singes %s.", getlfcol(targcell->lf, CC_BAD),lfname);
|
|
} else {
|
|
msg("A small burst of flame appears.");
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
|
|
if (targcell->lf) {
|
|
losehp(targcell->lf, roll("2d3"), DT_FIRE, caster, "a burst of flame");
|
|
}
|
|
for (o = targcell->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
// special cases
|
|
// this spell isn't powerful enough to burn or heat up other things
|
|
if (isflammable(o) || (o->type->id == OT_CANDLE) || (o->type->id == OT_TORCH)) {
|
|
takedamage(o, 1, DT_FIRE, caster);
|
|
donesomething = B_TRUE;
|
|
nburnt++;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_SPEAKDEAD) {
|
|
object_t *corpse = NULL;
|
|
|
|
if (!isplayer(caster)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
corpse = hasobwithflag(caster->cell->obpile, F_CORPSEOF);
|
|
if (corpse) {
|
|
char buf[BUFLEN],corpsename[BUFLEN];
|
|
char ch;
|
|
int done = B_FALSE;
|
|
flag_t *cf;
|
|
race_t *corpserace = NULL;
|
|
cf = hasflag(corpse->flags, F_CORPSEOF);
|
|
if (cf) {
|
|
corpserace = findrace(cf->val[0]);
|
|
}
|
|
if (corpserace) {
|
|
getobname(corpse, corpsename, 1);
|
|
msg("An ghostly spirit rises from %s!", corpsename); more();
|
|
if (racecantalk(corpserace->id)) {
|
|
// TODO: check if you speak a common language
|
|
while (!done) {
|
|
snprintf(buf, BUFLEN, "What will you ask %s?", corpsename);
|
|
initprompt(&prompt, buf);
|
|
addchoice(&prompt, 'a', "How did you die?", NULL, NULL, NULL);
|
|
addchoice(&prompt, 'b', "Tell me about this area", NULL, NULL, NULL);
|
|
addchoice(&prompt, 'c', "Are there any hidden dangers nearby?", NULL, NULL, NULL);
|
|
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
|
|
prompt.maycancel = B_TRUE;
|
|
|
|
|
|
ch = getchoice(&prompt);
|
|
if ((ch != '\0') && (ch != '-')) {
|
|
snprintf(buf, BUFLEN, "%s whispers:", corpsename);
|
|
msg(buf);
|
|
}
|
|
if (ch == 'a') {
|
|
flag_t *f;
|
|
char *p;
|
|
|
|
f = hasflag(corpse->flags, F_CORPSEOF);
|
|
if (f && strlen(f->text)) {
|
|
char killer[BUFLEN];
|
|
char weapon[BUFLEN];
|
|
p = readuntil(killer, f->text, '^');
|
|
if (strstr(p, "weilding")) {
|
|
p = readuntil(weapon, p, '^');
|
|
} else {
|
|
strcpy(weapon, "");
|
|
}
|
|
|
|
snprintf(buf, BUFLEN, "\"I was killed by %s", killer);
|
|
if (strlen(weapon)) {
|
|
strcat(buf, ", ");
|
|
strcat(buf, weapon);
|
|
}
|
|
strcat(buf, ".\"");
|
|
msg(buf);
|
|
} else {
|
|
msg("\"I do not know what killed me.\"");
|
|
}
|
|
} else if (ch == 'b') {
|
|
genareaknowledge(corpse->flags, 50);
|
|
docomms_areainfo(corpsename, corpse->flags, NULL);
|
|
if (onein(3)) done = B_TRUE;
|
|
} else if (ch == 'c') {
|
|
genareaknowledge(corpse->flags, 50);
|
|
docomms_areadangers(corpsename, corpse->flags, NULL);
|
|
if (onein(3)) done = B_TRUE;
|
|
} else {
|
|
done = B_TRUE;
|
|
}
|
|
} // end while !done
|
|
} else {
|
|
// can't talk
|
|
msg("Unfortunately, the spirit doesn't seem capable of speech.");
|
|
} // end if cantalk
|
|
|
|
// destroy the corpse.
|
|
msg("%s crumbles to dust.", corpsename);
|
|
removeob(corpse, ALL);
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} // end if corpse
|
|
} else if (spellid == OT_S_SPIKEVOLLEY) {
|
|
object_t *o;
|
|
flag_t *f;
|
|
|
|
// create a volley of spikes
|
|
o = addobfast(caster->pack, OT_SPIKEVOLLEY);
|
|
if (!o) {
|
|
fizzle(caster);
|
|
}
|
|
if (isplayer(caster) || cansee(player, caster)) {
|
|
msg("%s fire%s a volley of spikes!",castername,isplayer(caster) ? "" : "s");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
f = hasflag(o->flags, F_MISSILEDAM);
|
|
if (f) { // should always be true
|
|
char dambuf[BUFLEN];
|
|
free(f->text);
|
|
sprintf(dambuf, "%dd4", power*3);
|
|
f->text = strdup(dambuf);
|
|
}
|
|
real_fireat(caster, o, 1, targcell, 6, NULL, B_FALSE, OT_S_SPIKEVOLLEY, NULL);
|
|
} else if (spellid == OT_S_STASIS) {
|
|
flag_t *f;
|
|
f = addtempflag(caster->flags, F_STASIS, B_TRUE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_STENCH) {
|
|
int howlong;
|
|
enum ERROR why;
|
|
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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 (isplayer(target) || haslos(player, target->cell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (makenauseated(target, power, howlong, &why)) {
|
|
// if 'why' was 'resisted', then makenauseated()
|
|
// will ahve already announced the failure.
|
|
if (isplayer(caster) && (why != E_RESISTED)) {
|
|
char buf[BUFLEN];
|
|
getlfname(target, buf);
|
|
msg("%s seems unaffected.", buf);
|
|
}
|
|
// failed
|
|
return 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")) {
|
|
int tr;
|
|
tr = gettrrace(r);
|
|
if (power >= tr*2) {
|
|
raceposs[nraceposs++] = r;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
poss[0] = caster->cell;
|
|
nposs = 1;
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
if (caster->los[i] != caster->cell) {
|
|
poss[nposs++] = caster->los[i];
|
|
}
|
|
}
|
|
*/
|
|
// for each cell we can see... (including our own, but don't affect our own weapon)
|
|
for (i = 0; i < caster->nlos; i++) {
|
|
c = caster->los[i];
|
|
if (c->lf && cansee(caster, c->lf) && (c->lf != caster)) {
|
|
for (o = c->lf->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if ( 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, &ccwalkable, 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);
|
|
killflagsofid(snake->flags, F_FLEEONDAM);
|
|
killflagsofid(snake->flags, F_FLEEONHPPCT);
|
|
addflag(snake->flags, F_NOFLEE, B_TRUE, 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, or can't see it
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
lifeform_t *snake = NULL;
|
|
nexto = o->next;
|
|
if ( hasflag(o->flags, F_RODSHAPED) &&
|
|
(o->blessed == B_UNCURSED) &&
|
|
!hasflag(o->flags, F_HASBRAND)) {
|
|
cell_t *newcell = NULL;
|
|
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, &ccwalkable, 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);
|
|
killflagsofid(snake->flags, F_FLEEONDAM);
|
|
killflagsofid(snake->flags, F_FLEEONHPPCT);
|
|
addflag(snake->flags, F_NOFLEE, B_TRUE, 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);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_STUN) {
|
|
target = targcell->lf;
|
|
|
|
if (lfhasflag(target, F_ASLEEP) || !ischarmable(target)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
return B_FALSE;
|
|
} else {
|
|
stun(target, (power/5)+2);
|
|
if (isplayer(target) || cansee(player, target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_SLEEPMASS) {
|
|
int i;
|
|
int donesomething = B_FALSE;
|
|
target = caster;
|
|
// sleep everyone within los of caster
|
|
for (i = 0; i < target->nlos; i++) {
|
|
lifeform_t *thistarg;
|
|
|
|
targcell = target->los[i];
|
|
thistarg = targcell->lf;
|
|
if (thistarg && (thistarg != caster)) {
|
|
if (lfhasflag(thistarg, F_ASLEEP) || !ischarmable(thistarg)) {
|
|
continue;
|
|
}
|
|
// we set frompot to B_TRUE to avoid 'fizzled' messages if there is someone
|
|
// unaffected in range.
|
|
dospelleffects(caster, OT_S_SLEEP, power+1, thistarg,NULL,NULL,blessed,
|
|
seenbyplayer, B_TRUE, NULL);
|
|
}
|
|
}
|
|
|
|
if (!donesomething) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SUCK) {
|
|
target = targcell->lf;
|
|
|
|
if (target) {
|
|
int failed = B_FALSE;
|
|
|
|
|
|
if (!fromob && spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
|
|
failed = B_TRUE;
|
|
} else if (fromob && skillcheck(target, SC_DODGE, 60, 0)) {
|
|
failed = B_TRUE;
|
|
// announce whip failures
|
|
if (cansee(player, target)) {
|
|
char obname[BUFLEN];
|
|
char tname[BUFLEN];
|
|
real_getobname(fromob, obname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
getlfname(target, tname);
|
|
msg("%s%s %s misses %s.", castername, getpossessive(castername), noprefix(obname), tname);
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
if (!failed) {
|
|
// announce whip attacks
|
|
if (fromob && cansee(player, target)) {
|
|
char obname[BUFLEN];
|
|
char tname[BUFLEN];
|
|
real_getobname(fromob, obname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL);
|
|
getlfname(target, tname);
|
|
msg("%s%s %s wraps around %s.", castername, getpossessive(castername), noprefix(obname), tname);
|
|
}
|
|
if (caster) {
|
|
fightback(target, caster);
|
|
}
|
|
// 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 {
|
|
// if successful, target gets delayed so they can't just
|
|
// walk away again
|
|
taketime(target, getactspeed(target));
|
|
}
|
|
} else {
|
|
if (!fromob) fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_SUPERHEAT) {
|
|
char obname[BUFLEN];
|
|
// needs:
|
|
// object = which potion to throw
|
|
// cell = where to throw the potion
|
|
if (!targob && isplayer(caster)) {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_DRINKABLE, B_TRUE, NA,
|
|
CC_NONE);
|
|
targob = askobject(caster->pack, "Superheat which potion", NULL, NULL, 'q', &cs, B_FALSE);
|
|
}
|
|
if (!targob) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
targob = splitob(targob);
|
|
if (!targob) {
|
|
if (isplayer(caster)) {
|
|
msg("Your pack is too full for this spell to work!");
|
|
}
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
getobname(targob, obname, 1);
|
|
if (isplayer(caster)) {
|
|
msg("Your %s begins to bubble violently!", noprefix(obname)); more();
|
|
} else if (cansee(player, caster)) {
|
|
msg("%s%s %s begins to bubble violently!", castername, getpossessive(castername), noprefix(obname));
|
|
}
|
|
|
|
// potion will explode next turn if you don't throw it.
|
|
addflag(targob->flags, F_EXPLODEONDEATH, NA, 1, NA, "6d2"); //ie . 6-12 damage
|
|
addflag(targob->flags, F_EXPLODEONDAM, NA, 1, NA, "6d2"); //ie . 6-12 damage
|
|
addflag(targob->flags, F_OBHPDRAIN, 1, NA, NA, NULL);
|
|
|
|
if (isplayer(caster) && !targcell) {
|
|
char buf[BUFLEN],buf2[BUFLEN];
|
|
sprintf(buf, "Throw %s where?", obname);
|
|
sprintf(buf2, "Superheat->%s->", obname);
|
|
targcell = askcoords(buf, buf2,TT_MONSTER, caster, getmaxthrowrange(caster, targob), LOF_NEED, B_TRUE);
|
|
}
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
fireat(caster, targob, 1, targcell, getthrowspeed(caster), NULL);
|
|
} else if (spellid == OT_S_TAILWIND) {
|
|
if (!target) target = caster;
|
|
if (isplayer(target)) {
|
|
msg("A strong wind propels you forwards!");
|
|
} else if (cansee(player, target)) {
|
|
char targname[BUFLEN];
|
|
getlfname(target, targname);
|
|
msg("A strong wind propels %s forwards!", targname);
|
|
}
|
|
} else if (spellid == OT_S_TELEPORT) {
|
|
cell_t *c = NULL;
|
|
lifeform_t *ally[8];
|
|
int nallies = 0;
|
|
|
|
// target is always the caster
|
|
if (!target) target = caster;
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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 < 8) {
|
|
power = 8;
|
|
}
|
|
}
|
|
|
|
if ((power < 5) || !isplayer(caster)) {
|
|
c = NULL;
|
|
if (blessed == B_CURSED) {
|
|
lifeform_t *lf;
|
|
// next to a random monster
|
|
for (lf = target->cell->map->lf ; lf ; lf = lf->next) {
|
|
if (areenemies(lf, target)) {
|
|
c = getrandomadjcell(lf->cell, &ccwalkable, B_NOEXPAND);
|
|
if (c) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!c) {
|
|
// random
|
|
while (!c || !cellwalkable(target, c, NULL) || celldangerous(target, c, B_FALSE, NULL)) {
|
|
c = getrandomcell(target->cell->map);
|
|
}
|
|
}
|
|
} else if (power >= 8) {
|
|
// controlled
|
|
snprintf(buf, BUFLEN, "Where will you teleport to?");
|
|
while (!c) {
|
|
int ch;
|
|
c = askcoords(buf, "Teleport->",TT_NONE, caster, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
if (!c) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else if (!c->known) {
|
|
// confirm
|
|
ch = askchar("Are you sure to want to teleport into the unknown?", "yn", "n", B_TRUE, B_FALSE);
|
|
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, B_FALSE);
|
|
if (ch != 'y') c = NULL;
|
|
}
|
|
}
|
|
} else { // ie. if (power >= 5)
|
|
int dir;
|
|
cell_t *poss[MAX_MAPW * MAX_MAPH];
|
|
int nposs;
|
|
int x,y;
|
|
int xmin,ymin;
|
|
int xmax,ymax;
|
|
// semicontrolled
|
|
// ask for dir
|
|
dir = askdir("Teleport in which direction (- to cancel)", B_TRUE, B_TRUE);
|
|
if ((dir == D_NONE) || (dir == D_MYSELF)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// 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;
|
|
default: // should never happen
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
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) && !celldangerous(target, c, B_FALSE, NULL)) {
|
|
poss[nposs] = c;
|
|
nposs++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nposs <= 0) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
|
|
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, &ccwalkable, B_ALLOWEXPAND);
|
|
if (!c) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
// 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, &ccwalkable, B_ALLOWEXPAND);
|
|
if (c) {
|
|
teleportto(ally[i], c, B_TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
rv = B_FALSE;
|
|
} else if (spellid == OT_S_TELEKINESIS) {
|
|
cell_t *where = NULL;
|
|
int failed = B_FALSE;
|
|
float maxweight;
|
|
|
|
// if no target object...
|
|
if (!targob) {
|
|
// ask for a target cell (to take objects from)
|
|
snprintf(buf, BUFLEN, "Where will you focus your telekinetic power?");
|
|
where = askcoords(buf, "Telekinesis->", 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, NULL, '\0', NULL, B_FALSE);
|
|
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],buf2[BUFLEN];
|
|
char oidbuf[BUFLENSMALL];
|
|
getobname(targob, obname, 1);
|
|
snprintf(buf, BUFLEN, "Where will you throw %s to?", obname);
|
|
// TODO: start trail from the object
|
|
snprintf(buf2, BUFLEN, "Telekinesis->%s->",obname);
|
|
|
|
sprintf(oidbuf, "%ld", targob->id);
|
|
addflag(caster->flags, F_THROWING, B_TRUE, NA, NA, oidbuf);
|
|
targcell = askcoords(buf, buf2,TT_MONSTER | TT_PLAYER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE);
|
|
killflagsofid(caster->flags, F_THROWING);
|
|
}
|
|
|
|
// 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 (getobmass(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.
|
|
addflag(caster->flags, F_TKTHROW, A_IQ, SK_SS_MENTAL, NA, NULL);
|
|
real_fireat(caster, targob, targob->amt, targcell, power, NULL, B_TRUE, spellid, NULL);
|
|
killflagsofid(caster->flags, F_TKTHROW);
|
|
// note that we use fireat() rather than throwat() to avoid
|
|
// calling taketime() twice.
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_THORNS) {
|
|
flag_t *f;
|
|
if (!target) target = caster;
|
|
|
|
f = addtempflag(target->flags, F_RETALIATE, DT_PIERCE, NA, NA, "1d4^sharp thorns", FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_TRUESTRIKE) {
|
|
flag_t *f;
|
|
if (!targcell) targcell = caster->cell;
|
|
target = targcell->lf;
|
|
if (!target) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
f = addtempflag(target->flags, F_TRUESTRIKE, power, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
f = addtempflag(target->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_TURNUNDEAD) {
|
|
int i,ndone = 0;
|
|
lifeform_t *lf;
|
|
if (caster && !isplayer(caster) && cansee(player, caster)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(caster, lfname);
|
|
msg("%s glows brightly with the essense of life!", lfname);
|
|
}
|
|
if (!target) {
|
|
target = caster;
|
|
}
|
|
// works on all undead in _target's_ sight
|
|
for (i = 0; i < target->nlos; i++) {
|
|
targcell = target->los[i];
|
|
lf = targcell->lf;
|
|
if (lf && (lf != caster) && isundead(lf)) {
|
|
int howlong = 10;
|
|
int worked = B_FALSE;
|
|
if ((gettr(caster)+power)*2 > gettr(lf)) {
|
|
worked = B_TRUE;
|
|
} else {
|
|
int diff;
|
|
diff = gettr(lf) - ((gettr(caster)+power)*2);
|
|
// if monster level is too high, the chance of working
|
|
// is 50%, -10% for every level too high.
|
|
if (pctchance(50-diff)) {
|
|
worked = B_TRUE;
|
|
}
|
|
}
|
|
howlong = rnd(10,20)+(power*2);
|
|
if (worked) {
|
|
// don't use scare() since it will fail due to them being undead.
|
|
addtempflag(lf->flags, F_FLEEFROM, target->id, NA, NA, NULL,howlong);
|
|
ndone++;
|
|
} else {
|
|
if (isplayer(lf)) {
|
|
msg("You resist the urge to flee from the lifeforce aura.");
|
|
} else if (cansee(player, lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(lf, lfname);
|
|
msg("%s flinches.", lfname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_TWIDDLE) {
|
|
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!"); more();
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if (cansee(player, caster)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
swapplaces(caster, target, B_CHANGEDIR, B_NOCHANGEDIR, B_ONPURPOSE);
|
|
} else if (spellid == OT_S_VENTRILOQUISM) {
|
|
if (isplayer(caster)) {
|
|
noise(targcell, NULL, NC_OTHER, SV_TALK+power, "your voice shouting", "You hear your voice shouting.");
|
|
} else {
|
|
noise(targcell, NULL, NC_OTHER, SV_TALK+power, "shouting", "You hear shouting.");
|
|
}
|
|
} else if ((spellid == OT_S_SUMMONANIMALSSM) ||
|
|
(spellid == OT_S_SUMMONANIMALSMD) ||
|
|
(spellid == OT_S_SUMMONANIMALSLG) ||
|
|
(spellid == OT_S_SUMMONSWARM) ||
|
|
(spellid == OT_S_FRIENDS) ||
|
|
(spellid == OT_S_HECTASSERVANT) ||
|
|
(spellid == OT_S_SUMMONDEMON)) {
|
|
int lifetime, nwant,ngot,successrate;
|
|
int tempcount = 0;
|
|
enum LFSIZE wantsize;
|
|
enum RACECLASS wantrc;
|
|
enum RACE wantrace = R_NONE;
|
|
int friendly = B_MAYBE;
|
|
char racename[BUFLEN];
|
|
|
|
lifeform_t *summoner = NULL;
|
|
if (spellid == OT_S_HECTASSERVANT) {
|
|
lifetime = 10;
|
|
} else {
|
|
lifetime = (power * 9) + rnd(1,power*2);
|
|
}
|
|
|
|
switch (spellid) {
|
|
case OT_S_FRIENDS:
|
|
switch (rnd(1,2)) {
|
|
case 1: wantrace = R_CHICKEN; break;
|
|
case 2: wantrace = R_BUTTERFLY; break;
|
|
}
|
|
wantrc = RC_ANY;
|
|
wantsize = SZ_ANY;
|
|
nwant = 8;
|
|
successrate = 100;
|
|
friendly = B_MAYBE;
|
|
break;
|
|
case OT_S_HECTASSERVANT:
|
|
wantrace = R_HECTASSERVANT;
|
|
wantrc = RC_ANY;
|
|
wantsize = SZ_ANY;
|
|
nwant = 1;
|
|
successrate = 100;
|
|
friendly = B_FALSE;
|
|
break;
|
|
case OT_S_SUMMONDEMON:
|
|
wantrc = RC_DEMON;
|
|
wantsize = SZ_ANY;
|
|
nwant = 1;
|
|
successrate = power*10;
|
|
if (caster->race->raceclass->id == RC_DEMON) {
|
|
friendly = B_TRUE;
|
|
} else if (onein(3)) {
|
|
friendly = B_FALSE;
|
|
} else {
|
|
friendly = B_TRUE;
|
|
}
|
|
break;
|
|
case OT_S_SUMMONANIMALSSM:
|
|
wantrc = RC_ANIMAL;
|
|
wantsize = SZ_SMALL;
|
|
nwant = rnd(2,3);
|
|
successrate = 100;
|
|
friendly = B_TRUE;
|
|
break;
|
|
case OT_S_SUMMONANIMALSMD:
|
|
nwant = rnd(2,3);
|
|
successrate = 100;
|
|
friendly = B_TRUE;
|
|
break;
|
|
case OT_S_SUMMONANIMALSLG:
|
|
wantrc = RC_ANIMAL;
|
|
wantsize = SZ_LARGE;
|
|
nwant = rnd(2,3);
|
|
successrate = 100;
|
|
friendly = B_TRUE;
|
|
break;
|
|
case OT_S_SUMMONSWARM:
|
|
wantrc = RC_ANY;
|
|
wantsize = SZ_ANY;
|
|
nwant = 1;
|
|
switch (power) {
|
|
case 1: wantrace = R_SWARMRAT; break;
|
|
case 2: wantrace = R_SWARMSPIDER; break;
|
|
default: wantrace = R_SWARMLOCUST; break;
|
|
}
|
|
successrate = 100;
|
|
friendly = B_TRUE;
|
|
break;
|
|
default:
|
|
wantrc = RC_ANY;
|
|
wantsize = SZ_ANY;
|
|
nwant = 1;
|
|
successrate = 100;
|
|
friendly = B_TRUE;
|
|
break;
|
|
}
|
|
|
|
// override with forced race?
|
|
if (caster && getforcedspellrace(caster, spellid, racename, &tempcount)) {
|
|
race_t *r;
|
|
r = findracebyname(racename, NULL);
|
|
if (r) {
|
|
wantrace = r->id;
|
|
wantrc = RC_ANY;
|
|
wantsize = SZ_ANY;
|
|
}
|
|
}
|
|
// override count
|
|
if (tempcount > 0) {
|
|
nwant = tempcount;
|
|
}
|
|
|
|
if (!pctchance(successrate)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
// special case, mainly used for when gods summon creatures for you
|
|
if (target) {
|
|
summoner = target;
|
|
} else {
|
|
summoner = caster;
|
|
}
|
|
|
|
if (!isplayer(caster)) {
|
|
friendly = B_TRUE;
|
|
}
|
|
|
|
ngot = summonlfs(caster, caster->cell, wantrace, wantrc, wantsize, AL_NONE, nwant, lifetime, friendly);
|
|
if (!ngot) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
} else {
|
|
if (haslos(player, caster->cell)) { // intentionally not using cansee
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
if (wantrace) {
|
|
race_t *r;
|
|
r = findrace(wantrace);
|
|
if (ngot == 1) {
|
|
msg("%s %s appears near %s!", needan(r->name) ? "An" : "A", r->name, castername);
|
|
} else {
|
|
char *plur;
|
|
plur = strdup(r->name);
|
|
makeplural(&plur);
|
|
msg("%s appear around %s!", plur, castername);
|
|
free(plur);
|
|
}
|
|
} else {
|
|
raceclass_t *rc;
|
|
rc = findraceclass(wantrc);
|
|
if (ngot == 1) {
|
|
msg("%s %s appears near %s!", needan(rc->name) ? "An" : "A", rc->name, castername);
|
|
} else {
|
|
char *plur;
|
|
plur = strdup(rc->name);
|
|
makeplural(&plur);
|
|
msg("%s appear around %s!", plur, castername);
|
|
free(plur);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} 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_TRUE;
|
|
}
|
|
o = addob(target->pack, "energy blade");
|
|
if (o) {
|
|
if (canweild(target, o)) {
|
|
flag_t *f;
|
|
object_t *oldwep;
|
|
// unweild current weapon
|
|
|
|
oldwep = getweapon(target);
|
|
if (oldwep) {
|
|
if (iscursed(oldwep)) setblessed(oldwep, B_UNCURSED);
|
|
unweild(target, oldwep);
|
|
}
|
|
// announce
|
|
if (isplayer(target)) {
|
|
msg("^%cA blade of pure energy forms in your hands!", getlfcol(target, CC_GOOD));
|
|
} else if (cansee(player, target)) {
|
|
msg("^%cA blade of pure energy forms in %s%s hands!", getlfcol(target, CC_GOOD),castername,
|
|
getpossessive(castername));
|
|
}
|
|
weild(target, o);
|
|
// set its damage value
|
|
f = hasflag(o->flags, F_DAM);
|
|
if (f) {
|
|
f->val[1] = 3+power;
|
|
}
|
|
addflag(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL);
|
|
} else {
|
|
killob(o);
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_TRUE;
|
|
}
|
|
} else {
|
|
fizzle(caster);
|
|
stopspell(caster, spellid);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_WALLOFFIRE) {
|
|
object_t *o;
|
|
cell_t *c;
|
|
int vdist = 0,hdist = 0;
|
|
int vbad = B_FALSE, hbad = B_FALSE;
|
|
int walldir;
|
|
int seen = B_FALSE;
|
|
char firename[BUFLEN];
|
|
|
|
if (power < 5) {
|
|
sprintf(firename, "small fire");
|
|
} else if (power < 8) {
|
|
sprintf(firename, "medium fire");
|
|
} else {
|
|
sprintf(firename, "large fire");
|
|
}
|
|
|
|
// get vert distance
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) {
|
|
vdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
vbad = B_TRUE; break;
|
|
}
|
|
}
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) {
|
|
vdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
vbad = B_TRUE; break;
|
|
}
|
|
}
|
|
// get horz dist
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) {
|
|
hdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
hbad = B_TRUE; break;
|
|
}
|
|
}
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) {
|
|
hdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
hbad = B_TRUE; break;
|
|
}
|
|
}
|
|
|
|
if (vbad) vdist = 0;
|
|
if (hbad) hdist = 0;
|
|
|
|
if ((vdist == 0) && (hdist == 0)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// 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, firename);
|
|
}
|
|
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, firename);
|
|
}
|
|
} 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, firename);
|
|
}
|
|
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, firename);
|
|
}
|
|
}
|
|
|
|
if (seen) {
|
|
drawscreen();
|
|
msg("A wall of fire appears!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (isplayer(caster)) {
|
|
angergodmaybe(R_GODNATURE, 25, GA_HERESY);
|
|
}
|
|
} else if (spellid == OT_S_WALLOFICE) {
|
|
object_t *o;
|
|
int donesomething = B_FALSE;
|
|
cell_t *c;
|
|
int vdist = 0,hdist = 0;
|
|
int vbad = B_FALSE, hbad = B_FALSE;
|
|
int walldir;
|
|
int seen = B_FALSE;
|
|
flag_t *f;
|
|
// get vert distance
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) {
|
|
vdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
vbad = B_TRUE; break;
|
|
}
|
|
}
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) {
|
|
vdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
vbad = B_TRUE; break;
|
|
}
|
|
}
|
|
// get horz dist
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) {
|
|
hdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
hbad = B_TRUE; break;
|
|
}
|
|
}
|
|
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) {
|
|
hdist++;
|
|
if (c->lf && (c->lf == caster)) {
|
|
hbad = B_TRUE; break;
|
|
}
|
|
}
|
|
|
|
if (vbad) vdist = 0;
|
|
if (hbad) hdist = 0;
|
|
|
|
if ((vdist == 0) && (hdist == 0)) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// 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, B_FALSE), 1, NULL,
|
|
100+(power*10), B_DOANNOUNCE, B_DODAM);
|
|
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, B_FALSE), 1, NULL,
|
|
100+(power*10), B_DOANNOUNCE, B_DODAM);
|
|
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, B_FALSE), 1, NULL,
|
|
100+(power*10), B_DOANNOUNCE, B_DODAM);
|
|
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, B_FALSE), 1, NULL,
|
|
100+(power*10), B_DOANNOUNCE, B_DODAM);
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (seen) {
|
|
drawscreen();
|
|
msg("A wall of ice appears!");
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (isplayer(caster)) {
|
|
angergodmaybe(R_GODFIRE, 50, GA_HERESY);
|
|
}
|
|
} else if (spellid == OT_S_WATERJET) {
|
|
char lfname[BUFLEN];
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell,i;
|
|
int dam;
|
|
// 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;
|
|
}
|
|
|
|
|
|
dam = roll("2d6");
|
|
|
|
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, n;
|
|
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));
|
|
}
|
|
|
|
// does less damage the further you are away.
|
|
if (dam > 0) dam--;
|
|
|
|
// water damage will generally be turn to zero unless people are specifically
|
|
// vulnerable to water, so do bashing damage too.
|
|
losehp(target, dam, DT_WATER, caster, "a high-pressure jet of water");
|
|
losehp(target, dam, 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, B_DOANNOUNCE, B_DODAM);
|
|
// rust
|
|
getallouterarmour(target, arm, &narm);
|
|
for (n = 0; n < narm; n++) {
|
|
takedamage(arm[n], R_TRUSTY, DT_WATER, caster);
|
|
}
|
|
// add water object
|
|
addob(retcell[i]->obpile, "large puddle of water");
|
|
break;
|
|
} else {
|
|
damageallobs(NULL, retcell[i]->obpile, 0, DT_WATER, caster);
|
|
// add water object
|
|
addob(retcell[i]->obpile, "large puddle of water");
|
|
}
|
|
}
|
|
} else if (spellid == OT_S_WARPWOOD) {
|
|
object_t *o,*nexto;
|
|
int ndone = 0;
|
|
|
|
if (!targcell) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
|
|
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) || (o->type->material->id == MT_DRAGONWOOD)) {
|
|
if (isequipped(o)) {
|
|
int dam;
|
|
dam = rolldie(power, 4);
|
|
if (haslos(player, targcell)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s twist%s and writhe%s!",obname,
|
|
(o->amt == 1) ? "s" : "",
|
|
(o->amt == 1) ? "s" : "");
|
|
}
|
|
takedamage(o, dam, DT_DECAY, caster);
|
|
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) || (o->type->material->id == MT_DRAGONWOOD)) {
|
|
int dam;
|
|
dam = rolldie(power, 4);
|
|
if (haslos(player, targcell)) {
|
|
char obname[BUFLEN];
|
|
getobname(o, obname, o->amt);
|
|
msg("%s twist%s and writhe%s!",obname,
|
|
(o->amt == 1) ? "s" : "",
|
|
(o->amt == 1) ? "s" : "");
|
|
}
|
|
|
|
takedamage(o, dam, DT_DIRECT, caster);
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
ndone++;
|
|
}
|
|
}
|
|
if (targcell->lf) {
|
|
if ((targcell->lf->material->id == MT_WOOD) ||
|
|
(targcell->lf->material->id == MT_DRAGONWOOD)) {
|
|
int dam;
|
|
char dambuf[BUFLEN];
|
|
dam = rolldie(power, 4);
|
|
if (haslos(player, targcell)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(targcell->lf, lfname);
|
|
msg("%s twist%s and writhe%s!",lfname,
|
|
isplayer(targcell->lf) ? "" : "s",
|
|
isplayer(targcell->lf) ? "" : "s");
|
|
}
|
|
|
|
sprintf(dambuf, "%s%s warp wood spell", castername, getpossessive(castername));
|
|
losehp(targcell->lf, dam, DT_DIRECT, caster, dambuf);
|
|
if (haslos(player, targcell)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
ndone++;
|
|
}
|
|
}
|
|
if (!ndone) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_WEAKEN) {
|
|
target = targcell->lf;
|
|
|
|
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
|
|
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*-15, NA, NULL, howlong);
|
|
f->obfrom = OT_S_WEAKEN;
|
|
f = addtempflag(target->flags, F_MELEEDAMPCT, 50, NA, NA, NULL, howlong);
|
|
f->obfrom = OT_S_WEAKEN;
|
|
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_WEB) {
|
|
char numbuf[BUFLEN];
|
|
int i;
|
|
|
|
numtotext(power, numbuf);
|
|
// 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 {
|
|
condset_t cs;
|
|
initcondv(&cs, CC_SOLID, B_FALSE, NA, CC_NONE);
|
|
c = getrandomadjcell(targcell, &cs, B_ALLOWEXPAND);
|
|
}
|
|
if (c) {
|
|
if (c->lf && (c->lf->race->baseid != R_SPIDER)) {
|
|
if (skillcheck(c->lf, SC_DODGE, 90+(power*10), 0)) {
|
|
if (isplayer(c->lf)) {
|
|
msg("You dodge a web.");
|
|
} else if (cansee(player, c->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("%s dodges a web!", lfname);
|
|
}
|
|
} else {
|
|
addob(c->obpile, "web");
|
|
if (isplayer(c->lf)) {
|
|
msg("^%cYou suddenly find yourself in a web!", getlfcol(c->lf, CC_BAD));
|
|
} else if (cansee(player, c->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("^%c%s is stuck in a web!", getlfcol(c->lf, CC_BAD), lfname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
} else if (spellid == OT_S_WHATGOESUP) {
|
|
if (isplayer(caster)) {
|
|
msg("A positive gravity field forms around you!");
|
|
} else if (cansee(player, caster)) {
|
|
msg("A positive gravity field forms around %s!", castername);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
} else if ((spellid == OT_S_WHIRLWIND) || (spellid == OT_S_TORNADO) || (spellid == OT_S_HURRICANE)) {
|
|
int failed = B_FALSE;
|
|
char obname[BUFLEN],obdesc[BUFLEN];
|
|
switch (spellid) {
|
|
case OT_S_HURRICANE:
|
|
strcpy(obname, "hurricane");
|
|
strcpy(obdesc, "raging hurricane");
|
|
break;
|
|
case OT_S_TORNADO:
|
|
strcpy(obname, "tornado");
|
|
strcpy(obdesc, "towering tornado");
|
|
break;
|
|
default:
|
|
case OT_S_WHIRLWIND:
|
|
strcpy(obname, "whirlwind");
|
|
strcpy(obdesc, "spinning whirlwind");
|
|
break;
|
|
}
|
|
if (targcell) {
|
|
cell_t *c[5];
|
|
int ncells = 0,i,ndone = 0,doneannounce = B_FALSE;
|
|
if (spellid == OT_S_HURRICANE) {
|
|
c[0] = targcell;
|
|
c[1] = getcellindir(targcell, DC_N);
|
|
c[2] = getcellindir(targcell, DC_E);
|
|
c[3] = getcellindir(targcell, DC_S);
|
|
c[4] = getcellindir(targcell, DC_W);
|
|
ncells = 5;
|
|
} else {
|
|
c[0] = targcell;
|
|
ncells = 1;
|
|
}
|
|
for (i = 0; i < ncells; i++) {
|
|
if (c[i]) {
|
|
int ok = B_TRUE;
|
|
if (c[i]->type->solid) {
|
|
if (spellid == OT_S_HURRICANE) {
|
|
// this should always kill it!
|
|
damagecell(c[i], c[i]->hp, DT_DIRECT, NULL);
|
|
} else {
|
|
ok = B_FALSE;
|
|
}
|
|
}
|
|
if (ok) {
|
|
flag_t *f;
|
|
object_t *o;
|
|
// create flame there
|
|
if (haslos(player, c[i])) {
|
|
if (!doneannounce) {
|
|
msg("A %s appears!", obdesc);
|
|
doneannounce = B_TRUE;
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
o = addob(c[i]->obpile, obname);
|
|
// magically boost hp based on power
|
|
f = hasflag(o->flags, F_OBHP);
|
|
if (f) {
|
|
f->val[0] += (power*6);
|
|
// centre hurricane lasts a bit longer
|
|
if (i == 0) {
|
|
f->val[0] += 5;
|
|
}
|
|
f->val[1] = f->val[0];
|
|
}
|
|
setobcreatedby(o, caster);
|
|
// for non-centre hurricanes, remove randommove flag
|
|
// (they will move with the original)
|
|
if (i != 0) {
|
|
killflagsofid(o->flags, F_OBMOVESRANDOMLY);
|
|
}
|
|
ndone++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!ndone) {
|
|
failed = B_TRUE;
|
|
}
|
|
} else {
|
|
failed = B_TRUE;
|
|
}
|
|
|
|
if (failed) {
|
|
fizzle(caster);
|
|
return B_TRUE;
|
|
}
|
|
} else if (spellid == OT_S_WINDSHIELD) {
|
|
char retaltext[BUFLEN];
|
|
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;
|
|
|
|
sprintf(retaltext, "1d%d^whirling debris", power);
|
|
f = addtempflag(target->flags, F_RETALIATE, DT_BASH, NA, NA, retaltext, FROMSPELL);
|
|
f->obfrom = spellid;
|
|
} else if (spellid == OT_S_WISHLIMITED) {
|
|
object_t *o = NULL;
|
|
char obname[BUFLEN];
|
|
char ch;
|
|
int appearonground = B_FALSE;
|
|
if (!target) target = caster;
|
|
initprompt(&prompt, "For what do you wish?");
|
|
addchoice(&prompt, 'a', "Wealth (riches beyond your imagination)", NULL, NULL, NULL);
|
|
addchoice(&prompt, 'b', "Power (an item with which to smite your foes)", NULL, NULL, NULL);
|
|
addchoice(&prompt, 'c', "Protection (an item to defend against harm)", NULL, NULL, NULL);
|
|
addchoice(&prompt, 'd', "Fame (followers to obey your every whim)", NULL, NULL, NULL);
|
|
addchoice(&prompt, 'e', "Knowledge (identify everything and more)", NULL, NULL, NULL);
|
|
addchoice(&prompt, 'f', "Magic (books full of arcane writings)", NULL, NULL, NULL);
|
|
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
|
|
if (isplayer(target)) {
|
|
ch = getchoice(&prompt);
|
|
} else {
|
|
ch = 'e';
|
|
// ie. don't select 'knowledge'
|
|
while (ch == 'e') {
|
|
ch = rnd('a', 'f');
|
|
}
|
|
}
|
|
|
|
// warn if this was bad...
|
|
if (isplayer(target) && (blessed == B_CURSED)) {
|
|
msg("^%cUh oh, you have a bad feeling about this...", getlfcol(target, CC_VBAD)); more();
|
|
}
|
|
|
|
if (ch == 'a') { // wealth (gold, bad: massive chunk of gold which you can't carry)
|
|
if (blessed == B_CURSED) {
|
|
snprintf(buf, BUFLEN, "gigantic golden boulder");
|
|
appearonground = B_TRUE;
|
|
} else {
|
|
snprintf(buf, BUFLEN, "1000-2000 gold dollars");
|
|
}
|
|
} else if (ch == 'b') { // power (weapons, bad: battery)
|
|
if (blessed == B_CURSED) {
|
|
snprintf(buf, BUFLEN, "battery");
|
|
} else {
|
|
skill_t *sk,*poss[MAXSKILLS];
|
|
int nposs = 0;
|
|
// get a list of all valid weapon skills
|
|
for (sk = firstskill ; sk ; sk = sk->next) {
|
|
if (isweaponskill(sk->id) && getskill(target, sk->id)) {
|
|
poss[nposs++] = sk;
|
|
}
|
|
}
|
|
if (nposs) {
|
|
int nweps = 0;
|
|
objecttype_t *ot;
|
|
// pick a random one
|
|
sk = poss[rnd(0,nposs-1)];
|
|
// find all associated weapons
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if (hasflagval(ot->flags, F_USESSKILL, sk->id, NA, NA, NULL)) {
|
|
if (gettechlevel(ot->id) > getskill(player, SK_TECHUSAGE)) {
|
|
} else {
|
|
nweps++;
|
|
}
|
|
}
|
|
}
|
|
if (nweps) {
|
|
int sel,n = 0;
|
|
sel = rnd(0,nweps-1);
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if (hasflagval(ot->flags, F_USESSKILL, sk->id, NA, NA, NULL)) {
|
|
if (gettechlevel(ot->id) > getskill(player, SK_TECHUSAGE)) {
|
|
} else {
|
|
if (n == sel) break;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
snprintf(buf, BUFLEN, "excellent branded %s", ot->name);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "stick");
|
|
}
|
|
} else {
|
|
snprintf(buf, BUFLEN, "stick");
|
|
}
|
|
}
|
|
} else if (ch == 'c') { // protection. bad: turn to stone
|
|
enum BODYPART bp,poss[MAXBODYPARTS];
|
|
int nposs = 0;
|
|
if (blessed == B_CURSED) {
|
|
stone(caster);
|
|
} else {
|
|
// get a list of all valid body parts
|
|
if (hasbp(target, BP_HEAD)) poss[nposs++] = BP_HEAD;
|
|
if (hasbp(target, BP_SHOULDERS)) poss[nposs++] = BP_SHOULDERS;
|
|
if (hasbp(target, BP_BODY)) poss[nposs++] = BP_BODY;
|
|
if (hasbp(target, BP_LEGS)) poss[nposs++] = BP_LEGS;
|
|
if (hasbp(target, BP_FEET)) poss[nposs++] = BP_FEET;
|
|
if (hasbp(target, BP_HANDS)) poss[nposs++] = BP_HANDS;
|
|
if (nposs) {
|
|
int narms = 0;
|
|
objecttype_t *ot;
|
|
// pick a random body part
|
|
bp = poss[rnd(0,nposs-1)];
|
|
// find all associated armour
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if ((ot->obclass->id == OC_ARMOUR) &&
|
|
hasflagval(ot->flags, F_GOESON, bp, NA, NA, NULL)) {
|
|
// make sure this armour won't give us a penalty
|
|
if (!wouldgivepenalty(target, ot->flags)) {
|
|
narms++;
|
|
}
|
|
|
|
}
|
|
}
|
|
if (narms) {
|
|
int sel,n = 0;
|
|
sel = rnd(0,narms-1);
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if ((ot->obclass->id == OC_ARMOUR) &&
|
|
hasflagval(ot->flags, F_GOESON, bp, NA, NA, NULL)) {
|
|
if (!wouldgivepenalty(target, ot->flags)) {
|
|
if (n == sel) break;
|
|
n++;
|
|
}
|
|
}
|
|
}
|
|
snprintf(buf, BUFLEN, "excellent branded %s", ot->name);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "sun hat");
|
|
}
|
|
} else {
|
|
snprintf(buf, BUFLEN, "sun hat");
|
|
}
|
|
}
|
|
} else if (ch == 'd') { // fame (allies bad: hostile lfs )
|
|
lifeform_t *lf;
|
|
cell_t *c;
|
|
job_t *j;
|
|
char *p,lfname[BUFLEN];
|
|
// summon a human
|
|
c = getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND);
|
|
j = getrandomjob(B_TRUE);
|
|
lf = addlf(c, R_HUMAN, target->level);
|
|
givejob(lf, j->id);
|
|
getlfnamea(lf, lfname);
|
|
if (cansee(player, lf)) {
|
|
msg("%s appears!", lfname);
|
|
}
|
|
if (blessed == B_CURSED) {
|
|
aiattack(lf, target, aigetchasetime(lf));
|
|
} else {
|
|
petify(lf, target);
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
p = assignnpcname(lf->flags);
|
|
sayphrase(lf, SP_RECRUIT_ACCEPT, SV_TALK, NA, p, lf);
|
|
}
|
|
|
|
strcpy(buf, "");
|
|
} else if (ch == 'e') { // knowledge (makeknown everything you have plus 10% chance for everything else. bad: insanity)
|
|
// only possible for the player!
|
|
strcpy(buf, "");
|
|
if (isplayer(target)) {
|
|
if (blessed == B_CURSED) {
|
|
int newval;
|
|
msg("^%cYour sanity is blasted by a flood of alien knowledge!", getlfcol(caster, CC_VBAD));
|
|
newval = caster->att[A_IQ] - 25;
|
|
caster->baseatt[A_IQ] = newval;
|
|
setattr(caster, A_IQ, newval);
|
|
} else {
|
|
knowledge_t *k;
|
|
msg("^GKnowledge floods into your mind!");
|
|
for (o = target->pack->first ; o ; o = o->next) {
|
|
if (!isidentified(o)) {
|
|
identify(o);
|
|
getobname(o, obname, o->amt);
|
|
msgnocap("%c - %s.", o->letter, obname);
|
|
}
|
|
}
|
|
// now identify everything that you have tried,
|
|
// and 10% chance of identifying others
|
|
for (k = knowledge; k ; k = k->next) {
|
|
if ((k->known == B_TRIED) || pctchance(10)) {
|
|
makeknown(k->id);
|
|
}
|
|
}
|
|
msg("(check '%c' to see your knowledge)", cmdtochar(CMD_INFOKNOWLEDGE));
|
|
}
|
|
}
|
|
} else if (ch == 'f') { // magic (spellbooks), bad: gain 'explode self' magic
|
|
if (blessed == B_CURSED) {
|
|
flag_t *f;
|
|
f = lfhasflagval(target, F_CANWILL, OT_A_EXPLODESELF, NA, NA, NULL);
|
|
if (f) {
|
|
if (isplayer(target)) nothinghappens();
|
|
} else {
|
|
addflag(target->flags, F_CANWILL, OT_A_EXPLODESELF, 50, 50, NULL);
|
|
}
|
|
} else {
|
|
objecttype_t *ot,*poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
// find all castable spells (spellpower > 0)
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if ((ot->obclass->id == OC_SPELL) && getspellpower(target, ot->id)) {
|
|
poss[nposs++] = ot;
|
|
}
|
|
}
|
|
if (!nposs) {
|
|
// give knowledge of a random spell school plus sorcery
|
|
enum SPELLSCHOOL school;
|
|
skill_t *poss2[MAXSKILLS],*sk;
|
|
int nposs2 = 0;
|
|
for (school = SS_AIR; school < SS_LAST; school++) {
|
|
sk = findskill(getschoolskill(school));
|
|
poss2[nposs2++] = sk;
|
|
}
|
|
sk = poss2[rnd(0,nposs2-1)];
|
|
giveskill(target, sk->id);
|
|
|
|
// ... then try to find castable spells again
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if ((ot->obclass->id == OC_SPELL) && getspellpower(target, ot->id)) {
|
|
poss[nposs++] = ot;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nposs) {
|
|
// pick a random spell from this list
|
|
ot = poss[rnd(0,nposs-1)];
|
|
snprintf(buf, BUFLEN, "spellbook of %s",ot->name);
|
|
} else {
|
|
strcpy(buf, "");
|
|
}
|
|
}
|
|
} else if (ch == '-') { // ie. nothing
|
|
if (isplayer(target)) {
|
|
msg("You resist the temptation of instant gratification.");
|
|
if (frompot || fromob) {
|
|
// small wisdom increase
|
|
modattr(target, A_WIS, 2);
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
if (strlen(buf)) {
|
|
if (appearonground) {
|
|
cell_t *where;
|
|
where = real_getrandomadjcell(target->cell, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, target);
|
|
if (!where) {
|
|
where = target->cell;
|
|
}
|
|
o = addob(where->obpile, buf);
|
|
if (o) {
|
|
noise(caster->cell, NULL, NC_OTHER, SV_WHISPER, "something hitting the ground.", NULL);
|
|
if (haslos(player, where)) {
|
|
getobname(o, obname, o->amt);
|
|
msg("%s appear%s on the ground!", obname, (o->amt == 1) ? "s" : "");
|
|
}
|
|
}
|
|
} else {
|
|
// give the object
|
|
o = addob(target->pack, buf);
|
|
getobname(o, obname, o->amt);
|
|
msgnocap("%c - %s.", o->letter, obname);
|
|
}
|
|
}
|
|
|
|
// now age the caster
|
|
if (!isgod(caster)) {
|
|
age(caster, 25);
|
|
}
|
|
|
|
if (isplayer(target)) {
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
if (o && isplayer(caster)) {
|
|
angergodmaybe(R_GODFIRE, 75, GA_HERESY);
|
|
}
|
|
} else if ((spellid == OT_S_WISH) || (spellid == OT_S_GIFT)) {
|
|
object_t *o;
|
|
if (isplayer(caster)) {
|
|
char lfname[BUFLEN];
|
|
char question[BUFLEN];
|
|
int i,nbad = 0;
|
|
obpile_t *tempop = NULL;
|
|
object_t *badob = NULL;
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
// ask for target
|
|
if (spellid == OT_S_GIFT) {
|
|
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE;
|
|
target = targcell->lf;
|
|
} else {
|
|
target = caster;
|
|
}
|
|
getlfname(target, lfname);
|
|
|
|
// ask for an object
|
|
if (spellid == OT_S_GIFT) {
|
|
snprintf(question, BUFLEN, "What gift will %s receive",lfname);
|
|
} else {
|
|
snprintf(question, BUFLEN, "For what do you wish");
|
|
}
|
|
askstring(question, '?', buf, BUFLEN, NULL);
|
|
|
|
tempop = addobpile(NULL, NULL, NULL);
|
|
addob(tempop, buf);
|
|
|
|
if (!hasjob(player, J_GOD)) {
|
|
// remove any invalid objects
|
|
badob = hasobwithflag(tempop, F_NOWISH);
|
|
while (badob) {
|
|
flag_t *f;
|
|
lifeform_t *rndgod;
|
|
|
|
f = hasflag(badob->flags, F_LINKGOD);
|
|
if (f) {
|
|
rndgod = findgod(f->val[0]);
|
|
} else {
|
|
rndgod = getrandomgod();
|
|
}
|
|
|
|
assert(rndgod);
|
|
godsay(rndgod->race->id, B_TRUE, getflagtext(rndgod->flags, F_GODNOWISHTEXT));
|
|
more();
|
|
|
|
killob(badob);
|
|
nbad++;
|
|
|
|
badob = hasobwithflag(tempop, F_NOWISH);
|
|
}
|
|
}
|
|
|
|
nretobs -= nbad;
|
|
|
|
//addob(target->cell->obpile, buf);
|
|
if (nretobs > 0) {
|
|
int ncreated = 0;
|
|
for (i = 0; i < nretobs; i++) {
|
|
char obname[BUFLEN];
|
|
o = retobs[i];
|
|
// make sure armour fits
|
|
if (hasflag(o->flags, F_MULTISIZE)) {
|
|
resizeobject(o, getlfsize(target));
|
|
}
|
|
if (!hasflag(o->flags, F_IMPASSABLE) && canpickup(target, o, ALL)) {
|
|
// you gain it.
|
|
relinkob(o, target->pack); // move to pack
|
|
getobname(o, obname, o->amt);
|
|
|
|
if (isplayer(target)) {
|
|
msgnocap("%c - %s.", o->letter, obname);
|
|
} else {
|
|
msg("%s is gifted with %s.", lfname, obname);
|
|
}
|
|
ncreated++;
|
|
} 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, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED,
|
|
NULL, target);
|
|
o = relinkob(o, newloc->obpile);
|
|
} else {
|
|
// move to target cell under the lf
|
|
o = relinkob(o, target->cell->obpile);
|
|
}
|
|
if (o) {
|
|
getobname(o, obname, o->amt);
|
|
noise(caster->cell, NULL, NC_OTHER, SV_WHISPER, "something hitting the ground.", NULL);
|
|
if (!isblind(caster)) {
|
|
msg("%s appear%s on the ground!", obname, OBS1(o));
|
|
}
|
|
ncreated++;
|
|
//
|
|
o = obaffectsothers(o);
|
|
} 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.
|
|
}
|
|
}
|
|
}
|
|
if (ncreated) {
|
|
if (isplayer(caster)) {
|
|
angergodmaybe(R_GODFIRE, 75, GA_HERESY);
|
|
}
|
|
}
|
|
} else {
|
|
// couldn't make it appear - ob doesn't exist
|
|
msg("The air in front of %s seems to ripple for a while.", lfname);
|
|
}
|
|
if (tempop) killobpile(tempop);
|
|
|
|
// now age the caster
|
|
if (!isgod(caster)) {
|
|
age(caster, 50);
|
|
}
|
|
} 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;
|
|
}
|
|
|
|
enum SPELLSCHOOL findspellschoolbyname(char *buf) {
|
|
enum SPELLSCHOOL ss;
|
|
for (ss = SS_NONE; ss <= SS_LAST; ss++) {
|
|
if (!strcasecmp(buf, getschoolname(ss))) return ss;
|
|
}
|
|
return SS_NONE;
|
|
}
|
|
|
|
void fizzle(lifeform_t *caster) {
|
|
if (!caster) return;
|
|
if (isplayer(caster)) {
|
|
if (lfhasflag(caster, F_CASTINGSPELL)) {
|
|
msg("Your spell fizzles.");
|
|
} else {
|
|
nothinghappens();
|
|
}
|
|
} else if (cansee(player, caster)) {
|
|
char buf[BUFLEN];
|
|
getlfname(caster, buf);
|
|
capitalise(buf);
|
|
msg("%s's spell fizzles.", buf);
|
|
}
|
|
}
|
|
|
|
enum OBTYPE getfirstwizspell(enum SPELLSCHOOL school) {
|
|
enum OBTYPE firstspell = OT_S_MANASPIKE;
|
|
switch (school) {
|
|
case SS_AIR: firstspell = OT_S_PROPELMISSILE; break;
|
|
case SS_COLD: firstspell = OT_S_CHILL; break;
|
|
case SS_FIRE: firstspell = OT_S_SPARK; break;
|
|
case SS_DEATH: firstspell = OT_S_ANIMATEDEAD; break;
|
|
case SS_DIVINATION: firstspell = OT_S_SIXTHSENSE; break;
|
|
case SS_TRANSLOCATION: firstspell = OT_S_APPORTATION; break;
|
|
case SS_SUMMONING: firstspell = OT_S_GLYPHWARDING; break;
|
|
case SS_LIFE: firstspell = OT_S_HEALINGMIN; break;
|
|
default:
|
|
case SS_WILD: firstspell = OT_S_MANASPIKE; break;
|
|
}
|
|
return firstspell;
|
|
}
|
|
|
|
char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr, int *count) {
|
|
flag_t *f;
|
|
// forced?
|
|
f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL);
|
|
if (!f) {
|
|
f = lfhasflagval(lf, F_CANCAST, spellid, NA, NA, NULL);
|
|
}
|
|
if (f) {
|
|
texttospellopts(f->text, "race:", racestr, "count:", count, NULL);
|
|
if (strlen(racestr)) {
|
|
return racestr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
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 {
|
|
cost = pow(2,getspelllevel(oid)-1);
|
|
/*
|
|
f = hasflag(ot->flags, F_SPELLLEVEL);
|
|
if (f) {
|
|
switch (getspellschool(oid)) {
|
|
case SS_MENTAL:
|
|
case SS_ALLOMANCY:
|
|
cost = f->val[0];
|
|
break;
|
|
default:
|
|
cost = f->val[0] * f->val[0];
|
|
break;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
return cost;
|
|
}
|
|
|
|
int getmrdiff(enum OBTYPE spellid, int power) {
|
|
int lev,diff;
|
|
lev = getspelllevel(spellid);
|
|
diff = 90 + (lev*10) + (power*5);
|
|
return diff;
|
|
}
|
|
|
|
|
|
// maxlev <= 0 means 'any lev'
|
|
enum OBTYPE getrandomspell(int maxlev) {
|
|
int wantlev;
|
|
objecttype_t *ot,*poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
|
|
wantlev = 1;
|
|
while (rnd(1,2) == 1) {
|
|
wantlev++;
|
|
}
|
|
limit(&wantlev, 1, maxlev);
|
|
|
|
// 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;
|
|
}
|
|
|
|
// wantlev = 0 means 'any level'
|
|
enum OBTYPE getrandomspellfromschool(enum SPELLSCHOOL school, int wantlev) {
|
|
objecttype_t *ot,*poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
|
|
// get list of all spells of this school
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if ((ot->obclass->id == OC_SPELL) && spellisfromschool(ot->id, school)) {
|
|
if (!hasflag(ot->flags, F_NORANDOM)) {
|
|
if ((wantlev == 0) || (getspelllevel(ot->id) == wantlev)) {
|
|
poss[nposs++] = ot;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nposs <= 0) {
|
|
return OT_NONE;
|
|
}
|
|
ot = poss[rnd(0,nposs-1)];
|
|
return ot->id;
|
|
}
|
|
|
|
// if wantknowntolf isn't passed, then wantknown is ignored.
|
|
enum SPELLSCHOOL getrandomspellschool(lifeform_t *wantknowntolf, int wantknown) {
|
|
enum SPELLSCHOOL poss[MAXCANDIDATES],ss;
|
|
int nposs = 0;
|
|
// count valid schools
|
|
for (ss = SS_NONE; ss <= SS_LAST; ss++) {
|
|
if (schoolappearsinbooks(ss) && !streq(getschoolname(ss), "badschool")) {
|
|
int valid = B_FALSE;
|
|
if (wantknowntolf) {
|
|
enum SKILLLEVEL slev;
|
|
slev = getskill(wantknowntolf, getschoolskill(ss));
|
|
if ((slev && wantknown) || (!slev && !wantknown)) {
|
|
valid = B_TRUE;
|
|
}
|
|
} else {
|
|
valid = B_TRUE;
|
|
}
|
|
if (valid) {
|
|
poss[nposs++] = ss;
|
|
}
|
|
}
|
|
}
|
|
if (!nposs) {
|
|
return SS_NONE;
|
|
}
|
|
return poss[rnd(0,nposs-1)];
|
|
}
|
|
|
|
|
|
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_LIFE:
|
|
return SK_SS_LIFE;
|
|
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 the spellschool associated with the given skill
|
|
enum SPELLSCHOOL getskillschool(enum SKILL skid) {
|
|
switch (skid) {
|
|
case SK_SS_ALLOMANCY:
|
|
return SS_ALLOMANCY;
|
|
case SK_SS_AIR:
|
|
return SS_AIR;
|
|
case SK_SS_DEATH:
|
|
return SS_DEATH;
|
|
case SK_SS_DIVINATION:
|
|
return SS_DIVINATION;
|
|
case SK_SS_NATURE:
|
|
return SS_NATURE;
|
|
case SK_SS_FIRE:
|
|
return SS_FIRE;
|
|
case SK_SS_COLD:
|
|
return SS_COLD;
|
|
case SK_SS_LIFE:
|
|
return SS_LIFE;
|
|
case SK_SS_MENTAL:
|
|
return SS_MENTAL;
|
|
case SK_SS_SUMMONING:
|
|
return SS_SUMMONING;
|
|
case SK_SS_TRANSLOCATION:
|
|
return SS_TRANSLOCATION;
|
|
case SK_SS_WILD:
|
|
return SS_WILD;
|
|
default:
|
|
break;
|
|
}
|
|
return SS_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)) {
|
|
snprintf(buf, BUFLEN, "%d-%d MP%s", cost,
|
|
cost * power, ongoing ? ", ongoing" : "");
|
|
} else {
|
|
snprintf(buf, BUFLEN, "%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;
|
|
}
|
|
|
|
enum LOFTYPE getspellloftype(enum OBTYPE spellid) {
|
|
objecttype_t *ot;
|
|
flag_t *f;
|
|
ot = findot(spellid);
|
|
if (!ot) {
|
|
return LOF_DONTNEED;
|
|
}
|
|
f = hasflag(ot->flags, F_LOSLOF);
|
|
if (f) {
|
|
return f->val[1];
|
|
}
|
|
return LOF_DONTNEED;
|
|
}
|
|
|
|
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 forcepower) {
|
|
int power;
|
|
enum SPELLSCHOOL ss;
|
|
objecttype_t *ot;
|
|
ot = findot(spellid);
|
|
strcpy(buf, ot->name);
|
|
capitalise(buf);
|
|
if (forcepower) {
|
|
power = forcepower;
|
|
} else {
|
|
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 >= 6) {
|
|
strcat(buf, "(ctrl)");
|
|
}
|
|
} else 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 >= 5) {
|
|
strcat(buf, "(ctrl,entire-lev)");
|
|
} else if (power >= 3) {
|
|
strcat(buf, "(ctrl)");
|
|
}
|
|
} else if (spellid == OT_S_POLYMORPH) {
|
|
if (power >= 5) {
|
|
strcat(buf, "(ctrl)");
|
|
}
|
|
} else if (spellid == OT_S_SOFTENEARTH) {
|
|
if (power >= 2) {
|
|
strcat(buf, "(boost)");
|
|
}
|
|
} else if (spellid == OT_S_TELEPORT) {
|
|
if (power >= 8) {
|
|
strcat(buf, "(fullctrl)");
|
|
} else if (power >= 5) {
|
|
strcat(buf, "(semictrl)");
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// at what power level COULD lf cast 'spellid' ?
|
|
// note: doesn't matter if the lf doesn't actually have f_cancast / f_canwill,
|
|
// in this case the function will return the POTENTIAL power.
|
|
int getspellpower(lifeform_t *lf, enum OBTYPE spellid) {
|
|
int power = 0,willpower = 0, castpower = 0;
|
|
int spelllev;
|
|
enum SKILLLEVEL schoolskill;
|
|
enum SPELLSCHOOL school;
|
|
int db = B_FALSE;
|
|
int boost;
|
|
flag_t *willflag,*castflag;
|
|
char whatfrom[BUFLENSMALL];
|
|
|
|
if (db) {
|
|
objecttype_t *ot;
|
|
ot = findot(spellid);
|
|
if (db) dblog("getspellpower for lf %s, spell %s", lf->race->name, ot->name);
|
|
}
|
|
|
|
sumflags(lf->flags, F_MAGICBOOST, &boost, NULL, NULL);
|
|
|
|
////////////////////////////////////
|
|
// CAN WE CAST THIS AT ALL
|
|
////////////////////////////////////
|
|
// If we can will/cast this then we might have a set
|
|
// spellpower. if we both will _and_ cast this,
|
|
// then use whatever power is higher.
|
|
strcpy(whatfrom, "");
|
|
willflag = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL);
|
|
if (willflag && strlen(willflag->text)) {
|
|
texttospellopts(willflag->text, "pw:", &willpower, NULL );
|
|
}
|
|
castflag = lfhasflagval(lf, F_CANCAST, spellid, NA, NA, NULL);
|
|
if (castflag && strlen(castflag->text)) {
|
|
texttospellopts(castflag->text, "pw:", &castpower, NULL );
|
|
}
|
|
if ((willpower > 0) && (castpower > 0)) {
|
|
if (castpower > willpower) {
|
|
power = castpower;
|
|
strcpy(whatfrom, "cancast");
|
|
} else {
|
|
power = willpower;
|
|
strcpy(whatfrom, "canwill");
|
|
}
|
|
} else if (willpower > 0) {
|
|
power = willpower;
|
|
strcpy(whatfrom, "canwill");
|
|
} else if (castpower > 0) {
|
|
power = castpower;
|
|
strcpy(whatfrom, "cancast");
|
|
}
|
|
|
|
if (strlen(whatfrom)) {
|
|
if (db) {
|
|
dblog("-->power = %d (from %s)", power, whatfrom);
|
|
}
|
|
// note that this power _can_ override max spell power
|
|
power += boost;
|
|
return power;
|
|
}
|
|
|
|
// get spell details
|
|
school = getspellschoolknown(lf, spellid);
|
|
schoolskill = getskill(lf, getschoolskill(school));
|
|
spelllev = getspelllevel(spellid);
|
|
|
|
// for most spell schools, your skill in the school determines which
|
|
// spells you can cast.
|
|
//
|
|
// this check doesn't apply for monsters.
|
|
if (isplayer(lf)) {
|
|
int maxspelllevel = MAXSPELLLEV;
|
|
// if (hasjob(lf, J_DRUID) && (school == SS_NATURE)) {
|
|
//if ((school == SS_ALLOMANCY) || (school == SS_MENTAL)) {
|
|
//} else {
|
|
switch (schoolskill) {
|
|
case PR_INEPT: maxspelllevel = 0; break;
|
|
case PR_NOVICE: maxspelllevel = 1; break;
|
|
case PR_BEGINNER: maxspelllevel = 2; break;
|
|
case PR_ADEPT: maxspelllevel = 3; break;
|
|
case PR_SKILLED: maxspelllevel = 4; break;
|
|
case PR_EXPERT: maxspelllevel = 5; break;
|
|
case PR_MASTER: maxspelllevel = 6; break;
|
|
}
|
|
//}
|
|
// player can only ever cast spells up to your level.
|
|
if (!hasjob(lf, J_GOD)) limit(&maxspelllevel, NA, lf->level);
|
|
|
|
if (!willflag && (spelllev > maxspelllevel)) {
|
|
if (db) dblog("-->power = 0 (no skilled enough in spell school)");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////
|
|
// HOW POWERFUL IS THIS SPELL?
|
|
////////////////////////////////////
|
|
if (isplayer(lf)) {
|
|
power = 1;
|
|
// plus your hitdice/3
|
|
power += (gettr(lf)/3);
|
|
|
|
// plus stat modifier
|
|
power += getspellpowerstatmod(lf, school, NULL);
|
|
|
|
// plus god modifier
|
|
power += getspellpowergodmod(lf, school, NULL);
|
|
|
|
if (!willflag) {
|
|
// plus any extra school levels beyond what you need
|
|
power += ((int)schoolskill - spelllev);
|
|
}
|
|
} else {
|
|
// for monsters, just based on hitdice:
|
|
power = gettr(lf)/2;
|
|
limit(&power, 1, NA);
|
|
}
|
|
|
|
power += boost;
|
|
limit(&power, 0, getspellmaxpower(spellid));
|
|
return power;
|
|
}
|
|
|
|
int getspellpowergodmod(lifeform_t *lf, enum SPELLSCHOOL school, enum RACE *godid) {
|
|
int mod = 0;
|
|
if (godid) *godid = A_NONE;
|
|
|
|
if (!isplayer(lf)) return 0;
|
|
|
|
if ((school == SS_LIFE) && godprayedto(R_GODLIFE)) {
|
|
int plev;
|
|
if (godid) *godid = R_GODLIFE;
|
|
plev = getpietylev(R_GODLIFE, NULL, NULL);
|
|
if (plev > 0) mod = plev;
|
|
} else if ((school == SS_DEATH) && godprayedto(R_GODDEATH)) {
|
|
int plev;
|
|
if (godid) *godid = R_GODLIFE;
|
|
plev = getpietylev(R_GODLIFE, NULL, NULL);
|
|
if (plev > 0) mod = plev;
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
int getspellpowerstatmod(lifeform_t *lf, enum SPELLSCHOOL school, enum ATTRIB *att) {
|
|
int mod = 0;
|
|
if (att) *att = A_NONE;
|
|
|
|
if (!isplayer(lf)) return 0;
|
|
|
|
if (school == SS_MENTAL) {
|
|
if (att) *att = A_IQ;
|
|
// +/- 2 for iq
|
|
mod += (getstatmod(lf, A_IQ) / 25);
|
|
} else if (school == SS_NATURE) {
|
|
if (att) *att = A_WIS;
|
|
// +/- 1 for wisdom
|
|
mod += (getstatmod(lf, A_WIS) / 50);
|
|
} else if (school == SS_ALLOMANCY) {
|
|
if (att) *att = A_STR;
|
|
// +/- 1 for strength
|
|
mod += (getstatmod(lf, A_STR) / 50);
|
|
} else if (school == SS_LIFE) {
|
|
// ie. clerical.
|
|
// +/- 2 for wisdom
|
|
if (att) *att = A_WIS;
|
|
mod += (getstatmod(lf, A_WIS) / 25);
|
|
} else {
|
|
if (att) *att = A_IQ;
|
|
// +/- 1 for iq
|
|
mod += (getstatmod(lf, A_IQ) / 50);
|
|
}
|
|
|
|
return mod;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// return the school which the given spell belongs to, HOWEVER if it
|
|
// belongs to multiple ones, prefer ones which the given lifeform
|
|
// is skilled in.
|
|
enum SPELLSCHOOL getspellschoolknown(lifeform_t *lf, enum OBTYPE spellid) {
|
|
flag_t *f;
|
|
enum SPELLSCHOOL thisschool;
|
|
objecttype_t *ot;
|
|
enum SPELLSCHOOL poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
|
|
ot = findot(spellid);
|
|
if (!ot) {
|
|
return SS_NONE;
|
|
}
|
|
|
|
if (ot->obclass->id == OC_ABILITY) {
|
|
return SS_ABILITY;
|
|
}
|
|
|
|
// make a list of all schools which this spell belongs to, and which we know.
|
|
thisschool = SS_NONE;
|
|
for (f = ot->flags->first ; f ; f = f->next) {
|
|
if ((f->id == F_SPELLSCHOOL) && getskill(lf, getschoolskill(f->val[0]))) {
|
|
poss[nposs++] = f->val[0];
|
|
}
|
|
}
|
|
if (nposs) {
|
|
int i;
|
|
enum SPELLSCHOOL poss2[MAXCANDIDATES];
|
|
int nposs2 = 0;
|
|
enum SKILLLEVEL highestskill = PR_INEPT;
|
|
// find the school which we are most skilled in
|
|
for (i = 0; i < nposs; i++) {
|
|
enum SKILLLEVEL thisslev;
|
|
thisslev = getskill(lf, getschoolskill(poss[i]));
|
|
if (thisslev > highestskill) {
|
|
highestskill = thisslev;
|
|
}
|
|
}
|
|
|
|
// now only select from these ones...
|
|
for (i = 0; i < nposs; i++) {
|
|
enum SKILLLEVEL thisslev;
|
|
thisslev = getskill(lf, getschoolskill(poss[i]));
|
|
if (thisslev == highestskill) {
|
|
poss2[nposs2++] = poss[i];
|
|
}
|
|
}
|
|
|
|
// pick the first one.
|
|
//thisschool = poss2[rnd(0,nposs2-1)];
|
|
thisschool = poss2[0];
|
|
} else {
|
|
// if we don't know any of the schools...
|
|
// just pick the first one.
|
|
f = hasflag(ot->flags, F_SPELLSCHOOL);
|
|
assert(f);
|
|
thisschool = f->val[0];
|
|
}
|
|
|
|
return thisschool;
|
|
}
|
|
|
|
enum SKILLLEVEL getspellskill(lifeform_t *lf, enum OBTYPE spellid) {
|
|
enum SPELLSCHOOL school;
|
|
enum SKILLLEVEL slev;
|
|
school = getspellschoolknown(lf, spellid);
|
|
slev = getskill(lf, getschoolskill(school));
|
|
return slev;
|
|
}
|
|
|
|
int getspellrange(lifeform_t *lf, enum OBTYPE spellid, int power, int *minrange) {
|
|
objecttype_t *st;
|
|
int range = UNLIMITED;
|
|
|
|
// default
|
|
if (minrange) {
|
|
*minrange = 0;
|
|
}
|
|
|
|
// If we can _will_ this to occur then we might have a set
|
|
// range
|
|
if (lf) {
|
|
int newrange;
|
|
flag_t *f;
|
|
f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL);
|
|
if (f && strlen(f->text)) {
|
|
texttospellopts(f->text, "range:", &newrange, NULL);
|
|
if (newrange > 0) {
|
|
return newrange;
|
|
}
|
|
}
|
|
}
|
|
|
|
st = findot(spellid);
|
|
if (st) {
|
|
flag_t *f;
|
|
f = hasflag(st->flags, F_RANGE);
|
|
if (f) {
|
|
range = f->val[0];
|
|
if (minrange && (f->val[1] != NA)) {
|
|
*minrange = f->val[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
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_FLAMEPILLAR:
|
|
range = 3+(power/2);
|
|
break;
|
|
case OT_S_LIGHTNINGBOLT:
|
|
range = (power*3);
|
|
break;
|
|
case OT_S_CHAINLIGHTNING:
|
|
range = (power*3);
|
|
break;
|
|
case OT_S_SHARDSHOT:
|
|
range = power;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return range;
|
|
}
|
|
|
|
int getstamcost(lifeform_t *lf, enum OBTYPE oid) {
|
|
int stamcost = -1;
|
|
flag_t *f;
|
|
f = lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL);
|
|
if (f) {
|
|
int newcost;
|
|
// override stamina cost?
|
|
if (texttospellopts(f->text, "stamcost:", &newcost, NULL)) {
|
|
stamcost = newcost;
|
|
}
|
|
}
|
|
if (stamcost == -1) {
|
|
objecttype_t *ot;
|
|
ot = findot(oid);
|
|
if (ot) {
|
|
f = hasflag(ot->flags, F_STAMCOST);
|
|
if (f) stamcost = f->val[0];
|
|
}
|
|
}
|
|
return stamcost;
|
|
}
|
|
|
|
// provides a description for f_ongoing spells.
|
|
char *getspelldesc(enum OBTYPE spellid, int power, char *buf) {
|
|
// default
|
|
strcpy(buf, "");
|
|
switch (spellid) {
|
|
case OT_S_ALARM:
|
|
snprintf(buf, BUFLEN, "warnings about nearby enemies");
|
|
break;
|
|
case OT_S_BARKSKIN:
|
|
snprintf(buf, BUFLEN, "%d damage reduction, vuln to fire", gethardness(MT_WOOD));
|
|
break;
|
|
case OT_S_ENDUREELEMENTS:
|
|
snprintf(buf, BUFLEN, "resist fire, resist cold");
|
|
break;
|
|
case OT_S_EQANDOP:
|
|
snprintf(buf, BUFLEN, "reflects missile attacks");
|
|
break;
|
|
case OT_S_GRAVLOWER:
|
|
snprintf(buf, BUFLEN, "reduces load by %dkg", power*15);
|
|
break;
|
|
case OT_S_HEAVENARM:
|
|
snprintf(buf, BUFLEN, "negates %d damage", power*10);
|
|
break;
|
|
case OT_S_HOLYAURA:
|
|
snprintf(buf, BUFLEN, "weapons deal holy damage");
|
|
break;
|
|
case OT_S_LOWERMETAB:
|
|
if (power >= 5) {
|
|
snprintf(buf, BUFLEN, "reduces hunger");
|
|
} else {
|
|
snprintf(buf, BUFLEN, "reduces hunger and speed");
|
|
}
|
|
break;
|
|
case OT_S_MIRRORIMAGE:
|
|
snprintf(buf, BUFLEN, "Create %d mirror images", power);
|
|
break;
|
|
case OT_S_PSYARMOUR:
|
|
snprintf(buf, BUFLEN, "+%d Armour Rating", power*4);
|
|
break;
|
|
case OT_S_SIXTHSENSE:
|
|
snprintf(buf, BUFLEN, "warnings about adjacent enemies");
|
|
break;
|
|
case OT_S_SUMMONSWARM:
|
|
switch (power) {
|
|
case 1:
|
|
snprintf(buf, BUFLEN, "Summon a rat swarm."); break;
|
|
case 2:
|
|
snprintf(buf, BUFLEN, "Summon a spider swarm."); break;
|
|
default:
|
|
case 3:
|
|
snprintf(buf, BUFLEN, "Summon a locust swarm."); break;
|
|
}
|
|
break;
|
|
case OT_S_SUMMONWEAPON:
|
|
snprintf(buf, BUFLEN, "Create a %d damage rating magical weapon",2+(power*2));
|
|
break;
|
|
case OT_S_TAILWIND:
|
|
snprintf(buf, BUFLEN, "Speed boost when moving fowards");
|
|
break;
|
|
case OT_S_THORNS:
|
|
snprintf(buf, BUFLEN, "1d4 piercing damage to attackers");
|
|
break;
|
|
case OT_S_TRUESTRIKE:
|
|
if (power == 1) {
|
|
snprintf(buf, BUFLEN, "Next attack automatically hits");
|
|
} else {
|
|
snprintf(buf, BUFLEN, "Next %d attacks automatically hit",power);
|
|
}
|
|
break;
|
|
case OT_S_WHATGOESUP:
|
|
snprintf(buf, BUFLEN, "missiles return to source");
|
|
break;
|
|
case OT_S_WINDSHIELD:
|
|
snprintf(buf, BUFLEN, "Protection from missiles <= %d km/h, 1-%d dmg to attackers",speedtokph(power),
|
|
power);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// for the given skill (ie. metalwork or sewing) + level, _append_ the materials we can work on to the list, and
|
|
// how much we can repair them.
|
|
// returns the number of materials/cutoffs which were appended.
|
|
int getworkablematerials(lifeform_t *lf, enum SKILL skid , enum MATERIAL *repairablemats, int *cutoffpct, int *nmats) {
|
|
enum SKILLLEVEL slev;
|
|
int cutoff,cutoffmod = 0,nworkable = 0;
|
|
object_t *helpob = NULL;
|
|
|
|
// note: we DONT initialise nmats to zero.
|
|
|
|
slev = getskill(lf, skid);
|
|
switch (slev) {
|
|
case PR_NOVICE: cutoff = 80; break;
|
|
case PR_BEGINNER: cutoff = 65; break;
|
|
case PR_ADEPT: cutoff = 50; break;
|
|
case PR_SKILLED: cutoff = 25; break;
|
|
case PR_EXPERT: cutoff = 0; break;
|
|
case PR_MASTER: cutoff = 0; break;
|
|
default: cutoff = 100; break;
|
|
}
|
|
if (skid == SK_METALWORK) {
|
|
helpob = getworkhelpob(lf->pack, MT_METAL, NULL, &cutoffmod);
|
|
} else if (skid == SK_SEWING) {
|
|
helpob = getworkhelpob(lf->pack, MT_CLOTH, NULL, &cutoffmod);
|
|
}
|
|
if (helpob) {
|
|
cutoff -= cutoffmod;
|
|
}
|
|
|
|
limit(&cutoff, 0, NA);
|
|
|
|
if (cutoff < 100) {
|
|
if (skid == SK_METALWORK) {
|
|
repairablemats[*nmats] = MT_METAL;
|
|
cutoffpct[*nmats] = cutoff;
|
|
(*nmats)++;
|
|
nworkable++;
|
|
} else if (skid == SK_SEWING) {
|
|
repairablemats[*nmats] = MT_CLOTH;
|
|
cutoffpct[*nmats] = cutoff;
|
|
(*nmats)++;
|
|
repairablemats[*nmats] = MT_LEATHER;
|
|
cutoffpct[*nmats] = cutoff;
|
|
(*nmats)++;
|
|
repairablemats[*nmats] = MT_FLESH;
|
|
cutoffpct[*nmats] = cutoff;
|
|
(*nmats)++;
|
|
nworkable += 2;
|
|
}
|
|
}
|
|
return nworkable;
|
|
}
|
|
|
|
// returns an object from 'op' which will help with work on the given material
|
|
object_t *getworkhelpob(obpile_t *op, enum MATERIAL mat, int *howmuch, int *cutoffmod) {
|
|
object_t *helpob = NULL;
|
|
if (howmuch) *howmuch = 0;
|
|
if (cutoffmod) *cutoffmod = 0;
|
|
helpob = hasobwithflagval(op, F_HELPSREPAIR, mat, NA, NA, NULL);
|
|
if (helpob) {
|
|
flag_t *f;
|
|
f = hasflagval(helpob->flags, F_HELPSREPAIR, mat, NA, NA, NULL);
|
|
if (f) {
|
|
if (howmuch) *howmuch = f->val[1];
|
|
if (cutoffmod) *cutoffmod = f->val[2];
|
|
}
|
|
}
|
|
return helpob;
|
|
}
|
|
|
|
// 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, OB1(o,"flies","fly"), lfname);
|
|
}
|
|
|
|
// can we pick up the object?
|
|
if (hasflag(o->flags, F_NOPICKUP) || isimpassableob(o, lf, getlfsize(lf)) || !canpickup(lf, o, ALL)) {
|
|
char buf[BUFLEN];
|
|
int dir;
|
|
cell_t *obloc,*newcell;
|
|
|
|
snprintf(buf, BUFLEN, "a flying %s", noprefix(obname));
|
|
|
|
if (isplayer(lf) || haslos(player, lf->cell)) {
|
|
msg("%s %s into %s!", obname, OB1(o,"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, B_FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
int stopallspells(lifeform_t *lf) {
|
|
flag_t *f,*nextf;
|
|
int nstopped = 0;
|
|
|
|
for (f = lf->flags->first ; f ; f = nextf) {
|
|
nextf = f->next;
|
|
if (f->id == F_BOOSTSPELL) {
|
|
if (!stopspell(lf, f->val[0])) nstopped++;
|
|
}
|
|
}
|
|
|
|
// also stop spelllike abilities
|
|
nstopped += killflagsofid(lf->flags, F_FULLSHIELD);
|
|
|
|
return nstopped;
|
|
}
|
|
|
|
int stopallspellsexcept(lifeform_t *lf, ...) {
|
|
flag_t *f,*nextf;
|
|
va_list args;
|
|
enum OBTYPE exception[MAXCANDIDATES];
|
|
int nexceptions = 0;
|
|
int nstopped = 0;
|
|
|
|
va_start(args, lf);
|
|
exception[nexceptions] = va_arg(args, enum OBTYPE);
|
|
while (exception[nexceptions] != OT_NONE) {
|
|
nexceptions++;
|
|
exception[nexceptions] = va_arg(args, enum OBTYPE);
|
|
}
|
|
va_end(args);
|
|
|
|
for (f = lf->flags->first ; f ; f = nextf) {
|
|
nextf = f->next;
|
|
if (f->id == F_BOOSTSPELL) {
|
|
int stopthis = B_TRUE;
|
|
int n;
|
|
for (n = 0; n < nexceptions; n++) {
|
|
if (exception[n] == f->val[0]) {
|
|
stopthis = B_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (stopthis) {
|
|
if (!stopspell(lf, f->val[0])) nstopped++;
|
|
}
|
|
}
|
|
}
|
|
return nstopped;
|
|
}
|
|
|
|
int schoolappearsinbooks(enum SPELLSCHOOL ss) {
|
|
switch (ss) {
|
|
case SS_DIVINE:
|
|
case SS_ABILITY:
|
|
case SS_ALLOMANCY:
|
|
case SS_MENTAL:
|
|
case SS_NATURE:
|
|
return B_FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
|
|
void spellcloud(cell_t *srcloc, int radius, int dirtype, int ch, enum COLOUR col, enum OBTYPE sid, int power, int frompot, char *seetext, char *noseetext, int aimedateyes, object_t *fromob, int includecentre) {
|
|
int x,y;
|
|
char *see = NULL, *nosee = NULL;
|
|
objecttype_t *ot;
|
|
int (*distfunc)(cell_t *, cell_t *);
|
|
|
|
ot = findot(sid);
|
|
if (!ot) return;
|
|
|
|
if (dirtype == DT_ORTH) {
|
|
distfunc = getcelldistorth;
|
|
} else {
|
|
distfunc = getcelldist;
|
|
}
|
|
|
|
|
|
if (seetext) {
|
|
see = strdup(seetext);
|
|
}
|
|
if (noseetext) {
|
|
nosee = strdup(noseetext);
|
|
}
|
|
|
|
if (fromob) {
|
|
char obname[BUFLEN];
|
|
// replace "OB" in seetext/noseetext based on fromob
|
|
getobname(fromob, obname, 1);
|
|
if (see) {
|
|
strrep(&see, "OB", obname, NULL);
|
|
capitalise(see);
|
|
}
|
|
if (nosee) {
|
|
strrep(&nosee, "OB", obname, NULL);
|
|
capitalise(nosee);
|
|
}
|
|
}
|
|
|
|
|
|
if (ch != '\0') {
|
|
animradial(srcloc, radius, ch, col, DT_ORTH, see, nosee);
|
|
}
|
|
if (see) free(see);
|
|
if (nosee) free(nosee);
|
|
|
|
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->type->solid &&
|
|
haslof(srcloc, c, LOF_WALLSTOP, NULL)) {
|
|
int safe = B_FALSE;
|
|
if (!includecentre && (c == srcloc)) {
|
|
safe = B_TRUE;
|
|
}
|
|
if (aimedateyes && (!hasbp(c->lf, BP_EYES) || getarmour(c->lf, BP_EYES))) {
|
|
object_t *arm;
|
|
safe = B_TRUE;
|
|
arm = getarmour(c->lf, BP_EYES);
|
|
if (arm) {
|
|
char armname[BUFLEN];
|
|
getobname(arm, armname, 1);
|
|
if (isplayer(c->lf)) {
|
|
msg("Your %s protects your eyes.", noprefix(armname));
|
|
} else if (cansee(player, c->lf)) {
|
|
char lfname[BUFLEN];
|
|
getlfname(c->lf, lfname);
|
|
msg("%s%s %s protects its eyes.", lfname, getpossessive(lfname), noprefix(armname));
|
|
}
|
|
}
|
|
}
|
|
if (!safe) {
|
|
if (distfunc(srcloc, c) <= radius) {
|
|
// cast the spell
|
|
if (ot->obclass->id == OC_SPELL) {
|
|
dospelleffects(NULL, ot->id, power, c->lf, NULL, c, B_UNCURSED, NULL, frompot, fromob);
|
|
} else if (ot->obclass->id == OC_ABILITY) {
|
|
abilityeffects(c->lf, ot->id, c, c->lf, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int spellisfromschool(int spellid, enum SPELLSCHOOL school) {
|
|
objecttype_t *sp;
|
|
sp = findot(spellid);
|
|
if (hasflagval(sp->flags, F_SPELLSCHOOL, school, NA, NA, NULL)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int spelllearnable(lifeform_t *lf, enum OBTYPE spellid) {
|
|
enum SPELLSCHOOL knownschool;
|
|
int ok = B_FALSE;
|
|
|
|
// skilled in one of the spell's schools?
|
|
knownschool = getspellschoolknown(lf, spellid);
|
|
if (getskill(lf, getschoolskill(knownschool))) {
|
|
int mpneeded;
|
|
if (!cancast(lf, spellid, &mpneeded)) {
|
|
if (getspellpower(lf, spellid) > 0) {
|
|
if (getmaxmp(lf) >= mpneeded) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
int spellokformonsters(int spellid) {
|
|
objecttype_t *sp;
|
|
sp = findot(spellid);
|
|
if (!sp) return B_FALSE;
|
|
if (hasflag(sp->flags, F_AICASTTOATTACK)) return B_TRUE;
|
|
if (hasflag(sp->flags, F_AICASTTOFLEE)) return B_TRUE;
|
|
return B_FALSE;
|
|
}
|
|
|
|
// returns true if the spell was resisted.
|
|
int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power, int *seenbyplayer, int announce) {
|
|
char text[BUFLEN],buf[BUFLEN];
|
|
int bonus = 0;
|
|
int resisted = B_FALSE;
|
|
|
|
// cannot resist spells from gods when they are on their home plane
|
|
if (caster && (caster->race->raceclass->id == RC_GOD)) {
|
|
if (caster->cell && (caster->cell->map->region->rtype->id == BH_HEAVEN)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (announce) {
|
|
getlfname(target, buf);
|
|
if (spellisfromschool(spellid, SS_MENTAL)) {
|
|
if (isplayer(target)) {
|
|
strcpy(text, "You shrug off a mental attack.");
|
|
} else {
|
|
sprintf(text, "%s shrugs off %s mental attack.", buf, isplayer(caster) ? "your" : "a");
|
|
}
|
|
} else {
|
|
if (isplayer(target)) {
|
|
strcpy(text, "You resist the effects of a spell.");
|
|
} else {
|
|
sprintf(text, "%s resists %s spell.", buf, isplayer(caster) ? "your" : "a");
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((spellid == OT_S_SLEEP) && lfhasflag(target, F_RAGE)) {
|
|
bonus += 10;
|
|
}
|
|
if (caster && hasjob(caster, J_SCOURGE) && (spellid == OT_S_NULLIFY)) {
|
|
// cancel out the difficulty from NULLIFY being a level 4 spell
|
|
bonus += 8;
|
|
}
|
|
|
|
if (spellisfromschool(spellid, SS_MENTAL)) {
|
|
if (lfhasflag(target, F_MINDSHIELD)) {
|
|
resisted = B_TRUE;
|
|
} else {
|
|
resisted = skillcheck(target, SC_IQ, getmrdiff(spellid,power), bonus);
|
|
}
|
|
} else {
|
|
resisted = skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), bonus);
|
|
}
|
|
|
|
if (resisted) {
|
|
if (isplayer(target) || haslos(player, target->cell)) {
|
|
if (announce) {
|
|
msg("%s",text);
|
|
}
|
|
if (seenbyplayer) *seenbyplayer = B_TRUE;
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// returns true on error
|
|
int stopspell(lifeform_t *caster, enum OBTYPE spellid) {
|
|
flag_t *f,*nextf;
|
|
object_t *o, *nexto;
|
|
objecttype_t *sp;
|
|
|
|
sp = findot(spellid);
|
|
if (!sp) return B_TRUE;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (isplayer(caster)) {
|
|
msg("Your %s spell disappates.",sp->name);
|
|
}
|
|
|
|
// remove any other specific effects based on spell type.
|
|
for (o = caster->pack->first; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (hasflagval(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL)) {
|
|
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),
|
|
noprefix(obname));
|
|
}
|
|
killob(o);
|
|
}
|
|
}
|
|
|
|
if ((spellid == OT_S_BARKSKIN) && (getlfmaterial(caster) == MT_WOOD)) {
|
|
setlfmaterial(caster, caster->race->material->id, B_TRUE);
|
|
} else if (spellid == OT_S_FLOATINGDISC) {
|
|
map_t *m;
|
|
lifeform_t *lf;
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
for (lf = m->lf ; lf ; lf = lf->next) {
|
|
if (lf->race->id == R_FLOATINGDISC) {
|
|
if (lfhasflagval(lf, F_SUMMONEDBY, caster->id, NA, NA, NULL)) {
|
|
unsummon(lf, B_FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
// returns # created
|
|
// pass EITHER wantrace OR wantrc + wantsize + wantalign
|
|
//
|
|
// friendly can be:
|
|
// b_true - force summoned lfs to be peaceful
|
|
// b_maybe - leave hostility as set by race
|
|
// b_false - force summoned lfs to be hostile
|
|
int summonlfs(lifeform_t *caster, cell_t *where, enum RACE wantrace, enum RACECLASS wantrc, enum LFSIZE wantsize, enum ALIGNMENT wantalign, int howmany, int lifetime, int friendly) {
|
|
lifeform_t *newlf;
|
|
race_t *r = NULL;
|
|
enum RACE poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
int i;
|
|
int ncreated = 0;
|
|
cell_t *c;
|
|
|
|
if (wantrace) {
|
|
poss[0] = wantrace;
|
|
nposs = 1;
|
|
} else {
|
|
// determine possible types of race
|
|
for (r = firstrace ; r; r = r->next) {
|
|
int ok = B_TRUE;
|
|
if (hasflag(r->flags, F_GODOF)) {
|
|
ok = B_FALSE;
|
|
}
|
|
if (hasflag(r->flags, F_UNIQUE)) {
|
|
ok = B_FALSE;
|
|
}
|
|
if ((wantrc != RC_ANY) && (r->raceclass->id != wantrc)) {
|
|
ok = B_FALSE;
|
|
}
|
|
|
|
if (wantsize != SZ_ANY) {
|
|
flag_t *f;
|
|
f = hasflag(r->flags, F_SIZE);
|
|
if (f && (f->val[0] != wantsize)) {
|
|
ok = B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (wantalign != AL_NONE) {
|
|
flag_t *f;
|
|
f = hasflag(r->flags, F_ALIGNMENT);
|
|
if (f && (f->val[0] != wantalign)) {
|
|
ok = B_FALSE;
|
|
}
|
|
}
|
|
if (ok) {
|
|
poss[nposs++] = r->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nposs == 0) {
|
|
return 0;
|
|
}
|
|
|
|
ncreated = 0;
|
|
for (i = 0; i < howmany; i++) {
|
|
// get random adjacent cell
|
|
// (prefer cells in sight of caster)
|
|
c = real_getrandomadjcell(where, &ccwalkable, B_ALLOWEXPAND, LOF_NEED, NULL, caster);
|
|
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, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, 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);
|
|
if (friendly == B_TRUE) {
|
|
addflag(newlf->flags, F_PETOF, caster->id, NA, NA, NULL);
|
|
if (areallies(player, caster)) {
|
|
makefriendly(newlf, PERMENANT);
|
|
}
|
|
} else if (friendly == B_FALSE) {
|
|
if (!lfhasflag(newlf, F_HOSTILE)) {
|
|
addflag(newlf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
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],buf2[BUFLEN];
|
|
snprintf(buf, BUFLEN, "Where will you target your %s?",ot->name);
|
|
snprintf(buf2, BUFLEN, "%s->",ot->name);
|
|
where = askcoords(buf, buf2, 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 frompot) {
|
|
int maxrange = UNLIMITED;
|
|
int done = B_FALSE;
|
|
cell_t *where = NULL;
|
|
objecttype_t *sp;
|
|
int needlos = B_TRUE;
|
|
enum LOFTYPE needlof = LOF_NEED;
|
|
int minrange = 0;
|
|
|
|
if (!caster) {
|
|
return *targcell;
|
|
} else if ((caster->race->raceclass->id == RC_GOD) && targcell) {
|
|
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];
|
|
}
|
|
}
|
|
|
|
if (caster && (caster->race->raceclass->id == RC_GOD)) {
|
|
needlos = B_FALSE;
|
|
}
|
|
|
|
maxrange = getspellrange(caster, spellid, power, &minrange);
|
|
|
|
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) && !frompot) {
|
|
msg("You cannot see there!"); more();
|
|
}
|
|
where = NULL;
|
|
}
|
|
|
|
// line of fire interrupted?
|
|
if (where && needlof && !haslofknown(caster->cell, where, needlof, &newwhere)) {
|
|
if (newwhere) {
|
|
if (isplayer(caster) && !frompot) {
|
|
// warn!
|
|
int ch;
|
|
ch = askchar("Your have no clear line of fire - really target here?","yn","n", B_TRUE, B_FALSE);
|
|
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) && !frompot) {
|
|
msg("Too far away - max range is %d.",maxrange); more();
|
|
}
|
|
where = NULL;
|
|
}
|
|
if (where && (getcelldist(caster->cell, where) < minrange)) {
|
|
// out of range
|
|
if (isplayer(caster) && !frompot) {
|
|
msg("Too close - min range is %d.",minrange); more();
|
|
}
|
|
where = NULL;
|
|
}
|
|
|
|
if (!frompot && where && where->lf && haslos(caster, where) && isplayer(caster) && areallies(caster, where->lf)) {
|
|
// warn before targetting yourself!
|
|
if (getattrbracket(getattr(caster, A_WIS), A_WIS, NULL) >= AT_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)) {
|
|
snprintf(ques, BUFLEN,"Really target yourself");
|
|
} else {
|
|
char lfname[BUFLEN];
|
|
getlfname(where->lf, lfname);
|
|
snprintf(ques, BUFLEN, "Really target %s", lfname);
|
|
}
|
|
ch = askchar(ques,"yn","n", B_TRUE, B_FALSE);
|
|
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];
|
|
char buf2[BUFLEN];
|
|
if (maxrange == UNLIMITED) {
|
|
snprintf(buf, BUFLEN, "Where will you target your %s?", sp->name);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "Where will you target your %s [max range %d]?",sp->name, maxrange);
|
|
}
|
|
snprintf(buf2, BUFLEN, "%s->",sp->name);
|
|
where = real_askcoords(buf, buf2, targtype, caster, minrange, maxrange, needlof, needlof ? B_TRUE : B_FALSE, NULL, 0);
|
|
if (!where) {
|
|
char ques[BUFLEN];
|
|
int ch;
|
|
if (getoption(OPT_CONFIRM_SPELLCANCEL)) {
|
|
snprintf(ques, BUFLEN, "Abandon your %s?", (sp->obclass->id == OC_SPELL) ? "spell" : "ability");
|
|
ch = askchar(ques,"yn","n", B_TRUE, B_FALSE);
|
|
} else {
|
|
ch = 'y';
|
|
}
|
|
if (ch == 'y') return NULL;
|
|
}
|
|
} else {
|
|
// TODO: fill in monster code?
|
|
if (sp->obclass->id == OC_SPELL) 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;
|
|
}
|
|
*/
|
|
|
|
|