nexus/spell.c

9744 lines
254 KiB
C

#include <assert.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attack.h"
#include "defs.h"
#include "flag.h"
#include "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 map_t *heaven;
extern region_t *firstregion;
extern knowledge_t *knowledge;
extern int needredraw;
extern prompt_t prompt;
extern WINDOW *msgwin;
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 lifeform_t *godlf[];
extern int ngodlfs;
extern enum ERROR reason;
extern void (*precalclos)(lifeform_t *);
extern int needredraw, statdirty;
// 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];
float stamcost = 0;
objecttype_t *ot;
flag_t *f;
getlfname(user, username);
real_getlfname(user,killername, B_FALSE);
// defaults
strcpy(damstr,"");
power = 0;
needgrab = B_FALSE;
if (cwflag && strlen(cwflag->text)) {
texttospellopts(cwflag->text, "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);
}
f = hasflag(ot->flags, F_STAMCOST);
if (f) stamcost = f->val[0];
if (stamcost) {
if (getstamina(user) < stamcost) {
if (isplayer(user)) {
msg("You are too tired to do that right now.");
}
return B_TRUE;
}
}
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;
}
if (!range) {
// get max range - based on speed.
range = (int) (((float)SP_NORMAL / (float)getmovespeed(user)) * 2.5);
if (range <= 1) {
if (isplayer(user)) msg("You are too slow to charge!");
return B_TRUE;
}
}
if (!targcell) {
if (isplayer(user)) {
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;
}
// 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...
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;
}
// take some time
taketime(user, getactspeed(user));
// remember orig cell
origcell = user->cell;
// teleport next to them
movelf(user, adjcell);
if (haslos(player, adjcell)) {
char verb[BUFLEN];
// special case: sometimes we call this a 'leap'
switch (user->race->id) {
case R_CREEPINGCLAW:
case R_LEECH:
case R_SNAKETREE:
case R_PIRANHAKING:
strcpy(verb, "leap");
break;
default:
strcpy(verb, "charge");
break;
}
msg("%s %s%s towards %s!",username,verb, isplayer(user) ? "" : "s",targetname);
needredraw = B_TRUE;
drawlevelfor(player);
redraw();
//if (!isplayer(user)) {
more();
//}
}
// special cases....
if (user->race->id == R_CREEPINGCLAW) {
// creeping claw automatically grabs target
addflag(user->flags, F_GRABBING, target->id, NA, NA, NULL);
addflag(target->flags, F_GRABBEDBY, user->id, NA, NA, NULL);
} else {
// attack
attackcell(user, targcell, B_TRUE);
}
} else if (abilid == OT_A_COOK) {
object_t *water,*o;
race_t *r = NULL;
char buf[BUFLEN];
if (!isplayer(user)) {
return B_TRUE;
}
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
if (isplayer(user)) msg("You can't cook while swimming!");
return B_TRUE;
}
water = hasob(user->pack, OT_POT_WATER);
if (!water || !isknown(water)) {
msg("You need some water before you can cook.");
return B_TRUE;
}
o = doaskobject(user->pack, "What will you cook?", NULL, B_FALSE, B_FALSE, AO_EDIBLE, F_CORPSEOF);
if (!o) {
msg("Cancelled.");
return B_TRUE;
}
f = hasflag(o->flags, F_CORPSEOF);
if (f) {
r = findrace(f->val[0]);
}
taketime(user, getactspeed(user));
// remove object and water
removeob(o,ALL);
removeob(water,ALL);
// give food
if (getskill(user, SK_COOKING >= PR_EXPERT)) {
o = addobfast(user->pack, OT_JERKY);
} else {
o = addobfast(user->pack, OT_STEW);
}
if (o) {
if (r) {
addflag(o->flags, F_LINKRACE, r->id, NA, NA, NULL);
}
f = hasflag(o->flags, F_EDIBLE);
if (f) {
f->val[1] = getskill(user, SK_COOKING)*20;
}
getobname(o, buf, o->amt);
msgnocap("%c - %s", o->letter, buf);
practice(user, SK_COOKING, 1);
} else {
msg("Your cooking attempt fails (maybe your pack was too full?).");
}
} else if (abilid == OT_A_DARKWALK) {
if (range <= 0) range = UNLIMITED;
if (islit(user->cell)) {
if (isplayer(user)) msg("It is too light to darkwalk!");
return TRUE;
}
if (!targcell) {
if (isplayer(user)) {
if (range != UNLIMITED) {
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];
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
if (isplayer(user)) msg("You can't disarm traps while swimming!");
return B_TRUE;
}
// ask for direction
if (!targcell) {
int dirch;
dirch = askchar("Disarm trap in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if ((dirch == '-') || !dirch) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
}
dir = chartodir(dirch);
if (dir == D_NONE) {
targcell = user->cell;
} else {
targcell = getcellindir(user->cell, dir);
}
}
if (targcell->lf) {
if (isplayer(user)) {
char inwayname[BUFLEN];
getlfname(targcell->lf, inwayname);
msg("%s is in your way!",inwayname);
}
return B_TRUE;
}
// trap there?
for (o = targcell->obpile->first ; o ; o = o->next) {
if (hasflag(o->flags, F_TRAP) && !hasflag(o->flags, F_SECRET)) {
// 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);
}
// try to disarm it
if (skillcheck(user, SC_DISARM, trapflag->val[0], 0)) {
if (trapflag->id == F_TRAP) {
// ie. trapped cell
getobname(trapob, buf, 1);
removeob(trapob, trapob->amt);
} else {
// ie. trapped object
killflag(trapflag);
getobname(trapob, buf, 1); // get name AFTER killing the flag!
}
if (isplayer(user)) {
msg("You disarm %s.",buf);
} else if (cansee(player, user)) {
msg("%s disarms %s.",username, buf);
}
if (isplayer(user) && hasjob(user, J_ROGUE)) {
gainxp(user, trapflag->val[0]);
}
practice(user, SK_TRAPS, 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) {
trapeffects(trapob, trapob->type->id, user->cell);
} else {
trapeffects(NULL, trapflag->val[0], user->cell);
}
} else {
getobname(trapob, buf, 1);
if (isplayer(user)) {
msg("You fail to disarm %s.",buf);
} else if (cansee(player, user)) {
msg("%s fails to disarm %s.",username, buf);
}
}
}
} else if (abilid == OT_A_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);
}
}
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) {
char dirch;
dirch = askchar("Flip from which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if (dirch == '.') {
if (isplayer(user)) msg("You can't flip yourself!");
return B_TRUE;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to 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;
}
// victim gets a skilcheck to avoid being grabbed - hard.
if (skillcheck(target, SC_DODGE, getattr(user, A_AGI)+18, 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;
setfacing(user, dir); // turn player to face them
movelf(target, c); // move target behind player
// throw!
if (isplayer(user)) {
msg("You flip %s over your head!", targetname);
} else if (cansee(player, user)) {
msg("%s flips %s!", username, targetname);
}
dist = 2 + (getskill(user, SK_ATHLETICS)/3);
setfacing(target, getrandomdir(DT_COMPASS));
knockback(target, dir, dist, user, 25, B_FALSE);
} 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;
int dirch;
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
if (isplayer(user)) msg("You can't make an attack flurry traps while swimming!");
return B_TRUE;
}
if (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 two be dual-weilding to perform an attack flurry!");
return B_TRUE;
}
// ask for direction
if (targcell) {
dir = getdirtowards(user->cell, targcell, NULL, B_FALSE, DT_ORTH);
} else {
dirch = askchar("Flurry in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE, B_TRUE);
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to attack!");
return B_TRUE;
}
getlfname(target, targetname);
// announce
if (!isplayer(user) && cansee(player, user)) {
msg("%s performs a flurry of fast attacks!", username);
}
// push them back
knockback(target, dir, 1, user, 5, B_TRUE);
// if we succeeded in pushing them...
if (target->cell != targcell) {
movelf(user, targcell);
}
// now attack them
attackcell(user, target->cell, B_TRUE);
} else if (abilid == OT_A_GRAB) {
char dirch;
flag_t *f;
f = lfhasflag(user, F_GRABBING);
if (f) {
if (isplayer(user)) msg("You are already holding someone!");
return B_TRUE;
}
f = lfhasflag(user, F_GRABBEDBY);
if (f) {
if (isplayer(user)) msg("You are already being held by someone!");
return B_TRUE;
}
// ask for direction
if (!targcell) {
dirch = askchar("Grab in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if (dirch == '.') {
if (isplayer(user)) msg("You can't grab yourself!");
return B_TRUE;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to grab!");
return B_TRUE;
}
taketime(user, getactspeed(user));
getlfname(target, targetname);
if (lfhasflag(target, F_NONCORPOREAL)) {
if (isplayer(user)) {
msg("You grab at %s%s insubstantial body.", targetname, getpossessive(targetname));
} else if (cansee(player, user)) {
msg("%s grabs at %s%s insubstantial body.",
username, targetname, getpossessive(targetname));
}
return B_FALSE;
}
// victim gets a skilcheck to avoid being grabbed
if (skillcheck(target, SC_DODGE, getattr(user, A_AGI)+11, 0)) {
if (cansee(player, user)) {
msg("%s evade%s %s%s grasp.", targetname, isplayer(target) ? "" : "s",
username, getpossessive(username));
}
} else {
addflag(user->flags, F_GRABBING, target->id, NA, NA, NULL);
addflag(target->flags, F_GRABBEDBY, user->id, NA, NA, NULL);
// damage?
if (strlen(damstr)) {
losehp(target, roll(damstr), DT_CRUSH, user, killername);
}
}
} else if (abilid == OT_A_CRUSH) {
int dam = 0;
if (isimmobile(user)) {
if (isplayer(user)) msg("You cannot move!");
return B_TRUE;
}
// announce
if (cansee(player, target)) {
msg("%s squeeze%s %s tightly!", username, isplayer(user) ? "" : "s", targetname);
}
// victim gets a skilcheck to avoid damage...
if (skillcheckvs(target, SC_STR, 0, user, SC_STR, 1)) {
if (cansee(player, target)) {
// broke free
killflagsofid(target->flags, F_GRABBEDBY);
killflagsofid(user->flags, F_GRABBING);
}
} else {
int str;
// fixed damage?
if (strlen(damstr)) {
dam = roll(damstr);
} else {
// determine damage baesd on crusher's strength
str = getattr(user, A_STR);
dam = modifybystat((str/2), user, A_STR);
}
// announce
if (cansee(player, target)) {
msg("%s %s being crushed!", targetname, is(target));
}
losehp(target, dam, DT_CRUSH, user, killername);
}
taketime(user, getactspeed(user));
} else if (abilid == OT_A_JUMP) {
lifeform_t *victim = NULL;
char victimname[BUFLEN];
int dodged = B_FALSE;
cell_t *origcell;
int maxrange = 2;
object_t *o;
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
if (isplayer(user)) msg("You can't jump while swimming!");
return B_TRUE;
} else if (isairborne(user)) {
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;
}
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 (isburdened(user)) {
if (isplayer(user)) {
msg("Your load is too heavy to jump!");
}
return B_TRUE;
} else if (lfhasflag(user, F_GRAVBOOSTED)) {
if (isplayer(user)) {
msg("Gravity around you is too strong to jump in!");
}
return B_TRUE;
}
// we now have a cell - go there!
taketime(user, getactspeed(user));
origcell = user->cell;
// did you land on anyone?
victim = haslf(targcell);
if (victim) {
cell_t *c;
int acc;
getlfname(victim,victimname);
// TODO: if jumper is smaller, move them out of the way instead
// move them out of the way
c = getrandomadjcell(victim->cell, WE_EMPTY, B_NOEXPAND);
// nowhere to move? move to player's cell!
if (!c) {
c = user->cell;
}
movelf(victim, c);
// see if you actually landed on them or they dodge...
acc = 100 - getevasion(victim);
if (rnd(1,100) > acc) {
// dodged!
dodged = B_TRUE;
}
}
movelf(user, targcell);
// announce
if (isplayer(user)) {
if (victim && !dodged) {
msg("You leap high in the air and land on %s!",victimname);
} else {
msg("You leap high in the air!");
}
} else if (haslos(player, origcell)) {
if (cansee(player, user)) {
if (victim && !dodged) {
msg("%s leaps high in the air and lands on %s!", username,victimname);
} else {
msg("%s leaps high in the air and lands nearby!", username);
}
} else {
msg("%s leaps high in the air!", username);
}
} else if (cansee(player, user)) {
if (victim && !dodged) {
msg("%s drops from the air and lands nearby!", username);
} else {
msg("%s drops from the air and lands on %s!", username,victimname);
}
}
// 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);
break;
}
}
if (victim) {
if (dodged) {
if (cansee(player, user)) {
msg("%s dodges out of the way!", victimname);
}
} else {
int dam;
// they take damage based on the jumper's weight
// 1hp per 15 kg
// TODO: make divisor be dependant on difference in lf size
dam = getlfweight(user, B_WITHOBS) / 15;
if (dam > 0) {
if (lfhasflag(user, F_EXTRAINFO) || lfhasflag(user, F_OMNIPOTENT)) {
msg("[%s takes %d damage]",victimname,dam);
}
snprintf(buf, BUFLEN, "a falling %s", user->race->name);
losehp(victim, dam, DT_BASH, user, buf);
}
}
}
} else if (abilid == OT_A_RAGE) {
int howlong;
f = lfhasflag(user, F_RAGE);
if (f) {
if (isplayer(user)) {
msg("You are already enraged!");
}
return B_TRUE;
}
howlong = 10;
addtempflag(user->flags, F_RAGE, B_TRUE, NA, NA, NULL, howlong);
if (isplayer(user)) {
needredraw = B_TRUE;
statdirty = B_TRUE;
drawscreen();
}
} else if (abilid == OT_A_REPAIR) {
enum SKILLLEVEL slev;
object_t *o,*helpob = NULL;
enum MATERIAL repairablemats[MAXCANDIDATES];
int repaircutoff = 0;
int cutoffpct[MAXCANDIDATES];
int nmats = 0;
int i;
char helpobname[BUFLEN];
// get list of repairable materials
slev = getskill(user, SK_METALWORK);
if (slev) {
int cutoff;
switch (slev) {
case PR_NOVICE: cutoff = 33; break;
case PR_BEGINNER: cutoff = 50; break;
case PR_ADEPT: cutoff = 60; break;
case PR_SKILLED: cutoff = 70; break;
case PR_EXPERT: cutoff = 85; break;
case PR_MASTER: cutoff = 100; break;
default: cutoff = 0; break;
}
helpob = hasob(user->pack, OT_SPANNER);
if (helpob) cutoff += 15;
repairablemats[nmats] = MT_METAL;
cutoffpct[nmats] = cutoff;
nmats++;
}
slev = getskill(user, SK_SEWING);
if (slev) {
int cutoff;
switch (slev) {
case PR_NOVICE: cutoff = 33; break;
case PR_BEGINNER: cutoff = 50; break;
case PR_ADEPT: cutoff = 60; break;
case PR_SKILLED: cutoff = 70; break;
case PR_EXPERT: cutoff = 85; break;
case PR_MASTER: cutoff = 100; break;
default: cutoff = 0; break;
}
helpob = hasob(user->pack, OT_NEEDLE);
if (helpob) cutoff += 15;
repairablemats[nmats] = MT_CLOTH;
cutoffpct[nmats] = cutoff;
nmats++;
repairablemats[nmats] = MT_LEATHER;
cutoffpct[nmats] = cutoff;
nmats++;
}
if (helpob) {
real_getobname(helpob, helpobname, helpob->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE, B_FALSE);
} else {
strcpy(helpobname, "");
}
// 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);
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 (-> %d%%)",buf, cutoff);
// we can repair this object
addchoice(&prompt, o->letter, desc, desc, o, NULL);
}
}
}
if (prompt.nchoices <= 1) {
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)) {
getchoice(&prompt);
o = (object_t *) prompt.result;
} else {
// pick a random one
o = (object_t *) prompt.choice[rnd(0,prompt.nchoices-1)].data;
}
// in case it's on fire, etc
if (touch(user, o)) {
taketime(user, getactspeed(user));
return B_FALSE;
}
// select cutoff hp
repaircutoff = 0;
for (i = 0; i < nmats; i++) {
if (o->material->id == repairablemats[i]) {
repaircutoff = cutoffpct[i];
break;
}
}
assert(repaircutoff != 0);
// repair it!
f = hasflag(o->flags, F_OBHP);
f->val[0] = pctof(repaircutoff, f->val[1]);
if (isplayer(user)) {
char buf[BUFLEN],withbuf[BUFLEN];
real_getobname(o, buf, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE, B_FALSE);
if (helpob) sprintf(withbuf, " (with %s)", helpobname);
else strcpy(withbuf, "");
msg("You %srepair your %s%s.", (f->val[0] == f->val[1]) ? "" : "partially ",
noprefix(buf), withbuf);
} else {
char buf[BUFLEN],withbuf[BUFLEN];
real_getobname(o, buf, o->amt, B_TRUE, B_FALSE, B_TRUE, B_TRUE, B_FALSE);
if (helpob) sprintf(withbuf, " with %s", helpobname);
else strcpy(withbuf, "");
msg("%s %s repairs %s%s.", username, (f->val[0] == f->val[1]) ? "completely" : "partially",
buf, withbuf);
}
// TODO: make this like eating/resting/etc ?
taketime(user, getactspeed(user));
} else if (abilid == OT_A_SHIELDBASH) {
object_t *shield;
char dirch;
flag_t *f;
int badshield = B_FALSE;
int shpenalty,mod;
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;
}
shield = getshield(user);
if (!shield || !isshield(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) {
dirch = askchar("Shield bash in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if (dirch == '.') {
// yourself!
targcell = user->cell;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to attack!");
return B_TRUE;
}
getlfname(target, 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)) {
// success depends on shield skill, relative lf size, target's evasion
mod = 0;
modifyforsize(&mod, user, target, 5, M_VAL);
if (mod > 0) mod = 0;
if (skillcheck(user, SC_SHIELDBLOCK, 5 + getevasion(target), mod)) {
stun(target, 2);
}
}
// shield gets damaged
takedamage(shield, roll("1d3"), DT_DIRECT);
} else if (abilid == OT_A_SONICBOLT) {
int volume;
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)) {
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;
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 (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
if (isplayer(user)) msg("You can't sprint while swimming!");
return B_TRUE;
}
f = lfhasflag(user, F_SPRINTING);
if (f) {
if (isplayer(user)) {
msg("You are already sprinting!");
}
return B_TRUE;
}
if (isburdened(user)) {
if (isplayer(user)) {
msg("You cannot sprint while burdened.");
}
return B_TRUE;
}
if (isplayer(user)) {
int dir,dirch;
dirch = askchar("Sprint in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if (dirch == '.') {
msg("Cancelled.");
return B_TRUE;
}
dir = chartodir(dirch);
setfacing(user, dir);
}
addflag(user->flags, F_SPRINTING, B_TRUE, NA, NA, NULL);
practice(user, SK_ATHLETICS, 1);
} else if (abilid == OT_A_STINGACID) {
validateabillf(user, abilid, &target);
if (!target) return B_TRUE;
getlfname(target, targetname);
if (cansee(player, target)) {
msg("%s inject%s acid into %s!", username,
isplayer(user) ? "" : "s",
targetname);
}
losehp(target, roll(damstr), DT_ACID, user, killername);
// cause ongoing pain for 2 turns
addtempflag(target->flags, F_PAIN, DT_ACID, NA, NA, damstr, 2);
taketime(user, getactspeed(user));
} else if (abilid == OT_A_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?");
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 = 20 + (getspelllevel(f->val[0])*3);
sprintf(buf2, "%s (%d%% success chance)", buf, difficulty);
addchoice(&prompt, o->letter, buf, 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 = 20 + (getspelllevel(f->val[0])*3);
mod = getspellskill(user, f->val[0]) * 2;
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_SUCKBLOOD) {
int dam = 0;
f = lfhasflag(user, F_ATTACHEDTO);
if (!f) {
if (isplayer(user)) msg("You need to attach to someone before using this ability.");
return B_TRUE;
}
// announce
if (cansee(player, target)) {
msg("%s suck%s blood from %s!", username, isplayer(user) ? "" : "s", targetname);
}
// fixed damage?
if (strlen(damstr)) {
dam = roll(damstr);
} else {
dam = 1;
}
losehp(target, dam, DT_DIRECT, user, killername);
taketime(user, getactspeed(user));
// special case
if (!isplayer(user)) {
if ((user->race->id == R_STIRGE) || (user->race->id == R_LEECH)) {
int satedat = 8;
int num;
num = modcounter(user->flags, dam);
switch (user->race->id) {
default:
case R_STIRGE:
satedat = 8;
break;
case R_LEECH:
satedat = 12;
break;
}
if (num >= satedat) {
// sated.
killflagsofid(user->flags, F_ATTACHEDTO);
makepeaceful(user);
addflag(user->flags, F_FLEEONDAM, B_TRUE, NA, NA, NULL);
addflag(user->flags, F_DIESPLATTER, 3, NA, NA, "splash of blood");
// counter will keep ticking up.
// once it gets past next threshold, monster will go to sleep.
modcounter(user->flags, rnd(1,4));
}
}
}
} else if (abilid == OT_A_SWOOP) {
cell_t *adjcell = NULL,*origcell;
char targetname[BUFLEN];
int i;
cell_t *retcell[MAXRETCELLS];
int nretcell;
int srange = 5;
flag_t *srflag;
// get max range
srflag = lfhasflag(user, F_SWOOPRANGE);
if (srflag) {
srange = srflag->val[0];
}
if (isimmobile(user)) {
if (isplayer(user)) msg("You can't move!");
return B_TRUE;
} else if (!lfhasflag(user, F_FLYING)) {
if (isplayer(user)) msg("You are not flying, therefore cannot swoop.");
return B_TRUE;
}
if (!targcell) {
if (isplayer(user)) {
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;
}
// make sure we have LOF to there
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there!");
return TRUE;
} else if (!haslof(user->cell, targcell, LOF_NEED, NULL)) {
if (isplayer(user)) msg("Your path there is blocked!");
return TRUE;
}
getlfname(target, targetname);
// find cell on the way...
calcbresnham(user->cell->map, user->cell->x, user->cell->y, targcell->x, targcell->y, retcell, &nretcell);
for (i = 1; i < nretcell; i++) {
if (retcell[i] == targcell) {
adjcell = retcell[i-1];
break;
}
}
if (!adjcell) {
if (isplayer(user)) {
msg("There is no space nearby for you to attack!");
}
return B_TRUE;
}
// take some time
taketime(user, getactspeed(user));
// remember orig cell
origcell = user->cell;
// teleport next to them
movelf(user, adjcell);
if (haslos(player, adjcell)) {
msg("%s swoop%s towards %s!",username,isplayer(user) ? "" : "s",targetname);
needredraw = B_TRUE;
drawlevelfor(player);
redraw();
//if (!isplayer(user)) {
more();
//}
}
// attack
attackcell(user, targcell, B_TRUE);
// teleport back to initial pos
movelf(user, origcell);
if (haslos(player, origcell)) {
redraw();
}
} 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;
// start training!
if (!startresting(user, B_TRUE)) {
// do the first one right away
rest(user, B_TRUE);
}
} 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)) {
if (isplayer(user)) msg("You can't tumble while airbourne!");
return B_TRUE;
}
if (targcell) {
dir = getdirtowards(user->cell, targcell, NULL, B_FALSE, DT_ORTH);
} else {
char dirch;
dirch = askchar("Tumble in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
cell_t *cell1 = NULL,*cell2 = NULL;
cell1 = getcellindir(user->cell, dir);
if (cell1 && cellwalkable(user, cell1, NULL)) {
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)) {
// 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, 10, 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);
// 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, 20, 0)) {
// fail!
fall(user, NULL, B_TRUE);
return B_FALSE;
}
}
} 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 *lf;
if (!isplayer(user)) return B_FALSE;
// ask for which god
initprompt(&prompt, "To whom will you pray?");
prompt.maycancel = B_TRUE;
lf = askgod("To whom will you pray?");
if (!lf) {
msg("Cancelled.");
return B_TRUE;
}
prayto(user, lf);
} 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);
}
}
} 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 dirch;
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) {
dirch = askchar("Aimed strike in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if (dirch == '.') {
// yourself!
targcell = user->cell;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to attack!");
return B_TRUE;
}
getlfname(target, targetname);
f = addflag(user->flags, F_AIMEDSTRIKE, B_TRUE, NA, NA, NULL);
attackcell(user, targcell, B_TRUE);
killflag(f);
} 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);
} 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) {
char dirch;
cell_t *c;
keepgoing = B_FALSE;
// ask direction
c = NULL;
while (!c) {
char ques[BUFLEN];
snprintf(ques, BUFLEN,"%s combination in which direction (- to cancel)", nhits ? "Continue" : "Start");
dirch = askchar(ques, "yuhjklbn-","-", B_FALSE, B_TRUE);
if (dirch == '-') {
break;
} else {
int dir;
dir = chartodir(dirch);
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_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);
}
} 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_HEAVYBLOW) {
object_t *wep;
char dirch;
char targetname[BUFLEN];
flag_t *f;
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;
}
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) {
dirch = askchar("Heavy blow in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if (dirch == '.') {
// yourself!
targcell = user->cell;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to attack!");
return B_TRUE;
}
getlfname(target, targetname);
f = addflag(user->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL);
attackcell(user, targcell, B_TRUE);
killflag(f);
} else if (abilid == OT_A_QUIVERINGPALM) {
object_t *wep;
char dirch;
char targetname[BUFLEN];
flag_t *f;
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
if (isplayer(user)) msg("You lack the stability 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) {
dirch = askchar("Quivering Palm in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if (dirch == '.') {
// yourself!
targcell = user->cell;
} else {
int dir;
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to attack!");
return B_TRUE;
}
getlfname(target, targetname);
f = addflag(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;
char dirch;
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 shop items
if (isroom(user->cell) && hasobwithflagval(user->cell->obpile, F_SHOPITEM, NA, getroomid(user->cell), NA, NULL)) {
// stealing from a shop
char yn;
yn = askchar("Steal something from this shop?", "yn","n", B_TRUE, B_FALSE);
if (yn == 'y') {
object_t *o;
flag_t *f;
int value;
taketime(user, getactspeed(user));
value = 0;
for (o = user->cell->obpile->first ; o ; o = o->next) {
f = hasflagval(o->flags, F_SHOPITEM, NA, getroomid(user->cell), NA, NULL);
if (f) {
value += f->val[0];
}
}
// skillcheck - difficulty based on total value of objects here
// 15 + value/50 means:
// $50 is diff 16
// $100 is diff 17
// $200 is diff 19
// $500 is diff 25
// $1000 is diff 35
if (skillcheck(user, SC_STEAL, 15+(value/50), 0)) {
// success
if (steal(user, user->cell->obpile, F_SHOPITEM)) {
if (isplayer(user)) {
msg("There doesn't seem to be anything here which you could steal.");
}
} else {
pleasegodmaybe(R_GODTHIEVES, 5);
}
} else {
lifeform_t *shk;
msg("You try to steal something, but fail.");
// failed
shk = findshopkeeper(user->cell->map, getroomid(user->cell));
if (shk) { // doesn't need to see you - he SENSES it!
say(shk, "THIEF!", SV_ROAR);
fightback(shk, user);
callguards(shk, user);
}
}
}
} else {
// stealing from a lifeform
// ask for direction
if (!targcell) {
int dir;
dirch = askchar("Steal in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE, B_TRUE);
dir = chartodir(dirch);
if (dir == D_NONE) {
if (isplayer(user)) msg("Cancelled.");
return B_TRUE ;
} else {
targcell = getcellindir(user->cell, dir);
}
}
target = targcell->lf;
if (!target) {
if (isplayer(user)) msg("There is nobody there to 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)) {
// 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) {
// 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)) {
scare(target, user, rnd(5,10), 0);
}
}
}
} else if (abilid == OT_A_HURRICANESTRIKE) {
int dir;
cell_t *c;
flag_t *f,*f2,*f3;
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);
// 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) {
int penalty = 0;
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
if (isplayer(user)) msg("You can't hide while swimming!");
return B_TRUE;
}
if (lfhasflag(user, F_HIDING)) {
if (isplayer(user)) msg("You are already hiding!");
return B_TRUE;
}
penalty = gethidemodifier(user);
if (reason == E_IMPOSSIBLE) {
if (isplayer(user)) msg("You can't hide - there are monsters around!");
return B_TRUE;
}
// start hiding
addflag(user->flags, F_HIDING, penalty, NA, NA, NULL);
taketime(user, getactspeed(user));
} else if (abilid == OT_A_INSPECT) {
object_t *o;
int difficulty;
int rarity;
int mod;
enum SKILLLEVEL slev;
flag_t *f;
// only players can do this
if (!isplayer(user)) {
return B_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;
// can only inspect certain types of things
switch (o->type->obclass->id) {
case OC_SCROLL:
case OC_BOOK:
case OC_WAND:
case OC_RING:
classok = B_TRUE;
break;
default:
break;
}
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);
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) {
rarity = f->val[1];
} else {
rarity = 0;
}
difficulty = 25 + ((100 - rarity) / 5);
switch (o->type->obclass->id) {
case OC_SCROLL:
difficulty += 2; break;
case OC_BOOK:
difficulty += 4; break;
case OC_WAND:
difficulty += 6; break;
case OC_RING:
difficulty += 8; break;
default:
break;
}
slev = getskill(user, SK_LORE_ARCANA);
switch (slev) {
case PR_INEPT:
mod = -5;
break;
default:
mod = slev*4;
break;
}
if (skillcheck(user, SC_IQ, difficulty, mod)) {
char obname[BUFLEN];
// passed!
makeknown(o->type->id);
getobname(o, obname, o->amt);
msgnocap("This seems to be %s!", obname);
} else {
msg("You have no idea what this is.");
addflag(user->flags, F_FAILEDINSPECT, o->type->id, NA, NA, NULL);
}
taketime(user, getactspeed(user));
}
// expire ability
f = lfhasflagval(user, F_CANWILL, abilid, NA, NA, NULL);
if (f) {
if (f->val[2] != NA) {
// ie. it will go to 0 next turn.
f->val[1] = -1;
}
}
return B_FALSE;
}
// returns TRUE on error
int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_t *target, object_t *targob, cell_t *targcell, int blessed, int *seenbyplayer, int frompot) {
char buf[BUFLEN];
char castername[BUFLEN];
int rv = B_FALSE;
objecttype_t *sp;
flag_t *casttype = NULL;
sp = findot(spellid);
if (caster) {
getlfname(caster, castername);
} else {
strcpy(castername, "something");
}
// default to unseen
if (seenbyplayer) *seenbyplayer = B_FALSE;
if (caster) {
if (hasflag(sp->flags, F_ONGOING)) {
if (lfhasflagval(caster, F_BOOSTSPELL, spellid, NA, NA, NULL)) {
// cancel it.
stopspell(caster, spellid);
return B_FALSE;
} else {
objecttype_t *sp;
int cost;
cost = getmpcost(caster, spellid);
sp = findot(spellid);
if (sp && hasflag(sp->flags, F_VARPOWER)) {
cost *= power;
}
addflag(caster->flags, F_BOOSTSPELL, spellid, cost, power, NULL);
}
}
// 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 += getobweight(o);
}
}
}
for (o = caster->cell->obpile->first ; o ; o = o->next){
if (o->type->obclass->id == OC_FLORA) totweight += getobweight(o);
}
for (o = caster->pack->first ; o ; o = o->next){
if (o->type->obclass->id == OC_FLORA) totweight += getobweight(o);
}
powerinc = totweight / 50;
if (powerinc > 0) {
power += powerinc;
}
}
// special: spit attacks need an animation
casttype = lfhasflag(caster, F_CASTTYPE);
if (casttype && (casttype->val[0] == CT_EYESPIT)) {
enum COLOUR col;
if (casttype->val[1] == NA) {
col = C_GREEN;
} else {
col = casttype->val[1];
}
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, RC_DEMON, SZ_ANY, AL_NONE, 1, PERMENANT, B_FALSE)) {
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);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(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);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(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);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(targcell->lf->pack);
}
}
if (totalmass > 0) {
float max;
// heal 1 mp per kilo
howmuch = floor(totalmass);
// maximum based on power
max = 40 + (power*10);
if (howmuch > max) howmuch = max;
gainmp(caster, howmuch);
} else {
fizzle(caster);
}
} else if (spellid == OT_S_AIRBLAST) {
int dir;
object_t *o,*nexto;
cell_t *firstobcell = NULL;
cell_t *nextc;
if (targcell) {
dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS);
} else {
int dirch;
dirch = askchar("Airblast in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if ((dirch == '.') || (dirch == '-')) {
fizzle(caster);
return B_TRUE;
}
dir = chartodir(dirch);
}
// keep going that dir until the next cell is a wall/lf/impassable ob
targcell = caster->cell;
nextc = getcellindir(targcell, dir);
while (nextc && cellwalkable(NULL, nextc, NULL)) {
targcell = nextc;
if (!firstobcell) {
// obs here?
for (o = targcell->obpile->first ;o ; o = o->next) {
if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) {
firstobcell = targcell;
break;
}
}
}
nextc = getcellindir(targcell, dir);
}
if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
msg("%s emit%s a powerful blast of air!", castername, isplayer(caster) ? "" : "s");
}
// if it was a lifeform which stopped us...
if (nextc->lf) {
targcell = nextc;
target = targcell->lf;
knockback(target, dir, power, caster, 0, B_TRUE);
} else if (firstobcell) {
// objects
for (o = firstobcell->obpile->first ;o ; o = nexto) {
nexto = o->next;
if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) {
knockbackob(o, dir, power, power, caster);
}
}
}
} else if (spellid == OT_S_ALARM) {
if (isplayer(caster)) {
msg("You create a alarm field around yourself.");
}
} else if (spellid == OT_S_ANIMATEDEAD) {
int i;
object_t *o,*nexto;
int donesomething = B_FALSE;
if (isplayer(caster)) {
target = caster;
}
// animate corpses within lof of caster
for (i = 0; i < target->nlos; i++) {
targcell = target->los[i];
if (targcell != target->cell) {
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (o->type->id == OT_CORPSE) {
lifeform_t *newlf;
newlf = makezombie(o);
if (newlf) {
if (isplayer(target) && skillcheck(target, A_IQ, 20, power)) {
makefriendly(newlf, PERMENANT);
}
donesomething = B_TRUE;
}
}
}
}
}
if (donesomething) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
} else if (spellid == OT_S_ANIMATEMETAL) {
object_t *o;
o = getweapon(caster);
if (o) {
char obname[BUFLEN];
lifeform_t *newlf;
getobname(o, obname, 1);
if (!ismetal(o->material->id)) {
if (isplayer(caster)) {
msg("Your %s is not metal, therefore unaffected.", noprefix(obname));
} else {
fizzle(caster);
}
return B_FALSE;
} else if (o->amt != 1) {
if (isplayer(caster)) {
msg("Your %s wobbles a little.", obname);
} else {
fizzle(caster);
}
return B_FALSE;
}
newlf = makeanimated(caster, o, caster->level);
if (newlf) {
if (isplayer(caster)) {
msg("Your %s leaps into the air!", noprefix(obname));
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
msg("%s%s %s leaps into the air!",castername, getpossessive(castername),
noprefix(obname));
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
fizzle(caster);
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_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) {
// select object from cell...
targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE);
if (!targob) {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
}
if (!failed) {
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 (getobweight(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);
}
} 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
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);
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_FALSE;
}
// does caster have a weapon?
wep = getweapon(target);
if (!wep) {
fizzle(caster);
return B_FALSE;
}
if (isplayer(target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
addtempflag(wep->flags, F_ONFIRE, B_TRUE, NA, NA, "1d6", 10 + power*3);
} else if (spellid == OT_S_BLINDNESS) {
int failed = B_FALSE;
target = targcell->lf;
if (isblind(target)) {
fizzle(caster);
return B_FALSE;
}
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);
}
} else if (spellid == OT_S_BLINK) {
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)) {
fizzle(caster);
return B_TRUE;
}
} else {
cell_t *poss[MAXCANDIDATES];
int nposs = 0,x,y;
// 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_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_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)) {
msg("%s burn%s!",buf,isplayer(c->lf) ? "" : "s");
}
real_getlfname(caster, realcname, B_FALSE);
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;
howlong = getspellduration(20,30,blessed) + (power*10);
addtempflag(caster->flags, F_ENHANCESMELL, 4, NA, NA, NULL, howlong);
} 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_FALSE;
}
// which object to take?
initprompt(&prompt, "Confiscate which object?");
addchoice(&prompt, '-', "(Cancel)", NULL, NULL, NULL);
for (o = target->pack->first ; o ; o = o->next) {
getobname(o, obname, o->amt);
addchoice(&prompt, ch, obname, NULL, o, NULL);
if (ch == 'z') {
ch = 'A';
} else {
ch++;
}
}
if (isplayer(caster)) {
// 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), (targob->amt == 1) ? "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), (targob->amt == 1) ? "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),
(targob->amt == 1) ? "es" : "");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
}
moveob(targob, caster->pack, ALL);
} else {
fizzle(caster);
}
} 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("%s %s struck by a bolt of lightning!",targname,is(target));
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// outdoors?
if (isoutdoors(target->cell->map)) {
losehp(target, rolldie(4,6), DT_ELECTRIC, caster, "a bolt of lightning");
} else {
losehp(target, rolldie(3,6), DT_ELECTRIC, caster, "a bolt of lightning");
}
} else {
failed = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_CALLWIND) {
int failed = B_FALSE;
float maxweight;
// if no target object...
if (!targob) {
// ask for a target cell (to take objects from)
if (!validatespellcell(caster, &targcell, TT_OBJECT, spellid, power, frompot)) return B_TRUE;
if (targcell->obpile->first) {
// select object from cell...
targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE);
if (!targob) {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
}
if (!failed) {
targcell = caster->cell;
// not liftable?
if (hasflag(targob->flags, F_NOPICKUP)) {
if (isplayer(caster)) {
nothinghappens();
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
// too heavy? max weight is now based on our race's weight and intelligence
maxweight = getlfweight(caster, B_NOOBS) +
(getlfweight(caster, B_NOOBS) * (getstatmod(caster, A_IQ) / 100));
// modify by power
maxweight += (10*power);
if (getobweight(targob) > maxweight) {
cell_t *obloc;
char obname[BUFLEN];
obloc = getoblocation(targob);
getobname(targob, obname, targob->amt);
if (haslos(player, obloc)) {
msg("%s lifts slightly, then drops again.",obname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
pullobto(targob, caster);
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_CALMANIMALS) {
int i,ncalmed = 0;
int powerleft;
int donesomething = B_FALSE;
cell_t *c;
//powerleft = rolldie(power+1, 4);
powerleft = power;
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 (gethitdice(c->lf) <= powerleft) {
powerleft -= gethitdice(c->lf);
makepeaceful(c->lf);
ncalmed++;
// druids get 25% of the monster's XP value for calming it.
if (hasjob(caster, J_DRUID)) {
if (getallegiance(caster) == AL_FRIENDLY) {
awardxpfor(c->lf, 25);
}
}
if (cansee(player, c->lf)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
donesomething++;
} 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)) {
pleasegod(R_GODMERCY, ncalmed);
}
if (!donesomething) {
fizzle(caster);
}
} else if (spellid == OT_S_CALMINGSCENT) {
// just announce
if (isplayer(caster)) {
msg("A relaxing aroma surrounds you.");
}
} else if (spellid == OT_S_CHAINLIGHTNING) {
cell_t *hitcell[MAXRETCELLS]; // all cells which have been hit in total
cell_t *arccell[MAXRETCELLS]; // cells hit this time
cell_t *arccell2[MAXRETCELLS]; // cells hit this time
int nhitcells = 0, narccells,narccells2;
int i;
int range;
int nsides = 6;
range = getspellrange(caster, spellid, power);
// create a line of fire towards the target cell
animline(caster->cell, targcell, B_FALSE, '/', '\\', C_WHITE);
if (cansee(player, caster)) {
msg("%s shoot%s a bolt of electricity!",castername, isplayer(caster) ? "" : "s");
}
if (targcell->lf) {
arccell[0] = targcell;
narccells = 1;
} else {
fizzle(caster);
}
nhitcells = 0;
while (narccells) {
int stillseen = B_FALSE;
// everyone takes damage, and mark cells as hit
for (i = 0; i < narccells; i++) {
losehp(arccell[i]->lf, rolldie(3,nsides), DT_ELECTRIC, caster, "an electricity bolt");
hitcell[nhitcells++] = arccell[i];
if (haslos(player, arccell[i])) {
stillseen = B_TRUE;
needredraw = B_TRUE;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
}
// now range gets halved and damage lowered
range /= 2;
if (range <= 0) break;
if (--nsides <= 0) break; // ie. max of 5 arcs
// now arc to other cells with lfs still within range
narccells2 = 0;
for (i = 0; i < narccells; i++) {
int x,y;
cell_t *c;
for (y = arccell[i]->y - range ; y <= arccell[i]->y + range; y++) {
for (x = arccell[i]->x - range ; x <= arccell[i]->x + range; x++) {
c = getcellat(targcell->map, x, y);
if (c && c->lf && (c->lf != caster)
&& haslof(arccell[i], c, LOF_NEED, NULL)) {
int n,found = B_FALSE;
// if this cell hasn't been hit...
for (n = 0; n < nhitcells; n++) {
if (hitcell[n] == c) {
found = B_TRUE;
break;
}
}
if (!found) {
// will arc to here
arccell2[narccells2++] = c;
animline(arccell[i], c, B_FALSE, '/', '\\', C_WHITE);
}
}
}
}
}
// replace arccells
for (i = 0; i < narccells2; i++) {
arccell[i] = arccell2[i];
}
narccells = narccells2;
if (narccells && stillseen) {
msg("The electricity arcs!");
}
} // end while narccells
} else if (spellid == OT_S_CLOUDKILL) {
int radius;
if (targcell->type->solid) {
fizzle(caster);
return B_FALSE;
}
radius = (power/4)+1;
addobburst(targcell, radius, DT_COMPASS, "puff of poison gas", caster, LOF_WALLSTOP);
if (haslos(player, targcell)) {
msg("A puff 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_FALSE;
}
getlfname(target, targetname);
// only animals/humanoids
if (getraceclass(target) != getraceclass(caster)) {
if (isplayer(caster)) {
msg("%s%s mind is too alien for you to charm.",targetname,getpossessive(targetname));
}
return B_FALSE;
}
if (!ischarmable(target)) {
if (isplayer(caster)) {
switch (reason) {
case E_DRUNK:
msg("%s%s mind is too alcohol-impaired for you to charm.",targetname,getpossessive(targetname));
break;
case E_LOWIQ:
msg("%s%s intellect is too simple for you to charm.",targetname,getpossessive(targetname));
break;
case E_UNDEAD:
msg("The undead are immune to charming.");
break;
default:
msg("You cannot charm %s.", targetname);
break;
}
}
return B_FALSE;
}
if (getallegiance(caster) == AL_PEACEFUL) {
fizzle(caster);
return B_FALSE;
}
if (ispetof(target, caster)) {
if (isplayer(caster)) {
msg("%s is already allied with you!",targetname);
}
return B_FALSE;
}
if (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;
}
howlong = getspellduration(20,30,blessed) + (power*10);
//
addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong);
addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong);
}
} else if (spellid == OT_S_CHARMANIMAL) {
char targetname[BUFLEN];
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, targetname);
// only animals
if (getraceclass(target) != RC_ANIMAL) {
fizzle(caster);
return B_FALSE;
}
if (!ischarmable(target)) {
if (isplayer(caster)) {
switch (reason) {
case E_DRUNK:
msg("%s%s mind is too alcohol-impaired for you to charm.",targetname,getpossessive(targetname));
break;
case E_LOWIQ:
msg("%s%s intellect is too simple for you to charm.",targetname,getpossessive(targetname));
break;
case E_UNDEAD:
msg("The undead are immune to charming.");
break;
default:
msg("You cannot charm %s.", targetname);
break;
}
}
return B_FALSE;
}
if (getallegiance(caster) == AL_PEACEFUL) {
fizzle(caster);
return B_FALSE;
}
if (ispetof(target, caster)) {
if (isplayer(caster)) {
msg("%s is already allied with you!",targetname);
}
return B_FALSE;
}
// no saving throw, just depends on hit dice.
// too powerful?
if (gethitdice(target) <= power) {
int howlong;
if (isplayer(caster) || cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
howlong = getspellduration(20,30,blessed) + (power*10);
//
addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong);
addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong);
} else {
if (isplayer(caster) || cansee(player, target)) {
msg("%s resists.",targetname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// they get angry!
if (!isplayer(target) && cansee(target, caster)) {
fightback(target, caster);
}
}
} else if (spellid == OT_S_CHILL) {
char lfname[BUFLEN];
int exposedlimbs,dam;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, lfname);
// how many body parts are impacted?
exposedlimbs = getexposedlimbs(target);
dam = rnd(1,exposedlimbs);
if (isplayer(target)) {
if (isimmuneto(target->flags, DT_COLD)) {
msg("You feel mildly chilly.");
} else {
msg("You feel cold!");
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
if (isimmuneto(target->flags, DT_COLD)) {
msg("%s looks mildly chilly.", lfname);
} else {
msg("%s looks cold!", lfname);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// target takes magical damage
// always hit
if (!isimmuneto(target->flags, DT_COLD)) {
losehp(target, dam, DT_COLD, caster, "a chill spell");
}
} else if (spellid == OT_S_COLDBURST) {
int range = 1;
int x,y;
range = 1 + (power / 5);
// announce
if (isplayer(caster) || cansee(player, caster)) {
msg("%s emit%s a blast of icy cold!",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
animradialorth(caster->cell, range, '}', C_GREY);
for (y = caster->cell->y - range ; y <= caster->cell->y + range; y++) {
for (x = caster->cell->x - range ; x <= caster->cell->x + range; x++) {
targcell = getcellat(caster->cell->map, x,y);
if (targcell && (getcelldistorth(caster->cell, targcell) <= range) &&
haslof(caster->cell, targcell, B_FALSE, NULL)) {
if (targcell->lf) {
if (targcell->lf != caster) {
char lfname[BUFLEN];
// automatic hit
getlfname(targcell->lf, lfname);
if (haslos(caster, targcell)) {
msg("%s %s chilled!",lfname,is(targcell->lf));
}
losehp(targcell->lf, rolldie(1,8)+3, DT_COLD, caster, "a burst of coldness");
}
} else {
// noone there, hit objects.
damageallobs(NULL, targcell->obpile, 0, DT_COLD);
}
}
}
}
} else if (spellid == OT_S_COLDRAY) {
char lfname[BUFLEN];
cell_t *retcell[MAXRETCELLS];
int nretcell,i;
// animation
anim(caster->cell, targcell, '}', C_GREY);
if (isplayer(caster) || cansee(player, caster)) {
msg("%s shoot%s a ray of coldness.",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y,
targcell->x, targcell->y, retcell, &nretcell);
for (i = 1; i < nretcell; i++) {
target = haslf(retcell[i]);
if (target) {
getlfname(target, lfname);
// target takes magical damage
if (skillcheck(target, SC_DODGE, 20 + (power*2), 0)) {
// miss
if (cansee(player, target)) {
msg("A ray of coldness misses %s.",lfname);
}
} else {
// hit
if (cansee(player, target)) {
msg("A ray of coldness ray hits %s.",lfname);
}
losehp(target, roll("3d6"), DT_COLD, caster, "a blast of coldness");
// ray stops here.
break;
}
} else {
damageallobs(NULL, retcell[i]->obpile, 0, DT_COLD);
}
}
} else if (spellid == OT_S_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_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, WE_EMPTY, B_ALLOWEXPAND);
}
}
if (!targcell) {
fizzle(caster);
return B_TRUE;
}
// determine type of mosnter
if (getforcedspellrace(caster, spellid, buf)) {
} 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);
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);
}
}
}
}
needredraw = B_TRUE;
} else if (spellid == OT_S_CREATEVAULT) {
vault_t *v;
int vx,vy;
int vw,vh;
// ask for a vaulttype
v = askvault("Create which vault?");
if (createvault(caster->cell->map, caster->cell->map->nrooms, 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, getroomid(c));
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') {
int x,y;
// find it
for (y = vy; y < vy+vh; y++) {
for (x = vy; x < vx + vw; x++) {
c = getcellat(caster->cell->map, x, y);
if (c && cellwalkable(caster, c, NULL)) {
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("%s forms %s your %s!", obname, getbodypartequipname(bp[i]), getbodypartname(target, bp[i]));
} else if (cansee(player, target)) {
msg("%s forms %s %s%s %s!", obname, getbodypartequipname(bp[i]),
castername, getpossessive(castername), getbodypartname(target, bp[i]));
}
wear(target, o);
// 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_FALSE;
}
} else if (spellid == OT_S_CRYSTALSHIELD) {
object_t *o;
targcell = caster->cell;
target = caster;
if (getequippedob(target->pack, BP_SECWEAPON)) {
fizzle(caster);
stopspell(caster, spellid);
return B_FALSE;
}
o = addob(target->pack, "ice crystal shield");
if (o) {
if (canwear(target, o, BP_NONE)) {
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("A %s shield of shimmering ice forms in your hand!", getsizetext(sz));
} else if (cansee(player, target)) {
msg("A %s shield of shimmering ice forms in %s%s hand!", getsizetext(sz), castername,
getpossessive(castername));
}
wear(target, o);
// 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);
}
} else {
fizzle(caster);
stopspell(caster, spellid);
return B_FALSE;
}
} else if (spellid == OT_S_CUREPOISON) {
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
killflagsofid(target->flags, F_POISONED);
if (cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (isplayer(caster) && !frompot) {
pleasegodmaybe(R_GODMERCY, 3);
}
} else if (spellid == OT_S_DARKNESS) {
if (!targcell) targcell = caster->cell;
// centre on the caster
if (haslos(player, targcell)) {
msg("A cloud of darkness descends!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (blessed) {
// permenant darkness
makelitradius(targcell, power*2, L_PERMDARK, -1);
} else {
// temporary darkness
makelitradius(targcell, power*2, L_PERMDARK, rnd(5,10) + power );
}
calclight(targcell->map);
needredraw = B_TRUE;
drawscreen();
} else if (spellid == OT_S_DETECTAURA) {
if (isplayer(caster)) {
object_t *o;
char obname[BUFLEN];
int somethinghappened = B_FALSE;
for (o = caster->pack->first ; o ; o = o->next) {
if (o->blessed == B_BLESSED) {
getobname(o, obname, o->amt);
msg("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) {
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;
return digup(caster, NULL);
} else if (ch == '>') {
if (seenbyplayer && haslos(player, caster->cell)) *seenbyplayer = B_TRUE;
return digdown(caster, NULL);
} else {
dir = chartodir(ch);
}
}
if (dir == DT_NONE) {
fizzle(caster);
return B_TRUE;
}
ndigs = 0;
// get rid of rock 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) {
// can dig through stone, but nothing else.
if (c->type->material->id == MT_STONE) {
setcelltype(c, c->map->habitat->emptycelltype);
ndigs++;
if (seenthiscell) {
numseen++;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} 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, (o->amt == 1) ? "s" : "");
}
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();
}
} 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 = getrandomdirexcept(DT_COMPASS, target->facing);
setfacing(target, newdir);
// ai loses target info
killflagsofid(target->flags, F_TARGETLF);
killflagsofid(target->flags, F_TARGETCELL);
// announce
if (isplayer(target)) {
msg("You spin 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;
}
// make them dizzy?
if (pctchance(power*10)) {
confuse(target, 2);
}
}
} else if (spellid == OT_S_DETECTLIFE) {
target = caster;
if (isplayer(caster)) {
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 n;
for (n = 0; n < caster->nlos; n++) {
object_t *o;
for (o = caster->los[n]->obpile->first ; o ; o = o->next) {
flag_t *f;
int ispoisoned = B_FALSE, isdecayed = B_FALSE;
if (isrotting(o)) {
isdecayed = B_TRUE;
} else if (o->type->id == OT_POT_POISON) {
ispoisoned = B_TRUE;
} else {
for (f = o->flags->first ; f ; f = f->next) {
if (f->id == F_POISONED) {
ispoisoned = B_TRUE;
f->known = B_TRUE;
break;
} else if ((f->id == F_HITCONFER) && (f->val[0] == F_POISONED)) {
ispoisoned = B_TRUE;
f->known = B_TRUE;
break;
}
}
}
if (ispoisoned || isdecayed) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("%s %s %s!", obname, (o->amt == 1) ? "is" : "are",
ispoisoned ? "poisoned" : "rotted");
npoisoned++;
}
if (o->type->id == OT_POT_POISON) {
makeknown(o->type->id);
}
}
}
if (!npoisoned) {
msg("You can't detect any poison nearby.");
}
} // end if isplayer
} else if (spellid == OT_S_DETONATE) {
// don't need line of fire!
explodecells(targcell, 20, B_TRUE, NULL, power / 4, DT_ORTH, B_TRUE);
if (haslos(player, targcell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_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++) {
object_t *o,*nexto;
for (o = cell[i]->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (o->material->id == MT_WATER) {
int cellseen = B_FALSE;
if (haslos(player, cell[i])) {
cellseen = B_TRUE;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// if 20kilos or more, make steam
if (getobweight(o) >= 25) {
addob(cell[i]->obpile, "cloud of steam");
if (cellseen) nsteamseen++;
}
nseen++;
removeob(o, ALL);
} else if (o->material->id == MT_ICE) {
takedamage(o, roll("2d6"), DT_FIRE);
}
}
}
if (nsteamseen) {
msg("A huge cloud of steam appears!");
} else if (nseen) {
msg("Some nearby water evaporates!");
} else {
fizzle(caster);
}
} 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 += getobweight(o);
removeob(o, o->amt);
}
}
// destroy objects right away
removedeadobs(targcell->obpile);
// explosion, based on size...
if (totalmass > 0) {
// announce
if (totalmass < 10) {
// little one
explodecells(targcell, totalmass*5, B_FALSE, NULL, 0, DT_COMPASS, B_TRUE);
} else {
explodecells(targcell, totalmass*5, B_TRUE, NULL, 1, DT_COMPASS, B_TRUE);
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_DISPERSAL) {
cell_t *c = NULL;
object_t *o, *nexto;
int donesomething = B_FALSE;
if (!targcell) {
if (isplayer(caster)) {
fizzle(caster);
return B_TRUE;
}
}
target = targcell->lf;
if (targcell->lf) {
int resisted = B_FALSE;
// if you cast this at yourself, only check for magic resistance if you have the
// flag.
// other people get a check anyway.
if (targcell->lf == caster) {
if (getmr(targcell->lf) && spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_FALSE)) {
resisted = B_TRUE;
}
} else {
if (spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_FALSE)) {
resisted = B_TRUE;
}
}
if (resisted) {
if (isplayer(targcell->lf)) {
msg("You flicker.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
} else if (haslos(player, targcell)) {
getlfname(targcell->lf, buf);
msg("%s flickers.",buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
}
} else {
c = NULL;
while (!c || c->type->solid || haslf(c)) {
c = getrandomcell(targcell->map);
}
if (cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
}
teleportto(target, c, B_FALSE);
}
}
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
getobname(o, buf, o->amt);
c = NULL;
while (!c || c->type->solid || haslf(c) || hasobwithflag(c->obpile, F_CLIMBABLE)) {
c = getrandomcell(targcell->map);
}
if (haslos(player, targcell) && canseeob(player, o)) {
msg("%s disappear%s!", buf, (o->amt == 1) ? "s" : "");
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
}
moveob(o, c->obpile, o->amt);
if (haslos(player, c) && canseeob(player, o)) {
msg("%s appear%s nearby!", buf, (o->amt == 1) ? "s" : "");
}
}
if (isplayer(caster) && !donesomething) {
nothinghappens();
}
rv = B_FALSE;
} else if (spellid == OT_S_DRAINLIFE) {
char lfname[BUFLEN];
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
} else {
fizzle(caster);
return B_FALSE;
}
if (!isimmuneto(target->flags, DT_NECROTIC)) {
// animation (opposite dir)
anim(targcell, caster->cell, '%', C_MAGENTA);
}
if (isplayer(caster) || cansee(player, caster)) {
if (isimmuneto(target->flags, DT_NECROTIC)) {
msg("%s suck%s death from %s!",castername,isplayer(caster) ? "" : "s", lfname);
} else {
msg("%s suck%s life from %s!",castername,isplayer(caster) ? "" : "s", lfname);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (isplayer(target)) {
msg("You feel your life force draining away!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (target) {
int amt;
if (isimmuneto(target->flags, DT_NECROTIC)) {
// target gains hp
amt = rnd(1,6) + power;
gainhp(target, amt);
// caster loses it
amt = losehp(caster, amt, DT_NECROTIC, caster, "lifeforce drain");
} else {
// target loses hp
amt = losehp(target, rnd(1,6) + power, DT_NECROTIC, caster, "lifeforce drain");
// caster gains it
gainhp(caster, amt);
}
}
} else if (spellid == OT_S_ENERGYBLAST) {
int range;
int x,y;
// announce
if (caster) {
if (!targcell) targcell = caster->cell;
if (isplayer(caster) || cansee(player, caster)) {
msg("%s emit%s a radial blast of energy!",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
}
range = 2 + (power / 4);
animradial(targcell, range, '}', C_CYAN);
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)) {
// automatic hit
if (isplayer(c->lf)) {
msg("A blast of energy hits you!");
} else if (cansee(player, c->lf)) {
char lfname[BUFLEN];
getlfname(c->lf, lfname);
msg("A blast of energy hits %s.",lfname);
}
losehp(c->lf, roll("2d6"), 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("You suffer terrifying nightmares!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
msg("%s thrashes about in its sleep!",targname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
losehp(target, roll("2d6"), DT_DIRECT, caster, "terrifying nightmares");
} else {
scare(target, caster, 5+rnd(1,power), 5+power);
}
} else if (spellid == OT_S_FEEBLEMIND) {
target = targcell->lf;
if ((getattr(target, A_IQ) <= 3) || 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, 3, NA, NULL, howlong);
f->obfrom = OT_S_FEEBLEMIND;
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
} else if (spellid == OT_S_FLASH) {
if (isplayer(caster) || cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
brightflash(caster->cell, 2 + (power/4), caster);
} else if (spellid == OT_S_FLOATINGDISC) {
lifeform_t *newlf;
// get random adjacent cell
targcell = getrandomadjcell(caster->cell, WE_EMPTY, 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[2];
int i,amt;
int depth;
depth = DP_SHOULDERS + power;
//amt = ((power+1) * (power+1)) - 1;
//amt = power;
amt = power*2;
badoid[0] = OT_WATERDEEP;
badoid[1] = OT_NONE;
for (i = 0; i < amt; i++) {
cell_t *c;
c = real_getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND, LOF_WALLSTOP, badoid);
if (c) {
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;
}
} else {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_FORCESPHERE) {
cell_t *retcell[MAXRETCELLS];
int radius,nretcells,n,i;
if (!target) target = caster;
targcell = target->cell;
radius = power/3;
if (isplayer(target)) {
msg("You unleash a mighty shockwave!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
char tname[BUFLEN];
getlfname(target, tname);
msg("%s unleashes a mighty shockwave!", tname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// 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, 27+power, B_TRUE);
}
}
}
} 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) {
getlfname(target, lfname);
// target takes magical damage
// ALWAYS hits.
if (cansee(player, target)) {
if (power == 1) {
msg("A bolt of energy hits %s.",lfname);
} else {
msg("%s bolts of energy hit %s.",numbuf, lfname);
}
}
losehp(target, rolldie(power,4), DT_MAGIC, caster, "an energy bolt");
}
} else if (spellid == OT_S_FIREBALL) {
int failed = B_FALSE;
if (targcell) {
if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) {
int dir;
cell_t *c;
object_t *o;
// centre fireball here...
if (isplayer(caster)) {
msg("You launch an enormous ball of fire!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
msg("%s launches an enormous ball of fire!",castername);
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, targcell)) {
msg("An enormous ball of fire explodes!");
}
anim(caster->cell, targcell, '^', C_RED);
redrawpause();
// add fires as follows (3 = medium, 2 = medium, 1 = smell)
//
// 1
// 232
// 13331
// 232
// 1
// add large fires to surrounding cells
addob(targcell->obpile, "medium fire");
for (dir = D_N; dir <= D_W; dir++) {
c = getcellindir(targcell, dir);
if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) {
o = addob(c->obpile, "medium fire");
if (o) {
setobcreatedby(o, caster);
}
if (c->lf) {
losehp(c->lf, rolldie(power,3), DT_FIRE, caster, "a fireball");
}
}
}
for (dir = DC_NE; dir <= DC_NW; dir += 2) {
cell_t *c;
c = getcellindir(targcell, dir);
if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) {
o = addob(c->obpile, "medium fire");
if (o) {
setobcreatedby(o, caster);
}
if (c->lf) {
int ndice;
ndice = power / 2; if (ndice < 1) ndice = 1;
losehp(c->lf, rolldie(ndice,3), DT_FIRE, caster, "a fireball");
}
}
}
for (dir = D_N; dir <= D_W; dir++) {
cell_t *c;
c = getcellindir(targcell, dir);
if (c) {
c = getcellindir(c, dir);
if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) {
o = addob(c->obpile, "small fire");
if (o) {
setobcreatedby(o, caster);
}
if (c->lf) {
int ndice;
ndice = power / 2; if (ndice < 1) ndice = 1;
losehp(c->lf, rolldie(ndice,2), DT_FIRE, caster, "a fireball");
}
}
}
}
redrawresume();
} else {
failed = B_TRUE;
}
} else {
if (isplayer(caster)) {
msg("You have no line of fire to there!");
}
failed = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_FIREDART) {
char lfname[BUFLEN];
// animation
anim(caster->cell, targcell, '}', C_RED);
if (isplayer(caster) || cansee(player, caster)) {
msg("%s shoot%s a dart of flame.",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
// target takes magical damage
// check if it hits
if (skillcheck(target, SC_DODGE, 20 + (power*2), 0)) {
// miss
msg("A dart of flame misses %s.",lfname);
} else {
// hit
if (cansee(player, target)) {
msg("A dart of flame hits %s.",lfname);
}
losehp(target, rnd(1,6) + power, DT_FIRE, caster, "a dart of flame");
}
} else {
damageallobs(NULL, targcell->obpile, 0, DT_FIRE);
}
} else if (spellid == OT_S_FLAMEBURST) {
int range = 1;
int x,y;
range = 1 + (power / 5);
// announce
if (isplayer(caster) || cansee(player, caster)) {
msg("%s emit%s a %sblast of fire!",castername,isplayer(caster) ? "" : "s",
(power >= 5) ? "huge " : "");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
animradialorth(caster->cell, range, '}', C_RED);
for (y = caster->cell->y - range ; y <= caster->cell->y + range; y++) {
for (x = caster->cell->x - range ; x <= caster->cell->x + range; x++) {
targcell = getcellat(caster->cell->map, x,y);
if (targcell && (getcelldistorth(caster->cell, targcell) <= range)) {
if (targcell->lf && (targcell->lf != caster) && haslof(caster->cell, targcell, B_FALSE, NULL)) {
char lfname[BUFLEN];
// automatic hit
getlfname(targcell->lf, lfname);
if (haslos(caster, targcell)) {
msg("%s burn%s!",lfname,isplayer(targcell->lf) ? "" : "s");
}
losehp(targcell->lf, rolldie(2,6), DT_FIRE, caster, "a burst of fire");
}
}
}
}
} else if (spellid == OT_S_FLAMEPILLAR) {
int failed = B_FALSE;
if (targcell) {
if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) {
flag_t *f;
object_t *o;
// create flame there
if (haslos(player, targcell)) {
msg("A raging pillar of flame appears!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
o = addob(targcell->obpile, "large fire");
// magically boost hp based on power
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] += power;
f->val[1] += power;
}
setobcreatedby(o, caster);
} else {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_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 collapse 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
o = askobjectwithflag(caster->pack, "Enchant which object", NULL, AO_NONE, F_ENCHANTABLE);
}
if (!o) {
fizzle(caster);
return B_TRUE;
}
if (isplayer(caster)) {
if (!hasflag(o->flags, F_ENCHANTABLE)) {
if (isplayer(caster)) nothinghappens();
return B_TRUE;
} else {
int amt;
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("Your %s glows %s for a moment.", noprefix(obname), (blessed == B_CURSED) ? "black" : "green");
if (seenbyplayer) *seenbyplayer = B_TRUE;
if (blessed == B_CURSED) {
amt = -1;
} else {
amt = 1;
}
modbonus(o, amt);
if (iscursed(o)) {
setblessed(o, B_UNCURSED);
}
}
} else {
// monsters can't id things!
}
} else if (spellid == OT_S_ENDUREELEMENTS) {
flag_t *f;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
f = addtempflag(caster->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
f = addtempflag(caster->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_ENTANGLE) {
char targname[BUFLEN];
flag_t *f;
object_t *o;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_TRUE;
}
getlfname(target, targname);
if (cansee(player, target)) {
msg("Magical vines grasp %s!",targname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// create vine
o = addob(targcell->obpile, "entangling vine");
// set power
f = hasflag(o->flags, F_RESTRICTMOVEMENT);
if (f) {
f->val[0] = 30 + (power/2);
f->val[1] = B_FALSE; // struggling doesn't damage the vine
}
/// remmeber creator. if they don't have los to us, spell
// is broken and vines will vanish.
setobcreatedby(o, caster);
} else if (spellid == OT_S_FLIGHT) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_FLYING, B_TRUE, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_FREEZEOB) {
object_t *o;
if (targob) {
o = targob;
} else {
// rings?
o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_RIGHTFINGER, NA, NA, NULL);
if (!o) o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_LEFTFINGER, NA, NA, NULL);
// gloves?
if (!o) o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_HANDS, NA, NA, NULL);
// weapon?
if (!o) o = hasobwithflagval(caster->pack, F_EQUIPPED, BP_WEAPON, NA, NA, NULL);
if (o) {
// at high power, warn...
if (isplayer(caster) && (power > 5)) {
char obname[BUFLEN];
char ch;
getobname(o, obname, o->amt);
msg("%s %s start%s glowing bright blue!",
(o->pile->owner == caster) ? "Your" : "",
(o->pile->owner == caster) ? noprefix(obname) : obname,
(o->amt == 1) ? "s" : "");
more();
ch = askchar("Abort your spell?", "yn","y", B_TRUE, B_FALSE);
if (ch == 'y') {
msg("You release your spell into the air.");
return B_FALSE;
}
}
} else {
// next thing touched
addflag(caster->flags, F_FREEZINGTOUCH, 1, power, 10+power, NULL);
if (isplayer(caster)) {
msg("Your hands begin to glow blue!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
getlfname(caster, buf);
msg("%s's hands begin to glow blue!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
}
if (!o) {
if (isplayer(caster)) {
msg("Your hands feel freezing cold for a moment.");
}
fizzle(caster);
return B_TRUE;
}
// object will turn to ice immediately...
if (o->material->id == MT_ICE) { // already made of ice?
cell_t *loc;
flag_t *f;
loc = getoblocation(o);
// announce
if (haslos(caster, loc)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("%s %s freeze%s some more.",
(o->pile->owner == caster) ? "Your" : "",
(o->pile->owner == caster) ? noprefix(obname) : obname,
(o->amt == 1) ? "s" : "");
}
// restore it
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] = f->val[1];
}
return B_TRUE;
} else {
int rv;
getobname(o, buf, o->amt);
rv = changemat(o, MT_ICE);
if (rv) {
if (rv == E_NOEFFECT) {
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s glows blue for a moment.",noprefix(buf));
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, o->pile->owner->cell)) {
char buf2[BUFLEN];
getlfname(o->pile->owner, buf2);
msg("%s%s %s glows blue for a moment.", noprefix(buf2),getpossessive(buf2), buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (haslos(player, o->pile->where)) {
msg("%s glows blue for a moment.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
if (isplayer(caster)) {
nothinghappens();
}
}
return B_TRUE;
} else {
if (o->pile->owner) {
if (isplayer(o->pile->owner)) {
msg("Your %s turn%s to ice!",noprefix(buf), (o->amt == 1) ? "s" : "");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, o->pile->owner->cell)) {
getlfname(o->pile->owner, buf);
msg("A small cloud of vapour rises up from %s.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (haslos(player, o->pile->where)) {
msg("%s turns to ice!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
}
}
} else if (spellid == OT_S_FROSTBITE) {
char lfname[BUFLEN];
int exposedlimbs,dam;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, lfname);
// how many body parts are impacted?
exposedlimbs = getexposedlimbs(target);
dam = rolldie(exposedlimbs, 3);
if (isplayer(target)) {
if (isimmuneto(target->flags, DT_COLD)) {
msg("You feel mildly chilly.");
} else {
msg("You feel extremely cold!");
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
if (isimmuneto(target->flags, DT_COLD)) {
msg("%s looks mildly chilly.", lfname);
} else {
msg("%s looks extremely cold!", lfname);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// target takes magical damage
// always hit
if (!isimmuneto(target->flags, DT_COLD)) {
losehp(target, dam, DT_COLD, caster, "a 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 {
if (isplayer(caster) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// remember the original race
addflag(target->flags, F_ORIGRACE, target->race->id, NA, NA, NULL);
if (target->race->id != R_VAMPIRE) {
// polymorph is temporary
addtempflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, 10);
}
setrace(target, R_GASCLOUD, B_TRUE);
}
} else if (spellid == OT_S_GLACIATE) {
object_t *o,*nexto;
int donesomething = B_FALSE;
if (haslos(player, targcell)) {
msg("A puff of vapour appears.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
takedamage(o, 1, DT_COLD);
donesomething = B_TRUE;
}
} else if (spellid == OT_S_GREASE) {
int radius;
if (targcell->type->solid) {
fizzle(caster);
return B_FALSE;
}
radius = power/2;
if (radius < 1) radius = 1;
addobburst(targcell, radius, DT_ORTH, "puddle of oil", caster, LOF_WALLSTOP);
if (haslos(player, targcell)) {
char underbuf[BUFLEN];
if (targcell->lf && cansee(player, targcell->lf)) {
char lfname[BUFLEN];
getlfname(targcell->lf, lfname);
snprintf(underbuf, BUFLEN, " under %s",lfname);
} else {
strcpy(underbuf, "");
}
msg("A %spool of oil appears%s!", (radius == 1) ? "" : "huge ", underbuf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_HAILSTORM) {
int failed = B_FALSE;
if (isoutdoors(caster->cell->map)) {
power += 3;
limit(&power, NA, 10);
}
// ask for a target cell
if (targcell) {
if (!targcell->type->solid) {
object_t *o;
int x,y;
int radius;
char *dambuf;
// centre storm here...
if (haslos(player, targcell)) {
msg("An intense storm of hail appears!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
radius = power / 2;
if (radius < 1) radius = 1;
addobsinradius(targcell, radius, DT_ORTH, "hail storm", B_FALSE);
// replace damage per sec with power d4
asprintf(&dambuf, "%dd4", power);
for (y = targcell->y - radius; y <= targcell->y + radius; y++) {
for (x = targcell->x - radius; x <= targcell->x + radius; x++) {
cell_t *c;
c = getcellat(targcell->map, x, y);
if (c && (getcelldist(targcell, c) <= radius)) {
for (o = c->obpile->first ; o ; o = o->next) {
if (o->type->id == OT_HAILSTORM) {
flag_t *f;
f = hasflag(o->flags, F_WALKDAM);
if (f) {
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);
}
} else if (spellid == OT_S_HASTE) {
int howlong = 15;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
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;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
// cure certain bad effects
if (killflagsofid(target->flags, F_PAIN)) {
donesomething = B_TRUE;
}
if (killflagsofid(target->flags, F_POISONED)) {
donesomething = B_TRUE;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// severed body parts
getflags(target->flags, retflag, &nretflags, F_INJURY, F_NOBODYPART, F_NONE);
for (i = 0; i < nretflags; i++) {
if ((retflag[i]->id == F_NOBODYPART) && (retflag[i]->val[1] == B_FROMINJURY)) {
if (isplayer(target)) {
msg("Your %s grows back!", getbodypartname(target, retflag[0]->val[0]));
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
char targname[BUFLEN];
getlfname(target, targname);
msg("%s%s %s grows back!", targname, getpossessive(targname),
getbodypartname(target, retflag[0]->val[0]));
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 {
if (target->hp < target->maxhp) {
int min,max;
switch (spellid) {
case OT_S_HEALINGMIN:
default:
min = 1; max = 10;
break;
case OT_S_HEALING:
min = 10; max = 20;
break;
case OT_S_HEALINGMAJ:
min = 20; max = 30;
break;
}
gainhp(target, getspellduration(min,max,blessed) + (power*2));
if (isplayer(target)) {
if (target->hp >= target->maxhp) {
switch (spellid) {
case OT_S_HEALINGMIN:
msg("All of your scrapes and bruises are healed!");
break;
case OT_S_HEALING:
default:
msg("Your wounds close themselves!");
break;
case OT_S_HEALINGMAJ:
msg("Your injuries are healed!");
break;
}
} else {
switch (spellid) {
case OT_S_HEALINGMIN:
msg("Some of your scrapes and bruises are healed!");
break;
case OT_S_HEALING:
default:
msg("Some of your wounds close themselves!");
break;
case OT_S_HEALINGMAJ:
msg("Your injuries are partially healed!");
break;
}
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s looks healthier!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
donesomething = B_TRUE;
}
}
if (isplayer(target)) {
if (!donesomething) {
nothinghappens();
}
}
if (donesomething && isplayer(caster)) {
if (!frompot) {
pleasegodmaybe(R_GODMERCY, 3);
}
angergodmaybe(R_GODDEATH, 20);
}
// hostile monsters might calm down
if (!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, 3, target, SC_MORALE, 0)) {
makepeaceful(target);
}
}
}
} else if (spellid == OT_S_HOLDPORTAL) {
object_t *o;
o = hasobwithflag(targcell->obpile, F_DOOR);
if (!o) {
fizzle(caster);
return B_FALSE;
}
closedoor(NULL, o);
addflag(o->flags, F_JAMMED, 100, NA, NA, NULL);
if (haslos(player, targcell)) {
char buf[BUFLEN];
getobname(o, buf, 1);
msg("%s glows for a moment.",buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_HUNGER) {
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
if (isplayer(target)) {
msg("A sudden hunger comes over you!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("%s looks more hungry.", 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_ICEEDGE) {
object_t *wep;
enum DAMTYPE dt;
char obname[BUFLEN];
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
// does caster have a bladed weapon?
wep = getweapon(target);
if (!wep) {
fizzle(caster);
return B_FALSE;
}
dt = getdamtype(wep);
if ((dt != DT_PIERCE) && (dt != DT_SLASH) && (dt != DT_CHOP)) {
fizzle(caster);
return B_FALSE;
}
getobname(wep, obname, wep->amt);
if (isplayer(target)) {
msg("Your %s%s blade is covered with ice!", obname, getpossessive(obname));
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("%s%s %s is covered with ice!", lfname, getpossessive(lfname), lfname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
addtempflag(wep->flags, F_EXTRADAM, DT_COLD, NA, NA, "1d6", 10 + power*3);
} else if (spellid == OT_S_ICICLE) {
object_t *o;
int donesomething = B_FALSE;
// create icicle
o = addob(targcell->obpile, "huge icicle");
if (o) {
flag_t *f;
if (haslos(player, targcell)) {
drawscreen();
msg("A massive icicle rises from the ground!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] = 3+power;
f->val[1] = 3+power;
}
addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL);
donesomething = B_TRUE;
}
// knock lfs away
if (targcell->lf) {
knockback(targcell->lf, getdiraway(targcell, targcell, NULL, B_FALSE, DT_COMPASS, B_FALSE), 1, NULL, 25+power, B_TRUE);
donesomething = B_TRUE;
}
if (!donesomething) {
fizzle(caster);
return B_FALSE;
}
} else if (spellid == OT_S_IDENTIFY) {
object_t *o;
if (targob) {
o = targob;
} else {
// ask for an object
o = askobject(caster->pack, "Identify which object", NULL, AO_NOTIDENTIFIED);
}
if (!o) {
fizzle(caster);
return B_TRUE;
}
if (isplayer(caster)) {
if (isidentified(o)) { // already identified?
nothinghappens();
return B_TRUE;
} else {
int charges;
identify(o);
getobname(o, buf, o->amt);
// charges
charges = getcharges(o);
if (charges == -1) {
msgnocap("%c - %s.",o->letter, buf);
} else {
msgnocap("%c - %s (%d charges left).",o->letter, buf, charges);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
// monsters can't id things!
}
} else if (spellid == OT_S_INFINITEDEATH) {
lifeform_t *l;
if (isplayer(caster)) {
msg("A wave of incalculable evil blasts outwards from you!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
getlfname(caster, buf);
msg("A wave of incalculable evil blasts outwards from %s!", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (player->cell->map == caster->cell->map) { // should always be true
msg("Oh no! A wave of incalculable evil washes over you!");
}
for (l = caster->cell->map->lf ; l ; l = l->next) {
if (l != caster) {
if (isimmuneto(l->flags, DT_NECROTIC) ||
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, B_FALSE);
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) || 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, B_FALSE);
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) {
getlfname(target, lfname);
// target takes magical damage
// always hit
if (cansee(player, target)) {
if (power == 1) {
msg("A spike of mana hits %s.",lfname);
} else {
msg("%s spikes of mana hit %s.",numbuf, lfname);
}
}
losehp(target, rolldie(power,2), DT_MAGIC, caster, "a mana spike");
}
} else if (spellid == OT_S_MAGSHIELD) {
object_t *o,*nexto;
flag_t *f;
// wearing metal?
for (o = caster->pack->first ; o ; o = nexto) {
nexto = o->next;
if (ismetal(o->material->id)) {
f = isequipped(o);
if (f) {
if ((f->val[0] == BP_WEAPON) || (f->val[0] == BP_SECWEAPON)) {
} else if ((f->val[0] == BP_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_FALSE;
}
}
}
}
// add the magnetic field!
addtempflag(caster->flags, F_MAGSHIELD, B_TRUE, NA, NA, NULL, 25 + (power*2));
} else if (spellid == OT_S_MAPPING) {
int x,y;
int range;
map_t *m;
m = caster->cell->map;
if (power == 10) {
range = UNLIMITED;
} else {
range = power * 6;
}
// reveal map
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
if (c) {
if (range == UNLIMITED) {
setcellknown(c, MAXOF(PR_ADEPT, getskill(caster, SK_CARTOGRAPHY)));
} else if (getcelldist(caster->cell, c) <= range) {
setcellknown(c, MAXOF(PR_ADEPT, getskill(caster, SK_CARTOGRAPHY)));
}
}
}
}
if (isplayer(caster)) {
msg("An image of your surroundings appears in your mind!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
needredraw = B_TRUE;
}
rv = B_FALSE;
} else if (spellid == OT_S_METALHEAL) {
int i;
float totalmass = 0;
object_t *o;
int howmuch;
// works on all metal in sight
if (isplayer(caster)) {
if (seenbyplayer) *seenbyplayer = B_FALSE;
msg("You draw health from nearby metal!");
} else if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_FALSE;
msg("%s draws health from nearby metal!",castername);
}
totalmass = 0;
// destroy WORN metal objects by the caster (not CARRIED ones)
for (o = caster->pack->first ; o ; o = o->next) {
if (isequipped(o) && ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(caster->pack);
for (i = 0; i < caster->nlos; i++) {
targcell = caster->los[i];
for (o = targcell->obpile->first ; o ; o = o->next) {
// destroy metal items on the ground
if (ismetal(o->material->id)) {
takedamage(o, 9999, DT_DIRECT);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(targcell->obpile);
if (targcell->lf && !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);
if (hasflag(o->flags, F_DEAD)) {
totalmass += getobweight(o);
}
}
}
// destroy objects right away
removedeadobs(targcell->lf->pack);
}
}
if (totalmass > 0) {
// heal 2 hp per kilo
howmuch = floor(totalmass) * 2;
howmuch *= (power/3); // modify for power
gainhp(caster, howmuch);
} else {
fizzle(caster);
}
} else if (spellid == OT_S_MINDSCAN) {
int failed = B_FALSE;
if (isplayer(caster)) {
if (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);
}
} else if (spellid == OT_S_ACCELMETAL) {
if (!targob) {
// ask for an object
targob = askobject(caster->pack, "Fire which object", NULL, AO_NONE);
}
if (!targob || !ismetal(targob->material->id) ) {
fizzle(caster);
return B_TRUE;
}
if (isequipped(targob) && iscursed(targob)) {
char obname[BUFLEN];
getobname(targob, obname, 1);
msg("Your %s appears to be stuck to you!", noprefix(obname));
targob->blessknown = B_TRUE;
return B_TRUE;
}
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE;
// 5 is the same as AT_VHIGH strength
// 10 = gun speed
fireat(caster, targob, 1, targcell, 8 + (power / 2) , NULL);
} 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) {
regiontype_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;
}
m = findregionmap(RG_HEAVEN, 1);
}
if (m) {
int ntries = 0;
// find random free cell in map
targcell = getrandomcell(m);
while (!cellwalkable(target, targcell, NULL)) {
targcell = getrandomcell(m);
ntries++;
if (ntries >= 5) {
fizzle(caster);
return B_TRUE;
}
}
teleportto(target, targcell, B_TRUE);
} else {
fizzle(caster);
return B_TRUE;
}
} 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 (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
saved = B_TRUE;
} else if (skillcheck(target, SC_STR, 20 + power, 0)) {
saved = B_TRUE;
}
if (saved) {
if (isplayer(target)) {
msg("You stiffen momentarily.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
getlfname(target, buf);
msg("%s stiffens momentarily.", buf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
howlong = power*2;
addtempflag(target->flags, F_PARALYZED, B_TRUE, NA, NA, NULL, howlong);
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_PAIN) {
int failed = B_FALSE;
target = targcell->lf;
if (lfhasflag(target, F_PAIN)) {
fizzle(caster);
return B_FALSE;
}
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
failed = B_TRUE;
} else if (skillcheck(target, SC_CON, 20 + power, 0)) {
failed = B_TRUE;
}
if (target) {
if (failed) {
if (isplayer(target)) {
msg("Your skin hurts for a moment.");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (haslos(player, target->cell)) {
char targetname[BUFLEN];
getlfname(target, targetname);
msg("%s winces in pain, then recovers.", targetname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
} else {
int howlong = 7;
howlong = getspellduration(3,5,blessed) + (power/4);
addtempflag(target->flags, F_PAIN, DT_MAGIC, NA, NA, "1d3", howlong);
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_PETRIFY) {
target = targcell->lf;
// some thigns can't be stoned
if (!lfcanbestoned(target)) {
fizzle(caster);
return B_FALSE;
} else if (lfhasflag(target, F_BEINGSTONED)) {
fizzle(caster);
return B_FALSE;
}
// savingthrow
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE) || skillcheck(target, SC_CON, 30 + power, 0)) {
if (haslos(player, target->cell)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("%s glow%s grey momentarily.", lfname, isplayer(target) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_TRUE;
}
// successful
if (haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
addflag(target->flags, F_BEINGSTONED, 2, NA, NA, NULL);
} else if (spellid == OT_S_POISONBOLT) {
char lfname[BUFLEN];
// animation
anim(caster->cell, targcell, '}', C_GREEN);
target = haslf(targcell);
if (target) {
getlfname(target, lfname);
// target gets saving throw to avoid...
if (skillcheck(target, SC_DODGE, 20 + (power*2), 0)) {
// miss
if (cansee(player, target)) {
msg("A glob of venom misses %s.",lfname);
}
} else {
// hit
if (cansee(player, target)) {
msg("A glob of venom hits %s.",lfname);
}
if (!isimmuneto(target->flags, DT_POISON)) {
poison(target, power*3, P_VENOM, (power/4)+1, "a glob of venom");
}
}
} else {
damageallobs(NULL, targcell->obpile, 0, DT_FIRE);
}
} else if (spellid == OT_S_POSSESSION) {
char targname[BUFLEN];
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, targname);
// warn if it can't do magic
if (lfhasflag(target, F_NOSPELLS)) {
int cancel = B_FALSE;
if (isplayer(caster)) {
char buf[BUFLEN];
char ch;
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;
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_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_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)) {
flag_t *f;
f = isequipped(o);
if (f && (f->val[0] != BP_WEAPON) && (f->val[0] != BP_SECWEAPON)) {
gotmetal = B_TRUE;
metalweight += getobweight(o);
break;
}
}
}
if (gotmetal) {
donesomething = B_TRUE;
// include the target lf's weight since we will be pulling on them
target = targcell->lf;
metalweight += getlfweight(target, B_WITHOBS);
if (getobpileweight(caster->pack) + metalweight <= getmaxcarryweight(caster)) {
// they get pulled towards caster
failed = pullnextto(target, caster->cell);
} else {
// caster gets pulled towards them
failed = pullnextto(caster, target->cell);
}
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (failed) {
if (isplayer(target) || haslos(player, target->cell)) {
char buf[BUFLEN];
getlfname(target, buf);
msg("%s %s pulled forward slightly.", buf, is(target));
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
}
if (!donesomething) {
fizzle(caster);
}
} else if (targcell->obpile->first) { // no lifeform there
targob = NULL;
// select object from cell...
targob = askobject(targcell->obpile, "Target which object", NULL, AO_NONE);
if (targob) {
if (ismetal(targob->material->id)) {
if (isplayer(caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (getobpileweight(caster->pack) + getobweight(targob) <= getmaxcarryweight(caster)) {
// move it to the player
pullobto(targob, caster);
} else {
// move player to it
pullnextto(caster, targcell);
}
} else {
fizzle(caster);
}
} else {
fizzle(caster);
}
}
} else if (spellid == OT_S_GATE) {
int newdepth;
int min,max;
char query[BUFLEN];
// calculate range
min = caster->cell->map->depth - (power*2);
max = caster->cell->map->depth + (power*2);
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);
} else {
map_t *newmap;
cell_t *newcell,*srccell;
object_t *srcportal,*dstportal;
// find adjacent cell for portal
srccell = getrandomadjcell(caster->cell, WE_WALKABLE, B_ALLOWEXPAND);
if (!srccell) {
fizzle(caster);
return B_FALSE;
}
// create the source portal
srcportal = addob(srccell->obpile, "magic portal");
setobcreatedby(srcportal, caster);
// announce, because we might have a delay creating the level...
if (isplayer(caster)) {
msg("There is a blinding flash of light...");
wrefresh(msgwin);
}
// find new map !
//newmap = findmapofdepth(newdepth);
newmap = findregionmap(caster->cell->map->region->id, newdepth);
if (!newmap) {
// create new map
newmap = addmap();
createmap(newmap, newdepth, caster->cell->map->region, NULL, D_NONE, NULL);
}
// find a random cell there
newcell = getrandomcell(newmap);
while (!cellwalkable(caster, newcell, NULL) || hasenterableobject(newcell)) {
newcell = getrandomcell(newmap);
}
// add the dst portal
dstportal = addob(newcell->obpile, "magic portal");
setobcreatedby(dstportal, caster);
// link the dst portal
addflag_real(dstportal->flags, F_MAPLINK, caster->cell->map->id, srccell->x, srccell->y, NULL, PERMENANT, B_FALSE, -1);
// link the source portal
addflag_real(srcportal->flags, F_MAPLINK, newmap->id, newcell->x, newcell->y, NULL, PERMENANT, B_FALSE, -1);
// make both gates temporary
addflag(srcportal->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(srcportal->flags, F_OBHP, 6, 6, NA, NULL);
addflag(srcportal->flags, F_OBHPDRAIN, 1, DT_DIRECT, NA, NULL);
addflag(srcportal->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(srcportal->flags, F_OBDIETEXT, B_TRUE, NA, NA, "vanishes");
addflag(dstportal->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(dstportal->flags, F_OBHP, 6, 6, NA, NULL);
addflag(dstportal->flags, F_OBHPDRAIN, 1, DT_DIRECT, NA, NULL);
addflag(dstportal->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL);
addflag(dstportal->flags, F_OBDIETEXT, B_TRUE, NA, NA, "vanishes");
if (haslos(player, srccell)) {
char obname[BUFLEN];
getobname(srcportal, obname, 1);
msg("%s appears!",obname);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_INSCRIBE) {
char buf[BUFLEN];
if (isplayer(caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
// ask what to inscribe
askstring("What will you inscribe here", '?', buf, BUFLEN, NULL);
if (strlen(buf) > 0) {
char *p;
if (isblind(caster)) {
// if blind, garble text somewhat
for (p = buf ; *p; p++) {
if (*p != ' ') {
if (onein(4)) {
if (onein(2)) {
*p = rnd('a','z');
} else {
*p = rnd('A','Z');
}
}
}
}
msg("You feel something stirring in the air around you.");
} else {
msg("Magical writing appears before you!");
}
if (caster->cell->writing) {
free(caster->cell->writing);
}
caster->cell->writing = strdup(buf);
} else {
fizzle(caster);
}
} else {
// monsters can't cast
}
} else if (spellid == OT_S_INVISIBILITY) {
int howlong = 30;
int willannounce = B_FALSE;
char targname[BUFLEN];
// special case for ghosts...
if (caster->race->id == R_GHOST) {
targcell = caster->cell;
} else {
if (!validatespellcell(caster, &targcell,TT_PLAYER, spellid, power, frompot)) return B_TRUE;
}
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
} else if ( lfhasflag(target, F_INVISIBLE) ) {
fizzle(caster);
return B_FALSE;
}
getlfname(target, targname);
// 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;
}
} else if (spellid == OT_S_JOLT) {
target = haslf(targcell);
if (target) {
int dam;
// hit
if (isplayer(target)) {
msg("A pulse of electricity shocks you!");
} else if (cansee(player, target)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("A pulse of electricity shocks %s!",lfname);
}
dam = power*2;
losehp(target, dam, DT_ELECTRIC, caster, "a jolt of electricity");
} else {
fizzle(caster);
}
} 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_TRUE);
} else {
int donesomething = B_FALSE;
object_t *nexto;
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (hasflag(o->flags, F_IMPASSABLE)) {
int gotit = B_FALSE;
int dooropen;
if (isdoor(o, &dooropen)) {
if (!dooropen) {
gotit = B_TRUE;
}
} else {
gotit = B_TRUE;
}
if (gotit) {
if (haslos(player, targcell)) {
getobname(o, buf, o->amt);
msg("%s %s!",isplayer(caster) ? "You blast" : "Something blasts", buf);
}
addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL);
if (seenbyplayer) *seenbyplayer = B_TRUE;
donesomething = B_TRUE;
}
}
}
if (donesomething) {
if (isplayer(caster)) pleasegodmaybe(R_GODTHIEVES, 5);
} else {
fizzle(caster);
}
}
} else if (spellid == OT_S_LEVITATION) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_LEVITATING, B_TRUE, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
if (cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_LIGHT) {
lifeform_t *l;
// at power 3, you can control where the light appears
// at power 8, the light is permenant
if (power >= 3) {
// TODO: this actually means we can cast it through walls!!!
if (!validatespellcell(caster, &targcell,TT_NONE, spellid, power, frompot)) return B_TRUE;
} else {
targcell = caster->cell;
}
if (haslos(player, targcell)) {
msg("The area is lit by a magical light!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (blessed || (power >= 8)) {
// permenant light
makelitradius(targcell, power*2, L_PERMLIGHT, -1);
} else {
// temporary light
makelitradius(targcell, power*2, L_PERMLIGHT, rnd(5,10)+(power*2) );
}
// blind anyone with nightvis who sees it
// (player last)
for (l = caster->cell->map->lf ; l ; l = l->next) {
if (!isplayer(l) && haslos(l, caster->cell)) {
if (lfhasflag(l, F_SEEINDARK)) {
// if you don't have eyes, your'e safe!
if (!lfhasflagval(l, F_NOBODYPART, BP_EYES, NA, NA, NULL)) {
// blind for 1-3 turns
addtempflag(l->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(1,3));
}
}
// undead will flee from light
if (isundead(l)) {
// runs away from caster
addtempflag(l->flags, F_FLEEFROM, caster->id, NA, NA, NULL, 20);
}
}
}
if (isplayer(caster) || cansee(player, caster)) {
if (lfhasflag(player, F_SEEINDARK)) {
msg("The light burns your vision-enhanced eyes!");
// blind for 5-10 turns
addtempflag(player->flags, F_BLIND, B_TRUE, NA, NA, NULL, rnd(5,10));
}
}
// anyone there glows if the spell was controlled
if (targcell->lf && (power >= 3)) {
if (power >= 8) {
// permenant!
addflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL);
} else {
addtempflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, rnd(10,20)+(power*2) );
}
}
calclight(targcell->map);
needredraw = B_TRUE;
drawscreen();
} else if (spellid == OT_S_LIGHTNINGBOLT) {
cell_t *retcell[MAXRETCELLS];
int nretcells;
int i;
int nhits = power;
// create a line of fire towards the target cell
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcells);
animcells(caster->cell, &retcell[1], nretcells-1, B_FALSE, '/', '\\', C_WHITE);
if (cansee(player, caster)) {
msg("%s shoot%s a bolt of electricity!",castername, isplayer(caster) ? "" : "s");
}
// don't hit the caster cell on fire!
for (i = 1; (i < nretcells) && (nhits > 0); i++) {
cell_t *c;
c = retcell[i];
if (c->lf) {
// hit with lightning
losehp(c->lf, roll("2d6"), DT_ELECTRIC, caster, "an electricity bolt");
nhits--;
}
if (haslos(player, c)) {
needredraw = B_TRUE;
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
}
} else if (spellid == OT_S_LIGHTNINGSTORM) {
char targname[BUFLEN];
lifeform_t *poss[MAXCANDIDATES], *targ[MAXCANDIDATES];
int nposs = 0,ntarg = 0;
int seen = B_FALSE;
int i;
for (i = 0; i < caster->nlos; i++) {
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++) {
if (cansee(player, targ[i])) {
getlfname(targ[i],targname);
msg("%s %s struck by a bolt of lightning!",targname, is(targ[i]));
}
if (isoutdoors(targ[i]->cell->map)) {
losehp(targ[i], rolldie(4,6), DT_ELECTRIC, caster, "a bolt of lightning");
} else {
losehp(targ[i], rolldie(3,6), DT_ELECTRIC, caster, "a bolt of lightning");
}
}
if (seen) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_LOCATEOBJECT) {
char wantname[BUFLEN];
char buf[BUFLEN];
map_t *m;
object_t *o;
if (!isplayer(caster)) {
fizzle(caster);
return B_FALSE;
}
// ask for object
askstring("Locate what kind of object", '?', wantname, BUFLEN, NULL);
// find it in all maps!
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 = 'b';
// on the ground?
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
if (c) {
for (o = c->obpile->first ; o ; o = o->next) {
char obname[BUFLEN];
real_getobname(o, obname, o->amt, B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE);
if (strcasestr(obname, wantname)) {
addchoice(&prompt, ch++, obname, NULL, o, NULL);
}
}
}
}
}
// carried by someone?
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
if (c && c->lf) {
for (o = c->lf->pack->first ; o ; o = o->next) {
char obname[BUFLEN];
real_getobname(o, obname, o->amt, B_TRUE, B_TRUE, B_FALSE, B_FALSE, B_FALSE);
if (strcasestr(obname, wantname)) {
char lfname[BUFLEN];
real_getlfnamea(c->lf, lfname, B_FALSE);
strcat(obname, " (held by ");
strcat(obname, lfname);
strcat(obname, ")");
addchoice(&prompt, ch++, obname, NULL, o, NULL);
}
}
}
}
}
}
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_FALSE;
}
getchoice(&prompt);
sk = (skill_t *)prompt.result;
if (sk) {
flag_t *f;
f = addtempflag(caster->flags, F_HASSKILL, sk->id, (power/2)+1, NA, NULL, FROMSPELL);
f->obfrom = spellid;
msg("New knowledge floods into your mind!");
} else {
fizzle(caster);
return B_FALSE;
}
} else if (spellid == OT_S_GRAVBOOST) {
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);
}
} 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, B_TRUE, 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 (isoutdoors(targcell->map)) {
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 (getobunitweight(o) <= 5) {
poss[nposs++] = o;
}
}
for (i = 0; ((i < power) && (nposs > 0)); i++) {
int sel,n;
sel = rnd(0,nposs-1);
blowob[nblowobs++] = poss[sel];
// remove ob from possibility list
for (n = sel; n < (nposs-1); n++) {
poss[sel] = poss[sel+1];
}
nposs--;
}
if (target && cansee(player, target)) {
msg("A gust of wind whips up around %s!", targname);
} else if (haslos(player, targcell)) {
msg("A gust of wind whips up from nowhere!");
}
// chance of blowing each ob away
for (i = 0; i < nblowobs; i++) {
char obname[BUFLEN];
cell_t *c;
getobname(blowob[i], obname, 1);
c = getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND);
if (c && ((rnd(1,100)-power) <= 33)) {
// move it
fireat(NULL, blowob[i], 1, c, 4, NULL);
}
}
// easyish save to avoid falling
if (target && !skillcheck(target, SC_FALL, 12 + (power*2), 0)) {
fall(target, NULL, B_TRUE);
}
needredraw = B_TRUE;
} else if (spellid == OT_S_MIST) {
object_t *o;
lifeform_t *l;
targcell = getcellindir(caster->cell, caster->facing);
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("A thick veil of mist surrounds %s!", lfname);
} else {
msg("A thick veil of mist appears!");
}
}
o = addob(targcell->obpile, "thick mist");
if (o) {
flag_t *f;
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] += power;
f->val[1] += power;
}
}
// hack - make all monsters who can see you stop targetting you.
// otherwise they will just try to go to your last known location.
for (l = caster->cell->map->lf ; l ; l = l->next) {
flag_t *f;
if ((l != caster) && (gethitdice(l) <= gethitdice(caster)) ) {
f = lfhasflagval(l, F_TARGETLF, caster->id, NA, NA, NULL);
if (f) killflag(f);
f = lfhasflagval(l, F_TARGETCELL, caster->cell->x, caster->cell->y, NA, NULL);
if (f) killflag(f);
}
}
} 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 {
// ask for an object
o = askobject(caster->pack, "Mend which object", NULL, AO_DAMAGED);
}
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_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);
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 dulled weapons
if (!iscursed(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 (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_FALSE;
}
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;
howlong = getspellduration(5,10,blessed) + (power/2);
f = addtempflag(target->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL, howlong);
f->obfrom = OT_S_PASSWALL;
breakgrabs(target, B_TRUE, B_TRUE);
} else if (spellid == OT_S_POLYMORPH) {
race_t *r = NULL;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_TRUE;
}
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 ((caster == target) && getforcedspellrace(caster, spellid, buf)) {
r = findracebyname(buf);
} else {
if (lfhasflag(caster, F_CONTROL)) {
if (power < 5) {
power = 5;
}
}
if (isplayer(caster) && (power >= 5)) {
if (isplayer(target)) { // ie. polymorphing yourself
askstring("What will you become", '?', buf, BUFLEN, NULL);
} else {
char buf2[BUFLEN];
char targname[BUFLEN];
getlfname(target, targname);
snprintf(buf2, BUFLEN, "What will you transform %s into", targname);
askstring(buf2, '?', buf, BUFLEN, NULL);
}
r = findracebyname(buf);
// make sure race is valid:
// - can't turn into monsters which aren't randomly generated.
// - can't turn into unique monsters
// - can't turn into undead monsters
if (r && !canpolymorphto(r->id) && !hasjob(caster, J_GOD)) {
if (isplayer(caster)) msg("As you think of a %s, magic energy dampens around you.", r->name);
r = NULL;
}
} else { // random
if (isplayer(target) && lfhasflag(target, F_CONTROL)) {
askstring("What will you become", '?', buf, BUFLEN, NULL);
r = findracebyname(buf);
// make sure race is valid:
if (r && !canpolymorphto(r->id)) r = NULL;
}
if (!r) {
// random race, but not the same!
r = target->race;
while ((r == target->race) || !canpolymorphto(r->id)) {
r = getrandomrace(NULL, NA);
}
}
}
} // end if forcepoly
if (r == target->race) {
fizzle(caster);
return B_TRUE;
}
if (r) {
//int rememberorig = B_FALSE;
getlfname(target, buf);
// if this is the player, remember the original race and job...
if (!hasflag(target->flags, F_ORIGRACE)) {
// remember the original race
addflag(target->flags, F_ORIGRACE, target->race->id, NA, NA, NULL);
}
if (isplayer(target)) {
int howlong;
// polymorph will be temporary...
howlong = rnd(20,50);
addtempflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL, howlong);
} else {
// permenant
addflag(target->flags, F_POLYMORPHED, B_TRUE, NA, NA, NULL);
}
// become the new race!
setrace(target, r->id, B_TRUE);
// if someone cast the spell at themself, they can change back at will.
if (target == caster) {
addflag(target->flags, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL);
}
if (haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
breakgrabs(target, B_TRUE, B_TRUE);
} else {
fizzle(caster);
return B_TRUE;
}
} else if (spellid == OT_S_PURIFYFOOD) {
object_t *o;
if (targob) {
o = targob;
} else {
// ask for an object
o = askobject(caster->pack, "Purify what?", NULL, AO_EDIBLE | AO_DRINKABLE);
}
if (!o) {
fizzle(caster);
return B_TRUE;
}
if (isplayer(caster)) {
char obname[BUFLEN];
flag_t *f;
int donesomething = B_FALSE;
getobname(o, obname, o->amt);
if (o->type->id == OT_POT_POISON) {
msg("Your %s sparkles for a while.", noprefix(obname));
makeknown(o->type->id); // you now know that it was poison.
o->type = findot(OT_POT_WATER);
makeknown(o->type->id); // you now know what water is too.
donesomething = B_TRUE;
} else {
f = hasflag(o->flags, F_OBHP);
if (f) {
f->val[0] = f->val[1];
f = hasflagval(o->flags, F_OBHPDRAIN, NA, DT_DECAY, NA, NULL);
if (f) {
killflag(f);
}
donesomething = B_TRUE;
}
f = hasflag(o->flags, F_TAINTED);
if (f) {
killflag(f);
donesomething = B_TRUE;
}
}
if (donesomething) {
if (isdrinkable(o)) {
msg("Your %s looks more clean now.", noprefix(obname));
} else {
msg("Your %s looks more fresh now.", noprefix(obname));
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
nothinghappens();
return B_TRUE;
}
} else {
// monsters can't purify things!
}
} else if (spellid == OT_S_QUENCH) {
object_t *o,*nexto;
int ndone = 0;
if (!targcell) {
fizzle(caster);
}
if (targcell->lf) {
// all objects.
for (o = targcell->lf->pack->first ; o ; o = nexto) {
nexto = o->next;
if (o->type->material->id == MT_FIRE) {
if (haslos(player, targcell)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("%s vanishes!",obname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
killob(o);
ndone++;
} else {
if (haslos(player, targcell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
killflagsofid(o->flags, F_ONFIRE);
ndone++;
}
}
}
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (o->type->material->id == MT_FIRE) {
if (haslos(player, targcell)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("%s vanishes!",obname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
killob(o);
ndone++;
} else {
if (haslos(player, targcell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
killflagsofid(o->flags, F_ONFIRE);
ndone++;
}
}
if (!ndone) {
fizzle(caster);
}
} else if (spellid == OT_S_LESSENPOISON) {
flag_t *f;
int ndone = 0;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
for (f = target->flags->first ; f ; f = f->next) {
if (f->id == F_POISONED) {
// slightly lower time
if (f->lifetime > 1) {
f->lifetime--;
}
// cut power in half
f->val[1] /= 2; if (f->val[1] < 1) f->val[1] = 1;
if (isplayer(target)) {
msg("Your %s seems less intense.",getpoisonname(f->val[0]));
}
ndone++;
}
}
if (ndone) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
if (ndone && isplayer(caster) && !frompot) {
pleasegodmaybe(R_GODMERCY, 3);
}
} else if (spellid == OT_S_LETHARGY) {
int amttolose;
target = targcell->lf;
if (!target) {
fizzle(caster);
return B_FALSE;
}
amttolose = power*2;
// only announce if the target will have some stamina left.
// if they drop to 0, modstamina will handle the announce.
if (getstamina(target) >= amttolose) {
if (isplayer(target)) {
msg("You suddenly feel very lethargic!");
} else if (cansee(player, target)) {
char targname[BUFLEN];
getlfname(target, targname);
msg("%s looks very lethargic!", targname);
}
}
modstamina(target, -amttolose);
} else if (spellid == OT_S_REPELINSECTS) {
// just announce
if (isplayer(caster)) {
msg("A strange odour surrounds you...");
}
} else if (spellid == OT_S_REVEALHIDDEN) {
int i;
int seen = B_FALSE;
for (i = 0 ; i < caster->nlos; i++ ){
targcell = caster->los[i];
if (targcell) {
object_t *o;
if (isplayer(caster)) {
// reveal secret doors/obs/etc
for (o = targcell->obpile->first ; o ; o = o->next) {
flag_t *f;
f = hasflag(o->flags, F_SECRET);
if (f && (f->val[0] != NA)) {
char obname[BUFLEN];
getobname(o, obname, o->amt);
msg("%s is magically revealed!", obname);
killflag(f);
seen = B_TRUE;
}
}
}
// remove invisibility/hiding
if (targcell->lf) {
flag_t *f, *nextf;
for (f = targcell->lf->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->id == F_INVISIBLE) {
if ((f->lifetime > 0) || (f->lifetime == PERMENANT)) {
killflag(f);
// player can see whoever just appeared, and the caster?
if (cansee(player, targcell->lf) && cansee(player, caster)) {
char tname[BUFLEN];
seen = B_TRUE;
getlfname(targcell->lf, tname);
msg("%s becomes visible!", tname);
}
}
} else if ( (f->id == F_HIDING) &&
!lfhasflagval(caster, F_SPOTTED, targcell->lf->id, NA, NA, NULL)) {
addflag(caster->flags, F_SPOTTED, targcell->lf->id, NA, NA, NULL);
// player can see whoever just appeared, and the caster?
if (cansee(player, targcell->lf) && cansee(player, caster)) {
char tname[BUFLEN];
seen = B_TRUE;
getlfname(targcell->lf, tname);
msg("A hiding %s is revealed!", noprefix(tname));
}
}
}
}
}
}
if (seen) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
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_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) {
// hit with ice
losehp(c->lf, rolldie(nhits, 6), 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];
snprintf(buf, BUFLEN, "%s%s shatter spell", castername, getpossessive(castername));
if (!shattercell(targcell, caster, buf)) {
fizzle(caster);
}
} else if (spellid == OT_S_SIXTHSENSE) {
if (isplayer(caster)) {
msg("You will now be warned of creatures behind you.");
}
} else if (spellid == OT_S_SLEEP) {
int howlong;
target = targcell->lf;
if (lfhasflag(target, F_ASLEEP) || !cansleep(target)) {
fizzle(caster);
return B_TRUE;
}
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("You suddenly feel very lethargic!");
} else if (cansee(player, target)) {
getlfname(target, buf);
msg("%s looks very lethargic!", 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 (isoutdoors(caster->cell->map)) {
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);
} else {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
if (failed) {
fizzle(caster);
}
} 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 (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("The power of %s smites you!", (spellid == OT_S_SMITEEVIL) ? "good" : "evil");
} else if (cansee(player, target)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("The power of %s smites %s!", (spellid == OT_S_SMITEEVIL) ? "good" : "evil", lfname);
}
// use direct damage rather than holy, because otherwise it might be increased
// due to vulnerabilities
losehp(target, rnd(1,power*2), 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);
}
} 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;
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;
} else if (haslos(player, targcell)) {
msg("An enormous ball of fire explodes!");
}
anim(caster->cell, targcell, '^', C_WHITE);
animradialorth(targcell, 1, '}', C_WHITE);
redrawpause();
// add snow as follows (3 = medium, 2 = medium, 1 = smell)
// *
// ***
// *
if (targcell->lf) {
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) {
losehp(c->lf, 1, DT_COLD, caster, "a snowball");
} 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);
}
} else if (spellid == OT_S_SOFTENEARTH) {
int seen = B_FALSE;
int ndone = 0;
int powerleft = power;
// ask for a target cell
if (targcell) {
object_t *o;
enum OBTYPE badoid[2];
int i;
cell_t *c;
// do first cell
// TODO: is it actually earth here, not stone,metal etc?
o = addob(targcell->obpile, "pool of mud");
if (o) {
ndone++;
powerleft--;
if (haslos(player, targcell)) seen = B_TRUE;
// do the rest
badoid[0] = OT_MUDPOOL;
badoid[1] = OT_NONE;
for (i = 0; i < powerleft; i++) {
c = real_getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND, LOF_DONTNEED, badoid);
if (c) {
o = addob(c->obpile, "pool of mud");
if (o) {
ndone++;
if (haslos(player, targcell)) seen = B_TRUE;
// do the rest
}
}
}
}
}
if (ndone) {
if (seen) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
msg("The ground nearby softens into mud.");
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_SPARK) {
object_t *o,*nexto;
int donesomething = B_FALSE;
if (haslos(player, targcell)) {
if (targcell->lf && cansee(player, targcell->lf)) {
char lfname[BUFLEN];
getlfname(targcell->lf, lfname);
msg("A small spark of flame singes %s.", lfname);
} else {
msg("A small spark of flame appears.");
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (targcell->lf) {
losehp(targcell->lf, rnd(1,3), DT_FIRE, caster, "a spark");
} else {
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
// special cases
if (isflammable(o) || (o->type->id == OT_CANDLE) || (o->type->id == OT_TORCH)) {
takedamage(o, 1, DT_FIRE);
donesomething = B_TRUE;
}
}
}
} 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;
getobname(corpse, corpsename, 1);
snprintf(buf, BUFLEN, "What will you ask %s?", corpsename);
initprompt(&prompt, buf);
addchoice(&prompt, 'a', "How did you die?", NULL, NULL, NULL);
addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL);
ch = getchoice(&prompt);
if (ch == 'a') {
flag_t *f;
char *p;
snprintf(buf, BUFLEN, "%s whispers:", corpsename);
msg(buf);
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 (spellid == OT_S_STENCH) {
int howlong;
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 (makenauseated(target, power, howlong)) {
fizzle(caster);
return B_FALSE;
}
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_STICKTOSNAKE) {
int i;
cell_t *poss[MAXCANDIDATES];
int nposs;
race_t *raceposs[MAXCANDIDATES],*r;
int nraceposs = 0;
cell_t *c = NULL,*newcell = NULL;
object_t *o,*nexto;
int ndone = 0;
// power is always at lesat 2 for this spell
if (power < 2) power = 2;
// get list of possible snakes
for (r = firstrace ; r ; r = r->next) {
if (strstr(r->name, " snake")) {
flag_t *f;
f = hasflag(r->flags, F_HITDICE);
if (f) {
int ndice,nsides,bonus,maxroll;
texttodice(f->text, &ndice,&nsides,&bonus);
maxroll = ndice * nsides + bonus;
// ie. 1hd needs power 2
// ie. 2hd needs power 4
// ie. 3hd needs power 6
// ie. 4hd needs power 8
if (power >= (maxroll/4)) {
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...
for (i = 0; i < caster->nlos; i++) {
c = poss[i];
if (c->lf) {
if (cansee(caster, c->lf)) {
for (o = c->lf->pack->first ; o ; o = nexto) {
nexto = o->next;
if ((o->material->id == MT_WOOD) &&
hasflag(o->flags, F_RODSHAPED) &&
isequipped(o) &&
(o->blessed == B_UNCURSED) &&
!hasflag(o->flags, F_HASBRAND)) {
char obname[BUFLEN];
char lfname[BUFLEN];
getobname(o, obname, o->amt);
getlfname(c->lf, lfname);
newcell = getrandomadjcell(c, WE_WALKABLE, B_NOEXPAND);
if (newcell) {
lifeform_t *snake;
// add a snake there.
snake = addlf(newcell, raceposs[rnd(0,nraceposs-1)]->id, 1);
if (snake) {
msg("%s%s %s transforms into a snake!",lfname,
getpossessive(lfname),
noprefix(obname));
killflagsofid(snake->flags, F_XPVAL);
addflag(snake->flags, F_XPVAL, 0, NA, NA, NULL);
makefriendly(snake, PERMENANT);
if (c->lf == caster) {
char fullobname[BUFLEN];
getobnametrue(o, fullobname, 1);
killflagsofid(snake->flags, F_CORPSETYPE);
addflag(snake->flags, F_CORPSETYPE, NA, NA, NA, fullobname);
}
killob(o);
ndone++;
}
} else {
msg("%s%s %s quivers for a moment.",lfname, getpossessive(lfname),
noprefix(obname));
ndone++;
}
}
}
}
} else { // ie. no lf there
for (o = c->obpile->first ; o ; o = nexto) {
nexto = o->next;
if ((o->material->id == MT_WOOD) && hasflag(o->flags, F_RODSHAPED) && !isblessed(o)) {
cell_t *newcell = NULL;
lifeform_t *snake;
char obname[BUFLEN];
getobname(o, obname, o->amt);
// add to current cell or new one?
if (c->lf) { // ie. if a previous object made a snake here
newcell = getrandomadjcell(c, WE_WALKABLE, B_NOEXPAND);
} else {
newcell = c;
}
if (newcell) {
// add a snake there.
snake = addlf(newcell, raceposs[rnd(0,nraceposs-1)]->id, 1);
if (snake) {
msg("%s transforms into a snake!",obname);
killob(o);
killflagsofid(snake->flags, F_XPVAL);
addflag(snake->flags, F_XPVAL, 0, NA, NA, NULL);
makefriendly(snake, PERMENANT);
ndone++;
}
} else {
msg("%s quivers for a moment.",obname);
ndone++;
}
}
}
}
}
if (ndone) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
} else if (spellid == OT_S_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_STUNMASS) {
int i;
int donesomething = B_FALSE;
target = caster;
// stun 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)) {
fizzle(caster);
return B_TRUE;
}
if (spellresisted(thistarg, caster, spellid, power, seenbyplayer, B_TRUE)) {
} else {
donesomething = B_TRUE;
stun(thistarg, (power/5)+2);
if (isplayer(thistarg) || cansee(player, thistarg)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
}
}
}
if (!donesomething) {
fizzle(caster);
}
} else if (spellid == OT_S_SUCK) {
target = targcell->lf;
if (target) {
int failed = B_FALSE;
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) {
failed = B_TRUE;
} else {
// they get pulled towards caster
failed = pullnextto(target, caster->cell);
}
if (isplayer(target) || cansee(player, target)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (failed) {
if (isplayer(target) || cansee(player, target)) {
char buf[BUFLEN];
getlfname(target, buf);
msg("%s %s pulled forward slightly.", buf, is(target));
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
} else {
fizzle(caster);
}
} else if (spellid == OT_S_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 < 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, WE_WALKABLE, B_NOEXPAND);
if (c) break;
}
}
}
if (!c) {
// random
while (!c || c->type->solid || haslf(c)) {
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_FALSE;
} 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;
char dirch;
cell_t *poss[MAX_MAPW * MAX_MAPH];
int nposs;
int x,y;
int xmin,ymin;
int xmax,ymax;
// semicontrolled
// ask for dir
dirch = askchar("Teleport in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE, B_TRUE);
if ((dirch == '.') || (dirch == '-') || !dirch) {
fizzle(caster);
return B_TRUE;
} else {
dir = chartodir(dirch);
}
// make a list of all valid cells in that dir
nposs = 0;
switch (dir) {
case D_N:
case DC_N:
ymin = 0;
ymax = target->cell->y - 1;
xmin = target->cell->x - 5;
xmax = target->cell->x + 5;
break;
case DC_NE:
xmin = target->cell->x + 1;
ymin = 0;
xmax = target->cell->map->w - 1;
ymax = target->cell->y - 1;
break;
case D_E:
case DC_E:
xmin = target->cell->x + 1;
xmax = target->cell->map->w - 1;
ymin = target->cell->y - 5;;
ymax = target->cell->y + 5;;
break;
case DC_SE:
xmin = target->cell->x + 1;
ymin = target->cell->y + 1;
xmax = target->cell->map->w - 1;
ymax = target->cell->map->h - 1;
break;
case D_S:
case DC_S:
ymin = target->cell->y + 1;
ymax = target->cell->map->h - 1;
xmin = target->cell->x - 5;
xmax = target->cell->x + 5;
break;
case DC_SW:
xmin = 0;
ymin = target->cell->y + 1;
xmax = target->cell->x - 1;
ymax = target->cell->map->h - 1;
break;
case D_W:
case DC_W:
xmin = 0;
xmax = target->cell->x - 1;
ymin = target->cell->y - 5;
ymax = target->cell->y + 5;
break;
case DC_NW:
xmin = 0;
ymin = 0;
xmax = target->cell->x - 1;
ymax = target->cell->y - 1;
break;
default: // should never happen
fizzle(caster);
return B_FALSE;
}
if (xmin < 0) xmin = 0;
if (ymin < 0) ymin = 0;
if (xmax > target->cell->map->w-1) xmax = target->cell->map->w-1;
if (ymax > target->cell->map->h-1) ymax = target->cell->map->h-1;
for (y = ymin; y <= ymax ; y++) {
for (x = xmin; x <= xmax ; x++) {
c = getcellat(target->cell->map, x,y);
if (c && cellwalkable(target, c, NULL)) {
poss[nposs] = c;
nposs++;
}
}
}
if (nposs <= 0) {
fizzle(caster);
return B_FALSE;
}
c = poss[rnd(0,nposs-1)];
} // end if (semicontrolled | controlled | random )
targcell = c;
if (c->type->solid) {
// ok, but you'll die!
} else if (!cellwalkable(caster, c, NULL)) {
// go somewhere nearby...
c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND);
if (!c) {
fizzle(caster);
return B_FALSE;
}
}
// we can take up to 'power-1' allies with us.
if (caster == target) {
int dir;
for (dir = DC_N; (dir <= DC_NW) && (nallies < power); dir++) {
c = getcellindir(target->cell, dir);
if (c && c->lf && areallies(c->lf, caster)) {
ally[nallies] = c->lf;
nallies++;
}
}
}
if (isplayer(target) || haslos(player, target->cell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
teleportto(target, targcell, B_TRUE);
// now do allies
if (nallies) {
int i;
for (i = 0; i < nallies; i++) {
c = getrandomadjcell(targcell, WE_WALKABLE, B_ALLOWEXPAND);
if (c) {
teleportto(ally[i], c, B_TRUE);
}
}
}
rv = B_FALSE;
} else if (spellid == OT_S_TELEKINESIS) {
cell_t *where = 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, AO_NONE);
if (!targob) {
failed = B_TRUE;
}
// TODO: check object weight!
} else {
failed = B_TRUE;
}
} else {
failed = B_TRUE;
}
}
if (!failed) {
// is this a door?
if (hasflag(targob->flags, F_DOOR)) {
if (hasflag(targob->flags, F_OPEN)) {
closedoorat(caster, where);
} else {
opendoorat(caster, where);
}
return B_FALSE; // don't do rest of telekenesis code
}
// if no target cell, ask where to throw object
if (!targcell) {
char obname[BUFLEN],buf2[BUFLEN];
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);
targcell = askcoords(buf, buf2,TT_MONSTER | TT_PLAYER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE);
}
// not liftable?
if (hasflag(targob->flags, F_NOPICKUP)) {
if (isplayer(caster)) {
nothinghappens();
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
// too heavy? max weight is now based on our race's weight and intelligence
maxweight = getlfweight(caster, B_NOOBS) +
(getlfweight(caster, B_NOOBS) * (getstatmod(caster, A_IQ) / 100));
// modify by power
maxweight += (10*power);
if (getobweight(targob) > maxweight) {
cell_t *obloc;
char obname[BUFLEN];
obloc = getoblocation(targob);
getobname(targob, obname, targob->amt);
if (haslos(player, obloc)) {
msg("%s lifts slightly, then drops again.",obname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_FALSE;
}
if (targcell && haslos(caster, targcell)) {
// if it is on us, then take theobject
if (targcell == caster->cell) {
pullobto(targob, caster);
} else {
// otherwise throw it there - but speed is based on
// caster's INTELLIGENCE, not strength like normal.
fireat(caster, targob, targob->amt, targcell, power, NULL);
// note that we use fireat() rather than throwat() to avoid
// calling taketime() twice.
}
} else {
failed = B_TRUE;
}
}
if (failed) {
fizzle(caster);
}
} else if (spellid == OT_S_THORNS) {
flag_t *f;
if (!target) target = caster;
f = addtempflag(target->flags, F_RETALIATE, 1, 4, DT_PIERCE, "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_FALSE;
}
f = addtempflag(caster->flags, F_TRUESTRIKE, power, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
f = addtempflag(caster->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_TURNUNDEAD) {
int i;
lifeform_t *lf;
if (!target) {
target = caster;
}
// works on all undead in _target's_ sight
for (i = 0; i < caster->nlos; i++) {
targcell = caster->los[i];
lf = targcell->lf;
if (lf && (lf != caster) && isundead(lf)) {
int howlong = 10;
int worked = B_TRUE;
// TODO: does it work? depends on caster level & intelligence & power
worked = B_TRUE;
// TODO: how long for? depends on caster level & intelligence & power
howlong = rnd(10,20);
if (worked) {
// don't use scare() since it will fail due to them being undead.
addtempflag(lf->flags, F_FLEEFROM, target->id, NA, NA, NULL,howlong);
}
}
}
} 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!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, caster)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
swapplaces(caster, target, B_CHANGEDIR, B_ONPURPOSE);
} else if ((spellid == OT_S_SUMMONANIMALSSM) ||
(spellid == OT_S_SUMMONANIMALSMD) ||
(spellid == OT_S_SUMMONANIMALSLG) ||
(spellid == OT_S_SUMMONDEMON)) {
int lifetime, nwant,ngot,successrate;
enum LFSIZE wantsize;
enum RACECLASS wantrc;
int friendly;
lifetime = (power * 9) + rnd(1,power*2);
switch (spellid) {
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(4)) {
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:
wantrc = RC_ANIMAL;
wantsize = SZ_MEDIUM;
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;
default:
wantsize = SZ_ANY;
successrate = 100;
break;
}
if (!pctchance(successrate)) {
fizzle(caster);
return B_TRUE;
}
ngot = summonlfs(caster, caster->cell, wantrc, wantsize, AL_NONE, nwant, lifetime, friendly);
if (!ngot) {
fizzle(caster);
return B_TRUE;
} else {
if (haslos(player, caster->cell)) { // intentionally not using cansee
raceclass_t *rc;
rc = findraceclass(wantrc);
if (seenbyplayer) *seenbyplayer = B_TRUE;
if (ngot == 1) {
msg("%s %s appears near %s!", needan(rc->name) ? "An" : "A", rc->name, castername);
} else {
msg("%s appear around %s!", makeplural(rc->name), castername);
}
}
}
} else if (spellid == OT_S_SUMMONWEAPON) {
object_t *o;
targcell = caster->cell;
target = caster;
if (getequippedob(target->pack, BP_WEAPON)) {
fizzle(caster);
stopspell(caster, spellid);
return B_FALSE;
}
o = addob(target->pack, "energy blade");
if (o) {
if (canweild(target, o)) {
flag_t *f;
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("A blade of pure energy forms in your hands!");
} else if (cansee(player, target)) {
msg("A blade of pure energy forms in %s%s hands!",castername,
getpossessive(castername));
}
weild(target, o);
// set its damage value
f = hasflag(o->flags, F_DAM);
if (f) {
char buf[BUFLEN];
snprintf(buf, BUFLEN, "2d%d",power);
changeflagtext(f, buf);
}
addflag(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL);
} else {
killob(o);
fizzle(caster);
stopspell(caster, spellid);
}
} else {
fizzle(caster);
stopspell(caster, spellid);
return B_FALSE;
}
} else if (spellid == OT_S_WALLOFICE) {
object_t *o;
int donesomething = B_FALSE;
cell_t *c;
int vdist = 0,hdist = 0;
int walldir;
int seen = B_FALSE;
flag_t *f;
// get vert distance
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) {
vdist++;
}
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) {
vdist++;
}
// get horz dist
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) {
hdist++;
}
for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) {
hdist++;
}
if ((vdist == 0) && (hdist == 0)) {
fizzle(caster);
return B_FALSE;
}
// select direction
if (vdist < hdist) {
walldir = D_N;
} else if (hdist < vdist) {
walldir = D_E;
} else {
if (onein(2)) {
walldir = D_N;
} else {
walldir = D_E;
}
}
if (walldir == D_N) {
// vert wall
for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_N)) {
if (!seen && haslos(player, c)) seen = B_TRUE;
o = addob(c->obpile, "wall of ice");
if (o) {
f = hasflag(o->flags, F_OBHP);
if (f) { f->val[0] = power*10; f->val[1] = power*10; }
addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL);
}
// knock lfs away
if (c->lf) {
knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS, B_FALSE), 1, NULL, 30+power, B_TRUE);
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, 30+power, B_TRUE);
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, 30+power, B_TRUE);
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, 30+power, B_TRUE);
donesomething = B_TRUE;
}
}
}
if (seen) {
drawscreen();
msg("A wall of ice appears!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
} else if (spellid == OT_S_WATERJET) {
char lfname[BUFLEN];
cell_t *retcell[MAXRETCELLS];
int nretcell,i;
// animation
//anim(caster->cell, targcell, '}', C_BLUE);
if (isplayer(caster) || cansee(player, caster)) {
msg("%s fire%s a jet of high-pressure water.",castername,isplayer(caster) ? "" : "s");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell);
for (i = 1; i < nretcell; i++) {
target = haslf(retcell[i]);
if (target) {
int dir,amt, i;
object_t *arm[MAXBODYPARTS];
int narm = 0;
getlfname(target, lfname);
// hit
if (cansee(player, target)) {
msg("%s %s hit by a jet of water!",lfname, is(target));
}
// water damage will generally be turn to zero unless people are specifically
// vulnerable to water, so do bashing damage too.
losehp(target, roll("3d4"), DT_WATER, caster, "a high-pressure jet of water");
losehp(target, roll("3d4"), DT_BASH, caster, "a high-pressure jet of water");
// knock backwards
dir = getdirtowards(caster->cell, target->cell, target, B_FALSE, DT_COMPASS);
amt = (power/3); if (amt < 2) amt = 2;
knockback(target, dir, amt, caster, 0, B_TRUE);
// rust
getallouterarmour(target, arm, &narm);
for (i = 0; i < narm; i++) {
takedamage(arm[i], R_TRUSTY, DT_WATER);
}
// add water object
addob(retcell[i]->obpile, "large puddle of water");
break;
} else {
damageallobs(NULL, retcell[i]->obpile, 0, DT_WATER);
// add water object
addob(retcell[i]->obpile, "large puddle of water");
}
}
} else if (spellid == OT_S_WARPWOOD) {
object_t *o,*nexto;
flag_t *f;
int ndone = 0;
if (!targcell) {
fizzle(caster);
}
if (targcell->lf) {
// weilded weapons/armour
for (o = targcell->lf->pack->first ; o ; o = nexto) {
nexto = o->next;
if (o->type->material->id == MT_WOOD) {
if (isequipped(o)) {
int dam;
f = hasflag(o->flags, F_OBHP);
if (f) {
dam = rnd(f->val[0]/2,f->val[0]);
} else {
dam = roll("1d6");
}
takedamage(o, dam, DT_DECAY);
if (haslos(player, targcell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
ndone++;
}
}
}
}
for (o = targcell->obpile->first ; o ; o = nexto) {
nexto = o->next;
if (o->type->material->id == MT_WOOD) {
int dam;
f = hasflag(o->flags, F_OBHP);
if (f) {
dam = rnd(1,f->val[0]);
} else {
dam = roll("1d6");
}
takedamage(o, dam, DT_DIRECT);
if (haslos(player, targcell)) {
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
ndone++;
}
}
if (!ndone) {
fizzle(caster);
}
} else if (spellid == OT_S_WEAKEN) {
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, NA, NULL, howlong);
f->obfrom = OT_S_WEAKEN;
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
} else if (spellid == OT_S_WEB) {
char numbuf[BUFLEN];
int i;
numtotext(power, numbuf);
// animation
anim(caster->cell, targcell, '^', C_GREY);
if (isplayer(caster) || cansee(player, caster)) {
if (power == 1) {
msg("%s fire%s a burst of webbing.",castername,isplayer(caster) ? "" : "s");
} else {
msg("%s fire%s %s bursts of webbing.",castername,isplayer(caster) ? "" : "s", numbuf);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// create webs
for (i = 0; i < power; i++) {
cell_t *c;
if (i == 0) {
c = targcell;
} else {
c = getrandomadjcell(targcell, WE_NOTWALL, B_ALLOWEXPAND);
}
if (c) {
if (c->lf && (c->lf->race->baseid != R_SPIDER)) {
if (skillcheck(c->lf, SC_DODGE, 20+power, 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("You are stuck in a web!");
} else if (cansee(player, c->lf)) {
char lfname[BUFLEN];
getlfname(c->lf, lfname);
msg("%s is stuck in a web!", lfname);
}
}
}
}
}
} else if (spellid == OT_S_WINDSHIELD) {
flag_t *f;
// always targetted at caster
targcell = caster->cell;
target = caster;
f = addtempflag(caster->flags, F_WINDSHIELD, power, NA, NA, NULL, FROMSPELL);
f->obfrom = spellid;
} else if (spellid == OT_S_WISHLIMITED) {
object_t *o;
char obname[BUFLEN];
char ch;
if (!target) target = caster;
initprompt(&prompt, "For what do you wish?");
addchoice(&prompt, 'a', "Wealth", NULL, NULL, NULL);
addchoice(&prompt, 'b', "Power", NULL, NULL, NULL);
addchoice(&prompt, 'c', "Protection", NULL, NULL, NULL);
addchoice(&prompt, 'd', "Fame", NULL, NULL, NULL);
addchoice(&prompt, 'e', "Knowledge", NULL, NULL, NULL);
addchoice(&prompt, 'f', "Magic", NULL, NULL, NULL);
if (isplayer(target)) {
ch = getchoice(&prompt);
} else {
ch = 'e';
// ie. don't select 'knowledge'
while (ch == 'e') {
ch = rnd('a', 'f');
}
}
if (ch == 'a') { // wealth (gold, bad: goldtouch)
snprintf(buf, BUFLEN, "1000-2000 gold coins");
} else if (ch == 'b') { // power (weapons, bad: battery)
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)) {
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 (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;
// get a list of all valid body parts
for (bp = 0; bp <= MAXBODYPARTS; bp++) {
if (!lfhasflagval(target, F_NOBODYPART, bp, NA, NA, NULL)) {
poss[nposs++] = bp;
}
}
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)) {
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 (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: useless allies)
lifeform_t *lf;
cell_t *c;
job_t *j;
char *p,lfname[BUFLEN];
// summon a human
c = getrandomadjcell(caster->cell, WE_WALKABLE, 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);
}
petify(lf, target);
if (isplayer(target)) {
p = assignnpcname(lf);
sayphrase(lf, SP_RECRUIT_ACCEPT, SV_TALK, NA, p);
}
strcpy(buf, "");
} else if (ch == 'e') { // knowledge (makeknown everything you have plus 5-10 others. bad: insanity)
// only possible for the player!
strcpy(buf, "");
if (isplayer(target)) {
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);
}
}
}
} else if (ch == 'f') { // magic (spellbooks)
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, "");
}
}
if (strlen(buf)) {
// 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;
}
} else if ((spellid == OT_S_WISH) || (spellid == OT_S_GIFT)) {
object_t *o;
if (isplayer(caster)) {
char lfname[BUFLEN];
char question[BUFLEN];
int i;
if (seenbyplayer) *seenbyplayer = B_TRUE;
// ask for target
if (spellid == OT_S_GIFT) {
if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, 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);
addob(target->cell->obpile, buf);
if (nretobs) {
for (i = 0; i < nretobs; i++) {
char obname[BUFLEN];
o = retobs[i];
getobname(o, obname, o->amt);
if (!hasflag(o->flags, F_IMPASSABLE) && canpickup(target, o, ALL)) {
// you gain it.
relinkob(o, target->pack); // move to pack
if (isplayer(target)) {
msgnocap("%c - %s.", o->letter, obname);
} else {
msg("%s is gifted with %s.", lfname, obname);
}
} else {
// can't pick this up...
// impassable? goes in a nearby cell instead of at your feet.
if (hasflag(o->flags, F_IMPASSABLE)) {
cell_t *newloc;
// if so, don't put it where the player is!
newloc = real_getrandomadjcell(target->cell, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL);
o = relinkob(o, newloc->obpile);
}
if (o) {
noise(caster->cell, NULL, NC_OTHER, 1, "something hitting the ground.", NULL);
if (!isblind(caster)) {
msg("%s appear%s on the ground!", obname, (o->amt == 1) ? "s" : "");
}
} else {
// ob exists but couldn't make it appear
msg("The air in front of %s seems to ripple for a moment.", lfname);
break; // don't process any more.
}
}
}
} else {
// couldn't make it appear - ob doesn't exist
msg("The air in front of %s seems to ripple for a while.", lfname);
}
// 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;
}
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);
}
}
char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr) {
flag_t *f;
// forced?
f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL);
if (f) {
texttospellopts(f->text, "race:", racestr, 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 = getspelllevel(oid);
/*
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 = 20 + (lev*2) + power;
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;
}
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_ENCHANTMENT:
return SK_SS_ENCHANTMENT;
case SS_NATURE:
return SK_SS_NATURE;
case SS_FIRE:
return SK_SS_FIRE;
case SS_COLD:
return SK_SS_COLD;
case SS_GRAVITY:
return SK_SS_GRAVITY;
case SS_LIFE:
return SK_SS_LIFE;
case SS_MODIFICATION:
return SK_SS_MODIFICATION;
case SS_MENTAL:
return SK_SS_MENTAL;
case SS_SUMMONING:
return SK_SS_SUMMONING;
case SS_TRANSLOCATION:
return SK_SS_TRANSLOCATION;
case SS_WILD:
return SK_SS_WILD;
default:
break;
}
return SK_NONE;
}
// returns "x MP" or "x-y MP", or "x-y MP, ongoing" ect
char *getspellcosttext(lifeform_t *lf, enum OBTYPE spellid, int power, char *buf) {
objecttype_t *ot;
int ongoing = B_FALSE;
int cost;
ot = findot(spellid);
if (!ot) {
strcpy(buf, "?invalidspellid?");
return buf;
}
cost = getmpcost(lf, spellid);
if (hasflag(ot->flags, F_ONGOING)) ongoing = B_TRUE;
if (hasflag(ot->flags, F_VARPOWER)) {
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;
}
int getspellmaxpower(enum OBTYPE spellid) {
flag_t *f;
objecttype_t *ot;
int max = 10;
ot = findot(spellid);
if (!ot) {
return 10;
}
f = hasflag(ot->flags, F_MAXPOWER);
if (f) {
max = f->val[0];
}
return max;
}
char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf) {
int power;
enum SPELLSCHOOL ss;
objecttype_t *ot;
ot = findot(spellid);
strcpy(buf, ot->name);
capitalise(buf);
power = getspellpower(lf, spellid);
ss = getspellschool(spellid);
if ((power > 1) && (ss != SS_ABILITY)) {
strcat(buf, " ");
strcat(buf, roman(power));
}
if (spellid == OT_S_DETECTLIFE) {
if (power >= 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 >= 8) {
strcat(buf, "(perm,ctrl)");
} else if (power >= 3) {
strcat(buf, "(ctrl)");
}
} else if (spellid == OT_S_POLYMORPH) {
if (power >= 5) {
strcat(buf, "(ctrl)");
}
} else if (spellid == OT_S_TELEPORT) {
if (power >= 8) {
strcat(buf, "(fullctrl)");
} else if (power >= 5) {
strcat(buf, "(semictrl)");
}
}
return buf;
}
int getspellpower(lifeform_t *lf, enum OBTYPE spellid) {
int power = 0;
int spelllev;
enum SKILLLEVEL schoolskill;
enum SPELLSCHOOL school;
int db = B_FALSE;
flag_t *f;
if (db) {
objecttype_t *ot;
ot = findot(spellid);
if (db) dblog("getspellpower for lf %s, spell %s", lf->race->name, ot->name);
}
////////////////////////////////////
// CAN WE CAST THIS AT ALL
////////////////////////////////////
// If we can _will_ this to occur then we might have a set
// spellpower
f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL);
if (f && strlen(f->text)) {
texttospellopts(f->text, "pw:", &power, NULL );
if (power > 0) {
if (db) {
dblog("-->power = %d (from canwill)", power);
}
// note that this power _can_ override max spell power
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)) {
} else 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 = 4; break;
case PR_SKILLED: maxspelllevel = 6; break;
case PR_EXPERT: maxspelllevel = 8; break;
case PR_MASTER: maxspelllevel = 9; break;
}
}
// player can only ever cast spells up to your level.
if (!hasjob(lf, J_GOD)) limit(&maxspelllevel, NA, lf->level);
if (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; // base power of 1.
// plus your hitdice/3
power += (gethitdice(lf)/3);
// plus intelligence modifier
if (school == SS_MENTAL) {
// +/- 2 for iq
power += (getstatmod(lf, A_IQ) / 25);
} else if (school == SS_NATURE) {
// +/- 1 for wisdom
power += (getstatmod(lf, A_WIS) / 50);
// TODO: clerical +/- 2 for wisdom
} else {
// +/- 1 for iq
power += (getstatmod(lf, A_IQ) / 50);
}
// plus your school skill
power += schoolskill;
} else {
// for monsters, just based on hitdice:
power = gethitdice(lf);
}
limit(&power, 0, getspellmaxpower(spellid));
return power;
}
enum SPELLSCHOOL getspellschool(enum OBTYPE spellid) {
flag_t *f;
objecttype_t *ot;
ot = findot(spellid);
if (!ot) {
return SS_NONE;
}
if (ot->obclass->id == OC_ABILITY) {
return SS_ABILITY;
}
f = hasflag(ot->flags, F_SPELLSCHOOL);
if (f) {
return f->val[0];
}
return SS_NONE;
}
// 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 one randomly
thisschool = poss2[rnd(0,nposs2-1)];
} 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) {
objecttype_t *st;
int range = UNLIMITED;
// 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];
}
}
switch (spellid) {
case OT_S_BURNINGWAVE:
// base range for this spell is 3
range += (power/3);
break;
case OT_S_CALLWIND:
range = (power*2);
break;
case OT_S_DIG:
range = power;
break;
case OT_S_LIGHTNINGBOLT:
range = (power*3);
break;
case OT_S_CHAINLIGHTNING:
range = (power*3);
break;
case OT_S_SHARDSHOT:
range = power;
break;
default:
break;
}
return range;
}
char *getvarpowerspelldesc(enum OBTYPE spellid, int power, char *buf) {
// default
strcpy(buf, "");
switch (spellid) {
case OT_S_PSYARMOUR:
snprintf(buf, BUFLEN, "+%d Armour Rating", power*4);
break;
case OT_S_SUMMONWEAPON:
snprintf(buf, BUFLEN, "Create a 2d%d damage magical weapon",power);
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_WINDSHIELD:
snprintf(buf, BUFLEN, "Protection from missiles <= %d km/h",speedtokph(power));
break;
default:
break;
}
return buf;
}
// magically propel an object into lf's hands.
// if it's too big it'll hit them!
void pullobto(object_t *o, lifeform_t *lf) {
char obname[BUFLEN];
char lfname[BUFLEN];
glyph_t *gl;
gl = getglyph(o);
anim(getoblocation(o), lf->cell, gl->ch, gl->colour);
getobname(o, obname, o->amt);
getlfname(lf, lfname);
if (isplayer(lf) || haslos(player, lf->cell)) {
msg("%s %s towards %s!", obname, (o->amt == 1) ? "flies" : "fly",
lfname);
}
// can we pick up the object?
if (hasflag(o->flags, F_NOPICKUP) || hasflag(o->flags, F_IMPASSABLE) || !canpickup(lf, o, ALL)) {
char buf[BUFLEN];
int dir;
cell_t *obloc,*newcell;
snprintf(buf, BUFLEN, "a flying %s", noprefix(obname));
if (isplayer(lf) || haslos(player, lf->cell)) {
msg("%s %s into %s!", obname, (o->amt == 1) ? "slams" : "slam", lfname);
}
// where does it end up?
obloc = getoblocation(o);
dir = getdirtowards(lf->cell, obloc, NULL, B_FALSE, DT_COMPASS);
newcell = getcellindir(lf->cell, dir);
if (newcell) {
// move next to player
o = moveob(o, newcell->obpile, o->amt);
// lf takes damage
losehp(lf, getthrowdam(o), DT_PROJECTILE, NULL, buf);
} else {
o = moveob(o, lf->cell->obpile, o->amt);
// kill lf straight away!
lf->lastdamtype = DT_PROJECTILE;
setlastdam(lf, buf);
lf->hp = 0;
}
} else {
if (isplayer(lf) || haslos(player, lf->cell)) {
msg("%s %s %s.", lfname, isplayer(lf) ? "catch" : "catches", obname);
}
pickup(lf, o, o->amt, B_FALSE, B_FALSE);
}
}
void stopallspells(lifeform_t *lf) {
flag_t *f,*nextf;
for (f = lf->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->id == F_BOOSTSPELL) {
stopspell(lf, f->val[0]);
}
}
}
void stopallspellsexcept(lifeform_t *lf, ...) {
flag_t *f,*nextf;
va_list args;
enum OBTYPE exception[MAXCANDIDATES];
int nexceptions = 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) {
stopspell(lf, f->val[0]);
}
}
}
}
int schoolappearsinbooks(enum SPELLSCHOOL ss) {
switch (ss) {
case SS_DIVINE:
case SS_ABILITY:
case SS_ALLOMANCY:
case SS_MENTAL:
return B_FALSE;
default:
break;
}
return B_TRUE;
}
void spellcloud(cell_t *srcloc, int radius, char ch, enum COLOUR col, enum OBTYPE sid, int power, int frompot) {
int x,y;
if (ch != '\0') {
if (haslos(player, srcloc)) {
animradialorth(srcloc, radius, ch, col);
drawscreen();
}
}
for (y = srcloc->y - radius ; y <= srcloc->y + radius; y++) {
for (x = srcloc->x - radius ; x <= srcloc->x + radius; x++) {
cell_t *c;
c = getcellat(srcloc->map, x, y);
if (c && c->lf && (c != srcloc) && !c->type->solid &&
haslof(srcloc, c, LOF_WALLSTOP, NULL)) {
if (getcelldistorth(srcloc, c) <= radius) {
// fall asleep
dospelleffects(NULL, sid, power, c->lf, NULL, c, B_UNCURSED, NULL, frompot);
}
}
}
}
}
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;
}
// 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];
// 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 == RG_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 (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), 0)) {
if (isplayer(target) || haslos(player, target->cell)) {
if (announce) {
msg("%s",text);
}
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
return B_TRUE;
}
return B_FALSE;
}
void stopspell(lifeform_t *caster, enum OBTYPE spellid) {
flag_t *f,*nextf;
object_t *o, *nexto;
objecttype_t *sp;
sp = findot(spellid);
if (!sp) return;
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);
} 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);
}
}
}
}
}
}
// returns # created
int summonlfs(lifeform_t *caster, cell_t *where, 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;
// determine possible types of race
for (r = firstrace ; r; r = r->next) {
int ok = B_TRUE;
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
c = getrandomadjcell(where, WE_EMPTY, B_ALLOWEXPAND);
if (!c) {
return ncreated;
}
// determine exact type of monster
r = findrace(poss[rnd(0,nposs-1)]);
if (r) {
// add it!
newlf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL);
// not worth any xp
killflagsofid(newlf->flags, F_XPVAL);
addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL);
// summoned
addflag(newlf->flags, F_SUMMONEDBY, caster->id, lifetime, NA, NULL);
if (friendly) {
addflag(newlf->flags, F_PETOF, caster->id, NA, NA, NULL);
if (areallies(player, caster)) {
makefriendly(newlf, PERMENANT);
}
}
ncreated++;
}
}
return ncreated;
}
lifeform_t *validateabillf(lifeform_t *user, enum OBTYPE aid, lifeform_t **target) {
objecttype_t *ot;
int maxrange;
flag_t *f;
if (*target) {
return *target;
}
ot = findot(aid);
f = hasflag(ot->flags, F_RANGE);
if (f) {
maxrange = f->val[0];
} else {
maxrange = UNLIMITED;
}
// ask for a target lifeform
if (isplayer(user)) {
cell_t *where;
char buf[BUFLEN],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;
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);
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 && !haslof(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 (!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 = askcoords(buf, buf2, targtype, caster, maxrange, needlof, needlof ? B_TRUE : B_FALSE);
if (!where) {
char ques[BUFLEN];
int ch;
snprintf(ques, BUFLEN, "Abandon your %s?", (sp->obclass->id == OC_SPELL) ? "spell" : "ability");
ch = askchar(ques,"yn","n", B_TRUE, B_FALSE);
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;
}
}
// eye protection will stop some spells!
if (*targcell && (*targcell)->lf) {
flag_t *casttype;
lifeform_t *victim;
victim = (*targcell)->lf;
casttype = lfhasflag(caster, F_CASTTYPE);
if (casttype) {
object_t *glasses = NULL;
switch (casttype->val[0]) {
case CT_EYESPIT:
case CT_GAZE:
glasses = eyesshaded(victim);
if (glasses) {
if (isplayer(victim)) {
char gbuf[BUFLEN];
getobname(glasses, gbuf, glasses->amt);
msg("Your %s protects you.", noprefix(gbuf));
} else if (cansee(player, victim)) {
char lfname[BUFLEN],gbuf[BUFLEN];
getobname(glasses, gbuf, glasses->amt);
getlfname(caster, lfname);
msg("%s%s %s protects it.", lfname, getpossessive(lfname), noprefix(gbuf) );
}
*targcell = NULL;
}
break;
default:
break;
}
}
}
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;
}
*/