#define _GNU_SOURCE #include #include #include #include #include #include #include "ai.h" #include "attack.h" #include "defs.h" #include "flag.h" #include "god.h" #include "io.h" #include "lf.h" #include "map.h" #include "move.h" #include "nexus.h" #include "objects.h" #include "spell.h" #include "text.h" extern lifeform_t *player; extern skill_t *firstskill, *lastskill; extern race_t *firstrace, *lastrace; extern recipe_t *firstrecipe, *lastrecipe; extern map_t *heaven; extern region_t *firstregion; extern knowledge_t *knowledge; extern int needredraw; extern prompt_t prompt; extern WINDOW *msgwin; extern WINDOW *gamewin; extern void *rdata; extern job_t *firstjob; extern objecttype_t *objecttype; extern map_t *firstmap; extern object_t *retobs[MAXPILEOBS+1]; extern int retobscount[MAXPILEOBS+1]; extern int nretobs; extern enum ERROR reason; extern int needredraw, statdirty; extern condset_t ccwalkable; extern condset_t ccwalkableroom; // return value of FALSE means that stamina costs should be charged. // TRUE is a cancellation - don't change stamina int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifeform_t *target, flag_t *cwflag) { char username[BUFLEN]; char killername[BUFLEN]; char targetname[BUFLEN]; char buf[BUFLEN]; int power = 0,needgrab = B_FALSE, range = 0; char damstr[BUFLEN],racestr[BUFLEN]; objecttype_t *ot; object_t *fromob = NULL; char fromobname[BUFLEN]; flag_t *f; strcpy(fromobname, ""); if (user && cwflag && (cwflag->obfrom != -1)) { fromob = findobbyid(user->pack, cwflag->obfrom); if (fromob) { real_getobname(fromob, fromobname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); } } getlfname(user, username); real_getlfname(user,killername, NULL, B_NOSHOWALL, B_REALRACE); // defaults strcpy(damstr,""); power = 0; needgrab = B_FALSE; if (cwflag && strlen(cwflag->text)) { texttospellopts(cwflag->text, "pw:", &power, "dam:", damstr, "needgrab:", &needgrab, "range:", &range, "range:", racestr, NULL); } // get more options from ablity itself... ot = findot(abilid); if (ot) { if (hasflag(ot->flags, F_NEEDSGRAB)) { needgrab = B_TRUE; } } if (needgrab) { // target is whoever we are grabbing f = lfhasflag(user, F_GRABBING); if (f) { target = findlf(NULL, f->val[0]); } else { if (isplayer(user)) msg("You need to grab someone first!"); return B_TRUE; } } if (target) { getlfname(target, targetname); } if (abilid == OT_A_BUILD) { objecttype_t *tempot = NULL; int dir; char ch = 'a'; cell_t *c; object_t *o; int numtobuild = 1; enum SKILLLEVEL slev; enum OBTYPE needob = OT_NONE; int numneed = 0; if (!isplayer(user)) return B_TRUE; if (isimmobile(user)) { if (isplayer(user)) msg("You can't move!"); return B_TRUE; } if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't build anything while swimming!"); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't build anything while stuck!"); return B_TRUE; } else if (isairborne(user, NULL)) { if (isplayer(user)) msg("You can't build anything while airborne!"); return B_TRUE; } slev = getskill(user, SK_ENGINEERING); dir = askdir("Build something in which direction (. for self, - to cancel)", B_TRUE, B_TRUE); if (dir == D_MYSELF) { c = NULL; initprompt(&prompt, "What will you build?"); if (slev >= PR_NOVICE) { addbuildchoice(&prompt, user, OT_ARROW, &ch); addbuildchoice(&prompt, user, OT_BOLT, &ch); } } else if (dir < 0) { msg("Cancelled."); return B_TRUE; } else { c = getcellindir(user->cell, dir); if (c->lf || c->type->solid) { msg("There is no space to build anything there!"); return B_TRUE; } // what can we build? initprompt(&prompt, "What will you build?"); if (hasdoor(c)) { if (slev >= PR_BEGINNER) { // collapsing door addbuildchoice(&prompt, user, OT_TRAPDOORFALL, &ch); } if (slev >= PR_EXPERT) { // other door traps addbuildchoice(&prompt, user, OT_TRAPNEEDLEP, &ch); addbuildchoice(&prompt, user, OT_TRAPFIRE, &ch); addbuildchoice(&prompt, user, OT_TRAPMINE, &ch); } } // floor traps? if (cellwalkable(NULL, c, NULL)) { if (slev >= PR_BEGINNER) { addbuildchoice(&prompt, user, OT_TRAPTRIP, &ch); addbuildchoice(&prompt, user, OT_BARRICADE, &ch); addbuildchoice(&prompt, user, OT_RUBBLE, &ch); addbuildchoice(&prompt, user, OT_FENCEWOOD, &ch); } if (slev >= PR_ADEPT) { addbuildchoice(&prompt, user, OT_TRAPARROW, &ch); } if (slev >= PR_EXPERT) { addbuildchoice(&prompt, user, OT_TRAPROCK, &ch); } } } if (prompt.nchoices <= 0) { if (c) { msg("There is nothing that you can build there."); } else { msg("There is nothing that you can currently build."); } return B_TRUE; } prompt.maycancel = B_TRUE; ch = getchoice(&prompt); tempot = (objecttype_t *)prompt.result; if (!tempot || (ch == '\0')) { msg("Cancelled."); return B_TRUE; } // remove ingredients canbuild(user, tempot, NULL, &needob, &numneed); // just using this to get ingredients and count if (needob != OT_NONE) { o = hasob(user->pack, needob); if (o && (o->amt >= numneed)) { if (c) { // just build it removeob(o, numneed); } else { int npossible; npossible = (o->amt / numneed); // ask how many if (npossible >= 2) { char ptext[BUFLEN],buf[BUFLEN]; char *p; p = strdup(tempot->name); makeplural(&p); snprintf(ptext, BUFLEN, "How many %s will you build (max %d)", p, npossible); free(p); askstring(ptext, '?', buf, BUFLEN, "1"); numtobuild = atoi(buf); if (numtobuild <= 0) { msg("Cancelled."); return B_TRUE; } } removeob(o, numneed*numtobuild); } } else { msg("You don't have the required components to build that."); return B_TRUE; } } if (c) { // create the trap/obstacle/whatever o = hasdoor(c); if (o) { addflag(o->flags, F_TRAPPED, tempot->id, NA, B_TRUE, NULL); o = NULL; } else { o = addobfast(c->obpile, tempot->id); if (o) { killflagsofid(o->flags, F_SECRET); } else { msg("For some reason, you can't build there."); return B_TRUE; } } } else { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%d original %s", numtobuild, tempot->name); o = addob(user->pack, buf); } taketime(user, getactspeed(user)*2); if (c) { setlosdirty(user); msg("You build %s %s.", needan(tempot->name) ? "an" : "a", tempot->name); } else { char obname[BUFLEN]; getobname(o, obname, o->amt); msgnocap("%c - %s", o->letter, obname); } } else if (abilid == OT_A_CHARGE) { cell_t *adjcell = NULL,*origcell; char targetname[BUFLEN]; if (isimmobile(user)) { if (isplayer(user)) msg("You can't move!"); return B_TRUE; } if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't charge while swimming!"); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't charge while stuck!"); return B_TRUE; } if (!range) { int movespeed; // get max range - based on speed. movespeed = getmovespeed(user); if (movespeed > SP_NORMAL) { if (isplayer(user)) msg("You are too slow to charge!"); return B_TRUE; } range = 3 + ((SP_NORMAL - movespeed)/5); } if (!targcell) { if (isplayer(user)) { snprintf(buf, BUFLEN, "Charge who (max range %d)?",range); // TODO: ask for direction targcell = askcoords(buf, "Charge->", TT_MONSTER, user, range, LOF_NEED, B_TRUE); if (!targcell) { msg("Cancelled."); return TRUE; } } else { return B_FALSE; } } if (getcelldist(user->cell, targcell) > range) { if (isplayer(user)) msg("You can't charge that far!"); return B_TRUE; } else if (getcelldist(user->cell, targcell) < 2) { if (isplayer(user)) msg("You don't have enough space to charge there!"); return B_TRUE; } // make sure we have LOF to there target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there!"); return TRUE; } else if (!haslof_real(user->cell, targcell, LOF_NEED, NULL, user, B_FALSE)) { if (isplayer(user)) msg("Your path there is blocked!"); return TRUE; } getlfname(target, targetname); // find cell on the way... adjcell = get_closest_adjcell(user->cell, targcell); if (!adjcell) { if (isplayer(user)) { msg("There is no space nearby for you to attack!"); } return B_TRUE; } if (adjcell->lf) { if (isplayer(user)) msg("Your path there is blocked!"); return TRUE; } // take some time taketime(user, getactspeed(user)); // remember orig cell origcell = user->cell; // teleport next to them movelf(user, adjcell, B_FALSE); if (haslos(player, adjcell)) { char verb[BUFLEN]; // special case: sometimes we call this a 'leap' switch (user->race->id) { case R_CREEPINGCLAW: case R_LEECH: case R_SNAKETREE: case R_PIRANHAKING: case R_CATTIGER: case R_CATPANTHER: strcpy(verb, "leap"); break; default: strcpy(verb, "charge"); break; } // check baseid too if (user->race->baseid == R_LEECH) { strcpy(verb, "leap"); } msg("%s %s%s towards %s!",username,verb, isplayer(user) ? "" : "s",targetname); needredraw = B_TRUE; drawlevelfor(player); redraw(); //if (!isplayer(user)) { more(); //} } // special cases.... if (user->race->id == R_CREEPINGCLAW) { // creeping claw automatically grabs target addflag(user->flags, F_GRABBING, target->id, NA, NA, NULL); addflag(target->flags, F_GRABBEDBY, user->id, NA, NA, NULL); } else { // attack attackcell(user, targcell, B_TRUE); } } else if (abilid == OT_A_CHECKSTAIRS) { char obname[BUFLEN], buf[BUFLEN],ch; int madenewmap = B_TRUE; cell_t *origcell = NULL; lifeform_t *inway = NULL, *movedlf = NULL; cell_t *c; object_t *stairs; int stairdir, needsclimb = B_FALSE; char climbprompt[BUFLEN]; if (!isplayer(user)) { return B_FALSE; } // are there stairs here? stairs = hasobwithflag(user->cell->obpile, F_CLIMBABLE); if (!stairs) { msg("There are no stairs here!"); return B_TRUE; } stairdir = getstairdirection(stairs); if ((stairdir != D_UP) && (stairdir != D_DOWN)) { // slightly different message from above, for debugging msg("There are no stairs here to check!"); return B_TRUE; } // are stairs on the roof? if (isinroof(stairs) && !canreachroof(user)) { needsclimb = B_TRUE; snprintf(climbprompt, BUFLEN, "Climb to check %%s"); } // how can we check the stairs? getobname(stairs, obname, 1); snprintf(buf, BUFLEN, "How will you check %s", obname); initprompt(&prompt, buf); if (lfhasflag(user, F_DETECTLIFE)) { addchoice(&prompt, 'd', "Detect lifeforms", NULL, NULL, NULL); } if ( ((getskill(user, SK_LISTEN) >= PR_ADEPT) && !isdeaf(user)) || hasequippedobid(user->pack, OT_AMU_THIEF)) { addchoice(&prompt, 'l', "Listen for sounds", NULL, NULL, NULL); } if ((getskill(user, SK_PERCEPTION)) && !isblind(user)) { if (needsclimb) { addchoice(&prompt, 'f', "Check for footprints (requires climbing)", NULL, NULL, NULL); snprintf(climbprompt, BUFLEN, "Climb to check %%s for footprints"); } else { addchoice(&prompt, 'f', "Check for footprints", NULL, NULL, NULL); } } if (getskill(user, SK_STEALTH) >= PR_SKILLED) { if (needsclimb) { addchoice(&prompt, 'p', "Peek at the other end (requires climbing)", NULL, NULL, NULL); snprintf(climbprompt, BUFLEN, "Climb to peek through %%s"); } else { addchoice(&prompt, 'p', "Peek at the other end", NULL, NULL, NULL); } } if (lfhasflag(user, F_ENHANCESMELL)) { addchoice(&prompt, 's', "Sniff for scents", NULL, NULL, NULL); } addchoice(&prompt, '-', "(cancel)", NULL, NULL, NULL); prompt.maycancel = B_TRUE; if (prompt.nchoices == 1) { msg("You have no way to check %s.", obname); return B_TRUE; } ch = getchoice(&prompt); if ((ch == '-') || (ch == '\0')) { msg("Cancelled."); return B_TRUE; } if (needsclimb) { // must try to climb first. if (asktoclimb(stairs, climbprompt)) { // failed return B_TRUE; } } // announce the check, so that the player has feedback // if map generation takes a while. if (ch == 'd') { msg("You concentrate on %s...",obname); } else if (ch == 'l') { msg("You listen at %s...",obname); } else if (ch == 's') { // smell msg("You sniff %s...",obname); } else if (ch == 'f') { // footprints msg("You inspect %s...",obname); } else if (ch == 'p') { // peek msg("You peek %s the stairs...", getdirname(stairdir)); } // find out where the other end goes... user->changinglev = B_TRUE; c = getstairdestination(stairs, &madenewmap); // show --more-- after we have the destination more(); if (!c) { msg("These stairs don't seem to go anywhere!"); user->changinglev = B_FALSE; return B_TRUE; } redrawpause(); // move any lfs at the other end out of the way. if (c->lf) { if (movelfsoutofway(c)) { // can't move them? ie. no adj cells. inway = c->lf; } else { movedlf = c->lf; } } if (!inway) { // temporarily put the player at the other end // purposely not using movelf() because we don't // want to trigger map enter effects. origcell = user->cell; origcell->lf = NULL; user->cell = c; c->lf = user; } // now actually do the check if (ch == 'd') { lifeform_t *lf; char *dname[MAXCANDIDATES],thisdname[BUFLEN]; int nposs = 0,n,dcount[MAXCANDIDATES]; int maxdist,showreal = B_FALSE; f = lfhasflag(user, F_DETECTLIFE); assert(f); maxdist = f->val[0]; if (f->val[1] == B_TRUE) showreal = B_TRUE; // get all lfs which are within flag v0 of the other end for (lf = c->map->lf ; lf ; lf = lf->next) { if (lf == user) continue; // within maxdist of stairs? if (getcelldist(lf->cell, c) <= maxdist) { int dirtolf; dirtolf = getdirtowards(c, lf->cell, NULL, B_FALSE, DT_ORTH); int found = B_FALSE; if (showreal) { getlfnamea(lf, thisdname); } else { // detected size - "large monster" etc snprintf(thisdname, BUFLEN, "%s monster", getsizetext(getlfsize(lf))); } // already a name like this? for (n = 0; n < nposs; n++) { if (streq(dname[n], thisdname)) { dcount[n]++; found = B_TRUE; } } if (!found) { if (showreal) { dname[nposs] = strdup(noprefix(thisdname)); } else { dname[nposs] = strdup(thisdname); } dcount[nposs] = 1; nposs++; } } } user->changinglev = B_FALSE; // announce if (nposs) { for (n = 0; n < nposs; n++) { char amttext[BUFLEN]; if (dcount[n] == 1) { strcpy(amttext, ""); } else if (dcount[n] <= 3) { strcpy(amttext, "several "); } else { // 4+ strcpy(amttext, "lots of "); } msg("You detect %s%s.", amttext, dname[n]); } } else { msg("You don't detect any living creatures."); } } else if (ch == 'l') { lifeform_t *lf; enum SKILLLEVEL slev; char *movetext[MAXCANDIDATES],thismovetext[BUFLEN]; int nposs = 0,n,movetextcount[MAXCANDIDATES],found,vol; slev = getskill(user, SK_LISTEN); if (inway) { // just get the lf in the way if (getnoisedetails(inway, N_WALK, NULL, thismovetext, NULL, &vol, NULL)) { // doesn't make noise nposs = 0; } else { if (slev >= PR_EXPERT) { // overwrite name real_getlfnamea(inway, thismovetext, NULL, B_NOSHOWALL, B_CURRACE); } movetext[0] = strdup(thismovetext); movetextcount[0] = 1; nposs = 1; } } else { int nwalls; // get all lfs within direct hearing (not through walls) of the other end for (lf = c->map->lf ; lf ; lf = lf->next) { if (lf == user) continue; // get movement text if (getnoisedetails(lf, N_WALK, NULL, thismovetext, NULL, &vol, NULL)) continue; if (slev >= PR_EXPERT) { // overwrite name real_getlfnamea(lf, thismovetext, NULL, B_NOSHOWALL, B_CURRACE); } if (canhear(user, lf->cell, vol, &nwalls) && (nwalls == 0)) { // already have this text? found = B_FALSE; for (n = 0; n < nposs; n++) { if (streq(movetext[n], thismovetext)) { movetextcount[n]++; found = B_TRUE; break; } } if (!found) { movetext[nposs] = strdup(thismovetext); movetextcount[nposs] = 1; nposs++; } } } } // announce if (nposs) { for (n = 0; n < nposs; n++) { char amttext[BUFLEN]; if (slev >= PR_EXPERT) { // we're hearing monster names if (movetextcount[n] == 1) { strcpy(amttext, ""); } else if (movetextcount[n] <= 3) { strcpy(amttext, "some "); } else { strcpy(amttext, "lots of "); } } else { // we're hearing monster sounds strcpy(amttext, ""); } if (slev >= PR_EXPERT) { char *newtext; if (movetextcount[n] > 1) { newtext = strdup(movetext[n]); makeplural(&newtext); msg("You can hear %s%s.", amttext, noprefix(newtext)); free(newtext); } else { msg("You can hear %s.", movetext[n]); } } else { msg("You can hear %s%s", amttext, movetext[n]); } // free this mem free(movetext[n]); } } else { msg("You don't hear anything unusual."); } } else if (ch == 's') { // smell lifeform_t *lf; race_t *smellrace[MAXCANDIDATES]; int nposs = 0,n,smellcount[MAXCANDIDATES], range = 0; f = lfhasflag(user, F_ENHANCESMELL); if (f) { range = f->val[0]; } if (range) { // list everyone you can smell. if (inway) { // just smell the lf in the way if (issmellablelf(inway)) { smellrace[0] = inway->race; smellcount[0] = 1; nposs = 1; } else { nposs = 0; } } else { // get all lfs you can smell from the other end for (lf = c->map->lf ; lf ; lf = lf->next) { int found; if (lf == user) continue; if (getcelldist(lf->cell, c) <= range) { // already have this one? found = B_FALSE; for (n = 0; n < nposs; n++) { if (smellrace[n] == lf->race) { smellcount[n]++; found = B_TRUE; break; } } if (!found) { smellrace[nposs] = lf->race; smellcount[nposs] = 1; nposs++; } } } } } // announce if (nposs) { for (n = 0; n < nposs; n++) { char amttext[BUFLEN]; char *newtext; if (smellcount[n] == 1) { strcpy(amttext, ""); } else if (smellcount[n] <= 3) { strcpy(amttext, "some "); } else { strcpy(amttext, "lots of "); } if (smellcount[n] > 1) { newtext = strdup(smellrace[n]->name); makeplural(&newtext); msg("You can smell %s%s.", amttext, newtext); free(newtext); } else { msg("You can smell %s %s.", needan(smellrace[n]->name) ? "an" : "a", smellrace[n]->name); } } } else { msg("You don't smell anything unusual."); } } else if (ch == 'f') { // footprints lifeform_t *lf; object_t *trailob; char *fpname[MAXCANDIDATES],thisfpname[BUFLEN]; int nposs = 0,n,fpcount[MAXCANDIDATES]; if (inway) { // just do footprints for the lf in the way trailob = addtrail(inway, c, inway->facing, B_TRUE, B_FALSE); if (trailob) { getobname(trailob, thisfpname, 1); fpname[0] = strdup(thisfpname); fpcount[0] = 1; nposs = 1; killob(trailob); } else { nposs = 0; } } else { // get all lfs which make trails from the other end for (lf = c->map->lf ; lf ; lf = lf->next) { if (lf == user) continue; // within lof of stairs? if (haslof(lf->cell, c, LOF_WALLSTOP, NULL)) { int dirtolf; dirtolf = getdirtowards(c, lf->cell, NULL, B_FALSE, DT_ORTH); trailob = addtrail(lf, c, dirtolf, B_TRUE, B_FALSE); if (trailob) { if (canseeob(user, trailob)) { int found = B_FALSE; // already a trail like this? getobname(trailob, thisfpname, 1); for (n = 0; n < nposs; n++) { if (streq(fpname[n], thisfpname)) { fpcount[n]++; found = B_TRUE; } } if (!found) { fpname[nposs] = strdup(thisfpname); fpcount[nposs] = 1; nposs++; } } killob(trailob); } } } } user->changinglev = B_FALSE; // announce if (nposs) { for (n = 0; n < nposs; n++) { char amttext[BUFLEN]; if (fpcount[n] == 1) { strcpy(amttext, ""); } else if (fpcount[n] <= 3) { strcpy(amttext, "several "); } else { // 4+ strcpy(amttext, "lots of "); } msg("You find %s%s.", amttext, fpname[n]); } } else { msg("You don't find any unusual tracks."); } } else if (ch == 'p') { // peek flag_t *awareness; // process light sources for the other end (otherwise new // maps will be dark by default and you might not see anything) //calclight(c->map); // prevent announcement of the flag we're about to give user->born = B_FALSE; // temporarily give the player 360degree sight awareness = addflag(user->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); // we now need born to be true so that precalclos() works. user->born = B_TRUE; // allow redraws redrawresume(); setlosdirty(user); // this will redraw the screen askcoords("Peek (ESC when done)->", "Peek (ESC when done)->", TT_NONE, user, UNLIMITED, LOF_DONTNEED, B_FALSE); user->born = B_FALSE; killflag(awareness); user->born = B_TRUE; } else { msg("Not implemented yet."); } // end ways of checking if (!inway) { // move user back user->cell->lf = NULL; user->cell = origcell; origcell->lf = user; } if (movedlf) { // move lf back movelf(movedlf, c, B_FALSE); } // resume redrawing capability. // might have already been done if we used the 'p'eek commaned (see above). redrawresume(); if (ch == 'p') { // if we peeked, then we have to redraw now. setlosdirty(user); if (needsclimb) { msg("You drop to the ground."); } else { msg("You return to your original position."); } } else if (needsclimb) { msg("You drop to the ground."); } taketime(user, getactspeed(user)*2); } else if (abilid == OT_A_CLIMB) { enum ERROR why; int origdir = D_NONE; // for ai only: turn to face target cell first. if (!isplayer(user) && targcell) { turntoface(user, targcell); } if (isplayer(user) && !isorthogonal(user->facing)) { int dir; dir = askdir("Climb in which direction (orthogonal only, - to cancel)", B_TRUE, B_FALSE); if ((dir == D_NONE) || (dir == D_MYSELF) || !isorthogonal(dir)) { msg("You can only climb in orthogonal directions."); return B_TRUE; } else { origdir = user->facing; setfacing(user, dir); targcell = getcellindir(user->cell, user->facing); } } if (!canclimb(user, &why)) { if (isplayer(user)){ switch (why) { case E_CLIMBING: msg("You are already climbing!"); break; case E_CANTMOVE: msg("You can't move!"); break; case E_BADCLIMBDIR: msg("There is no wall to climb in front of you!"); break; case E_BADCLIMBDIR2: msg("You can only climb in orthogonal directions."); break; case E_LFINWAY: msg("Something is in the way!"); break; case E_SWIMMING: msg("You can't climb while swimming!"); break; case E_TOOHEAVY: msg("Your load is too heavy to climb with!"); break; case E_NOABIL: msg("You cannot climb!"); break; default: msg("For some reason, you can't climb."); break; } } if (origdir != D_NONE) { setfacing(user, origdir); } return B_TRUE; } startclimbing(user); } else if (abilid == OT_A_COOK) { object_t *o; recipe_t *rec; char *longdesc,ch; int i; cell_t fakecell; map_t fakemap; char obname[BUFLEN]; int donesomething = B_TRUE; int ncooked = 0; int cooktime = 0; if (!isplayer(user)) return B_TRUE; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't cook while swimming!"); return B_TRUE; } while (donesomething) { object_t *corpse = NULL; donesomething = B_FALSE; // anything here to cook? for (o = user->cell->obpile->first ; o ; o = o->next) { if (iscorpse(o) && !hasflag(o->flags, F_PREPARED)) { char yn; char ques[BUFLEN]; getobname(o, obname, o->amt); snprintf(ques, BUFLEN, "There %s %s here. Cook it?", OB1(o,"is","are"),obname); yn = askchar(ques, "yn","n", B_TRUE, B_FALSE); if (yn == 'y') { corpse = o; break; } } } if (corpse) { getobname(corpse, obname, o->amt); if (getobsize(corpse) > (getskill(user, SK_COOKING)+1)) { msg("%s is too large for you to cook (Cooking skill too low).", obname); ncooked++; break; } else if (isimmuneto(corpse->flags, DT_FIRE, B_FALSE)) { msg("^wYou attempt to cook %s, but it won't heat up.^n", obname); cooktime += getactspeed(user); ncooked++; break; } else { preparecorpse(user, corpse); if (isresistantto(corpse->flags, DT_FIRE, B_FALSE)) { // takes longer cooktime += (getactspeed(user)*2); } else { cooktime += getactspeed(user); } practice(user, SK_COOKING, 1); ncooked++; if (isplayer(user)) pleasegodmaybe(R_GODNATURE, 1); } // set donesomething even if the actual cooking failed. donesomething = B_TRUE; corpse = NULL; } } // end while donesomething if (ncooked) { return B_FALSE; } // didn't cook anything from the ground? // make recipes. createfakes(&fakemap, &fakecell); longdesc = malloc(HUGEBUFLEN * sizeof(char)); initprompt(&prompt, "What will you cook (ESC to cancel)?"); for (rec = firstrecipe ; rec ; rec = rec->next ) { if (cancook(user, rec, NULL)) { object_t *o; o = addobfast(fakecell.obpile, rec->result); makedesc_ob(o, longdesc); addchoice(&prompt, 'a', o->type->name, NULL, rec, longdesc); killob(o); } } free(longdesc); killfakes(&fakemap, &fakecell); addchoice(&prompt, '\0', "nothing", NULL, NULL, NULL); prompt.maycancel = B_TRUE; ch = getchoicestr(&prompt, B_FALSE, B_TRUE); rec = (recipe_t *)prompt.result; if (!rec) { msg("Cancelled."); return B_TRUE; } else { object_t *retob[MAXRECIPEINGREDIENTS]; int retcount[MAXRECIPEINGREDIENTS]; int retconsume[MAXRECIPEINGREDIENTS]; int nretobs; char obname[BUFLEN]; enum RACE corpsetype = R_NONE; // try to cook this... // give the new object o = addobfast(user->pack, rec->result); if (o) { // now remove the ingredients... getingredients(user->pack, rec, retob, retcount, retconsume, &nretobs, B_TRUE); for (i = 0; i < nretobs; i++) { if (retconsume[i]) { // remember corpse types if (retob[i]->type->id == OT_CORPSE) { f = hasflag(retob[i]->flags, F_CORPSEOF); if (f) corpsetype = f->val[0]; } removeob(retob[i], retcount[i]); } } if (corpsetype != R_NONE) { addflag(o->flags, F_LINKRACE, corpsetype, NA, NA, NULL); } getobname(o, obname, o->amt); msgnocap("%c - %s", o->letter, obname); // take some time taketime(user, getactspeed(user)); practice(user, SK_COOKING, 1); if (isplayer(user)) pleasegodmaybe(R_GODNATURE, 5); } else { // pack full? msg("You have no space to cook!"); } } } else if (abilid == OT_A_DARKWALK) { if (range <= 0) range = UNLIMITED; if (islit(user->cell)) { if (isplayer(user)) msg("It is too light to darkwalk!"); return TRUE; } if (!targcell) { if (isplayer(user)) { if (range != UNLIMITED) { snprintf(buf, BUFLEN, "Darkwalk to where (max range %d)?", range); } else { snprintf(buf, BUFLEN, "Darkwalk to where?"); } targcell = askcoords(buf, "Darkwalk->", TT_NONE, user, range, LOF_DONTNEED, B_FALSE); } else { return B_TRUE; } } if (!targcell) { msg("Cancelled."); return TRUE; } else if (!cellwalkable(user, targcell, NULL)) { msg("There is something in your way."); return TRUE; } else if (!haslos(user, targcell)) { msg("You cannot see there!"); return TRUE; } else if (islit(targcell)) { msg("It is too light to darkwalk there!"); return TRUE; } else if ((range != UNLIMITED) && (getcelldist(user->cell, targcell) > range)) { msg("You cannot darkwalk that far!"); return TRUE; } taketime(user, getactspeed(user)); if (isplayer(user)) { msg("You step through the shadows."); } else if (cansee(player, user)) { msg("%s steps through the shadows.",username); } moveto(user, targcell, B_TRUE, B_TRUE); } else if (abilid == OT_A_DISARM) { int dir; object_t *o,*trapob = NULL; flag_t *trapflag = NULL; char buf[BUFLEN]; int bonus = 0; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't disable traps while swimming!"); return B_TRUE; } // ask for direction if (!targcell) { dir = askdir("Disable trap in which direction (- to cancel)", B_TRUE, B_FALSE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE ; } else if (dir == D_MYSELF) { targcell = user->cell; } else { targcell = getcellindir(user->cell, dir); } } if (targcell->lf) { if (isplayer(user)) { char inwayname[BUFLEN]; getlfname(targcell->lf, inwayname); msg("%s is in your way!",inwayname); } return B_TRUE; } // trap there? for (o = targcell->obpile->first ; o ; o = o->next) { if (hasflag(o->flags, F_TRAP) && !hasflag(o->flags, F_SECRET)) { // ie. a known trapped cell trapob = o; trapflag = hasflag(trapob->flags, F_TRAP); break; } else if (hasflagval(o->flags, F_TRAPPED, NA, NA, B_TRUE, NULL)) { // ie. a known trapped object trapob = o; trapflag = hasflag(trapob->flags, F_TRAPPED); break; } } if (!trapob) { if (isplayer(user)) msg("You can't see any traps there!"); return B_TRUE; } if (isplayer(user) || cansee(player, user)) { needredraw = B_TRUE; } // taketime taketime(user, getactspeed(user)); // if this was a cell trap, move player there. if (trapflag->id == F_TRAP) { movelf(user, targcell, B_TRUE); } // have any objects which will help? bonus = 0; for (o = user->pack->first ; o ; o = o->next) { f = hasflag(o->flags, F_HELPSDISARM); if (f && (f->val[0] > bonus)) { bonus = f->val[0]; } } // try to disarm it if (skillcheck(user, SC_DISARM, trapflag->val[0], bonus)) { if (trapflag->id == F_TRAP) { // ie. trapped cell getobname(trapob, buf, 1); removeob(trapob, trapob->amt); } else { objecttype_t *ot; ot = findot(trapflag->val[0]); snprintf(buf, BUFLEN, "%s %s", needan(ot->name) ? "an" : "a", ot->name); // ie. trapped object killflag(trapflag); } if (isplayer(user)) { msg("You disable %s.",buf); } else if (cansee(player, user)) { msg("%s disables %s.",username, buf); } if (isplayer(user) && hasjob(user, J_ROGUE)) { gainxp(user, trapflag->val[0]); } practice(user, SK_ENGINEERING, 1); } else { int trapgoesoff = B_FALSE; // failed. another check to see if it goes off if (trapflag->id == F_TRAP) { getobname(trapob, buf, 1); if ((trapflag->val[1] == B_TRUE) && !skillcheck(user, SC_DISARM, trapflag->val[0], 0)) { trapgoesoff = B_TRUE; } } else { strcpy(buf, "the trap"); if (!skillcheck(user, SC_DISARM, trapflag->val[0], 0)) { trapgoesoff = B_TRUE; } } if (trapgoesoff) { if (isplayer(user)) { msg("Oops - you trigger %s!",buf); } else if (cansee(player, user)) { msg("%s triggers %s!",username, buf); } if (trapflag->id == F_TRAP) { // an actual trap trapeffects(trapob, trapob->type->id, user->cell, (trapflag->id == F_TRAPPED) ? trapob : NULL); } else { // a trap on an object triggerattachedtraps(trapob, user, B_NOANNOUNCE); //trapeffects(NULL, trapflag->val[0], user->cell, //(trapflag->id == F_TRAPPED) ? trapob : NULL); } } else { getobname(trapob, buf, 1); if (isplayer(user)) { msg("You fail to disable %s.",buf); } else if (cansee(player, user)) { msg("%s fails to disable %s.",username, buf); } } } } else if (abilid == OT_A_DISARMLF) { object_t *wep,*targetwep; int skillmod = 0,targetskillmod = 0; // ask for direction if (!targcell) { int dir; dir = askdir("Disarm in which direction (- to cancel)", B_TRUE, B_FALSE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else if (dir == D_MYSELF) { if (isplayer(user)) msg("You can't disarm yourself!"); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } if (!haslos(user, targcell)) { if (isplayer(user)) msg("You can't see there!"); return B_TRUE; } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to disarm!"); return B_TRUE; } getlfname(target, targetname); targetwep = getweapon(target); if (!targetwep) { targetwep = getequippedob(target->pack, BP_SECWEAPON); if (!targetwep) { if (isplayer(user)) msg("%s has no weapon!", targetname); return B_TRUE; } } wep = getweapon(user); // try to disarm them... taketime(user, getactspeed(user)); if (wep) { skillmod = getweaponskill(user, wep) * 10; if (isplayer(user) && (skillmod == 0)) skillmod = -25; } else { skillmod = getskill(user, SK_UNARMED) * 10; } targetskillmod = getweaponskill(target, targetwep) * 10; if (isplayer(target) && (targetskillmod == 0)) targetskillmod = -25; if (skillcheckvs(user, SC_DEX, skillmod, target, SC_DEX, targetskillmod)) { if (cansee(player, user)) { msg("^w%s disarm%s %s!",username, isplayer(user) ? "" : "s", targetname); } // calling drop() will cause the target to take some time - this will normally give // free hit in before they can pick it back up. drop(targetwep, ALL); } else { if (isplayer(user)) { msg("You try to disarm %s, but fail.",targetname); } else if (cansee(player, user)) { msg("%s tries to disarm %s, but fail.",username, targetname); } } } else if (abilid == OT_A_DISMANTLE) { objecttype_t *newot = NULL; int numnew = 0; int dir; cell_t *c; object_t *o; char obname[BUFLEN]; enum SKILLLEVEL slev; if (!isplayer(user)) return B_TRUE; if (isimmobile(user)) { if (isplayer(user)) msg("You can't move!"); return B_TRUE; } if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't dismantle anything while swimming!"); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't dismantle anything while stuck!"); return B_TRUE; } slev = getskill(user, SK_ENGINEERING); dir = askdir("Dismantle obstacle in which direction (- to cancel)", B_TRUE, B_FALSE); if (dir == D_NONE) { msg("Cancelled."); return B_TRUE; } else if (dir == D_MYSELF) { c = user->cell; } c = getcellindir(user->cell, dir); // antything there? o = hasobwithflag(c->obpile, F_CLIMBOBSTACLE); if (c->type->solid) { if (slev >= PR_ADEPT) { // you can try to dismantle walls. digcell() will // check whether it is actually diggable. } else { msg("There is nothing you can dismantle there."); return B_TRUE; } } else if (!o) { if (slev >= PR_ADEPT) { // you can dismantle doors too... o = hasdoor(c); } if (!o) { msg("There is nothing you can dismantle there."); return B_TRUE; } } if (!o) { // ie trying to dismantle a wall return digcell(user, c, NULL, B_TRUE); } // figrue out what to replace it with switch (o->material->id) { case MT_STONE: newot = findot(OT_STONE); break; case MT_METAL: newot = findot(OT_IRONSTAFF); break; case MT_WOOD: newot = findot(OT_WOODPLANK); break; default: break; } if (newot) { int origsize,junksize; // figure out how many origsize = getobsize(o); junksize = newot->size; numnew = origsize / junksize; limit(&numnew, 1, 10); } getobname(o, obname, 1); removeob(o, 1); if (numnew && newot) { char newobname[BUFLEN]; snprintf(newobname, BUFLEN, "%d %s", numnew, newot->name); addob(c->obpile, newobname); } taketime(user, getactspeed(user)*2); msg("You dismantle %s.", obname); } else if (abilid == OT_A_DRAGUNDERGROUND) { // announce if (cansee(player, target)) { msg("%s drags %s underground.", username, targetname); } addflag(target->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); addflag(target->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); target->hp = 0; killallobs(target->pack); } else if (abilid == OT_A_FLY) { flag_t *f; // already flying? stop. f = isflyingwithwings(user); if (f) { killflag(f); taketime(user, getactspeed(user)); return B_FALSE; } if (isimmobile(user)) { if (isplayer(user)) msg("You can't move!"); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't start flying while stuck!"); return B_TRUE; } else if (isburdened(user)) { if (isplayer(user)) msg("You can't flying while burdened!"); return B_TRUE; } // start flying addtempflag(user->flags, F_FLYING, getnaturalflightheight(user), NA, NA, NULL, FROMABIL); taketime(user, getactspeed(user)); } else if (abilid == OT_A_FEIGNDEATH) { lifeform_t *lf; if (hasflag(user->flags, F_FEIGNINGDEATH)) { if (isplayer(user)) msg("You are already feigning death!"); return B_TRUE; } taketime(user, getactspeed(user)); if (isplayer(user)) { msg("You drop to the ground."); } else if (cansee(player, user)) { if (target) { char targname[BUFLEN]; getlfname(target, targname); if (isplayer(target)) { msg("^gYou kill %s!", username); } else { msg("%s kills %s.", targname, username); } } else { msg("%s dies.", username); } } if (!isprone(user)) { addflag(user->flags, F_PRONE, B_TRUE, NA, NA, NULL); } addflag(user->flags, F_FEIGNINGDEATH, B_TRUE, NA, NA, NULL); // anyone attacking you stops for (lf = user->cell->map->lf ; lf ; lf = lf->next) { flag_t *f; char buf[BUFLENTINY]; f = hasflagval(lf->flags, F_TARGETLF, user->id, NA, NA, NULL); if (f) killflag(f); f = hasflagval(lf->flags, F_TARGETCELL, user->cell->x, user->cell->y, NA, NULL); if (f) killflag(f); snprintf(buf, BUFLENTINY, "%d\n",user->id); f = hasflagval(lf->flags, F_TARGETCELL, NA, NA, MR_LF, buf); if (f) killflag(f); } needredraw = B_TRUE; statdirty = B_TRUE; } else if (abilid == OT_A_FLIP) { // ask for direction if (!targcell) { int dir; dir = askdir("Flip from which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_MYSELF) { if (isplayer(user)) msg("You can't flip yourself!"); return B_TRUE; } else if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to flip!"); return B_TRUE; } getlfname(target, targetname); if (getlfsize(target) > getlfsize(user)) { if (isplayer(user)) msg("%s is too large for you to flip.", targetname); return B_TRUE; } taketime(user, getactspeed(user)); if (lfhasflag(target, F_NONCORPOREAL)) { if (isplayer(user)) { msg("You grab at %s%s insubstantial body.", targetname, getpossessive(targetname)); } else if (cansee(player, user)) { msg("%s grabs at %s%s insubstantial body.", username, targetname, getpossessive(targetname)); } return B_FALSE; } // if not already held, victim gets a skillcheck to avoid being grabbed - hard. if (!lfhasflagval(user, F_GRABBING, target->id, NA, NA, NULL) && skillcheck(target, SC_DODGE, getattr(user, A_AGI)+50, 0)) { if (cansee(player, user)) { msg("%s evade%s %s%s grasp.", targetname, isplayer(target) ? "" : "s", username, getpossessive(username)); } } else { int dir; cell_t *c; // free space behind the flipper? dir = diropposite(user->facing); c = getcellindir(user->cell, dir); if (c && cellwalkable(target, c, NULL)) { int dist; // break grabs, but don't annouce "you break free" breakgrabs(target, B_TRUE, B_TRUE, B_FALSE); // throw! // - announce BEFORE moving the target. if (isplayer(user)) { msg("^%cYou flip %s over your head!", getlfcol(target, CC_BAD), targetname); } else if (cansee(player, user)) { if (hasbp(user, BP_HEAD)) { char yr[BUFLEN]; strcpy(yr, your(user)); yr[0] = tolower(yr[0]); msg("^%c%s flips %s over %s %s!", getlfcol(target, CC_BAD), username, targetname, yr, getbodypartname(user, BP_HEAD)); } else { msg("^%c%s flips %s!", getlfcol(target, CC_BAD), username, targetname); } } setfacing(user, dir); // turn player to face them movelf(target, c, B_FALSE); // move target behind player dist = 2 + (getskill(user, SK_ATHLETICS)/3); setfacing(target, getrandomdir(DT_COMPASS)); knockback(target, dir, dist, user, 125, B_NOANNOUNCE, B_DODAM); } else { if (isplayer(user)) { msg("There is no room to flip %s!", targetname); } else if (cansee(player, user)) { msg("%s tries to flip %s but fails.", username, targetname); } } } } else if (abilid == OT_A_FLURRY) { int dir; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't make an attack flurry traps while swimming!"); return B_TRUE; } if (hasjob(user, J_MONK)) { if (getweapon(user)) { if (isplayer(user)) msg("You need be unarmed to perform an attack flurry!"); return B_TRUE; } } else if (!isdualweilding(user)) { if (isplayer(user)) msg("You need to be dual-weilding to perform an attack flurry!"); return B_TRUE; } // ask for direction if (targcell) { dir = getdirtowards(user->cell, targcell, NULL, B_FALSE, DT_ORTH); } else { dir = askdir("Flurry in which direction (- to cancel)", B_TRUE, B_TRUE); if ((dir == D_NONE) || (dir == D_MYSELF)) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to attack!"); return B_TRUE; } getlfname(target, targetname); // announce if (!isplayer(user) && cansee(player, user)) { msg("%s performs a flurry of fast attacks!", username); } // push them back knockback(target, dir, 1, user, 25, B_DOANNOUNCE, B_NODAM); // if we succeeded in pushing them... if (target->cell != targcell) { movelf(user, targcell, B_FALSE); } // now attack them attackcell(user, target->cell, B_TRUE); } else if (abilid == OT_A_FULLSHIELD) { object_t *shield[MAXPILEOBS],*selshield = NULL; int checkmod[MAXPILEOBS]; int nshields,i; char shtext[BUFLEN]; if (isimmobile(user)) { if (isplayer(user)) msg("You can't do that while stuck!"); return B_TRUE; } if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't do that while swimming!"); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't do that while stuck!"); return B_TRUE; } // stop fullshielding. if (lfhasflag(user, F_FULLSHIELD)) { killflagsofid(user->flags, F_FULLSHIELD); taketime(user, getactspeed(user)); return B_FALSE; } getallshields(user, DT_BASH, shield, checkmod, &nshields); for (i = 0; i < nshields; i++) { if (isshield(shield[i])) { selshield = shield[i]; break; } } if (!selshield) { if (isplayer(user)) msg("You need to equip a shield first!"); return B_TRUE; } snprintf(shtext, BUFLEN, "%ld", selshield->id); addflag(user->flags, F_FULLSHIELD, NA, NA, NA, shtext); taketime(user, getactspeed(user)); } else if (abilid == OT_A_GRAB) { flag_t *f; f = lfhasflag(user, F_GRABBING); if (f) { if (isplayer(user)) msg("You are already holding someone!"); return B_TRUE; } f = lfhasflag(user, F_GRABBEDBY); if (f) { if (isplayer(user)) msg("You are already being held by someone!"); return B_TRUE; } // ask for direction if (!targcell) { int dir; dir = askdir("Grab in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else if (dir == D_MYSELF) { if (isplayer(user)) msg("You can't grab yourself!"); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to grab!"); return B_TRUE; } taketime(user, getactspeed(user)); getlfname(target, targetname); if (lfhasflag(target, F_NONCORPOREAL)) { if (isplayer(user)) { msg("You grab at %s%s insubstantial body.", targetname, getpossessive(targetname)); } else if (cansee(player, user)) { msg("%s grabs at %s%s insubstantial body.", username, targetname, getpossessive(targetname)); } return B_FALSE; } // victim gets a skilcheck to avoid being grabbed if (skillcheck(target, SC_DODGE, (getattr(user, A_AGI))+50, 0)) { if (cansee(player, user)) { msg("%s evade%s %s%s grasp.", targetname, isplayer(target) ? "" : "s", username, getpossessive(username)); } } else { addflag(user->flags, F_GRABBING, target->id, NA, NA, NULL); addflag(target->flags, F_GRABBEDBY, user->id, NA, NA, NULL); // damage? if (strlen(damstr)) { losehp(target, roll(damstr), DT_CRUSH, user, killername); } } } else if (abilid == OT_A_CRUSH) { int dam = 0; if (isimmobile(user)) { if (isplayer(user)) msg("You cannot move!"); return B_TRUE; } // announce if (cansee(player, target)) { msg("%s squeeze%s %s tightly!", username, isplayer(user) ? "" : "s", targetname); } // victim gets a skilcheck to avoid damage... if (skillcheckvs(target, SC_STR, 0, user, SC_STR, 5)) { if (cansee(player, target)) { // broke free killflagsofid(target->flags, F_GRABBEDBY); killflagsofid(user->flags, F_GRABBING); } } else { int str; // fixed damage? if (strlen(damstr)) { dam = roll(damstr); } else { // determine damage baesd on crusher's strength str = getattr(user, A_STR)/10; dam = modifybystat(str, user, A_STR); } // announce if (cansee(player, target)) { msg("%s %s being crushed!", targetname, is(target)); } losehp(target, dam, DT_CRUSH, user, killername); } taketime(user, getactspeed(user)); } else if (abilid == OT_A_JUMP) { lifeform_t *victim = NULL; char victimname[BUFLEN]; int dodged = B_FALSE; cell_t *origcell; int maxrange = 2; object_t *o; int swapped = B_FALSE; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't jump while swimming!"); return B_TRUE; } else if (isairborne(user, NULL)) { if (isplayer(user)) msg("You can't jump while airbourne!"); return B_TRUE; } else if (hasflag(user->flags, F_GRAVBOOSTED)) { if (isplayer(user)) msg("You can't jump with gravity boosted around you!"); return B_TRUE; } else if (lfhasflag(user, F_GRABBING)) { if (isplayer(user)) msg("You can't jump while holding someone!"); return B_TRUE; } else if (lfhasflag(user, F_GRABBEDBY)) { if (isplayer(user)) msg("You can't jump while being held!"); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't jump while stuck!"); return B_TRUE; } if (hasflag(user->flags, F_GRAVLESSENED)) { maxrange++; } if (!targcell) { snprintf(buf, BUFLEN, "Jump where (max distance %d)?", maxrange); while (!targcell) { // ask where targcell = askcoords(buf, "Jump->", TT_NONE, user, maxrange, LOF_DONTNEED, B_TRUE); if (!targcell) { return B_TRUE; } else if (getcelldist(user->cell, targcell) > maxrange) { targcell = NULL; if (isplayer(user)) { snprintf(buf, BUFLEN, "You can't jump that far! Jump where (max distance %d)?", maxrange); } } else if (!haslos(user, targcell)) { targcell = NULL; if (isplayer(user)) { snprintf(buf, BUFLEN, "You can't see where to land! Jump where (max distance %d)?", maxrange); } } } } if (iswoozy(user)) { cell_t *retcell[MAXCANDIDATES]; int nretcells; // change destination. getradiuscells(user->cell, maxrange, DT_COMPASS, B_FALSE, LOF_NEED, B_FALSE, retcell, &nretcells, B_FALSE); targcell = retcell[rnd(0,nretcells-1)]; } if (isburdened(user)) { if (isplayer(user)) { msg("Your load is too heavy to jump!"); } return B_TRUE; } else if (lfhasflag(user, F_GRAVBOOSTED)) { if (isplayer(user)) { msg("Gravity around you is too strong to jump in!"); } return B_TRUE; } // did you land on something impassable? for (o = targcell->obpile->first ; o ; o = o->next) { if (isimpassableob(o, user, getlfsize(user))) { char obname[BUFLEN]; getobname(o, obname, o->amt); if (isplayer(user)) { msg("There %s %s in the way!", (o->amt == 1) ? "is" : "are", obname); } return B_TRUE; } } // we now have a cell - go there! taketime(user, getactspeed(user)); if (!targcell || (targcell == user->cell)) { if (isplayer(user)) { msg("You jump on the spot."); } else if (cansee(player, user)) { msg("%s jumps on the spot.", username); } return B_FALSE; } origcell = user->cell; // did you land on anyone? victim = haslf(targcell); if (victim) { int acc; getlfname(victim,victimname); // see if you actually landed on them or they dodge... acc = 100 - (getevasion(victim)*2); if (rnd(1,100) > acc) { // dodged! dodged = B_TRUE; } } // announce if (isplayer(user)) { if (victim && !dodged) { msg("You leap high in the air onto %s!",victimname); } else { msg("You leap high in the air!"); } } else if (haslos(player, origcell)) { if (cansee(player, user)) { if (victim && !dodged) { msg("%s leaps high in the air onto %s!", username,victimname); } else { msg("%s leaps high in the air and lands nearby!", username); } } else { msg("%s leaps high in the air!", username); } } else if (cansee(player, user)) { if (victim && !dodged) { msg("%s drops from the air onto %s!", username,victimname); } else { msg("%s drops from the air and lands nearby!", username); } } if (victim) { cell_t *c; getlfname(victim,victimname); // move them out of the way c = getrandomadjcell(victim->cell, &ccwalkable, B_NOEXPAND); // nowhere to move? move to where the lf jumped from! if (c) { movelf(victim, c, B_FALSE); } else { swapplaces(victim, user, B_CHANGEDIR, B_CHANGEDIR, B_NOTONPURPOSE); swapped = B_TRUE; } } if (!swapped) { movelf(user, targcell, B_FALSE); } // splash? for (o = targcell->obpile->first ; o ; o = o->next) { if ((o->material->id == MT_WATER) && (o->type->id != OT_PUDDLEWATER)) { addobburst(targcell, 1, DT_COMPASS, "small puddle of water", NULL, LOF_NEED); noise(targcell, NULL, NC_OTHER, SV_TALK, "a splash.", "Splash!"); break; } } if (victim) { if (dodged) { if (cansee(player, user)) { msg("%s dodges out of the way!", victimname); } } else { // both victim and attacker fall if (!isdead(victim)) fall(victim, NULL, B_TRUE); fall(user, NULL, B_TRUE); } } practice(user, SK_ATHLETICS, 1); } else if (abilid == OT_A_PICKLOCK) { lockpick(user, NULL, NULL, NULL); } else if (abilid == OT_A_REFLEXDODGE) { // stopping? if (killflagsofid(user->flags, F_DODGES)) { return B_FALSE; } addflag(user->flags, F_DODGES, B_TRUE, NA, NA, NULL); } else if (abilid == OT_A_RAGE) { int howlong; f = lfhasflag(user, F_RAGE); if (f) { if (isplayer(user)) { msg("You are already enraged!"); } return B_TRUE; } howlong = DEF_RAGETIME; enrage(user, howlong); } else if (abilid == OT_A_REPAIR) { object_t *o; enum MATERIAL repairablemats[MAXCANDIDATES]; int cutoffpct[MAXCANDIDATES]; int nmats = 0; int i; // get list of repairable materials getworkablematerials(user, SK_METALWORK, repairablemats, cutoffpct, &nmats); getworkablematerials(user, SK_SEWING, repairablemats, cutoffpct, &nmats); // 1.compile a list of repairable objects // sk_armour lets you repair armour up to xx% (depends on skill) initprompt(&prompt, "Repair which object?"); addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL); addchoice(&prompt, ',', "All", "All", NULL, NULL); for (o = user->pack->first ; o ; o = o->next) { int ok = B_FALSE; int cutoff = 0; for (i = 0; i < nmats; i++) { if (o->material->id == repairablemats[i]) { ok = B_TRUE; cutoff = cutoffpct[i]; break; } } if (ok && isdamaged(o)) { float pct; f = hasflag(o->flags, F_OBHP); pct = ((float)f->val[0] /(float) f->val[1]) * 100; if (pct >= cutoff) { char buf[BUFLEN],desc[BUFLEN]; getobname(o, buf, o->amt); snprintf(desc, BUFLEN, "%s",buf); // we can repair this object addchoice(&prompt, o->letter, desc, desc, o, NULL); } } } if (prompt.nchoices <= 2) { if (isplayer(user)) { msg("You don't have anything which you are able to repair."); } return B_TRUE; } // 2. ask which ones to repair (or ALL) if (isplayer(user)) { char ch; ch = getchoice(&prompt); if (ch == ',') { // all int n; for (n = 0 ; n < prompt.nchoices; n++) { o = (object_t *)prompt.choice[n].data; if (o) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%ld", o->id); addflag(user->flags, F_REPAIRING, NA, NA, NA, buf); } } o = NULL; } else { o = (object_t *) prompt.result; if (!o) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } } } else { // pick a random one (not 'cancel' or 'all') o = (object_t *) prompt.choice[rnd(2,prompt.nchoices-1)].data; if (!o) return B_TRUE; } if (o) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "%ld", o->id); addflag(user->flags, F_REPAIRING, NA, NA, NA, buf); getobname(o, buf, o->amt); if (isplayer(user)) { msg("You start repairing %s...", buf); } else if (cansee(player, user)) { char lfname[BUFLEN]; getlfname(user, lfname); msg("%s starts repairing %s...", lfname, buf); } } else { // repairing all... if (isplayer(user)) { msg("You start repairing your equipment..."); } else if (cansee(player, user)) { char lfname[BUFLEN]; getlfname(user, lfname); msg("%s starts repairing its equipment...", lfname); } } } else if (abilid == OT_A_RESIZE) { object_t *o; enum MATERIAL repairablemats[MAXCANDIDATES]; enum LFSIZE wantsize,origsize; int cutoffpct[MAXCANDIDATES]; int i,nmats = 0; char ch,obname[BUFLEN]; if (!isplayer(user)) { return B_TRUE; } // get list of resizable materials if (getskill(user, SK_METALWORK) >= PR_BEGINNER) { getworkablematerials(user, SK_METALWORK, repairablemats, cutoffpct, &nmats); } if (getskill(user, SK_SEWING) >= PR_BEGINNER) { getworkablematerials(user, SK_SEWING, repairablemats, cutoffpct, &nmats); } // 1.compile a list of resizable objects initprompt(&prompt, "Resize which object?"); addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL); for (o = user->pack->first ; o ; o = o->next) { int ok = B_FALSE; for (i = 0; i < nmats; i++) { if (o->material->id == repairablemats[i]) { ok = B_TRUE; break; } } if (ok && hasflag(o->flags, F_MULTISIZE)) { getobname(o, obname, o->amt); // we can resize this object addchoice(&prompt, o->letter, obname, NULL, o, NULL); } } prompt.maycancel = B_TRUE; if (prompt.nchoices <= 1) { msg("You don't have anything which you are able to resize."); return B_TRUE; } // 2. ask which ones to resize (or ALL) getchoice(&prompt); o = (object_t *) prompt.result; if (!o) { msg("Cancelled."); return B_TRUE; } getobname(o, obname, 1); origsize = getarmoursize(o); // ask what size we want. snprintf(buf, BUFLEN, "Resize %s to what size?", obname); initprompt(&prompt, buf); for (i = SZ_MEDIUM; i <= SZ_LARGE; i++) { if (i != origsize) { switch (i) { case SZ_MEDIUM: addchoice(&prompt, 's', "half-sized", NULL, NULL, NULL); break; case SZ_HUMAN: addchoice(&prompt, 'm', "human-sized", NULL, NULL, NULL); break; case SZ_LARGE: addchoice(&prompt, 'l', "giant-sized", NULL, NULL, NULL); break; } } } addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL); ch = getchoice(&prompt); switch (ch) { case 's': wantsize = SZ_MEDIUM; break; case 'm': wantsize = SZ_HUMAN; break; case 'l': wantsize = SZ_LARGE; break; default: msg("Cancelled."); return B_TRUE; } // in case it's on fire, etc if (touch(user, o)) { taketime(user, getactspeed(user)); return B_FALSE; } // resize it! resizeobject(o, wantsize); f = hasflag(o->flags, F_OBHP); if (f) { f->val[1] /= 2; if (f->val[1] < 1) f->val[1] = 1; f->val[0] = f->val[1]; } msg("You %s the size of your %s.", (wantsize > origsize) ? "increase" : "reduce", noprefix(obname)); getobname(o, obname, 1); more(); msgnocap("%c - %s", o->letter, obname); practice(user, SK_METALWORK, 1); practice(user, SK_SEWING, 1); // TODO: make this like eating/resting/etc ? taketime(user, getactspeed(user)); } else if (abilid == OT_A_SCREAM) { lifeform_t *lf; if (lfhasflag(user, F_SILENCED)) { if (isplayer(user)) msg("You are unable to make a sound!"); return B_TRUE; } if (!validatespellcell(user, &targcell,TT_MONSTER, abilid, power, B_FALSE)) return B_TRUE; makenoise(user, N_DEAFENSCREAM); // adjacent lfs must pass a magic resistance check or die for (lf = user->cell->map->lf ; lf ; lf = lf->next) { if (lf != user) { int nwalls; if (canhear(lf, user->cell, SV_PLANE, &nwalls) && isadjacent(lf->cell, user->cell)) { if ((nwalls == 0) && !lfhasflag(lf, F_DEAF)) { addtempflag(lf->flags, F_DEAF, B_TRUE, NA, NA, NULL, rnd(30,50)); } } } } } else if (abilid == OT_A_SHIELDBASH) { object_t *shield = NULL; flag_t *f; int badshield = B_FALSE; int shpenalty,mod; object_t *shieldlist[MAXPILEOBS]; int nshields,i; char shname[BUFLEN], tname[BUFLEN]; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You lack the stability for a shield bash while swimming."); return B_TRUE; } getallshields(user, DT_ALL, shieldlist, NULL, &nshields); for (i = 0; i < nshields; i++) { if (isshield(shieldlist[i])) { shield = shieldlist[i]; break; } } if (!shield) { badshield = B_TRUE; } if (badshield) { if (isplayer(user)) msg("You need a shield equipped to perform a shield bash!"); return B_TRUE; } getobname(shield, shname, 1); f = hasflagval(shield->flags, F_EQUIPCONFER, F_SHIELDPENALTY, NA, NA, NULL); if (f) { shpenalty = adjustshieldpenalty(user, f->val[1]); } else { shpenalty = 0; } if (shpenalty) { if (isplayer(user)) msg("Your %s is too cumbersome to bash with.",shname); return B_TRUE; } // ask for direction if (!targcell) { int dir; dir = askdir("Shield bash in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_MYSELF) { // yourself! targcell = user->cell; } else if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to attack!"); return B_TRUE; } getlfname(target, tname); if (isplayer(user)) { msg("You bash %s with your %s!",tname, noprefix(shname)); } else if (cansee(player, user)) { msg("%s bashes %s with %s!",username, tname, shname); } // only works if shield is hard if (gethardness(shield->material->id)) { int damamt; // victim takes a little bit of damage damamt = getstrdammod(user); if (damamt > 0) { char damstr[BUFLEN]; snprintf(damstr, BUFLEN, "%s%s shield bash", username, getpossessive(username)); losehp(target, damamt, DT_BASH, user, damstr); } if (!isdead(target)) { // stun success depends on shield skill, relative lf size, target's evasion mod = 0; modifyforsize(&mod, user, target, 10, M_VAL); if (mod > 0) mod = 0; if (skillcheck(user, SC_SHIELDBLOCK, 25 + getevasion(target), mod)) { stun(target, 3); } } } if (!touch(target, shield)) { // if the victim touching the shield didn't destroy it, the shield gets damaged takedamage(shield, roll("1d3"), DT_DIRECT, user); } } else if (abilid == OT_A_SNATCH) { object_t *o = NULL; flag_t *f; char obname[BUFLEN]; if (!targcell) { int dir; // ask which dir dir = askdir("Snatch from which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } if (targcell->obpile->first) { if (isplayer(user)) { // select object from cell... o = askobject(targcell->obpile, "Snatch which object", NULL, NULL, '\0', NULL, B_FALSE); } else { object_t *oo; for (oo = targcell->obpile->first; oo ; oo = oo->next) { if (canpickup(user, oo, 1) && aiwants(user, oo, NULL)) { o = oo; break; } } } } else { if (isplayer(user)) msg("There is nothing there to snatch!"); return B_TRUE; } if (!o) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } getobname(o, obname, 1); // spell doesn't take any time, using an object does. if (fromob) { if (cansee(player, user)) { msg("%s%s %s wraps around %s.", username, getpossessive(username), noprefix(fromobname), obname); } } else { // setting v0 to spellid just in case pickup() changes flags addflag(user->flags, F_NOTIME, OT_A_SNATCH, NA, NA, NULL); } if (!pickup(user, o, 1, B_TRUE, B_FALSE)) { if (isplayer(user)) { msg("You snatch %s from the ground!",obname); } else if (cansee(player, user)) { msg("%s snatches %s from the ground!",username, obname); } } f = lfhasflagval(user, F_NOTIME, OT_A_SNATCH, NA, NA, NULL); if (f) killflag(f); } else if (abilid == OT_A_SONICBOLT) { int volume,nwalls; if (lfhasflag(user, F_SILENCED)) { if (isplayer(user)) msg("You are unable to make a sound!"); return B_TRUE; } if (!validatespellcell(user, &targcell,TT_MONSTER, abilid, power, B_FALSE)) return B_TRUE; target = targcell->lf; makenoise(user, N_SONICBOLT); // if target can hear, they take damage f = hasflagval(user->flags, F_NOISETEXT, N_SONICBOLT, NA, NA, NULL); if (f) { volume = f->val[1]; } else { volume = power; } if (target && canhear(target, user->cell, volume, &nwalls)) { if (nwalls == 0) { if (isplayer(target)) { msg("Pain shoots through your eardrums!"); } losehp(target, roll(damstr), DT_SONIC, user, "a bolt of sound"); } } } else if (abilid == OT_A_SPRINT) { flag_t *f; f = lfhasflag(user, F_SPRINTING); if (f) { // stop sprinting. killflag(f); return B_FALSE; } if (lfhasflagval(user, F_INJURY, IJ_WINDPIPECRUSHED, NA, NA, NULL)) { if (isplayer(user)) msg("You can't sprint with a crushed windpipe."); return B_TRUE; } if (hasinjuredbp(user, BP_LEGS)) { if (isplayer(user)) msg("You can't sprint with injured legs."); return B_TRUE; } if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't sprint while swimming!"); return B_TRUE; } if (isairborne(user, NULL)) { if (isplayer(user)) msg("You can't sprint while airborne!"); return B_TRUE; } if (isburdened(user)) { if (isplayer(user)) { msg("You cannot sprint while burdened."); } return B_TRUE; } if (isplayer(user)) { int dir; dir = askdir("Sprint in which direction (- to cancel)", B_TRUE, B_TRUE); if ((dir == D_NONE) || (dir == D_MYSELF)) { msg("Cancelled."); return B_TRUE; } setfacing(user, dir); } addflag(user->flags, F_SPRINTING, B_TRUE, NA, NA, NULL); practice(user, SK_ATHLETICS, 1); killflagsofid(user->flags, F_HIDING); } else if (abilid == OT_A_STINGACID) { validateabillf(user, abilid, &target); if (!target) return B_TRUE; getlfname(target, targetname); if (cansee(player, target)) { msg("%s inject%s acid into %s!", username, isplayer(user) ? "" : "s", targetname); } losehp(target, roll(damstr), DT_ACID, user, killername); if (!lfhasflagval(target, F_PAIN, DT_ACID, NA,NA, NULL) && !isundead(target)) { int howlong = 2; if (power > 0) { howlong = power * 2; } // cause ongoing pain for power*2 turns addtempflag(target->flags, F_PAIN, DT_ACID, NA, NA, damstr, howlong); } taketime(user, getactspeed(user)); } else if (abilid == OT_A_STUDYSCROLL) { object_t *o; int difficulty, mod; flag_t *f; // only players can do this if (!isplayer(user)) { return B_TRUE; } if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("That wouldn't be a good idea while swimming."); return B_TRUE; } if (!haslos(user, user->cell)) { msg("You can't study anything, since you can't see!"); return B_TRUE; } // ask what to inspect initprompt(&prompt, "Study which scroll?"); prompt.maycancel = B_TRUE; for (o = user->pack->first ; o ; o = o->next) { if ((o->type->obclass->id == OC_SCROLL) && isknown(o)) { f = hasflag(o->flags, F_LINKSPELL); if (f && !cancast(user, f->val[0], NULL)) { char buf2[BUFLEN]; getobname(o, buf, o->amt); difficulty = 100 + (getspelllevel(f->val[0])*10); mod = getspellskill(user, f->val[0]) * 10; //pct = getskillcheckchance(user, SC_LEARNMAGIC, difficulty, mod); snprintf(buf2, BUFLEN, "%s (%d%% success chance)", buf, difficulty-mod); addchoice(&prompt, o->letter, buf2, NULL, f, NULL); } } } if (!prompt.nchoices) { msg("You have no scrolls which you need to study."); return B_TRUE; } getchoice(&prompt); f = (flag_t *)prompt.result; if (!f) { msg("Cancelled"); return B_TRUE; } o = f->pile->ob; // try to transcribe it... difficulty = 100 + (getspelllevel(f->val[0])*10); mod = getspellskill(user, f->val[0]) * 10; if (skillcheck(user, SC_LEARNMAGIC, difficulty, mod)) { addflag(user->flags, F_CANCAST, f->val[0], NA, NA, NULL); } else { // failed! msg("You fail to comprehend this spell."); } taketime(user, getactspeed(user)); // now kill the scroll msg("The scroll crumbles to dust."); removeob(o, 1); } else if (abilid == OT_A_STRIKETOKO) { object_t *wep; wep = getweapon(user); if (wep) { skill_t *sk; sk = getobskill(wep->flags); if (!sk || (sk->id != SK_CLUBS)) { if (isplayer(user)) msg("You can only use bashing weapons to fight mercifully."); return B_TRUE; } } if (lfhasflag(user, F_STRIKETOKO)) { killflagsofid(user->flags, F_STRIKETOKO); } else { addflag(user->flags, F_STRIKETOKO, B_TRUE, NA, NA, NULL); } // note: takes no time. } else if (abilid == OT_A_SUCKBLOOD) { int dam = 0; f = lfhasflag(user, F_ATTACHEDTO); if (!f) { if (isplayer(user)) msg("You need to attach to someone before using this ability."); return B_TRUE; } // announce if (cansee(player, target)) { msg("^%c%s suck%s blood from %s!", getlfcol(target, CC_BAD), username, isplayer(user) ? "" : "s", targetname); } // fixed damage? if (strlen(damstr)) { dam = roll(damstr); } else { dam = 1; } losehp(target, dam, DT_DIRECT, user, killername); taketime(user, getactspeed(user)); // special case if (!isplayer(user)) { if ((user->race->id == R_STIRGE) || (user->race->id == R_LEECH)) { int satedat = 8; int num; num = modcounter(user->flags, dam); switch (user->race->id) { default: case R_STIRGE: satedat = 6; break; case R_LEECH: satedat = 8; break; } if (num >= satedat) { // sated. killflagsofid(user->flags, F_ATTACHEDTO); makepeaceful(user, NULL); addflag(user->flags, F_FLEEONDAM, B_TRUE, NA, NA, NULL); addflag(user->flags, F_DIESPLATTER, 3, NA, NA, "splash of blood"); // counter will keep ticking up. // once it gets past next threshold, monster will go to sleep. modcounter(user->flags, rnd(1,4)); } } } } else if (abilid == OT_A_SWALLOW) { object_t *o = NULL,*nexto; int nswallowed = 0,dodged = B_FALSE; if (!targcell) { int dir; // ask which dir dir = askdir("Swallow in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } // take some time taketime(user, getactspeed(user)); for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; if (!hasflag(o->flags, F_NOPICKUP)) { // TODO: move to new map instead? killob(o); nswallowed++; } } target = targcell->lf; if (target) { if (skillcheck(target, SC_DODGE, 100, 0)) { dodged = B_TRUE; } } if (target) { char targname[BUFLEN]; getlfname(target, targname); if (dodged) { if (isplayer(user)) { msg("^WYou try to swallow %s, but miss.", targname); } else if (cansee(player, user) || isplayer(target)) { msg("^W%s tries to swallow %s, but misses!", username, targname); } } else { if (isplayer(user)) { msg("^WYou swallow %s!", targname); } else if (cansee(player, user) || isplayer(target)) { msg("^W%s swallows %s!", username, targname); } } } else { if (isplayer(user)) { msg("You swallow some %s!", (nswallowed || target) ? "objects" : "air"); } else if (cansee(player, user)) { msg("%s swallows some %s!", username, (nswallowed || target) ? "objects" : "air"); } } if (target) { if (isplayer(user)) { // just kill the target killlf(target); } else { // move the target into the swallower's stomach map_t *newmap; cell_t *c,*entry; flag_t *lflinkflag; int x,y; condset_t cs; lflinkflag = hasflag(user->flags, F_MAPLINK); if (lflinkflag) { newmap = findmap(lflinkflag->val[0]); } else { region_t *r; // create and move to new "worm" map. // this map will be destroyed when you leave it. r = findregionbytype(BH_STOMACH); if (!r) { r = addregion(BH_STOMACH, NULL, -1, 0, user->cell->map->id); } // create stomach map newmap = addmap(); createmap(newmap, 1, r, targcell->map, D_NONE, NULL); addflag(newmap->flags, F_STOMACHOF, user->id, NA, NA, username); } // find a random empty cell here //entry = getrandomroomcell(newmap, ANYROOM, WE_WALKABLE); initcondv(&cs, CC_ISROOM, B_TRUE, NA, CC_WALKABLEFOR, B_TRUE, target->id, CC_NONE); entry = getcell_cond(newmap, &cs); /* while (!cellwalkable(target, entry, NULL)) { //entry = getrandomroomcell(newmap, ANYROOM, WE_WALKABLE); entry = getcell_cond(newmap, &ccwalkableroom); } */ // link the map to this lf if (!lflinkflag) { addflag(user->flags, F_MAPLINK, newmap->id, NA, NA, NULL); } // udpate the map's exits to go to where we were swallowed for (y = 0; y < newmap->h; y++) { for (x = 0; x < newmap->w; x++) { c = getcellat(newmap, x, y); o = hasob(c->obpile, OT_STOMACHEXIT); if (o) { killflagsofid(o->flags, F_MAPLINK); addflag(o->flags, F_MAPLINK, targcell->map->id, targcell->x, targcell->y, NULL); } } } if (isplayer(target)) { // pause before moving... more(); } // move target close to entrypoint movelf(target, entry, B_FALSE); // announce announcearrival(target, target->cell->map); } } } else if (abilid == OT_A_SWOOP) { cell_t *adjcell = NULL,*origcell; char targetname[BUFLEN]; int i; cell_t *retcell[MAXRETCELLS]; int nretcell; int srange = 5; flag_t *srflag; // get max range srflag = lfhasflag(user, F_SWOOPRANGE); if (srflag) { srange = srflag->val[0]; } if (isimmobile(user)) { if (isplayer(user)) msg("You can't move!"); return B_TRUE; } else if (!lfhasflag(user, F_FLYING)) { if (isplayer(user)) msg("You are not flying, therefore cannot swoop."); return B_TRUE; } else if (!lfhasflagval(user, F_HASATTACK, OT_CLAWS, NA, NA, NULL)) { if (isplayer(user)) msg("You need claws to perform a swoop attack."); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't swoop while stuck!"); return B_TRUE; } if (!targcell) { if (isplayer(user)) { snprintf(buf, BUFLEN, "Swoop who (max range %d)?",srange); // TODO: ask for direction targcell = askcoords(buf, "Swoop->", TT_MONSTER, user, srange, LOF_NEED, B_TRUE); if (!targcell) { msg("Cancelled."); return TRUE; } } else { return B_FALSE; } } if (getcelldist(user->cell, targcell) > srange) { if (isplayer(user)) msg("You can't swoop that far!"); return B_TRUE; } else if (getcelldist(user->cell, targcell) < 2) { if (isplayer(user)) msg("You don't have enough space to charge there!"); return B_TRUE; } // make sure we have LOF to there target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there!"); return TRUE; } else if (!haslof(user->cell, targcell, LOF_NEED, NULL)) { if (isplayer(user)) msg("Your path there is blocked!"); return TRUE; } getlfname(target, targetname); // find cell on the way... calcbresnham(user->cell->map, user->cell->x, user->cell->y, targcell->x, targcell->y, retcell, &nretcell); for (i = 1; i < nretcell; i++) { if (retcell[i] == targcell) { adjcell = retcell[i-1]; break; } } if (!adjcell) { if (isplayer(user)) { msg("There is no space nearby for you to attack!"); } return B_TRUE; } // take some time taketime(user, getactspeed(user)); // remember orig cell origcell = user->cell; // teleport next to them movelf(user, adjcell, B_FALSE); if (haslos(player, adjcell)) { msg("%s swoop%s towards %s!",username,isplayer(user) ? "" : "s",targetname); needredraw = B_TRUE; drawlevelfor(player); redraw(); //if (!isplayer(user)) { more(); //} } // attack addflag(user->flags, F_FORCEATTACK, OT_CLAWS, NA, NA, "fromuseability"); attackcell(user, targcell, B_TRUE); f = lfhasflagval(user, F_FORCEATTACK, OT_CLAWS, NA, NA, "fromuseability"); if (f) killflag(f); // teleport back to initial pos if (hasfreeaction(user)) { movelf(user, origcell, B_FALSE); if (haslos(player, adjcell)) { msg("%s swoop%s away!",username,isplayer(user) ? "" : "s"); needredraw = B_TRUE; drawlevelfor(player); redraw(); //if (!isplayer(user)) { //} } } } else if (abilid == OT_A_THRUST) { object_t *wep; flag_t *f; char targetname[BUFLEN]; int badweapon = B_FALSE; flag_t *damflag = NULL; obpile_t *op = NULL; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You lack the stability for a thrust while swimming."); return B_TRUE; } wep = getweapon(user); if (!wep) { object_t *weplist[MAXPILEOBS]; int nweps; // look for innate attack getweapons(user, B_MELEEONLY, weplist, NULL, NULL, &op, &nweps); if (nweps) { wep = weplist[0]; } else { badweapon = B_TRUE; } } if (wep) { if (getdamtype(wep) != DT_PIERCE) { badweapon = B_TRUE; } damflag = hasflag(wep->flags, F_DAM); } if (!damflag) { badweapon = B_TRUE; } if (badweapon) { if (isplayer(user)) msg("You need a long piercing weapon to perform a thrust!" ); if (op) killobpile(op); return B_TRUE; } // ask for direction if (!targcell) { snprintf(buf, BUFLEN, "Thrust at who (max range 2)?"); targcell = askcoords(buf, "Thrust->", TT_MONSTER, user, 2, LOF_NEED, B_TRUE); if (!targcell) { msg("Cancelled."); if (op) killobpile(op); return TRUE; } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to attack!"); if (op) killobpile(op); return B_TRUE; } getlfname(target, targetname); f = addflag(user->flags, F_ACCURACYMOD, -10, NA, NA, NULL); attacklf(user, target, wep, damflag); killflag(f); taketime(user, getattackspeed(user)); if (op) killobpile(op); } else if (abilid == OT_A_TRAIN) { // can we train? if (!user->skillpoints && !lfhasflag(user, F_HASNEWLEVEL)) { if(isplayer(user)) { msg("You are not ready for any training right now."); } return B_TRUE; } // safe to train? if (check_rest_ok(user)) return B_TRUE; // warn if we're a mage training without a spellbook /* f = lfhasflag(user, F_CANSTUDY); if (f) { if (!hasobwithflagval(user->pack, F_LINKSCHOOL, f->val[0], NA, NA, NULL) && !hasob(user->pack, OT_GRIMOIRE)) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "You cannot study %s without a spellbook. Continue", getschoolname(f->val[0])); // warn if (!real_warnabout(buf, DEF_WARNINGTIME, B_TRUE)) { return B_TRUE; } } } */ // start training! if (!startresting(user, B_TRUE)) { // do the first one right away rest(user, B_TRUE); } } else if (abilid == OT_A_TIPTOE) { if (isswimming(user)) { if (isplayer(user)) msg("You can't tiptoe while swimming!"); return B_TRUE; } if (isairborne(user, NULL)) { if (isplayer(user)) msg("You can't tiptoe while airborne!"); return B_TRUE; } if (isclimbing(user)) { if (isplayer(user)) msg("You can't tiptoe while climbing!"); return B_TRUE; } trysneak(user, D_NONE); } else if (abilid == OT_A_TRIPLF) { object_t *wep; int skillmod = 0; int legs; // ask for direction if (!targcell) { int dir; dir = askdir("Trip in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_MYSELF) { if (isplayer(user)) msg("You can't trip yourself!"); return B_TRUE; } else if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } if (!haslos(user, targcell)) { if (isplayer(user)) msg("You can't see there!"); return B_TRUE; } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to trip!"); return B_TRUE; } if (isairborne(target, NULL)) { if (isplayer(user)) msg("You can't trip someone in the air!"); return B_TRUE; } legs = countlegs(target); if (legs == 0) { if (isplayer(user)) msg("You can't trip something which has no legs!"); return B_TRUE; } getlfname(target, targetname); wep = getweapon(user); // try to trip them... taketime(user, getactspeed(user)); if (wep) { skillmod = getweaponskill(user, wep)*10; if (isplayer(user) && (skillmod == 0)) skillmod = -5; } else { skillmod = getskill(user, SK_UNARMED)*10; } if (skillcheckvs(user, SC_DEX, skillmod, target, SC_SLIP, (legs > 2) ? 25 : 0)) { if (cansee(player, user)) { msg("^w%s trip%s %s.",username, isplayer(user) ? "" : "s", targetname); } fall(target, NULL, B_TRUE); } else { if (isplayer(user)) { msg("You try to trip %s, but fail.",targetname); } else if (cansee(player, user)) { msg("%s tries to trip %s, but fails.",username, targetname); } } } else if (abilid == OT_A_TUMBLE) { cell_t *origcell,*c; cell_t *retcell[MAXRETCELLS]; char stopthing[BUFLEN],verb[BUFLEN], stopverb[BUFLEN]; int i,nretcell = 0,dir = D_NONE; enum RELATIVEDIR reldir; object_t *stopob = NULL; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't tumble while swimming!"); return B_TRUE; } else if (isairborne(user, NULL)) { if (isplayer(user)) msg("You can't tumble while airbourne!"); return B_TRUE; } else if (lfhasflag(user, F_GRABBING)) { if (isplayer(user)) msg("You can't tumble while holding someone!"); return B_TRUE; } else if (lfhasflag(user, F_GRABBEDBY)) { if (isplayer(user)) msg("You can't tumble while being held!"); return B_TRUE; } else if (isstuck(user)) { if (isplayer(user)) msg("You can't tumble while stuck!"); return B_TRUE; } if (targcell) { dir = getdirtowards(user->cell, targcell, NULL, B_FALSE, DT_ORTH); } else { dir = askdir("Tumble in which direction (- to cancel)", B_TRUE, B_TRUE); if ((dir == D_NONE) || (dir == D_MYSELF)) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { cell_t *cell1 = NULL,*cell2 = NULL; cell1 = getcellindir(user->cell, dir); if (cell1) { cell2 = getcellindir(cell1, dir); } else { if (isplayer(user)) msg("There is no room to tumble that way!"); return B_TRUE; } if (cell2) targcell = cell2; else targcell = cell1; if (!targcell) { if (isplayer(user)) msg("There is no room to tumble that way!"); return B_TRUE; } } } reldir = getrelativedir(user, dir); switch (reldir) { case RD_FORWARDS: strcpy(verb, "tumble"); break; case RD_BACKWARDS: strcpy(verb, "backflip"); break; case RD_SIDEWAYS: strcpy(verb, "cartwheel"); break; } if (isburdened(user)) { if (isplayer(user)) { msg("Your load is too heavy to %s with!", verb); } return B_TRUE; } else if (lfhasflag(user, F_GRAVBOOSTED)) { if (isplayer(user)) { msg("Gravity around you is too strong to %s!", verb); } return B_TRUE; } origcell = user->cell; taketime(user, getactspeed(user)); // will you be interrupted on the way? strcpy(stopthing, ""); strcpy(stopverb, ""); calcbresnham(origcell->map, origcell->x, origcell->y, targcell->x, targcell->y, retcell, &nretcell); for (i = 0; i < nretcell; i++) { c = retcell[i]; if (getcellwaterdepth(c, user)) { // stop here. targcell = c; stopob = hasobwithflag(c->obpile, F_DEEPWATER); strcpy(stopverb, "fall"); getobname(stopob, stopthing, stopob->amt); break; } else if (hasobwithflagval(c->obpile, F_PIT, D_DOWN, NA, NA, NULL)) { // stop here. targcell = c; stopob = hasobwithflagval(c->obpile, F_PIT, D_DOWN, NA, NA, NULL); strcpy(stopverb, "fall"); getobname(stopob, stopthing, stopob->amt); break; } // run into a person, wall or impassable object? if (i >= 1) { enum ERROR why; if (!cellwalkable(user, c, &why)) { // this will set "rdata" // stop in previous cell strcpy(stopverb, "bump"); targcell = retcell[i-1]; if (haslos(user, c)) { switch (why) { case E_WALLINWAY: strcpy(stopthing, c->type->name); break; case E_OBINWAY: if (canseeob(user, (object_t *)rdata)) { getobname((object_t *)rdata, stopthing, ((object_t *)rdata)->amt); } else { strcpy(stopthing, "something"); } break; case E_LFINWAY: if (cansee(user, (lifeform_t *)rdata)) { getlfname((lifeform_t *)rdata, stopthing); } else { strcpy(stopthing, "someone"); } break; default: strcpy(stopthing, "something"); break; } } else { strcpy(stopthing, "something"); } break; } } } // skillcheck... if (!skillcheck(user, SC_TUMBLE, 50, 0)) { // fail! if (isplayer(user)) { msg("You fumble and fall."); } else if (cansee(player, user)) { msg("%s fumbles and falls.", username); } fall(user, NULL, B_FALSE); return B_FALSE; } // announce if (isplayer(user)) { msg("You %s across the ground!", verb); } else if (cansee(player, user)) { msg("%s %ss across the ground!", username, verb); } // rolling around will put out fires if (reldir == RD_FORWARDS) { extinguishlf(user); } // go there! movelf(user, targcell, B_FALSE); // pits/water? if (strlen(stopthing)) { if (isplayer(user)) { msg("You %s into %s!", stopverb, stopthing); } else if (cansee(player, user)) { msg("%s %ss into %s!",username, stopverb, stopthing); } // pass another (harder) skill check or fall if (!skillcheck(user, SC_TUMBLE, 100, 0)) { // fail! fall(user, NULL, B_TRUE); return B_FALSE; } } practice(user, SK_ATHLETICS, 1); } else if (abilid == OT_A_POLYREVERT) { flag_t *f; if (!target) target = user; // take time taketime(user, getactspeed(user)); f = lfhasflag(target, F_ORIGRACE); if (!f) { if (isplayer(user)) nothinghappens(); return B_TRUE; } // this call will also remove this ability... setrace(user, f->val[0], B_TRUE); } else if (abilid == OT_A_PRAY) { lifeform_t *god; if (!isplayer(user)) return B_FALSE; if (lfhasflag(player, F_NOPRAY)) { if (isplayer(user)) { msg("Your pride prevents you from worshipping another being."); } return B_TRUE; } // ask for which god god = askgod("To whom will you pray?", B_FALSE, B_TRUE); if (!god) { msg("Cancelled."); return B_TRUE; } prayto(user, god); } else if (abilid == OT_A_LEARN) { skill_t *sk; char ch = 'a'; initprompt(&prompt, "Which skill will you learn?"); ch = 'a'; for (sk = firstskill ; sk ; sk = sk->next) { char buf2[HUGEBUFLEN]; enum SKILLLEVEL curlev; curlev = getskill(user, sk->id); if (curlev != PR_MASTER) { snprintf(buf, BUFLEN, "%s (%s)",getskillname(sk->id), getskilldesc(sk->id)); makedesc_skill(sk->id, buf2, curlev+1); addchoice(&prompt, ch++, getskillname(sk->id), buf, sk, buf2); } } getchoicestr(&prompt, B_FALSE, B_TRUE); sk = (skill_t *)prompt.result; if (sk) { enum SKILLLEVEL firstlev,wantlev; int i; ch = 'a'; firstlev = getskill(user, sk->id) + 1; initprompt(&prompt, "How much will you learn this skill?"); for (i = firstlev ; i <= PR_MASTER; i++) { snprintf(buf, BUFLEN, "%s",getskilllevelname(i)); addchoice(&prompt, ch++, buf, buf, NULL, NULL); } if (prompt.nchoices <= 0) { msg("You have already mastered this skill!"); return B_TRUE; } ch = getchoice(&prompt); wantlev = firstlev + (ch - 'a'); //while (strcmp(getskilllevelname(getskill(user, sk->id)), prompt.choice[prompt.selection].text)) { giveskilllev(user, sk->id, wantlev); //} } else { msg("Cancelled."); } } else if (abilid == OT_A_LEVELUP) { char buf[BUFLEN]; int lev; askstring("Which xp level will you attain", '?', buf, BUFLEN, NULL); lev = atoi(buf); if (lev <= user->level) { msg("Cancelled."); } else { while (user->level < lev) { gainlevel(user, B_TRUE); } } } else if (abilid == OT_A_BLINDALL) { lifeform_t *lf; for (lf = user->cell->map->lf ; lf ; lf = lf->next) { if (!isplayer(lf)) { addflag(lf->flags, F_BLIND, B_TRUE, NA, NA, NULL); killflagsofid(lf->flags, F_WANTS); killflagsofid(lf->flags, F_WANTSOBFLAG); } } msg("all blinded!"); } else if (abilid == OT_A_AIMEDSTRIKE) { object_t *wep; char targetname[BUFLEN]; flag_t *f; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You lack the control for an aimed strike while swimming."); return B_TRUE; } wep = getweapon(user); // ask for direction if (!targcell) { int dir; dir = askdir("Aimed strike in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else if (dir == D_MYSELF) { // yourself! targcell = user->cell; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to attack!"); return B_TRUE; } getlfname(target, targetname); f = addflag(user->flags, F_AIMEDSTRIKE, B_TRUE, NA, NA, NULL); attackcell(user, targcell, B_TRUE); killflag(f); } else if (abilid == OT_A_ALTERATTACK) { object_t *wep; enum SKILLLEVEL slev = PR_INEPT; char obname[BUFLEN],buf[BUFLEN]; flag_t *retflag[MAXCANDIDATES],*damflag; int nretflags,i; char ch = 'a'; enum DAMTYPE curdt = DT_NONE; wep = getweapon(user); if (wep) { slev = getweaponskill(user, wep); curdt = getdamtype(wep); } else { msg("You need to weild a weapon first!"); return B_TRUE; } if (slev < PR_ADEPT) { msg("You are not skilled enough with your weapon to do this!"); return B_TRUE; } damflag = hasflag(wep->flags, F_DAM); if (damflag) { getflags(wep->flags, retflag, &nretflags, F_ALTDAM, F_NONE); } else { nretflags = 0; } if (!nretflags) { msg("Your weapon is not capable of dealing any other damage type."); return B_TRUE; } getobname(wep, obname, 1); snprintf(buf, BUFLEN, "Deal what kind of damage with your %s?", noprefix(obname)); initprompt(&prompt, buf); for (i = 0; i < nretflags; i++) { f = retflag[i]; snprintf(buf, BUFLEN, "%s, DR %d", getdamname(f->val[0]), f->val[1]); if (strlen(f->text)) { strcat(buf, "("); strcat(buf, f->text); strcat(buf, ")"); } if (f->val[0] == curdt) { strcat(buf, " (current)"); } addchoice(&prompt, ch++, buf, NULL, f, NULL); } ch = getchoice(&prompt); f = (flag_t *)prompt.result; if (f->val[0] == curdt) { msg("Cancelled."); } else { // change dam type and amt damflag->val[0] = f->val[0]; damflag->val[1] = f->val[1]; msg("Your %s will now deal %s damage.", noprefix(obname), getdamname(damflag->val[0])); } } else if (abilid == OT_A_COMBOSTRIKE) { object_t *wep; skill_t *wepsk = NULL; int keepgoing = B_TRUE,nhits = 0; enum SKILLLEVEL slev; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You cannot perform a combination attack while swimming."); return B_TRUE; } wep = getweapon(user); if (wep) { wepsk = getobskill(wep->flags); } else { wepsk = findskill(SK_UNARMED); } if (wepsk) { slev = getskill(user, wepsk->id); } else { slev = PR_INEPT; } if (slev < PR_MASTER) { if (isplayer(user)) msg("You must have Mastered your weapon to perform a combination attack."); return B_TRUE; } if (!getadjenemies(user, NULL, NULL)) { if (isplayer(user)) msg("You cannot see anyone nearby to attack!"); return B_TRUE; } // take time taketime(user, getattackspeed(user)); // remember that we are doing a combo. this will stop taketime() from being // called during our attacks. addflag(user->flags, F_COMBOSTRIKE, B_TRUE, NA, NA, NULL); while (keepgoing) { cell_t *c; keepgoing = B_FALSE; // ask direction c = NULL; while (!c) { int dir; char ques[BUFLEN]; snprintf(ques, BUFLEN,"%s combination in which direction (- to cancel)", nhits ? "Continue" : "Start"); dir = askdir(ques, B_TRUE, B_TRUE); if (dir == D_NONE) { break; } else { c = getcellindir(user->cell, dir); } } // attack in that dir if (c && c->lf) { attackcell(user, c, B_TRUE); // if the lf there died, keep going. if (!c->lf || isdead(c->lf)) { keepgoing = B_TRUE; } // other reasons to stop the combo now? if (keepgoing) { if (isdead(user) || (getweapon(user) != wep)) { // dead or lost our weapon? keepgoing = B_FALSE; } else if (!getadjenemies(user, NULL, NULL)) { // noone left to attack keepgoing = B_FALSE; } } } else { msg("Combination ended."); keepgoing = B_FALSE; } // process lf deaths, then redraw the screen checkdeath(); needredraw = B_TRUE; drawscreen(); more(); // clear msgs nhits++; } killflagsofid(user->flags, F_COMBOSTRIKE); } else if (abilid == OT_A_DEBUG) { cell_t *where; where = askcoords("Debug who?", "Debug->",TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf) { debug(where->lf); } } else if (abilid == OT_A_DUMPMON) { msg("Dumping monsters..."); dumpmonsters(H_DUNGEON); msg("Monster list dumped to monsters.html."); } else if (abilid == OT_A_PATHFIND) { cell_t *where; where = askcoords("Pathfind to where?", "Pathfind->",TT_NONE, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where) { if (ai_createpathto(user, where)) { msg("Success!"); more(); showpath(user); killflagsofid(user->flags, F_AIPATH); } else { msg("PATHFIND FAILED."); } } } else if (abilid == OT_A_PETIFY) { cell_t *where; where = askcoords("Petify who?", "Petify->",TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf) { petify(where->lf, user); msg("Petified %s.", where->lf->race->name); } } else if (abilid == OT_A_EMPLOY) { cell_t *where; where = askcoords("Assign job to who?", "Assignjob->",TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf) { char question[BUFLEN]; char lfname[BUFLEN]; char buf[BUFLEN]; job_t *j; lifeform_t *target; target = where->lf; getlfname(target, lfname); snprintf(question, BUFLEN, "What job will you assign to %s",lfname); askstring(question, '?', buf, BUFLEN, NULL); j = findjobbyname(buf); if (j) { givejob(target, j->id); msg("%s is now a %s.", lfname, j->name); } else { fizzle(user); return B_TRUE; } } else { msg("There is nobody there!"); } } else if (abilid == OT_A_ENHANCE) { cell_t *where; where = askcoords("Enhance stats of who?", "Enhancestats->",TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf) { char ch; enum ATTRIB att; ch = askchar("Enhance which stat (n for none)?", "sacin",NULL, B_TRUE, B_FALSE); switch (ch) { case 's': att = A_STR; break; case 'a': att = A_AGI; break; case 'c': att = A_CON; break; case 'i': att = A_IQ; break; default: att = A_NONE; break; } if (att != A_NONE) { char buf[BUFLEN]; int val; askstring("Set stat to what", '?', buf, BUFLEN, NULL); val = atoi(buf); if ((val <= 0) || (val > 18)) { msg("Invalid value."); } else { setattr(where->lf, att, val); getlfname(where->lf, buf); msg("%s%s %s set to %d.", buf, getpossessive(buf), getattrname(att), val); } // } } } else if (abilid == OT_A_ENHANCEOB) { object_t *o,*o2; enum MATERIAL repairablemats[MAXCANDIDATES]; int cutoffpct[MAXCANDIDATES]; int i,nmats = 0; char ch,obname[BUFLEN]; if (!isplayer(user)) { return B_TRUE; } // get list of resizable materials getworkablematerials(user, SK_METALWORK, repairablemats, cutoffpct, &nmats); getworkablematerials(user, SK_SEWING, repairablemats, cutoffpct, &nmats); // 1.compile a list of enhancable objects initprompt(&prompt, "Enhance which object?"); addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL); for (o = user->pack->first ; o ; o = o->next) { int ok = B_FALSE; for (i = 0; i < nmats; i++) { if (o->material->id == repairablemats[i]) { ok = B_TRUE; break; } } if (ok && (isweapon(o) || isarmour(o)) && !hasflag(o->flags, F_NOQUALITY) && !hasflag(o->flags, F_MASTERWORK)) { int gotdupe = B_FALSE; // do we have another? for (o2 = user->pack->first ; o2 ; o2 = o2->next) { if ((o2 != o) && (o2->type->id == o->type->id)) { gotdupe = B_TRUE; break; } } if (gotdupe) { // we can enhance this object getobname(o, obname, o->amt); addchoice(&prompt, o->letter, obname, NULL, o, NULL); } } } if (prompt.nchoices <= 1) { msg("You don't have anything which you are able to enhance."); return B_TRUE; } // 2. ask which ones to enhance getchoice(&prompt); o = (object_t *) prompt.result; getobname(o, obname, 1); // ask what size we want. snprintf(buf, BUFLEN, "Destroy which object to enahnce %s?", obname); initprompt(&prompt, buf); for (o2 = user->pack->first ; o2 ; o2 = o2->next) { if ((o2 != o) && (o2->type->id == o->type->id)) { getobname(o2, obname, o2->amt); addchoice(&prompt, o2->letter, obname, NULL, o2, NULL); } } addchoice(&prompt, '-', "Cancel", "Cancel", NULL, NULL); prompt.maycancel = B_TRUE; ch = getchoice(&prompt); if ((ch == '-') || (ch == '\0')) { msg("Cancelled."); return B_TRUE; } o2 = (object_t *)prompt.result; // in case it's on fire, etc if (touch(user, o)) { taketime(user, getactspeed(user)); return B_FALSE; } if (touch(user, o2)) { taketime(user, getactspeed(user)); return B_FALSE; } // destroy second ob removeob(o2, ALL); // fully repair first ob f = hasflag(o->flags, F_OBHP); if (f) f->val[0] = f->val[1]; // enhance first ob addflag(o->flags, F_MASTERWORK, B_TRUE, NA, NA, NULL); getobname(o, obname, o->amt); msgnocap("%c - %s", o->letter, obname); practice(user, SK_METALWORK, 1); practice(user, SK_SEWING, 1); taketime(user, getactspeed(user)); } else if (abilid == OT_A_EXPLODESELF) { // special case to avoid abuse if (isplayer(user) && isimmuneto(user->flags, DT_EXPLOSIVE, B_FALSE)) { msg("Your resistance to explosive damage prevents the use of this ability."); return B_TRUE; } msg("^%c%s explode%s!", getlfcol(user, CC_VBAD), username, isplayer(user) ? "" : "s"); explodecells(user->cell, 9999, B_TRUE, NULL, 2, DT_COMPASS, B_FALSE, user); } else if (abilid == OT_A_EXPOSEDSTRIKE) { flag_t *f; if (getweaponskill(user, getweapon(user)) < PR_BEGINNER) { if (isplayer(user)) msg("You are not skilled enough to perform a wild strike.", HEAVYWEPKG); return B_TRUE; } // ask for direction if (!targcell) { int dir; dir = askdir("Wild strike in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE ; } else { targcell = getcellindir(user->cell, dir); } } f = addflag(user->flags, F_ACCURACYMOD, 200, NA, NA, NULL); attackcell(user, targcell, B_FALSE); taketime(user, getattackspeed(user)*2); killflag(f); } else if (abilid == OT_A_HEAVYBLOW) { object_t *wep; char targetname[BUFLEN]; flag_t *f,*damflag; int badweapon = B_FALSE; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You lack the stability for a heavy blow while swimming."); return B_TRUE; } wep = getweapon(user); if (!wep) { if (!hasjob(user, J_MONK)) { badweapon = B_TRUE; } } else if (!ismeleeweapon(wep) || !isheavyweapon(wep)) { badweapon = B_TRUE; } damflag = hasflag(wep->flags, F_DAM); if (!damflag) { badweapon = B_TRUE; } if (badweapon) { if (isplayer(user)) msg("You need a heavy weapon (%dkg or more) to perform a heavy blow!", HEAVYWEPKG); return B_TRUE; } // ask for direction if (!targcell) { int dir; dir = askdir("Heavy blow in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to attack!"); return B_TRUE; } getlfname(target, targetname); f = addflag(user->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL); attacklf(user, target, wep, damflag); taketime(user, getattackspeed(user)); killflag(f); } else if (abilid == OT_A_QUIVERINGPALM) { object_t *wep; char targetname[BUFLEN]; flag_t *f; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You lack the stability to use this ability while swimming."); return B_TRUE; } wep = getweapon(user); if (wep) { if (isplayer(user)) msg("You must be unarmed to make a quivering palm strike."); return B_TRUE; } // ask for direction if (!targcell) { int dir; dir = askdir("Quivering Palm in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE ; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to attack!"); return B_TRUE; } getlfname(target, targetname); f = addflag(user->flags, F_QUIVERINGPALM, B_TRUE, NA, NA, NULL); attackcell(user, targcell, B_TRUE); killflag(f); } else if (abilid == OT_A_STEAL) { enum SKILLLEVEL slev; object_t *wep; char targetname[BUFLEN]; flag_t *penalty = NULL; int failed = B_TRUE; slev = getskill(user, SK_THIEVERY); if (slev == PR_INEPT) { if (isplayer(user)) msg("You are too unskilled to steal anything!"); return B_TRUE; } // stealing from a lifeform // ask for direction if (!targcell) { int dir; dir = askdir("Steal in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE ; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to steal from!"); return B_TRUE; } taketime(user, getactspeed(user)); getlfname(target, targetname); if (slev == PR_NOVICE) { penalty = addflag(user->flags, F_ACCURACYMOD, -14, NA, NA, NULL); } else if (slev == PR_BEGINNER) { penalty = addflag(user->flags, F_ACCURACYMOD, -7, NA, NA, NULL); } // use empty handed attack accuracy wep = getweapon(user); if (rolltohit(user, target, wep, NULL, NULL, NULL)) { // success! failed = B_FALSE; if (steal(user, target->pack, F_NONE)) { if (isplayer(user)) { msg("%s has nothing for you to steal!", targetname); } } } else { failed = B_TRUE; } if (penalty) { killflag(penalty); } if (failed) { if (isplayer(user)) { msg("You try to steal from %s, but fail.", targetname); } else if (cansee(player, user)) { msg("%s tries to steal from %s, but fails.", username, targetname); } // ai will get angry! if (cansee(target, user) && !isplayer(target)) { fightback(target, user); } } else { practice(user, SK_THIEVERY, 1); } } else if (abilid == OT_A_WARCRY) { if (lfhasflag(user, F_SILENCED)) { if (isplayer(user)) msg("You are unable to make a sound!"); return B_TRUE; } // announce if (isplayer(user)) { msg("You shout a blood-curdling war cry!"); } makenoise(user, N_WARCRY); // take a lot of time, so that there is a danger in just // using it all the time. taketime(user, getactspeed(user)*2); // all in range must pass a morale check or flee for (target = user->cell->map->lf ; target ; target = target->next) { if ((target != user) && cansee(target, user) && areenemies(target, user)) { int ok = B_FALSE; switch (target->race->raceclass->id) { case RC_ANIMAL: case RC_HUMANOID: ok = B_TRUE; break; default: break; } if (ok && canhear(target, user->cell, 4, NULL)) { scare(target, user, rnd(5,10), 0); } } } } else if (abilid == OT_A_HURRICANESTRIKE) { int dir; cell_t *c; flag_t *f,*f2,*f3; object_t *wep; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You cannot do that while swimming."); return B_TRUE; } // take time // - NOTE: purposely using action speed, not weapon speed. taketime(user, getactspeed(user)); // now don't take any more time for the actual attacks. f = addflag(user->flags, F_NOTIME, B_TRUE, NA, NA, NULL); // lower accuracy f2 = addflag(user->flags, F_ACCURACYMOD, -20, NA, NA, NULL); // remember we are doing a hurricane attack to avoid lots of // "there is nothing to attack there" messages. f3 = addflag(user->flags, F_HURRICANESTRIKE, B_TRUE, NA, NA, NULL); wep = getweapon(user); if (wep) { char buf[BUFLEN],wepname[BUFLEN]; getobname(wep, buf, 1); snprintf(wepname, BUFLEN, "%s", noprefix(buf)); if (isplayer(user)) { msg("You swing your %s in a wide arc!", wepname); } else if (cansee(player, user)) { msg("%s swings its %s in a wide arc!", username, wepname); } } else { if (isplayer(user)) { msg("You attack in a wide arc!"); } else if (cansee(player, user)) { msg("%s attacks in a wide arc!", username); } } // attack all adjacent enemies for (dir = DC_N; dir <= DC_NW; dir++) { c = getcellindir(user->cell, dir); if (c) { attackcell(user, c, B_TRUE); } } // remove temporary flags killflag(f); killflag(f2); killflag(f3); } else if (abilid == OT_A_HIDE) { lifeform_t *lf; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You can't hide while swimming!"); return B_TRUE; } if (lfproduceslight(user, NULL)) { if (isplayer(user)) msg("You can't hide while producing light!"); return B_TRUE; } if (lfhasflag(user, F_HIDING)) { // stop hiding. killflagsofid(user->flags, F_HIDING); taketime(user, getactspeed(user)); return B_TRUE; } if (isplayer(user)) { if (!safetorest(user, NULL)) { if (getattrbracket(getattr(user, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { if (askchar("Really try to hide while in view of enemies?", "yn", "n", B_TRUE, B_FALSE) != 'y') { return B_TRUE; } } } } if (getskill(user, SK_STEALTH) < PR_EXPERT) { // anyone who saw you start hiding can still see you for (lf = user->cell->map->lf ; lf ; lf = lf->next) { if (lf == user) continue; if (cansee(lf, user)) { addflag(lf->flags, F_SPOTTED, user->id, NA, NA, NULL); } } } stopsprinting(user); // start hiding addflag(user->flags, F_HIDING, 0, NA, NA, NULL); // even though it's untrainable - this will // still mark it as used. practice(user, SK_STEALTH, 1); taketime(user, getactspeed(user)); } else if (abilid == OT_A_INSPECT) { object_t *o; int difficulty; enum RARITY rarity; int mod; enum SKILLLEVEL slev; flag_t *f; // only players can do this if (!isplayer(user)) { return B_FALSE; } if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("That wouldn't be a good idea while swimming."); return B_TRUE; } if (!haslos(user, user->cell)) { msg("You can't inspect anything, since you can't see!"); return B_TRUE; } // ask what to inspect initprompt(&prompt, "Inspect which object"); for (o = user->pack->first ; o ; o = o->next) { int classok = B_FALSE; enum SKILL whichsk; whichsk = getclassloreskill(o->type->obclass->id); // can we inspect this? if ((whichsk != SK_NONE) && getskill(user, whichsk)) classok = B_TRUE; if (classok && !isknown(o)) { char buf[BUFLEN]; getobname(o, buf, o->amt); addchoice(&prompt, o->letter, buf, NULL, o, NULL); } } addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); prompt.maycancel = B_TRUE; if (prompt.nchoices == 1) { msg("You don't have anything which you can inspect."); return B_TRUE; } getchoice(&prompt); o = (object_t *)prompt.result; if (!o) { msg("Cancelled"); return B_TRUE; } // failed this already? if (lfhasflagval(user, F_FAILEDINSPECT, o->type->id, NA, NA, NULL)) { msg("You won't be able to recognise this until you are more experienced."); return B_TRUE; } // skillcheck... f = hasflag(o->flags, F_RARITY); if (f) { if (f->val[2] == NA) { rarity = RR_COMMON; } else { rarity = f->val[2]; } } else { rarity = 0; } difficulty = 120 + (rarity*10); /* switch (o->type->obclass->id) { case OC_SCROLL: difficulty += 2; break; case OC_BOOK: difficulty += 4; break; case OC_WAND: difficulty += 6; break; case OC_RING: difficulty += 8; break; default: break; } */ slev = getskill(user, getclassloreskill(o->type->obclass->id)); switch (slev) { case PR_INEPT: mod = -5; // should never happen break; default: mod = slev*4; break; } if (skillcheck(user, SC_IQ, difficulty, mod)) { char obname[BUFLEN]; // passed! makeknown(o->type->id); getobname(o, obname, o->amt); msgnocap("This seems to be %s!", obname); } else { msg("You have no idea what this is."); addflag(user->flags, F_FAILEDINSPECT, o->type->id, NA, NA, NULL); } taketime(user, getactspeed(user)); } else if (abilid == OT_A_KILLALL) { lifeform_t *nextlf; if (isplayer(user)) { msg("^wDeath radiates around you!"); } else if (cansee(player, user)) { msg("^wDeath radiates around %s!", username); } for (target = user->cell->map->lf ; target ; target = nextlf) { nextlf = target->next; if (target == user) continue; killlf(target); } } else if (abilid == OT_A_IRONFIST) { char tname[BUFLEN]; int dam,dir = D_NONE; if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { if (isplayer(user)) msg("You lack the stability to invoke the iron fist manouver while swimming."); return B_TRUE; } if (getweapon(user)) { if (isplayer(user)) msg("You must be unarmed to invoke the iron fist."); return B_TRUE; } // ask for direction if (!targcell) { dir = askdir("Iron fist in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { if (isplayer(user)) msg("Cancelled."); return B_TRUE ; } else { targcell = getcellindir(user->cell, dir); } } target = targcell->lf; if (!target) { if (isplayer(user)) msg("There is nobody there to attack!"); return B_TRUE; } getlfname(target, tname); if (isplayer(user)) { msg("^%cYou strike %s with THE IRON FIST!",getlfcol(user, CC_GOOD),tname); } else if (cansee(player, user)) { msg("^%c%s strikes %s with THE IRON FIST!",getlfcol(user, CC_GOOD),username, tname); } // convert all remaining stamina into damage. dam = getstamina(user); setstamina(user, 0); snprintf(damstr, BUFLEN, "%s%s invocation of the iron fist",username,getpossessive(username)); losehp(target, dam, DT_DIRECT, user, damstr); if (dir != D_NONE) { // knockback too knockback(target, dir, dam, user, 0, B_DOANNOUNCE, B_DODAM); } } // expire ability f = lfhasflagval(user, F_CANWILL, abilid, NA, NA, NULL); if (f) { if (f->val[2] != NA) { // ie. it will go to 0 next turn. f->val[1] = -1; } } return B_FALSE; } void addbuildchoice(prompt_t *p, lifeform_t *lf, enum OBTYPE oid, char *ch) { objecttype_t *tempot; char tempname[BUFLEN]; tempot = findot(oid); if (!haschoicedata(p, tempot) && canbuild(lf, tempot, tempname, NULL, NULL)) { addchoice(p, *ch, tempname, NULL, tempot, NULL); (*ch)++; } } // returns true if you can't charm them. int checkcharm(lifeform_t *caster, lifeform_t *target) { char targetname[BUFLEN]; getlfname(target, targetname); // if the target is of a different raceclass, you usually // can't charm them. if (getraceclass(target) != getraceclass(caster)) { int willfail = B_FALSE; switch (getraceclass(target)) { case RC_DEMON: case RC_DRAGON: case RC_GOD: case RC_SLIME: case RC_MAGIC: case RC_PLANT: willfail = B_TRUE; break; default: break; } if (willfail) { if (isplayer(caster)) { msg("%s%s mind is too alien for you to charm.",targetname,getpossessive(targetname)); } return B_TRUE; } } if (!ischarmable(target)) { if (isplayer(caster)) { err_nocharm(reason, targetname); } return B_TRUE; } // ok return B_FALSE; } // returns TRUE on error int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_t *target, object_t *targob, cell_t *targcell, int blessed, int *seenbyplayer, int frompot, object_t *fromob) { char buf[BUFLEN]; char castername[BUFLEN]; int rv = B_FALSE,b; objecttype_t *sp; flag_t *casttype = NULL; // sometimes change spellid based on blessed status if (blessed == B_CURSED) { switch (spellid) { case OT_S_LIGHT: spellid = OT_S_DARKNESS; default: break; } } sp = findot(spellid); if (caster) { getlfname(caster, castername); } else { strcpy(castername, "something"); } // default to unseen if (seenbyplayer) *seenbyplayer = B_FALSE; // adjust destination/range for wands, since you might inadvertantly // target an invalid cell if you don't know what it is yet. if (fromob && caster) { enum LOFTYPE loft; int maxr; // reduce range if required maxr = getspellrange(caster, spellid, power, NULL); if (targcell && (maxr != UNLIMITED) && (getcelldist(caster->cell, targcell) > maxr)) { cell_t *retcell[MAXCANDIDATES]; int nretcells; calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcells); if (maxr > (nretcells-1)) { maxr = nretcells-1; } targcell = retcell[maxr]; } // adjust for LOF - we already validated this during castspell()'s call to validatespellcell(), // but check again in case, for example, there was an invisible lf in the way which we couldn't // see. loft = getspellloftype(spellid); if (targcell && (loft != LOF_DONTNEED)) { cell_t *newtarg; haslof(caster->cell, targcell, loft, &newtarg); targcell = newtarg; } } if (caster) { if (hasflag(sp->flags, F_ONGOING)) { if (lfhasflagval(caster, F_BOOSTSPELL, spellid, NA, NA, NULL)) { // cancel it. stopspell(caster, spellid); return B_FALSE; } else { objecttype_t *sp; int cost; cost = getmpcost(caster, spellid); sp = findot(spellid); if (sp && hasflag(sp->flags, F_VARPOWER)) { cost *= power; } addflag(caster->flags, F_BOOSTSPELL, spellid, cost, power, NULL); } } // druids gain power from nearby plants if (hasjob(caster, J_DRUID)) { cell_t *c; int d; float totweight = 0; int powerinc = 0; object_t *o; for (d = DC_N; d <= DC_NW; d++) { c = getcellindir(caster->cell, d); if (c) { for (o = c->obpile->first ; o ; o = o->next){ if (o->type->obclass->id == OC_FLORA) totweight += getobmass(o); } } } for (o = caster->cell->obpile->first ; o ; o = o->next){ if (o->type->obclass->id == OC_FLORA) totweight += getobmass(o); } for (o = caster->pack->first ; o ; o = o->next){ if (o->type->obclass->id == OC_FLORA) totweight += getobmass(o); } powerinc = totweight / 50; if (powerinc > 0) { power += powerinc; } } // worshipers of ekrub gain power from piety if (isplayer(caster) && godprayedto(R_GODNATURE) && (hasflagval(sp->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL))) { enum PIETYLEV pl; pl = getpietylev(R_GODNATURE, NULL, NULL); if (pl >= PL_PLEASED) { power += pl; limit(&power, NA, 10); } } // special: spit attacks need an animation casttype = lfhasflag(caster, F_CASTTYPE); if (casttype && (casttype->val[1] == CT_EYESPIT)) { enum COLOUR col; if (casttype->val[2] == NA) { col = C_GREEN; } else { col = casttype->val[2]; } anim(caster->cell, targcell, '}', col); } } // special case: summoning spell on a pentagram if (caster && hasob(caster->cell->obpile, OT_PENTAGRAM) && hasflagval(sp->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL) && (sp->id != OT_S_SUMMONDEMON)) { if (haslos(player, caster->cell)) { msg("The pentagram pulses red."); } if (summonlfs(caster, caster->cell, R_NONE, RC_DEMON, SZ_ANY, AL_NONE, 1, PERMENANT, B_MAYBE)) { if (isplayer(caster) || cansee(player, caster)) { msg("An other-worldly demon appears!"); } } return B_FALSE; } // switch based on spell effects... if (spellid == OT_S_ABSOLUTEZERO) { cell_t *retcell[MAXCANDIDATES]; int nretcells,i; if (isplayer(caster)) { msg("You unleash a freezing blast!"); } else if (cansee(player, caster)) { msg("%s unleashes a freezing blast!", castername, getpossessive(castername)); } // freeze lfs, obs in most cells getradiuscells(caster->cell, 7, DT_ORTH, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 90); for (i = 0; i < nretcells; i++) { object_t *o, *nexto; if (retcell[i]->lf) { for (o = retcell[i]->lf->pack->first ; o ; o = nexto) { nexto = o->next; changemat(o, MT_ICE); } freezelf(retcell[i]->lf, caster, PERMENANT); } for (o = retcell[i]->obpile->first ; o ; o = nexto) { nexto = o->next; changemat(o, MT_ICE); } o = addobfast(retcell[i]->obpile, OT_ICESHEET); if (o) killflagsofid(o->flags, F_OBHPDRAIN); } } else if (spellid == OT_S_ABSORBMETAL) { int i; float totalmass = 0; object_t *o; int howmuch; // works on all metal in sight if (isplayer(caster)) { if (seenbyplayer) *seenbyplayer = B_FALSE; msg("You draw power from nearby metal!"); } else if (cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_FALSE; msg("%s draws power from nearby metal!",castername); } totalmass = 0; // destroy WORN metal objects by the caster (not CARRIED ones) for (o = caster->pack->first ; o ; o = o->next) { if (isequipped(o) && ismetal(o->material->id)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(caster->pack); for (i = 0; i < caster->nlos; i++) { targcell = caster->los[i]; for (o = targcell->obpile->first ; o ; o = o->next) { // destroy metal items on the ground if (ismetal(o->material->id)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(targcell->obpile); if (targcell->lf && !spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) { // destroy only WORN metal objects, not CARRIED ones for (o = targcell->lf->pack->first ; o ; o = o->next) { if (isequipped(o) && ismetal(o->material->id)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(targcell->lf->pack); } } if (totalmass > 0) { float max; int mptoheal; // heal 1 mp per kilo howmuch = floor(totalmass); // maximum based on power max = 40 + (power*10); if (howmuch > max) howmuch = max; mptoheal = getmaxmp(caster) - caster->mp; gainmp(caster, howmuch); // any left? heal hp with half the remaining amt now. howmuch -= mptoheal; howmuch /= 2; if (howmuch > 0) { gainhp(caster, howmuch); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_ABSORBWOOD) { int i; float totalmass = 0; object_t *o; int howmuch; // works on all metal in sight if (isplayer(caster)) { if (seenbyplayer) *seenbyplayer = B_FALSE; msg("You draw power from nearby wood!"); } else if (cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_FALSE; msg("%s draws power from nearby wood!",castername); } totalmass = 0; for (i = 0; i < caster->nlos; i++) { targcell = caster->los[i]; // destroy wood items on the ground for (o = targcell->obpile->first ; o ; o = o->next) { if (o->material->id == MT_WOOD) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(targcell->obpile); // carried wood? if (targcell->lf && (targcell->lf != caster) && !spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) { // destroy only WORN wood objects, not CARRIED ones for (o = targcell->lf->pack->first ; o ; o = o->next) { if (isequipped(o) && (o->material->id == MT_WOOD)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(targcell->lf->pack); } } if (totalmass > 0) { float max; int mptoheal; // heal 1 mp per kilo howmuch = floor(totalmass); // maximum based on power max = 40 + (power*10); if (howmuch > max) howmuch = max; mptoheal = getmaxmp(caster) - caster->mp; gainmp(caster, howmuch); // any left? heal hp with half the remaining amt now. howmuch -= mptoheal; howmuch /= 2; if (howmuch > 0) { gainhp(caster, howmuch); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_AIRBLAST) { int dir; object_t *o,*nexto; cell_t *firstobcell = NULL; cell_t *nextc; if (targcell) { dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS); } else { dir = askdir("Airblast in which direction (- to cancel)", B_TRUE, B_TRUE); if (dir == D_NONE) { fizzle(caster); return B_TRUE; } } // keep going that dir until the next cell is a wall/lf/impassable ob targcell = caster->cell; nextc = getcellindir(targcell, dir); while (nextc && cellwalkable(NULL, nextc, NULL)) { targcell = nextc; if (!firstobcell) { // obs here? for (o = targcell->obpile->first ;o ; o = o->next) { if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) { firstobcell = targcell; break; } } } nextc = getcellindir(targcell, dir); } if (cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; msg("%s emit%s a powerful blast of air!", castername, isplayer(caster) ? "" : "s"); } // if it was a lifeform which stopped us... if (nextc->lf) { // knock them back as well targcell = nextc; target = nextc->lf; knockback(target, dir, power, caster, 0, B_DOANNOUNCE, B_DODAM); real_fall_from_air(target, SZ_HUMAN + (power/4)); } else if (firstobcell) { // objects for (o = firstobcell->obpile->first ;o ; o = nexto) { nexto = o->next; if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) { knockbackob(o, dir, power, power*5, caster); } } } } else if (spellid == OT_S_ALARM) { if (isplayer(caster)) { msg("You create a alarm field around yourself."); } } else if (spellid == OT_S_ALCHEMY) { char obname[BUFLEN]; char obtocreate[BUFLEN]; obpile_t *op; object_t *newob = NULL; int obamt; float obweight; float convrate, powerpct; float obvalue; // needs: // object = which object to convert if (!targob && isplayer(caster)) { condset_t cs; initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_STONE, CC_NONE); targob = doaskobject(caster->pack, "Convert which object to gold", NULL, NULL, B_FALSE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, &cs, B_FALSE); } if (!targob) { fizzle(caster); return B_TRUE; } op = targob->pile; obamt = targob->amt; getobname(targob, obname, targob->amt); obweight = getobmass(targob); // get conversion rate powerpct = ((double)power / getspellmaxpower(spellid)) ; convrate = (powerpct * (4.0 - 0.5)) + 0.5; // determine value of gold obvalue = convrate * obweight; limitf(&obvalue, 0, NA); // original object vanishes. killob(targob); // create new object if (obvalue > 0) { if (op->owner) { newob = hasob(op, OT_GOLD); if (newob) { newob->amt += (int)obvalue; } else { snprintf(obtocreate, BUFLEN, "%d gold dollars", (int)obvalue); newob = addob(op, obtocreate); } } else { snprintf(obtocreate, BUFLEN, "%d gold dollars", (int)obvalue); newob = addob(op, obtocreate); } } // announce if (isplayer(op->owner) || (op->where && haslos(player, op->where)) ) { if (seenbyplayer) *seenbyplayer = B_TRUE; if (newob) { char newobname[BUFLEN]; msg("%s change%s into %d gold dollar%s!", obname, (obamt == 1) ? "s" : "", (int)obvalue, ((int)obvalue == 1) ? "" : "s"); if (newob->pile->owner && isplayer(newob->pile->owner)) { getobname(newob, newobname, newob->amt); msgnocap("%c - %s", newob->letter, newobname); } } else { msg("%s vanishes!", obname); if (isplayer(caster)) angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT); } } } else if (spellid == OT_S_ANIMATEDEAD) { int i,nposs = 0; object_t *o,*nexto; int donesomething = B_FALSE; object_t **poss; if (!target) { target = caster; } poss = malloc(target->nlos * sizeof(object_t *)); // animate corpses within lof of caster for (i = 0; i < target->nlos; i++) { targcell = target->los[i]; //if (targcell != target->cell) { // DO include the cell the target is standing on, // since makezombie() will look for adjacent cells // to place the zombie. for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; if (o->type->id == OT_CORPSE) { poss[nposs++] = o; } } //} } for (i = 0 ; (i < power) && (nposs > 0); i++) { lifeform_t *newlf; int sel,n; sel = rnd(0,nposs-1); o = poss[sel]; newlf = makezombie(o, power, "rises from the dead", target); if (newlf) { donesomething = B_TRUE; } // remove from list for (n = sel; n < nposs - 1; n++) { poss[n] = poss[n+1]; } nposs--; } free(poss); if (donesomething) { if (seenbyplayer) *seenbyplayer = B_TRUE; } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_ANIMATEMETAL) { object_t *o; o = getweapon(caster); if (o) { char obname[BUFLEN]; lifeform_t *newlf; getobname(o, obname, 1); if (!ismetal(o->material->id)) { if (isplayer(caster)) { msg("Your %s is not metal, therefore unaffected.", noprefix(obname)); } else { fizzle(caster); return B_TRUE; } return B_FALSE; } else if (o->amt != 1) { if (isplayer(caster)) { msg("Your %s wobbles a little.", obname); } else { fizzle(caster); return B_TRUE; } return B_FALSE; } newlf = makeanimated(caster, o, caster->level); if (newlf) { if (isplayer(caster)) { msg("Your %s leaps into the air!", noprefix(obname)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { msg("%s%s %s leaps into the air!",castername, getpossessive(castername), noprefix(obname)); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { fizzle(caster); return B_TRUE; } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_ANIMATESTATUE) { object_t *o; target = targcell->lf; if (target) { if (lfhasflag(target, F_BEINGSTONED)) { killflagsofid(target->flags, F_BEINGSTONED); if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } else { fizzle(caster); return B_TRUE; } } // otherwise find first statue o = hasob(targcell->obpile, OT_STATUE); if (o) { lifeform_t *lf = NULL; char obname[BUFLEN]; flag_t *f; // what is it a statue of? f = hasflag(o->flags, F_CORPSEOF); if (!f) { fizzle(caster); return B_TRUE; } // add it! getobname(o, obname, 1); removeob(o, ALL); lf = addmonster(targcell, f->val[0], NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); setlfmaterial(lf, MT_STONE, B_FALSE); if (cansee(player, lf)) { msg("%s comes to life!",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL); if (caster) { petify(lf, caster); addflag(lf->flags, F_SUMMONEDBY, caster->id, rnd(50,100), NA, NULL); } // no corpse after death (so you can't keep reanimating it) addflag(lf->flags, F_NOCORPSE, NA, NA, NA, NULL); if (isplayer(caster)) { angergodmaybe(R_GODFIRE, 50, GA_HERESY); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_ANIMATETREE) { object_t *o; o = hasob(targcell->obpile, OT_TREE); if (o) { lifeform_t *lf = NULL; char obname[BUFLEN]; enum RACE rid; // determine new race switch (rnd(1,6)) { case 1: case 2: case 3: rid = R_TREANTYOUNG; break; case 4: case 5: rid = R_TREANT; break; case 6: rid = R_TREANTOLD; break; } getobname(o, obname, 1); removeob(o, ALL); lf = addmonster(targcell, rid, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); if (cansee(player, lf)) { msg("%s comes to life!",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } addflag(lf->flags, F_XPVAL, 0, NA, NA, NULL); if (caster) { petify(lf, caster); addflag(lf->flags, F_SUMMONEDBY, caster->id, rnd(50,100), NA, NULL); } if (isplayer(caster)) { angergodmaybe(R_GODFIRE, 50, GA_HERESY); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_ANTICIPATE) { if (!target) target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } addflag(caster->flags, F_ANTICIPATE, target->id, power, NA, NULL); } else if (spellid == OT_S_APPORTATION) { int failed = B_FALSE; float maxweight; // if no target object... if (!targob) { // ask for a target cell (to take objects from) if (!validatespellcell(caster, &targcell, TT_OBJECT, spellid, power, frompot)) return B_TRUE; if (targcell->obpile->first) { // more than object? if (targcell->obpile->first->next) { // select object from cell... targob = askobject(targcell->obpile, "Target which object", NULL, NULL, '\0', NULL, B_FALSE); } else { targob = targcell->obpile->first; } if (!targob) { failed = B_TRUE; } } else { failed = B_TRUE; } } if (!failed) { char obname[BUFLEN]; cell_t *obloc; obloc = getoblocation(targob); getobname(targob, obname, 1); targcell = caster->cell; // not liftable? if (hasflag(targob->flags, F_NOPICKUP)) { if (isplayer(caster)) { nothinghappens(); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } // modify by power maxweight = 10; if (getobmass(targob) > maxweight) { if (haslos(player, obloc)) { msg("%s fades out of view slightly, then reappears.",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } if (moveob(targob, caster->pack, 1)) { if (isplayer(caster)) { msg("%s appears in your pack!", obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, obloc)) { msg("%s vanishes!", obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { failed = B_TRUE; } } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_AWARENESS) { flag_t *f; f = addtempflag(caster->flags, F_AWARENESS, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_BAFFLE) { target = targcell->lf; if (lfhasflag(target, F_ASLEEP) || !ischarmable(target)) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { return B_FALSE; } else { confuse(target, power*4); if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } } else if (spellid == OT_S_BARKSKIN) { flag_t *f; // always targetted at caster if (!target) { targcell = caster->cell; target = caster; } //f = addtempflag(caster->flags, F_MAGICARMOUR, power*2, NA, NA, "skin of bark", FROMSPELL); //f->obfrom = spellid; setlfmaterial(target, MT_WOOD, B_TRUE); f = addtempflag(caster->flags, F_DTVULN, DT_FIRE, NA, NA, "2d4", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_BLADEBURN) { object_t *wep; if (!targcell) { targcell = caster->cell; } target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } // does caster have a weapon? wep = getweapon(target); if (!wep) { fizzle(caster); return B_TRUE; } if (isplayer(target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } addtempflag(wep->flags, F_ONFIRE, B_TRUE, NA, NA, NULL, 10 + power*3); } else if (spellid == OT_S_BLIGHT) { char obname[BUFLEN]; if (targob) { if (isedible(targob)) { // just affect one item targcell = targob->pile->where; target = targob->pile->owner; getobname(targob, obname, targob->amt); if (target && cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%cA green miasma surrounds %s%s %s.", getlfcol(target, CC_BAD), lfname, getpossessive(lfname), obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (targcell && haslos(player, targcell)) { msg("A green miasma surrounds %s.", obname); } if (!hasflag(targob->flags, F_TAINTED)) { addflag(targob->flags, F_TAINTED, B_TRUE, NA, NA, NULL); } return B_FALSE; } else { fizzle(caster); return B_TRUE; } } else { obpile_t *op; object_t *o,*nexto; int ndone = 0; if (targcell->lf) { op = targcell->lf->pack; if (cansee(player, targcell->lf)) { char lfname[BUFLEN]; getlfname(targcell->lf, lfname); msg("^%cA green miasma surrounds %s.", getlfcol(targcell->lf, CC_BAD), lfname); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isliving(targcell->lf)) { int dam; char damstr[BUFLEN]; dam = rnd(1,power); if (caster) { char cname[BUFLEN]; real_getlfnamea(caster, cname, NULL, B_SHOWALL, B_REALRACE); snprintf(damstr, BUFLEN, "%s%s blight spell.", cname, getpossessive(cname)); } else { strcpy(damstr, "a blight spell."); } if (isplayer(targcell->lf)) { msg("^%cA blight courses through your veins!", getlfcol(targcell->lf, CC_BAD)); } else if (cansee(player, targcell->lf)) { char tname[BUFLEN]; getlfname(targcell->lf, tname); msg("%s looks unwell.", tname); } losehp(targcell->lf, dam, DT_DECAY, caster, damstr); } } else { op = targcell->obpile; } // taint all objects here for (o = op->first ; o ; o = nexto) { nexto = o->next; if (isedible(o)) { if (!targcell->lf && haslos(player, targcell)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("A green miasma surrounds %s.", obname); } if (!hasflag(o->flags, F_TAINTED)) { addflag(o->flags, F_TAINTED, B_TRUE, NA, NA, NULL); } if (++ndone >= power) { break; } } } } } else if (spellid == OT_S_BLINDNESS) { int failed = B_FALSE; target = targcell->lf; if (isblind(target)) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { failed = B_TRUE; } if (target) { if (failed) { if (isplayer(target)) { msg("Your vision darkens for a moment."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char targetname[BUFLEN]; getlfname(target, targetname); msg("%s seems to lose its vision for a moment.", targetname); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } else { int howlong = 7; howlong = getspellduration(5,10,blessed) + power; addtempflag(target->flags, F_BLIND, B_TRUE, NA, NA, NULL, howlong); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_BLINK) { cell_t *poss[MAXCANDIDATES]; int nposs = 0,x,y; if (lfhasflag(caster, F_CONTROL) && (power < 6)) { power = 6; } if (power >= 6) { // controlled // must be within line of sight. if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; if (!targcell || !cellwalkable(caster, targcell, NULL)) { char ch; ch = askchar("Teleport randomly?", "yn","n", B_TRUE, B_FALSE); if (ch != 'y') { fizzle(caster); return B_TRUE; } } } if (!targcell) { // pick a random location // only needs to be in line-of-FIRE, not neccesarily sight. for (y = 0 ; y <= caster->cell->map->h; y++) { for (x = 0 ; x <= caster->cell->map->w; x++) { cell_t *c; c = getcellat(caster->cell->map, x, y); if (c && haslof(caster->cell, c, LOF_WALLSTOP, NULL)) { if (cellwalkable(caster, c, NULL) && !celldangerous(caster, c, B_FALSE, NULL)) { poss[nposs++] = c; } } if (nposs >= MAXCANDIDATES) break; } if (nposs >= MAXCANDIDATES) break; } if (!nposs) { fizzle(caster); return B_TRUE; } targcell = poss[rnd(0,nposs-1)]; } teleportto(caster, targcell, B_TRUE); } else if (spellid == OT_S_BLINKASS) { target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } // find cell behind caster targcell = getcellindir(targcell, diropposite(target->facing)); if (!cellwalkable(caster, targcell, NULL)) { fizzle(caster); return B_TRUE; } teleportto(caster, targcell, B_TRUE); setfacing(caster, target->facing); } else if (spellid == OT_S_BLOODBOIL) { if (!target) { target = targcell->lf; } if (!target || lfhasflag(target, F_BLOODBOIL)) { fizzle(caster); return B_TRUE; } addflag(target->flags, F_BLOODBOIL, B_TRUE, NA, NA, NULL); } else if (spellid == OT_S_LOWERMETAB) { flag_t *f; // ie. 2 - 4 f = addtempflag(caster->flags, F_SLOWMETAB, 2+(power/4), NA, NA, NULL, FROMSPELL); f->obfrom = spellid; // you move more slowly too. if (power < 3) { f = addtempflag(caster->flags, F_SLOWMOVE, 10, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (power < 5) { f = addtempflag(caster->flags, F_SLOWMOVE, 5, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } } else if (spellid == OT_S_BOOSTCONFIDENCE) { flag_t *f; if (!target) { target = targcell->lf; } if (!target) { fizzle(caster); return B_TRUE; } if (isplayer(target)){ msg("You suddenly feel supremely confident!"); } else if (cansee(player, target)) { char targname[BUFLEN]; getlfname(target, targname); msg("%s looks more confident!", targname); } killflagsofid(target->flags, F_FLEEONDAM); killflagsofid(target->flags, F_FLEEONHPPCT); killflagsofid(target->flags, F_FLEEFROM); killflagsofid(target->flags, F_TIMID); killflagsofid(target->flags, F_ATTACKRANGE); addflag(target->flags, F_NOFLEE, B_TRUE, NA, NA, NULL); f = lfhasflag(target, F_MORALE); if (f) f->val[0] = 30; } else if (spellid == OT_S_BURNINGFEET) { char killertext[BUFLEN]; if (!target) { target = targcell->lf; } if (!target) { fizzle(caster); return B_TRUE; } if (!hasbp(target, BP_FEET)) { fizzle(caster); return B_TRUE; } snprintf(killertext, BUFLEN, "%s%s hotfoot spell", castername, getpossessive(castername)); addflag(target->flags, F_HOTFEET, 1, DT_FIRE, NA, killertext); if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_BURNINGWAVE) { cell_t *retcell[MAXRETCELLS]; int nretcell; int i; if (cansee(player, caster)) { msg("%s shoot%s a wave of fire!",castername, isplayer(caster) ? "" : "s"); } // create a line of fire towards the target cell calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell); redrawpause(); // don't set caster's cell on fire! for (i = 1; i < nretcell; i++) { cell_t *c; object_t *o; c = retcell[i]; // set on fire o = addob(c->obpile, "medium fire"); if (o) { setobcreatedby(o, caster); } } redrawresume(); // now burn things for (i = 1; i < nretcell; i++) { cell_t *c; c = retcell[i]; if (c->lf) { char buf[BUFLEN]; char damstring[BUFLEN]; char realcname[BUFLEN]; getlfname(c->lf,buf); if (!isimmuneto(c->lf->flags, DT_FIRE, B_FALSE) && cansee(player, c->lf)) { msg("^%c%s burn%s!",getlfcol(c->lf, CC_BAD), buf,isplayer(c->lf) ? "" : "s"); } real_getlfname(caster, realcname, NULL, B_SHOWALL, B_REALRACE); snprintf(damstring, BUFLEN, "%s%s wave of fire", realcname, getpossessive(realcname)); losehp(c->lf, rolldie(2,10), DT_FIRE, caster, damstring); } } if (cansee(player, caster)) { needredraw = B_TRUE; } } else if (spellid == OT_S_CANINETRACKING) { int howlong; if (hasflag(caster->flags, F_NOSMELL)) { if (isplayer(caster)) { nothinghappens(); } else { fizzle(caster); return B_TRUE; } } else { howlong = getspellduration(20,30,blessed) + (power*10); addtempflag(caster->flags, F_ENHANCESMELL, 4, NA, NA, NULL, howlong); } } else if ((spellid == OT_S_COLDSNAP) || (spellid == OT_S_HEATWAVE)) { object_t *retob[MAXCANDIDATES]; int nretobs,radius,tempmod; char obname[BUFLEN]; if (spellid == OT_S_COLDSNAP) { snprintf(obname, BUFLEN, "unnatural coldness"); tempmod = -30; radius = power+1; } else { snprintf(obname, BUFLEN, "unnatural heat"); tempmod = 30; radius = power-1; } addobsinradius(targcell, radius, DT_ORTH, obname, B_FALSE, B_TRUE, caster, retob, NULL, &nretobs); if (nretobs) { int i,donemsg = B_FALSE; for (i = 0; i < nretobs; i++) { flag_t *f; object_t *o; if (!donemsg && haslos(player, targcell)) { if (spellid == OT_S_COLDSNAP) { msg("A large cloud of vapour appears."); } else { msg("A shimmering haze of heat appears."); } if (seenbyplayer) *seenbyplayer = B_TRUE; donemsg = B_TRUE; } o = retob[i]; // undo temperature effects from adding the ob affect_temperature(o, B_REMOVE); // replace the TEMPMOD flag. killflagsofid(o->flags, F_TEMPMOD); addflag(o->flags, F_TEMPMOD, tempmod, NA, NA, NULL); // now re-apply temperature effects affect_temperature(o, B_ADD); f = hasflag(o->flags, F_OBHP); if (f) { f->val[1] = 40; f->val[0] = f->val[1]; } } } else { fizzle(caster); } } else if (spellid == OT_S_CONFISCATE) { char ch = 'a'; char obname[BUFLEN]; if (targob) { target = targob->pile->owner; } else { object_t *o; // ask for a target cell if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } // which object to take? initprompt(&prompt, "Confiscate which object?"); addchoice(&prompt, '-', "(Cancel)", NULL, NULL, NULL); for (o = target->pack->first ; o ; o = o->next) { if (o->type->obclass->id == OC_GODSTONE) continue; getobname(o, obname, o->amt); addchoice(&prompt, ch, obname, NULL, o, NULL); if (ch == 'z') { ch = 'A'; } else { ch++; } } if (isplayer(caster) && (power > 1)) { // select one getchoice(&prompt); targob = (object_t *)prompt.result; } else { // random one targob = (object_t *) prompt.choice[rnd(0,prompt.nchoices-1)].data; } } if (targob) { if (target) { if (isplayer(target)) { getobname(targob, obname, targob->amt); msg("^%cYour %s vanish%s!", getlfcol(target, CC_VBAD), noprefix(obname), OB1(targob,"es","")); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (isplayer(caster)) { char targname[BUFLEN]; getlfname(target, targname); getobname(targob, obname, targob->amt); msg("%s%s %s appear%s in your pack!", targname, getpossessive(targname), noprefix(obname), OB1(targob,"es","")); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char targname[BUFLEN]; getlfname(target, targname); getobname(targob, obname, targob->amt); msg("^%c%s%s %s vanish%s!", getlfcol(target, CC_VBAD), targname, getpossessive(targname), noprefix(obname), OB1(targob,"es","")); if (seenbyplayer) *seenbyplayer = B_TRUE; } } moveob(targob, caster->pack, ALL); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_CALLLIGHTNING) { int failed = B_FALSE; target = targcell->lf; if (target) { char targname[BUFLEN]; getlfname(target, targname); if (haslos(player, targcell)) { animsky(targcell, '}', C_WHITE); msg("^%c%s %s struck by a bolt of lightning!",getlfcol(target, CC_BAD), targname,is(target)); if (seenbyplayer) *seenbyplayer = B_TRUE; } // outdoors? if (caster && (getraceclass(caster) == RC_GOD)) { losehp(target, rolldie(5,6), DT_ELECTRIC, caster, "a heavenly bolt of lightning"); } else if (target->cell->map->habitat->id == H_FOREST) { losehp(target, rolldie(4,6), DT_ELECTRIC, caster, "a bolt of lightning"); } else { losehp(target, rolldie(3,6), DT_ELECTRIC, caster, "a bolt of lightning"); } } else { failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_CALLWIND) { int failed = B_FALSE; float maxweight; // if no target object... if (!targob) { // ask for a target cell (to take objects from) if (!validatespellcell(caster, &targcell, TT_OBJECT, spellid, power, frompot)) return B_TRUE; if (targcell->obpile->first) { // select object from cell... targob = askobject(targcell->obpile, "Target which object", NULL, NULL, '\0', NULL, B_FALSE); if (!targob) { failed = B_TRUE; } } else { failed = B_TRUE; } } if (!failed) { targcell = caster->cell; // not liftable? if (hasflag(targob->flags, F_NOPICKUP)) { if (isplayer(caster)) { nothinghappens(); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } // too heavy? max weight is now based on our race's weight and intelligence maxweight = getlfweight(caster, B_NOOBS) + (getlfweight(caster, B_NOOBS) * (getstatmod(caster, A_IQ) / 100)); // modify by power maxweight += (10*power); if (getobmass(targob) > maxweight) { cell_t *obloc; char obname[BUFLEN]; obloc = getoblocation(targob); getobname(targob, obname, targob->amt); if (haslos(player, obloc)) { msg("%s lifts slightly, then drops again.",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } pullobto(targob, caster); } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_CALMANIMALS) { int i,ncalmed = 0; int powerleft; int donesomething = B_FALSE; cell_t *c; //powerleft = rolldie(power+1, 4); powerleft = power+2; for (i = 0; i < caster->nlos; i++) { c = caster->los[i]; if (c->lf && (c->lf != caster) && (c->lf->race->raceclass->id == RC_ANIMAL) && lfhasflag(c->lf, F_HOSTILE)) { if (gettr(c->lf) <= powerleft) { powerleft -= gettr(c->lf); makepeaceful(c->lf, caster); ncalmed++; // druids get 50% of the monster's XP value for calming it. // everyone else gets 25%. if (getallegiance(caster) == AL_FRIENDLY) { if (hasjob(caster, J_DRUID)) { awardxpfor(c->lf, 50); } else { awardxpfor(c->lf, 25); } // prevent attacking it and getting xp again. addflag(c->lf->flags, F_XPVAL, 0, NA, NA, NULL); } if (cansee(player, c->lf)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } donesomething++; } else { if (isplayer(caster)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("%s is not affected.", lfname); } } } if (powerleft <= 0) break; } if (ncalmed && isplayer(caster)) { pleasegodmaybe(R_GODMERCY, ncalmed); } if (!donesomething) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_CALMINGSCENT) { // just announce if (isplayer(caster)) { msg("A relaxing aroma surrounds you."); } } else if (spellid == OT_S_CHAINLIGHTNING) { cell_t *hitcell[MAXRETCELLS*2]; // all cells which have been hit in total cell_t *arccell[MAXRETCELLS]; // cells hit this time cell_t *arccell2[MAXRETCELLS]; // cells hit this time int nhitcells = 0, narccells,narccells2; int i; int range; int nsides = 6; range = getspellrange(caster, spellid, power, NULL); if (caster) { // create a line of fire towards the target cell animline(caster->cell, targcell, B_FALSE, '/', '\\', C_WHITE); if (cansee(player, caster)) { msg("%s shoot%s a bolt of electricity!",castername, isplayer(caster) ? "" : "s"); } } else { if (haslos(player, targcell)) { msg("A bolt of electricity arcs out of the air!"); } } if (targcell->lf) { arccell[0] = targcell; narccells = 1; } else { narccells = 0; if (haslos(player, targcell)) { msg("A bolt of electricity fizzles out."); } return B_TRUE; } nhitcells = 0; while (narccells) { int stillseen = B_FALSE; // everyone takes damage, and mark cells as hit for (i = 0; i < narccells; i++) { losehp(arccell[i]->lf, rolldie(3,nsides), DT_ELECTRIC, caster, "an electricity bolt"); hitcell[nhitcells++] = arccell[i]; assert(nhitcells < MAXRETCELLS*2); if (haslos(player, arccell[i])) { stillseen = B_TRUE; needredraw = B_TRUE; if (seenbyplayer) *seenbyplayer = B_TRUE; } } // now range gets halved and damage lowered range /= 2; if (range <= 0) break; if (--nsides <= 0) break; // ie. max of 5 arcs // now arc to other cells with lfs still within range narccells2 = 0; for (i = 0; i < narccells; i++) { int x,y; cell_t *c; for (y = arccell[i]->y - range ; y <= arccell[i]->y + range; y++) { for (x = arccell[i]->x - range ; x <= arccell[i]->x + range; x++) { c = getcellat(targcell->map, x, y); if (c && c->lf && (c->lf != caster) && haslof(arccell[i], c, LOF_NEED, NULL)) { int n,found = B_FALSE; // if this cell hasn't been hit... for (n = 0; n < nhitcells; n++) { if (hitcell[n] == c) { found = B_TRUE; break; } } if (!found) { // will arc to here arccell2[narccells2++] = c; assert(narccells2 < MAXRETCELLS); animline(arccell[i], c, B_FALSE, '/', '\\', C_WHITE); } } } } } // replace arccells for (i = 0; i < narccells2; i++) { arccell[i] = arccell2[i]; } narccells = narccells2; assert(narccells < MAXRETCELLS); if (narccells && stillseen) { msg("The electricity arcs!"); } } // end while narccells } else if (spellid == OT_S_CLEANSINGFIRE) { int i; int pct = 0; int ndone = 0; object_t *o,*nexto; if (!target) target = caster; // find all fires within los for (i = 0; i < target->nlos; i++) { for (o = target->los[i]->obpile->first ; o ; o = nexto) { nexto = o->next; if (ndone >= power) break; if (o->material->id == MT_FIRE) { char obname[BUFLEN]; int oamt; getobname(o, obname, o->amt); oamt = o->amt; killob(o); pct += 30; if (haslos(player, target->los[i])) { msg("%s %s out!", obname, (oamt == 1) ? "goes" : "go"); } ndone++; continue; } if (killflagsofid(o->flags, F_ONFIRE)) { pct += 20; ndone++; continue; } } } limit(&pct, 0, 100); if (pct) { if (isplayer(target)) { msg("^%cYour wounds are healed!", getlfcol(target, CC_GOOD)); } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%c%s%s wounds are healed!", getlfcol(target, CC_GOOD), lfname, getpossessive(lfname)); } gainhp(target, pctof(pct, target->maxhp)); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_CLONE) { // duplicate the caster targcell = getrandomadjcell(caster->cell, &ccwalkable, B_NOEXPAND); if (!targcell) { if (isplayer(caster)) fizzle(caster); return B_TRUE; } if (isplayer(caster) || haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } clonelf(caster, targcell); } else if (spellid == OT_S_CLOUDKILL) { int radius; if (targcell->type->solid) { fizzle(caster); return B_TRUE; } radius = (power/3); addobburst(targcell, radius, DT_COMPASS, "puff of poison gas", caster, LOF_WALLSTOP); if (haslos(player, targcell)) { msg("A cloud of poison gas appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_CHARM) { char targetname[BUFLEN]; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, targetname); if (checkcharm(caster, target)) return B_FALSE; if ((getallegiance(target) == AL_PEACEFUL) || ispetof(target, caster)) { if (isplayer(caster)) { msg("%s is already allied with you!",targetname); } return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); } } else { int howlong; if (isplayer(caster) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(target)) { howlong = 2+(power/5); // ie. 2-4 } else { howlong = getspellduration(2,5,blessed) + power; } if (isplayer(target)) { addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong); } else { // becaomes allied to caster addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong); addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong); } } } else if (spellid == OT_S_CHARMANIMAL) { char targetname[BUFLEN]; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, targetname); // only animals if (getraceclass(target) != RC_ANIMAL) { fizzle(caster); return B_TRUE; } if (checkcharm(caster, target)) return B_FALSE; if (getallegiance(caster) == AL_PEACEFUL) { fizzle(caster); return B_TRUE; } if (ispetof(target, caster)) { if (isplayer(caster)) { msg("%s is already allied with you!",targetname); } return B_TRUE; } // no saving throw, just depends on hit dice. // too powerful? if (gettr(target) <= power) { int howlong; if (isplayer(caster) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } howlong = getspellduration(20,30,blessed) + (power*10); // addtempflag(target->flags, F_CHARMEDBY, caster->id, NA, NA, NULL, howlong); addtempflag(target->flags, F_PETOF, caster->id, NA, NA, NULL, howlong); } else { if (isplayer(caster) || cansee(player, target)) { msg("%s resists.",targetname); if (seenbyplayer) *seenbyplayer = B_TRUE; } // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); } } } else if (spellid == OT_S_CHIBOLT) { char lfname[BUFLEN]; char numbuf[BUFLEN]; numtotext(power, numbuf); // animation anim(caster->cell, targcell, '}', C_LIGHTGREEN); if (isplayer(caster) || cansee(player, caster)) { msg("%s fire%s a bolt of chi energy.",castername,isplayer(caster) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } target = haslf(targcell); if (target) { int dam; char attackname[BUFLEN]; getlfname(target, lfname); dam = rnd(1,4); snprintf(attackname, BUFLEN, "a bolt of chi energy"); // target takes magical damage // always hit if (check_for_block(caster, target, dam, DT_EXPLOSIVE, 999, attackname, B_RANGED)) { } else { if (cansee(player, target)) { msg("^%cA bolt of chi energy hits %s.",getlfcol(target, CC_BAD), lfname); } losehp(target, dam, DT_MAGIC, caster, attackname); } } } else if (spellid == OT_S_CHISTRIKE) { flag_t *f; if (isplayer(caster)) { msg("An aura of chi energy forms around your %s!",getbodypartname(caster, BP_HANDS)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { msg("An aura of chi energy forms around %s%s %s!",castername, getpossessive(castername), getbodypartname(caster, BP_HANDS)); if (seenbyplayer) *seenbyplayer = B_TRUE; } f = addtempflag(caster->flags, F_AWARENESS, DT_EXPLOSIVE, NA, NA, "1d4", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_CHILL) { char lfname[BUFLEN]; int exposedlimbs,dam; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, lfname); // how many body parts are impacted? exposedlimbs = getexposedlimbs(target); dam = rnd(1,exposedlimbs/2); if (isplayer(target)) { msg("^%cThe air around you feels icy cold!", getlfcol(target, CC_BAD)); } if (isplayer(target)) { if (isimmuneto(target->flags, DT_COLD, B_FALSE)) { msg("You feel mildly chilly."); } else { msg("^%cYou feel very cold!", getlfcol(target, CC_BAD)); } if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { if (isimmuneto(target->flags, DT_COLD, B_FALSE)) { msg("%s doesn't seem to mind the cold.", lfname); } else { msg("^%c%s looks very cold!", getlfcol(target, CC_BAD), lfname); } if (seenbyplayer) *seenbyplayer = B_TRUE; } // target takes magical damage // always hit if (!isimmuneto(target->flags, DT_COLD, B_FALSE)) { // partially avoidable if (check_for_block(caster, target, dam, DT_COLD, 999, "the icy air", B_RANGED)) { dam /= 2; } losehp(target, dam, DT_COLD, caster, "a chill spell"); } } else if (spellid == OT_S_COLDBURST) { int range = 1; int x,y; char buf[BUFLEN]; range = 1 + (power / 5); // announce snprintf(buf, BUFLEN, "%s emit%s a blast of icy cold!",castername,isplayer(caster) ? "" : "s"); animradial(caster->cell, range, '}', C_GREY, DT_ORTH, buf, "Something emits a blast of icy cold!"); if (isplayer(caster) || cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } for (y = caster->cell->y - range ; y <= caster->cell->y + range; y++) { for (x = caster->cell->x - range ; x <= caster->cell->x + range; x++) { targcell = getcellat(caster->cell->map, x,y); if (targcell && (getcelldistorth(caster->cell, targcell) <= range) && haslof(caster->cell, targcell, B_FALSE, NULL)) { if (targcell->lf) { if (targcell->lf != caster) { char lfname[BUFLEN]; int dam; // automatic hit getlfname(targcell->lf, lfname); dam = rolldie(1,8)+3; // partially avoidable if (check_for_block(caster, target, dam, DT_COLD, 999, "a burst of coldness", B_RANGED)) { dam /= 2; } else { if (haslos(player, targcell)) { msg("^%c%s %s chilled!",getlfcol(targcell->lf, CC_BAD), lfname,is(targcell->lf)); } } losehp(targcell->lf, dam, DT_COLD, caster, "a burst of coldness"); } } else { // noone there, hit objects. damageallobs(NULL, targcell->obpile, 0, DT_COLD, caster); } } } } } else if (spellid == OT_S_COLDRAY) { char lfname[BUFLEN]; cell_t *retcell[MAXRETCELLS]; int nretcell,i; // animation anim(caster->cell, targcell, '}', C_GREY); if (isplayer(caster) || cansee(player, caster)) { msg("%s shoot%s a blast of ice-cold air.",castername,isplayer(caster) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell); for (i = 1; i < nretcell; i++) { target = haslf(retcell[i]); if (target) { getlfname(target, lfname); // target takes magical damage if (skillcheck(target, SC_DODGE, 90 + (power*10), 0)) { // miss if (cansee(player, target)) { msg("A blast of icy air misses %s.",lfname); } } else { int dam; // hit dam = roll("3d6"); if (power > 1) { // overcast for dragons dam += (power-1); } if (check_for_block(caster, target, dam, DT_COLD, 999, "a blast of icy air", B_RANGED)) { dam /= 2; } else { if (cansee(player, target)) { msg("^%cA blast of icy air assails %s.",getlfcol(target, CC_BAD), lfname); } } losehp(target, dam, DT_COLD, caster, "a blast of ice-cold air"); // ray stops here. break; } } else { damageallobs(NULL, retcell[i]->obpile, 0, DT_COLD, caster); } } } else if (spellid == OT_S_COMMANDUNDEAD) { // mosnters won't cast this. if (!isplayer(caster)) { fizzle(caster); return B_TRUE; } target = targcell->lf; if (!target || !isundead(target)) { fizzle(caster); return B_TRUE; } // saving throw if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); } } else { flag_t *f; if (isplayer(caster) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } // it worked. temporarily make them a pet so that you // can command them. f = addflag(target->flags, F_PETOF, caster->id, NA, NA, NULL); docomms(target); killflag(f); } } else if (spellid == OT_S_CREATEFOOD) { objecttype_t *ot; object_t *o; char obname[BUFLEN]; int i,amt = 1; if (!targcell || targcell->type->solid) { fizzle(caster); return B_TRUE; } for (i = 0; i < power; i++) { int pickagain = B_TRUE; flag_t *f; while (pickagain) { pickagain = B_FALSE; ot = getrandomobofclass(OC_FOOD, NA, NA, NA, NULL, NULL); if (ot) { if (hasflag(ot->flags, F_VENOMSAC)) { pickagain = B_TRUE; continue; } f = hasflag(ot->flags, F_EDIBLE); if (!f || (f->val[1] <= 5)) { pickagain = B_TRUE; continue; } } } o = addobfast(targcell->obpile, ot->id); if (i == 0) { getobname(o, obname, o->amt); amt = o->amt; } } if (haslos(player, targcell)) { if (power >= 8) { msg("A grand feast appears!"); } else if (power >= 5) { msg("A hearty meal appears!"); } else if (power >= 2) { msg("Some light refreshments appear!"); } else { // ie. power == 1 msg("%s appear%s!", obname, (amt == 1) ? "s" : ""); } if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(caster)) { angergodmaybe(R_GODFIRE, 50, GA_HERESY); } } else if (spellid == OT_S_CREATEMONSTER) { lifeform_t *newlf; race_t *r = NULL; if (!targcell) { if ((power >= 5) && isplayer(caster)) { // control location if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power, frompot)) return B_TRUE; // make sure it's empty if (!cellwalkable(NULL, targcell, NULL)) { targcell = NULL; } } else { // get random adjacent cell targcell = getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND); } } if (!targcell) { fizzle(caster); return B_TRUE; } // determine type of mosnter if (getforcedspellrace(caster, spellid, buf, NULL)) { } else if ((power >= 7) && isplayer(caster)) { // ask what kind of monster askstring("Create what kind of monster", '?', buf, BUFLEN, NULL); } else { r = getreallyrandomrace(RC_ANY, NULL); snprintf(buf, BUFLEN, "%s", r->name); } // add the monster newlf = summonmonster(caster, targcell, R_SPECIFIED, buf, PERMENANT, B_FALSE); if (newlf) { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } rv = B_FALSE; } else { // didn't work for some reason if (isplayer(caster)) { nothinghappens(); } return B_TRUE; } } else if (spellid == OT_S_CLEARLEVEL) { int x,y; cell_t *c; for (y = 0; y < caster->cell->map->h ; y++) { for (x = 0; x < caster->cell->map->w ; x++) { c = getcellat(caster->cell->map, x, y); if (c) { if (c->lf && isplayer(c->lf)) { } else { clearcell(c); setcelltype(c, c->map->habitat->solidcelltype); c->locked = B_FALSE; } } } } needredraw = B_TRUE; } else if (spellid == OT_S_CREATEVAULT) { vault_t *v; int vx,vy; int vw,vh; int newroomid; // ask for a vaulttype v = askvault("Create which vault?"); newroomid = caster->cell->map->nrooms; if (createvault(caster->cell->map, newroomid, v, &vw, &vh, &vx, &vy)) { msg("Couldn't create a vault."); } else { char ch; cell_t *c; // link the new vault to the rest of the map //c = getcellat(caster->cell->map, vx, vy); linkexits(caster->cell->map, newroomid); msg("BAM! A vault has appeared nearby."); more(); needredraw = B_TRUE; ch = askchar("Teleport to the new vault", "yn","y", B_TRUE, B_FALSE); if (ch == 'y') { condset_t cs; initcondv(&cs, CC_WALKABLEFOR, B_TRUE, caster->id, CC_HASROOMID, B_TRUE, caster->cell->map->nrooms-1, CC_NONE); //c = getrandomroomcell(caster->cell->map, caster->cell->map->nrooms-1, WE_WALKABLE); c = getcell_cond(caster->cell->map, &cs); if (!c) { msg("Oops, couldn't find a free space."); return B_FALSE; } teleportto(caster, c, B_TRUE); return B_FALSE; } } } else if (spellid == OT_S_CRYSTALARM) { object_t *o; enum BODYPART bp[4]; int nbp, donesomething = B_FALSE,i; targcell = caster->cell; target = caster; nbp = (power/3)+1; bp[0] = BP_BODY; bp[1] = BP_HEAD; bp[2] = BP_HANDS; bp[3] = BP_FEET; for (i = 0; (i < 4) && (i < nbp); i++) { if (hasbp(target, bp[i]) && !getequippedob(target->pack, bp[i])) { switch (bp[i]) { case BP_BODY: default: o = addob(target->pack, "ice crystal armour"); break; case BP_HEAD: o = addob(target->pack, "ice crystal helmet"); break; case BP_HANDS: o = addob(target->pack, "ice crystal gauntlets"); break; case BP_FEET: o = addob(target->pack, "ice crystal boots"); break; } if (o) { if (canwear(target, o, BP_NONE)) { char obname[BUFLEN]; flag_t *f; // announce getobname(o, obname, 1); if (isplayer(target)) { msg("^%c%s forms %s your %s!", getlfcol(target, CC_VGOOD), obname, getbodypartequipname(bp[i]), getbodypartname(target, bp[i])); } else if (cansee(player, target)) { msg("^%c%s forms %s %s%s %s!", getlfcol(target, CC_VGOOD),obname, getbodypartequipname(bp[i]), castername, getpossessive(castername), getbodypartname(target, bp[i])); } // don't use "wear" because we don't want it being announced. addflag(o->flags, F_EQUIPPED, bp[i], -1, -1, NULL); // set its values f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = power*5; f->val[1] = power*5; } addflag(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL); donesomething = B_TRUE; } else { killob(o); } } } } if (!donesomething) { fizzle(caster); stopspell(caster, spellid); return B_TRUE; } } else if (spellid == OT_S_CRYSTALSHIELD) { object_t *o; targcell = caster->cell; target = caster; o = getequippedob(target->pack, BP_SECWEAPON); if (o) { if (isplayer(caster)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("Your %s shimmers like a crystal for a moment.", noprefix(obname)); } fizzle(caster); stopspell(caster, spellid); return B_TRUE; } o = addob(target->pack, "ice crystal shield"); if (o) { if (canwear(target, o, BP_SECWEAPON)) { flag_t *f; enum LFSIZE sz; if (power <= 2) { sz = SZ_SMALL; } else if (power <= 4) { sz = SZ_MEDIUM; } else { sz = SZ_LARGE; } // announce if (isplayer(target)) { msg("^%cA %s shield of shimmering ice forms in your hand!", getlfcol(target, CC_GOOD), getsizetext(sz)); } else if (cansee(player, target)) { msg("^%cA %s shield of shimmering ice forms in %s%s hand!",getlfcol(target, CC_GOOD), getsizetext(sz), castername, getpossessive(castername)); } // don't use "wear" because we don't want it being announced. addflag(o->flags, F_EQUIPPED, BP_SECWEAPON, -1, -1, NULL); // set its values f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = power*5; f->val[1] = power*5; } addflag(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL); } else { killob(o); fizzle(caster); stopspell(caster, spellid); return B_TRUE; } } else { fizzle(caster); stopspell(caster, spellid); return B_TRUE; } } else if (spellid == OT_S_CUREPOISON) { flag_t *retflag[MAXCANDIDATES]; int nretflags,i,donesomething = B_FALSE; if (!target) { target = targcell->lf; } if (!target) { fizzle(caster); return B_TRUE; } getflags(target->flags, retflag, &nretflags, F_INCUBATING, F_POISONED, F_NONE); for (i = 0; i < nretflags; i++) { poisontype_t *pt; pt = findpoisontype(retflag[i]->val[0]); if (pt->severity == PS_CURSE) { } else { killflag(retflag[i]); donesomething = B_TRUE; } } if (donesomething && cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } else { fizzle(caster); } } else if (spellid == OT_S_CURSE) { int ndone = 0,i; object_t *o,*poss[MAXPILEOBS]; int nposs = 0; if (!target) target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } for (o = target->pack->first ; o ; o = o->next) { if (isequipped(o) && !iscursed(o) && !hasflag(o->flags, F_NOBLESS)) { poss[nposs++] = o; } } for (i = 0 ; nposs && (i < power); i++) { int n; // pick a random one o = poss[rnd(0,nposs-1)]; curseob(o); if (o->blessed == B_CURSED) { // also apply a penalty if (isweapon(o) || isarmour(o)) { modbonus(o, -1); } } // remove this from the list for (n = i; n < nposs-1; n++) { poss[n] = poss[n+1]; } nposs--; ndone++; } if (ndone) { if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_DANCINGFLAME) { int i; int ndone = 0; object_t *o; if (!target) target = caster; // find all fires within los for (i = 0; i < target->nlos; i++) { enum OBTYPE fireid = OT_NONE; o = hasobofmaterial(target->los[i]->obpile, MT_FIRE); if (o) { fireid = o->type->id; } else { o = hasobwithflag(target->los[i]->obpile, F_ONFIRE); if (o) { fireid = OT_FIRESMALL; } } if (fireid != OT_NONE) { int dir; cell_t *c; // any nearby lfs? for (dir = DC_N; dir <= DC_NW; dir++) { c = getcellindir(target->los[i], dir); if (c && c->lf) { object_t *newob; newob = addobfast(c->obpile, OT_FIRESMALL); if (newob) { ndone++; if (haslos(player, c)) { char obname[BUFLEN]; getobname(newob, obname, 1); msg("%s spreads!", obname); } } } } } } if (!ndone) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_DARKNESS) { int radius; if (!targcell) targcell = caster->cell; // create light objects radius = power / 3; limit(&radius, 1, NA); makelitradius(targcell, radius, OT_MAGICDARK, rnd(5,10)+(power*2), power ); if (blessed || (power >= 5)) { modillumination(targcell->map, D_DARKER); } if (haslos(player, targcell)) { msg("A cloud of darkness descends nearby!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (getcelldist(player->cell, targcell) <= radius) { msg("A cloud of darkness descends on you!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_DECAYFIELD) { obpile_t *op; object_t *o,*nexto; int range = 0, ncells,i,lfdam,obdam,walldam; cell_t *cell[MAXCANDIDATES]; // default to radiating out from the caster's cell if (!targcell) { if (caster) { targcell = caster->cell; } else { // fail return B_TRUE; } } // determine damage (will do double this to object) lfdam = (power*3)/2; obdam = power*3; walldam = power*5; // determine radius range = (power/2)+1; // announce if (caster && (targcell == caster->cell)) { snprintf(buf, BUFLEN, "%s radiate%s a field of decay!",castername,isplayer(caster) ? "" : "s"); } else { snprintf(buf, BUFLEN, "A field of decay radiates outwards!"); } animradial(targcell, range, '}', C_INDIGO, DT_ORTH, buf, "Something radiates a field of decay!"); // don't affect centre cell getradiuscells(targcell, range, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_FALSE, cell, &ncells, B_FALSE); for (i = 0; i < ncells; i++) { if (seenbyplayer) { if (haslos(player, targcell)) { *seenbyplayer = B_TRUE; } } if (cell[i]->lf && !isundead(cell[i]->lf) && isorganicmat(getlfmaterial(cell[i]->lf))) { char damstr[BUFLEN]; if (caster) { char cname[BUFLEN]; real_getlfnamea(caster, cname, NULL, B_SHOWALL, B_REALRACE); snprintf(damstr, BUFLEN, "%s%s field of decay spell.", cname, getpossessive(cname)); } else { strcpy(damstr, "a field of decay spell."); } if (isplayer(cell[i]->lf)) { msg("^%cPieces of your body decay away!", getlfcol(cell[i]->lf, CC_BAD)); } else if (cansee(player, cell[i]->lf)) { char tname[BUFLEN]; getlfname(targcell->lf, tname); msg("Pieces of %s decay away!", tname); } losehp(cell[i]->lf, lfdam, DT_DECAY, caster, damstr); } op = cell[i]->obpile; // damage all organic objects here for (o = op->first ; o ; o = nexto) { nexto = o->next; if (isorganicmat(o->material->id)) { if (!cell[i]->lf && haslos(player, cell[i])) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s decays!", obname); } takedamage(o, obdam, DT_DECAY, caster); } } // damage all organic walls here if (isorganicmat(cell[i]->type->material->id)) { damagecell(cell[i], walldam, DT_DECAY, caster); } } } else if (spellid == OT_S_DEATHKEEN) { if (!isnighttime()) { fizzle(caster); return B_TRUE; } if (isplayer(caster)) { msg("You wail with the power of death!"); } noise(caster->cell, NULL, NC_OTHER, SV_TALK, "the dread wail of a banshee", NULL); makenoise(caster, N_DEATHKEEN); // all in range must pass a magic resistance check or die for (target = caster->cell->map->lf ; target ; target = target->next) { if (target != caster) { int nwalls; if (canhear(target, caster->cell, SV_TALK, &nwalls)) { if (nwalls == 0) { if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { } else { losehp(target, target->hp, DT_SONIC, caster, "a banshee's wail"); } } } } } } else if (spellid == OT_S_DELAYDEATH) { if (!target) { target = caster; } if (isplayer(target)) { msg("^gYou bend your psionic will towards defying death!"); if (seenbyplayer) *seenbyplayer = B_TRUE; statdirty = B_TRUE; } } else if (spellid == OT_S_DETECTAURA) { if (isplayer(caster)) { object_t *o; char obname[BUFLEN]; int somethinghappened = B_FALSE; for (o = caster->pack->first ; o ; o = o->next) { if (o->blessed == B_BLESSED) { getobname(o, obname, o->amt); msg("You sense a holy aura from your %s!", noprefix(obname)); somethinghappened = B_TRUE; } else if (o->blessed == B_CURSED) { getobname(o, obname, o->amt); msg("Your sense an evil aura from your %s!", noprefix(obname)); somethinghappened = B_TRUE; } } // now check objects on the ground... for (o = caster->cell->obpile->first ; o ; o = o->next) { if (o->blessed == B_BLESSED) { getobname(o, obname, o->amt); msg("You sense a holy aura from %s!", obname); somethinghappened = B_TRUE; } else if (o->blessed == B_CURSED) { getobname(o, obname, o->amt); msg("You sense an evil aura from %s!", obname); somethinghappened = B_TRUE; } } if (somethinghappened) { if (seenbyplayer) *seenbyplayer = B_TRUE; // we know that everything else is uncursed for (o = caster->pack->first ; o ; o = o->next) { o->blessknown = B_TRUE; } for (o = caster->cell->obpile->first ; o ; o = o->next) { o->blessknown = B_TRUE; } } else { if (isplayer(caster)) nothinghappens(); } } else { // monsters can't use this } } else if (spellid == OT_S_DIG) { int numseen = 0; cell_t *c; int ndigs; char ch; int dir; if (targcell) { if (targcell == caster->cell) { dir = D_DOWN; } else { dir = getdirtowards(caster->cell, targcell, NULL, B_FALSE, DT_COMPASS); } } else { // don't need line of fire OR sight! //if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power, frompot)) return B_TRUE; ch = askchar("Dig in which direction (- to cancel)", "yuhjklbn.-<>","-", B_FALSE, B_TRUE); if ((ch == '.') || (ch == '-') || !ch) { fizzle(caster); return B_TRUE; } else if (ch == '<') { if (seenbyplayer && haslos(player, caster->cell)) *seenbyplayer = B_TRUE; dir = D_UP; } else if (ch == '>') { if (seenbyplayer && haslos(player, caster->cell)) *seenbyplayer = B_TRUE; dir = D_DOWN; } else { dir = chartodir(ch); } } if (dir == D_UP) { return digup(caster, NULL); } else if (dir == D_DOWN) { return digdown(caster, NULL); } if (dir == DT_NONE) { fizzle(caster); return B_TRUE; } ndigs = 0; // get rid of rock/flesh in the cells... c = getcellindir(caster->cell, dir); while (c && (ndigs <= power)) { int seenthiscell = B_FALSE; if (haslos(player, c)) seenthiscell = B_TRUE; if (c->type->solid) { if (isdiggable(c, OT_S_DIG)) { int d2; if (seenthiscell) { msg("%s %s collapses!", needan(c->type->name) ? "An" : "A", c->type->name); numseen++; if (seenbyplayer) *seenbyplayer = B_TRUE; } setcelltype(c, c->map->habitat->emptycelltype); ndigs++; if (c->type->material->id == MT_STONE) { // debris shower for (d2 = DC_N; d2 <= DC_NW; d2++) { cell_t *c2; c2 = getcellindir(c, d2); if (c2 && !c2->type->solid && c2->lf) { if (!isimmuneto(c2->lf->flags, DT_PROJECTILE, B_FALSE)) { int dam; dam = roll("2d6"); if (check_for_block(caster, c2->lf, dam, DT_PROJECTILE, 999, "flying debris", B_RANGED)) { } else { if (cansee(player, c2->lf)) { char lfname[BUFLEN]; getlfname(c2->lf, lfname); msg("^%c%s %s showered with debris!", getlfcol(c2->lf, CC_BAD), lfname, isplayer(c2->lf) ? "are" : "is"); } losehp(c2->lf, dam, DT_PROJECTILE, NULL, "flying debris"); } } } } addobsinradius(c, 1, DT_COMPASS, "5-10 stones", B_TRUE, B_NOCENTRE, NULL, NULL, NULL, NULL); addob(c->obpile, "5-10 stones"); } else if (c->type->material->id == MT_BLOOD) { addobsinradius(c, 1, DT_COMPASS, "pool of blood", B_TRUE, B_NOCENTRE, NULL, NULL, NULL, NULL); addob(c->obpile, "pool of blood"); } } else { // stop. break; } } else { object_t *o; for (o = c->obpile->first ; o ; o = o->next) { if (hasflag(o->flags, F_IMPASSABLE)) { char obname[BUFLEN]; // destroy this object then stop. addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); if (seenthiscell) { if (!hasflag(o->flags, F_OBDIETEXT)) { getobname(o, obname, o->amt); msg("%s crumble%s to dust!", obname, OBS1(o)); } if (seenbyplayer) *seenbyplayer = B_TRUE; } ndigs++; } } } ndigs++; // go to next cell c = getcellindir(c, dir); } // announce destruction of any walls seen /* if (numseen) { msg("The wall%s crumble%s to dust!", (numseen == 1) ? "" : "s", (numseen == 1) ? "s" : "" ); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (ndigs == 0) { if (isplayer(caster)) nothinghappens(); } */ if (ndigs == 0) { if (isplayer(caster)) nothinghappens(); } } else if (spellid == OT_S_DISRUPTUNDEAD) { target = targcell->lf; if (!target || !isundead(target)) { fizzle(caster); return B_TRUE; } if (isplayer(target)) { msg("^%cYou feel your body's essence unraveling!", getlfcol(target, CC_BAD)); } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("%s convulses.", lfname); } // use direct damage rather than holy, because otherwise it might be increased // due to vulnerabilities losehp(target, roll("3d6") + power, DT_DIRECT, caster, "disruption"); } else if (spellid == OT_S_DISORIENT) { target = targcell->lf; if (lfhasflag(target, F_ASLEEP) || !ischarmable(target)) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { return B_FALSE; } else { int newdir; // make them face a random (different) direction newdir = getdiraway(targcell, caster->cell, NULL, B_FALSE, DT_ORTH, B_FALSE); // announce - do this before turning in case it makes the // caster be out of sight. if (isplayer(target)) { msg("You are twisted around to face %s!", getdirname(target->facing)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char tname[BUFLEN]; getlfname(target, tname); msg("%s spins around to face %s!", tname, getdirname(target->facing)); if (seenbyplayer) *seenbyplayer = B_TRUE; } setfacing(target, newdir); // ai loses target info loseaitargets(target); } } else if (spellid == OT_S_DETECTLIFE) { if (!target) { if (targcell) { target = targcell->lf; } else { target = caster; } } if (isplayer(target)) { int howlong,radius; howlong = getspellduration(40,80,blessed) + (power*2); radius = power * 10; addtempflag(target->flags, F_DETECTLIFE, 10, (power >= 8) ? B_TRUE : NA, NA, NULL, howlong); } else { // monsters can't use this } } else if (spellid == OT_S_DETECTMAGIC) { target = caster; if (isplayer(caster)) { int howlong; howlong = getspellduration(40,90,blessed)+(power*2); addtempflag(target->flags, F_DETECTMAGIC, B_TRUE, NA, NA, NULL, howlong); } else { // monsters can't use this } /* } else if (spellid == OT_S_DETECTMETAL) { target = caster; if (isplayer(caster)) { int howlong = 15; howlong = getspellduration(10,20,blessed); addtempflag(target->flags, F_DETECTMETAL, 10, NA, NA, NULL, howlong); } else { // monsters can't use this } */ } else if (spellid == OT_S_DETECTOBS) { target = caster; if (isplayer(caster)) { int howlong,radius; howlong = getspellduration(25,35,blessed) + (power*10); radius = power*10; addtempflag(target->flags, F_DETECTOBS, radius, NA, NA, NULL, howlong); } else { // monsters can't use this } } else if (spellid == OT_S_DETECTPOISON) { if (isplayer(caster)) { int npoisoned = 0; int selfpoisoned = B_FALSE; int n,i; flag_t *retflag[MAXCANDIDATES]; int nretflags; getflags(caster->flags, retflag, &nretflags, F_INCUBATING, F_POISONED, F_NONE); for (i = 0; i < nretflags ; i++) { flag_t *f; poisontype_t *pt; f = retflag[i]; pt = findpoisontype(f->val[0]); msg("You detect %s in your body.", pt->name); f->known = B_TRUE; selfpoisoned = B_TRUE; } for (n = 0; n < caster->nlos; n++) { object_t *o; for (o = caster->los[n]->obpile->first ; o ; o = o->next) { flag_t *f; int ispoisoned = B_FALSE, isdecayed = B_FALSE; if (isrotting(o)) { isdecayed = B_TRUE; } else if ((o->type->id == OT_POT_POISON) || (hasflag(o->flags, F_PURIFIESTO))) { ispoisoned = B_TRUE; } else if ((o->type->id == OT_TRAPARROWP) || (o->type->id == OT_TRAPNEEDLEP) || (o->type->id == OT_TRAPGAS)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s %s revealed!", obname, OB1(o,"is","are")); npoisoned++; killflagsofid(o->flags, F_SECRET); } else { foreach_bucket(b) { for (f = o->flags->first[b] ; f ; f = f->next) { if (f->id == F_POISONED) { ispoisoned = B_TRUE; f->known = B_TRUE; break; } else if ((f->id == F_HITCONFER) && (f->val[0] == F_POISONED)) { ispoisoned = B_TRUE; f->known = B_TRUE; break; } } } f = hasflag(o->flags, F_TRAPPED); if (f) { if ((f->val[0] == OT_TRAPARROWP) || (f->val[0] == OT_TRAPNEEDLEP) || (f->val[0] == OT_TRAPGAS)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("A poison trap on %s is revealed!", obname); npoisoned++; f->val[2] = B_TRUE; } } } if (ispoisoned || isdecayed) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s %s %s!", obname, OB1(o,"is","are"), ispoisoned ? "poisoned" : "rotted"); npoisoned++; } if (o->type->id == OT_POT_POISON) { makeknown(o->type->id); } } } if (!npoisoned) { msg("You can't detect any %spoison nearby.", selfpoisoned ? "other " : ""); } } // end if isplayer } else if (spellid == OT_S_DETONATE) { // don't need line of fire! explodecells(targcell, 20+(power*2), B_TRUE, NULL, power / 4, DT_ORTH, B_TRUE, caster); if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_DETONATEDELAY) { addobfast(targcell->obpile, OT_VIBCLOUD); if (haslos(player, targcell)) { msg("^%cThe air nearby begins to vibrate violently...", getlfcol(player, CC_VBAD)); more(); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_EQANDOP) { flag_t *f; f = addtempflag(caster->flags, F_REFLECTION, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (spellid == OT_S_ETHEREALSTEED) { int howlong; howlong = power*5; addtempflag(caster->flags, F_AUTOCREATEOB, 0, NA, NA, "whirlwind", howlong); if (isplayer(caster)) { msg("^%cA spinning whirlwind lifts you off the ground!", getlfcol(caster, CC_GOOD)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { msg("^%cA spinning whirlwind lifts %s off the ground!", getlfcol(caster, CC_GOOD), castername); if (seenbyplayer) *seenbyplayer = B_TRUE; } addtempflag(caster->flags, F_LEVITATING, B_TRUE, NA, NA, NULL, howlong); addtempflag(caster->flags, F_DTIMMUNE, DT_PROJECTILE, NA, NA, NULL, howlong); } else if (spellid == OT_S_EVAPORATE) { cell_t *cell[MAXCANDIDATES]; int i,ncells; int nseen = 0; int nsteamseen = 0; // don't need line of fire! getradiuscells(targcell, power, DT_ORTH, B_FALSE, LOF_NEED, B_TRUE, cell, &ncells, B_FALSE); for (i = 0; i < ncells; i++) { int cellseen = B_FALSE; object_t *o,*nexto; if (haslos(player, cell[i])) { cellseen = B_TRUE; } for (o = cell[i]->obpile->first ; o ; o = nexto) { int dosteam = B_FALSE; nexto = o->next; if (getmaterialstate(o->material->id) == MS_LIQUID) { // if 20kilos or more, make steam if (getobmass(o) >= 25) { dosteam = B_TRUE; if (cellseen) nsteamseen++; } nseen++; removeob(o, ALL); } else if ((o->material->id == MT_ICE) || (o->type->obclass->id == OC_POTION)) { char obname[BUFLEN]; dosteam = B_TRUE; if (cellseen) nsteamseen++; nseen++; getobname(o, obname, o->amt); msg("%s boil%s and explode%s!", obname, OBS1(o), OBS1(o)); removeob(o, ALL); } if (dosteam) { addob(cell[i]->obpile, "cloud of steam"); } } if (cell[i]->lf) { for (o = cell[i]->lf->pack->first ; o ; o = nexto) { nexto = o->next; if ((o->material->id == MT_ICE) || (o->type->obclass->id == OC_POTION)) { char obname[BUFLEN]; if (cellseen) nsteamseen++; nseen++; getobname(o, obname, o->amt); msg("%s boil%s and explode%s!", obname, OBS1(o), OBS1(o)); removeob(o, ALL); addob(cell[i]->obpile, "cloud of steam"); } } } } if (nsteamseen) { if (seenbyplayer) *seenbyplayer = B_TRUE; msg("A huge cloud of steam appears!"); } else if (nseen) { if (seenbyplayer) *seenbyplayer = B_TRUE; msg("Some nearby liquid evaporates!"); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_EXORCISE) { if (!target) target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (lfhasflag(target, F_SUMMONEDBY) || (getraceclass(target) == RC_DEMON)) { if (canexorcise(caster, target, power)) { unsummon(target, B_TRUE); } else { if (isplayer(caster)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%c%s is too powerful for you to exorcise!", getlfcol(caster, CC_BAD), lfname); } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("%s resists the effects of an exorcism.", lfname); } } } else { if (isplayer(caster)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("%s seems unaffected.", lfname); } // this counts as a failure since you targetted the // wrong kind of creature return B_TRUE; } } else if (spellid == OT_S_EXORCISEMASS) { int i,ndone = 0; for (i = 1; i < caster->nlos; i++) { cell_t *c; c = caster->los[i]; if (c->lf && (lfhasflag(c->lf, F_SUMMONEDBY) || (getraceclass(c->lf) == RC_DEMON)) ) { if (canexorcise(caster, c->lf, power)) { unsummon(c->lf, B_TRUE); ndone++; } else { if (!isplayer(caster) && cansee(player, target)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("%s resists the effects of an exorcism.", lfname); } } } } if (ndone == 0) { if (isplayer(caster)) { msg("Your exorcism fails."); } return B_TRUE; } } else if (spellid == OT_S_EXPLODEMETAL) { float totalmass = 0; object_t *o, *nexto; // how much metal is there? for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; // destroy metal items on the ground if (ismetal(o->material->id)) { totalmass += getobmass(o); removeob(o, o->amt); } } // destroy objects right away if (removedeadobs(targcell->obpile)) { if (isplayer(caster)) angergodmaybe(R_GODNATURE, 20, GA_ATTACKOBJECT); } // explosion, based on size... if (totalmass > 0) { // announce if (totalmass < 10) { // little one explodecells(targcell, totalmass*5, B_FALSE, NULL, 0, DT_COMPASS, B_TRUE, caster); } else { explodecells(targcell, totalmass*5, B_TRUE, NULL, 1, DT_COMPASS, B_TRUE, caster); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_DISPERSAL) { cell_t *c = NULL; object_t *o, *nexto; int donesomething = B_FALSE; if (!targcell) { if (isplayer(caster)) { fizzle(caster); return B_TRUE; } } target = targcell->lf; if (target) { int resisted = B_FALSE, failed = B_FALSE; // if you cast this at yourself, only check for magic resistance if you have the // flag. // other people get a check anyway. if (target == caster) { if (getmr(target) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { resisted = B_TRUE; } } else { if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { resisted = B_TRUE; } } if (!resisted) { c = getrandomcell_forteleport(targcell->map, target); if (c) { if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; donesomething = B_TRUE; } teleportto(target, c, B_FALSE); } else { // nothing happens. failed = B_TRUE; } } if (resisted || failed) { if (isplayer(target)) { msg("You flicker."); if (seenbyplayer) *seenbyplayer = B_TRUE; donesomething = B_TRUE; } else if (haslos(player, targcell)) { getlfname(target, buf); msg("%s flickers.",buf); if (seenbyplayer) *seenbyplayer = B_TRUE; donesomething = B_TRUE; } } } for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; c = getrandomcell_forteleport(targcell->map, NULL); if (c) { getobname(o, buf, o->amt); if (haslos(player, targcell) && canseeob(player, o)) { msg("%s disappear%s!", buf, OBS1(o)); if (seenbyplayer) *seenbyplayer = B_TRUE; donesomething = B_TRUE; } moveob(o, c->obpile, o->amt); if (haslos(player, c) && canseeob(player, o)) { msg("%s appear%s nearby!", buf, OBS1(o)); } } } if (isplayer(caster) && !donesomething) { nothinghappens(); } rv = B_FALSE; } else if (spellid == OT_S_DRAINIQ) { target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (isplayer(target)) { msg("^%cYour brain cells are blasted!",getlfcol(target, CC_VBAD)); } else if (isplayer(caster)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%cYou blast %s%s brain cells!", getlfcol(target, CC_VBAD), lfname, getpossessive(lfname)); } if (!ischarmable(target) && (reason != E_ALREADYUSING) && (reason != E_LOWIQ)) { if (isplayer(caster)) { char tname[BUFLEN]; getlfname(target, tname); msg("%s %s unaffected.", tname, isplayer(target) ? "are" : "is"); } return B_TRUE; } statdrain(target, A_IQ, roll("2d6") + rolldie(power,6),SC_NONE,-1,caster); } else if (spellid == OT_S_DRAINLIFE) { char lfname[BUFLEN]; target = haslf(targcell); if (target) { getlfname(target, lfname); } else { fizzle(caster); return B_TRUE; } if (!isimmuneto(target->flags, DT_NECROTIC, B_FALSE)) { // animation (opposite dir) anim(targcell, caster->cell, '%', C_MAGENTA); } if (isplayer(caster) || cansee(player, caster)) { if (isimmuneto(target->flags, DT_NECROTIC, B_FALSE)) { msg("^%c%s suck%s death from %s!",getlfcol(target, CC_BAD), castername,isplayer(caster) ? "" : "s", lfname); } else { msg("^%c%s suck%s life from %s!",getlfcol(target, CC_BAD), castername,isplayer(caster) ? "" : "s", lfname); } if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (isplayer(target)) { msg("^%cYou feel your life force draining away!", getlfcol(target, CC_BAD)); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (target) { int amt; if (isimmuneto(target->flags, DT_NECROTIC, B_FALSE)) { // target gains hp amt = rnd(1,4) + power; gainhp(target, amt); // caster loses it amt = losehp(caster, amt, DT_NECROTIC, caster, "lifeforce drain"); } else { // target loses hp amt = losehp(target, rnd(1,4) + power, DT_NECROTIC, caster, "lifeforce drain"); // caster gains it gainhp(caster, amt); } } } else if (spellid == OT_S_ENERGYBLAST) { int range; int x,y; char buf[BUFLEN]; if (caster && !targcell) targcell = caster->cell; range = 2 + (power / 4); snprintf(buf, BUFLEN, "%s emit%s a radial blast of energy!",castername,isplayer(caster) ? "" : "s"); animradial(targcell, range, '}', C_CYAN, DT_COMPASS, buf, "Something emits a radial blast of energy!"); if (isplayer(caster) || haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } for (y = targcell->y - range ; y <= targcell->y + range; y++) { for (x = targcell->x - range ; x <= targcell->x + range; x++) { cell_t *c; c = getcellat(targcell->map, x,y); if (c && (getcelldist(targcell, c) <= range)) { if (c->lf && (c->lf != caster) && haslof(targcell, c, B_FALSE, NULL)) { int dam; dam = roll("2d6"); // automatic hit if (check_for_block(caster, c->lf, dam, DT_MAGIC, 999, "an energy blast", B_RANGED)) { } else { if (isplayer(c->lf)) { msg("^%cA blast of energy hits you!", getlfcol(c->lf, CC_BAD)); } else if (cansee(player, c->lf)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("^%cA blast of energy hits %s.", getlfcol(c->lf, CC_BAD), lfname); } losehp(c->lf, dam, DT_MAGIC, caster, "an energy blast"); } } } } } } else if (spellid == OT_S_FEAR) { char targname[BUFLEN]; // ask for target target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, targname); if (isplayer(caster)) { msg("You emit an aura of fear!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { msg("%s emits an aura of fear!",castername); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (lfhasflag(target, F_ASLEEP)) { if (isplayer(target)) { msg("^%cYou suffer terrifying nightmares!", getlfcol(target, CC_BAD)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { msg("^%c%s thrashes about in its sleep!",getlfcol(target, CC_BAD), targname); if (seenbyplayer) *seenbyplayer = B_TRUE; } losehp(target, roll("2d6"), DT_DIRECT, caster, "terrifying nightmares"); } else { scare(target, caster, 5+rnd(1,power), 6+power); } } else if (spellid == OT_S_FEEBLEMIND) { target = targcell->lf; if ((getattr(target, A_IQ) <= 15) || spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (cansee(player, target)) { getlfname(target, buf); msg("%s %s momentarily foolish.", buf, isplayer(target) ? "feel" : "looks"); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } if (target) { int howlong = 15; flag_t *f; // already feebleminded? foreach_bucket(b) { for (f = target->flags->first[b]; f ; f = f->next) { if ((f->id == F_ATTRMOD) && (f->val[0] == A_IQ) && (f->obfrom == OT_S_FEEBLEMIND)) { if (cansee(player, target)) { getlfname(target, buf); msg("%s %s momentarily more foolish.", buf, isplayer(target) ? "feel" : "looks"); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } } } howlong = getspellduration(5,10,blessed) + power; // always set to 3 (ie. animal) f = addtempflag(target->flags, F_ATTRSET, A_IQ, 15, NA, NULL, howlong); f->obfrom = OT_S_FEEBLEMIND; if (seenbyplayer) *seenbyplayer = B_TRUE; } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_FIREWORKS) { lifeform_t *poss[MAXCANDIDATES]; int nposs = 0; if (isplayer(caster)) { msg("^wA burst of fireworks explodes around you!"); } else if (cansee(player, caster)) { msg("^wA burst of fireworks explodes around %s!", castername); } for (target = caster->cell->map->lf ; target ; target = target->next) { if (target == caster) continue; if (haslos(target, caster->cell)) { poss[nposs] = target; nposs++; if (nposs >= MAXCANDIDATES) break; } } if (nposs) { int i,n,idx; for (i = 0; (i < power) && nposs; i++) { idx = rnd(0,nposs-1); stun(poss[idx], 2); for (n = idx; n < nposs-1; n++) { poss[n] = poss[n+1]; } nposs--; } } } else if (spellid == OT_S_FLASH) { if (isplayer(caster) || cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } brightflash(caster->cell, 2 + (power/4), caster); } else if (spellid == OT_S_FLAYFLESH) { char targetname[BUFLEN]; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (target->race->material->id != MT_FLESH) { fizzle(caster); return B_TRUE; } if (isplayer(target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; msg("^%cUnseen forces rip into your flesh!", getlfcol(target, CC_VBAD)); } else if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; getlfname(target, targetname); msg("^%cUnseen forces rip into %s%s flesh!", getlfcol(target, CC_VBAD), targetname, getpossessive(targetname)); } criticalhit(caster, target, getrandomcorebp(target, NULL), NULL, rnd(1,6), DT_SLASH); } else if (spellid == OT_S_GLYPHWARDING) { char buf[BUFLEN]; int gpower; gpower = 2 + (power / 2); limit(&gpower, 1, NA); snprintf(buf, BUFLEN, "^g*WARD%d*^n", gpower); writetextonground(caster, caster->cell, buf, power*5); } else if (spellid == OT_S_FLOATINGDISC) { lifeform_t *newlf; // get random adjacent cell targcell = getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND); if (!targcell) { fizzle(caster); return B_TRUE; } // add it newlf = summonmonster(caster, targcell, R_FLOATINGDISC, NULL, PERMENANT, B_TRUE); if (newlf) { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } // set carrying capacity setattr(newlf, A_STR, 8 + power); } else { // didn't work for some reason fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_FLOOD) { int failed = B_FALSE; if (targcell) { if (!targcell->type->solid) { // create water there object_t *o; o = addob(targcell->obpile, "water"); if (o) { enum OBTYPE badoid; int i,amt; int depth; condset_t cs; depth = DP_SHOULDERS + power; //amt = ((power+1) * (power+1)) - 1; //amt = power; amt = power*2; badoid = OT_WATERDEEP; for (i = 0; i < amt; i++) { cell_t *c; initcondv(&cs, CC_SOLID, B_FALSE, NA, CC_HASOBTYPE, B_FALSE, badoid, CC_NONE); c = real_getrandomadjcell(targcell, &cs, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, NULL); if (c) { object_t *water; water = addob(c->obpile, "water"); if (water) { flag_t *f; f = hasflag(water->flags, F_DEEPWATER); if (f) f->val[0] = depth; } } else { break; } } } if (haslos(player, targcell)) { msg("A huge pool of water appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(caster)) { angergodmaybe(R_GODFIRE, 75, GA_HERESY); } } else { failed = B_TRUE; } } else { failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_FORCESPHERE) { cell_t *retcell[MAXRETCELLS]; char buf[BUFLEN]; int radius,nretcells,n,i; if (!target) target = caster; targcell = target->cell; radius = power/3; limit(&radius, 1, NA); if (isplayer(target)) { snprintf(buf, BUFLEN, "You unleash a mighty shockwave!"); if (seenbyplayer) *seenbyplayer = B_TRUE; animradial(target->cell, radius, '}', C_RED, DT_COMPASS, buf, buf); } else if (cansee(player, target)) { char tname[BUFLEN]; getlfname(target, tname); snprintf(buf, BUFLEN, "%s unleashes a mighty shockwave!", tname); if (seenbyplayer) *seenbyplayer = B_TRUE; animradial(target->cell, radius, '}', C_RED, DT_COMPASS, buf, buf); } // start at outside. for (n = radius; n >= 1; n--) { getradiuscells(targcell, n, DT_COMPASS, B_TRUE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 0); for (i = 0; i < nretcells; i++) { if (retcell[i]->lf) { knockback(retcell[i]->lf, getdiraway(retcell[i], targcell, NULL, B_FALSE, DT_COMPASS, B_FALSE), 2, target, 100+(power*10), B_DOANNOUNCE, B_DODAM); } } } } else if (spellid == OT_S_ENERGYBOLT) { char lfname[BUFLEN]; char numbuf[BUFLEN]; numtotext(power, numbuf); // animation anim(caster->cell, targcell, '}', C_CYAN); if (isplayer(caster) || cansee(player, caster)) { if (power == 1) { msg("%s fire%s a bolt of energy.",castername,isplayer(caster) ? "" : "s"); } else { msg("%s fire%s %s bolts of energy.",castername,isplayer(caster) ? "" : "s", numbuf); } if (seenbyplayer) *seenbyplayer = B_TRUE; } target = haslf(targcell); if (target) { int dam; getlfname(target, lfname); // target takes magical damage // ALWAYS hits. dam = rolldie(power*2,4); if (check_for_block(caster, target, dam, DT_MAGIC, 999, "an energy bolt", B_RANGED)) { } else { if (cansee(player, target)) { if (power == 1) { msg("^%cA bolt of energy hits %s.",getlfcol(target, CC_BAD), lfname); } else { msg("^%c%s bolts of energy hit %s.",getlfcol(target, CC_BAD), numbuf, lfname); } } } losehp(target, dam, DT_MAGIC, caster, "an energy bolt"); } } else if ((spellid == OT_S_FIREBALL) || (spellid == OT_S_METEOR)) { int failed = B_FALSE; char fbname[BUFLEN]; enum OBTYPE fire1, fire2,fire3; if (spellid == OT_S_FIREBALL) { strcpy(fbname, "a huge ball of fire"); fire1 = OT_FIREMED; fire2 = OT_FIREMED; fire3 = OT_FIRESMALL; } else { strcpy(fbname, "a flaming meteorite"); fire1 = OT_FIRELARGE; fire2 = OT_FIRELARGE; fire3 = OT_FIREMED; } if (targcell) { if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) { int dir; cell_t *c; object_t *o; // centre fireball here... if (isplayer(caster)) { msg("You launch %s!", fbname); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { msg("%s launches %s!",castername, fbname); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, targcell)) { msg("%s explodes!", fbname); } anim(caster->cell, targcell, '^', C_RED); redrawpause(); // add fires as follows (3 = medium, 2 = medium, 1 = small) // // 1 // 232 // 13331 // 232 // 1 // add large fires to surrounding cells addobfast(targcell->obpile, fire1); for (dir = D_N; dir <= D_W; dir++) { c = getcellindir(targcell, dir); if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) { o = addobfast(c->obpile, fire1); if (o) { setobcreatedby(o, caster); } if (c->lf) { int dam; char attackname[BUFLEN]; if (spellid == OT_S_FIREBALL) { dam = rolldie(power,3); strcpy(attackname, "a fireball"); } else { dam = 30+rolldie(power,6); strcpy(attackname, "a meteorite"); } if (check_for_block(caster, c->lf, dam, DT_FIRE, 999, attackname, B_RANGED)) { // partial damage dam /= 2; } losehp(c->lf, dam, DT_FIRE, caster, attackname); } } } for (dir = DC_NE; dir <= DC_NW; dir += 2) { cell_t *c; c = getcellindir(targcell, dir); if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) { o = addobfast(c->obpile, fire2); if (o) { setobcreatedby(o, caster); } if (c->lf) { int ndice; ndice = power / 2; if (ndice < 1) ndice = 1; if (spellid == OT_S_FIREBALL) { losehp(c->lf, rolldie(ndice,3), DT_FIRE, caster, "a fireball"); } else { losehp(c->lf, 15+rolldie(ndice,5), DT_FIRE, caster, "a meteorite"); } } } } if ((spellid = OT_S_METEOR) || (power > 5)) { for (dir = D_N; dir <= D_W; dir++) { cell_t *c; c = getcellindir(targcell, dir); if (c) { c = getcellindir(c, dir); if (c && (!c->type->solid || hasflag(c->type->material->flags, F_FLAMMABLE)) ) { o = addobfast(c->obpile, fire3); if (o) { setobcreatedby(o, caster); } if (c->lf) { int ndice; ndice = power / 2; if (ndice < 1) ndice = 1; if (spellid == OT_S_FIREBALL) { losehp(c->lf, rolldie(ndice,2), DT_FIRE, caster, "a fireball"); } else { losehp(c->lf, 7+rolldie(ndice,4), DT_FIRE, caster, "a meteorite"); } } } } } } redrawresume(); } else { failed = B_TRUE; } } else { if (isplayer(caster)) { msg("You have no line of fire to there!"); } failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_FIREDART) { char lfname[BUFLEN]; // animation anim(caster->cell, targcell, '}', C_RED); if (isplayer(caster) || cansee(player, caster)) { msg("%s shoot%s a dart of flame.",castername,isplayer(caster) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } target = haslf(targcell); if (target) { getlfname(target, lfname); // target takes magical damage // check if it hits if (skillcheck(target, SC_DODGE, 90 + (power*10), 0)) { // miss if (isplayer(target) || cansee(player, target)) { msg("A dart of flame misses %s.",lfname); } } else { int dam; // hit dam = rnd(1,6) + power; if (check_for_block(caster, target, dam, DT_FIRE, 999, "a dart of flame", B_RANGED)) { } else { if (isplayer(target) || cansee(player, target)) { msg("^%cA dart of flame hits %s.",getlfcol(target, CC_BAD), lfname); } losehp(target, dam, DT_FIRE, caster, "a dart of flame"); } } } else { damageallobs(NULL, targcell->obpile, 0, DT_FIRE, caster); } } else if (spellid == OT_S_FLAMEBURST) { int range = 1; int x,y; char buf[BUFLEN],buf2[BUFLEN]; range = 1 + (power / 5); // announce snprintf(buf, BUFLEN, "%s emit%s a %sblast of fire!",castername,isplayer(caster) ? "" : "s", (power >= 5) ? "huge " : ""); snprintf(buf2, BUFLEN, "Something emit%s a %sblast of fire!",isplayer(caster) ? "" : "s", (power >= 5) ? "huge " : ""); animradial(caster->cell, range, '}', C_RED, DT_ORTH, buf, buf2); if (isplayer(caster) || cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } for (y = caster->cell->y - range ; y <= caster->cell->y + range; y++) { for (x = caster->cell->x - range ; x <= caster->cell->x + range; x++) { targcell = getcellat(caster->cell->map, x,y); if (targcell && (getcelldistorth(caster->cell, targcell) <= range)) { if (targcell->lf && (targcell->lf != caster) && haslof(caster->cell, targcell, B_FALSE, NULL)) { char lfname[BUFLEN]; int dam; dam = rolldie(2,6); // automatic hit getlfname(targcell->lf, lfname); if (check_for_block(caster, targcell->lf, dam, DT_FIRE, 999, "a burst of fire", B_RANGED)) { } else { if (haslos(caster, targcell)) { msg("^%c%s burn%s!",getlfcol(targcell->lf, CC_BAD), lfname,isplayer(targcell->lf) ? "" : "s"); } losehp(targcell->lf, dam, DT_FIRE, caster, "a burst of fire"); } } damageallobs(NULL, targcell->obpile, rolldie(2,6), DT_FIRE, caster); } } } } else if (spellid == OT_S_FLAMEPILLAR) { int failed = B_FALSE; if (targcell) { if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) { flag_t *f; object_t *o; // create flame there if (haslos(player, targcell)) { msg("A raging pillar of flame appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (power < 5) { o = addob(targcell->obpile, "small fire"); } else if (power < 8) { o = addob(targcell->obpile, "medium fire"); } else { o = addob(targcell->obpile, "large fire"); } // magically boost hp based on power f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] += power; f->val[1] += power; } setobcreatedby(o, caster); } else { failed = B_TRUE; } } else { failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_EARTHQUAKE) { cell_t *retcell[MAXRETCELLS],*c; int nretcells,i,radius,seenwalls = 0, seenpits = 0; radius = 3 + power; getradiuscells(caster->cell, radius, DT_ORTH, B_FALSE, LOF_DONTNEED, B_FALSE, retcell, &nretcells, 80); for (i = 0; i < nretcells; i++) { c = retcell[i]; if (c->type->solid) { if (haslos(player, c)) seenwalls++; setcelltype(c, c->map->habitat->emptycelltype); addob(c->obpile, "50-100 stones"); } else { // it collapses addobfast(c->obpile, OT_HOLEINGROUND); if (haslos(player, c)) seenpits++; } } getradiuscells(caster->cell, radius, DT_ORTH, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 80); for (i = 0; i < nretcells; i++) { c = retcell[i]; if (!c->type->solid) { addobfast(c->obpile, OT_DUSTCLOUD); } } if (seenpits || seenwalls || cansee(player, caster)) { msg("The earth below you shudders and shakes violently!"); setlosdirty(player); } if (seenpits) { if (seenbyplayer) *seenbyplayer = B_TRUE; if (seenwalls == 1) { msg("A huge rent opens up in the ground!"); } else { msg("Huge rents open up in the ground!"); } } if (seenwalls) { if (seenbyplayer) *seenbyplayer = B_TRUE; if (seenwalls == 1) { msg("A nearby wall collapses into the ground!"); } else { msg("Nearby walls collapse into the ground!"); } } } else if (spellid == OT_S_ENCHANT) { object_t *o; if (targob) { o = targob; } else { // ask for an object condset_t cs; initcondv(&cs, CC_HASFLAG, B_TRUE, F_ENCHANTABLE, CC_NONE); o = askobject(caster->pack, "Enchant which object", NULL, NULL, '\0', &cs, B_FALSE); } if (!o) { fizzle(caster); return B_TRUE; } if (isplayer(caster)) { if (!hasflag(o->flags, F_ENCHANTABLE)) { if (isplayer(caster)) nothinghappens(); return B_TRUE; } else { int amt; char obname[BUFLEN]; getobname(o, obname, o->amt); msg("Your %s glow%s %s for a moment.", noprefix(obname), (o->amt == 1) ? "s" : "", (blessed == B_CURSED) ? "black" : "green"); if (seenbyplayer) *seenbyplayer = B_TRUE; if (blessed == B_CURSED) { amt = -1; } else { amt = 1; } modbonus(o, amt); if (iscursed(o)) { setblessed(o, B_UNCURSED); } killflagsofid(o->flags, F_RUSTED); } } else { // monsters can't id things! } } else if (spellid == OT_S_ENDURECOLD) { flag_t *f; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } f = addtempflag(caster->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_ENDUREELEMENTS) { flag_t *f; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } f = addtempflag(caster->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; f = addtempflag(caster->flags, F_DTRESIST, DT_COLD, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_ENDUREFIRE) { flag_t *f; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } f = addtempflag(caster->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_ENTANGLE) { char targname[BUFLEN]; flag_t *f; object_t *o; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, targname); if (cansee(player, target)) { msg("^%cEntangling vines rise up and grasp %s!",getlfcol(target, CC_BAD), targname); if (seenbyplayer) *seenbyplayer = B_TRUE; } // create vine o = addobfast(targcell->obpile, OT_VINE); // set power f = hasflag(o->flags, F_RESTRICTMOVEMENT); if (f) { f->val[0] = 100 + (power*10); //f->val[1] = B_FALSE; // struggling doesn't damage the vine f->val[1] = B_TRUE; // struggling doesn't damage the vine } // boost hp based on power, up 300% f = hasflag(o->flags, F_OBHP); if (f) { f->val[1] = pctof(100 + (power*20), f->val[1]); f->val[0] = f->val[1]; } if (caster && (caster->cell->map == targcell->map)) { // if caster is on the same map (ie. not being cast by a god) /// remember creator. if they don't have los to us, spell // is broken and vines will vanish. setobcreatedby(o, caster); } } else if (spellid == OT_S_EXCAVATE) { cell_t *retcell[MAXRETCELLS],*c; int nretcells,i,radius,seenwalls = 0, seenobs = 0; int killedobs = 0; radius = power; limit(&radius, 3, NA); getradiuscells(caster->cell, radius, DT_ORTH, B_FALSE, LOF_DONTNEED, B_FALSE, retcell, &nretcells, 0); for (i = 0; i < nretcells; i++) { object_t *o, *nexto; c = retcell[i]; if (c->type->solid) { if (haslos(player, c)) seenwalls++; setcelltype(c, c->map->habitat->emptycelltype); addob(c->obpile, "10-20 piles of ash"); } // impassable objects here are destroyed for (o = c->obpile->first ; o ; o = nexto) { nexto = o->next; if ((o->type->id != OT_ASH) && (getmaterialstate(o->material->id) == MS_SOLID)) { killob(o); addobfast(c->obpile, OT_ASH); if (haslos(player, c)) seenobs++; killedobs++; } } } if (seenobs || seenwalls || cansee(player, caster)) { msg("A shockwave of destructive force rips through the air!"); setlosdirty(player); } if (isplayer(caster) && killedobs) { angergodmaybe(R_GODNATURE, 10*killedobs, GA_ATTACKOBJECT); } } else if (spellid == OT_S_FLIGHT) { flag_t *f; int height; // always targetted at caster targcell = caster->cell; target = caster; height = (power/2)+1; limit(&height, SZ_MEDIUM, NA); f = addtempflag(caster->flags, F_FLYING, height, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_FREEZEOB) { if (lfhasflag(caster, F_FREEZINGTOUCH)) { fizzle(caster); return B_TRUE; } if (isplayer(caster) || cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } // next thing touched addflag(caster->flags, F_FREEZINGTOUCH, 1, power, 10+power, NULL); return B_FALSE; } else if (spellid == OT_S_FROSTBITE) { char lfname[BUFLEN]; int exposedlimbs,dam; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, lfname); // how many body parts are impacted? exposedlimbs = getexposedlimbs(target); dam = rolldie(exposedlimbs, 4); if (isplayer(target)) { if (isimmuneto(target->flags, DT_COLD, B_FALSE)) { msg("You feel mildly chilly."); } else { msg("^%cYou feel extremely cold!", getlfcol(target, CC_BAD) ); } if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { if (isimmuneto(target->flags, DT_COLD, B_FALSE)) { msg("%s looks mildly chilly.", lfname); } else { msg("^%c%s looks extremely cold!", getlfcol(target, CC_BAD), lfname); } if (seenbyplayer) *seenbyplayer = B_TRUE; } // target takes magical damage // always hit if (!isimmuneto(target->flags, DT_COLD, B_FALSE)) { losehp(target, dam, DT_COLD, caster, "a frostbite spell"); } } else if (spellid == OT_S_GASEOUSFORM) { if (!target) target = caster; if (getmr(target) && (target->race->id != R_VAMPIRE) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily insubstantial."); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { int howlong; if (isplayer(caster) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (target->race->id == R_VAMPIRE) { howlong = PERMENANT; } else { howlong = 10; } polymorphto(target, R_GASCLOUD, howlong); } } else if (spellid == OT_S_GATHERFLAME) { cell_t *c; int amt = 0,i; if (!target) target = caster; // override castername getlfname(target, castername); // all flame in sight for (i = 0; i < target->nlos; i++) { c = target->los[i]; if (c->lf && (c->lf->material->id == MT_FIRE)) { if (gettr(c->lf) <= power) { char buf[BUFLEN]; // instadeath snprintf(buf, BUFLEN, "being sucked into %s", castername); losehp(c->lf, c->lf->maxhp, DT_DIRECT, target, buf); if (isplayer(c->lf)) { msg("^%cYour essence is sucked into %s!", getlfcol(c->lf, CC_VBAD), castername); } else if (cansee(player, c->lf)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("^%c%s%s essence is sucked into %s!",getlfcol(c->lf, CC_VBAD), lfname, getpossessive(lfname), castername); } amt++; } } } if (isdead(target)) { // consumed yourself? return B_FALSE; } // now gather flame from cells in los for (i = 0; (i < target->nlos) && (amt < power); i++) { object_t *o, *nexto; for (o = target->los[i]->obpile->first ; o ; o = nexto) { nexto = o->next; if (o->material->id == MT_FIRE) { removeob(o, ALL); amt++; } else { int nfound; nfound = killflagsofid(o->flags, F_ONFIRE); if (nfound) { amt += nfound; // kill temperature mods too. amt += killflagsofid(o->flags, F_TEMPMOD); } } break; } } if (amt) { // boost spells... addflag(target->flags, F_TEMPMAGICBOOST, amt, NA, NA, NULL); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_GLACIATE) { object_t *o; o = addobfast(targcell->obpile, OT_COLDNESS); if (o) { flag_t *f; if (haslos(player, targcell)) { msg("A puff of vapour appears."); if (seenbyplayer) *seenbyplayer = B_TRUE; } addflag(o->flags, F_TEMPMOD, (power == 2) ? -25 : -15, NA, NA, NULL); f = hasflag(o->flags, F_OBHP); if (f) { f->val[1] = power*20; f->val[0] = f->val[1]; } affect_temperature(o, B_ADD); } else { fizzle(caster); } } else if ((spellid == OT_S_GREASE) || (spellid == OT_S_CREATEWATER)) { int radius; char createname[BUFLEN]; if (targcell->type->solid) { fizzle(caster); return B_TRUE; } radius = power/2; if (radius < 1) radius = 1; switch (spellid) { case OT_S_CREATEWATER: strcpy(createname, "large puddle of water"); break; case OT_S_GREASE: strcpy(createname, "puddle of oil"); break; default: fizzle(caster); return B_TRUE; } addobburst(targcell, radius, DT_ORTH, createname, caster, LOF_WALLSTOP); if (haslos(player, targcell)) { char underbuf[BUFLEN]; if (targcell->lf && cansee(player, targcell->lf)) { char lfname[BUFLEN]; getlfname(targcell->lf, lfname); snprintf(underbuf, BUFLEN, " under %s",lfname); } else { strcpy(underbuf, ""); } msg("A %s%s appears%s!", (radius == 1) ? "" : "huge ", createname, underbuf); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_HAILSTORM) { int failed = B_FALSE; if (caster && (caster->cell->map->habitat->id == H_FOREST)) { power += 3; limit(&power, NA, 10); } // ask for a target cell if (targcell) { if (!targcell->type->solid) { object_t *o; int x,y; int radius; char *dambuf; // centre storm here... if (haslos(player, targcell)) { msg("An intense storm of hail appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } radius = power / 2; if (radius < 1) radius = 1; addobsinradius(targcell, radius, DT_ORTH, "hail storm", B_FALSE, B_INCLUDECENTRE, caster, NULL, NULL, NULL); // replace damage per sec with power d4 asprintf(&dambuf, "%dd4", power); for (y = targcell->y - radius; y <= targcell->y + radius; y++) { for (x = targcell->x - radius; x <= targcell->x + radius; x++) { cell_t *c; c = getcellat(targcell->map, x, y); if (c && (getcelldist(targcell, c) <= radius)) { for (o = c->obpile->first ; o ; o = o->next) { if (o->type->id == OT_HAILSTORM) { flag_t *f; f = hasflagval(o->flags, F_WALKDAM, DT_PROJECTILE, NA, NA, NULL); if (f) { changeflagtext(f, dambuf); } } } } } // end forx } // end fory free(dambuf); } else { // end if targcell not solid failed = B_TRUE; } } else { failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_HASTE) { int howlong = 15; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (getmr(target) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target) || haslos(player, target->cell)) { getlfname(target, buf); msg("%s blur%s for a moment.",buf, isplayer(target) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { howlong = getspellduration(5,25,blessed) + power; addtempflag(target->flags, F_FASTACTMOVE, 10, NA, NA, NULL, howlong); } } else if ((spellid == OT_S_HEALING) || (spellid == OT_S_HEALINGMIN) || (spellid == OT_S_HEALINGMAJ)) { int donesomething = B_FALSE,i; flag_t *retflag[MAXCANDIDATES]; int nretflags; int undead = B_FALSE; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } undead = isundead(target); // cure certain bad effects if (!undead) { if (killflagsofid(target->flags, F_PAIN)) { donesomething = B_TRUE; } getflags(target->flags, retflag, &nretflags, F_INCUBATING, F_POISONED, F_NONE); for (i = 0; i < nretflags; i++) { poisontype_t *pt; pt = findpoisontype(retflag[i]->val[0]); if (pt->severity == PS_CURSE) { } else { killflag(retflag[i]); donesomething = B_TRUE; if (seenbyplayer) *seenbyplayer = B_TRUE; } } if (!lfhasflagval(target, F_POISONED, P_ROT, NA, NA, NULL)) { // severed body parts getflags(target->flags, retflag, &nretflags, F_INJURY, F_NOBODYPART, F_NONE); for (i = 0; i < nretflags; i++) { enum BODYPART bpgone = BP_NONE; if ((retflag[i]->id == F_NOBODYPART) && (retflag[i]->val[1] == B_FROMINJURY)) { bpgone = retflag[i]->val[0]; } else if ((retflag[i]->id == F_INJURY) && (retflag[i]->lifetime == PERMENANT)) { // permenant injury bpgone = retflag[i]->val[1]; } if (bpgone != BP_NONE) { if (isplayer(target)) { msg("^%cYour %s grows back!", getlfcol(target, CC_GOOD), getbodypartname(target, bpgone)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char targname[BUFLEN]; getlfname(target, targname); msg("^%c%s%s %s grows back!", getlfcol(target, CC_GOOD), targname, getpossessive(targname), getbodypartname(target, bpgone)); if (seenbyplayer) *seenbyplayer = B_TRUE; } killflag(retflag[i]); donesomething = B_TRUE; } else if ((retflag[i]->id == F_INJURY) && (retflag[i]->lifetime > 0)) { killflag(retflag[i]); donesomething = B_TRUE; } } } } if ((spellid == OT_S_HEALINGMIN) && donesomething) { // minor healing will stop here } else { int min,max,amt; switch (spellid) { case OT_S_HEALINGMIN: default: min = 1; max = 10; break; case OT_S_HEALING: min = 10; max = 20; break; case OT_S_HEALINGMAJ: min = 20; max = 30; break; } amt = getspellduration(min,max,blessed); limit(&amt, min+(power*2)-1, max); if (undead) { losehp(target, amt, DT_HOLY, caster, "the power of healing"); if (isplayer(target) || cansee(player, target)) { getlfname(target, buf); msg("^%c%s writhe%s in agony!", getlfcol(target, CC_BAD), buf, isplayer(target) ? "" : "s"); } } else if (target->hp < target->maxhp) { if (!undead && lfhasflagval(target, F_POISONED, P_ROT, NA, NA, NULL)) { amt /= 10; } if (amt > 0) { gainhp(target, amt); if (isplayer(target)) { if (target->hp >= target->maxhp) { switch (spellid) { case OT_S_HEALINGMIN: msg("^%cAll of your scrapes and bruises are healed!", getlfcol(target, CC_GOOD)); break; case OT_S_HEALING: default: msg("^%cYour wounds close themselves!", getlfcol(target, CC_GOOD)); break; case OT_S_HEALINGMAJ: msg("^%cYour injuries are healed!", getlfcol(target, CC_GOOD)); break; } } else { switch (spellid) { case OT_S_HEALINGMIN: msg("^%cSome of your scrapes and bruises are healed!", getlfcol(target, CC_GOOD)); break; case OT_S_HEALING: default: msg("^%cSome of your wounds close themselves!", getlfcol(target, CC_GOOD)); break; case OT_S_HEALINGMAJ: msg("^%cYour injuries are partially healed!", getlfcol(target, CC_GOOD)); break; } } if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, target->cell)) { getlfname(target, buf); msg("^%c%s looks healthier!", getlfcol(target, CC_GOOD), buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } donesomething = B_TRUE; } } } if (isplayer(target)) { if (!donesomething) { nothinghappens(); } } if (donesomething && isplayer(caster)) { // god of death REALLY doesn't like healing. angergodmaybe(R_GODDEATH, 40, GA_HERESY); } // hostile monsters might calm down if (!undead && !frompot && donesomething && isplayer(caster) && !isplayer(target) && (getallegiance(target) == AL_HOSTILE)) { enum ATTRBRACKET iqb; iqb = getattrbracket(getattr(target, A_IQ), A_IQ, NULL); if ((iqb >= IQ_ANIMAL) && cansee(target, caster)) { if (skillcheckvs(caster, SC_MORALE, 15, target, SC_MORALE, 0)) { makepeaceful(target, caster); } } } } else if (spellid == OT_S_HEATMETAL) { int donesomething = B_FALSE; object_t *o,*nexto; target = targcell->lf; if (target) { if (ismetal(target->race->material->id)) { if (!isimmuneto(target->flags, DT_HEAT, B_FALSE)) { if (isplayer(target)) { msg("^%cYou suffer massive burns!", getlfcol(target, CC_BAD) ); } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%c%s glows red hot!", getlfcol(target, CC_BAD), lfname); } losehp(target, roll("6d4"), DT_HEAT, caster, "massive burns"); donesomething = B_TRUE; } } else { int nburn = 0,ndropobs = 0; object_t *dropob[MAXPILEOBS]; // affect equipped objects for (o = target->pack->first ; o ; o = nexto) { int amt; nexto = o->next; amt = isheatable(o); if (ismetal(o->material->id) && amt) { int dodam = B_FALSE; if (isequippedon(o, BP_WEAPON) || isequippedon(o, BP_SECWEAPON)) { nburn++; dodam++; if (!getequippedob(target->pack, BP_HANDS)) { dropob[ndropobs++] = o; } donesomething = B_TRUE; } else if (isequipped(o)) { nburn++; donesomething = B_TRUE; dodam++; } if (dodam) { if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } makehot(o, amt, 2); } } } if (ndropobs && !isimmuneto(target->flags, DT_FIRE, B_FALSE)) { int i; for (i = 0; i < ndropobs; i++) { if (isplayer(target)) { char obname[BUFLEN]; getobname(dropob[i], obname, dropob[i]->amt); msg("^BYour %s %s too hot to hold!", obname, (dropob[i]->amt == 1) ? "is" : "are"); } drop(dropob[i], dropob[i]->amt); } } } } else { // affect objects on ground for (o = targcell->obpile->first ; o ; o = nexto) { int amt; nexto = o->next; // damage metal items on the ground amt = isheatable(o); if (ismetal(o->material->id) && amt) { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; donesomething = B_TRUE; } makehot(o, amt, 2); if (isplayer(caster)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s glow%s red hot!", obname, OBS1(o)); } } } } if (!donesomething) { if (isplayer(caster)) { nothinghappens(); } else { fizzle(caster); return B_TRUE; } } } else if (spellid == OT_S_HEAVENARM) { flag_t *f; if (!target) target = caster; if (lfhasflag(target, F_HEAVENARM)) { fizzle(target); return B_TRUE; } f = addtempflag(target->flags, F_HEAVENARM, power*10, NA, NA, "shell of divine armour", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_HOLDPORTAL) { object_t *o,*oo; cell_t *c; condset_t cs; int jamamt = 1; o = hasobwithflag(targcell->obpile, F_DOOR); if (!o) { fizzle(caster); return B_TRUE; } // move any objects which are in the way (but not lfs) initcondv(&cs, CC_SOLID, B_FALSE, NA, CC_NONE); for (oo = targcell->obpile->first ; oo ; oo = oo->next) { if ((oo != o) && (getmaterialstate(oo->material->id) == MS_SOLID)) { c = getrandomadjcell(targcell, &cs, B_ALLOWEXPAND); if (c) moveob(oo, c->obpile, ALL); } } closedoor(NULL, o); jamamt = power/2; limit(&jamamt, 1, NA); addflag(o->flags, F_JAMMED, jamamt, haslos(player, targcell) ? B_TRUE : B_FALSE, 150 + (power*10), NULL); if (haslos(player, targcell)) { char buf[BUFLEN]; getobname(o, buf, 1); msg("%s glows for a moment.",buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_HONEMETAL) { object_t *o; int donesomething = B_FALSE; flag_t *f; char fullobname[BUFLEN]; char obname[BUFLEN]; if (targob) { o = targob; } else { condset_t cs; initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_METAL, CC_HASFLAG, B_TRUE, F_DAM, CC_NONE); // ask for an object o = doaskobject(caster->pack, "Hone which metal weapon", NULL, NULL, B_FALSE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, &cs, B_FALSE); } if (!o) { fizzle(caster); return B_TRUE; } if (!wepdullable(o)) { fizzle(caster); return B_TRUE; } getobname(o, obname, o->amt); if (isplayer(caster)) { snprintf(fullobname, BUFLEN, "Your %s", noprefix(obname)); } else if (cansee(player, caster)) { snprintf(fullobname, BUFLEN, "%s%s %s", castername, getpossessive(castername), noprefix(obname)); } else { strcpy(fullobname, ""); } f = hasflag(o->flags, F_IMMUTABLE); if (!f) { f = hasflag(o->flags, F_ARMOURPIERCE); } if (f) { if (isplayer(caster)) { char obname[BUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); msg("For some reason, %s is unaffected!", fullobname); } f->known = B_TRUE; return B_FALSE; } f = addtempflag(o->flags, F_ARMOURPIERCE, 5+power, NA, NA, NULL, (power == 3) ? PERMENANT : 20*power); if (f) { donesomething = B_TRUE; } // fix rust and dulled weapons if (isweapon(o)) { f = hasflag(o->flags, F_BONUS); if (f && (f->val[0] < 0)) { killflag(f); if (isplayer(caster) || cansee(player, caster)) msg("%s seems more effective!", fullobname); donesomething = B_TRUE; } } if (killflagsofid(o->flags, F_RUSTED)) { if (isplayer(caster) || cansee(player, caster)) msg("%s is no longer rusted!", fullobname); donesomething = B_TRUE; } if (donesomething) { if (strlen(fullobname)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { if (isplayer(caster)) nothinghappens(); return B_TRUE; } } else if (spellid == OT_S_HUNGER) { target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (isplayer(target)) { msg("^%cA sudden hunger comes over you!", getlfcol(target, CC_BAD)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%c%s looks ravenous!", getlfcol(target, CC_BAD), lfname); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (lfhasflag(target, F_HUNGER)) { modhunger(target, HUNGERCONST); // make more hungry } else { addflag(target->flags, F_HUNGER, HUNGERCONST*2, NA, NA, NULL); } } else if (spellid == OT_S_HOLYAURA) { flag_t *f; if (!target) target = caster; f = addtempflag(target->flags, F_HOLYAURA, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; f = addtempflag(target->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_ICECRUST) { object_t *wep; //enum DAMTYPE dt; char obname[BUFLEN]; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } // does caster have a weapon? wep = getweapon(target); if (!wep) { fizzle(caster); return B_TRUE; } getobname(wep, obname, wep->amt); if (isplayer(target)) { msg("^%cYour %s is covered with ice!", getlfcol(target, CC_GOOD), noprefix(obname)); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%c%s%s %s is covered with ice!", getlfcol(target, CC_GOOD), lfname, getpossessive(lfname), noprefix(obname)); if (seenbyplayer) *seenbyplayer = B_TRUE; } addtempflag(wep->flags, F_FROZEN, B_TRUE, NA, NA, NULL, 10 + power*3); } else if (spellid == OT_S_ICICLE) { object_t *o; int donesomething = B_FALSE; if (haslos(player, targcell)) { msg("A massive icicle rises from the ground!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } // note: we don't actually create the icicle yet, because "getrandomadjcell" will fail // for cells which contain impassable objects (ie. the icicle). // knock lfs away if (targcell->lf) { cell_t *c; c = getrandomadjcell(targcell, &ccwalkable, B_NOEXPAND); if (c) { knockback(targcell->lf, getdirtowards(targcell, c, NULL, B_FALSE, DT_COMPASS), 1, NULL, 100+(power*10), B_DOANNOUNCE, B_DODAM); } else { if (isplayer(targcell->lf)) { msg("^%cYou are impaled by an icicle!", getlfcol(targcell->lf, CC_BAD) ); } else if (cansee(player, targcell->lf)) { char lfname[BUFLEN]; getlfname(targcell->lf, lfname); msg("^%c%s is impaled by an icicle!", getlfcol(targcell->lf, CC_BAD), lfname); } losehp(targcell->lf, rolldie(3,power), DT_PIERCE, caster, "impalement on an icicle"); } donesomething = B_TRUE; } // NOW create the icicle o = addob(targcell->obpile, "huge icicle"); if (o) { flag_t *f; f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = 3+power; f->val[1] = 3+power; } addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); donesomething = B_TRUE; } else { if (haslos(player, targcell)) { msg("The icicle vanishes."); } } if (!donesomething) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_IDENTIFY) { object_t *o; if (!target) target = caster; if (targob) { o = targob; } else { // ask for an object condset_t cs; initcondv(&cs, CC_IDENTIFIED, B_FALSE, NA, CC_NONE); o = askobject(target->pack, "Identify which object", "You have nothing which needs identifying.", NULL, '\0', &cs, B_FALSE); } if (!o) { fizzle(caster); return B_TRUE; } if (o->pile->owner && isplayer(o->pile->owner)) { if (isidentified(o)) { // already identified? nothinghappens(); return B_TRUE; } else { char extrabuf[BUFLEN]; identify(o); //getobname(o, buf, o->amt); getobnametrue(o, buf, o->amt); getobextrainfo(o, extrabuf); if (strlen(extrabuf)) strcat(buf, extrabuf); // charges msgnocap("%c - %s.",o->letter, buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { // monsters can't id things! } } else if (spellid == OT_S_IMMOLATE) { char targname[BUFLEN],buf[BUFLEN]; if (!target) { target = targcell->lf; } if (!target) { fizzle(caster); return B_TRUE; } if (hasobofmaterial(targcell->obpile, MT_FIRE)) { fizzle(caster); return B_TRUE; } getlfname(target, targname); if (rolltohit(caster, target, NULL, NULL, NULL, NULL)) { if (isplayer(caster) || cansee(player, caster)) { construct_hit_string(caster, target, castername, targname, targname, NULL, DT_TOUCH, 0, target->maxhp, 0, B_FALSE, B_FALSE, B_FALSE, B_TRUE, buf); msg("%s", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; msg("^%c%s is engulfed in roaring flames!", getlfcol(target, CC_BAD), targname); addobfast(target->cell->obpile, OT_FIREMED); } } else { if (isplayer(caster)) { msg("You try to touch %s, but fail.", targname); } else if (cansee(player, caster)) { msg("%s tries to touch %s, but fails.", castername, targname); } } } else if (spellid == OT_S_INFINITEDEATH) { lifeform_t *l; if (isplayer(caster)) { msg("A wave of incalculable evil blasts outwards from you!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { getlfname(caster, buf); msg("A wave of incalculable evil blasts outwards from %s!", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (player->cell->map == caster->cell->map) { // should always be true msg("^%cOh no! A wave of incalculable evil washes over you!", getlfcol(player, CC_VBAD)); } for (l = caster->cell->map->lf ; l ; l = l->next) { if (l != caster) { /* if (isimmuneto(l->flags, DT_NECROTIC, B_FALSE) || spellresisted(l, caster, spellid, power, seenbyplayer, B_FALSE)) { */ if (spellresisted(l, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(l)) { msg("Luckily, the evil doesn't seem to harm you."); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { char dambuf[BUFLEN]; char cname[BUFLEN]; real_getlfname(caster, cname, NULL, B_SHOWALL, B_REALRACE); snprintf(dambuf, BUFLEN, "%s%s infinite death spell", cname, getpossessive(cname)); losehp(l, 500, DT_NECROTIC, NULL, dambuf); } } } // now hit the caster! if (isimmuneto(caster->flags, DT_NECROTIC, B_FALSE) || spellresisted(caster, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(caster)) { msg("Luckily, the evil doesn't seem to harm you."); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { char dambuf[BUFLEN]; char cname[BUFLEN]; real_getlfname(caster, cname, NULL, B_SHOWALL, B_REALRACE); snprintf(dambuf, BUFLEN, "%s%s infinite death spell", cname, getpossessive(cname)); losehp(caster, 500, DT_NECROTIC, NULL, dambuf); } } else if (spellid == OT_S_MANASPIKE) { char lfname[BUFLEN]; char numbuf[BUFLEN]; numtotext(power, numbuf); // animation anim(caster->cell, targcell, '}', C_CYAN); if (isplayer(caster) || cansee(player, caster)) { if (power == 1) { msg("%s fire%s a spike of mana.",castername,isplayer(caster) ? "" : "s"); } else { msg("%s fire%s %s spikes of mana.",castername,isplayer(caster) ? "" : "s", numbuf); } if (seenbyplayer) *seenbyplayer = B_TRUE; } target = haslf(targcell); if (target) { int dam; char attackname[BUFLEN]; getlfname(target, lfname); dam = rolldie(power, 4); if (power == 1) { snprintf(attackname, BUFLEN, "a spike of mana"); } else { snprintf(attackname, BUFLEN, "%s spikes of mana", numbuf); } // target takes magical damage // always hit if (check_for_block(caster, target, dam, DT_MAGIC, 999, attackname, B_RANGED)) { } else { if (cansee(player, target)) { if (power == 1) { msg("^%cA spike of mana hits %s.",getlfcol(target, CC_BAD), lfname); } else { msg("^%c%s spikes of mana hit %s.",getlfcol(target, CC_BAD), numbuf, lfname); } } losehp(target, dam, DT_MAGIC, caster, attackname); } } } else if (spellid == OT_S_MAGSHIELD) { object_t *o,*nexto; flag_t *f; // wearing metal? for (o = caster->pack->first ; o ; o = nexto) { nexto = o->next; if (ismetal(o->material->id)) { f = hasflag(o->flags, F_EQUIPPED); if (f) { if ((f->val[0] == BP_RIGHTFINGER) || (f->val[0] == BP_LEFTFINGER)) { if (isplayer(caster)) { getobname(o, buf, 1); msg("Your %s slides off your %s!", buf, getbodypartname(caster, f->val[0])); } else if (cansee(player, caster)) { getobname(o, buf, 1); msg("%s%s %s slides off its %s!", castername, getpossessive(castername), buf, getbodypartname(caster, f->val[0])); } moveob(o, caster->cell->obpile, o->amt); } else { if (isplayer(caster)) { getobname(o, buf, 1); msg("Your %s pulls away from you a little.", noprefix(buf)); } fizzle(caster); return B_TRUE; } } } } // add the magnetic field! addtempflag(caster->flags, F_MAGSHIELD, B_TRUE, NA, NA, NULL, 25 + (power*2)); } else if (spellid == OT_S_MAPPING) { int x,y; int range; map_t *m; if (!target) target = caster; m = target->cell->map; if (power == 10) { range = UNLIMITED; } else { range = power * 6; } // reveal map for (y = 0; y < m->h; y++) { for (x = 0; x < m->w; x++) { cell_t *c; c = getcellat(m, x, y); if (c) { if ((range == UNLIMITED) || (getcelldist(target->cell, c) <= range)) { if (blessed == B_CURSED) { if (!c->known) { // set unknown cells to a random glyph! if (onein(3)) { // 33% chance of the correct thing setcellknown(c, MAXOF(PR_ADEPT, getskill(target, SK_CARTOGRAPHY))); } else if (onein(2)) { // otherwise 50/50 . setcellknown_fake(c, getcellempty(c)); } else { // otherwise solid setcellknown_fake(c, getcellsolid(c)); } } } else { setcellknown(c, MAXOF(PR_ADEPT, getskill(target, SK_CARTOGRAPHY))); } } } } } if (isplayer(target)) { msg("An image of your surroundings appears in your mind!"); if (seenbyplayer) *seenbyplayer = B_TRUE; needredraw = B_TRUE; } rv = B_FALSE; } else if (spellid == OT_S_METALHEAL) { int i; float totalmass = 0; object_t *o; int howmuch; // works on all metal in sight if (isplayer(caster)) { if (seenbyplayer) *seenbyplayer = B_FALSE; msg("^%cYou draw health from nearby metal!", getlfcol(caster, CC_GOOD)); } else if (cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_FALSE; msg("^%c%s draws health from nearby metal!", getlfcol(caster, CC_GOOD),castername); } totalmass = 0; // destroy WORN metal objects by the caster (not CARRIED ones) for (o = caster->pack->first ; o ; o = o->next) { if (isequipped(o) && ismetal(o->material->id)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(caster->pack); for (i = 0; i < caster->nlos; i++) { targcell = caster->los[i]; for (o = targcell->obpile->first ; o ; o = o->next) { // destroy metal items on the ground if (ismetal(o->material->id)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(targcell->obpile); if (targcell->lf && !spellresisted(targcell->lf, caster, spellid, power, seenbyplayer, B_TRUE)) { // destroy only WORN metal objects, not CARRIED ones for (o = targcell->lf->pack->first ; o ; o = o->next) { if (isequipped(o) && ismetal(o->material->id)) { takedamage(o, 9999, DT_DIRECT, caster); if (hasflag(o->flags, F_DEAD)) { totalmass += getobmass(o); } } } // destroy objects right away removedeadobs(targcell->lf->pack); } } if (totalmass > 0) { // heal 2 hp per kilo howmuch = floor(totalmass) * 2; howmuch *= (power/3); // modify for power gainhp(caster, howmuch); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_MINDSCAN) { int failed = B_FALSE; if (isplayer(caster)) { if (targcell && haslos(caster, targcell) && haslf(targcell)) { char targname[BUFLEN]; lifeform_t *oldplayer; // calc view setlosdirty(targcell->lf); //precalclos(targcell->lf); // temporarily change player pointer... oldplayer = player; player = targcell->lf; // getlfname(targcell->lf, targname); snprintf(buf, BUFLEN, "Mindscanning %s, 'v' to view info, ESC to quit.", targname); doexplain(buf); // restore player pointer player = oldplayer; //showlfstats(where->lf, B_TRUE); needredraw = B_TRUE; statdirty = B_TRUE; } else { failed = B_TRUE; } if (seenbyplayer) *seenbyplayer = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_MINDSHIELD) { flag_t *f; f = addtempflag(caster->flags, F_MINDSHIELD, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_MINDWHIP) { int fail = B_FALSE; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (isplayer(target)) { msg("^%cA lash of psychic power assaults your mind!",getlfcol(target, CC_BAD)); } else if (isplayer(caster)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%cYou psycically lash %s%s mind!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname)); } if (getattrbracket(getattr(target, A_IQ), A_IQ, NULL) <= AT_VLOW) { fail = B_TRUE; } else if (!ischarmable(target) && (reason != E_ALREADYUSING)) { fail = B_TRUE; } if (fail) { if (isplayer(caster)) { char tname[BUFLEN]; getlfname(target, tname); msg("%s %s unaffected.", tname, isplayer(target) ? "are" : "is"); } return B_TRUE; } losehp(target, rnd(2,6), DT_DIRECT, caster, "a psychic lash"); } else if (spellid == OT_S_MIRRORIMAGE) { int i,ndone = 0; int seen = B_FALSE; for (i = 0; i < power; i++) { lifeform_t *lf; job_t *j; object_t *o; // create a mirror image targcell = real_getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND, LOF_NEED, NULL, caster); if (!targcell) break; lf = clonelf(caster, targcell); if (!lf) break; if (isplayer(caster) || cansee(player, lf)) { seen = B_TRUE; } killflagsofid(lf->flags, F_CORPSETYPE); killflagsofid(lf->flags, F_EXTRACORPSE); addflag(lf->flags, F_NOCORPSE, B_TRUE, NA, NA, NULL); addflag(lf->flags, F_PHANTASM, caster->hp, NA, NA, NULL); // copy caster's job j = getjob(caster); if (j) { givejob(lf, j->id); } addflag(lf->flags, F_NOTIME, B_TRUE, NA, NA, NULL); lf->created = B_FALSE; // to avoid "the xxx puts on armour" messages // copy caster's weapons & armour for (o = caster->pack->first ; o ; o = o->next) { flag_t *equipflag; equipflag = isequipped(o); if (equipflag) { object_t *newob; newob = addobfast(lf->pack,o->type->id); if (newob) { if (isweapon(newob) && canweild(lf, newob)) { weild(lf, newob); } else if (canwear(lf, newob, equipflag->val[0])) { wear(lf, newob); } } } } killflagsofid(lf->flags, F_NOTIME); lf->created = B_TRUE; addflag(lf->flags, F_NODEATHSPEECH, B_TRUE, NA, NA, NULL); petify(lf, caster); setfollowdistance(lf, 1, 2); // stay close addflag(lf->flags, F_SUMMONEDBY, caster->id, PERMENANT, NA, NULL); ndone++; } if (!ndone) { fizzle(caster); return B_TRUE; } else if (seen) { char copytext[BUFLEN]; if (ndone == 1) { snprintf(copytext, BUFLEN, "A duplicate"); } else { snprintf(copytext, BUFLEN, "%d duplicates", ndone); } msg("%s of %s appear%s!", copytext, castername, (ndone == 1) ? "s" : ""); } } else if (spellid == OT_S_NEGATECOLD) { flag_t *f; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } f = addtempflag(caster->flags, F_DTIMMUNE, DT_COLD, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_NEGATEFIRE) { flag_t *f; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } f = addtempflag(caster->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_SLOWMISSILES) { if (!target) { target = caster; } if (isplayer(target)) { msg("^gYou attune your mind to deflect incoming projectiles."); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_NULLIFY) { flag_t *retflag[MAXCANDIDATES],*poss[MAXCANDIDATES],*f; int nretflags,i,ndone = 0,nposs; char announcebuf[BUFLEN]; int seen; char lfname[BUFLEN]; strcpy(announcebuf, ""); target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, lfname); seen = cansee(player, target); if (isplayer(target)) { msg("^BYou are engulfed in an anti-magic field!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (seen) { msg("^B%s is engulfed in an anti-magic field!", lfname); if (seenbyplayer) *seenbyplayer = B_TRUE; } // resist ? if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("Luckily, you shrug off its effects."); } else if (seen) { msg("%s seems unaffected.", lfname); } return B_FALSE; } // lose some mana if (target->mp > 0) { int howmuch; howmuch = pctof(power*10, target->maxmp); losemp(target, howmuch); if (isplayer(target)) msg("^wYour mana drains away!"); ndone++; } if (killflagsofid(target->flags, F_REVIVETIMER)) { ndone++; if (seen && !isplayer(target)) { msg("^%c%s%s ability to regenerate is nullified!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname)); } } // now stop active spells or abilities while (ndone < power) { nposs = 0; getflags(target->flags, retflag, &nretflags, F_BOOSTSPELL, F_NONE); for (i = 0; i < nretflags; i++) { int ok = B_TRUE; // exception: don't stop animals etc from flying if ((retflag[i]->val[0] == OT_S_FLIGHT) && lfhasflag(target, F_NATURALFLIGHT)) { ok = B_FALSE; } if (ok) { poss[nposs++] = retflag[i]; } } if (nposs) { enum OBTYPE sid; objecttype_t *ot; sid = (poss[rnd(0,nposs-1)])->val[0]; ot = findot(sid); stopspell(target, sid); ndone++; if (seen && !isplayer(target)) { msg("^%c%s%s active '%s' spell is nullified!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname), ot->name); } } else { break; } } // now remove the ability to cast them! while (ndone < power) { // for player: // remove memorised spells // for monsters: // remove racial spells AND abilities // // abilities/spells conferred by objects are not affected. nposs = 0; getflags(target->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE); for (i = 0; i < nretflags; i++) { int ok = B_TRUE; if (isplayer(target)) { if (retflag[i]->id == F_CANWILL) { ok = B_FALSE; } else if (retflag[i]->lifetime != PERMENANT) { ok = B_FALSE; } } else { if ((retflag[i]->lifetime != FROMRACE) && (retflag[i]->lifetime != PERMENANT)) { ok = B_FALSE; } } // for player, only CANCAST SPELLs can be nullified. if (ok && isplayer(target)) { if (retflag[i]->id == F_CANCAST) { objecttype_t *ot; ot = findot(retflag[i]->val[0]); if (ot->obclass->id != OC_SPELL) { ok = B_FALSE; } } } if (ok) { poss[nposs++] = retflag[i]; } } if (nposs) { enum OBTYPE sid; objecttype_t *ot; f = poss[rnd(0,nposs-1)]; sid = f->val[0]; ot = findot(sid); if (seen && !isplayer(target)) { msg("^%c%s can no longer %s '%s'!", getlfcol(target, CC_BAD), lfname, (f->id == F_CANCAST) ? "cast" : "use the ability", ot->name); } killflag(f); ndone++; } else { break; } } if (!ndone) { if (isplayer(target)) { msg("You are unaffected."); } else if (seen) { char lfname[BUFLEN]; getlfname(target, lfname); msg("%s is unaffected.", lfname); } } if (isplayer(caster)) angergodmaybe(R_GODMAGIC, 10, GA_HERESY); } else if (spellid == OT_S_OBJECTGROWTH) { enum OBTYPE newoid = OT_NONE; enum LFSIZE newsize = SZ_ANY; enum CELLTYPE newcelltype = CT_NONE; skill_t *obsk = NULL; char obname[BUFLEN],newobname[BUFLEN]; int seen; if (targcell->obpile->first) { targob = doaskobject(targcell->obpile, "Target which object", NULL, NULL, B_TRUE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, NULL, B_FALSE); } if (!targob) { fizzle(caster); return B_TRUE; } targcell = getoblocation(targob); if (haslos(player, targcell)) { seen = B_TRUE; if (seenbyplayer) *seenbyplayer = B_TRUE; } getobname(targob, obname, 1); obsk = getobskill(targob->flags); if (isweapon(targob) && (obsk->id == SK_LONGBLADES)) { newoid = OT_GREATSWORD; } else if (isweapon(targob) && (obsk->id == SK_SHORTBLADES)) { newoid = OT_GREATSWORD; } else if (isweapon(targob) && (obsk->id == SK_CLUBS)) { newoid = OT_GREATCLUB; } else if (isweapon(targob) && (obsk->id == SK_AXES)) { newoid = OT_GREATAXE; } else if (targob->type->obclass->id == OC_ROCK) { newoid = OT_BOULDER; } else if ((targob->type->obclass->id == OC_ARMOUR) && hasflag(targob->flags, F_MULTISIZE) && (getarmoursize(targob) < SZ_LARGE)) { newsize = getarmoursize(targob) + 1; } else if (targob->type->obclass->id == OC_POTION) { newcelltype = CT_WALLGLASS; } else { flag_t *f; f = hasflag(targob->flags, F_GROWSTO); if (f) { if (f->val[1] == VT_OB) { newoid = f->val[0]; } else { // ie. cell newcelltype = f->val[0]; } } } if (newsize != SZ_ANY) { resizeobject(targob, newsize); getobname(targob, newobname, 1); getobname(targob, newobname, 1); if (seen) msg("%s grows into %s!", obname, newobname); } else if (newcelltype != CT_NONE) { cell_t *where; if (seen) { celltype_t *ct; ct = findcelltype(newcelltype); msg("%s grows into %s %s!", obname, needan(ct->name) ? "an" : "a", ct->name); } where = getoblocation(targob); killob(targob); setcelltype(where, newcelltype); } else if (newoid == targob->id) { if (seen) msg("%s shudders for a moment.", obname); } else if (newoid != OT_NONE) { obpile_t *op; op = targob->pile; killob(targob); targob = addobfast(op, newoid); if (targob) { getobname(targob, newobname, 1); if (seen) msg("%s grows into %s!", obname, newobname); if (targob->type->id == OT_WATERDEEP) { cell_t *where; where = getoblocation(targob); setcelltype(where, CT_LOWFLOOR); // lower floor so water doesn't spread } } else { if (seen) msg("%s shudders then explodes!", obname); } } else { if (seen) msg("%s shudders for a moment.", obname); } } else if (spellid == OT_S_OBJECTSHRINK) { enum OBTYPE newoid = OT_NONE; char obtocreate[BUFLEN]; enum LFSIZE newsize = SZ_ANY; enum CELLTYPE newcelltype = CT_NONE; skill_t *obsk = NULL; char obname[BUFLEN],newobname[BUFLEN]; int seen; if (targcell->obpile->first) { targob = doaskobject(targcell->obpile, "Target which object", NULL, NULL, B_TRUE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, NULL, B_FALSE); } strcpy(obtocreate, ""); if (!targob) { fizzle(caster); return B_TRUE; } targcell = getoblocation(targob); if (haslos(player, targcell)) { seen = B_TRUE; if (seenbyplayer) *seenbyplayer = B_TRUE; } getobname(targob, obname, 1); obsk = getobskill(targob->flags); if (isweapon(targob) && (obsk->id == SK_LONGBLADES)) { newoid = OT_DAGGER; } else if (isweapon(targob) && (obsk->id == SK_SHORTBLADES)) { newoid = OT_KNIFE; } else if (isweapon(targob) && (obsk->id == SK_CLUBS)) { newoid = OT_STICK; } else if (isweapon(targob) && (obsk->id == SK_AXES)) { newoid = OT_KNIFE; } else if ((targob->type->obclass->id == OC_ARMOUR) && hasflag(targob->flags, F_MULTISIZE) && (getarmoursize(targob) > SZ_TINY)) { newsize = getarmoursize(targob) - 1; } else if (targob->type->obclass->id == OC_POTION) { newoid = OT_BROKENGLASS; } else { flag_t *f; f = hasflag(targob->flags, F_SHRINKSTO); if (f) { if (f->val[1] == VT_OB) { if (strlen(f->text)) { strcpy(obtocreate, f->text); } else { newoid = f->val[0]; } } else { // ie. cell newcelltype = f->val[0]; } } } if (newsize != SZ_ANY) { resizeobject(targob, newsize); getobname(targob, newobname, 1); getobname(targob, newobname, 1); if (seen) msg("%s shrinks into %s!", obname, newobname); } else if (newcelltype != CT_NONE) { cell_t *where; if (seen) { celltype_t *ct; ct = findcelltype(newcelltype); msg("%s shrinks into %s %s!", obname, needan(ct->name) ? "an" : "a", ct->name); } where = getoblocation(targob); killob(targob); setcelltype(where, newcelltype); } else if (newoid == targob->id) { if (seen) msg("%s shudders for a moment.", obname); } else if (strlen(obtocreate) || (newoid != OT_NONE)) { obpile_t *op; op = targob->pile; killob(targob); if (strlen(obtocreate)) { targob = addob(op, obtocreate); } else { targob = addobfast(op, newoid); } if (targob) { getobname(targob, newobname, 1); if (seen) msg("%s shrinks into %s!", obname, newobname); if (targob->type->id == OT_WATERDEEP) { cell_t *where; where = getoblocation(targob); setcelltype(where, CT_LOWFLOOR); // lower floor so water doesn't spread } } else { if (seen) msg("%s shudders then implodes!", obname); } } else { if (seen) msg("%s shudders for a moment.", obname); } } else if ((spellid == OT_S_ACCELMETAL) || (spellid == OT_S_PROPELMISSILE)) { char oidbuf[BUFLEN]; char obname[BUFLEN]; flag_t *f; if (!targob) { // ask for an object if (spellid == OT_S_ACCELMETAL) { condset_t cs; initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_METAL, CC_NONE); // TODO: handle ai casting targob = askobject(caster->pack, "Accelerate which metal object", NULL, NULL, 't', &cs, B_FALSE); } else { // ie. propel if (isplayer(caster)) { targob = askobject(caster->pack, "Propel which object", NULL, NULL, 't', NULL, B_FALSE); } else { lifeform_t *target; target = gettargetlf(caster); if (target) { targob = getbestthrowmissile(caster, target); } else { fizzle(caster); return B_TRUE; } } } } if (!targob || ((spellid == OT_S_ACCELMETAL) && !ismetal(targob->material->id)) ) { fizzle(caster); return B_TRUE; } getobname(targob, obname, 1); if ((spellid == OT_S_PROPELMISSILE) && (getmaxthrowrange(caster, targob) <= 0)) { if (isplayer(caster)) { msg("%s is too heavy for you to throw!"); } return B_FALSE; // don't cost mana } if (isequipped(targob) && iscursed(targob)) { msg("Your %s appears to be stuck to you!", noprefix(obname)); targob->blessknown = B_TRUE; return B_TRUE; } snprintf(oidbuf, BUFLEN, "%ld", targob->id); f = addflag(caster->flags, F_THROWING, B_TRUE, NA, NA, oidbuf); if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; killflagsofid(caster->flags, F_THROWING); // 5 is the same as AT_VHIGH strength addflag(targob->flags, F_MISSILEALWAYSDIES, B_TRUE, NA, NA, NULL); addflag(caster->flags, F_TKTHROW, A_IQ, SK_SS_AIR, NA, NULL); f = addflag(caster->flags, F_ACCURACYMOD, 50, NA, NA, NULL); real_fireat(caster, targob, 1, targcell, 4+power, NULL, B_TRUE, spellid, NULL); killflag(f); killflagsofid(caster->flags, F_TKTHROW); } else if (spellid == OT_S_TRAVEL) { regionthing_t *poss[MAXCANDIDATES],*rt; region_t *srcregion = NULL; int nposs,i,depth; char ch = 'a'; cell_t *dstcell = NULL; if (!isplayer(caster)) return B_TRUE; // ask which region to go to getbranchlinks(poss, &nposs, RT_BRANCHLINK, RT_HABITAT, RT_NONE); if (!nposs) { fizzle(caster); return B_TRUE; } initprompt(&prompt, "Where do you wish to travel?"); prompt.maycancel = B_TRUE; for (i = 0; i < nposs; i++) { char choicetext[BUFLEN]; branch_t *destbranch; if (poss[i]->whatkind == RT_BRANCHLINK) { destbranch = findbranch(poss[i]->value); strcpy(choicetext, destbranch->name); } else if (poss[i]->whatkind == RT_HABITAT) { habitat_t *h; h = findhabitat(poss[i]->value); strcpy(choicetext, h->name); } capitalise(choicetext); addchoice(&prompt, ch++, choicetext, NULL, poss[i], NULL); } ch = getchoice(&prompt); rt = (regionthing_t *)prompt.result; if (!rt) { msg("Cancelled."); return B_TRUE; } depth = rt->depth; // find region containing this link. findregionthing(rt->id, &srcregion); if (srcregion) { map_t *srcmap; if (isplayer(caster)) { msg("There is a blinding flash of light..."); wrefresh(msgwin); } // does the map contining the link exist? srcmap = findregionmap(srcregion->id, depth); if (!srcmap) { // we'll need to create the map. srcmap = addmap(); createmap(srcmap, depth, srcregion, NULL, D_NONE, NULL); } if (rt->whatkind == RT_BRANCHLINK) { object_t *dstob = NULL; region_t *destregion = NULL; // find the branchlink object. ie. the stairs/portal which goes to // the given region. destregion = findregionbytype(rt->value); dstob = findmapobwithflagval(srcmap, F_CLIMBABLE, NA, destregion->id, NA, NULL); assert(dstob); dstcell = getoblocation(dstob); } else if (rt->whatkind == RT_HABITAT) { // on top of the 'up' staircase dstcell = findobinmap(srcmap, srcmap->habitat->upstairtype); if (!dstcell) { dstcell = getrandomcell(srcmap); } } } else { msg("srcregion doesnt exist!"); } if (dstcell && !cellwalkable(caster, dstcell, NULL)) { dstcell = real_getrandomadjcell(dstcell, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); } if (dstcell) { // make sure it's walkable teleportto(caster, dstcell, B_FALSE); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_PLANESHIFT) { map_t *m; target = caster; if (isplayer(target)) { region_t *r; char ch = 'a'; if (seenbyplayer) *seenbyplayer = B_TRUE; // ask for region initprompt(&prompt, "Where do you wish to travel?"); prompt.maycancel = B_TRUE; for (r = firstregion ; r ; r = r->next) { branch_t *rt; rt = r->rtype; addchoice(&prompt, ch++, rt->name, NULL, r, NULL); } ch = getchoice(&prompt); r = (region_t *)prompt.result; // find first map in region m = findregionmap(r->id, 1); } else { if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } // alwyas go realm of gods m = heaven; } if (m) { condset_t cs; // find random free cell in map //targcell = getrandomcell(m); //targcell = getrandomroomcell(m, ANYROOM, WE_WALKABLE); initcondv(&cs, CC_ISROOM, B_TRUE, NA, CC_WALKABLEFOR, B_TRUE, target->id, CC_DANGEROUSFOR, B_FALSE, target->id, CC_NONE); targcell = getcell_cond(m, &ccwalkableroom); if (!targcell) { fizzle(caster); return B_TRUE; } teleportto(target, targcell, B_TRUE); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_PLANTWALK) { int x,y; cell_t *poss[MAX_MAPW*MAX_MAPH]; object_t *srcob; int nposs = 0; srcob = hasobofclass(targcell->obpile, OC_FLORA); if (!srcob) { fizzle(caster); return B_TRUE; } // after this point the spell always counts as successful // (ie. costs mana) if (isplayer(caster)) { char obname[BUFLEN]; getobname(srcob, obname, 1); msg("You touch %s.", obname); } else if (cansee(player, caster)) { char obname[BUFLEN]; getobname(srcob, obname, 1); msg("%s touches %s.", castername, obname); } for (y = 0; y < caster->cell->map->h; y++) { for (x = 0; x < caster->cell->map->w; x++) { cell_t *c; c = getcellat(caster->cell->map, x, y); if (c && (c != targcell)) { if ((power == 1) && hasob(c->obpile, srcob->type->id)) { poss[nposs++] = c; } else if ((power == 2) && hasobofclass(c->obpile, OC_FLORA)) { poss[nposs++] = c; } } } } // no possible destinations? if (!nposs) { if (isplayer(caster) || cansee(player, caster)) { nothinghappens(); } return B_FALSE; } if (isplayer(caster)) { int i; // reveal all potential locations for (i = 0; i < nposs; i++) { setcellknown(poss[i], PR_EXPERT); } // then ask to select one targcell = real_askcoords("Travel where?", "Plantwalk->", TT_SPECIFIED, player, 0, UNLIMITED, LOF_DONTNEED, B_FALSE, poss, nposs); if (!hasobofclass(targcell->obpile, OC_FLORA)) { targcell = NULL; } } else { targcell = poss[rnd(0,nposs-1)]; } if (!targcell) { fizzle(caster); // purposely returning false here, since we've already revealed // some information to the caster. return B_FALSE; } if (!cellwalkable(caster, targcell, NULL)) { targcell = real_getrandomadjcell(targcell, &ccwalkable, B_NOEXPAND, LOF_DONTNEED, NULL, NULL); if (!targcell) { fizzle(caster); return B_FALSE; } } // teleport to destination. teleportto(caster, targcell, B_FALSE); } else if (spellid == OT_S_PARALYZE) { int howlong; int saved = B_FALSE; target = targcell->lf; if (lfhasflag(target, F_PARALYZED)) { fizzle(caster); return B_TRUE; } if (isimmuneto(target->flags, DT_PETRIFY, B_FALSE)) { saved = B_TRUE; } else if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { saved = B_TRUE; } else if (skillcheck(target, SC_STR, 100 + (power*5), 0)) { saved = B_TRUE; } if (saved) { if (isplayer(target)) { msg("You stiffen momentarily."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, target->cell)) { getlfname(target, buf); msg("%s stiffens momentarily.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } howlong = power*2; addtempflag(target->flags, F_PARALYZED, B_TRUE, NA, NA, NULL, howlong); if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_PAIN) { int failed = B_FALSE; target = targcell->lf; if (lfhasflag(target, F_PAIN) || isundead(target)) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { failed = B_TRUE; } else if (skillcheck(target, SC_CON, 100 + (power*5), 0)) { failed = B_TRUE; } if (target) { if (failed) { if (isplayer(target)) { msg("Your skin hurts for a moment."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, target->cell)) { char targetname[BUFLEN]; getlfname(target, targetname); msg("%s winces in pain, then recovers.", targetname); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } else { int howlong = 7; howlong = getspellduration(3,5,blessed) + (power/4); addtempflag(target->flags, F_PAIN, DT_MAGIC, NA, NA, "1d3", howlong); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_PETRIFY) { target = targcell->lf; // some thigns can't be stoned if (!lfcanbestoned(target)) { fizzle(caster); return B_TRUE; } else if (lfhasflag(target, F_BEINGSTONED)) { fizzle(caster); return B_TRUE; } // savingthrow if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE) || skillcheck(target, SC_CON, 100 + power*5, 0)) { if (haslos(player, target->cell)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("%s glow%s grey momentarily.", lfname, isplayer(target) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_TRUE; } // successful if (haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } // power determines how long before stoning. // power 1 = 6 turns // power 10 = 3 turns addflag(target->flags, F_BEINGSTONED, 8-(power/2), NA, NA, NULL); } else if (spellid == OT_S_POISONBOLT) { char lfname[BUFLEN]; // animation anim(caster->cell, targcell, '}', C_GREEN); target = haslf(targcell); if (target) { getlfname(target, lfname); // target gets saving throw to avoid... if (skillcheck(target, SC_DODGE, 90 + (power*10), 0)) { // miss if (cansee(player, target)) { msg("A glob of venom misses %s.",lfname); } } else { // hit if (cansee(player, target)) { msg("^%cA glob of venom hits %s.",getlfcol(target, CC_BAD), lfname); } if (!isimmuneto(target->flags, DT_POISON, B_FALSE)) { poison(target, power*3, P_VENOM, (power/4)+1, "a glob of venom", caster ? caster->race->id : R_NONE, B_TRUE); } } } if (isplayer(caster)) god_usepoison_response(); } else if (spellid == OT_S_POSSESSION) { char targname[BUFLEN]; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, targname); // warn if it can't do magic if (lfhasflag(target, F_NOSPELLS)) { int cancel = B_FALSE; if (isplayer(caster)) { char buf[BUFLEN]; char ch; snprintf(buf, BUFLEN, "You may be stuck in %s%s body - proceed?", targname, getpossessive(targname)); ch = askchar(buf, "yn", "n", B_TRUE, B_FALSE); if (ch != 'y') { cancel = B_TRUE; } } else { cancel = B_TRUE; } if (cancel) { if (isplayer(caster)) msg("Cancelled."); return B_TRUE; } } // charmable? if (!ischarmable(target)) { if (isplayer(caster)) { switch (reason) { case E_DRUNK: msg("%s%s mind is too alcohol-impaired for you to possess.",targname,getpossessive(targname)); break; case E_LOWIQ: msg("%s%s intellect is too simple for you to possess.",targname,getpossessive(targname)); break; case E_UNDEAD: msg("The undead are immune to possession."); break; case E_ROBOT: msg("Robots are immune to charming."); break; case E_ALREADYUSING: msg("%s is already charmed by another!", targname); break; default: msg("You cannot possesss %s.", targname); break; } } return B_FALSE; } // saving throw.... if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { return B_FALSE; } // announce if (cansee(player,target)) { if (isplayer(caster)) { msg("You take possession of %s%s mind!", targname, getpossessive(targname)); } else { msg("%s takes possession of %s%s mind!",cansee(player, caster) ? castername : "Something", targname, getpossessive(targname)); } } // remove 'become a ghost' flag fro mcaster killflagsofid(caster->flags, F_RISEASGHOST); // possess! if (isplayer(caster)) { // player name copyflag(target->flags, caster->flags, F_NAME); // level target->level = player->level; player->controller = C_AI; player = target; target->controller = C_PLAYER; // now kill the caster! die(caster); // if they survived somehow... if (!isdead(caster)) { // ... they will be hostile! fightback(caster, player); } } else { switch (getallegiance(caster)) { case AL_HOSTILE: killflagsofid(target->flags, F_FRIENDLY); addflag(target->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); break; case AL_PEACEFUL: killflagsofid(target->flags, F_FRIENDLY); killflagsofid(target->flags, F_HOSTILE); break; case AL_FRIENDLY: makefriendly(target, PERMENANT); break; } // now kill the caster! die(caster); } } else if (spellid == OT_S_PRECOGNITION) { flag_t *f; // always targetted at caster targcell = caster->cell; target = caster; f = addtempflag(caster->flags, F_EVASION, 5, NA, NA, "precognition", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_MFEEDBACK) { int dam,iq; enum ATTRBRACKET iqb; char targetname[BUFLEN]; int fail = B_FALSE; if (!target) { target = targcell->lf; } if (!target) { fizzle(caster); return B_TRUE; } iq = getattr(target, A_IQ); iqb = getattrbracket(iq, A_IQ, NULL); // not smart enough if (iqb <= AT_EXLOW) { fail = B_TRUE; } else if (!ischarmable(target) && (reason != E_ALREADYUSING)) { fail = B_TRUE; } if (fail) { if (isplayer(caster)) { char tname[BUFLEN]; getlfname(target, tname); msg("%s %s unaffected.", tname, isplayer(target) ? "are" : "is"); } return B_TRUE; } dam = rnd((iq/5),(iq/4)); losehp(target, dam, DT_DIRECT, caster, "a mental feedback loop"); getlfname(target, targetname); if (isplayer(target)) { msg("^%cYour brain is blasted!",getlfcol(target, CC_BAD) ); } else if (cansee(player, target)) { msg("^%c%s%s brain is blasted!",getlfcol(target, CC_BAD), targetname, getpossessive(targetname)); } } else if (spellid == OT_S_PSIBLAST) { int range; int x,y; char buf[BUFLEN]; if (caster && !targcell) targcell = caster->cell; range = 3; snprintf(buf, BUFLEN, "%s emit%s a blast of psionic power!",castername,isplayer(caster) ? "" : "s"); animradial(targcell, range, '}', C_CYAN, DT_COMPASS, buf, "Something emits a radial blast of psionic power!"); if (isplayer(caster) || haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } for (y = targcell->y - range ; y <= targcell->y + range; y++) { for (x = targcell->x - range ; x <= targcell->x + range; x++) { cell_t *c; c = getcellat(targcell->map, x,y); if (c && (getcelldist(targcell, c) <= range)) { if (c->lf && (c->lf != caster) && haslof(targcell, c, B_FALSE, NULL)) { stun(c->lf, 4+power); } } } } } else if (spellid == OT_S_PSYARMOUR) { flag_t *f; // always targetted at caster targcell = caster->cell; target = caster; f = addtempflag(caster->flags, F_MAGICARMOUR, power*4, NA, NA, "psychic barrier", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_PSYSHOVE) { if (targcell->lf) { float targweight = 0,lfweight = 0; char tname[BUFLEN]; int pushpower, dirtowards, diraway; int pushcaster = B_FALSE; cell_t *cellaway; target = targcell->lf; pushpower = power+1; getlfname(target, tname); targweight = getlfweight(target, B_WITHOBS); lfweight = getlfweight(caster, B_WITHOBS); if (isplayer(caster)) { msg("You psychically push against %s!", tname); } else if (cansee(player, caster)) { msg("%s psychically pushes against %s!", castername, tname); } dirtowards = whichwayto(caster->cell, targcell, NULL, B_FALSE); diraway = diropposite(dirtowards); cellaway = getcellindir(target->cell, dirtowards); if (lfweight < targweight) { pushcaster = B_TRUE; } else if (!cellaway) { pushcaster = B_TRUE; } else if (issolid(cellaway) || hascloseddoor(cellaway)) { pushcaster = B_TRUE; } if (pushcaster) { // push caster back msg("%s %s backwards through the air!", castername, isplayer(caster) ? "sail" : "sails"); knockback(caster, diraway, pushpower, caster, NA, B_NOANNOUNCE, B_NODAM); // never fall } else { // push them away, but airborne people don't fall knockback(target, dirtowards, pushpower, caster, 15*power, B_DOANNOUNCE, B_NODAM); // ie. 45 max falldiff } if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { fizzle(caster); } } else if (spellid == OT_S_PULLMETAL) { int donesomething = B_FALSE; if (targcell->lf) { object_t *o; int failed = B_FALSE; int gotmetal = B_FALSE; float metalweight = 0; object_t *wep; donesomething = B_FALSE; // if they are weilding a metal weapon... wep = getweapon(targcell->lf); if (wep && ismetal(wep->material->id) && !lfhasflag(caster, F_NOPACK)) { // pull their weapon from them pullobto(wep, caster); donesomething = B_TRUE; } // if they're wearing metal... for (o = targcell->lf->pack->first ; o ; o = o->next) { if (ismetal(o->material->id) && isequipped(o)) { if (!isequippedon(o, BP_WEAPON) && !isequippedon(o, BP_SECWEAPON)) { gotmetal = B_TRUE; metalweight += getobmass(o); break; } } } if (gotmetal) { donesomething = B_TRUE; // include the target lf's weight since we will be pulling on them target = targcell->lf; metalweight += getlfweight(target, B_WITHOBS); if (getobpileweight(caster->pack) + metalweight <= getmaxcarryweight(caster)) { // they get pulled towards caster failed = pullnextto(target, caster->cell); } else { // caster gets pulled towards them failed = pullnextto(caster, target->cell); } if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (failed) { if (isplayer(target) || haslos(player, target->cell)) { char buf[BUFLEN]; getlfname(target, buf); msg("%s %s pulled forward slightly.", buf, is(target)); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } } if (!donesomething) { fizzle(caster); return B_TRUE; } } else if (targcell->obpile->first) { // no lifeform there targob = NULL; // select object from cell... targob = askobject(targcell->obpile, "Target which object", NULL, NULL, '\0', NULL, B_FALSE); if (targob) { if (ismetal(targob->material->id)) { if (isplayer(caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (getobpileweight(caster->pack) + getobmass(targob) <= getmaxcarryweight(caster)) { // move it to the player pullobto(targob, caster); } else { // move player to it pullnextto(caster, targcell); } } else { fizzle(caster); return B_TRUE; } } else { fizzle(caster); return B_TRUE; } } } else if (spellid == OT_S_GATE) { int newdepth; int min,max; char query[BUFLEN]; // calculate range min = caster->cell->map->depth - (power*2); max = caster->cell->map->depth + (power*2); limit(&min, 1, caster->cell->map->region->rtype->maxdepth); limit(&max, 1, caster->cell->map->region->rtype->maxdepth); if (min < 1) min = 1; // ask which level snprintf(query, BUFLEN, "Create a portal to which level (%d-%d)",min,max); askstring(query, '?', buf, BUFLEN, NULL); newdepth = atoi(buf); if ((newdepth < min) || (newdepth > max)) { fizzle(caster); return B_TRUE; } else { cell_t *srccell; object_t *srcportal,*dstportal; // find adjacent cell for portal srccell = real_getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND, LOF_NEED, NULL, caster); if (!srccell) { fizzle(caster); return B_TRUE; } // create the source portal srcportal = addobfast(srccell->obpile, OT_PORTAL); assert(srcportal); setobcreatedby(srcportal, caster); // announce, because we might have a delay creating the level... if (isplayer(caster)) { msg("There is a blinding flash of light..."); wrefresh(msgwin); } dstportal = linkportaltodepth(srcportal, newdepth); setobcreatedby(dstportal, caster); //newmap = findmapofdepth(newdepth); // make both gates temporary maketemporary(srcportal, 6, "vanishes"); maketemporary(dstportal, 6, "vanishes"); if (haslos(player, srccell)) { char obname[BUFLEN]; getobname(srcportal, obname, 1); msg("%s appears!",obname); } if (seenbyplayer) *seenbyplayer = B_TRUE; } /*} else if (spellid == OT_S_INSCRIBE) { char buf[BUFLEN]; if (isplayer(caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; // ask what to inscribe askstring("What will you inscribe here", '?', buf, BUFLEN, NULL); if (strchr(buf, '*') || strchr(buf, '^')) { // prevent glyphs / colours fizzle(caster); } else if (strlen(buf) > 0) { writetextonground(caster, caster->cell, buf, power*50); } else { fizzle(caster); } } else { // monsters can't cast } */ } else if (spellid == OT_S_INSTANTDISROBE) { cell_t *newcell = NULL; int howmany = (power / 4)+1,ndone = 0; int i,announcethump = B_FALSE; object_t *o; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } // find cell next to caster newcell = getrandomadjcell(targcell, &ccwalkable, B_NOEXPAND); if (!newcell) { newcell = targcell; } for (i = 0; i < howmany; i++) { // pick armour o = getrandomarmour(target, NULL); if (o) { char obname[BUFLEN]; // move it ndone++; moveob(o, newcell->obpile, o->amt); if (isplayer(target)) { getobname(o, obname, o->amt); if (haslos(player, newcell)) { msg("^%cYour %s suddenly appears next to you!", getlfcol(target, CC_BAD), noprefix(obname)); } else { msg("^%cYour %s suddenly vanishes!", getlfcol(target, CC_BAD), noprefix(obname)); announcethump = B_TRUE; } } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); getobname(o, obname, o->amt); if (haslos(player, newcell)) { msg("^%c%s%s %s suddenly appears next to it!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname), noprefix(obname)); } else { msg("^%c%s%s %s suddenly vanishes!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname), noprefix(obname)); } } } else { break; } } if (!ndone) { fizzle(caster); return B_TRUE; } if (announcethump && !isdeaf(player)) { msg("You hear something hitting the ground next to you."); } } else if (spellid == OT_S_INVISIBILITY) { int howlong = 30; int willannounce = B_FALSE; char targname[BUFLEN]; // special case for ghosts... if (caster->race->id == R_GHOST) { targcell = caster->cell; } else { if (!validatespellcell(caster, &targcell,TT_PLAYER, spellid, power, frompot)) return B_TRUE; } target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } else if ( lfhasflag(target, F_INVISIBLE) ) { fizzle(caster); return B_TRUE; } getlfname(target, targname); // time is based on spellpower howlong = getspellduration(5,10,blessed) + (power*2); if (!isplayer(target) && cansee(player, target) ) { willannounce = B_TRUE; if (seenbyplayer) *seenbyplayer = B_TRUE; } addtempflag(target->flags, F_INVISIBLE, B_TRUE, NA, NA, NULL, howlong); if (willannounce && !lfhasflag(player, F_SEEINVIS)) { msg("%s flicker%s then vanishes!",targname, isplayer(target) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } breakaitargets(target, B_TRUE); } else if (spellid == OT_S_JOLT) { target = haslf(targcell); if (target) { int dam; // hit if (isplayer(target)) { msg("^%cA pulse of electricity shocks you!",getlfcol(target, CC_BAD)); } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%cA pulse of electricity shocks %s!",getlfcol(target, CC_BAD),lfname); } dam = power*2; losehp(target, dam, DT_ELECTRIC, caster, "a jolt of electricity"); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_KNOCK) { object_t *o; target = targcell->lf; // high powered knock can knock back monsters if (target && (power >= 7)) { int dir; dir = getdirtowards(caster->cell, targcell, target, B_FALSE, DT_COMPASS); knockback(target, dir, 2, caster, 0, B_DOANNOUNCE, B_DODAM); } else { int donesomething = B_FALSE; object_t *nexto; for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; if (hasflag(o->flags, F_IMPASSABLE)) { int gotit = B_FALSE; int dooropen; if (isdoor(o, &dooropen)) { // closed door? if (!dooropen) { gotit = B_TRUE; } } else { // something else impassable gotit = B_TRUE; } if (gotit) { if (haslos(player, targcell)) { getobname(o, buf, o->amt); msg("%s %s!",isplayer(caster) ? "You blast" : "Something blasts", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } addflag(o->flags, F_DEAD, B_TRUE, NA, NA, NULL); donesomething = B_TRUE; } } else { flag_t *f; f = hasflag(o->flags, F_LOCKED); if (f) { if (f->val[2] == NA) { // unlock it if (haslos(player, targcell)) { getobname(o, buf, 1); msg("%s unlocks.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } killflagsofid(o->flags, F_LOCKED); donesomething = B_TRUE; } } } } if (!donesomething) { fizzle(caster); return B_TRUE; } } } else if (spellid == OT_S_KNOWWEAKNESS) { flag_t *f; // always targetted at caster targcell = caster->cell; target = caster; if (isplayer(caster)) { msg("^gYour mind becomes attuned to your foes' weaknesses!"); } f = addtempflag(caster->flags, F_EXTRADAM, NA, NA, 2, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_LEVITATION) { flag_t *f; // always targetted at caster targcell = caster->cell; target = caster; f = addtempflag(caster->flags, F_LEVITATING, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; if (cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_LIGHT) { int radius; // at power 3, you can control where the light appears // at power 5, also increase illumination of entire map too! if (power >= 3) { // TODO: this actually means we can cast it through walls!!! if (!validatespellcell(caster, &targcell,TT_NONE, spellid, power, frompot)) return B_TRUE; } else { targcell = caster->cell; } // create light objects radius = power / 3; limit(&radius, 1, NA); makelitradius(targcell, radius, OT_MAGICLIGHT, rnd(5,10)+(power*2), power ); if (blessed || (power >= 5)) { // permenant light modillumination(targcell->map, D_LIGHTER); } if (haslos(player, targcell)) { if (power < 5) { msg("A magical light appears!"); } else { msg("The area is lit by a magical light!"); } if (seenbyplayer) *seenbyplayer = B_TRUE; } if (targcell->lf) { // undead will flee from light if (isundead(targcell->lf) && !skillcheck(targcell->lf, SC_WILL, 100, 0)) { // runs away from this cell addtempflag(targcell->lf->flags, F_FLEEFROM, caster->id, NA, NA, NULL, 15+power); } } //calclight(targcell->map); /* if (targcell->map == player->cell->map) { needredraw = B_TRUE; drawscreen(); } */ } else if (spellid == OT_S_LIGHTNINGBOLT) { cell_t *retcell[MAXRETCELLS]; int nretcells; int i; int nhits = power; // create a line of fire towards the target cell calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcells); animcells(caster->cell, &retcell[1], nretcells-1, B_FALSE, '/', '\\', C_WHITE); if (cansee(player, caster)) { // if fromob is set, this was from a lightning javelin. instead of announcing // the bolt firing, announce it hitting people (since we've already said // "the javelin turns into lightning"). if (!fromob) { msg("%s shoot%s a bolt of lightning!",castername, isplayer(caster) ? "" : "s"); } } // don't hit the caster cell on fire! for (i = 1; (i < nretcells) && (nhits > 0); i++) { cell_t *c; c = retcell[i]; if (c->lf) { int dam; // hit with lightning dam = roll("2d6"); if (power > 1) { dam += power; // maxpower is 1, but blue dragons can exceed this } if (check_for_block(caster, c->lf, dam, DT_ELECTRIC, 999, "a lightning bolt", B_RANGED)) { } else { losehp(c->lf, dam, DT_ELECTRIC, caster, "a lightning bolt"); if (haslos(player, c)) { if (fromob) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("^%cThe bolt of lightning strikes %s!", getlfcol(c->lf, CC_BAD), lfname); } needredraw = B_TRUE; if (seenbyplayer) *seenbyplayer = B_TRUE; } } nhits--; } } } else if (spellid == OT_S_LIGHTNINGSTORM) { char targname[BUFLEN]; lifeform_t *poss[MAXCANDIDATES], *targ[MAXCANDIDATES]; int nposs = 0,ntarg = 0; int seen = B_FALSE; int i; for (i = 0; i < caster->nlos; i++) { if (caster->los[i] != caster->cell) { target = caster->los[i]->lf; if (target && areenemies(caster, target)) { poss[nposs++] = target; } } } if (nposs) { int nhits; nhits = power*3; for (i = 0; (i < nhits) && (nposs > 0); i++) { int sel,n; // select a random target sel = rnd(0,nposs-1); targ[ntarg++] = poss[sel]; // remove selection from list for (n = sel; n < nposs-1; n++) { poss[n] = poss[n+1]; } nposs--; } for (i = 0; i < ntarg; i++) { if (haslos(player, targ[i]->cell)) { // hit this target with lightning animsky(targ[i]->cell, '}', C_CYAN); seen = B_TRUE; } } if (seen) { drawlevelfor(player); } for (i = 0; i < ntarg; i++) { int dam; if (cansee(player, targ[i])) { getlfname(targ[i],targname); msg("^%c%s %s struck by a bolt of lightning!",getlfcol(targ[i], CC_BAD), targname, is(targ[i])); } if (caster && (targ[i]->cell->map->habitat->id == H_FOREST)) { dam = rolldie(4,6); } else { dam = rolldie(3,6); } if (check_for_block(caster, targ[i], dam, DT_ELECTRIC, 999, "a bolt of lightning", B_RANGED)) { } else { losehp(targ[i], dam, DT_ELECTRIC, caster, "a bolt of lightning"); } } if (seen) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_LOCATEOBJECT) { char wantname[BUFLEN]; char buf[BUFLEN]; map_t *m; object_t *o; if (!isplayer(caster)) { fizzle(caster); return B_TRUE; } // ask for object askstring("Locate what kind of object", '?', wantname, BUFLEN, NULL); // find it in all maps! snprintf(buf, BUFLEN, "Locations of '%s':", wantname); initprompt(&prompt, buf); prompt.maycancel = B_TRUE; for (m = firstmap ; m ; m = m->next) { int x,y; char ch = 'a'; char mapname[BUFLEN]; getregionname(mapname, m, NULL, RF_WITHLEVEL); capitalise(mapname); if (m->habitat->id == H_HEAVEN) continue; for (y = 0; y < m->h; y++) { for (x = 0; x < m->w; x++) { cell_t *c; c = getcellat(m, x, y); if (c) { // on the ground? for (o = c->obpile->first ; o ; o = o->next) { char obname[BUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_CONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_USED, B_NOSHOWALL); if (!hasflag(o->flags, F_TRAIL) && strcasestr(obname, wantname)) { char ptext[BUFLEN]; char distbuf[BUFLEN],dirbuf[BUFLEN]; getdisttext(caster->cell, c, distbuf, NULL, dirbuf); snprintf(ptext, BUFLEN, "%s: %s (%s to the %s)", mapname, obname, distbuf, dirbuf); addchoice(&prompt, ch++, ptext, NULL, o, NULL); } } // carried by someone? if (c->lf) { for (o = c->lf->pack->first ; o ; o = o->next) { char obname[BUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_CONDITION, B_NOBLINDADJUST, B_NOBLESSINGS, B_USED, B_NOSHOWALL); if (strcasestr(obname, wantname)) { char ptext[BUFLEN]; char distbuf[BUFLEN],dirbuf[BUFLEN]; char lfname[BUFLEN]; real_getlfnamea(c->lf, lfname, NULL, B_SHOWALL, B_REALRACE); getdisttext(caster->cell, c, distbuf, NULL, dirbuf); snprintf(ptext, BUFLEN, "%s: %s (%s to the %s, held by %s)", mapname, obname, distbuf, dirbuf, lfname); addchoice(&prompt, ch++, ptext, NULL, o, NULL); } } } } } } } if (prompt.nchoices == 0) { msg("Your spell does not detect anything nearby."); } else { getchoice(&prompt); o = (object_t *)prompt.result; while (o) { // describe it describeob(o); // ask for another one getchoice(&prompt); o = (object_t *)prompt.result; } // restore screen needredraw = B_TRUE; statdirty = B_TRUE; drawscreen(); msg("Your spell finishes."); } } else if (spellid == OT_S_LORE) { enum SKILL skid; skill_t *sk; char ch = 'a'; if (!isplayer(caster)) { fizzle(caster); return B_TRUE; } // always targetted at caster targcell = caster->cell; target = caster; // ask for which knowledge skill initprompt(&prompt, "Which knowledge do you desire?"); prompt.maycancel = B_TRUE; for (skid = 0; skid < MAXSKILLS; skid++) { sk = findskill(skid); if (sk && isloreskill(skid)) { if (!getskill(caster, skid)) { addchoice(&prompt, ch++, sk->name, NULL, sk, NULL); } } } if (prompt.nchoices == 0) { fizzle(caster); return B_TRUE; } getchoice(&prompt); sk = (skill_t *)prompt.result; if (sk) { flag_t *f; f = addtempflag(caster->flags, F_HASSKILL, sk->id, (power/2)+1, NA, NULL, FROMSPELL); f->obfrom = spellid; msg("New knowledge floods into your mind!"); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_GRAVBOOST) { target = targcell->lf; if (target) { int howlong = 15; flag_t *f; int i; if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } f = hasactivespell(target, OT_S_GRAVLOWER); if (f) { stopspell(target, OT_S_GRAVLOWER); return B_FALSE; } i = 0; i += killtransitoryflags(target->flags, F_GRAVLESSENED); i += killtransitoryflagvals(target->flags, F_DTIMMUNE, DT_FALL, NA, NA, NULL); if (i) { return B_FALSE; } if (lfhasflag(target, F_GRAVBOOSTED) || spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily heavier."); } else if (cansee(player, target)) { char targname[BUFLEN]; getlfname(target, targname); msg("%s looks momentarily heavier.", targname); } return B_FALSE; } howlong = getspellduration(3,10,blessed) + (power/2); addtempflag(target->flags, F_GRAVBOOSTED, B_TRUE, NA, NA, NULL, howlong); } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_GRAVLOWER) { flag_t *f; // always targetted at caster targcell = caster->cell; target = caster; f = lfhasflag(target, F_GRAVBOOSTED); if (f) { killflag(f); if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } f = addtempflag(caster->flags, F_DTIMMUNE, DT_FALL, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; f = addtempflag(caster->flags, F_GRAVLESSENED, power, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_GUSTOFWIND) { obpile_t *op; object_t *o, *nexto; object_t *poss[MAXPILEOBS],*blowob[MAXPILEOBS]; int nposs = 0,nblowobs = 0; int i; char targname[BUFLEN]; target = targcell->lf; if (targcell->map->habitat->id == H_FOREST) { power += 5; limit(&power, NA, 10); } if (target) { getlfname(target, targname); // objects held by target op = target->pack; } else { // objects on ground op = targcell->obpile; } for (o = op->first ; o ; o = nexto) { nexto = o->next; if (!hasflag(o->flags, F_NOSTEAL) && !hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_COSMETIC)) { if ((getobweight(o) <= 5) || ((isweapon(o)) && (isequipped(o))) ) { poss[nposs++] = o; } } } for (i = 0; ((i < power) && (nposs > 0)); i++) { int sel,n; sel = rnd(0,nposs-1); blowob[nblowobs++] = poss[sel]; // remove ob from possibility list for (n = sel; n < (nposs-1); n++) { poss[sel] = poss[sel+1]; } nposs--; } if (target && cansee(player, target)) { msg("A gust of wind whips up around %s!", targname); } else if (haslos(player, targcell)) { msg("A gust of wind whips up from nowhere!"); } // chance of blowing each ob away for (i = 0; i < nblowobs; i++) { char obname[BUFLEN]; cell_t *c; condset_t cs; initcondv(&cs, CC_SOLID, B_FALSE, NA, CC_NONE); getobname(blowob[i], obname, 1); c = getrandomadjcell(targcell, &cs, B_ALLOWEXPAND); if (c && ((rnd(1,100)-power) <= 33)) { // move it fireat(NULL, blowob[i], 1, c, 4, NULL); } } if (target) { if (!real_fall_from_air(target, SZ_SMALL + (power/4))) { int pen = 0; // easyish save to avoid falling switch (getlfsize(target)) { case SZ_MEDIUM: pen = -15; break; case SZ_SMALL: pen = -25; break; case SZ_TINY: pen = -35; break; case SZ_MINI: pen = -45; break; break; default: pen = 0; break; } if (!skillcheck(target, SC_FALL, 60 + (power*5), pen)) { fall(target, NULL, B_TRUE); } } } needredraw = B_TRUE; } else if (spellid == OT_S_MIST) { object_t *o; if (!targcell || targcell->type->solid) { fizzle(caster); return B_TRUE; } if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; if (targcell->lf) { char lfname[BUFLEN]; getlfname(targcell->lf, lfname); msg("^%cA thick veil of mist surrounds %s!", getlfcol(targcell->lf, CC_GOOD), lfname); } else { msg("A thick veil of mist appears!"); } } o = addob(targcell->obpile, "thick mist"); if (o) { flag_t *f; f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] += power; f->val[1] += power; } } // hack - make all monsters who can see you stop targetting you. // otherwise they will just try to go to your last known location. breakaitargets(caster, B_TRUE); } else if (spellid == OT_S_MENDING) { object_t *o; int donesomething = B_FALSE; flag_t *f; char fullobname[BUFLEN]; char obname[BUFLEN]; if (targob) { o = targob; } else { condset_t cs; initcondv(&cs, CC_DAMAGED, B_TRUE, NA, CC_NONE); // ask for an object o = askobject(caster->pack, "Mend which object", NULL, NULL, '\0', &cs, B_FALSE); } if (!o) { fizzle(caster); return B_TRUE; } getobname(o, obname, o->amt); if (isplayer(caster)) { snprintf(fullobname, BUFLEN, "Your %s", noprefix(obname)); } else if (cansee(player, caster)) { snprintf(fullobname, BUFLEN, "%s%s %s", castername, getpossessive(castername), noprefix(obname)); } else { strcpy(fullobname, ""); } f = hasflag(o->flags, F_IMMUTABLE); if (f) { if (isplayer(caster)) { char obname[BUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); msg("For some reason, %s is unaffected!", fullobname); } f->known = B_TRUE; return B_FALSE; } f = hasflag(o->flags, F_OBHP); if (f) { if (blessed == B_CURSED) { if (isplayer(caster) || cansee(player, caster)) msg("%s deteriorates!", fullobname); takedamage(o, rnd(1,6) + power, DT_DIRECT, caster); donesomething = B_TRUE; } else if (isdamaged(o)) { f->val[0] += (rnd(1,6) + power); if (f->val[0] >= f->val[1]) { if (isplayer(caster) || cansee(player, caster)) msg("%s is completely repaired!", fullobname); f->val[0] = f->val[1]; } else { if (isplayer(caster) || cansee(player, caster)) msg("%s is repaired a little!", fullobname); } donesomething = B_TRUE; } } // fix rust and dulled weapons if (!iscursed(o)) { if (isweapon(o)) { f = hasflag(o->flags, F_BONUS); if (f && (f->val[0] < 0)) { killflag(f); if (isplayer(caster) || cansee(player, caster)) msg("%s seems more effective!", fullobname); donesomething = B_TRUE; } } if (killflagsofid(o->flags, F_RUSTED)) { if (isplayer(caster) || cansee(player, caster)) msg("%s is no longer rusted!", fullobname); donesomething = B_TRUE; } } if (donesomething) { if (strlen(fullobname)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { if (isplayer(caster)) nothinghappens(); return B_TRUE; } } else if (spellid == OT_S_PACIFY) { char targetname[BUFLEN]; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, targetname); if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { } else { int donesomething = B_FALSE; // stop targetting anybody if (killflagsofid(target->flags, F_TARGETLF)) { donesomething = B_TRUE; } if (killflagsofid(target->flags, F_TARGETCELL)) { donesomething = B_TRUE; } if (killflagsofid(target->flags, F_HOSTILE)) { donesomething = B_TRUE; } if (donesomething) { if (cansee(player, target)) { msg("%s calms down.",targetname); } } else { if (isplayer(caster)) { msg("%s already seems calm enough.",targetname); } } return B_FALSE; } } else if (spellid == OT_S_PASSWALL) { int howlong = 7; flag_t *f; target = caster; if (lfhasflag(target, F_NONCORPOREAL)) { if (frompot) { if (isplayer(caster)) nothinghappens(); } else { fizzle(caster); } return B_TRUE; } if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } howlong = getspellduration(SP_NORMAL,SP_NORMAL*2,blessed) + (power*SP_VERYFAST); // minimum of two moves. limit(&howlong, getmovespeed(target)*2, NA); f = addtempflag(target->flags, F_NONCORPOREAL, B_TRUE, NA, NA, NULL, howlong); f->obfrom = OT_S_PASSWALL; if (isplayer(target) && (howlong < getmovespeed(target)*3)) { msg("This won't last for long..."); } breakgrabs(target, B_TRUE, B_TRUE, B_TRUE); } else if ((spellid == OT_S_POLYMORPH) || (spellid == OT_S_SHAPESHIFT)) { race_t *r = NULL; flag_t *f; if (caster && (frompot || (spellid == OT_S_SHAPESHIFT))) { target = caster; } else { target = targcell->lf; } if (!target) { fizzle(caster); return B_TRUE; } if (spellid != OT_S_SHAPESHIFT) { if ((target != caster) && spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily different."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, target->cell)) { getlfname(target, buf); msg("%s looks momentarily different.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } } // if target has been resized, just revert to original size f = lfhasflag(target, F_SIZETIMER); if (f) { f->val[1] = 0; if (isplayer(target)) { msg("Your body seems to be changing shape!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char tname[BUFLEN]; getlfname(target, tname); msg("%s seems to be changing shape!", tname); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } //if ((caster == target) && getforcedspellrace(caster, spellid, buf)) { if (getforcedspellrace(target, spellid, buf, NULL)) { r = findracebyname(buf, NULL); } else { if (spellid == OT_S_POLYMORPH) { int dorandom = B_FALSE; if (caster && lfhasflag(caster, F_CONTROL)) { if (power < 5) { power = 5; } } if (power >= 5) { // controlled if (isplayer(caster)) { if (isplayer(target)) { // ie. polymorphing yourself askstring("What will you become", '?', buf, BUFLEN, NULL); } else { char buf2[BUFLEN]; char targname[BUFLEN]; getlfname(target, targname); snprintf(buf2, BUFLEN, "What will you transform %s into", targname); askstring(buf2, '?', buf, BUFLEN, NULL); } r = findracebyname(buf, NULL); } else { // TODO: select based on player damage, etc dorandom = B_TRUE; } } else { // random if (isplayer(target) && lfhasflag(target, F_CONTROL)) { askstring("What will you become", '?', buf, BUFLEN, NULL); r = findracebyname(buf, NULL); // make sure race is valid: if (r && !canpolymorphto(r->id)) r = NULL; } else { dorandom = B_TRUE; } } if (!r || dorandom) { int forcedepth; // want a random race with similar Threat Rating to the // target's current one. // // normally tr range is (depth/2) +/- RARITYVARIANCELF // // so for a given tr, ask for: // // depth = tr*2 forcedepth = gettr(target)*2; r = target->race; while ((r == target->race) || !canpolymorphto(r->id)) { r = getrandomrace(NULL, forcedepth, NULL); } } } else if (spellid == OT_S_SHAPESHIFT) { int i,ch = 'a'; // get list of all lfs in sight initprompt(&prompt, "What will you become?"); for (i = 0; i < target->nlos; i++) { if (target->los[i]->lf && cansee(target, target->los[i]->lf)) { race_t *potrace; int n; potrace = target->los[i]->lf->race; // same race? if (potrace == target->race) continue; // too powerful? if (gettrrace(potrace) > power) { continue; } // already in the list? for (n = 0; n < prompt.nchoices; n++) { if (prompt.choice[n].data == potrace) { continue; } } addchoice(&prompt, ch++, potrace->name, NULL, potrace, NULL); } } addchoice(&prompt, '-', "(cancel)", NULL, NULL, NULL); prompt.maycancel = B_TRUE; if (prompt.nchoices == 1) { if (isplayer(caster)) { msg("You cannot see any forms to copy!"); } else { fizzle(caster); } return B_TRUE; } else { if (isplayer(caster)) { getchoice(&prompt); r = (race_t *)prompt.result; } else { r = (race_t *)prompt.choice[rnd(0,prompt.nchoices-1)].data; } } } // make sure race is valid: // - can't turn into monsters which aren't randomly generated. // - can't turn into unique monsters // - can't turn into undead monsters if (r && !canpolymorphto(r->id) && !hasjob(caster, J_GOD)) { if (isplayer(caster)) msg("As you think of a %s, magic energy dampens around you.", r->name); r = NULL; } } // end if forcepoly if (r == target->race) { fizzle(caster); return B_TRUE; } if (r) { int howlong; //int rememberorig = B_FALSE; if (isplayer(target)) { // polymorph will be temporary... howlong = rnd(20,50); } else { howlong = PERMENANT; } polymorphto(target, r->id, howlong); if (isplayer(caster) && !isplayer(target) && !lfhasflag(target, F_UNIQUE)) { // permenant killflagsofid(target->flags, F_POLYMORPHED); killflagsofid(target->flags, F_ORIGRACE); } // if someone cast the spell at themself and it's controlled, they can change back at will. if (target == caster) { int canrevert = B_FALSE; if ((spellid == OT_S_POLYMORPH) && (power >= 5)) { canrevert = B_TRUE; } else if (spellid == OT_S_SHAPESHIFT) { canrevert = B_TRUE; } if (canrevert) { addflag(target->flags, F_CANWILL, OT_A_POLYREVERT, NA, NA, NULL); } } if (haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } breakgrabs(target, B_TRUE, B_TRUE, B_TRUE); } else { fizzle(caster); return B_TRUE; } } else if ((spellid == OT_S_PROTGOOD) || (spellid == OT_S_PROTEVIL)) { flag_t *f; if (!targcell) targcell = caster->cell; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } f = addtempflag(target->flags, F_PROTALIGN, 5+(power*2), (spellid == OT_S_PROTGOOD) ? AL_GOOD : AL_EVIL, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_PURIFYFOOD) { object_t *o,*nexto; obpile_t *op; int ndone = 0; if (power < 3) { targcell = caster->cell; } if (targcell->lf) { target = targcell->lf; op = targcell->lf->pack; } else { target = NULL; op = targcell->obpile; } for (o = op->first ; o ; o = nexto) { char obname[BUFLEN]; int donesomething = B_FALSE; flag_t *f; nexto = o->next; getobname(o, obname, o->amt); if ((f = hasflag(o->flags, F_PURIFIESTO)) != NULL) { if (target && isplayer(target)) { msg("Your %s sparkles for a while.", noprefix(obname)); } else if (haslos(player, targcell)) { msg("%s sparkles for a while.", obname); } o->type = findot(f->val[0]); if (isplayer(target)) { // you now know what water is. // you DONT know what the original type was, since it could have been a number // of different things. makeknown(o->type->id); } donesomething = B_TRUE; ndone++; } else { f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = f->val[1]; f = hasflag(o->flags, F_DECAY); if (f) { f->val[2] = 0; killflag(f); donesomething = B_TRUE; ndone++; } } if (killflagsofid(o->flags, F_TAINTED)) { donesomething = B_TRUE; ndone++; } } if (donesomething) { if (target && isplayer(target)) { if (isdrinkable(o)) { msg("Your %s looks more clean now.", noprefix(obname)); } else if (isedible(o)) { msg("Your %s looks more fresh now.", noprefix(obname)); } if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, targcell)) { if (isdrinkable(o)) { msg("%s looks more clean now.", obname); } else if (isedible(o)) { msg("%s looks more fresh now.", obname); } if (seenbyplayer) *seenbyplayer = B_TRUE; } } } if (!ndone) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_PYROMANIA) { int i,donesomething = B_FALSE; object_t *retob[MAXRECIPEINGREDIENTS]; int nretobs; if (!target) target = caster; for (i = 0; i < target->nlos; i++) { int n; flag_t *f; targcell = target->los[i]; getflamingobs(targcell->obpile, retob, &nretobs); for (n = 0 ; n < nretobs; n++) { donesomething = B_TRUE; if (retob[n]->material->id == MT_FIRE) { // large fires get surrounded with small ones f = hasflag(retob[n]->flags, F_DIECONVERT); if (f) { cell_t *retcell[MAXCANDIDATES]; int nretcells,nn; getradiuscells(targcell, 1, DT_COMPASS, B_FALSE, LOF_WALLSTOP, B_FALSE, retcell, &nretcells, 90); for (nn = 0; nn < nretcells; nn++) { addob(retcell[nn]->obpile, f->text); } } else { // small fires last longer f = hasflag(retob[n]->flags, F_OBHP); if (f) { f->val[1] = pctof(150, f->val[1]); f->val[0] = f->val[1]; } } } else { f = hasflag(retob[n]->flags, F_ONFIRE); if (f) { addob(targcell->obpile, "small fire"); } } if (haslos(player, targcell)) { char obname[BUFLEN]; getobname(retob[n], obname, retob[n]->amt); msg("%s flare%s up!", obname, OBS1(retob[n])); if (seenbyplayer) *seenbyplayer = B_TRUE; } } // end foreach flaming ob } if (!donesomething) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_QUENCH) { object_t *o,*nexto; int ndone = 0; if (!targcell) { fizzle(caster); return B_TRUE; } if (targcell->lf) { // all objects. for (o = targcell->lf->pack->first ; o ; o = nexto) { nexto = o->next; if (o->type->material->id == MT_FIRE) { if (haslos(player, targcell)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s vanishes!",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } killob(o); ndone++; } else { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } killflagsofid(o->flags, F_ONFIRE); ndone++; } } } for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; if (o->type->material->id == MT_FIRE) { if (haslos(player, targcell)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s vanishes!",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } killob(o); ndone++; } else { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } killflagsofid(o->flags, F_ONFIRE); ndone++; } } if (!ndone) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_QUICKENFIRE) { int howmany,i,n,sel,nposs = 0,nseen = 0; cell_t *c; object_t *o,*poss[MAXCANDIDATES]; howmany = (power / 2) + 1; // get a list of all fire cells near caster for (i = DC_N; i <= DC_NW; i++) { c = getcellindir(caster->cell, i); if (c) { o = hasobofmaterial(c->obpile, MT_FIRE); if (o) { poss[nposs++] = o; } } } limit(&howmany, NA, nposs); if (!howmany) { fizzle(caster); return B_TRUE; } // now change them for (i = 0; i < howmany; i++) { lifeform_t *lf; enum RACE rid; // pick a random one sel = rnd(0,nposs-1); o = poss[sel]; // turn it into a golem c = getoblocation(o); killob(o); if (power < 6) { rid = R_PRIMALFIREL; } else { rid = R_PRIMALFIRE; } lf = summonmonster(caster, c, rid, NULL, 30, B_TRUE); if (haslos(player, c)) { if (seenbyplayer) *seenbyplayer = B_TRUE; nseen++; } // remove it from the list for (n = sel ; n < (nposs-1); n++) { poss[n] = poss[n+1]; } nposs--; } // set dirty line of sight for caster, as walls have vanished caster->losdirty = B_TRUE; } else if (spellid == OT_S_QUICKENSTONE) { int howmany,i,n,sel,nposs = 0,nseen = 0; cell_t *c,*poss[MAXCANDIDATES]; howmany = (power / 2) + 1; // get a list of all stone cells near caster for (i = DC_N; i <= DC_NW; i++) { c = getcellindir(caster->cell, i); if (c && c->type->solid && (c->type->material->id == MT_STONE)) { poss[nposs++] = c; } } limit(&howmany, NA, nposs); if (!howmany) { fizzle(caster); return B_TRUE; } // now change them for (i = 0; i < howmany; i++) { lifeform_t *lf; enum RACE rid; // pick a random one sel = rnd(0,nposs-1); c = poss[sel]; // turn it into a golem setcelltype(c, c->map->habitat->emptycelltype); if (power < 6) { rid = R_PRIMALSTONEL; } else { rid = R_PRIMALSTONE; } lf = summonmonster(caster, c, rid, NULL, 30, B_TRUE); if (haslos(player, c)) { if (seenbyplayer) *seenbyplayer = B_TRUE; nseen++; } // remove it from the list for (n = sel ; n < (nposs-1); n++) { poss[n] = poss[n+1]; } nposs--; } // set dirty line of sight for caster, as walls have vanished caster->losdirty = B_TRUE; } else if (spellid == OT_S_LESSENPOISON) { flag_t *f; int ndone = 0; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } foreach_bucket(b) { for (f = target->flags->first[b] ; f ; f = f->next) { if (f->id == F_POISONED) { poisontype_t *pt; pt = findpoisontype(f->val[0]); if (pt->severity != PS_CURSE) { // slightly lower time if (f->lifetime > 1) { f->lifetime--; } // reduce power to minimum f->val[1] = 1; if (isplayer(target)) { msg("^%cYour %s seems less intense.",getlfcol(target, CC_GOOD),pt->name); } ndone++; } } else if (f->id == F_INCUBATING) { f->val[1] *= 2; // take longer to incubate } } } if (ndone) { if (seenbyplayer) *seenbyplayer = B_TRUE; } else { fizzle(caster); return B_TRUE; } if (!ndone) { return B_TRUE; } } else if (spellid == OT_S_LETHARGY) { int amttolose; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } //amttolose = roll("1d6") + power; amttolose = power; if (isplayer(target)) { msg("^%cYou suddenly feel very lethargic!", getlfcol(target, CC_BAD)); } else if (cansee(player, target)) { char targname[BUFLEN]; getlfname(target, targname); msg("^%c%s looks very lethargic!", getlfcol(target, CC_BAD), targname); } // cancels rage killflagsofid(target->flags, F_RAGE); modstamina(target, -amttolose); } else if (spellid == OT_S_REFRACTION) { flag_t *f; f = addtempflag(caster->flags, F_EVASION, (power*10), NA, NA, NULL, FROMSPELL); f->obfrom = spellid; if (isplayer(caster)) { msg("^%cYour body seems to shimmer and bend!", getlfcol(caster, CC_GOOD)); } else if (cansee(player, caster)) { msg("^%c%s%s body seems to shimmer and bend!", getlfcol(caster, CC_GOOD), castername, getpossessive(castername)); } } else if (spellid == OT_S_REMOTEKO) { char targetname[BUFLEN]; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } getlfname(target, targetname); if (checkcharm(caster, target)) return B_FALSE; if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); } } else { // ko ! loseconsciousness(target, rnd(50,100), caster); } } else if (spellid == OT_S_REPELINSECTS) { // just announce if (isplayer(caster)) { msg("A strange odour surrounds you..."); } } else if (spellid == OT_S_REPLENISH) { int incamt,newamt,maxed = B_FALSE; char obname[BUFLEN]; flag_t *f; if (!targob) { if (isplayer(caster)) { condset_t cs; initcondv(&cs, CC_HASFLAG, B_TRUE, F_REPLENISHABLE, CC_NONE); // ask for an object targob = askobject(caster->pack, "Replenish which object", NULL, NULL, '\0', &cs, B_FALSE); } } if (!targob) { fizzle(caster); return B_TRUE; } f = hasflag(targob->flags, F_CHARGES); if (!f) { if (isplayer(caster)) nothinghappens(); return B_TRUE; } // restores power% of maximum incamt = pctof(power*10, f->val[1]); limit(&incamt, 1, NA); // at least 1! newamt = f->val[0] + incamt; limit(&newamt, NA, f->val[1]); if (f->val[0] == newamt) { // already maxed maxed = B_TRUE; } f->val[0] = newamt; targcell = getoblocation(targob); if (maxed) { if (haslos(player, targcell)) { nothinghappens(); } return B_TRUE; } getobname(targob, obname, targob->amt); if (targob->pile->owner) { if (isplayer(targob->pile->owner)) { msg("Your %s %s %s.", noprefix(obname), (targob->amt == 1) ? "pulses" : "pulse", (power < 5) ? "briefly" : "for a while"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player,targob->pile->owner)) { char lfname[BUFLEN]; getlfname(targob->pile->owner, lfname); msg("%s%s %s %s %s.", lfname, getpossessive(lfname), noprefix(obname), (targob->amt == 1) ? "pulses" : "pulse", (power < 5) ? "briefly" : "for a while"); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { if (haslos(player, targcell)) { msg("%s %s %s.", noprefix(obname), (targob->amt == 1) ? "pulses" : "pulse", (power < 5) ? "briefly" : "for a while"); if (seenbyplayer) *seenbyplayer = B_TRUE; } } } else if (spellid == OT_S_RESSURECTION) { lifeform_t *newlf; if (!targob) { condset_t cs; initcondv(&cs, CC_HASFLAG, B_TRUE, F_CORPSEOF, CC_NONE); // select object from cell... targob = askobject(targcell->obpile, "Revive which corpse", NULL, NULL, '\0', &cs, B_FALSE); if (!targob) { fizzle(caster); return B_TRUE; } } newlf = ressurect(targob); if (newlf) { if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_RESTORATION) { int seen = B_FALSE; int i,hpheal,mpheal; int failed = B_TRUE; char lfname[BUFLEN]; if (!target) target = caster; getlfname(target, lfname); if (frompot) { if (blessed == B_BLESSED) { power = 3; } else if (blessed == B_CURSED) { power = 1; } else { power = 2; } } if (!isplayer(target) && cansee(player, target)) { seen = B_TRUE; } if (isundead(target)) { if (seen) { msg("^%c%s writhe%s in agony!",getlfcol(target, CC_BAD), lfname, isplayer(target) ? "" : "s"); } losehp(target, power*10, DT_HOLY, caster, "the power of restoration"); return B_FALSE; } for (i = 0; i < MAXATTS; i++ ){ if (getattr(target,i) < target->baseatt[i]) { setattr(target, i, target->baseatt[i]); failed = B_FALSE; } } // fix diseases (even magical ones) if (killflagsofid(target->flags, F_POISONED)) { failed = B_FALSE; } // wake up from sleep/ko if (killflagsofid(target->flags, F_ASLEEP)) { failed = B_FALSE; } // fix pain if (killflagsofid(target->flags, F_PAIN)) { failed = B_FALSE; } // fix injuries if (killflagsofid(target->flags, F_INJURY)) { failed = B_FALSE; } // blessed restores hp/mp to full if (power == 3) { // heal ALL hp & mp hpheal = target->maxhp; mpheal = getmaxmp(target); } else if (power == 2) { // heal 3/4 hp & mp hpheal = pctof(target->maxhp, 75); mpheal = pctof(getmaxmp(target), 75); } else { // heal half hp & mp hpheal = target->maxhp / 2; mpheal = getmaxmp(target) / 2; } if (target->hp < hpheal) { gainhp(target, hpheal - target->hp); failed = B_FALSE; if (!isplayer(target) && cansee(player, target)) { msg("^%c%s%s health has been restored!", getlfcol(target, CC_GOOD),lfname, getpossessive(lfname)); } } if (target->mp < mpheal) { gainmp(target, mpheal - target->mp); failed = B_FALSE; } if (failed) { if (isplayer(target)) msg("You feel momentarily restored."); fizzle(caster); return B_TRUE; } else { if (isplayer(target)) msg("You feel restored!"); } } else if (spellid == OT_S_REVEALHIDDEN) { int i; int seen = B_FALSE; if (!isplayer(caster)) return B_TRUE; if (!target) target = caster; for (i = 0 ; i < target->nlos; i++ ){ targcell = target->los[i]; if (targcell) { object_t *o; if (isplayer(target)) { // reveal secret doors/obs/etc for (o = targcell->obpile->first ; o ; o = o->next) { flag_t *f; f = hasflag(o->flags, F_SECRET); if (f && (f->val[0] != NA)) { char obname[BUFLEN]; killflag(f); getobname(o, obname, o->amt); msg("^G%s is magically revealed!", obname); seen = B_TRUE; } if (hasflag(o->flags, F_ISMONSTER)) { lifeform_t *newlf; newlf = reveal_pretendob(o); if (newlf) { char lfname[BUFLEN]; getlfnamea(newlf, lfname); msg("^G%s is magically revealed!", lfname); seen = B_TRUE; } } } } // remove invisibility/hiding if (targcell->lf) { flag_t *f, *nextf; foreach_bucket(b) { for (f = targcell->lf->flags->first[b] ; f ; f = nextf) { nextf = f->next; if (f->id == F_INVISIBLE) { if ((f->lifetime > 0) || (f->lifetime == PERMENANT)) { killflag(f); // player can see whoever just appeared, and the caster? if (cansee(player, targcell->lf) && cansee(player, target)) { char tname[BUFLEN]; seen = B_TRUE; getlfname(targcell->lf, tname); msg("%s becomes visible!", tname); } } } else if ( (f->id == F_HIDING) && !lfhasflagval(target, F_SPOTTED, targcell->lf->id, NA, NA, NULL)) { addflag(target->flags, F_SPOTTED, targcell->lf->id, NA, NA, NULL); // player can see whoever just appeared, and the caster? if (cansee(player, targcell->lf) && cansee(player, target)) { char tname[BUFLEN]; seen = B_TRUE; getlfname(targcell->lf, tname); msg("A hiding %s is revealed!", noprefix(tname)); } } } } } } } if (seen) { if (seenbyplayer) *seenbyplayer = B_TRUE; if (isplayer(caster)) { needredraw = B_TRUE; } } else { if (isplayer(caster)) { nothinghappens(); } } } else if (spellid == OT_S_SATEHUNGER) { int hunger; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } hunger = gethungerval(target); if (hunger > 0) { int modamt; modamt = hunger; // take away all current hunger modamt += HUNGERCONST * 2; // ... then make them stuffed modhunger(target, -modamt); if (isplayer(target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SEEINVIS) { flag_t *f; // always targetted at caster targcell = caster->cell; target = caster; f = addtempflag(caster->flags, F_SEEINVIS, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_SHAPEMETAL) { object_t *o; int donesomething = B_FALSE; flag_t *f; char fullobname[BUFLEN]; char obname[BUFLEN]; if (targob) { o = targob; } else { condset_t cs; initcondv(&cs, CC_HASMATERIAL, B_TRUE, MT_METAL, CC_NONE); // ask for an object o = doaskobject(caster->pack, "Repair which metal object", NULL, NULL, B_FALSE, B_FALSE, B_FALSE, '\0', NULL, SA_NONE, &cs, B_FALSE); } if (!o) { fizzle(caster); return B_TRUE; } getobname(o, obname, o->amt); if (isplayer(caster)) { snprintf(fullobname, BUFLEN, "Your %s", noprefix(obname)); } else if (cansee(player, caster)) { snprintf(fullobname, BUFLEN, "%s%s %s", castername, getpossessive(castername), noprefix(obname)); } else { strcpy(fullobname, ""); } f = hasflag(o->flags, F_IMMUTABLE); if (f) { if (isplayer(caster)) { char obname[BUFLEN]; real_getobname(o, obname, o->amt, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); msg("For some reason, %s is unaffected!", fullobname); } f->known = B_TRUE; return B_FALSE; } f = hasflag(o->flags, F_OBHP); if (f) { if (isdamaged(o)) { f->val[0] = f->val[1]; if (isplayer(caster) || cansee(player, caster)) msg("%s is completely repaired!", fullobname); donesomething = B_TRUE; } } // fix rust and dulled weapons if (isweapon(o)) { f = hasflag(o->flags, F_BONUS); if (f && (f->val[0] < 0)) { killflag(f); if (isplayer(caster) || cansee(player, caster)) msg("%s seems more effective!", fullobname); donesomething = B_TRUE; } } if (killflagsofid(o->flags, F_RUSTED)) { if (isplayer(caster) || cansee(player, caster)) msg("%s is no longer rusted!", fullobname); donesomething = B_TRUE; } if (donesomething) { if (strlen(fullobname)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { if (isplayer(caster)) nothinghappens(); return B_TRUE; } } else if (spellid == OT_S_SHARDSHOT) { cell_t *retcell[MAXRETCELLS]; int nretcells; int i; int nhits = power; // create a line of fire towards the target cell calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcells); animcells(caster->cell, &retcell[1], nretcells-1, B_FALSE, '*', '\0', C_WHITE); if (cansee(player, caster)) { msg("%s fire%s a burst of ice shards!",castername, isplayer(caster) ? "" : "s"); } // don't hit the caster cell on fire! for (i = 1; (i < nretcells) && (nhits > 0); i++) { cell_t *c; c = retcell[i]; if (c->lf) { int dam; dam = rolldie(nhits, 6); // hit with ice if (check_for_block(caster, c->lf, dam, DT_COLD, 999, "a burst of ice shards", B_RANGED)) { } else { if (isplayer(c->lf) || cansee(player, c->lf)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("^%cA burst of ice shards hits %s.",getlfcol(target, CC_BAD), lfname); } losehp(c->lf, dam, DT_COLD, caster, "a burst of ice shards"); } nhits--; } if (haslos(player, c)) { needredraw = B_TRUE; if (seenbyplayer) *seenbyplayer = B_TRUE; } } } else if (spellid == OT_S_SHATTER) { char buf[BUFLEN]; if (targcell->lf && isplayer(targcell->lf)) { msg("Ultra-sonic vibrations ring through your body!"); } snprintf(buf, BUFLEN, "%s%s shatter spell", castername, getpossessive(castername)); if (!shattercell(targcell, caster, buf)) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SILENCE) { int howlong = 30; target = targcell->lf; if (!target || hasflag(target->flags, F_SILENCED)) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (!isdeaf(player)) { if (isplayer(target)) { msg("Noise around you sound softer for a moment."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, target->cell)) { getlfname(target, buf); msg("Noises around %s sound softer for a moment.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } } return B_FALSE; } howlong = rnd(20,30) + power*2; addtempflag(target->flags, F_SILENCED, NA, NA, NA, NULL, howlong); if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_SIXTHSENSE) { flag_t *f; if (!target) target = caster; if (lfhasflag(target, F_SIXTHSENSE)) { fizzle(target); return B_TRUE; } f = addtempflag(caster->flags, F_SIXTHSENSE, power, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if ((spellid == OT_S_SIZEUP) || (spellid == OT_S_SIZEDOWN)) { enum LFSIZE origsize,newsize; char origstats[BUFLEN]; int reverting = B_FALSE; int doobs = B_FALSE; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } origsize = getlfsize(target); if ((power >= 5) && areallies(target,caster)) { doobs = B_TRUE; } if (spellid == OT_S_SIZEUP) { if (origsize >= SZ_ENORMOUS) { fizzle(caster); return B_TRUE; } newsize = origsize + 1; } else { // ie. sizedown if (origsize <= SZ_MINI) { fizzle(caster); return B_TRUE; } newsize = origsize - 1; } if (lfhasflag(target, F_SIZETIMER)) { reverting = B_TRUE; } if (resizelf(target, newsize, doobs)) { // failed fizzle(caster); return B_TRUE; } if (!reverting) { int newstr; snprintf(origstats, BUFLEN, "%d,%d", target->att[A_STR], target->maxhp); // affect str / hp newstr = target->att[A_STR] + 50; target->att[A_STR] = newstr; target->maxhp *= 2; if (isplayer(target)) statdirty = B_TRUE; // revert in a little while... addflag(target->flags, F_SIZETIMER, origsize, power*10, doobs, origstats); } if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_SLEEP) { int howlong; if (!target) target = targcell->lf; if (lfhasflag(target, F_ASLEEP) || !cansleep(target)) { if (!frompot) fizzle(caster); return B_TRUE; } if (isplayer(target)) { msg("A sudden feeling of drowsiness washes over you!"); } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { // 1st skill check passed. make another one. if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { // 2nd one passed - no effect.. if (isplayer(target)) { msg("You yawn."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { getlfname(target, buf); msg("%s yawns.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { // 2nd one failed - lose all stamina instead modstamina(target, -(getstamina(target))); if (isplayer(target)) { msg("^%cYou suddenly feel very lethargic!",getlfcol(target, CC_BAD)); } else if (cansee(player, target)) { getlfname(target, buf); msg("^%c%s looks very lethargic!", getlfcol(target, CC_BAD),buf); } } return B_FALSE; } howlong = getspellduration(5,10,blessed) + (power/2); fallasleep(target, ST_ASLEEP, howlong); if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if (spellid == OT_S_SLEETSTORM) { int failed = B_FALSE; if (caster && caster->cell->map->habitat->id == H_FOREST) { power += 3; limit(&power, NA, 10); } if (targcell) { if (!targcell->type->solid) { int radius; // centre storm here... if (haslos(player, targcell)) { msg("An raging storm of sleet appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } radius = power / 2; if (radius < 1) radius = 1; addobsinradius(targcell, radius, DT_ORTH, "storm of sleet", B_FALSE, B_INCLUDECENTRE, caster, NULL, NULL, NULL); } else { failed = B_TRUE; } } else { failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SLIDE) { flag_t *f; target = caster; if (lfhasflag(target, F_ICESLIDE)) { fizzle(target); return B_TRUE; } f = addtempflag(target->flags, F_ICESLIDE, power, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; f = addtempflag(target->flags, F_AUTOCREATEOB, 0, NA, NA, "sheet of ice", FROMSPELL); f->obfrom = spellid; f = addtempflag(target->flags, F_FASTMOVE, 10, NA, NA, "sheet of ice", FROMSPELL); f->obfrom = spellid; if (cansee(player, target) && seenbyplayer) if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (spellid == OT_S_SLOW) { int howlong = 15; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily slower."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, target->cell)) { getlfname(target, buf); msg("%s looks momentarily slower.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } howlong = rnd(5,25) + power; addtempflag(target->flags, F_SLOWACTMOVE, 10, NA, NA, NULL, howlong); if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else if ((spellid == OT_S_SMITEEVIL) || (spellid == OT_S_SMITEGOOD)) { enum ALIGNMENT wantalign; if (spellid == OT_S_SMITEEVIL) { wantalign = AL_EVIL; } else { wantalign = AL_GOOD; } target = targcell->lf; if (!target || (getalignment(target) != wantalign)) { fizzle(caster); return B_TRUE; } if (isplayer(target)) { msg("^%cThe power of %s smites you!",getlfcol(target, CC_BAD), (spellid == OT_S_SMITEEVIL) ? "good" : "evil"); } else if (cansee(player, target)) { char lfname[BUFLEN]; getlfname(target, lfname); msg("^%cThe power of %s smites %s!", getlfcol(target, CC_BAD), (spellid == OT_S_SMITEEVIL) ? "good" : "evil", lfname); } // use direct damage rather than holy, because otherwise it might be increased // due to vulnerabilities losehp(target, rolldie(1+power,4), DT_DIRECT, caster, "a smiting"); } else if (spellid == OT_S_SNAPFREEZE) { target = haslf(targcell); if (target) { if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { // they get angry! if (!isplayer(target) && cansee(target, caster)) { fightback(target, caster); } } else { if (seenbyplayer && (isplayer(caster) || cansee(player, target))) *seenbyplayer = B_TRUE; freezelf(target, caster, rnd(20,40)); } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SNOWBALL) { int failed = B_FALSE; // ask for a target cell if (targcell) { if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) { int dir; char buf[BUFLEN]; cell_t *c; // centre snowball here... if (isplayer(caster)) { msg("You launch a huge snowball!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { msg("%s launches a huge snowball!",castername); if (seenbyplayer) *seenbyplayer = B_TRUE; } anim(caster->cell, targcell, '^', C_WHITE); snprintf(buf, BUFLEN, "A huge snowball explodes! "); animradial(targcell, 1, '}', C_WHITE, DT_ORTH, buf, buf); redrawpause(); // add snow as follows (3 = medium, 2 = medium, 1 = smell) // * // *** // * if (targcell->lf) { if (check_for_block(caster, targcell->lf, 1, DT_COLD, 999, "a snowball", B_RANGED)) { } else { losehp(targcell->lf, 1, DT_COLD, caster, "a snowball"); } } else { addob(targcell->obpile, "small puddle of water"); } for (dir = D_N; dir <= D_W; dir++) { c = getcellindir(targcell, dir); if (c && !c->type->solid ) { if (c->lf) { if (check_for_block(caster, c->lf, 1, DT_COLD, 999, "a burst of snow", B_RANGED)) { } else { losehp(c->lf, 1, DT_COLD, caster, "a burst of snow"); } } else { addob(c->obpile, "small puddle of water"); } } } redrawresume(); } else { failed = B_TRUE; } } else { if (isplayer(caster)) { msg("You have no line of fire to there!"); } failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SOFTENEARTH) { int seenground = B_FALSE; int seenwall = B_FALSE; int seenboulder = B_FALSE; int ndone = 0; int powerleft = power; // ask for a target cell if (targcell) { object_t *o,*oo; int i; oo = hasob(targcell->obpile, OT_BOULDER); while (oo && powerleft) { removeob(oo, 1); o = addobfast(targcell->obpile, OT_RUBBLE); ndone++; powerleft--; if (haslos(player, targcell)) { seenboulder++; msg("A boulder crumbles into rubble!"); } oo = hasob(targcell->obpile, OT_BOULDER); } if (powerleft) { // do first cell // is it actually earth here? if (targcell->type->material->id == MT_STONE) { if (targcell->type->solid) { if (power >= 2) { // soften into dirt setcelltype(targcell, CT_WALLDIRT); if (haslos(player, targcell)) seenwall = B_TRUE; ndone++; powerleft = 0; } } else { if (targcell->type->id != CT_DIRT) setcelltype(targcell, CT_DIRT); o = addob(targcell->obpile, "pool of mud"); if (o) { condset_t cs; initcondv(&cs, CC_SOLID, B_FALSE, NA, CC_HASOBTYPE, B_FALSE, OT_MUDPOOL, CC_HASMATERIAL, B_TRUE, MT_STONE, CC_NONE); ndone++; powerleft--; if (haslos(player, targcell)) seenground = B_TRUE; // do the rest for (i = 0; i < powerleft; i++) { cell_t *c; c = real_getrandomadjcell(targcell, &cs, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); if (c) { if (targcell->type->id != CT_DIRT) setcelltype(targcell, CT_DIRT); o = addob(c->obpile, "pool of mud"); if (o) { ndone++; if (haslos(player, targcell)) seenground = B_TRUE; } } } } } // end if solid } // end if material is stone } // end if powerleft } // end if targcell if (ndone) { if (seenground) { if (seenbyplayer) *seenbyplayer = B_TRUE; msg("The ground nearby softens into mud."); } if (seenwall) { if (seenbyplayer) *seenbyplayer = B_TRUE; msg("The walls nearby soften into dirt."); } if (seenboulder) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SOULLINK) { lifeform_t *targ2 = NULL; char buf[BUFLEN],tname[BUFLEN],t2name[BUFLEN]; if (caster && !isplayer(caster)) { fizzle(caster); return B_TRUE; } if (!target) { target = targcell->lf; } if (!target || (target == caster)) { fizzle(caster); return B_TRUE; } real_getlfnamea(target, tname, NULL, B_SHOWALL, B_REALRACE); if (power < 3) { // link caster to target targ2 = caster; } else { cell_t *where; char smallprompt[BUFLEN]; // link target to target2 // ask for a target cell snprintf(buf, BUFLEN, "Who will you soul-link %s to?", tname); snprintf(smallprompt, BUFLEN, "soul link:%s->", tname); where = askcoords(buf, smallprompt, TT_MONSTER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && where->lf && cansee(caster, where->lf)) { targ2 = where->lf; } else { fizzle(caster); return B_TRUE; } } if (targ2 == target) { fizzle(caster); return B_TRUE; } real_getlfnamea(targ2, t2name, NULL, B_SHOWALL, B_REALRACE); if (hasflag(target->flags, F_SOULLINK) || hasflag(targ2->flags, F_SOULLINK)) { msg("^bAn existing soul link prevents your spell from working!"); return B_TRUE; } // link caster to target snprintf(buf, BUFLEN, "%s%s soul link spell", castername, getpossessive(castername)); addflag(target->flags, F_SOULLINK, targ2->id, NA, NA, buf); addflag(targ2->flags, F_SOULLINK, target->id, NA, NA, buf); // soullink flag is only announce for the player, so oif we linked to someone else, // provide some feedback. if (targ2 != player) { msg("^gYou establish a soul link between %s and %s!",tname,t2name); } } else if (spellid == OT_S_SPARK) { object_t *o,*nexto; int donesomething = B_FALSE; int nburnt = 0; if (haslos(player, targcell)) { if (targcell->lf && cansee(player, targcell->lf)) { char lfname[BUFLEN]; getlfname(targcell->lf, lfname); msg("^%cA small burst of flame singes %s.", getlfcol(targcell->lf, CC_BAD),lfname); } else { msg("A small burst of flame appears."); } if (seenbyplayer) *seenbyplayer = B_TRUE; } if (targcell->lf) { losehp(targcell->lf, roll("2d3"), DT_FIRE, caster, "a burst of flame"); } for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; // special cases // this spell isn't powerful enough to burn or heat up other things if (isflammable(o) || (o->type->id == OT_CANDLE) || (o->type->id == OT_TORCH)) { takedamage(o, 1, DT_FIRE, caster); donesomething = B_TRUE; nburnt++; } } } else if (spellid == OT_S_SPEAKDEAD) { object_t *corpse = NULL; if (!isplayer(caster)) { return B_FALSE; } corpse = hasobwithflag(caster->cell->obpile, F_CORPSEOF); if (corpse) { char buf[BUFLEN],corpsename[BUFLEN]; char ch; int done = B_FALSE; flag_t *cf; race_t *corpserace = NULL; cf = hasflag(corpse->flags, F_CORPSEOF); if (cf) { corpserace = findrace(cf->val[0]); } if (corpserace) { getobname(corpse, corpsename, 1); msg("An ghostly spirit rises from %s!", corpsename); more(); if (racecantalk(corpserace->id)) { // TODO: check if you speak a common language while (!done) { snprintf(buf, BUFLEN, "What will you ask %s?", corpsename); initprompt(&prompt, buf); addchoice(&prompt, 'a', "How did you die?", NULL, NULL, NULL); addchoice(&prompt, 'b', "Tell me about this area", NULL, NULL, NULL); addchoice(&prompt, 'c', "Are there any hidden dangers nearby?", NULL, NULL, NULL); addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); prompt.maycancel = B_TRUE; ch = getchoice(&prompt); if ((ch != '\0') && (ch != '-')) { snprintf(buf, BUFLEN, "%s whispers:", corpsename); msg(buf); } if (ch == 'a') { flag_t *f; char *p; f = hasflag(corpse->flags, F_CORPSEOF); if (f && strlen(f->text)) { char killer[BUFLEN]; char weapon[BUFLEN]; p = readuntil(killer, f->text, '^'); if (strstr(p, "weilding")) { p = readuntil(weapon, p, '^'); } else { strcpy(weapon, ""); } snprintf(buf, BUFLEN, "\"I was killed by %s", killer); if (strlen(weapon)) { strcat(buf, ", "); strcat(buf, weapon); } strcat(buf, ".\""); msg(buf); } else { msg("\"I do not know what killed me.\""); } } else if (ch == 'b') { genareaknowledge(corpse->flags, 50); docomms_areainfo(corpsename, corpse->flags, NULL); if (onein(3)) done = B_TRUE; } else if (ch == 'c') { genareaknowledge(corpse->flags, 50); docomms_areadangers(corpsename, corpse->flags, NULL); if (onein(3)) done = B_TRUE; } else { done = B_TRUE; } } // end while !done } else { // can't talk msg("Unfortunately, the spirit doesn't seem capable of speech."); } // end if cantalk // destroy the corpse. msg("%s crumbles to dust.", corpsename); removeob(corpse, ALL); } else { fizzle(caster); return B_TRUE; } } else { fizzle(caster); return B_TRUE; } // end if corpse } else if (spellid == OT_S_SPIKEVOLLEY) { object_t *o; flag_t *f; // create a volley of spikes o = addobfast(caster->pack, OT_SPIKEVOLLEY); if (!o) { fizzle(caster); } if (isplayer(caster) || cansee(player, caster)) { msg("%s fire%s a volley of spikes!",castername,isplayer(caster) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } f = hasflag(o->flags, F_MISSILEDAM); if (f) { // should always be true char dambuf[BUFLEN]; free(f->text); snprintf(dambuf, BUFLEN, "%dd4", power*3); f->text = strdup(dambuf); } real_fireat(caster, o, 1, targcell, 6, NULL, B_FALSE, OT_S_SPIKEVOLLEY, NULL); } else if (spellid == OT_S_STASIS) { flag_t *f; f = addtempflag(caster->flags, F_STASIS, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_STENCH) { int howlong; enum ERROR why; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You feel momentarily nauseated."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { char buf[BUFLEN]; getlfname(target, buf); msg("%s looks momentarily unwell.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } howlong = getspellduration(5,10,blessed) + power; if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (makenauseated(target, power, howlong, &why)) { // if 'why' was 'resisted', then makenauseated() // will ahve already announced the failure. if (isplayer(caster) && (why != E_RESISTED)) { char buf[BUFLEN]; getlfname(target, buf); msg("%s seems unaffected.", buf); } // failed return B_TRUE; } } else if (spellid == OT_S_STICKTOSNAKE) { int i; //cell_t *poss[MAXCANDIDATES]; //int nposs; race_t *raceposs[MAXCANDIDATES],*r; int nraceposs = 0; cell_t *c = NULL,*newcell = NULL; object_t *o,*nexto; int ndone = 0; // power is always at lesat 2 for this spell if (power < 2) power = 2; // get list of possible snakes for (r = firstrace ; r ; r = r->next) { if (strstr(r->name, " snake")) { int tr; tr = gettrrace(r); if (power >= tr*2) { raceposs[nraceposs++] = r; } } } /* poss[0] = caster->cell; nposs = 1; for (i = 0; i < caster->nlos; i++) { if (caster->los[i] != caster->cell) { poss[nposs++] = caster->los[i]; } } */ // for each cell we can see... (including our own, but don't affect our own weapon) for (i = 0; i < caster->nlos; i++) { c = caster->los[i]; if (c->lf && cansee(caster, c->lf) && (c->lf != caster)) { for (o = c->lf->pack->first ; o ; o = nexto) { nexto = o->next; if ( hasflag(o->flags, F_RODSHAPED) && isequipped(o) && (o->blessed == B_UNCURSED) && !hasflag(o->flags, F_HASBRAND)) { char obname[BUFLEN]; char lfname[BUFLEN]; getobname(o, obname, o->amt); getlfname(c->lf, lfname); newcell = getrandomadjcell(c, &ccwalkable, B_NOEXPAND); if (newcell) { lifeform_t *snake; // add a snake there. snake = addlf(newcell, raceposs[rnd(0,nraceposs-1)]->id, 1); if (snake) { msg("%s%s %s transforms into a snake!",lfname, getpossessive(lfname), noprefix(obname)); killflagsofid(snake->flags, F_XPVAL); addflag(snake->flags, F_XPVAL, 0, NA, NA, NULL); killflagsofid(snake->flags, F_FLEEONDAM); killflagsofid(snake->flags, F_FLEEONHPPCT); addflag(snake->flags, F_NOFLEE, B_TRUE, NA, NA, NULL); makefriendly(snake, PERMENANT); if (c->lf == caster) { char fullobname[BUFLEN]; getobnametrue(o, fullobname, 1); killflagsofid(snake->flags, F_CORPSETYPE); addflag(snake->flags, F_CORPSETYPE, NA, NA, NA, fullobname); } killob(o); ndone++; } } else { msg("%s%s %s quivers for a moment.",lfname, getpossessive(lfname), noprefix(obname)); ndone++; } } } } else { // ie. no lf there, or can't see it for (o = c->obpile->first ; o ; o = nexto) { lifeform_t *snake = NULL; nexto = o->next; if ( hasflag(o->flags, F_RODSHAPED) && (o->blessed == B_UNCURSED) && !hasflag(o->flags, F_HASBRAND)) { cell_t *newcell = NULL; char obname[BUFLEN]; getobname(o, obname, o->amt); // add to current cell or new one? if (c->lf) { // ie. if a previous object made a snake here newcell = getrandomadjcell(c, &ccwalkable, B_NOEXPAND); } else { newcell = c; } if (newcell) { // add a snake there. snake = addlf(newcell, raceposs[rnd(0,nraceposs-1)]->id, 1); if (snake) { msg("%s transforms into a snake!",obname); killob(o); killflagsofid(snake->flags, F_XPVAL); addflag(snake->flags, F_XPVAL, 0, NA, NA, NULL); killflagsofid(snake->flags, F_FLEEONDAM); killflagsofid(snake->flags, F_FLEEONHPPCT); addflag(snake->flags, F_NOFLEE, B_TRUE, NA, NA, NULL); makefriendly(snake, PERMENANT); ndone++; } } else { msg("%s quivers for a moment.",obname); ndone++; } } } } } if (ndone) { if (seenbyplayer) *seenbyplayer = B_TRUE; } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_STUN) { target = targcell->lf; if (lfhasflag(target, F_ASLEEP) || !ischarmable(target)) { fizzle(caster); return B_TRUE; } if (spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { return B_FALSE; } else { stun(target, (power/5)+2); if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } } } else if (spellid == OT_S_SLEEPMASS) { int i; int donesomething = B_FALSE; target = caster; // sleep everyone within los of caster for (i = 0; i < target->nlos; i++) { lifeform_t *thistarg; targcell = target->los[i]; thistarg = targcell->lf; if (thistarg && (thistarg != caster)) { if (lfhasflag(thistarg, F_ASLEEP) || !ischarmable(thistarg)) { continue; } // we set frompot to B_TRUE to avoid 'fizzled' messages if there is someone // unaffected in range. dospelleffects(caster, OT_S_SLEEP, power+1, thistarg,NULL,NULL,blessed, seenbyplayer, B_TRUE, NULL); } } if (!donesomething) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SUCK) { target = targcell->lf; if (target) { int failed = B_FALSE; if (!fromob && spellresisted(target, caster, spellid, power, seenbyplayer, B_TRUE)) { failed = B_TRUE; } else if (fromob && skillcheck(target, SC_DODGE, 60, 0)) { failed = B_TRUE; // announce whip failures if (cansee(player, target)) { char obname[BUFLEN]; char tname[BUFLEN]; real_getobname(fromob, obname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); getlfname(target, tname); msg("%s%s %s misses %s.", castername, getpossessive(castername), noprefix(obname), tname); } return B_FALSE; } if (!failed) { // announce whip attacks if (fromob && cansee(player, target)) { char obname[BUFLEN]; char tname[BUFLEN]; real_getobname(fromob, obname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); getlfname(target, tname); msg("%s%s %s wraps around %s.", castername, getpossessive(castername), noprefix(obname), tname); } if (caster) { fightback(target, caster); } // they get pulled towards caster failed = pullnextto(target, caster->cell); } if (isplayer(target) || cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (failed) { if (isplayer(target) || cansee(player, target)) { char buf[BUFLEN]; getlfname(target, buf); msg("%s %s pulled forward slightly.", buf, is(target)); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } else { // if successful, target gets delayed so they can't just // walk away again taketime(target, getactspeed(target)); } } else { if (!fromob) fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_SUPERHEAT) { char obname[BUFLEN]; // needs: // object = which potion to throw // cell = where to throw the potion if (!targob && isplayer(caster)) { condset_t cs; initcondv(&cs, CC_DRINKABLE, B_TRUE, NA, CC_NONE); targob = askobject(caster->pack, "Superheat which potion", NULL, NULL, 'q', &cs, B_FALSE); } if (!targob) { fizzle(caster); return B_TRUE; } targob = splitob(targob); if (!targob) { if (isplayer(caster)) { msg("Your pack is too full for this spell to work!"); } fizzle(caster); return B_TRUE; } getobname(targob, obname, 1); if (isplayer(caster)) { msg("Your %s begins to bubble violently!", noprefix(obname)); more(); } else if (cansee(player, caster)) { msg("%s%s %s begins to bubble violently!", castername, getpossessive(castername), noprefix(obname)); } // potion will explode next turn if you don't throw it. addflag(targob->flags, F_EXPLODEONDEATH, NA, 1, NA, "6d2"); //ie . 6-12 damage addflag(targob->flags, F_EXPLODEONDAM, NA, 1, NA, "6d2"); //ie . 6-12 damage addflag(targob->flags, F_OBHPDRAIN, 1, NA, NA, NULL); if (isplayer(caster) && !targcell) { char buf[BUFLEN],buf2[BUFLEN]; snprintf(buf, BUFLEN, "Throw %s where?", obname); snprintf(buf2, BUFLEN, "Superheat->%s->", obname); targcell = askcoords(buf, buf2,TT_MONSTER, caster, getmaxthrowrange(caster, targob), LOF_NEED, B_TRUE); } if (!targcell) { fizzle(caster); return B_TRUE; } fireat(caster, targob, 1, targcell, getthrowspeed(caster), NULL); } else if (spellid == OT_S_TAILWIND) { if (!target) target = caster; if (isplayer(target)) { msg("A strong wind propels you forwards!"); } else if (cansee(player, target)) { char targname[BUFLEN]; getlfname(target, targname); msg("A strong wind propels %s forwards!", targname); } } else if (spellid == OT_S_TELEPORT) { cell_t *c = NULL; lifeform_t *ally[8]; int nallies = 0; // target is always the caster if (!target) target = caster; if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (isplayer(target)) { msg("You flicker."); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (haslos(player, target->cell)) { getlfname(target, buf); msg("%s flickers.", buf); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } if (lfhasflag(caster, F_CONTROL)) { /* if (power < 5) { power = 5; } else if (power < 8) { power = 8; } */ if (power < 8) { power = 8; } } if ((power < 5) || !isplayer(caster)) { c = NULL; if (blessed == B_CURSED) { lifeform_t *lf; // next to a random monster for (lf = target->cell->map->lf ; lf ; lf = lf->next) { if (areenemies(lf, target)) { c = getrandomadjcell(lf->cell, &ccwalkable, B_NOEXPAND); if (c) break; } } } if (!c) { // random while (!c || !cellwalkable(target, c, NULL) || celldangerous(target, c, B_FALSE, NULL)) { c = getrandomcell(target->cell->map); } } } else if (power >= 8) { // controlled snprintf(buf, BUFLEN, "Where will you teleport to?"); while (!c) { int ch; c = askcoords(buf, "Teleport->",TT_NONE, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); if (!c) { fizzle(caster); return B_TRUE; } else if (!c->known) { // confirm ch = askchar("Are you sure to want to teleport into the unknown?", "yn", "n", B_TRUE, B_FALSE); if (ch != 'y') c = NULL; } else if (c->type->solid) { // confirm ch = askchar("Are you sure to want to teleport into solid rock?", "yn", "n", B_TRUE, B_FALSE); if (ch != 'y') c = NULL; } } } else { // ie. if (power >= 5) int dir; cell_t *poss[MAX_MAPW * MAX_MAPH]; int nposs; int x,y; int xmin,ymin; int xmax,ymax; // semicontrolled // ask for dir dir = askdir("Teleport in which direction (- to cancel)", B_TRUE, B_TRUE); if ((dir == D_NONE) || (dir == D_MYSELF)) { fizzle(caster); return B_TRUE; } // make a list of all valid cells in that dir nposs = 0; switch (dir) { case D_N: case DC_N: ymin = 0; ymax = target->cell->y - 1; xmin = target->cell->x - 5; xmax = target->cell->x + 5; break; case DC_NE: xmin = target->cell->x + 1; ymin = 0; xmax = target->cell->map->w - 1; ymax = target->cell->y - 1; break; case D_E: case DC_E: xmin = target->cell->x + 1; xmax = target->cell->map->w - 1; ymin = target->cell->y - 5;; ymax = target->cell->y + 5;; break; case DC_SE: xmin = target->cell->x + 1; ymin = target->cell->y + 1; xmax = target->cell->map->w - 1; ymax = target->cell->map->h - 1; break; case D_S: case DC_S: ymin = target->cell->y + 1; ymax = target->cell->map->h - 1; xmin = target->cell->x - 5; xmax = target->cell->x + 5; break; case DC_SW: xmin = 0; ymin = target->cell->y + 1; xmax = target->cell->x - 1; ymax = target->cell->map->h - 1; break; case D_W: case DC_W: xmin = 0; xmax = target->cell->x - 1; ymin = target->cell->y - 5; ymax = target->cell->y + 5; break; case DC_NW: xmin = 0; ymin = 0; xmax = target->cell->x - 1; ymax = target->cell->y - 1; break; default: // should never happen fizzle(caster); return B_TRUE; } if (xmin < 0) xmin = 0; if (ymin < 0) ymin = 0; if (xmax > target->cell->map->w-1) xmax = target->cell->map->w-1; if (ymax > target->cell->map->h-1) ymax = target->cell->map->h-1; for (y = ymin; y <= ymax ; y++) { for (x = xmin; x <= xmax ; x++) { c = getcellat(target->cell->map, x,y); if (c && cellwalkable(target, c, NULL) && !celldangerous(target, c, B_FALSE, NULL)) { poss[nposs] = c; nposs++; } } } if (nposs <= 0) { fizzle(caster); return B_TRUE; } c = poss[rnd(0,nposs-1)]; } // end if (semicontrolled | controlled | random ) targcell = c; if (c->type->solid) { // ok, but you'll die! } else if (!cellwalkable(caster, c, NULL)) { // go somewhere nearby... c = getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND); if (!c) { fizzle(caster); return B_TRUE; } } // we can take up to 'power-1' allies with us. if (caster == target) { int dir; for (dir = DC_N; (dir <= DC_NW) && (nallies < power); dir++) { c = getcellindir(target->cell, dir); if (c && c->lf && areallies(c->lf, caster)) { ally[nallies] = c->lf; nallies++; } } } if (isplayer(target) || haslos(player, target->cell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } teleportto(target, targcell, B_TRUE); // now do allies if (nallies) { int i; for (i = 0; i < nallies; i++) { c = getrandomadjcell(targcell, &ccwalkable, B_ALLOWEXPAND); if (c) { teleportto(ally[i], c, B_TRUE); } } } rv = B_FALSE; } else if (spellid == OT_S_TELEKINESIS) { cell_t *where = NULL; int failed = B_FALSE; float maxweight; // if no target object... if (!targob) { // ask for a target cell (to take objects from) snprintf(buf, BUFLEN, "Where will you focus your telekinetic power?"); where = askcoords(buf, "Telekinesis->", TT_OBJECT | TT_DOOR, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); if (where && haslos(caster, where)) { if (where->obpile->first) { // select object from cell... targob = askobject(where->obpile, "Target which object", NULL, NULL, '\0', NULL, B_FALSE); if (!targob) { failed = B_TRUE; } // TODO: check object weight! } else { failed = B_TRUE; } } else { failed = B_TRUE; } } if (!failed) { // is this a door? if (hasflag(targob->flags, F_DOOR)) { if (hasflag(targob->flags, F_OPEN)) { closedoorat(caster, where); } else { opendoorat(caster, where); } return B_FALSE; // don't do rest of telekenesis code } // if no target cell, ask where to throw object if (!targcell) { char obname[BUFLEN],buf2[BUFLEN]; char oidbuf[BUFLENSMALL]; getobname(targob, obname, 1); snprintf(buf, BUFLEN, "Where will you throw %s to?", obname); // TODO: start trail from the object snprintf(buf2, BUFLEN, "Telekinesis->%s->",obname); snprintf(oidbuf, BUFLENSMALL, "%ld", targob->id); addflag(caster->flags, F_THROWING, B_TRUE, NA, NA, oidbuf); targcell = askcoords(buf, buf2,TT_MONSTER | TT_PLAYER, caster, UNLIMITED, LOF_DONTNEED, B_FALSE); killflagsofid(caster->flags, F_THROWING); } // not liftable? if (hasflag(targob->flags, F_NOPICKUP)) { if (isplayer(caster)) { nothinghappens(); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } // too heavy? max weight is now based on our race's weight and intelligence maxweight = getlfweight(caster, B_NOOBS) + (getlfweight(caster, B_NOOBS) * (getstatmod(caster, A_IQ) / 100)); // modify by power maxweight += (10*power); if (getobmass(targob) > maxweight) { cell_t *obloc; char obname[BUFLEN]; obloc = getoblocation(targob); getobname(targob, obname, targob->amt); if (haslos(player, obloc)) { msg("%s lifts slightly, then drops again.",obname); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } if (targcell && haslos(caster, targcell)) { // if it is on us, then take theobject if (targcell == caster->cell) { pullobto(targob, caster); } else { // otherwise throw it there - but speed is based on // caster's INTELLIGENCE, not strength like normal. addflag(caster->flags, F_TKTHROW, A_IQ, SK_SS_MENTAL, NA, NULL); real_fireat(caster, targob, targob->amt, targcell, power, NULL, B_TRUE, spellid, NULL); killflagsofid(caster->flags, F_TKTHROW); // note that we use fireat() rather than throwat() to avoid // calling taketime() twice. } } else { failed = B_TRUE; } } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_THORNS) { flag_t *f; if (!target) target = caster; f = addtempflag(target->flags, F_RETALIATE, DT_PIERCE, NA, NA, "1d4^sharp thorns", FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_TRUESTRIKE) { flag_t *f; if (!targcell) targcell = caster->cell; target = targcell->lf; if (!target) { fizzle(caster); return B_TRUE; } f = addtempflag(target->flags, F_TRUESTRIKE, power, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; f = addtempflag(target->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_TURNUNDEAD) { int i,ndone = 0; lifeform_t *lf; if (caster && !isplayer(caster) && cansee(player, caster)) { char lfname[BUFLEN]; getlfname(caster, lfname); msg("%s glows brightly with the essense of life!", lfname); } if (!target) { target = caster; } // works on all undead in _target's_ sight for (i = 0; i < target->nlos; i++) { targcell = target->los[i]; lf = targcell->lf; if (lf && (lf != caster) && isundead(lf)) { int howlong = 10; int worked = B_FALSE; if ((gettr(caster)+power)*2 > gettr(lf)) { worked = B_TRUE; } else { int diff; diff = gettr(lf) - ((gettr(caster)+power)*2); // if monster level is too high, the chance of working // is 50%, -10% for every level too high. if (pctchance(50-diff)) { worked = B_TRUE; } } howlong = rnd(10,20)+(power*2); if (worked) { // don't use scare() since it will fail due to them being undead. addtempflag(lf->flags, F_FLEEFROM, target->id, NA, NA, NULL,howlong); ndone++; } else { if (isplayer(lf)) { msg("You resist the urge to flee from the lifeforce aura."); } else if (cansee(player, lf)) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s flinches.", lfname); } } } } if (!ndone) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_TWIDDLE) { target = targcell->lf; if (!target || (target == caster)) { fizzle(caster); return B_TRUE; } if (isplayer(caster)) { msg("You feel a wrenching sensation!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (isplayer(target)) { msg("You feel a wrenching sensation!"); more(); if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, caster)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } swapplaces(caster, target, B_CHANGEDIR, B_NOCHANGEDIR, B_ONPURPOSE); } else if (spellid == OT_S_VENTRILOQUISM) { if (isplayer(caster)) { noise(targcell, NULL, NC_OTHER, SV_TALK+power, "your voice shouting", "You hear your voice shouting."); } else { noise(targcell, NULL, NC_OTHER, SV_TALK+power, "shouting", "You hear shouting."); } } else if ((spellid == OT_S_SUMMONANIMALSSM) || (spellid == OT_S_SUMMONANIMALSMD) || (spellid == OT_S_SUMMONANIMALSLG) || (spellid == OT_S_SUMMONSWARM) || (spellid == OT_S_FRIENDS) || (spellid == OT_S_HECTASSERVANT) || (spellid == OT_S_SUMMONDEMON)) { int lifetime, nwant,ngot,successrate; int tempcount = 0; enum LFSIZE wantsize; enum RACECLASS wantrc; enum RACE wantrace = R_NONE; int friendly = B_MAYBE; char racename[BUFLEN]; lifeform_t *summoner = NULL; if (spellid == OT_S_HECTASSERVANT) { lifetime = 10; } else { lifetime = (power * 9) + rnd(1,power*2); } switch (spellid) { case OT_S_FRIENDS: switch (rnd(1,2)) { case 1: wantrace = R_CHICKEN; break; case 2: wantrace = R_BUTTERFLY; break; } wantrc = RC_ANY; wantsize = SZ_ANY; nwant = 8; successrate = 100; friendly = B_MAYBE; break; case OT_S_HECTASSERVANT: wantrace = R_HECTASSERVANT; wantrc = RC_ANY; wantsize = SZ_ANY; nwant = 1; successrate = 100; friendly = B_FALSE; break; case OT_S_SUMMONDEMON: wantrc = RC_DEMON; wantsize = SZ_ANY; nwant = 1; successrate = power*10; if (caster->race->raceclass->id == RC_DEMON) { friendly = B_TRUE; } else if (onein(3)) { friendly = B_FALSE; } else { friendly = B_TRUE; } break; case OT_S_SUMMONANIMALSSM: wantrc = RC_ANIMAL; wantsize = SZ_SMALL; nwant = rnd(2,3); successrate = 100; friendly = B_TRUE; break; case OT_S_SUMMONANIMALSMD: nwant = rnd(2,3); successrate = 100; friendly = B_TRUE; break; case OT_S_SUMMONANIMALSLG: wantrc = RC_ANIMAL; wantsize = SZ_LARGE; nwant = rnd(2,3); successrate = 100; friendly = B_TRUE; break; case OT_S_SUMMONSWARM: wantrc = RC_ANY; wantsize = SZ_ANY; nwant = 1; switch (power) { case 1: wantrace = R_SWARMRAT; break; case 2: wantrace = R_SWARMSPIDER; break; default: wantrace = R_SWARMLOCUST; break; } successrate = 100; friendly = B_TRUE; break; default: wantrc = RC_ANY; wantsize = SZ_ANY; nwant = 1; successrate = 100; friendly = B_TRUE; break; } // override with forced race? if (caster && getforcedspellrace(caster, spellid, racename, &tempcount)) { race_t *r; r = findracebyname(racename, NULL); if (r) { wantrace = r->id; wantrc = RC_ANY; wantsize = SZ_ANY; } } // override count if (tempcount > 0) { nwant = tempcount; } if (!pctchance(successrate)) { fizzle(caster); return B_TRUE; } // special case, mainly used for when gods summon creatures for you if (target) { summoner = target; } else { summoner = caster; } if (!isplayer(caster)) { friendly = B_TRUE; } ngot = summonlfs(caster, caster->cell, wantrace, wantrc, wantsize, AL_NONE, nwant, lifetime, friendly); if (!ngot) { fizzle(caster); return B_TRUE; } else { if (haslos(player, caster->cell)) { // intentionally not using cansee if (seenbyplayer) *seenbyplayer = B_TRUE; if (wantrace) { race_t *r; r = findrace(wantrace); if (ngot == 1) { msg("%s %s appears near %s!", needan(r->name) ? "An" : "A", r->name, castername); } else { char *plur; plur = strdup(r->name); makeplural(&plur); msg("%s appear around %s!", plur, castername); free(plur); } } else { raceclass_t *rc; rc = findraceclass(wantrc); if (ngot == 1) { msg("%s %s appears near %s!", needan(rc->name) ? "An" : "A", rc->name, castername); } else { char *plur; plur = strdup(rc->name); makeplural(&plur); msg("%s appear around %s!", plur, castername); free(plur); } } } } } else if (spellid == OT_S_SUMMONWEAPON) { object_t *o; targcell = caster->cell; target = caster; if (getequippedob(target->pack, BP_WEAPON)) { fizzle(caster); stopspell(caster, spellid); return B_TRUE; } o = addob(target->pack, "energy blade"); if (o) { if (canweild(target, o)) { flag_t *f; object_t *oldwep; // unweild current weapon oldwep = getweapon(target); if (oldwep) { if (iscursed(oldwep)) setblessed(oldwep, B_UNCURSED); unweild(target, oldwep); } // announce if (isplayer(target)) { msg("^%cA blade of pure energy forms in your hands!", getlfcol(target, CC_GOOD)); } else if (cansee(player, target)) { msg("^%cA blade of pure energy forms in %s%s hands!", getlfcol(target, CC_GOOD),castername, getpossessive(castername)); } weild(target, o); // set its damage value f = hasflag(o->flags, F_DAM); if (f) { f->val[1] = 3+power; } addflag(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL); } else { killob(o); fizzle(caster); stopspell(caster, spellid); return B_TRUE; } } else { fizzle(caster); stopspell(caster, spellid); return B_TRUE; } } else if (spellid == OT_S_WALLOFFIRE) { object_t *o; cell_t *c; int vdist = 0,hdist = 0; int vbad = B_FALSE, hbad = B_FALSE; int walldir; int seen = B_FALSE; char firename[BUFLEN]; if (power < 5) { snprintf(firename, BUFLEN, "small fire"); } else if (power < 8) { snprintf(firename, BUFLEN, "medium fire"); } else { snprintf(firename, BUFLEN, "large fire"); } // get vert distance for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) { vdist++; if (c->lf && (c->lf == caster)) { vbad = B_TRUE; break; } } for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) { vdist++; if (c->lf && (c->lf == caster)) { vbad = B_TRUE; break; } } // get horz dist for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) { hdist++; if (c->lf && (c->lf == caster)) { hbad = B_TRUE; break; } } for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) { hdist++; if (c->lf && (c->lf == caster)) { hbad = B_TRUE; break; } } if (vbad) vdist = 0; if (hbad) hdist = 0; if ((vdist == 0) && (hdist == 0)) { fizzle(caster); return B_TRUE; } // select direction if (vdist < hdist) { walldir = D_N; } else if (hdist < vdist) { walldir = D_E; } else { if (onein(2)) { walldir = D_N; } else { walldir = D_E; } } if (walldir == D_N) { // vert wall for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_N)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, firename); } for (c = getcellindir(targcell, D_S) ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_S)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, firename); } } else { // horz wall for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_E)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, firename); } for (c = getcellindir(targcell, D_W) ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_W)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, firename); } } if (seen) { drawscreen(); msg("A wall of fire appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(caster)) { angergodmaybe(R_GODNATURE, 25, GA_HERESY); } } else if (spellid == OT_S_WALLOFICE) { object_t *o; int donesomething = B_FALSE; cell_t *c; int vdist = 0,hdist = 0; int vbad = B_FALSE, hbad = B_FALSE; int walldir; int seen = B_FALSE; flag_t *f; // get vert distance for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_N)) { vdist++; if (c->lf && (c->lf == caster)) { vbad = B_TRUE; break; } } for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_S)) { vdist++; if (c->lf && (c->lf == caster)) { vbad = B_TRUE; break; } } // get horz dist for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_E)) { hdist++; if (c->lf && (c->lf == caster)) { hbad = B_TRUE; break; } } for (c = targcell ; c->lf || (cellwalkable(NULL, c, NULL)); c = getcellindir(c, D_W)) { hdist++; if (c->lf && (c->lf == caster)) { hbad = B_TRUE; break; } } if (vbad) vdist = 0; if (hbad) hdist = 0; if ((vdist == 0) && (hdist == 0)) { fizzle(caster); return B_TRUE; } // select direction if (vdist < hdist) { walldir = D_N; } else if (hdist < vdist) { walldir = D_E; } else { if (onein(2)) { walldir = D_N; } else { walldir = D_E; } } if (walldir == D_N) { // vert wall for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_N)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, "wall of ice"); if (o) { f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = power*10; f->val[1] = power*10; } addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); } // knock lfs away if (c->lf) { knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS, B_FALSE), 1, NULL, 100+(power*10), B_DOANNOUNCE, B_DODAM); donesomething = B_TRUE; } } for (c = getcellindir(targcell, D_S) ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_S)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, "wall of ice"); if (o) { f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = power*10; f->val[1] = power*10; } addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); } // knock lfs away if (c->lf) { knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS, B_FALSE), 1, NULL, 100+(power*10), B_DOANNOUNCE, B_DODAM); donesomething = B_TRUE; } } } else { // horz wall for (c = targcell ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_E)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, "wall of ice"); if (o) { f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = power*10; f->val[1] = power*10; } addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); } // knock lfs away if (c->lf) { knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS, B_FALSE), 1, NULL, 100+(power*10), B_DOANNOUNCE, B_DODAM); donesomething = B_TRUE; } } for (c = getcellindir(targcell, D_W) ; (c == targcell) || c->lf || cellwalkable(NULL, c, NULL); c = getcellindir(c, D_W)) { if (!seen && haslos(player, c)) seen = B_TRUE; o = addob(c->obpile, "wall of ice"); if (o) { f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] = power*10; f->val[1] = power*10; } addflag(o->flags, F_OBHPDRAIN, 1, DT_MELT, NA, NULL); } // knock lfs away if (c->lf) { knockback(c->lf, getdiraway(c, c, NULL, B_FALSE, DT_COMPASS, B_FALSE), 1, NULL, 100+(power*10), B_DOANNOUNCE, B_DODAM); donesomething = B_TRUE; } } } if (seen) { drawscreen(); msg("A wall of ice appears!"); if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(caster)) { angergodmaybe(R_GODFIRE, 50, GA_HERESY); } } else if (spellid == OT_S_WATERJET) { char lfname[BUFLEN]; cell_t *retcell[MAXRETCELLS]; int nretcell,i; int dam; // animation //anim(caster->cell, targcell, '}', C_BLUE); if (isplayer(caster) || cansee(player, caster)) { msg("%s fire%s a jet of high-pressure water.",castername,isplayer(caster) ? "" : "s"); if (seenbyplayer) *seenbyplayer = B_TRUE; } dam = roll("2d6"); calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, targcell->x, targcell->y, retcell, &nretcell); for (i = 1; i < nretcell; i++) { target = haslf(retcell[i]); if (target) { int dir,amt, n; object_t *arm[MAXBODYPARTS]; int narm = 0; getlfname(target, lfname); // hit if (cansee(player, target)) { msg("%s %s hit by a jet of water!",lfname, is(target)); } // does less damage the further you are away. if (dam > 0) dam--; // water damage will generally be turn to zero unless people are specifically // vulnerable to water, so do bashing damage too. losehp(target, dam, DT_WATER, caster, "a high-pressure jet of water"); losehp(target, dam, DT_BASH, caster, "a high-pressure jet of water"); // knock backwards dir = getdirtowards(caster->cell, target->cell, target, B_FALSE, DT_COMPASS); amt = (power/3); if (amt < 2) amt = 2; knockback(target, dir, amt, caster, 0, B_DOANNOUNCE, B_DODAM); // rust getallouterarmour(target, arm, &narm); for (n = 0; n < narm; n++) { takedamage(arm[n], R_TRUSTY, DT_WATER, caster); } // add water object addob(retcell[i]->obpile, "large puddle of water"); break; } else { damageallobs(NULL, retcell[i]->obpile, 0, DT_WATER, caster); // add water object addob(retcell[i]->obpile, "large puddle of water"); } } } else if (spellid == OT_S_WARPWOOD) { object_t *o,*nexto; int ndone = 0; if (!targcell) { fizzle(caster); return B_TRUE; } if (targcell->lf) { // weilded weapons/armour for (o = targcell->lf->pack->first ; o ; o = nexto) { nexto = o->next; if ((o->type->material->id == MT_WOOD) || (o->type->material->id == MT_DRAGONWOOD)) { if (isequipped(o)) { int dam; dam = rolldie(power, 4); if (haslos(player, targcell)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s twist%s and writhe%s!",obname, (o->amt == 1) ? "s" : "", (o->amt == 1) ? "s" : ""); } takedamage(o, dam, DT_DECAY, caster); if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } ndone++; } } } } for (o = targcell->obpile->first ; o ; o = nexto) { nexto = o->next; if ((o->type->material->id == MT_WOOD) || (o->type->material->id == MT_DRAGONWOOD)) { int dam; dam = rolldie(power, 4); if (haslos(player, targcell)) { char obname[BUFLEN]; getobname(o, obname, o->amt); msg("%s twist%s and writhe%s!",obname, (o->amt == 1) ? "s" : "", (o->amt == 1) ? "s" : ""); } takedamage(o, dam, DT_DIRECT, caster); if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } ndone++; } } if (targcell->lf) { if ((targcell->lf->material->id == MT_WOOD) || (targcell->lf->material->id == MT_DRAGONWOOD)) { int dam; char dambuf[BUFLEN]; dam = rolldie(power, 4); if (haslos(player, targcell)) { char lfname[BUFLEN]; getlfname(targcell->lf, lfname); msg("%s twist%s and writhe%s!",lfname, isplayer(targcell->lf) ? "" : "s", isplayer(targcell->lf) ? "" : "s"); } snprintf(dambuf, BUFLEN, "%s%s warp wood spell", castername, getpossessive(castername)); losehp(targcell->lf, dam, DT_DIRECT, caster, dambuf); if (haslos(player, targcell)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } ndone++; } } if (!ndone) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_WEAKEN) { target = targcell->lf; if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) { if (cansee(player, target)) { getlfname(target, buf); msg("%s %s momentarily weaker.", buf, isplayer(target) ? "feel" : "looks"); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } if (target) { int howlong = 15; flag_t *f; // already weakaned? foreach_bucket(b) { for (f = target->flags->first[b]; f ; f = f->next) { if ((f->id == F_ATTRMOD) && (f->val[0] == A_STR) && (f->obfrom == OT_S_WEAKEN)) { if (cansee(player, target)) { getlfname(target, buf); msg("%s %s momentarily weaker.", buf, isplayer(target) ? "feel" : "looks"); if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_FALSE; } } } howlong = getspellduration(5,10,blessed) + power; f = addtempflag(target->flags, F_ATTRMOD, A_STR, power*-15, NA, NULL, howlong); f->obfrom = OT_S_WEAKEN; f = addtempflag(target->flags, F_MELEEDAMPCT, 50, NA, NA, NULL, howlong); f->obfrom = OT_S_WEAKEN; if (seenbyplayer) *seenbyplayer = B_TRUE; } else { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_WEB) { char numbuf[BUFLEN]; int i; numtotext(power, numbuf); // animation anim(caster->cell, targcell, '^', C_GREY); if (isplayer(caster) || cansee(player, caster)) { if (power == 1) { msg("%s fire%s a burst of webbing.",castername,isplayer(caster) ? "" : "s"); } else { msg("%s fire%s %s bursts of webbing.",castername,isplayer(caster) ? "" : "s", numbuf); } if (seenbyplayer) *seenbyplayer = B_TRUE; } // create webs for (i = 0; i < power; i++) { cell_t *c; if (i == 0) { c = targcell; } else { condset_t cs; initcondv(&cs, CC_SOLID, B_FALSE, NA, CC_NONE); c = getrandomadjcell(targcell, &cs, B_ALLOWEXPAND); } if (c) { if (c->lf && (c->lf->race->baseid != R_SPIDER)) { if (skillcheck(c->lf, SC_DODGE, 90+(power*10), 0)) { if (isplayer(c->lf)) { msg("You dodge a web."); } else if (cansee(player, c->lf)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("%s dodges a web!", lfname); } } else { addob(c->obpile, "web"); if (isplayer(c->lf)) { msg("^%cYou suddenly find yourself in a web!", getlfcol(c->lf, CC_BAD)); } else if (cansee(player, c->lf)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("^%c%s is stuck in a web!", getlfcol(c->lf, CC_BAD), lfname); } } } } } } else if (spellid == OT_S_WHATGOESUP) { if (isplayer(caster)) { msg("A positive gravity field forms around you!"); } else if (cansee(player, caster)) { msg("A positive gravity field forms around %s!", castername); } if (seenbyplayer) *seenbyplayer = B_TRUE; } else if ((spellid == OT_S_WHIRLWIND) || (spellid == OT_S_TORNADO) || (spellid == OT_S_HURRICANE)) { int failed = B_FALSE; char obname[BUFLEN],obdesc[BUFLEN]; switch (spellid) { case OT_S_HURRICANE: strcpy(obname, "hurricane"); strcpy(obdesc, "raging hurricane"); break; case OT_S_TORNADO: strcpy(obname, "tornado"); strcpy(obdesc, "towering tornado"); break; default: case OT_S_WHIRLWIND: strcpy(obname, "whirlwind"); strcpy(obdesc, "spinning whirlwind"); break; } if (targcell) { cell_t *c[5]; int ncells = 0,i,ndone = 0,doneannounce = B_FALSE; if (spellid == OT_S_HURRICANE) { c[0] = targcell; c[1] = getcellindir(targcell, DC_N); c[2] = getcellindir(targcell, DC_E); c[3] = getcellindir(targcell, DC_S); c[4] = getcellindir(targcell, DC_W); ncells = 5; } else { c[0] = targcell; ncells = 1; } for (i = 0; i < ncells; i++) { if (c[i]) { int ok = B_TRUE; if (c[i]->type->solid) { if (spellid == OT_S_HURRICANE) { // this should always kill it! damagecell(c[i], c[i]->hp, DT_DIRECT, NULL); } else { ok = B_FALSE; } } if (ok) { flag_t *f; object_t *o; // create flame there if (haslos(player, c[i])) { if (!doneannounce) { msg("A %s appears!", obdesc); doneannounce = B_TRUE; } if (seenbyplayer) *seenbyplayer = B_TRUE; } o = addob(c[i]->obpile, obname); // magically boost hp based on power f = hasflag(o->flags, F_OBHP); if (f) { f->val[0] += (power*6); // centre hurricane lasts a bit longer if (i == 0) { f->val[0] += 5; } f->val[1] = f->val[0]; } setobcreatedby(o, caster); // for non-centre hurricanes, remove randommove flag // (they will move with the original) if (i != 0) { killflagsofid(o->flags, F_OBMOVESRANDOMLY); } ndone++; } } } if (!ndone) { failed = B_TRUE; } } else { failed = B_TRUE; } if (failed) { fizzle(caster); return B_TRUE; } } else if (spellid == OT_S_WINDSHIELD) { char retaltext[BUFLEN]; flag_t *f; // always targetted at caster targcell = caster->cell; target = caster; f = addtempflag(caster->flags, F_WINDSHIELD, power, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; snprintf(retaltext, BUFLEN, "1d%d^whirling debris", power); f = addtempflag(target->flags, F_RETALIATE, DT_BASH, NA, NA, retaltext, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_WISHLIMITED) { object_t *o = NULL; char obname[BUFLEN]; char ch; int appearonground = B_FALSE; if (!target) target = caster; initprompt(&prompt, "For what do you wish?"); addchoice(&prompt, 'a', "Wealth (riches beyond your imagination)", NULL, NULL, NULL); addchoice(&prompt, 'b', "Power (an item with which to smite your foes)", NULL, NULL, NULL); addchoice(&prompt, 'c', "Protection (an item to defend against harm)", NULL, NULL, NULL); addchoice(&prompt, 'd', "Fame (followers to obey your every whim)", NULL, NULL, NULL); addchoice(&prompt, 'e', "Knowledge (identify everything and more)", NULL, NULL, NULL); addchoice(&prompt, 'f', "Magic (books full of arcane writings)", NULL, NULL, NULL); addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); if (isplayer(target)) { ch = getchoice(&prompt); } else { ch = 'e'; // ie. don't select 'knowledge' while (ch == 'e') { ch = rnd('a', 'f'); } } // warn if this was bad... if (isplayer(target) && (blessed == B_CURSED)) { msg("^%cUh oh, you have a bad feeling about this...", getlfcol(target, CC_VBAD)); more(); } if (ch == 'a') { // wealth (gold, bad: massive chunk of gold which you can't carry) if (blessed == B_CURSED) { snprintf(buf, BUFLEN, "gigantic golden boulder"); appearonground = B_TRUE; } else { snprintf(buf, BUFLEN, "1000-2000 gold dollars"); } } else if (ch == 'b') { // power (weapons, bad: battery) if (blessed == B_CURSED) { snprintf(buf, BUFLEN, "battery"); } else { skill_t *sk,*poss[MAXSKILLS]; int nposs = 0; // get a list of all valid weapon skills for (sk = firstskill ; sk ; sk = sk->next) { if (isweaponskill(sk->id) && getskill(target, sk->id)) { poss[nposs++] = sk; } } if (nposs) { int nweps = 0; objecttype_t *ot; // pick a random one sk = poss[rnd(0,nposs-1)]; // find all associated weapons for (ot = objecttype ; ot ; ot = ot->next) { if (hasflagval(ot->flags, F_USESSKILL, sk->id, NA, NA, NULL)) { if (gettechlevel(ot->id) > getskill(player, SK_TECHUSAGE)) { } else { nweps++; } } } if (nweps) { int sel,n = 0; sel = rnd(0,nweps-1); for (ot = objecttype ; ot ; ot = ot->next) { if (hasflagval(ot->flags, F_USESSKILL, sk->id, NA, NA, NULL)) { if (gettechlevel(ot->id) > getskill(player, SK_TECHUSAGE)) { } else { if (n == sel) break; n++; } } } snprintf(buf, BUFLEN, "excellent branded %s", ot->name); } else { snprintf(buf, BUFLEN, "stick"); } } else { snprintf(buf, BUFLEN, "stick"); } } } else if (ch == 'c') { // protection. bad: turn to stone enum BODYPART bp,poss[MAXBODYPARTS]; int nposs = 0; if (blessed == B_CURSED) { stone(caster); } else { // get a list of all valid body parts if (hasbp(target, BP_HEAD)) poss[nposs++] = BP_HEAD; if (hasbp(target, BP_SHOULDERS)) poss[nposs++] = BP_SHOULDERS; if (hasbp(target, BP_BODY)) poss[nposs++] = BP_BODY; if (hasbp(target, BP_LEGS)) poss[nposs++] = BP_LEGS; if (hasbp(target, BP_FEET)) poss[nposs++] = BP_FEET; if (hasbp(target, BP_HANDS)) poss[nposs++] = BP_HANDS; if (nposs) { int narms = 0; objecttype_t *ot; // pick a random body part bp = poss[rnd(0,nposs-1)]; // find all associated armour for (ot = objecttype ; ot ; ot = ot->next) { if ((ot->obclass->id == OC_ARMOUR) && hasflagval(ot->flags, F_GOESON, bp, NA, NA, NULL)) { // make sure this armour won't give us a penalty if (!wouldgivepenalty(target, ot->flags)) { narms++; } } } if (narms) { int sel,n = 0; sel = rnd(0,narms-1); for (ot = objecttype ; ot ; ot = ot->next) { if ((ot->obclass->id == OC_ARMOUR) && hasflagval(ot->flags, F_GOESON, bp, NA, NA, NULL)) { if (!wouldgivepenalty(target, ot->flags)) { if (n == sel) break; n++; } } } snprintf(buf, BUFLEN, "excellent branded %s", ot->name); } else { snprintf(buf, BUFLEN, "sun hat"); } } else { snprintf(buf, BUFLEN, "sun hat"); } } } else if (ch == 'd') { // fame (allies bad: hostile lfs ) lifeform_t *lf; cell_t *c; job_t *j; char *p,lfname[BUFLEN]; // summon a human c = getrandomadjcell(caster->cell, &ccwalkable, B_ALLOWEXPAND); j = getrandomjob(B_TRUE); lf = addlf(c, R_HUMAN, target->level); givejob(lf, j->id); getlfnamea(lf, lfname); if (cansee(player, lf)) { msg("%s appears!", lfname); } if (blessed == B_CURSED) { aiattack(lf, target, aigetchasetime(lf)); } else { petify(lf, target); } if (isplayer(target)) { p = assignnpcname(lf->flags); sayphrase(lf, SP_RECRUIT_ACCEPT, SV_TALK, NA, p, lf); } strcpy(buf, ""); } else if (ch == 'e') { // knowledge (makeknown everything you have plus 10% chance for everything else. bad: insanity) // only possible for the player! strcpy(buf, ""); if (isplayer(target)) { if (blessed == B_CURSED) { int newval; msg("^%cYour sanity is blasted by a flood of alien knowledge!", getlfcol(caster, CC_VBAD)); newval = caster->att[A_IQ] - 25; caster->baseatt[A_IQ] = newval; setattr(caster, A_IQ, newval); } else { knowledge_t *k; msg("^GKnowledge floods into your mind!"); for (o = target->pack->first ; o ; o = o->next) { if (!isidentified(o)) { identify(o); getobname(o, obname, o->amt); msgnocap("%c - %s.", o->letter, obname); } } // now identify everything that you have tried, // and 10% chance of identifying others for (k = knowledge; k ; k = k->next) { if ((k->known == B_TRIED) || pctchance(10)) { makeknown(k->id); } } msg("(check '%c' to see your knowledge)", cmdtochar(CMD_INFOKNOWLEDGE)); } } } else if (ch == 'f') { // magic (spellbooks), bad: gain 'explode self' magic if (blessed == B_CURSED) { flag_t *f; f = lfhasflagval(target, F_CANWILL, OT_A_EXPLODESELF, NA, NA, NULL); if (f) { if (isplayer(target)) nothinghappens(); } else { addflag(target->flags, F_CANWILL, OT_A_EXPLODESELF, 50, 50, NULL); } } else { objecttype_t *ot,*poss[MAXCANDIDATES]; int nposs = 0; // find all castable spells (spellpower > 0) for (ot = objecttype ; ot ; ot = ot->next) { if ((ot->obclass->id == OC_SPELL) && getspellpower(target, ot->id)) { poss[nposs++] = ot; } } if (!nposs) { // give knowledge of a random spell school plus sorcery enum SPELLSCHOOL school; skill_t *poss2[MAXSKILLS],*sk; int nposs2 = 0; for (school = SS_AIR; school < SS_LAST; school++) { sk = findskill(getschoolskill(school)); poss2[nposs2++] = sk; } sk = poss2[rnd(0,nposs2-1)]; giveskill(target, sk->id); // ... then try to find castable spells again for (ot = objecttype ; ot ; ot = ot->next) { if ((ot->obclass->id == OC_SPELL) && getspellpower(target, ot->id)) { poss[nposs++] = ot; } } } if (nposs) { // pick a random spell from this list ot = poss[rnd(0,nposs-1)]; snprintf(buf, BUFLEN, "spellbook of %s",ot->name); } else { strcpy(buf, ""); } } } else if (ch == '-') { // ie. nothing if (isplayer(target)) { msg("You resist the temptation of instant gratification."); if (frompot || fromob) { // small wisdom increase modattr(target, A_WIS, 2); } } return B_FALSE; } if (strlen(buf)) { if (appearonground) { cell_t *where; where = real_getrandomadjcell(target->cell, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, target); if (!where) { where = target->cell; } o = addob(where->obpile, buf); if (o) { noise(caster->cell, NULL, NC_OTHER, SV_WHISPER, "something hitting the ground.", NULL); if (haslos(player, where)) { getobname(o, obname, o->amt); msg("%s appear%s on the ground!", obname, (o->amt == 1) ? "s" : ""); } } } else { // give the object o = addob(target->pack, buf); getobname(o, obname, o->amt); msgnocap("%c - %s.", o->letter, obname); } } // now age the caster if (!isgod(caster)) { age(caster, 25); } if (isplayer(target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; } if (o && isplayer(caster)) { angergodmaybe(R_GODFIRE, 75, GA_HERESY); } } else if ((spellid == OT_S_WISH) || (spellid == OT_S_GIFT)) { object_t *o; if (isplayer(caster)) { char lfname[BUFLEN]; char question[BUFLEN]; int i,nbad = 0; obpile_t *tempop = NULL; object_t *badob = NULL; if (seenbyplayer) *seenbyplayer = B_TRUE; // ask for target if (spellid == OT_S_GIFT) { if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; target = targcell->lf; } else { target = caster; } getlfname(target, lfname); // ask for an object if (spellid == OT_S_GIFT) { snprintf(question, BUFLEN, "What gift will %s receive",lfname); } else { snprintf(question, BUFLEN, "For what do you wish"); } askstring(question, '?', buf, BUFLEN, NULL); tempop = addobpile(NULL, NULL, NULL); addob(tempop, buf); if (!hasjob(player, J_GOD)) { // remove any invalid objects badob = hasobwithflag(tempop, F_NOWISH); while (badob) { flag_t *f; lifeform_t *rndgod; f = hasflag(badob->flags, F_LINKGOD); if (f) { rndgod = findgod(f->val[0]); } else { rndgod = getrandomgod(); } assert(rndgod); godsay(rndgod->race->id, B_TRUE, getflagtext(rndgod->flags, F_GODNOWISHTEXT)); more(); killob(badob); nbad++; badob = hasobwithflag(tempop, F_NOWISH); } } nretobs -= nbad; //addob(target->cell->obpile, buf); if (nretobs > 0) { int ncreated = 0; for (i = 0; i < nretobs; i++) { char obname[BUFLEN]; o = retobs[i]; // make sure armour fits if (hasflag(o->flags, F_MULTISIZE)) { resizeobject(o, getlfsize(target)); } if (!hasflag(o->flags, F_IMPASSABLE) && canpickup(target, o, ALL)) { // you gain it. relinkob(o, target->pack); // move to pack getobname(o, obname, o->amt); if (isplayer(target)) { msgnocap("%c - %s.", o->letter, obname); } else { msg("%s is gifted with %s.", lfname, obname); } ncreated++; } else { // can't pick this up... // impassable? goes in a nearby cell instead of at your feet. if (hasflag(o->flags, F_IMPASSABLE)) { cell_t *newloc; // if so, don't put it where the player is! newloc = real_getrandomadjcell(target->cell, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, target); o = relinkob(o, newloc->obpile); } else { // move to target cell under the lf o = relinkob(o, target->cell->obpile); } if (o) { getobname(o, obname, o->amt); noise(caster->cell, NULL, NC_OTHER, SV_WHISPER, "something hitting the ground.", NULL); if (!isblind(caster)) { msg("%s appear%s on the ground!", obname, OBS1(o)); } ncreated++; // o = obaffectsothers(o); } else { // ob exists but couldn't make it appear msg("The air in front of %s seems to ripple for a moment.", lfname); break; // don't process any more. } } } if (ncreated) { if (isplayer(caster)) { angergodmaybe(R_GODFIRE, 75, GA_HERESY); } } } else { // couldn't make it appear - ob doesn't exist msg("The air in front of %s seems to ripple for a while.", lfname); } if (tempop) killobpile(tempop); // now age the caster if (!isgod(caster)) { age(caster, 50); } } else { // monsters can't wish } } return rv; } objecttype_t *findspelln(char *buf) { objecttype_t *ot; for (ot = objecttype ; ot ; ot = ot->next) { if (ot->obclass->id == OC_SPELL) { if (!strcasecmp(ot->name, buf)) { return ot; } } } return NULL; } enum SPELLSCHOOL findspellschoolbyname(char *buf) { enum SPELLSCHOOL ss; for (ss = SS_NONE; ss <= SS_LAST; ss++) { if (!strcasecmp(buf, getschoolname(ss))) return ss; } return SS_NONE; } void fizzle(lifeform_t *caster) { if (!caster) return; if (isplayer(caster)) { if (lfhasflag(caster, F_CASTINGSPELL)) { msg("Your spell fizzles."); } else { nothinghappens(); } } else if (cansee(player, caster)) { char buf[BUFLEN]; getlfname(caster, buf); capitalise(buf); msg("%s's spell fizzles.", buf); } } enum OBTYPE getfirstwizspell(enum SPELLSCHOOL school) { enum OBTYPE firstspell = OT_S_MANASPIKE; switch (school) { case SS_AIR: firstspell = OT_S_PROPELMISSILE; break; case SS_COLD: firstspell = OT_S_CHILL; break; case SS_FIRE: firstspell = OT_S_SPARK; break; case SS_DEATH: firstspell = OT_S_ANIMATEDEAD; break; case SS_DIVINATION: firstspell = OT_S_SIXTHSENSE; break; case SS_TRANSLOCATION: firstspell = OT_S_APPORTATION; break; case SS_SUMMONING: firstspell = OT_S_GLYPHWARDING; break; case SS_LIFE: firstspell = OT_S_HEALINGMIN; break; default: case SS_WILD: firstspell = OT_S_MANASPIKE; break; } return firstspell; } char *getforcedspellrace(lifeform_t *lf, enum OBTYPE spellid, char *racestr, int *count) { flag_t *f; // forced? f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL); if (!f) { f = lfhasflagval(lf, F_CANCAST, spellid, NA, NA, NULL); } if (f) { texttospellopts(f->text, "race:", racestr, "count:", count, NULL); if (strlen(racestr)) { return racestr; } } return NULL; } int getmpcost(lifeform_t *lf, enum OBTYPE oid) { flag_t *f; objecttype_t *ot; int cost = 0; ot = findot(oid); if (!ot) { return 0; } if (lf) { if (lfhasflagval(lf, F_BOOSTSPELL, oid, NA, NA, NULL)) { return 0; // ie. deactivating it } if (lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL)) { return 0; // ie. deactivating it } } f = hasflag(ot->flags, F_MPCOST); if (f) { cost = f->val[0]; } else { cost = pow(2,getspelllevel(oid)-1); /* f = hasflag(ot->flags, F_SPELLLEVEL); if (f) { switch (getspellschool(oid)) { case SS_MENTAL: case SS_ALLOMANCY: cost = f->val[0]; break; default: cost = f->val[0] * f->val[0]; break; } } */ } return cost; } int getmrdiff(enum OBTYPE spellid, int power) { int lev,diff; lev = getspelllevel(spellid); diff = 90 + (lev*10) + (power*5); return diff; } // maxlev <= 0 means 'any lev' enum OBTYPE getrandomspell(int maxlev) { int wantlev; objecttype_t *ot,*poss[MAXCANDIDATES]; int nposs = 0; wantlev = 1; while (rnd(1,2) == 1) { wantlev++; } limit(&wantlev, 1, maxlev); // get list of all spells of this level for (ot = objecttype ; ot ; ot = ot->next) { if ((ot->obclass->id == OC_SPELL) && (getspelllevel(ot->id) == wantlev) ) { poss[nposs++] = ot; } } if (nposs <= 0) { return OT_NONE; } ot = poss[rnd(0,nposs-1)]; return ot->id; } // wantlev = 0 means 'any level' enum OBTYPE getrandomspellfromschool(enum SPELLSCHOOL school, int wantlev) { objecttype_t *ot,*poss[MAXCANDIDATES]; int nposs = 0; // get list of all spells of this school for (ot = objecttype ; ot ; ot = ot->next) { if ((ot->obclass->id == OC_SPELL) && spellisfromschool(ot->id, school)) { if (!hasflag(ot->flags, F_NORANDOM)) { if ((wantlev == 0) || (getspelllevel(ot->id) == wantlev)) { poss[nposs++] = ot; } } } } if (nposs <= 0) { return OT_NONE; } ot = poss[rnd(0,nposs-1)]; return ot->id; } // if wantknowntolf isn't passed, then wantknown is ignored. enum SPELLSCHOOL getrandomspellschool(lifeform_t *wantknowntolf, int wantknown) { enum SPELLSCHOOL poss[MAXCANDIDATES],ss; int nposs = 0; // count valid schools for (ss = SS_NONE; ss <= SS_LAST; ss++) { if (schoolappearsinbooks(ss) && !streq(getschoolname(ss), "badschool")) { int valid = B_FALSE; if (wantknowntolf) { enum SKILLLEVEL slev; slev = getskill(wantknowntolf, getschoolskill(ss)); if ((slev && wantknown) || (!slev && !wantknown)) { valid = B_TRUE; } } else { valid = B_TRUE; } if (valid) { poss[nposs++] = ss; } } } if (!nposs) { return SS_NONE; } return poss[rnd(0,nposs-1)]; } enum SKILL getschoolskill(enum SPELLSCHOOL ss) { switch (ss) { case SS_ALLOMANCY: return SK_SS_ALLOMANCY; case SS_AIR: return SK_SS_AIR; case SS_DEATH: return SK_SS_DEATH; case SS_DIVINATION: return SK_SS_DIVINATION; case SS_NATURE: return SK_SS_NATURE; case SS_FIRE: return SK_SS_FIRE; case SS_COLD: return SK_SS_COLD; case SS_LIFE: return SK_SS_LIFE; case SS_MENTAL: return SK_SS_MENTAL; case SS_SUMMONING: return SK_SS_SUMMONING; case SS_TRANSLOCATION: return SK_SS_TRANSLOCATION; case SS_WILD: return SK_SS_WILD; default: break; } return SK_NONE; } // returns the spellschool associated with the given skill enum SPELLSCHOOL getskillschool(enum SKILL skid) { switch (skid) { case SK_SS_ALLOMANCY: return SS_ALLOMANCY; case SK_SS_AIR: return SS_AIR; case SK_SS_DEATH: return SS_DEATH; case SK_SS_DIVINATION: return SS_DIVINATION; case SK_SS_NATURE: return SS_NATURE; case SK_SS_FIRE: return SS_FIRE; case SK_SS_COLD: return SS_COLD; case SK_SS_LIFE: return SS_LIFE; case SK_SS_MENTAL: return SS_MENTAL; case SK_SS_SUMMONING: return SS_SUMMONING; case SK_SS_TRANSLOCATION: return SS_TRANSLOCATION; case SK_SS_WILD: return SS_WILD; default: break; } return SS_NONE; } // returns "x MP" or "x-y MP", or "x-y MP, ongoing" ect char *getspellcosttext(lifeform_t *lf, enum OBTYPE spellid, int power, char *buf) { objecttype_t *ot; int ongoing = B_FALSE; int cost; ot = findot(spellid); if (!ot) { strcpy(buf, "?invalidspellid?"); return buf; } cost = getmpcost(lf, spellid); if (hasflag(ot->flags, F_ONGOING)) ongoing = B_TRUE; if (hasflag(ot->flags, F_VARPOWER)) { snprintf(buf, BUFLEN, "%d-%d MP%s", cost, cost * power, ongoing ? ", ongoing" : ""); } else { snprintf(buf, BUFLEN, "%d MP%s", cost, ongoing ? ", ongoing" : ""); } return buf; } // returns a number between min & max // if blessed, always the max // if cursed, always the min int getspellduration(int min,int max,int blessed) { int howlong; switch (blessed) { case B_BLESSED: howlong = max; break; case B_CURSED: howlong = min; break; default: case B_UNCURSED: howlong = rnd(min,max); break; } return howlong; } // abilities count as level 0 int getspelllevel(enum OBTYPE spellid) { flag_t *f; objecttype_t *ot; int level = 0; ot = findot(spellid); if (!ot) { return 0; } f = hasflag(ot->flags, F_SPELLLEVEL); if (f) { level = f->val[0]; } return level; } enum LOFTYPE getspellloftype(enum OBTYPE spellid) { objecttype_t *ot; flag_t *f; ot = findot(spellid); if (!ot) { return LOF_DONTNEED; } f = hasflag(ot->flags, F_LOSLOF); if (f) { return f->val[1]; } return LOF_DONTNEED; } int getspellmaxpower(enum OBTYPE spellid) { flag_t *f; objecttype_t *ot; int max = 10; ot = findot(spellid); if (!ot) { return 10; } f = hasflag(ot->flags, F_MAXPOWER); if (f) { max = f->val[0]; } return max; } char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf, int forcepower) { int power; enum SPELLSCHOOL ss; objecttype_t *ot; ot = findot(spellid); strcpy(buf, ot->name); capitalise(buf); if (forcepower) { power = forcepower; } else { power = getspellpower(lf, spellid); } ss = getspellschool(spellid); if ((power > 1) && (ss != SS_ABILITY)) { strcat(buf, " "); strcat(buf, roman(power)); } if (spellid == OT_S_DETECTLIFE) { if (power >= 6) { strcat(buf, "(ctrl)"); } } else if (spellid == OT_S_DETECTLIFE) { if (power >= 5) { strcat(buf, "(enhanced)"); } } else if (spellid == OT_S_CREATEMONSTER) { if (power >= 7) { strcat(buf, "(fullctrl)"); } else if (power >= 5) { strcat(buf, "(semictrl)"); } } else if (spellid == OT_S_KNOCK) { if (power >= 7) { strcat(buf, "(enhanced)"); } } else if (spellid == OT_S_LIGHT) { if (power >= 5) { strcat(buf, "(ctrl,entire-lev)"); } else if (power >= 3) { strcat(buf, "(ctrl)"); } } else if (spellid == OT_S_POLYMORPH) { if (power >= 5) { strcat(buf, "(ctrl)"); } } else if (spellid == OT_S_SOFTENEARTH) { if (power >= 2) { strcat(buf, "(boost)"); } } else if (spellid == OT_S_TELEPORT) { if (power >= 8) { strcat(buf, "(fullctrl)"); } else if (power >= 5) { strcat(buf, "(semictrl)"); } } return buf; } // at what power level COULD lf cast 'spellid' ? // note: doesn't matter if the lf doesn't actually have f_cancast / f_canwill, // in this case the function will return the POTENTIAL power. int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { int power = 0,willpower = 0, castpower = 0; int spelllev; enum SKILLLEVEL schoolskill; enum SPELLSCHOOL school; int db = B_FALSE; int boost; flag_t *willflag,*castflag; char whatfrom[BUFLENSMALL]; if (db) { objecttype_t *ot; ot = findot(spellid); if (db) dblog("getspellpower for lf %s, spell %s", lf->race->name, ot->name); } sumflags(lf->flags, F_MAGICBOOST, &boost, NULL, NULL); //////////////////////////////////// // CAN WE CAST THIS AT ALL //////////////////////////////////// // If we can will/cast this then we might have a set // spellpower. if we both will _and_ cast this, // then use whatever power is higher. strcpy(whatfrom, ""); willflag = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL); if (willflag && strlen(willflag->text)) { texttospellopts(willflag->text, "pw:", &willpower, NULL ); } castflag = lfhasflagval(lf, F_CANCAST, spellid, NA, NA, NULL); if (castflag && strlen(castflag->text)) { texttospellopts(castflag->text, "pw:", &castpower, NULL ); } if ((willpower > 0) && (castpower > 0)) { if (castpower > willpower) { power = castpower; strcpy(whatfrom, "cancast"); } else { power = willpower; strcpy(whatfrom, "canwill"); } } else if (willpower > 0) { power = willpower; strcpy(whatfrom, "canwill"); } else if (castpower > 0) { power = castpower; strcpy(whatfrom, "cancast"); } if (strlen(whatfrom)) { if (db) { dblog("-->power = %d (from %s)", power, whatfrom); } // note that this power _can_ override max spell power power += boost; return power; } // get spell details school = getspellschoolknown(lf, spellid); schoolskill = getskill(lf, getschoolskill(school)); spelllev = getspelllevel(spellid); // for most spell schools, your skill in the school determines which // spells you can cast. // // this check doesn't apply for monsters. if (isplayer(lf)) { int maxspelllevel = MAXSPELLLEV; // if (hasjob(lf, J_DRUID) && (school == SS_NATURE)) { //if ((school == SS_ALLOMANCY) || (school == SS_MENTAL)) { //} else { switch (schoolskill) { case PR_INEPT: maxspelllevel = 0; break; case PR_NOVICE: maxspelllevel = 1; break; case PR_BEGINNER: maxspelllevel = 2; break; case PR_ADEPT: maxspelllevel = 3; break; case PR_SKILLED: maxspelllevel = 4; break; case PR_EXPERT: maxspelllevel = 5; break; case PR_MASTER: maxspelllevel = 6; break; } //} // player can only ever cast spells up to your level. if (!hasjob(lf, J_GOD)) limit(&maxspelllevel, NA, lf->level); if (!willflag && (spelllev > maxspelllevel)) { if (db) dblog("-->power = 0 (no skilled enough in spell school)"); return 0; } } //////////////////////////////////// // HOW POWERFUL IS THIS SPELL? //////////////////////////////////// if (isplayer(lf)) { power = 1; // plus your hitdice/3 power += (gettr(lf)/3); // plus stat modifier power += getspellpowerstatmod(lf, school, NULL); // plus god modifier power += getspellpowergodmod(lf, school, NULL); if (!willflag) { // plus any extra school levels beyond what you need power += ((int)schoolskill - spelllev); } } else { // for monsters, just based on hitdice: power = gettr(lf)/2; limit(&power, 1, NA); } power += boost; limit(&power, 0, getspellmaxpower(spellid)); return power; } int getspellpowergodmod(lifeform_t *lf, enum SPELLSCHOOL school, enum RACE *godid) { int mod = 0; if (godid) *godid = A_NONE; if (!isplayer(lf)) return 0; if ((school == SS_LIFE) && godprayedto(R_GODLIFE)) { int plev; if (godid) *godid = R_GODLIFE; plev = getpietylev(R_GODLIFE, NULL, NULL); if (plev > 0) mod = plev; } else if ((school == SS_DEATH) && godprayedto(R_GODDEATH)) { int plev; if (godid) *godid = R_GODLIFE; plev = getpietylev(R_GODLIFE, NULL, NULL); if (plev > 0) mod = plev; } return mod; } int getspellpowerstatmod(lifeform_t *lf, enum SPELLSCHOOL school, enum ATTRIB *att) { int mod = 0; if (att) *att = A_NONE; if (!isplayer(lf)) return 0; if (school == SS_MENTAL) { if (att) *att = A_IQ; // +/- 2 for iq mod += (getstatmod(lf, A_IQ) / 25); } else if (school == SS_NATURE) { if (att) *att = A_WIS; // +/- 1 for wisdom mod += (getstatmod(lf, A_WIS) / 50); } else if (school == SS_ALLOMANCY) { if (att) *att = A_STR; // +/- 1 for strength mod += (getstatmod(lf, A_STR) / 50); } else if (school == SS_LIFE) { // ie. clerical. // +/- 2 for wisdom if (att) *att = A_WIS; mod += (getstatmod(lf, A_WIS) / 25); } else { if (att) *att = A_IQ; // +/- 1 for iq mod += (getstatmod(lf, A_IQ) / 50); } return mod; } enum SPELLSCHOOL getspellschool(enum OBTYPE spellid) { flag_t *f; objecttype_t *ot; ot = findot(spellid); if (!ot) { return SS_NONE; } if (ot->obclass->id == OC_ABILITY) { return SS_ABILITY; } f = hasflag(ot->flags, F_SPELLSCHOOL); if (f) { return f->val[0]; } return SS_NONE; } // return the school which the given spell belongs to, HOWEVER if it // belongs to multiple ones, prefer ones which the given lifeform // is skilled in. enum SPELLSCHOOL getspellschoolknown(lifeform_t *lf, enum OBTYPE spellid) { flag_t *f; enum SPELLSCHOOL thisschool; objecttype_t *ot; enum SPELLSCHOOL poss[MAXCANDIDATES]; int nposs = 0,b; 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; foreach_bucket(b) { for (f = ot->flags->first[b] ; f ; f = f->next) { if ((f->id == F_SPELLSCHOOL) && getskill(lf, getschoolskill(f->val[0]))) { poss[nposs++] = f->val[0]; } } } if (nposs) { int i; enum SPELLSCHOOL poss2[MAXCANDIDATES]; int nposs2 = 0; enum SKILLLEVEL highestskill = PR_INEPT; // find the school which we are most skilled in for (i = 0; i < nposs; i++) { enum SKILLLEVEL thisslev; thisslev = getskill(lf, getschoolskill(poss[i])); if (thisslev > highestskill) { highestskill = thisslev; } } // now only select from these ones... for (i = 0; i < nposs; i++) { enum SKILLLEVEL thisslev; thisslev = getskill(lf, getschoolskill(poss[i])); if (thisslev == highestskill) { poss2[nposs2++] = poss[i]; } } // pick the first one. //thisschool = poss2[rnd(0,nposs2-1)]; thisschool = poss2[0]; } else { // if we don't know any of the schools... // just pick the first one. f = hasflag(ot->flags, F_SPELLSCHOOL); assert(f); thisschool = f->val[0]; } return thisschool; } enum SKILLLEVEL getspellskill(lifeform_t *lf, enum OBTYPE spellid) { enum SPELLSCHOOL school; enum SKILLLEVEL slev; school = getspellschoolknown(lf, spellid); slev = getskill(lf, getschoolskill(school)); return slev; } int getspellrange(lifeform_t *lf, enum OBTYPE spellid, int power, int *minrange) { objecttype_t *st; int range = UNLIMITED; // default if (minrange) { *minrange = 0; } // If we can _will_ this to occur then we might have a set // range if (lf) { int newrange; flag_t *f; f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL); if (f && strlen(f->text)) { texttospellopts(f->text, "range:", &newrange, NULL); if (newrange > 0) { return newrange; } } } st = findot(spellid); if (st) { flag_t *f; f = hasflag(st->flags, F_RANGE); if (f) { range = f->val[0]; if (minrange && (f->val[1] != NA)) { *minrange = f->val[1]; } } } switch (spellid) { case OT_S_BURNINGWAVE: // base range for this spell is 3 range += (power/3); break; case OT_S_CALLWIND: range = (power*2); break; case OT_S_DIG: range = power; break; case OT_S_FLAMEPILLAR: range = 3+(power/2); break; case OT_S_LIGHTNINGBOLT: range = (power*3); break; case OT_S_CHAINLIGHTNING: range = (power*3); break; case OT_S_SHARDSHOT: range = power; break; default: break; } return range; } int getstamcost(lifeform_t *lf, enum OBTYPE oid) { int stamcost = -1; flag_t *f; f = lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL); if (f) { int newcost; // override stamina cost? if (texttospellopts(f->text, "stamcost:", &newcost, NULL)) { stamcost = newcost; } } if (stamcost == -1) { objecttype_t *ot; ot = findot(oid); if (ot) { f = hasflag(ot->flags, F_STAMCOST); if (f) stamcost = f->val[0]; } } return stamcost; } // provides a description for f_ongoing spells. char *getspelldesc(enum OBTYPE spellid, int power, char *buf) { // default strcpy(buf, ""); switch (spellid) { case OT_S_ALARM: snprintf(buf, BUFLEN, "warnings about nearby enemies"); break; case OT_S_BARKSKIN: snprintf(buf, BUFLEN, "%d damage reduction, vuln to fire", gethardness(MT_WOOD)); break; case OT_S_ENDUREELEMENTS: snprintf(buf, BUFLEN, "resist fire, resist cold"); break; case OT_S_EQANDOP: snprintf(buf, BUFLEN, "reflects missile attacks"); break; case OT_S_GRAVLOWER: snprintf(buf, BUFLEN, "reduces load by %dkg", power*15); break; case OT_S_HEAVENARM: snprintf(buf, BUFLEN, "negates %d damage", power*10); break; case OT_S_HOLYAURA: snprintf(buf, BUFLEN, "weapons deal holy damage"); break; case OT_S_LOWERMETAB: if (power >= 5) { snprintf(buf, BUFLEN, "reduces hunger"); } else { snprintf(buf, BUFLEN, "reduces hunger and speed"); } break; case OT_S_MIRRORIMAGE: snprintf(buf, BUFLEN, "Create %d mirror images", power); break; case OT_S_PSYARMOUR: snprintf(buf, BUFLEN, "+%d Armour Rating", power*4); break; case OT_S_SIXTHSENSE: snprintf(buf, BUFLEN, "warnings about adjacent enemies"); break; case OT_S_SUMMONSWARM: switch (power) { case 1: snprintf(buf, BUFLEN, "Summon a rat swarm."); break; case 2: snprintf(buf, BUFLEN, "Summon a spider swarm."); break; default: case 3: snprintf(buf, BUFLEN, "Summon a locust swarm."); break; } break; case OT_S_SUMMONWEAPON: snprintf(buf, BUFLEN, "Create a %d damage rating magical weapon",2+(power*2)); break; case OT_S_TAILWIND: snprintf(buf, BUFLEN, "Speed boost when moving fowards"); break; case OT_S_THORNS: snprintf(buf, BUFLEN, "1d4 piercing damage to attackers"); break; case OT_S_TRUESTRIKE: if (power == 1) { snprintf(buf, BUFLEN, "Next attack automatically hits"); } else { snprintf(buf, BUFLEN, "Next %d attacks automatically hit",power); } break; case OT_S_WHATGOESUP: snprintf(buf, BUFLEN, "missiles return to source"); break; case OT_S_WINDSHIELD: snprintf(buf, BUFLEN, "Protection from missiles <= %d km/h, 1-%d dmg to attackers",speedtokph(power), power); break; default: break; } return buf; } // for the given skill (ie. metalwork or sewing) + level, _append_ the materials we can work on to the list, and // how much we can repair them. // returns the number of materials/cutoffs which were appended. int getworkablematerials(lifeform_t *lf, enum SKILL skid , enum MATERIAL *repairablemats, int *cutoffpct, int *nmats) { enum SKILLLEVEL slev; int cutoff,cutoffmod = 0,nworkable = 0; object_t *helpob = NULL; // note: we DONT initialise nmats to zero. slev = getskill(lf, skid); switch (slev) { case PR_NOVICE: cutoff = 80; break; case PR_BEGINNER: cutoff = 65; break; case PR_ADEPT: cutoff = 50; break; case PR_SKILLED: cutoff = 25; break; case PR_EXPERT: cutoff = 0; break; case PR_MASTER: cutoff = 0; break; default: cutoff = 100; break; } if (skid == SK_METALWORK) { helpob = getworkhelpob(lf->pack, MT_METAL, NULL, &cutoffmod); } else if (skid == SK_SEWING) { helpob = getworkhelpob(lf->pack, MT_CLOTH, NULL, &cutoffmod); } if (helpob) { cutoff -= cutoffmod; } limit(&cutoff, 0, NA); if (cutoff < 100) { if (skid == SK_METALWORK) { repairablemats[*nmats] = MT_METAL; if (cutoffpct) cutoffpct[*nmats] = cutoff; (*nmats)++; nworkable++; } else if (skid == SK_SEWING) { repairablemats[*nmats] = MT_CLOTH; if (cutoffpct) cutoffpct[*nmats] = cutoff; (*nmats)++; repairablemats[*nmats] = MT_LEATHER; if (cutoffpct) cutoffpct[*nmats] = cutoff; (*nmats)++; repairablemats[*nmats] = MT_FLESH; if (cutoffpct) cutoffpct[*nmats] = cutoff; (*nmats)++; nworkable += 2; } } return nworkable; } // returns an object from 'op' which will help with work on the given material object_t *getworkhelpob(obpile_t *op, enum MATERIAL mat, int *howmuch, int *cutoffmod) { object_t *helpob = NULL; if (howmuch) *howmuch = 0; if (cutoffmod) *cutoffmod = 0; helpob = hasobwithflagval(op, F_HELPSREPAIR, mat, NA, NA, NULL); if (helpob) { flag_t *f; f = hasflagval(helpob->flags, F_HELPSREPAIR, mat, NA, NA, NULL); if (f) { if (howmuch) *howmuch = f->val[1]; if (cutoffmod) *cutoffmod = f->val[2]; } } return helpob; } // magically propel an object into lf's hands. // if it's too big it'll hit them! void pullobto(object_t *o, lifeform_t *lf) { char obname[BUFLEN]; char lfname[BUFLEN]; glyph_t *gl; gl = getglyph(o); anim(getoblocation(o), lf->cell, gl->ch, gl->colour); getobname(o, obname, o->amt); getlfname(lf, lfname); if (isplayer(lf) || haslos(player, lf->cell)) { msg("%s %s towards %s!", obname, OB1(o,"flies","fly"), lfname); } // can we pick up the object? if (hasflag(o->flags, F_NOPICKUP) || isimpassableob(o, lf, getlfsize(lf)) || !canpickup(lf, o, ALL)) { char buf[BUFLEN]; int dir; cell_t *obloc,*newcell; snprintf(buf, BUFLEN, "a flying %s", noprefix(obname)); if (isplayer(lf) || haslos(player, lf->cell)) { msg("%s %s into %s!", obname, OB1(o,"slams","slam"), lfname); } // where does it end up? obloc = getoblocation(o); dir = getdirtowards(lf->cell, obloc, NULL, B_FALSE, DT_COMPASS); newcell = getcellindir(lf->cell, dir); if (newcell) { // move next to player o = moveob(o, newcell->obpile, o->amt); // lf takes damage losehp(lf, getthrowdam(o), DT_PROJECTILE, NULL, buf); } else { o = moveob(o, lf->cell->obpile, o->amt); // kill lf straight away! lf->lastdamtype = DT_PROJECTILE; setlastdam(lf, buf); lf->hp = 0; } } else { if (isplayer(lf) || haslos(player, lf->cell)) { msg("%s %s %s.", lfname, isplayer(lf) ? "catch" : "catches", obname); } pickup(lf, o, o->amt, B_FALSE, B_FALSE); } } int stopallspells(lifeform_t *lf) { flag_t *f,*nextf; int nstopped = 0,b; foreach_bucket(b) { for (f = lf->flags->first[b] ; f ; f = nextf) { nextf = f->next; if (f->id == F_BOOSTSPELL) { if (!stopspell(lf, f->val[0])) nstopped++; } } } // also stop spelllike abilities nstopped += killflagsofid(lf->flags, F_FULLSHIELD); return nstopped; } int stopallspellsexcept(lifeform_t *lf, ...) { flag_t *f,*nextf; va_list args; enum OBTYPE exception[MAXCANDIDATES]; int nexceptions = 0; int nstopped = 0,b; 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); foreach_bucket(b) { for (f = lf->flags->first[b] ; f ; f = nextf) { nextf = f->next; if (f->id == F_BOOSTSPELL) { int stopthis = B_TRUE; int n; for (n = 0; n < nexceptions; n++) { if (exception[n] == f->val[0]) { stopthis = B_FALSE; break; } } if (stopthis) { if (!stopspell(lf, f->val[0])) nstopped++; } } } } return nstopped; } int schoolappearsinbooks(enum SPELLSCHOOL ss) { switch (ss) { case SS_DIVINE: case SS_ABILITY: case SS_ALLOMANCY: case SS_MENTAL: case SS_NATURE: return B_FALSE; default: break; } return B_TRUE; } void spellcloud(cell_t *srcloc, int radius, int dirtype, int ch, enum COLOUR col, enum OBTYPE sid, int power, int frompot, char *seetext, char *noseetext, int aimedateyes, object_t *fromob, int includecentre) { int x,y; char *see = NULL, *nosee = NULL; objecttype_t *ot; int (*distfunc)(cell_t *, cell_t *); ot = findot(sid); if (!ot) return; if (dirtype == DT_ORTH) { distfunc = getcelldistorth; } else { distfunc = getcelldist; } if (seetext) { see = strdup(seetext); } if (noseetext) { nosee = strdup(noseetext); } if (fromob) { char obname[BUFLEN]; // replace "OB" in seetext/noseetext based on fromob getobname(fromob, obname, 1); if (see) { strrep(&see, "OB", obname, NULL); capitalise(see); } if (nosee) { strrep(&nosee, "OB", obname, NULL); capitalise(nosee); } } if (ch != '\0') { animradial(srcloc, radius, ch, col, DT_ORTH, see, nosee); } if (see) free(see); if (nosee) free(nosee); for (y = srcloc->y - radius ; y <= srcloc->y + radius; y++) { for (x = srcloc->x - radius ; x <= srcloc->x + radius; x++) { cell_t *c; c = getcellat(srcloc->map, x, y); if (c && c->lf && !c->type->solid && haslof(srcloc, c, LOF_WALLSTOP, NULL)) { int safe = B_FALSE; if (!includecentre && (c == srcloc)) { safe = B_TRUE; } if (aimedateyes && (!hasbp(c->lf, BP_EYES) || getarmour(c->lf, BP_EYES))) { object_t *arm; safe = B_TRUE; arm = getarmour(c->lf, BP_EYES); if (arm) { char armname[BUFLEN]; getobname(arm, armname, 1); if (isplayer(c->lf)) { msg("Your %s protects your eyes.", noprefix(armname)); } else if (cansee(player, c->lf)) { char lfname[BUFLEN]; getlfname(c->lf, lfname); msg("%s%s %s protects its eyes.", lfname, getpossessive(lfname), noprefix(armname)); } } } if (!safe) { if (distfunc(srcloc, c) <= radius) { // cast the spell if (ot->obclass->id == OC_SPELL) { dospelleffects(NULL, ot->id, power, c->lf, NULL, c, B_UNCURSED, NULL, frompot, fromob); } else if (ot->obclass->id == OC_ABILITY) { abilityeffects(c->lf, ot->id, c, c->lf, NULL); } } } } } } } int spellisfromschool(int spellid, enum SPELLSCHOOL school) { objecttype_t *sp; sp = findot(spellid); if (hasflagval(sp->flags, F_SPELLSCHOOL, school, NA, NA, NULL)) { return B_TRUE; } return B_FALSE; } int spelllearnable(lifeform_t *lf, enum OBTYPE spellid) { enum SPELLSCHOOL knownschool; int ok = B_FALSE; // skilled in one of the spell's schools? knownschool = getspellschoolknown(lf, spellid); if (getskill(lf, getschoolskill(knownschool))) { int mpneeded; if (!cancast(lf, spellid, &mpneeded)) { if (getspellpower(lf, spellid) > 0) { if (getmaxmp(lf) >= mpneeded) { ok = B_TRUE; } } } } return ok; } int spellokformonsters(int spellid) { objecttype_t *sp; sp = findot(spellid); if (!sp) return B_FALSE; if (hasflag(sp->flags, F_AICASTTOATTACK)) return B_TRUE; if (hasflag(sp->flags, F_AICASTTOFLEE)) return B_TRUE; return B_FALSE; } // returns true if the spell was resisted. int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power, int *seenbyplayer, int announce) { char text[BUFLEN],buf[BUFLEN]; int bonus = 0; int resisted = B_FALSE; // cannot resist spells from gods when they are on their home plane if (caster && (caster->race->raceclass->id == RC_GOD)) { if (caster->cell && (caster->cell->map->region->rtype->id == BH_HEAVEN)) { return B_FALSE; } } if (announce) { getlfname(target, buf); if (spellisfromschool(spellid, SS_MENTAL)) { if (isplayer(target)) { strcpy(text, "You shrug off a mental attack."); } else { snprintf(text, BUFLEN, "%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 { snprintf(text, BUFLEN, "%s resists %s spell.", buf, isplayer(caster) ? "your" : "a"); } } } if ((spellid == OT_S_SLEEP) && lfhasflag(target, F_RAGE)) { bonus += 10; } if (caster && hasjob(caster, J_SCOURGE) && (spellid == OT_S_NULLIFY)) { // cancel out the difficulty from NULLIFY being a level 4 spell bonus += 8; } if (spellisfromschool(spellid, SS_MENTAL)) { if (lfhasflag(target, F_MINDSHIELD)) { resisted = B_TRUE; } else { resisted = skillcheck(target, SC_IQ, getmrdiff(spellid,power), bonus); } } else { resisted = skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), bonus); } if (resisted) { if (isplayer(target) || haslos(player, target->cell)) { if (announce) { msg("%s",text); } if (seenbyplayer) *seenbyplayer = B_TRUE; } return B_TRUE; } return B_FALSE; } // returns true on error int stopspell(lifeform_t *caster, enum OBTYPE spellid) { flag_t *f,*nextf; object_t *o, *nexto; objecttype_t *sp; int b; sp = findot(spellid); if (!sp) return B_TRUE; foreach_bucket(b) { for (f = caster->flags->first[b] ; f ; f = nextf) { nextf = f->next; if ((f->id == F_BOOSTSPELL) && (f->val[0] == spellid)) { killflag(f); } else if ((f->lifetime == FROMSPELL) && (f->obfrom == spellid)) { killflag(f); } } } if (isplayer(caster)) { msg("Your %s spell disappates.",sp->name); } // remove any other specific effects based on spell type. for (o = caster->pack->first; o ; o = nexto) { nexto = o->next; if (hasflagval(o->flags, F_CREATEDBYSPELL, spellid, NA, NA, NULL)) { if (cansee(player, caster)) { char obname[BUFLEN]; char lfname[BUFLEN]; getobname(o, obname, 1); getlfname(caster, lfname); msg("%s%s %s vanishes.", lfname, getpossessive(lfname), noprefix(obname)); } killob(o); } } if ((spellid == OT_S_BARKSKIN) && (getlfmaterial(caster) == MT_WOOD)) { setlfmaterial(caster, caster->race->material->id, B_TRUE); } else if (spellid == OT_S_FLOATINGDISC) { map_t *m; lifeform_t *lf; for (m = firstmap ; m ; m = m->next) { for (lf = m->lf ; lf ; lf = lf->next) { if (lf->race->id == R_FLOATINGDISC) { if (lfhasflagval(lf, F_SUMMONEDBY, caster->id, NA, NA, NULL)) { unsummon(lf, B_FALSE); } } } } } return B_FALSE; } // returns # created // pass EITHER wantrace OR wantrc + wantsize + wantalign // // friendly can be: // b_true - force summoned lfs to be peaceful // b_maybe - leave hostility as set by race // b_false - force summoned lfs to be hostile int summonlfs(lifeform_t *caster, cell_t *where, enum RACE wantrace, enum RACECLASS wantrc, enum LFSIZE wantsize, enum ALIGNMENT wantalign, int howmany, int lifetime, int friendly) { lifeform_t *newlf; race_t *r = NULL; enum RACE poss[MAXCANDIDATES]; int nposs = 0; int i; int ncreated = 0; cell_t *c; if (wantrace) { poss[0] = wantrace; nposs = 1; } else { // determine possible types of race for (r = firstrace ; r; r = r->next) { int ok = B_TRUE; if (hasflag(r->flags, F_GODOF)) { ok = B_FALSE; } if (hasflag(r->flags, F_UNIQUE)) { ok = B_FALSE; } if ((wantrc != RC_ANY) && (r->raceclass->id != wantrc)) { ok = B_FALSE; } if (wantsize != SZ_ANY) { flag_t *f; f = hasflag(r->flags, F_SIZE); if (f && (f->val[0] != wantsize)) { ok = B_FALSE; } } if (wantalign != AL_NONE) { flag_t *f; f = hasflag(r->flags, F_ALIGNMENT); if (f && (f->val[0] != wantalign)) { ok = B_FALSE; } } if (ok) { poss[nposs++] = r->id; } } } if (nposs == 0) { return 0; } ncreated = 0; for (i = 0; i < howmany; i++) { // get random adjacent cell // (prefer cells in sight of caster) c = real_getrandomadjcell(where, &ccwalkable, B_ALLOWEXPAND, LOF_NEED, NULL, caster); if (!c) { return ncreated; } // determine exact type of monster r = findrace(poss[rnd(0,nposs-1)]); if (r) { // add it! newlf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, B_NOEXTRA, NULL); // not worth any xp killflagsofid(newlf->flags, F_XPVAL); addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL); // summoned addflag(newlf->flags, F_SUMMONEDBY, caster->id, lifetime, NA, NULL); if (friendly == B_TRUE) { addflag(newlf->flags, F_PETOF, caster->id, NA, NA, NULL); if (areallies(player, caster)) { makefriendly(newlf, PERMENANT); } } else if (friendly == B_FALSE) { if (!lfhasflag(newlf, F_HOSTILE)) { addflag(newlf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); } } ncreated++; } } return ncreated; } lifeform_t *validateabillf(lifeform_t *user, enum OBTYPE aid, lifeform_t **target) { objecttype_t *ot; int maxrange; flag_t *f; if (*target) { return *target; } ot = findot(aid); f = hasflag(ot->flags, F_RANGE); if (f) { maxrange = f->val[0]; } else { maxrange = UNLIMITED; } // ask for a target lifeform if (isplayer(user)) { cell_t *where; char buf[BUFLEN],buf2[BUFLEN]; snprintf(buf, BUFLEN, "Where will you target your %s?",ot->name); snprintf(buf2, BUFLEN, "%s->",ot->name); where = askcoords(buf, buf2, TT_MONSTER, user, maxrange, LOF_DONTNEED, B_FALSE); if (where) { if (!haslf(where)) { msg("There is nobody there!"); return NULL; } if (maxrange != UNLIMITED) { if (getcelldist(user->cell,where) > maxrange) { msg("That location is out of range!"); return NULL; } } *target = haslf(where); } else { msg("Cancelled."); return NULL; } } else { // TODO: fill in monster code? return NULL; } return *target; } cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, enum OBTYPE spellid, int power, int frompot) { int maxrange = UNLIMITED; int done = B_FALSE; cell_t *where = NULL; objecttype_t *sp; int needlos = B_TRUE; enum LOFTYPE needlof = LOF_NEED; int minrange = 0; if (!caster) { return *targcell; } else if ((caster->race->raceclass->id == RC_GOD) && targcell) { return *targcell; } sp = findot(spellid); if (sp) { flag_t *f; f = hasflag(sp->flags, F_LOSLOF); if (f) { needlos = f->val[0]; needlof = f->val[1]; } } if (caster && (caster->race->raceclass->id == RC_GOD)) { needlos = B_FALSE; } maxrange = getspellrange(caster, spellid, power, &minrange); if (*targcell) where = *targcell; while (!done) { if (where) { cell_t *newwhere = NULL; // validate it if (where && needlos && !haslos(caster, where) && (where != caster->cell)) { if (isplayer(caster) && !frompot) { msg("You cannot see there!"); more(); } where = NULL; } // line of fire interrupted? if (where && needlof && !haslofknown(caster->cell, where, needlof, &newwhere)) { if (newwhere) { if (isplayer(caster) && !frompot) { // warn! int ch; ch = askchar("Your have no clear line of fire - really target here?","yn","n", B_TRUE, B_FALSE); if (ch == 'y') { where = newwhere; } else { where = NULL; } } else { where = newwhere; } } else { where = NULL; } } if (where && (maxrange != UNLIMITED) && (getcelldist(caster->cell, where) > maxrange)) { // out of range if (isplayer(caster) && !frompot) { msg("Too far away - max range is %d.",maxrange); more(); } where = NULL; } if (where && (getcelldist(caster->cell, where) < minrange)) { // out of range if (isplayer(caster) && !frompot) { msg("Too close - min range is %d.",minrange); more(); } where = NULL; } if (!frompot && where && where->lf && haslos(caster, where) && isplayer(caster) && areallies(caster, where->lf)) { // warn before targetting yourself! if (getattrbracket(getattr(caster, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { objecttype_t *sp; sp = findot(spellid); if (sp) { if (hasflagval(sp->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL) || hasflagval(sp->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL)) { char ques[BUFLEN]; int ch; if (isplayer(where->lf)) { snprintf(ques, BUFLEN,"Really target yourself"); } else { char lfname[BUFLEN]; getlfname(where->lf, lfname); snprintf(ques, BUFLEN, "Really target %s", lfname); } ch = askchar(ques,"yn","n", B_TRUE, B_FALSE); if (ch != 'y') { where = NULL; } } } } } // still got a target? if (where) { *targcell = where; done = B_TRUE; } } else { // ask for a target cell if (isplayer(caster)) { char buf[BUFLEN]; char buf2[BUFLEN]; if (maxrange == UNLIMITED) { snprintf(buf, BUFLEN, "Where will you target your %s?", sp->name); } else { snprintf(buf, BUFLEN, "Where will you target your %s [max range %d]?",sp->name, maxrange); } snprintf(buf2, BUFLEN, "%s->",sp->name); where = real_askcoords(buf, buf2, targtype, caster, minrange, maxrange, needlof, needlof ? B_TRUE : B_FALSE, NULL, 0); if (!where) { char ques[BUFLEN]; int ch; if (getoption(OPT_CONFIRM_SPELLCANCEL)) { snprintf(ques, BUFLEN, "Abandon your %s?", (sp->obclass->id == OC_SPELL) ? "spell" : "ability"); ch = askchar(ques,"yn","n", B_TRUE, B_FALSE); } else { ch = 'y'; } if (ch == 'y') return NULL; } } else { // TODO: fill in monster code? if (sp->obclass->id == OC_SPELL) fizzle(caster); return NULL; } } } if (*targcell && isplayer(caster)) { flag_t *f; // update last cmd f = hasflag(caster->flags, F_LASTCMD); if (f && (f->text[0] == 'm')) { f->val[0] = (*targcell)->x; f->val[1] = (*targcell)->y; } } return *targcell; } /* // returns the amount if IQ needed to learn this spell // required intelligence is 9 + spelllevel // ie. spelllev1 needs 9+ intelligence // ie. spelllev7 needs 16+ intelligence int getiqreq(enum OBTYPE oid) { flag_t *f; objecttype_t *ot; int iqreq = 0; ot = findot(oid); if (!ot) { return 0; } f = hasflag(ot->flags, F_SPELLLEVEL); if (f) { iqreq = 9 + f->val[0]; } return iqreq; } */