diff --git a/ai.c b/ai.c index 3669081..9391ab6 100644 --- a/ai.c +++ b/ai.c @@ -215,7 +215,7 @@ cell_t *aigetlastknownpos(lifeform_t *lf, lifeform_t *target, int *lastx, int *l // can only obtain direction from footprints if your // tracking skill is high enough. if (bestflag->val[2] == S_SIGHT) { - if (getskill(lf, SK_TRACKING) >= PR_SKILLED) { + if (getskill(lf, SK_PERCEPTION) >= PR_SKILLED) { *lastdir = bestflag->val[1]; } else { *lastdir = D_NONE; diff --git a/attack.c b/attack.c index 788fd71..06668ff 100644 --- a/attack.c +++ b/attack.c @@ -21,11 +21,13 @@ int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damty object_t *armour = NULL; int damtaken = 0; + /* // first of all, only apply some of the damage dam /= 2; if (dam == 0) { return 0; } + */ // special case - missiles always hit flak jacket if (damtype == DT_PROJECTILE) { @@ -63,7 +65,7 @@ int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damty // ALL of damage reduction goes towards armour } else { // SOME of the damage reduction goes towards the armour - // damage taken by armour is reduced by half its armour rating + // damage taken by armour is reduced by _UP TO_ half its armour rating if (ar) { int maxreduction; maxreduction = ar/2; @@ -310,7 +312,10 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) { // take time attacktime = getattackspeed(lf); - taketime(lf, attacktime); + + if (!lfhasflag(lf, F_COMBOSTRIKE)) { + taketime(lf, attacktime); + } if (nweps <= 0) { if (isplayer(lf)) { @@ -433,7 +438,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) int ndam = 0; char buf[BUFLEN]; char attackername[BUFLEN]; - char victimname[BUFLEN]; + char victimname[BUFLEN],victimbpname[BUFLEN]; int fatal = B_FALSE; int feigneddeath = B_FALSE; int deflected = B_FALSE; @@ -522,6 +527,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } } + // did you hit? ndam = 0; @@ -531,11 +537,13 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) critpos = getrandomcorebp(victim); // replace victicname to include body part if ((lf == victim) && !isplayer(lf)) { - sprintf(victimname, "it's %s", getbodypartname(critpos)); + sprintf(victimbpname, "its %s", getbodypartname(critpos)); } else { getlfname(victim, buf); - sprintf(victimname, "%s%s %s", buf, getpossessive(buf), getbodypartname(critpos)); + sprintf(victimbpname, "%s%s %s", buf, getpossessive(buf), getbodypartname(critpos)); } + } else { + strcpy(victimbpname, ""); } if (lf == victim) { @@ -548,6 +556,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) getlfname(victim, victimname); } + // weapon passing through ghosts etc? if (hit) { if (lfhasflag(victim, F_NONCORPOREAL) && @@ -639,8 +648,8 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) // backstab? if ((damtype[0] == DT_PIERCE) && // using a stabbing weapon getskill(lf, SK_BACKSTAB)) { // able to backstab - if (!lfhasflagval(victim, F_STABBEDBY, lf->id, NA, NA, NULL) && // haven't stabbed them before - !lfhasflagval(victim, F_TARGETLF, lf->id, NA, NA, NULL)) { // victim isnt attacking us + if (!lfhasflagval(victim, F_STABBEDBY, lf->id, NA, NA, NULL) ) { // haven't stabbed them before + // && !lfhasflagval(victim, F_TARGETLF, lf->id, NA, NA, NULL)) { // victim isnt attacking us if (!cansee(victim, lf) || // victim can't see us isfleeing(victim)) { @@ -779,6 +788,10 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) // announce it if (!feigneddeath) { + int usecrittext = B_FALSE; + + if ((i == 0) && critical && !fatal) usecrittext = B_TRUE; + if (isplayer(lf)) { char extradambuf[BUFLEN]; char withwep[BUFLEN]; @@ -817,9 +830,9 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) verb = getattackverb(lf, wep, damtype[i], pctof(10, victim->maxhp), victim->maxhp); } } - warn("^%cYou %s %s%s%s%s", fatal ? 'g' : 'n', - verb, - victimname, withwep,extradambuf, + warn("^%cYou %s%s %s%s%s%s", fatal ? 'g' : 'n', + usecrittext ? "critically " : "", verb, + usecrittext ? victimbpname : victimname, withwep,extradambuf, (fatal || backstab) ? "!" : "."); if (fatal && strstr(verb, "behead")) { @@ -857,9 +870,10 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) } else { strcpy(nodamstr, ""); } - warn("^%c%s %s%s %s%s%s.", (isplayer(victim) && !nodam) ? 'b' : 'n', buf, attackverb, + warn("^%c%s %s%s%s %s%s%s.", (isplayer(victim) && !nodam) ? 'b' : 'n', buf, + usecrittext ? "critically " : "", attackverb, needses(attackverb) ? "es" : "s", - victimname,withwep, nodamstr); + usecrittext ? victimbpname : victimname,withwep, nodamstr); } noise(lf->cell, lf, NC_OTHER, 3, "sounds of fighting.", NULL); } @@ -1848,9 +1862,11 @@ int getdamroll(object_t *o, lifeform_t *victim, flag_t *damflag) { } } - if (lfhasflagval(victim, F_INJURY, BP_BODY, DT_SLASH, NA, NULL)) { - // extra damage - dam += rnd(1,2); + if (victim) { + if (lfhasflagval(victim, F_INJURY, BP_BODY, DT_SLASH, NA, NULL)) { + // extra damage + dam += rnd(1,2); + } } return dam; diff --git a/data/hiscores.db b/data/hiscores.db index 792f6ab..f2b2d2c 100644 Binary files a/data/hiscores.db and b/data/hiscores.db differ diff --git a/defs.h b/defs.h index 3e7580a..644b5d4 100644 --- a/defs.h +++ b/defs.h @@ -173,6 +173,9 @@ #define SPEEDUNIT 5 +// experience +#define SKILLXPPERPOINT 250 + // speed settings (lower is faster) #define SPEED_ATTACK SP_NORMAL #define SPEED_DEAD 50 @@ -223,7 +226,7 @@ // String buffer lengths #define BUFLENTINY 10 #define BUFLENSMALL 64 -#define BUFLEN 128 +#define BUFLEN 256 #define BIGBUFLEN 512 #define HUGEBUFLEN 1024 #define MAXPNAMELEN 12 // max player name length @@ -363,18 +366,17 @@ enum SKILL { SK_LISTEN, SK_LOCKPICKING, SK_METALWORK, + SK_PERCEPTION, SK_RANGED, SK_SEWING, SK_SHIELDS, SK_SPEECH, SK_SPELLCASTING, - SK_SPOTHIDDEN, SK_STEALTH, SK_SWIMMING, SK_TECHUSAGE, SK_THIEVERY, SK_THROWING, - SK_TRACKING, SK_TRAPS, SK_TWOWEAPON, // knowledge @@ -408,7 +410,7 @@ enum SKILL { SK_SS_TRANSLOCATION, SK_SS_WILD, }; -#define MAXSKILLS 54 +#define MAXSKILLS 53 // proficiency levels enum SKILLLEVEL { @@ -458,6 +460,7 @@ enum CHECKTYPE { SC_SHIELDBLOCK, SC_FALL, SC_SLIP, + SC_LEARNMAGIC, SC_LISTEN, SC_MORALE, SC_OPENLOCKS, @@ -1062,10 +1065,14 @@ enum OBTYPE { OT_S_CHILL, OT_S_COLDBURST, OT_S_COLDRAY, + OT_S_CRYSTALSHIELD, OT_S_FREEZEOB, OT_S_FROSTBITE, + OT_S_GLACIATE, OT_S_ICEEDGE, OT_S_ICICLE, + OT_S_SLIDE, + OT_S_SNOWBALL, OT_S_WALLOFICE, // -- gravity OT_S_TRUESTRIKE, @@ -1179,12 +1186,14 @@ enum OBTYPE { OT_A_FLURRY, OT_A_GRAB, OT_A_CHARGE, + OT_A_COMBOSTRIKE, OT_A_CRUSH, OT_A_JUMP, OT_A_PRAY, OT_A_RAGE, OT_A_REPAIR, OT_A_SPRINT, + OT_A_STUDYSCROLL, OT_A_STINGACID, // need to define dam in f_canwill OT_A_SUCKBLOOD, OT_A_SWOOP, @@ -1196,6 +1205,7 @@ enum OBTYPE { OT_A_POLYREVERT, OT_A_QUIVERINGPALM, OT_A_STEAL, + OT_A_TRAIN, OT_A_TUMBLE, OT_A_WARCRY, // uses F_NOISETEXT -> N_WARCRY if it is there. // otherwise 'shouts a blood-curdling war cry' @@ -1404,6 +1414,7 @@ enum OBTYPE { OT_BOLT, OT_DART, OT_NANODART, + OT_NEEDLE, OT_JAVELIN, OT_BULLET, OT_RUBBERBULLET, @@ -1466,6 +1477,7 @@ enum OBTYPE { // special weapons OT_ENERGYBLADE, OT_HANDOFGOD, + OT_ICESHIELD, }; @@ -1883,6 +1895,7 @@ enum FLAG { F_MPCOST, // v0=mp cost of spell. if missing, mpcost if splev^2 F_ONGOING, // this spell has an ongoing cost F_CASTINGTIME, // this spell takes v0 turns to cast + F_EXTRADESC, // extra descriptions for this object //F_SPELLLETTER, // text[0] = letter to cast this spell F_AICASTTOFLEE, // AI can cast this spell to help flee/heal // v0 is who to target @@ -1946,6 +1959,7 @@ enum FLAG { F_AUTOCMD, // val0 = how many times to repeat this F_LASTCMD, // text[0] = last command performed, v0/1 = x/y of cell, v2=various F_CANLEARN, // lf is able to learn skill val0 + // v1 = max lev F_STARTOB, // val0 = %chance of starting with it, text = ob name // val1,2 = min/max amounts. if NA, min=max=1. F_STARTOBDT, // val0 = %chance of starting with damtype val1 @@ -2139,6 +2153,8 @@ enum FLAG { F_MORALE, // gain +v0 in morale checks. F_SPOTTED, // you have spotted hiding lf id v0. you lsoe this if they // go out of sight. + // special attack flags + F_COMBOSTRIKE, // lf is performing a combination strike F_HEAVYBLOW, // next attack is a heavy blow F_QUIVERINGPALM, // your next strike will be a quivpalm attack // INTRINSICS @@ -2204,6 +2220,8 @@ enum FLAG { F_GRABBING, // you are grabbing lf id v0 F_HURRICANESTRIKE, // lf is performing a hurricane strike F_HIDING, // lifeform is hiding. v0 is modifier to stealth checks. + F_ICESLIDE, // lf has "slide" spell active. + // v0 = timeleft (since 'lifetime' is used for FROMSPELL) F_INJURY, // v0 = where, v1 = damtype F_INVISIBLE, // lifeform is invisible F_INVULNERABLE,// immune to most damage @@ -2616,6 +2634,7 @@ typedef struct map_s { struct cell_s *cell[MAX_MAPW*MAX_MAPH]; // list of cells in this map int nextmap[MAXDIR_MAP]; // which map is in each direction int beingcreated; + int nfixedrooms; // used for map creation only, not saved. struct lifeform_s *lf,*lastlf; @@ -2751,7 +2770,7 @@ typedef struct lifeform_s { struct race_s *race; int level; int newlevel; - long xp; + long xp,skillxp; int skillpoints; int hp,maxhp; int mp,maxmp; diff --git a/flag.c b/flag.c index 2b883b3..6cf08f9 100644 --- a/flag.c +++ b/flag.c @@ -353,6 +353,7 @@ int countflags(flagpile_t *fp) { int flagcausesloscalc(enum FLAG fid) { switch (fid) { case F_BLOCKSVIEW: + case F_PRONE: return B_TRUE; default: break; } @@ -413,6 +414,7 @@ int flagcausesstatredraw(lifeform_t *lf, enum FLAG fid) { case F_FLYING: case F_HASNEWLEVEL: case F_HIDING: + case F_ICESLIDE: case F_INVISIBLE: case F_LEVITATING: case F_PARALYZED: @@ -822,12 +824,20 @@ void killflag(flag_t *f) { } } if (redolos) { - // everyone who can see this cell recalcs their los - lifeform_t *l; - for (l = redolos->map->lf ; l ; l = l->next) { - if (haslos(l, redolos)) { - precalclos(l); - if (isplayer(l)) redoscreen = B_TRUE; + // - if this was a lf's flag, they recalc their los. + // - if it was an object flag, then everyone who can see + // its cell recalcs their los. + if (f->pile->owner) { + precalclos(f->pile->owner); + if (isplayer(f->pile->owner)) redoscreen = B_TRUE; + } else { + // everyone who can see this cell recalcs their los + lifeform_t *l; + for (l = redolos->map->lf ; l ; l = l->next) { + if (haslos(l, redolos)) { + precalclos(l); + if (isplayer(l)) redoscreen = B_TRUE; + } } } } diff --git a/io.c b/io.c index 4a32316..8228101 100644 --- a/io.c +++ b/io.c @@ -468,6 +468,7 @@ char askchar(char *prompt, char *validchars, char *def, int showchars) { char ch; int valid = B_FALSE; + more(); wclear(msgwin); if (showchars) { sprintf(buf, "%s (",prompt); @@ -1002,6 +1003,8 @@ char *askstring(char *prompt, char punc, char *retbuf, int retbuflen, char *def) char buf[BUFLEN]; char *ending; + more(); + wclear(msgwin); sprintf(buf, "%s",prompt); if (def) { @@ -1300,6 +1303,10 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; } break; + case F_ICESLIDE: + msg("Sheets of ice start forming under %s%s feet!",lfname, getpossessive(lfname)); + donesomething = B_TRUE; + break; case F_INJURY: strcpy(buf, getinjuredbpname(f->val[0])); strcpy(buf3, getinjuryname(f->val[1])); @@ -1551,7 +1558,7 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; break; case F_SLOWACTMOVE: - msg("%s %s",getlfcol(lf, CC_VBAD), lfname, isplayer(lf) ? "feel slow and sluggish." : "looks slow and sluggish."); + msg("^%c%s %s",getlfcol(lf, CC_VBAD), lfname, isplayer(lf) ? "feel slow and sluggish." : "looks slow and sluggish."); donesomething = B_TRUE; break; case F_SPRINTING: @@ -1880,6 +1887,10 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; } break; + case F_ICESLIDE: + msg("%s%s feet are longer generating ice.",lfname, getpossessive(lfname)); + donesomething = B_TRUE; + break; case F_INJURY: strcpy(buf, getinjuredbpname(f->val[0])); strcpy(buf3, getinjuryname(f->val[1])); @@ -3917,6 +3928,7 @@ void describeob(object_t *o) { void describeskill(enum SKILL skid) { skill_t *sk; + enum SKILLLEVEL slev; char buf[BUFLEN]; int i; cls(); @@ -3933,13 +3945,17 @@ void describeskill(enum SKILL skid) { // descriptions wmove(mainwin, 4, 0); - for (i = 0; i < sk->nskilldesc; i++) { - if (sk->skilldesclev[i] == PR_INEPT) { - sprintf(buf, "%s\n",sk->skilldesctext[i]); - } else { - sprintf(buf, "At %s level: %s\n",getskilllevelname(sk->skilldesclev[i]), sk->skilldesctext[i]); + for (slev = PR_INEPT; slev <= PR_MASTER; slev++) { + for (i = 0; i < sk->nskilldesc; i++) { + if (sk->skilldesclev[i] == slev) { + if (slev == PR_INEPT) { + sprintf(buf, "%s\n",sk->skilldesctext[i]); + } else { + sprintf(buf, "At %s level: %s\n",getskilllevelname(sk->skilldesclev[i]), sk->skilldesctext[i]); + } + textwithcol(mainwin, buf); + } } - textwithcol(mainwin, buf); } wrefresh(mainwin); @@ -3952,9 +3968,8 @@ void describeskill(enum SKILL skid) { void describespell(objecttype_t *ot) { char buf[BUFLEN]; - flag_t *f; - int i; - int power,range; + flag_t *retflag[MAXCANDIDATES], *f; + int nretflags,i,power,range; cls(); @@ -3972,9 +3987,16 @@ void describespell(objecttype_t *ot) { mvwprintw(mainwin, 0, 0, buf); wmove(mainwin, 2, 0); - sprintf(buf, "%s\n\n", ot->desc); + sprintf(buf, "%s\n", ot->desc); textwithcol(mainwin, buf); + getflags(ot->flags, retflag, &nretflags, F_EXTRADESC, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + sprintf(buf, "%s\n", f->text); + textwithcol(mainwin, buf); + } + wprintw(mainwin, "\n"); // properties f = hasflag(ot->flags, F_SPELLLEVEL); @@ -5684,83 +5706,61 @@ void doread(obpile_t *op) { void dorest(void) { // can we rest? - if (safetorest(player)) { - int willtrain = B_FALSE; - if (lfhasflag(player, F_HASNEWLEVEL)) { - int ch; - ch = askchar("Would you like to train your skills?","yn","y", B_TRUE); - if (ch == 'y') { - willtrain = B_TRUE; - } - } - // if not training, only rest if we need to. - if (!willtrain) { - char validchars[BUFLEN]; - char ques[BUFLEN]; - char ch; - if (needstorest(player, validchars)) { - if (strchr(validchars, 'h') && strchr(validchars, 'm')) { - strcat(validchars, "bn"); - strcpy(ques, "Rest until full HP, Mana, Both, or none"); - ch = askchar(ques, validchars, "b", B_TRUE); - if (ch == 'b') { - addflag(player->flags, F_RESTUNTILHP, B_TRUE, NA, NA, NULL); - addflag(player->flags, F_RESTUNTILMP, B_TRUE, NA, NA, NULL); - } else if (ch == 'h') { - addflag(player->flags, F_RESTUNTILHP, B_TRUE, NA, NA, NULL); - } else if (ch == 'm') { - addflag(player->flags, F_RESTUNTILMP, B_TRUE, NA, NA, NULL); - } - } else if (strchr(validchars, 'h')) { - strcpy(ques, "Rest until full HP"); - ch = askchar(ques, "yn", "y", B_TRUE); - if (ch == 'y') { - addflag(player->flags, F_RESTUNTILHP, B_TRUE, NA, NA, NULL); - } - } else if (strchr(validchars, 'm')) { - strcpy(ques, "Rest until full Mana"); - ch = askchar(ques, "yn", "y", B_TRUE); - if (ch == 'y') { - addflag(player->flags, F_RESTUNTILMP, B_TRUE, NA, NA, NULL); - } - } - } else { - if (countnearbyhurtallies(player)) { - strcpy(ques, "Rest until nearby allies are healed?"); - ch = askchar(ques, "yn", "y", B_TRUE); - if (ch == 'y') { - addflag(player->flags, F_RESTUNTILALLIES, B_TRUE, NA, NA, NULL); - } - } else { - msg("You don't need to rest at the moment."); - return; - } - } + char validchars[BUFLEN]; + char ques[BUFLEN]; + char ch; - if (!lfhasflag(player, F_RESTUNTILHP) && - !lfhasflag(player, F_RESTUNTILMP) && - !lfhasflag(player, F_RESTUNTILALLIES)) { - msg("Cancelled."); - return; - } - } - if (!startresting(player, willtrain)) { - // do the first one right away - rest(player, B_TRUE); + if (check_rest_ok(player)) return; + + if (needstorest(player, validchars)) { + if (strchr(validchars, 'h') && strchr(validchars, 'm')) { + strcat(validchars, "bn"); + strcpy(ques, "Rest until full HP, Mana, Both, or none"); + ch = askchar(ques, validchars, "b", B_TRUE); + if (ch == 'b') { + addflag(player->flags, F_RESTUNTILHP, B_TRUE, NA, NA, NULL); + addflag(player->flags, F_RESTUNTILMP, B_TRUE, NA, NA, NULL); + } else if (ch == 'h') { + addflag(player->flags, F_RESTUNTILHP, B_TRUE, NA, NA, NULL); + } else if (ch == 'm') { + addflag(player->flags, F_RESTUNTILMP, B_TRUE, NA, NA, NULL); + } + } else if (strchr(validchars, 'h')) { + strcpy(ques, "Rest until full HP"); + ch = askchar(ques, "yn", "y", B_TRUE); + if (ch == 'y') { + addflag(player->flags, F_RESTUNTILHP, B_TRUE, NA, NA, NULL); + } + } else if (strchr(validchars, 'm')) { + strcpy(ques, "Rest until full Mana"); + ch = askchar(ques, "yn", "y", B_TRUE); + if (ch == 'y') { + addflag(player->flags, F_RESTUNTILMP, B_TRUE, NA, NA, NULL); + } } } else { - switch (reason) { - case E_LEVITATING: - msg("You cannot rest while levitating in mid-air!"); - break; - case E_MONSTERNEARBY: - msg("You cannot rest - there are monsters nearby!"); - break; - default: - msg("You cannot rest for some reason."); - break; + if (countnearbyhurtallies(player)) { + strcpy(ques, "Rest until nearby allies are healed?"); + ch = askchar(ques, "yn", "y", B_TRUE); + if (ch == 'y') { + addflag(player->flags, F_RESTUNTILALLIES, B_TRUE, NA, NA, NULL); + } + } else { + msg("You don't need to rest at the moment."); + return; } } + + if (!lfhasflag(player, F_RESTUNTILHP) && + !lfhasflag(player, F_RESTUNTILMP) && + !lfhasflag(player, F_RESTUNTILALLIES)) { + msg("Cancelled."); + return; + } + if (!startresting(player, B_FALSE)) { + // do the first one right away + rest(player, B_TRUE); + } } int doselguntarget(void) { @@ -6303,6 +6303,7 @@ char getchoice(prompt_t *prompt) { int nextpage = -1; int lastline = SCREENH - 4; + more(); gotheadings = B_FALSE; for (i = 0; i < prompt->nchoices; i++) { @@ -6416,6 +6417,8 @@ char getchoicestr(prompt_t *prompt, int useshortcuts, int showallatstart) { strcpy(inpstring, ""); + more(); + // mark valid choices, and determine whether we have // headings gotheadings = B_FALSE; @@ -6885,6 +6888,17 @@ void handleinput(void) { } temp[0] = ch; temp[1] = '\0'; + + // actions other than movement will cancel certain effects + switch (tolower(ch)) { + case 'h': case 'j': case 'k': case 'l': + case 'y': case 'u': case 'b': case 'n': + break; + default: + stoprunning(player); + if (hasactivespell(player, OT_S_SLIDE)) stopspell(player, OT_S_SLIDE); + break; + } switch (ch) { // movement @@ -7167,16 +7181,18 @@ void dblog_nocr(char *format, ... ) { // force a '--more--' prompt void more(void) { - //msg("^"); - strcat(msgbuf, MSGMORESTRING); - //mvwprintw(msgwin, 0, 0, msgbuf); - drawmsg(); - curs_set(1); - // wait for space - while (getch() != ' '); - curs_set(0); - // clear msg - clearmsg(); + if (strlen(msgbuf)) { + //msg("^"); + strcat(msgbuf, MSGMORESTRING); + //mvwprintw(msgwin, 0, 0, msgbuf); + drawmsg(); + curs_set(1); + // wait for space + while (getch() != ' '); + curs_set(0); + // clear msg + clearmsg(); + } } @@ -7354,6 +7370,7 @@ void drawstatus(void) { wattron(statwin, A_BOLD); wprintw(statwin, "/"); wattroff(statwin, A_BOLD); wprintw(statwin, "%d%%",xpleft); } + // blinded? if (isblind(player) && !lfhasflag(player, F_ASLEEP)) { setcol(statwin, C_RED); @@ -7384,7 +7401,7 @@ void drawstatus(void) { unsetcol(statwin, C_BOLDBLUE); } else { setcol(statwin, C_BOLDBLUE); - wprintw(statwin, " Flt"); // "float"ing + wprintw(statwin, " Hov"); // "hov"ering unsetcol(statwin, C_BOLDBLUE); } } @@ -7807,7 +7824,7 @@ void showlfarmour(lifeform_t *lf) { strcat(rhs, "(covered) "); } f = hasflag(o->flags, F_ARMOURRATING); - if (f) { + if (f && (f->val[0])) { char numbuf[BUFLENSMALL]; sprintf(numbuf, " ^g[AR:%d]^n",f->val[0]); strcat(rhs, numbuf); @@ -7992,7 +8009,11 @@ void showlfstats(lifeform_t *lf, int showall) { wprintw(mainwin, "%d / %d%s", lf->mp , lf->maxmp,maxmpstr); y++; } if (showall) { - doheadingsmall(mainwin, y, 0, ftext, "Exp Level"); + if (isplayer(lf)) { + doheadingsmall(mainwin, y, 0, ftext, "Exp Level"); + } else { + doheadingsmall(mainwin, y, 0, ftext, "Hit Dice"); + } if (isplayer(lf)) { xpneeded = getxpforlev(lf->level + 1) - lf->xp; wprintw(mainwin, "%d (%ld XP, %ld for next)", lf->level, lf->xp, xpneeded); y++; @@ -8006,10 +8027,14 @@ void showlfstats(lifeform_t *lf, int showall) { if ((lf->skillpoints == 0) && (attpoints == 0)) { wprintw(mainwin, "n/a"); } else { + /* wprintw(mainwin, "%d skill%s, %d attrib%s", lf->skillpoints, (lf->skillpoints == 1) ? "" : "s", attpoints, (attpoints == 1) ? "" : "s"); + */ + wprintw(mainwin, "%d skill point%s", lf->skillpoints, + (lf->skillpoints == 1) ? "" : "s"); } y++; } @@ -8483,12 +8508,14 @@ void showlfstats(lifeform_t *lf, int showall) { strcpy(buf2, getinjuryname(f->val[1])); strcpy(buf3, getinjurydesc(f->val[0], f->val[1])); if (isplayer(lf)) { - msg("^%cYour %s is %s%s.", getlfcol(lf, CC_VBAD), buf, buf2, buf3); + mvwprintw(mainwin, y, 0, "^%cYour %s is %s%s.", getlfcol(lf, CC_VBAD), buf, buf2, buf3); + y++; } else { char lfname[BUFLEN]; getlfname(lf, lfname); - msg("^%c%s%s %s is %s%s.", getlfcol(lf, CC_VBAD), + mvwprintw(mainwin, y, 0, "^%c%s%s %s is %s%s.", getlfcol(lf, CC_VBAD), lfname, getpossessive(lfname), buf, buf2, buf3); + y++; } } f = lfhasknownflag(lf, F_INVISIBLE); @@ -8813,7 +8840,7 @@ void showlfstats(lifeform_t *lf, int showall) { //centre(mainwin, y, "SKILLS"); y ++; - sprintf(skilltitle, "%-40s%-40s","AVAILABLE SKILLS", "KNOWN SKILLS"); + sprintf(skilltitle, "%-40s%-40s","AVAILABLE SKILLS", "KNOWN SKILLS (*=maxed)"); doheading(mainwin, &y, 0, skilltitle); for (n = 0; n < MAXOF(numknown,numavailable); n++) { if (n < numavailable) { @@ -9231,16 +9258,21 @@ void showlfstats(lifeform_t *lf, int showall) { y++; } f = lfhasknownflag(lf, F_HEAVYBLOW); - if (f && (f->known)) { + if (f) { sprintf(buf,"%s%s attacks knock enemies back.", you(lf), getpossessive(you(lf))); mvwprintw(mainwin, y, 0, buf); y++; } f = lfhasknownflag(lf, F_HOLYAURA); - if (f && (f->known)) { + if (f) { mvwprintw(mainwin, y, 0, "%s %s surrounded by a holy aura.", you(lf), is(lf)); y++; } + f = lfhasknownflag(lf, F_ICESLIDE); + if (f) { + msg("%s%s feet automatically generating sheets of ice.",your(lf)); + } + f = lfhasknownflag(lf, F_QUICKBITE); if (f && (f->known)) { sprintf(buf,"%s can bite wounded enemies for extra damage.", you(lf)); diff --git a/lf.c b/lf.c index ca691f7..4d3fcd3 100644 --- a/lf.c +++ b/lf.c @@ -714,6 +714,8 @@ int cannotmove(lifeform_t *lf) { } int canlearn(lifeform_t *lf, enum SKILL skid) { + if (ismaxedskill(lf, skid)) return B_FALSE; + if (lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL)) { return B_TRUE; } @@ -1545,6 +1547,26 @@ void checkxp(enum RACE rid) { } */ +int check_rest_ok(lifeform_t *lf) { + if (!safetorest(lf)) { + if (isplayer(lf)) { + switch (reason) { + case E_LEVITATING: + msg("You cannot rest while levitating in mid-air!"); + break; + case E_MONSTERNEARBY: + msg("You cannot rest - there are monsters nearby!"); + break; + default: + msg("You cannot rest for some reason."); + break; + } + } + return B_TRUE; + } + return B_FALSE; +} + // how dangerous is lf2 to lf1? // < 0 = harder // > 0 = easier @@ -2706,38 +2728,48 @@ void enhanceskills(lifeform_t *lf) { int newskillcost = 1; float hpratio,mpratio; enum SKILLLEVEL slev; + int gainedxplev = B_FALSE; - lf->level = lf->newlevel; - - // special cases which happen before doing hp/mp - if (hasjob(lf, J_MONK) && (lf->level == 2)) { - addflag(lf->flags, F_MPDICE, 1, 0, NA, NULL); + if (lf->newlevel != lf->level) { + lf->level = lf->newlevel; + gainedxplev = B_TRUE; } - // update hp - hpratio = ((float)lf->hp / (float)lf->maxhp); - lf->maxhp += rollhitdice(lf); - lf->hp = hpratio * (float)lf->maxhp; - - // update mp - if (lfhasflag(lf, F_MPDICE)) { - if (lf->maxmp == 0) { - mpratio = 1; - } else { - mpratio = ((float)lf->mp / (float)lf->maxmp); + if (gainedxplev) { + // special cases which happen before doing hp/mp + if (hasjob(lf, J_MONK) && (lf->level == 2)) { + addflag(lf->flags, F_MPDICE, 1, 0, NA, NULL); } - lf->maxmp += rollmpdice(lf); - lf->mp = mpratio * (float)lf->maxmp; - } - if (isplayer(lf)) { - statdirty = B_TRUE; - drawstatus(); - wrefresh(statwin); - msg("^GWelcome to level %d!",lf->level); - more(); - } + // update hp + hpratio = ((float)lf->hp / (float)lf->maxhp); + lf->maxhp += rollhitdice(lf); + lf->hp = hpratio * (float)lf->maxhp; + // update mp + if (lfhasflag(lf, F_MPDICE)) { + if (lf->maxmp == 0) { + mpratio = 1; + } else { + mpratio = ((float)lf->mp / (float)lf->maxmp); + } + lf->maxmp += rollmpdice(lf); + lf->mp = mpratio * (float)lf->maxmp; + } + + if (isplayer(lf)) { + statdirty = B_TRUE; + drawstatus(); + wrefresh(statwin); + msg("^GWelcome to level %d!",lf->level); + } + + // enhance a random skill every 2 levels (ie. 3/5/7/etc) + if (((lf->level + 1) % 2) == 0) { + enhancerandomskill(lf); + } + } + // increase str/int etc if we can f = lfhasflag(lf, F_STATGAINREADY); @@ -2745,6 +2777,7 @@ void enhanceskills(lifeform_t *lf) { enum ATTRIB att; if (isplayer(lf)) { char ch; + more(); ch = askchar("Increase your Strength, Dexterity, Fitness, IQ or Wisdom?", "sdfiw",NULL, B_TRUE); switch (ch) { case 's': att = A_STR; break; @@ -2772,13 +2805,9 @@ void enhanceskills(lifeform_t *lf) { f = lfhasflag(lf, F_STATGAINREADY); } - // enhance a random skill every 2 levels (ie. 3/5/7/etc) - if (((lf->level + 1) % 2) == 0) { - enhancerandomskill(lf); - } // now ask about learning/enhancing skills - if (isplayer(lf)) { + if (isplayer(lf)) { char eorl = 'e'; int skillstoenhance = 0; int skillstolearn = 0; @@ -2787,31 +2816,38 @@ void enhanceskills(lifeform_t *lf) { skillstoenhance = 0; for (f = lf->flags->first ; f ; f = f->next) { if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) { - skillstoenhance++; + if (lf->skillpoints >= getskilllevcost(f->val[1] + 1)) { + skillstoenhance++; + } } } - skillstolearn = 0; - for (sk = firstskill ; sk ; sk = sk->next) { - if (!getskill(player, sk->id) && canlearn(player, sk->id)) { - skillstolearn++; + if (lf->skillpoints >= newskillcost) { + skillstolearn = 0; + for (sk = firstskill ; sk ; sk = sk->next) { + if (!getskill(player, sk->id) && canlearn(player, sk->id)) { + skillstolearn++; + } } } while (!done && lf->skillpoints && (skillstolearn || skillstoenhance)) { - if (skillstolearn && (lf->skillpoints >= newskillcost)) { + if (skillstolearn) { + char buf[BUFLEN]; if (skillstoenhance) { - char buf[BUFLEN]; - sprintf(buf, "Enhance existing skills or Learn a new one (%d points left)?",lf->skillpoints); + sprintf(buf, "(E)nhance skills, (L)earn skills, or (N)either (%d points left)?",lf->skillpoints); eorl = askchar(buf,"eln","e", B_TRUE); } else { - ch = askchar("Learn a new skill?","yn","y", B_TRUE); + sprintf(buf,"Learn a new skill (%d points left)?",lf->skillpoints); + ch = askchar(buf,"yn","y", B_TRUE); if (ch == 'y') eorl = 'l'; else eorl = 'n'; } } else if (skillstoenhance) { - ch = askchar("Enhance your current skills?","yn","y", B_TRUE); + char buf[BUFLEN]; + sprintf(buf,"Enhance your current skills (%d points left)?",lf->skillpoints); + ch = askchar(buf,"yn","y", B_TRUE); if (ch == 'y') eorl = 'e'; else eorl = 'n'; } else { @@ -2825,18 +2861,22 @@ void enhanceskills(lifeform_t *lf) { if (skillstoenhance) { char ques[BUFLEN],ques2[BUFLEN]; int done = B_FALSE; - sprintf(ques, "Enhance which skill (%d left)?", lf->skillpoints); - sprintf(ques2, "Describe which skill (%d left)?", lf->skillpoints); + sprintf(ques, "Enhance which skill (%d points left)?", lf->skillpoints); + sprintf(ques2, "Describe which skill?"); initprompt(&prompt, ques); addpromptq(&prompt, ques2); ch = 'a'; for (f = lf->flags->first ; f ; f = f->next) { if ((f->id == F_HASSKILL) && (f->val[1] != PR_MASTER)) { - char buf[BUFLEN]; - sprintf(buf, "%s (%s -> %s)", getskillname(f->val[0]), - getskilllevelname(f->val[1]), getskilllevelname(f->val[1] + 1)); - addchoice(&prompt, ch++, getskillname(f->val[0]), buf, f); + int cost; + cost = getskilllevcost(f->val[1] + 1); + if (lf->skillpoints >= cost) { + char buf[BUFLEN]; + sprintf(buf, "%s (%s, %d points)", getskillname(f->val[0]), + getskilllevelname(f->val[1] + 1), cost); + addchoice(&prompt, ch++, getskillname(f->val[0]), buf, f); + } } } addchoice(&prompt, '-', "None", "None", NULL); @@ -2848,8 +2888,8 @@ void enhanceskills(lifeform_t *lf) { whichsk = f->val[0]; if (prompt.whichq == 0) { + lf->skillpoints -= getskilllevcost(f->val[1]+1); giveskill(lf, whichsk); - lf->skillpoints--; done = B_TRUE; } else { // ie. describing a skill describeskill(whichsk); @@ -2868,12 +2908,11 @@ void enhanceskills(lifeform_t *lf) { if (player->skillpoints < newskillcost) { msg("You need at least %d skill points to learn a new skill.", newskillcost); } else { - if (skillstolearn) { int done = B_FALSE; char ques[BUFLEN],ques2[BUFLEN]; - sprintf(ques, "Learn which new skill (%d left)?", player->skillpoints); - sprintf(ques2, "Describe which skill (%d left)?", lf->skillpoints); + sprintf(ques, "Learn which new skill (%d points left)?", player->skillpoints); + sprintf(ques2, "Describe which skill?"); initprompt(&prompt, ques); addpromptq(&prompt, ques2); @@ -2910,184 +2949,185 @@ void enhanceskills(lifeform_t *lf) { } // end enhance/learnnew } // whiel skillstolearn || skillstoenhance statdirty = B_TRUE; - } else { + } else if (lf->skillpoints) { // monsters will just enhance a random skill, they never learn new ones. enhancerandomskill(lf); + // only costs 1 point for monsters to get any skill lf->skillpoints--; } // end if isplayer - // give job-based level rewards - f = levelabilityready(lf); - while (f) { - if (f->id == F_LEVABIL) { - addflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text); - } else if (f->id == F_LEVFLAG) { - addflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text); - } else if (f->id == F_LEVSKILL) { - giveskill(lf, f->val[1]); - } else if (f->id == F_LEVSPELL) { - addflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL); - } else if (f->id == F_LEVSPELLSCHOOL) { // select a spell from school - if (isplayer(lf)) { - int done = B_FALSE; - while (!done) { - makespellchoicelist(&prompt, player, "Learn which new spell:","Describe which spell:", f->val[1], B_TRUE, B_FALSE, player->maxmp); + if (gainedxplev) { + // give job-based level rewards + f = levelabilityready(lf); + while (f) { + if (f->id == F_LEVABIL) { + addflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text); + } else if (f->id == F_LEVFLAG) { + addflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text); + } else if (f->id == F_LEVSKILL) { + giveskill(lf, f->val[1]); + } else if (f->id == F_LEVSPELL) { + addflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL); + } else if (f->id == F_LEVSPELLSCHOOL) { // select a spell from school + if (isplayer(lf)) { + int done = B_FALSE; + while (!done) { + makespellchoicelist(&prompt, player, "Learn which new spell:","Describe which spell:", f->val[1], B_TRUE, B_FALSE, player->maxmp); + if (prompt.nchoices > 0) { + objecttype_t *ot; + getchoicestr(&prompt, B_TRUE, B_TRUE); + ot = prompt.result; + if (ot) { + if (prompt.whichq == 0) { // learn the spell + addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); + done = B_TRUE; + } else { + describespell(ot); + } + } + } else { + msg("There are no new spells for you to learn at this time."); + done = B_TRUE; + } + } + } else { + // monster gets random spell + makespellchoicelist(&prompt, lf, "xx","xx:", f->val[1], B_TRUE, B_FALSE, lf->maxmp); if (prompt.nchoices > 0) { objecttype_t *ot; - getchoicestr(&prompt, B_TRUE, B_TRUE); - ot = prompt.result; + // pick one randomly + ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices)].data; if (ot) { - if (prompt.whichq == 0) { // learn the spell - addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); - done = B_TRUE; - } else { - describespell(ot); - } + addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); } - } else { - msg("There are no new spells for you to learn at this time."); - done = B_TRUE; } } - } else { - // monster gets random spell - makespellchoicelist(&prompt, lf, "xx","xx:", f->val[1], B_TRUE, B_FALSE, lf->maxmp); - if (prompt.nchoices > 0) { - objecttype_t *ot; - // pick one randomly - ot = (objecttype_t *)prompt.choice[rnd(0,prompt.nchoices)].data; - if (ot) { + } + f->lifetime = LEVABILITYDONE; // mark as done. + + // get next one + f = levelabilityready(lf); + } + // now refresh them all for next level. + refreshlevelabilities(lf); + + // special case level-based job effects + if (hasjob(lf, J_MONK)) { + // enhance fist strength + f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL); + if (f) { + char newtext[BUFLEN]; + strcpy(newtext, f->text); + switch(lf->level) { + case 2: + strcpy(newtext,"1d6"); + break; + case 3: + case 4: + strcpy(newtext,"1d6+1"); + break; + case 5: + strcpy(newtext,"2d4"); + break; + case 6: + case 7: + strcpy(newtext,"3d3"); + break; + case 8: + strcpy(newtext,"2d6"); + break; + case 9: + case 10: + strcpy(newtext,"3d4"); + break; + case 11: + strcpy(newtext,"3d4+1"); + break; + case 12: + case 13: + strcpy(newtext,"4d4"); + break; + case 14: + strcpy(newtext,"4d4+1"); + break; + case 15: + strcpy(newtext,"5d4"); + break; + case 16: + case 17: + strcpy(newtext,"4d6"); + break; + case 18: + strcpy(newtext,"6d4"); + break; + case 19: + strcpy(newtext,"5d6"); + break; + case 20: + strcpy(newtext,"8d4"); + break; + default: + strcpy(newtext,"6d6"); + break; + } + if (!streq(newtext, f->text)) { + changeflagtext(f, newtext); + if (isplayer(lf)) msg("^gYour unarmed attack damage has increased!"); + } + } + // enhance # attacks + f = lfhasflag(lf, F_MAXATTACKS); + if (f) { + int min,max; + min = f->val[0]; + max = f->val[1]; + if ((lf->level >= 2) && (lf->level <= 3)) { + min = 1; max = 1; + } else if ((lf->level >= 4) && (lf->level <= 6)) { + min = 1; max = 2; + } else if ((lf->level >= 7) && (lf->level <= 9)) { + min = 2; max = 2; + } else if ((lf->level >= 10) && (lf->level <= 12)) { + min = 2; max = 3; + } else if ((lf->level >= 13) && (lf->level <= 16)) { + min = 3; max = 3; + } else if (lf->level >= 17) { + min = 3; max = 4; + } + if ((min != f->val[0]) || (max != f->val[1])) { + f->val[0] = min; + f->val[1] = max; + if (isplayer(lf)) msg("^gYour number of unarmed attacks has increased!"); + } + } + } + + // psionics sometimes lets you learn spells + slev = getskill(lf, SK_SS_MENTAL); + if (pctchance(slev*10)) { + // construct list of castable mental spells + makespellchoicelist(&prompt, lf, "Learn which new psionic power:","Describe which psionic power:", SS_MENTAL, B_TRUE, B_FALSE, player->maxmp); + if (prompt.nchoices > 0) { + objecttype_t *ot; + msg("Your brain has unlocked a new psionic power!"); more(); + getchoicestr(&prompt, B_TRUE, B_TRUE); + ot = prompt.result; + if (ot) { + if (prompt.whichq == 0) { // learn the spell addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); + } else { + describespell(ot); } } } + } - f->lifetime = LEVABILITYDONE; // mark as done. - - // get next one - f = levelabilityready(lf); - } - // now refresh them all for next level. - refreshlevelabilities(lf); - - // special case level-based job effects - if (hasjob(lf, J_MONK)) { - // enhance fist strength - f = lfhasflagval(lf, F_HASATTACK, OT_FISTS, NA, NA, NULL); - if (f) { - char newtext[BUFLEN]; - strcpy(newtext, f->text); - switch(lf->level) { - case 2: - strcpy(newtext,"1d6"); - break; - case 3: - case 4: - strcpy(newtext,"1d6+1"); - break; - case 5: - strcpy(newtext,"2d4"); - break; - case 6: - case 7: - strcpy(newtext,"3d3"); - break; - case 8: - strcpy(newtext,"2d6"); - break; - case 9: - case 10: - strcpy(newtext,"3d4"); - break; - case 11: - strcpy(newtext,"3d4+1"); - break; - case 12: - case 13: - strcpy(newtext,"4d4"); - break; - case 14: - strcpy(newtext,"4d4+1"); - break; - case 15: - strcpy(newtext,"5d4"); - break; - case 16: - case 17: - strcpy(newtext,"4d6"); - break; - case 18: - strcpy(newtext,"6d4"); - break; - case 19: - strcpy(newtext,"5d6"); - break; - case 20: - strcpy(newtext,"8d4"); - break; - default: - strcpy(newtext,"6d6"); - break; - } - if (!streq(newtext, f->text)) { - changeflagtext(f, newtext); - if (isplayer(lf)) msg("^gYour unarmed attack damage has increased!"); - } + killflagsofid(lf->flags, F_HASNEWLEVEL); + // ready for another level? + if (lf->xp >= getxpforlev(lf->level + 1)) { + gainlevel(lf); // this will increment 'newlevel' } - // enhance # attacks - f = lfhasflag(lf, F_MAXATTACKS); - if (f) { - int min,max; - min = f->val[0]; - max = f->val[1]; - if ((lf->level >= 2) && (lf->level <= 3)) { - min = 1; max = 1; - } else if ((lf->level >= 4) && (lf->level <= 6)) { - min = 1; max = 2; - } else if ((lf->level >= 7) && (lf->level <= 9)) { - min = 2; max = 2; - } else if ((lf->level >= 10) && (lf->level <= 12)) { - min = 2; max = 3; - } else if ((lf->level >= 13) && (lf->level <= 16)) { - min = 3; max = 3; - } else if (lf->level >= 17) { - min = 3; max = 4; - } - if ((min != f->val[0]) || (max != f->val[1])) { - f->val[0] = min; - f->val[1] = max; - if (isplayer(lf)) msg("^gYour number of unarmed attacks has increased!"); - } - } - } - - // psionics sometimes lets you learn spells - slev = getskill(lf, SK_SS_MENTAL); - if (pctchance(slev*10)) { - // construct list of castable mental spells - makespellchoicelist(&prompt, lf, "Learn which new psionic power:","Describe which psionic power:", SS_MENTAL, B_TRUE, B_FALSE, player->maxmp); - if (prompt.nchoices > 0) { - objecttype_t *ot; - msg("You have developed a new psionic power!"); more(); - getchoicestr(&prompt, B_TRUE, B_TRUE); - ot = prompt.result; - if (ot) { - if (prompt.whichq == 0) { // learn the spell - addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL); - } else { - describespell(ot); - } - } - } - - } - - killflagsofid(lf->flags, F_HASNEWLEVEL); - - // ready for another level? - if (lf->xp >= getxpforlev(lf->level + 1)) { - gainlevel(lf); // this will increment 'newlevel' - } + } // end if gainedxplev } void extinguishlf(lifeform_t *lf) { @@ -3605,7 +3645,7 @@ void gainhp(lifeform_t *lf, int amt) { void gainlevel(lifeform_t *lf) { flag_t *f; race_t *mybaserace; - int skillready = B_FALSE; + //int skillready = B_FALSE; //int preready,postready; if (isplayer(lf)) { @@ -3633,12 +3673,10 @@ void gainlevel(lifeform_t *lf) { } } - // skill gain - // new skill at level 2, 4, 6, etc - //if ((lf->newlevel % 2) == 0) { + // auto skill gain for monsters + if (!isplayer(lf)) { lf->skillpoints++; - skillready = B_TRUE; -// } + } //if (postready && !preready) { /* @@ -3648,11 +3686,7 @@ void gainlevel(lifeform_t *lf) { */ if (isplayer(lf)) { - if (skillready) { - msg("^GYou feel ready to learn a new skill!"); - } else { - msg("^GYou feel ready for a training session!"); - } + msg("^GYou are ready to train a new experience level!"); more(); } else if (cansee(player, lf)) { //getlfname(lf, buf); @@ -3718,23 +3752,36 @@ void gainmp(lifeform_t *lf, int amt) { } void gainxp(lifeform_t *lf, long amt) { - if (lfhasflag(lf, F_HASNEWLEVEL)) { - // can't gain any more xp until you do training - return; + int newskillpoints = 0; + int doxp = B_TRUE; + if (lfhasflag(lf, F_HASNEWLEVEL) || (lf->level == 0)) { + doxp = B_FALSE; + } + + if (doxp) { + lf->xp += amt; + if (isplayer(lf)) statdirty = B_TRUE; } + // skill xp if (isplayer(lf)) { - statdirty = B_TRUE; + lf->skillxp += amt; + while (lf->skillxp >= SKILLXPPERPOINT) { + newskillpoints++; + lf->skillxp -= SKILLXPPERPOINT; + if (isplayer(lf)) statdirty = B_TRUE; + } } - // level 0 lifeforms can't gain xp - if (lf->level == 0) return; - - lf->xp += amt; - - // ready for next level? can only go up ONE level. - if (lf->xp >= getxpforlev(lf->level + 1)) { - gainlevel(lf); // this will increment 'newlevel' + if (doxp) { + // ready for next level? can only go up ONE level. + if (lf->xp >= getxpforlev(lf->level + 1)) { + gainlevel(lf); // this will increment 'newlevel' + } + } + if (newskillpoints) { + lf->skillpoints += newskillpoints; + msg("^GYou feel ready to learn a new skill!"); } } @@ -3782,10 +3829,28 @@ int getactspeed(lifeform_t *lf) { return speed; } +int getadjenemies(lifeform_t *who, lifeform_t **adjlf, int *nadjlfs) { + int d,nadj = 0; + for (d = DC_N; d <= DC_NW; d++) { + cell_t *c; + c = getcellindir(who->cell, d); + if (c && c->lf && !isdead(c->lf) && areenemies(who, c->lf)) { + if (cansee(who, c->lf) && haslof(c->lf->cell, who->cell, LOF_WALLSTOP, NULL)) { + if (adjlf) { + adjlf[*nadjlfs] = c->lf; + (*nadjlfs)++; + } + nadj++; + } + } + } + return nadj; +} + // include allies or enemies which will follow you up/down stairs etc // ie. allies within LOS // ie. adjacent enemies -void getadjallies(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *nadjallies) { +void getwhowillfollow(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *nadjallies) { int x,y; for (y = 0; y < lf->cell->map->h; y++) { for (x = 0; x < lf->cell->map->w; x++) { @@ -4547,7 +4612,7 @@ int getfootprinttime(lifeform_t *lf) { } if (time > 0) { - if (getskill(lf, SK_TRACKING) == PR_EXPERT) { + if (getskill(lf, SK_PERCEPTION) == PR_EXPERT) { time /= 2; limit(&time, 1, NA); } @@ -4804,7 +4869,7 @@ int getlfaccuracy(lifeform_t *lf, object_t *wep) { } // modify for being on the ground if (isprone(lf)) { - acc -= 30; + acc -= 40; } // modify for swimming if (isswimming(lf) && !isaquatic(lf)) { @@ -4906,6 +4971,16 @@ enum LFCONDITION getlfcondition(lifeform_t *lf) { return C_DEAD; } +enum SKILLLEVEL getmaxskilllevel(lifeform_t *lf, enum SKILL skid) { + flag_t *f; + enum SKILLLEVEL maxlev = PR_MASTER; + f = lfhasflagval(lf, F_CANLEARN, skid, NA, NA, NULL); + if (f && (f->val[1] != NA)) { + maxlev = f->val[1]; + } + return maxlev; +} + int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions) { flag_t *f; lifeform_t *min; @@ -5240,6 +5315,11 @@ int getvisrange(lifeform_t *lf) { range = MAXVISRANGE; } + // can't see as far if you're on the ground + if (isprone(lf)) { + range /= 2; + } + // modify for darkness outside ? if ((range > 0) && isoutdoors(lf->cell->map)) { int hours,mins,secs; @@ -5259,7 +5339,8 @@ int getvisrange(lifeform_t *lf) { } } - // modifications? + + // other modifications? getflags(lf->flags, retflag, &nretflags, F_VISRANGEMOD, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; @@ -5268,7 +5349,7 @@ int getvisrange(lifeform_t *lf) { } } - if (range < 0) range = 0; + limit(&range, 0, MAXVISRANGE); return range; } @@ -6013,6 +6094,13 @@ enum SKILLLEVEL getskill(lifeform_t *lf, enum SKILL id) { return PR_INEPT; } +int getskilllevcost(enum SKILLLEVEL slev) { + int cost; + cost = slev - 1; + limit(&cost, 1, NA); + return cost; +} + int getsounddist(int volume) { return (3 * volume); } @@ -6405,8 +6493,6 @@ void givejob(lifeform_t *lf, enum JOB jobid) { int rollatt[MAXATTS]; int db = B_FALSE; - if (jobid == J_WIZARD) db = B_TRUE; - if (db) dblog("givejob() starting.\n"); for (i = 0; i < MAXATTS; i++) { @@ -6715,6 +6801,12 @@ int giveskill(lifeform_t *lf, enum SKILL id) { newf = addflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL); newf->lifetime = FROMSKILL; } + } else if (id == SK_SPELLCASTING) { + newf = hasflagval(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL); + if (!newf) { + newf = addflag(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL); + newf->lifetime = FROMSKILL; + } } else if (id == SK_THIEVERY) { newf = addflag(lf->flags, F_CANWILL, OT_A_STEAL, NA, NA, NULL); newf->lifetime = FROMSKILL; @@ -6795,6 +6887,14 @@ int giveskill(lifeform_t *lf, enum SKILL id) { */ } + if (isweaponskill(id)) { + if (f->val[1] == PR_MASTER) { + if (!hasflagval(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL)) { + addflag(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL); + } + } + } + // announecments based on skill level if ( (gamemode == GM_GAMESTARTED) && isplayer(lf)) { int i; @@ -7928,20 +8028,20 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_FIRSTAID, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SWIMMING, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_TECHUSAGE, PR_BEGINNER, NA, NULL); - addflag(lastjob->flags, F_STARTSKILL, SK_TRACKING, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_PERCEPTION, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LORE_HUMANOID, PR_SKILLED, NA, NULL); // learnable skills - addflag(lastjob->flags, F_CANLEARN, SK_BACKSTAB, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_BACKSTAB, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, PR_EXPERT, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CLIMBING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_FIRSTAID, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_FIRSTAID, PR_SKILLED, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_METALWORK, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_STEALTH, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_THROWING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_UNARMED, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_STEALTH, PR_SKILLED, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_THROWING, PR_EXPERT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_UNARMED, PR_SKILLED, NA, NULL); // abilities addjob(J_DRUID, "Druid"); // stats @@ -7961,7 +8061,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LORE_NATURE, PR_SKILLED, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_STAVES, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_CARTOGRAPHY, PR_ADEPT, NA, NULL); - addflag(lastjob->flags, F_STARTSKILL, SK_TRACKING, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_PERCEPTION, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SPEECH, PR_ADEPT, NA, NULL); // learnable skills addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); @@ -7970,8 +8070,8 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_SWIMMING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_THROWING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_RANGED, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_CLUBS, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_CLUBS, PR_ADEPT, NA, NULL); // abilities mayusespellschool(lastjob->flags, SS_NATURE, F_CANCAST); addflag(lastjob->flags, F_HASPET, NA, NA, NA, "young wolf"); @@ -8000,12 +8100,11 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_ATHLETICS, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_CARTOGRAPHY, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_EVASION, PR_NOVICE, NA, NULL); - addflag(lastjob->flags, F_STARTSKILL, SK_SPOTHIDDEN, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_PERCEPTION, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_STEALTH, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_UNARMED, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SS_MENTAL, PR_NOVICE, NA, NULL); // learnable skills - addflag(lastjob->flags, F_CANLEARN, SK_BACKSTAB, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CLIMBING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_FIRSTAID, NA, NA, NULL); @@ -8013,7 +8112,7 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_SEWING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SWIMMING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_THROWING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_TRAPS, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_TRAPS, PR_ADEPT, NA, NULL); // abilities addflag(lastjob->flags, F_VEGETARIAN, B_TRUE, NA, NA, NULL); addflag(lastjob->flags, F_CANWILL, OT_A_FEIGNDEATH, NA, NA, NULL); @@ -8065,9 +8164,9 @@ void initjobs(void) { addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_METALWORK, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_TECHUSAGE, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SPELLCASTING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SS_FIRE, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SS_COLD, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SPELLCASTING, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SS_FIRE, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SS_COLD, PR_ADEPT, NA, NULL); // abilities addflag(lastjob->flags, F_EVASION, 30, NA, NA, NULL); addflag(lastjob->flags, F_OBESE, B_TRUE, NA, NA, NULL); @@ -8096,15 +8195,15 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_SWIMMING, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SPEECH, PR_SKILLED, NA, NULL); // learnable skills - addflag(lastjob->flags, F_CANLEARN, SK_ATHLETICS, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_ATHLETICS, PR_ADEPT, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, PR_EXPERT, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_METALWORK, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SEWING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LORE_ARCANA, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_POLEARMS, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_TRACKING, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_POLEARMS, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_PERCEPTION, NA, NA, NULL); // abilities addflag(lastjob->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); @@ -8132,16 +8231,16 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LORE_HUMANOID, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LORE_UNDEAD, PR_NOVICE, NA, NULL); // learnable skills - addflag(lastjob->flags, F_CANLEARN, SK_ATHLETICS, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_BACKSTAB, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_LORE_ARCANA, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_ATHLETICS, PR_EXPERT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_BACKSTAB, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, PR_EXPERT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, PR_SKILLED, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_LORE_ARCANA, PR_SKILLED, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SEWING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_STEALTH, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_THIEVERY, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_THROWING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_TRACKING, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_STEALTH, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_THIEVERY, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_THROWING, PR_SKILLED, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_PERCEPTION, NA, NA, NULL); // abilities addflag(lastjob->flags, F_STABILITY, B_TRUE, NA, NA, NULL); addflag(lastjob->flags, F_NOBODYPART, BP_LEFTHAND, NA, NA, NULL); @@ -8174,27 +8273,29 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LISTEN, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_BACKSTAB, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LOCKPICKING, PR_BEGINNER, NA, NULL); - addflag(lastjob->flags, F_STARTSKILL, SK_SPOTHIDDEN, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_PERCEPTION, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SHORTBLADES, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_THIEVERY, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LORE_HUMANOID, PR_BEGINNER, NA, NULL); // learnable skills - addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, PR_SKILLED, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SPEECH, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_STAVES, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, PR_SKILLED, NA, NULL); // + addflag(lastjob->flags, F_CANLEARN, SK_STAVES, PR_ADEPT, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SWIMMING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_UNARMED, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_UNARMED, PR_EXPERT, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_TECHUSAGE, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_TRACKING, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_PERCEPTION, PR_SKILLED, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_THROWING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_TWOWEAPON, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SS_DIVINATION, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SS_GRAVITY, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SS_MODIFICATION, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SS_MENTAL, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SS_TRANSLOCATION, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_LORE_ARCANA, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_TWOWEAPON, PR_EXPERT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SPELLCASTING, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SS_DIVINATION, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SS_GRAVITY, PR_BEGINNER, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SS_MODIFICATION, PR_BEGINNER, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SS_MENTAL, PR_BEGINNER, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SS_TRANSLOCATION, PR_ADEPT, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_LORE_ARCANA, PR_ADEPT, NA, NULL); // abilities addflag(lastjob->flags, F_MPDICE, 1, NA, NA, NULL); addflag(lastjob->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); @@ -8226,9 +8327,9 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LORE_NATURE, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SEWING, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_METALWORK, PR_NOVICE, NA, NULL); - addflag(lastjob->flags, F_STARTSKILL, SK_TRACKING, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_PERCEPTION, PR_NOVICE, NA, NULL); // learnable skills - addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, PR_SKILLED, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CLIMBING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_COOKING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); @@ -8278,7 +8379,7 @@ void initjobs(void) { // learnable skills addflag(lastjob->flags, F_CANLEARN, SK_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CHANNELING, NA, NA, NULL); - addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_SHORTBLADES, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SEWING, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SPEECH, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_SS_AIR, NA, NA, NULL); @@ -8881,7 +8982,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_A_HEAVYBLOW, NA, NA, NULL); addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_GIANTFIRE, "fire giant", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -8912,7 +9013,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 4, NA, "bellows^a bellow"); addflag(lastrace->flags, F_FLEEONHPPCT, 50, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_GIANTFIREFC, "fire giant forgecaller", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GIANTFIRE; @@ -8949,7 +9050,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_HASSKILL, SK_SS_FIRE, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_GIANTFIRETITAN, "fire titan", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -8978,7 +9079,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_S_BURNINGWAVE, 3, 3, "pw:6;"); addflag(lastrace->flags, F_DTRESIST, DT_FIRE, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); // TODO: storm giant // TODO: storm titan @@ -9036,7 +9137,7 @@ void initrace(void) { addflag(lastrace->flags, F_PACKATTACK, 3, NA, 2, NULL); addflag(lastrace->flags, F_MINIONS, 75, 1, 2, "gnoll"); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_GNOLLMR, "gnoll marauder", 130, 'h', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GNOLL; @@ -9121,7 +9222,7 @@ void initrace(void) { addflag(lastrace->flags, F_PACKATTACK, 2, DT_SLASH, 3, NULL); addflag(lastrace->flags, F_MINIONS, 90, 1, 2, "goblin"); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_GOBLINSHOOTER, "goblin sharpshooter", 20, 'g', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GOBLIN; @@ -9152,7 +9253,7 @@ void initrace(void) { addflag(lastrace->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL); addflag(lastrace->flags, F_STARTHIDDENPCT, 75, NA, NA, NULL); addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_GOBLINHEXER, "goblin hexer", 20, 'g', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_GOBLIN; @@ -9322,7 +9423,7 @@ void initrace(void) { addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "spits"); addflag(lastrace->flags, F_CANWILL, OT_S_POISONBOLT, 5, 5, "pw:5;"); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_LURKINGHORROR, "lurking horror", 100, 'U', C_MAGENTA, MT_FLESH, RC_DEMON); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -9372,7 +9473,7 @@ void initrace(void) { addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "+2 heavy flail"); addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "greataxe"); addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_EXPERT, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_A_GRAB, NA, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_A_CHARGE, NA, NA, "range:5;"); addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "roars^a roar"); @@ -9438,7 +9539,7 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 20, NA, NA, NULL); addflag(lastrace->flags, F_MINIONS, 50, 1, 5, "orc"); addflag(lastrace->flags, F_MINIONS, 50, 1, 3, "orc warrior"); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_OGREWARHULK, "ogre warhulk", 160, 'O', C_BROWN, MT_FLESH, RC_HUMANOID); lastrace->baseid = R_OGRE; @@ -9583,7 +9684,7 @@ void initrace(void) { addflag(lastrace->flags, F_RESISTMAG, 5, NA, NA, NULL); addflag(lastrace->flags, F_MORALE, 25, NA, NA, NULL); addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_POLTERGEIST, "poltergeist", 50, 'p', C_GREEN, MT_FLESH, RC_UNDEAD); // sPirit addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -9643,7 +9744,7 @@ void initrace(void) { addflag(lastrace->flags, F_HASSKILL, SK_SS_MENTAL, PR_ADEPT, NA, NULL); addflag(lastrace->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL); addflag(lastrace->flags, F_STARTHIDDENPCT, 60, NA, NA, NULL); - addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_SKILLED, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL); addrace(R_SHADOWCAT, "shadowcat", 5, 'f', C_BLUE, MT_FLESH, RC_MAGIC); addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL); @@ -11128,6 +11229,8 @@ int isairborne(lifeform_t *lf) { return B_TRUE; } else if (lfhasflag(lf, F_LEVITATING)) { return B_TRUE; + } else if (lfhasflag(lf, F_ICESLIDE)) { + return B_TRUE; } return B_FALSE; } @@ -11442,8 +11545,7 @@ int isloreskill(enum SKILL skid) { } int ismaxedskill(lifeform_t *lf, enum SKILL skid) { - // TODO: implement skill maximums per job - if (getskill(lf, skid) == PR_MASTER) { + if (getskill(lf, skid) >= getmaxskilllevel(lf, skid)) { return B_TRUE; } return B_FALSE; @@ -11642,6 +11744,7 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { a->level = level; a->newlevel = level; a->xp = getxpforlev(a->level); + a->skillxp = 0; a->skillpoints = 0; a->cell = cell; a->alive = B_TRUE; @@ -11838,7 +11941,7 @@ void addtrail(lifeform_t *lf, int dir) { int fpdir; enum SKILLLEVEL slev; - slev = getskill(lf, SK_TRACKING); + slev = getskill(lf, SK_PERCEPTION); if (slev == PR_MASTER) { // no footprints! return; @@ -14212,6 +14315,10 @@ int startresting(lifeform_t *lf, int willtrain) { drawmsg(); drawcursor(); } + + // do the first one right away + rest(lf, B_TRUE); + return B_FALSE; } @@ -14901,10 +15008,18 @@ void initskills(void) { addskilldesc(SK_SPEECH, PR_EXPERT, "^gShop item prices are reduced by 25%.", B_FALSE); addskilldesc(SK_SPEECH, PR_MASTER, "^gShop item prices are reduced by 30%.", B_FALSE); addskill(SK_SPELLCASTING, "Sorcery", "Increases the power of spells from all schools except Allomancy, Nature and Psionics.", 50); - addskill(SK_SPOTHIDDEN, "Searching", "Helps you to spot hidden traps or creatures.", 50); + addskilldesc(SK_SPELLCASTING, PR_NOVICE, "^gYou gain the 'study scrolls' ability.", B_FALSE); + addskill(SK_PERCEPTION, "Perception", "Your ability to notice hidden details, from simple footprints to sinister traps.", 50); + addskilldesc(SK_PERCEPTION, PR_INEPT, "- At higher levels this skill will also let you obscure your own tracks.", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_NOVICE, "^gYou can now see footprints.", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_BEGINNER, "^gYou can now determine how recently footprints were made.", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_ADEPT, "^gYou can now identify creatures from their footprints.", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_SKILLED, "^gYou can now recognise the direction of footprints.", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_EXPERT, "^gYou can now partially obscure your own footprints.", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_MASTER, "^gYou can now move without leaving footprints.", B_TRUE); addskill(SK_STEALTH, "Stealth", "Affects your ability to move silently.", 0); // untrainable? addskilldesc(SK_STEALTH, PR_BEGINNER, "^gYou gain the 'hide' ability.", B_FALSE); - addskilldesc(SK_STEALTH, PR_EXPERT, "^gYou can now hide even when mosnters are nearby.", B_TRUE); + addskilldesc(SK_STEALTH, PR_EXPERT, "^gYou can now hide even when monsters are nearby.", B_TRUE); addskill(SK_SWIMMING, "Swimming", "Allows you to safely swim through deep water.", 50); addskilldesc(SK_SWIMMING, PR_NOVICE, "^gYou can now swim.", B_TRUE); addskilldesc(SK_SWIMMING, PR_BEGINNER, "^gYou can now swim a bit faster.", B_TRUE); @@ -14927,13 +15042,6 @@ void initskills(void) { addskilldesc(SK_RANGED, PR_SKILLED, "^gYou gain a +10% accuracy bonus when throwing items.", B_FALSE); addskilldesc(SK_RANGED, PR_EXPERT, "^gYou gain a +20% accuracy bonus when throwing items.", B_FALSE); addskilldesc(SK_RANGED, PR_MASTER, "^gYou gain a +30% accuracy bonus when throwing items.", B_FALSE); - addskill(SK_TRACKING, "Tracking", "Allows you to track enemies by their footprints.", 0); // untrain - addskilldesc(SK_TRACKING, PR_NOVICE, "^gYou can now see footprints.", B_TRUE); - addskilldesc(SK_TRACKING, PR_BEGINNER, "^gYou can now determine how recently footprints were made.", B_TRUE); - addskilldesc(SK_TRACKING, PR_ADEPT, "^gYou can now identify creatures from their footprints.", B_TRUE); - addskilldesc(SK_TRACKING, PR_SKILLED, "^gYou can now recognise the direction of footprints.", B_TRUE); - addskilldesc(SK_TRACKING, PR_EXPERT, "^gYou can now partially obscure your own footprints.", B_TRUE); - addskilldesc(SK_TRACKING, PR_MASTER, "^gYou can now move without leaving footprints.", B_TRUE); addskill(SK_TRAPS, "Traps", "Affects your ability to locate and disarm traps.", 25); addskilldesc(SK_TRAPS, PR_NOVICE, "^gYou gain the 'disarm traps' ability.", B_FALSE); addskill(SK_TWOWEAPON, "Dual Weilding", "Allows you to weild two melee weapons at once.", 50); @@ -14950,30 +15058,6 @@ void initskills(void) { addskill(SK_LORE_NATURE, "Lore:Nature", "Determines your knowledge of plants, animals and insects.", 0); addskill(SK_LORE_UNDEAD, "Lore:Undead", "Determines your knowledge of the undead.", 0); - for (sk = firstskill ; sk ; sk = sk->next) { - if (isloreskill(sk->id)) { - raceclass_t *rc; - // find raceclass for this one - for (rc = firstraceclass ; rc ; rc = rc->next) { - if (rc->skill == sk->id) { - break; - } - } - if (rc) { - char buf[BUFLEN]; - sprintf(buf, "^gYou now know basic information about %s.", rc->pluralname); - addskilldesc(sk->id, PR_NOVICE, buf, B_TRUE); - sprintf(buf, "^gYou can now determine how much damage %s will deal.", rc->pluralname); - addskilldesc(sk->id, PR_BEGINNER, buf, B_TRUE); - sprintf(buf, "^gYou can now determine how dangerous %s are.", rc->pluralname); - addskilldesc(sk->id, PR_ADEPT, buf, B_TRUE); - sprintf(buf, "^gYou can now anticipate how %s will react.", rc->pluralname); - addskilldesc(sk->id, PR_SKILLED, buf, B_TRUE); - sprintf(buf, "^gYou now know everything there is to know about %s.", rc->pluralname); - addskilldesc(sk->id, PR_MASTER, buf, B_TRUE); - } - } - } // weaponry addskill(SK_AXES, "Axes", "Helps you use chopping weapons like axes.", 50); @@ -14985,6 +15069,7 @@ void initskills(void) { addskill(SK_UNARMED, "Unarmed Combat", "Helps you fight using your bare hands.", 50); addskilldesc(SK_UNARMED, PR_ADEPT, "^gYour unarmed attacks can now smash wooden objects.", B_TRUE); addskilldesc(SK_UNARMED, PR_SKILLED, "^gYou can now make melee attacks with your off-hand.", B_TRUE); + // spell schools addskill(SK_SS_ALLOMANCY, "Allomancy", "Boosts casting of spells from this school.", 50); addskilldesc(SK_SS_ALLOMANCY, PR_NOVICE, "You gain knowledge of all Allomancy spells.", B_FALSE); @@ -15093,6 +15178,41 @@ void initskills(void) { addskilldesc(SK_SS_WILD, PR_SKILLED, "Allows you to cast Wild Magic spells up to level 4.", B_FALSE); addskilldesc(SK_SS_WILD, PR_EXPERT, "Allows you to cast Wild Magic spells up to level 5.", B_FALSE); addskilldesc(SK_SS_WILD, PR_MASTER, "Allows you to cast Wild Magic spells up to level 6.", B_FALSE); + + for (sk = firstskill ; sk ; sk = sk->next) { + if (isloreskill(sk->id)) { + raceclass_t *rc; + // find raceclass for this one + for (rc = firstraceclass ; rc ; rc = rc->next) { + if (rc->skill == sk->id) { + break; + } + } + if (rc) { + char buf[BUFLEN]; + sprintf(buf, "^gYou now know basic information about %s.", rc->pluralname); + addskilldesc(sk->id, PR_NOVICE, buf, B_TRUE); + sprintf(buf, "^gYou can now determine how much damage %s will deal.", rc->pluralname); + addskilldesc(sk->id, PR_BEGINNER, buf, B_TRUE); + sprintf(buf, "^gYou can now determine how dangerous %s are.", rc->pluralname); + addskilldesc(sk->id, PR_ADEPT, buf, B_TRUE); + sprintf(buf, "^gYou can now anticipate how %s will react.", rc->pluralname); + addskilldesc(sk->id, PR_SKILLED, buf, B_TRUE); + sprintf(buf, "^gYou now know everything there is to know about %s.", rc->pluralname); + addskilldesc(sk->id, PR_MASTER, buf, B_TRUE); + } + } + + if (isweaponskill(sk->id) || (sk->id == SK_UNARMED)) { + addskilldesc(sk->id, PR_INEPT, "This skill increases your accuracy and damage when using matching weapons.", B_FALSE); + addskilldesc(sk->id, PR_NOVICE, "^g-10% accuracy penalty.", B_FALSE); + addskilldesc(sk->id, PR_BEGINNER, "^g-5% accuracy penalty, +10% damage bonus", B_FALSE); + addskilldesc(sk->id, PR_ADEPT, "^gNo accuracy penalty, +20% damage bonus.", B_FALSE); + addskilldesc(sk->id, PR_SKILLED, "^g+25% accuracy bonus, +30% damage bonus.", B_FALSE); + addskilldesc(sk->id, PR_EXPERT, "^g+50% accuracy bonus, +40% damage bonus.", B_FALSE); + addskilldesc(sk->id, PR_MASTER, "^g+50% damage bonus, and you can perform combination strikes.", B_FALSE); + } + } } void interrupt(lifeform_t *lf) { @@ -15219,7 +15339,8 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r attrib = getattr(lf, A_IQ); break; case SC_CHA: - attrib = getattr(lf, A_CHA); + // if you're bleeding you're less attractive! + attrib = pctof(gethppct(lf), getattr(lf, A_CHA)); break; case SC_WIS: attrib = getattr(lf, A_WIS); @@ -15234,6 +15355,9 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r case SC_TUMBLE: attrib = getskill(lf, SK_ATHLETICS); break; + case SC_LEARNMAGIC: + attrib = (getattr(lf, A_IQ) / 2) + (getskill(lf, SK_SPELLCASTING)/2) + lf->level; + break; case SC_MORALE: // based on level/hitdice and size. attrib = (lf->hp / 4); attrib += ((float)getlfsize(lf) * 1.5); @@ -15276,13 +15400,13 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r attrib = (getattr(lf, A_CON)/6) + (getattr(lf, A_WIS)/6) + getmr(lf); break; case SC_SEARCH: - attrib = (getskill(lf, SK_SPOTHIDDEN)*4); + attrib = (getskill(lf, SK_PERCEPTION)*3); break; case SC_STEAL: attrib = (getskill(lf, SK_THIEVERY)); break; case SC_STEALTH: - attrib = (getskill(lf, SK_STEALTH)*4); + attrib = (getskill(lf, SK_STEALTH)*3); break; } @@ -15873,6 +15997,10 @@ int takeoff(lifeform_t *lf, object_t *o) { // lose flags loseobflags(lf, o, F_EQUIPCONFER); + if (o->type->id == OT_ICESHIELD) { + stopspell(lf, OT_S_CRYSTALSHIELD); + } + if (obproduceslight(o)) { calclight((getoblocation(o))->map); precalclos(lf); @@ -16005,6 +16133,10 @@ void timeeffectslf(lifeform_t *lf) { killob(o); continue; } + if ((o->type->id == OT_ICESHIELD) && !hasactivespell(lf, OT_S_CRYSTALSHIELD)) { + killob(o); + continue; + } timeeffectsob(o); } @@ -16240,9 +16372,22 @@ void turneffectslf(lifeform_t *lf) { } } - if (hasactivespell(lf, OT_S_SUMMONWEAPON)) { - if (!hasob(lf->pack, OT_ENERGYBLADE)) { - stopspell(lf, OT_S_SUMMONWEAPON); + if (hasactivespell(lf, OT_S_SUMMONWEAPON) && !hasob(lf->pack, OT_ENERGYBLADE)) { + stopspell(lf, OT_S_SUMMONWEAPON); + } + if (hasactivespell(lf, OT_S_CRYSTALSHIELD) && !hasob(lf->pack, OT_ICESHIELD)) { + stopspell(lf, OT_S_CRYSTALSHIELD); + } + + if (hasactivespell(lf, OT_S_SLIDE)) { + f = lfhasflag(lf, F_ICESLIDE); + if (f) { + f->val[0]--; + if (f->val[0] <= 0) { + stopspell(lf, OT_S_SLIDE); + } + } else { + stopspell(lf, OT_S_SLIDE); } } @@ -16401,7 +16546,7 @@ void turneffectslf(lifeform_t *lf) { getlfname(lf, lfname); msg("You think %s has spotted you!", lfname); } - practice(lf, SK_SPOTHIDDEN, 1); + practice(lf, SK_PERCEPTION, 1); } } } @@ -16428,11 +16573,12 @@ void turneffectslf(lifeform_t *lf) { // reveal it getobname(o, obname, o->amt); msg("^wYou notice %s!",obname); + interrupt(lf); killflag(f); needredraw = B_TRUE; drawscreen(); // train skills - practice(lf, SK_SPOTHIDDEN, 1); + practice(lf, SK_PERCEPTION, 1); if (hasflag(o->flags, F_TRAP)) { practice(lf, SK_TRAPS, 1); } @@ -16456,11 +16602,12 @@ void turneffectslf(lifeform_t *lf) { // reveal it getobname(o, obname, o->amt); msg("^wYou notice a trap on %s!",obname); + interrupt(lf); f->val[2] = B_TRUE; needredraw = B_TRUE; drawscreen(); // train skills - practice(lf, SK_SPOTHIDDEN, 1); + practice(lf, SK_PERCEPTION, 1); if (hasflag(o->flags, F_TRAP)) { practice(lf, SK_TRAPS, 1); } @@ -17134,9 +17281,9 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { } // find adjacent allies or enemies which will follow you - // (getadjallies will handle following through pits) + // (getwhowillfollow will handle following through pits) if (isplayer(lf)) { - getadjallies(lf, o, adjally, &nadjallies); + getwhowillfollow(lf, o, adjally, &nadjallies); } // do stairs go somewhere? @@ -17948,7 +18095,7 @@ int wear(lifeform_t *lf, object_t *o) { // warn if it will be cumbersome if (isplayer(lf)) { - f = hasflag(o->flags, F_SHIELD); + f = hasflagval(o->flags, F_EQUIPCONFER, F_SHIELDPENALTY, NA, NA, NULL); if (f && (getskill(lf, SK_SHIELDS) <= PR_INEPT) ) { msg("^wYou find this shield very cumbersome to use."); } diff --git a/lf.h b/lf.h index dd583a5..6b0874d 100644 --- a/lf.h +++ b/lf.h @@ -47,6 +47,7 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar int celllitfor(lifeform_t *lf, cell_t *c, int maxvisrange, int nightvisrange); int celltransparentfor(lifeform_t *lf, cell_t *c, int *xray, int *rangemod); int checkfordrowning(lifeform_t *lf, object_t *o); +int check_rest_ok(lifeform_t *lf); //void checkxp(enum RACE rid); float comparelfs(lifeform_t *lf1, lifeform_t *lf2); int countinnateattacks(lifeform_t *lf); @@ -91,7 +92,8 @@ void gainmp(lifeform_t *lf, int amt); void gainxp(lifeform_t *lf, long amt); void genxplist(void); int getactspeed(lifeform_t *lf); -void getadjallies(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *nadjallies); +int getadjenemies(lifeform_t *lf, lifeform_t **adjlf, int *nadjlfs); +void getwhowillfollow(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *nadjallies); enum ALIGNMENT getalignment(lifeform_t *lf); enum ALLEGIENCE getallegiance(lifeform_t *lf); int getallouterarmour(lifeform_t *lf, object_t **ob, int *nobs); @@ -131,6 +133,7 @@ int getlastdir(lifeform_t *lf); int getlfaccuracy(lifeform_t *lf, object_t *wep); char getlfcol(lifeform_t *lf, enum MSGCHARCOL cc); enum LFCONDITION getlfcondition(lifeform_t *lf); +enum SKILLLEVEL getmaxskilllevel(lifeform_t *lf, enum SKILL skid); int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions); int getnightvisrange(lifeform_t *lf); char *getlfconditionname(enum LFCONDITION cond); @@ -184,6 +187,7 @@ race_t *getreallyrandomrace(enum RACECLASS wantrc); enum SKILL getrandomskill(void); object_t *getrestob(lifeform_t *lf); enum SKILLLEVEL getskill(lifeform_t *lf, enum SKILL id); +int getskilllevcost(enum SKILLLEVEL slev); int getsounddist(int volume); char *getspeedname(int speed, char *buf); char *getspeednameshort(int speed, char *buf); diff --git a/map.c b/map.c index 536426c..373db7f 100644 --- a/map.c +++ b/map.c @@ -152,6 +152,7 @@ map_t *addmap(void) { a->beingcreated = B_TRUE; a->habitat = findhabitat(H_DUNGEON); // default!!! a->lastplayervisit = -1; + a->nfixedrooms = 0; return a; } @@ -262,7 +263,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int } } if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_DEAF) && cansleep(lf) ) { - int asleepchance = 50; + int asleepchance = 70; f = lfhasflag(lf, F_STARTASLEEPPCT); if (f) { asleepchance = f->val[0]; @@ -1785,8 +1786,15 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } } } + + // adjust min/maxrooms based on fixed vaults + minrooms -= map->nfixedrooms; + maxrooms -= map->nfixedrooms; + limit(&minrooms, 0, NA); + limit(&maxrooms, 0, NA); + // create rooms - if (wantrooms) { + if (wantrooms && (maxrooms > 0)) { numrooms = rnd(minrooms, maxrooms); //printf("using %d rooms\n",numrooms); @@ -1843,6 +1851,12 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ if (map->region->rtype->id == RG_FIRSTDUNGEON) { if (c->lf) killlf(c->lf); addobfast(c->obpile, OT_MAGICBARRIER); + // also clear all the cells around it to prevent reachability errors + for (d = DC_N; d <= DC_NW; d++) { + cell_t *c2; + c2 = getcellindir(c, d); + if (c2) setcelltype(c2, map->habitat->emptycelltype); + } } } else { for (i = 0; i < map->region->rtype->stairsperlev; i++) { @@ -2276,6 +2290,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex map->name = strdup(buf); // get a list of what things are here based on the region's outline + map->nfixedrooms = 0; nthings = 0; if (region->outline) { if (db) dblog(" checking region outline for things..."); @@ -2312,6 +2327,16 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex if (db) dblog(" remembering region thing for later."); // remember this thing thing[nthings] = ®ion->outline->thing[i]; + switch (thing[nthings]->whatkind) { + case RT_VAULT: + case RT_RNDVAULTWITHFLAG: + // this will reduce the amount of random rooms which can + // be created on this map. + map->nfixedrooms++; + break; + default: + break; + } nthings++; } } diff --git a/move.c b/move.c index 1f9cffc..37fcc7b 100644 --- a/move.c +++ b/move.c @@ -1419,18 +1419,7 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { for (o = newcell->obpile->first ; o ; o = nexto ) { nexto = o->next; if (hasflag(o->flags, F_TRAP)) { - char trapname[BUFLEN]; - getobname(o, trapname, 1); - if (haslos(player, newcell)) { - msg("%s trigger%s %s!", lfname, isplayer(lf) ? "" : "s", trapname); - if (isplayer(lf)) more(); - } - if (haslos(player, newcell)) { - // no longer hidden - killflagsofid(o->flags, F_SECRET); - } - // NOTE: after trapeffects(), o might be killed. - trapeffects(o, o->type->id, lf); + triggertrap(lf, NULL, o, lf->cell); interrupt(lf); } } @@ -2086,6 +2075,31 @@ int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) { return B_FALSE; } +void triggertrap(lifeform_t *lf, object_t *o, object_t *trapob, cell_t *where) { + char triggerer[BUFLEN]; + char trapname[BUFLEN]; + int wants; + getobname(trapob, trapname, 1); + if (lf) { + getlfname(lf, triggerer); + if (isplayer(lf)) wants = B_FALSE; + else wants = B_TRUE; + } else { + getobname(o, triggerer, o->amt); + if (o->amt == 1) wants = B_TRUE; + else wants = B_FALSE; + } + if (haslos(player, where)) { + msg("%s trigger%s %s!", triggerer, (wants) ? "s" : "", trapname); + if (lf && isplayer(lf)) more(); + + // no longer hidden + killflagsofid(trapob->flags, F_SECRET); + } + // NOTE: after trapeffects(), oo might be killed. + trapeffects(trapob, trapob->type->id, where); + if (lf) interrupt(lf); +} int trymove(lifeform_t *lf, int dir, int onpurpose) { cell_t *cell; @@ -2498,7 +2512,7 @@ int walkoffmap(lifeform_t *lf, int dir, int onpurpose) { if (onpurpose) { // get list of adjacent allies if (isplayer(lf)) { - getadjallies(lf, NULL, adjally, &nadjallies); + getwhowillfollow(lf, NULL, adjally, &nadjallies); } } diff --git a/move.h b/move.h index aa4961a..fb4bddc 100644 --- a/move.h +++ b/move.h @@ -27,6 +27,7 @@ int isorthogonal(int dir); int ispossiblemove(lifeform_t *lf, int dir); void swapplaces(lifeform_t *lf1, lifeform_t *lf2, int onpurpose); int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke); +void triggertrap(lifeform_t *lf, object_t *o, object_t *trapob, cell_t *where); int trymove(lifeform_t *lf, int dir, int onpurpose); int tryrun(lifeform_t *lf, int dir); int trysneak(lifeform_t *lf, int dir); diff --git a/nexus.c b/nexus.c index 8e3287c..06cd7b7 100644 --- a/nexus.c +++ b/nexus.c @@ -231,6 +231,7 @@ int main(int argc, char **argv) { // add abilities which the player always has addflag(player->flags, F_CANWILL, OT_A_PRAY, NA, NA, NULL); + addflag(player->flags, F_CANWILL, OT_A_TRAIN, NA, NA, NULL); user = getenv("USER"); if (user) { @@ -246,7 +247,7 @@ int main(int argc, char **argv) { skill_t *sk; initprompt(&prompt, "Select your spell specialty:"); addchoice(&prompt, 'a', getskillname(SK_SS_AIR), NULL, findskill(SK_SS_AIR)); - addchoice(&prompt, 'i', getskillname(SK_SS_COLD), NULL, findskill(SK_SS_COLD)); + addchoice(&prompt, 'c', getskillname(SK_SS_COLD), NULL, findskill(SK_SS_COLD)); addchoice(&prompt, 'f', getskillname(SK_SS_FIRE), NULL, findskill(SK_SS_FIRE)); getchoice(&prompt); sk = (skill_t *) prompt.result; @@ -256,7 +257,7 @@ int main(int argc, char **argv) { addflag(player->flags, F_CANCAST, OT_S_MIST, NA, NA, NULL); break; case SK_SS_COLD: - addflag(player->flags, F_CANCAST, OT_S_FROSTBITE, NA, NA, NULL); + addflag(player->flags, F_CANCAST, OT_S_CHILL, NA, NA, NULL); break; case SK_SS_FIRE: addflag(player->flags, F_CANCAST, OT_S_SPARK, NA, NA, NULL); diff --git a/objects.c b/objects.c index 0ca8f34..c5d1da0 100644 --- a/objects.c +++ b/objects.c @@ -2184,7 +2184,7 @@ int canseeob(lifeform_t *lf, object_t *o) { enum SKILLLEVEL slev; int cutoffpct; int cutoff; - slev = getskill(lf, SK_TRACKING); + slev = getskill(lf, SK_PERCEPTION); switch (slev) { case PR_NOVICE: cutoffpct = 80; break; case PR_BEGINNER: cutoffpct = 65; break; @@ -2524,7 +2524,7 @@ int doobtraps(object_t *o, lifeform_t *lf) { } trapid = f->val[0]; - trapeffects(NULL, trapid, lf); + trapeffects(NULL, trapid, lf->cell); killflag(f); // now the trap gets removed // explosion traps kill the object @@ -3099,10 +3099,8 @@ int getobaccuracy(object_t *wep, lifeform_t *weilder) { acc += 25; break; case PR_EXPERT: - acc += 50; - break; case PR_MASTER: - acc += 75; + acc += 50; break; } } @@ -4118,7 +4116,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan if (f) { race_t *r = NULL; enum SKILLLEVEL slev; - slev = getskill(player, SK_TRACKING); + slev = getskill(player, SK_PERCEPTION); r = findrace(f->val[0]); assert(r); @@ -5704,7 +5702,7 @@ void initobjects(void) { addflag(lastmaterial->flags, F_HARDNESS, 3, NA, NA, NULL); addmaterial(MT_OIL, "oil", 5); addmaterial(MT_ICE, "ice",6); - addflag(lastmaterial->flags, F_HARDNESS, 2, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 3, NA, NA, NULL); addmaterial(MT_WOOD, "wood", 6); addflag(lastmaterial->flags, F_HARDNESS, 3, NA, NA, NULL); addflag(lastmaterial->flags, F_FLAMMABLE, 5, NA, NA, NULL); @@ -5714,7 +5712,7 @@ void initobjects(void) { addmaterial(MT_BLOOD, "blood", 7); addmaterial(MT_SLIME, "slime", 9); addmaterial(MT_STONE, "stone", 10); - addflag(lastmaterial->flags, F_HARDNESS, 5, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 4, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_PIERCE, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_BITE, NA, NA, NULL); @@ -5722,7 +5720,7 @@ void initobjects(void) { addflag(lastmaterial->flags, F_DTRESIST, DT_CHOP, NA, NA, NULL); addflag(lastmaterial->flags, F_DTRESIST, DT_PROJECTILE, NA, NA, NULL); addmaterial(MT_METAL, "metal", 13); - addflag(lastmaterial->flags, F_HARDNESS, 6, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 5, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_PIERCE, NA, NA, NULL); addflag(lastmaterial->flags, F_DTIMMUNE, DT_BITE, NA, NA, NULL); @@ -5730,7 +5728,7 @@ void initobjects(void) { addflag(lastmaterial->flags, F_DTRESIST, DT_SLASH, NA, NA, NULL); addflag(lastmaterial->flags, F_DTRESIST, DT_PROJECTILE, NA, NA, NULL); addmaterial(MT_GLASS, "glass", 13); - addflag(lastmaterial->flags, F_HARDNESS, 3, NA, NA, NULL); + addflag(lastmaterial->flags, F_HARDNESS, 2, NA, NA, NULL); addflag(lastmaterial->flags, F_DTVULN, DT_BASH, NA, NA, NULL); addflag(lastmaterial->flags, F_DTVULN, DT_COLD, NA, NA, NULL); addmaterial(MT_GOLD, "gold", 16); @@ -5854,7 +5852,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LOCKABLE, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_CANBETRAPPED, 10, 5, 60, NULL); + addflag(lastot->flags, F_CANBETRAPPED, 5, 10, 60, NULL); addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL); addflag(lastot->flags, F_DTVULN, DT_BASH, NA, NA, NULL); addflag(lastot->flags, F_DTVULN, DT_CHOP, NA, NA, NULL); @@ -6598,6 +6596,7 @@ void initobjects(void) { /////////////////// // l1 addot(OT_S_ABSORBMETAL, "absorb metal", "Draws mana from nearby metallic objects.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the maximum amount of metal which can be absorbed."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MPCOST, 0, NA, NA, NULL); @@ -6606,12 +6605,15 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); addot(OT_S_ACCELMETAL, "accelerate metal", "Greatly accelerates a metal object thrown by the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how fast the object is propelled."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_WALLSTOP, NA, NULL); addot(OT_S_METALHEAL, "metal healing", "Uses nearby metal for accelerated healing.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how much damage is healed."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); @@ -6619,14 +6621,17 @@ void initobjects(void) { addot(OT_S_EXPLODEMETAL, "explode metal", "Causes all metal objects in a location explode.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_MAGSHIELD, "magnetic shield", "Surrounds the caster with magnetic force, repelling metal objects and attacks.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how long the magnetic shield will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); // l4 addot(OT_S_ANIMATEMETAL, "animate metal", "Imbues a metallic weapon with temporary life, enabling it to fight on its own.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ALLOMANCY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); /////////////////// @@ -6634,6 +6639,7 @@ void initobjects(void) { /////////////////// // l1 addot(OT_S_STENCH, "stench", "Nauseates the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability and duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 4, NA, NA, NULL); @@ -6641,6 +6647,7 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l2 addot(OT_S_BLINDNESS, "blindness", "Temporarily blinds the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability and duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -6648,10 +6655,12 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_COMMANDUNDEAD, "command undead", "Compels an undead creature to follow a single simple command.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_FEAR, "cause fear", "Causes intense fear in the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability and duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); @@ -6666,30 +6675,34 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l3 addot(OT_S_POISONBOLT, "venom bolt", "Fires a glob of venom at the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how difficult the venom is to dodge, and how powerful the poison is."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); addot(OT_S_DRAINLIFE, "drain life", "Draws life force from the victim in order to heal the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The amount of life force drained is ^b1d6+power^n."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); addot(OT_S_PAIN, "pain", "Causes extreme pain in the target whenever they move.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the duration of the pain effect."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 - addot(OT_S_WEAKEN, "weaken", "Temporarily lowers the target's muscle strength.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_WEAKEN, "weaken", "Temporarily lowers the target's muscle strength by ^bpower^n points.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the duration of the weakness effect."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - addot(OT_S_PARALYZE, "paralyze", "Disables the target's muscles, leaving them unable to move.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_PARALYZE, "paralyze", "Disables the target's muscles, leaving them unable to move for ^bpower*2^n turns.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -6697,11 +6710,13 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l5 addot(OT_S_ANIMATEDEAD, "animate dead", "Imbues nearby corpses with life, creating an undead zombie.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the chances of the zombies being friendly."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); // TODO: should be "castnearob ot_corpse" addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); addot(OT_S_FEEBLEMIND, "brain freeze", "Temporarily lowers the target's intelligence to that of an animal.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); @@ -6710,10 +6725,12 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l6 addot(OT_S_POSSESSION, "possession", "Completely possess an enemy, moving your consciousness into their body.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines its resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_INFINITEDEATH, "infinite death", "Annihilates all nearby life, including the caster!", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_CASTINGTIME, 2, NA, NA, NULL); @@ -6722,7 +6739,9 @@ void initobjects(void) { // divination /////////////////// // l1 - addot(OT_S_DETECTLIFE, "detect life", "Senses the size of creatures near the caster. At Power V, it will detect exact creature types.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_DETECTLIFE, "detect life", "Senses the size of creatures near the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the detection range."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power VI, exact creatures types are detected."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); // l2 @@ -6731,6 +6750,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addot(OT_S_MAPPING, "sense surroundings", "Magically imbues the caster with a map of his/her surroundings.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the detection range."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); // l3 @@ -6740,6 +6760,7 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addot(OT_S_DETECTOBS, "detect objects", "Senses objects near the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the detection range and spell duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -6747,20 +6768,30 @@ void initobjects(void) { addot(OT_S_DETECTAURA, "detect aura", "Senses holiness or evil near the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addot(OT_S_LORE, "lore", "Obtain knowledge about any one species.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power I, you gain Novice level knowledge."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power II, you gain Beginner level knowledge."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power IV, you gain Adept level knowledge."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power VI, you gain Skilled level knowledge."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power VIII, you gain Expert level knowledge."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power X, you gain Master level knowledge."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addot(OT_S_REVEALHIDDEN, "reveal hidden", "Reveals hidden doors or invisible creatures in the caster's line of sight.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); // l5 addot(OT_S_DETECTMAGIC, "detect magic", "Allows the caster to detect magical enchantments.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); // l6 addot(OT_S_IDENTIFY, "identification", "Completely identifies any one item.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); @@ -6769,6 +6800,7 @@ void initobjects(void) { /////////////////// // l7 addot(OT_S_ENCHANT, "enchantment", "Magically enhances a weapon or piece of armour.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); // TODO: hardcode how ai casts this spell @@ -6778,12 +6810,14 @@ void initobjects(void) { /////////////////// // l1 addot(OT_S_MIST, "pea soup", "Hides the caster from view with a thick cloud of mist.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how long the mist will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_ADJSELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_CALLWIND, "zephyr", "Conjures a friendly wind, carrying a single object to the caster's hands.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines its range and the weight of objects affected."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); @@ -6798,6 +6832,8 @@ void initobjects(void) { // l2 addot(OT_S_GUSTOFWIND, "gust of wind", "Causes a gust of wind to blow up to ^bpower^n of the target's objects away.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The chance of each object blowing away is determined by the spell's power."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell's power is boosted when cast outside."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -6808,16 +6844,19 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l3 addot(OT_S_AIRBLAST, "airblast", "Knocks enemies back with a powerful blast of air.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell's power determines how far objects or enemies will be knocked back."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_NEED, NA, NULL); - addot(OT_S_WINDSHIELD, "cyclonic shield", "Surrounds the caster with a whirling cyclone to repel missiles. Spell power determines the speed of missile which can be repelled.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_WINDSHIELD, "cyclonic shield", "Surrounds the caster with a whirling cyclone to repel missiles.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the speed of missile which can be repelled."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -6831,6 +6870,7 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); // l5 addot(OT_S_CHAINLIGHTNING, "chain lightning", "Electricity arcs up to 5 times between all nearby enemies. The initial arc deals 2d6 damage, the next deals 2d5 damage, etc.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's range is based on its power."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -6847,6 +6887,7 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l2 addot(OT_S_BLADEBURN, "bladeburn", "Ignites the target's bladed weapon, causing it to temporarily deal fire damage. The spell's power determines how long it will last.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines its duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -6858,12 +6899,14 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); // l3 - addot(OT_S_FLAMEBURST, "flame burst", "Creates a radial blast of fire out from the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_FLAMEBURST, "flame burst", "Creates a radial blast of fire out from the caster, inflicting 2d6 fire damage to all within.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The size of the radial blast is based on the spell's power."); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); // l4 addot(OT_S_FLAMEPILLAR, "flame pillar", "Creates a tall pillar of flame.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how long the flames will persist."); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); @@ -6871,13 +6914,15 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); // l5 addot(OT_S_BURNINGWAVE, "burning wave", "Fire bursts from the ground in a line towards the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's range is based on its power."); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_RANGE, 3, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); - addot(OT_S_FIREBALL, "fireball", "Creates a huge ball of fire.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_FIREBALL, "fireball", "Creates a huge ball of fire, dealing up to ^bpower^nd4 damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The damage is lower for enemies further away from the ball's centre."); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); // TODO: should be "near victim" @@ -6888,9 +6933,21 @@ void initobjects(void) { // l1 addot(OT_S_CHILL, "chill", "Deals minor (1d3) cold damage to a single target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_GLACIATE, "glaciate", "Slows down molecules in a given area, instantly freezing any water present. Has no effect on living creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_SNOWBALL, "snowball", "Throws a large snowball, dealing 1 cold damage to all within its blast. The remaining snow will quickly melt to water.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_NEED, NA, NULL); // l2 addot(OT_S_FREEZEOB, "freezing touch", "Changes the next thing touched into solid ice. The effect is permenant for inanimate objects, but will wear off on living creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); @@ -6898,36 +6955,55 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addot(OT_S_ICEEDGE, "ice edge", "Enhances the edge of a bladed weapon with a layer of ice.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how long the enchantment will remain."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - addot(OT_S_COLDRAY, "cold ray", "Shoots a blast of ice cold air, dealing 2-10 cold damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_CRYSTALSHIELD, "crystalline shield", "Summons a shield of ice crystals to protect you from missiles.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how effective the shield is."); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 6, NA, NA, NULL); + addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); + // l3 + addot(OT_S_COLDRAY, "cold ray", "Shoots a blast of ice cold air, dealing 2-10 cold damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how difficult the ray is to dodge."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); - // l3 - addot(OT_S_COLDBURST, "cold burst", "Creates a radial blast of coldness out from the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_COLDBURST, "cold burst", "Creates a radial blast of coldness out from the caster, dealing 1d8+3 cold damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's range is based on its power."); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); addot(OT_S_ICICLE, "icicle", "A huge icicle rises form the ground, knocking enemies away and blocking passage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the icicle will remain."); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addot(OT_S_SLIDE, "slipslide", "Generate ice underneath your feet, allowing you to slide along the ground at high speeds.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's duration is based on its power, however its effects will also end if you stop moving."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 - addot(OT_S_FROSTBITE, "frostbite", "Deals 3-24 cold damage to target creature per exposed body part.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_FROSTBITE, "frostbite", "Deals 1d3 cold damage to target creature per exposed body part.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_WALLOFICE, "wall of ice", "Creates an impassable wall of solid ice.", MT_ICE, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the wall will remain."); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); @@ -6937,6 +7013,7 @@ void initobjects(void) { /////////////////// // l1 addot(OT_S_CALMANIMALS, "calm animals", "Makes animals within the casters line of sight become peaceful.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how many hit dice worth of creatures will be affected."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); @@ -6962,6 +7039,7 @@ void initobjects(void) { addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addot(OT_S_CHARMANIMAL, "befriend animal", "Temporarily makes a single animal friendly to you.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how the maximum hit dice creature which can be affected."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); @@ -6978,11 +7056,13 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addot(OT_S_SOFTENEARTH, "soften earth", "Converts earth into mud, slowing down.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how much mud will be created."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_SUMMONANIMALSSM, "summon small animals", "Summons 2-3 small animals.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the summoned creatures will remain."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); @@ -7002,6 +7082,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_LIGHTNINGBOLT, "electricity bolt", "Fires a bolt of electricity through multiple enemies, dealing 2d6 damage to each.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's range is based on its power."); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); @@ -7011,18 +7092,23 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_QUENCH, "quench", "Extinguishes all fires within the given area.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_EVAPORATE, "evaporate", "Instantly converts all water in the given area into steam.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the amount of water affected."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_SLEETSTORM, "sleet storm", "Creates an cloud of sleet, hampering vision and movement.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the size of the storm."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell's power is boosted when cast outside."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); @@ -7036,6 +7122,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_S_WEB, "web", "Slows down pursuers with a burst of sticky spider web.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how much webbing is created."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -7043,10 +7130,12 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_NEED, NA, NULL); // l4 addot(OT_S_DIG, "dig", "Blasts away earth to create passages.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how much earth can be destroyed."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_S_ENTANGLE, "entangle", "Causes magical vines to hold enemies.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the strength of the vines."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -7061,19 +7150,23 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - addflag(lastot->flags, F_MAXPOWER, 10, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addot(OT_S_SUMMONANIMALSMD, "summon medium animals", "Summons 2-3 medium animals.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the summoned creatures will remain."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); addot(OT_S_WATERJET, "water spray", "Fires a high pressure blast of water from the caster's hands.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how far enemies are knocked back."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); // l5 addot(OT_S_HAILSTORM, "hail storm", "Creates an intense storm of hail, causing damage to all within.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the size of the storm."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell's power is boosted when cast outside."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); @@ -7083,16 +7176,19 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l6 addot(OT_S_LIGHTNINGSTORM, "lightning storm", "Blasts all visible enemies bolts of lightning from the sky.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how many bolts will appear."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); addot(OT_S_SUMMONANIMALSLG, "summon large animals", "Summons 2-3 large animals.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the summoned creatures will remain."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); addot(OT_S_FLOOD, "flood", "Creates a massive ball of water.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the amount of water which appears."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -7101,6 +7197,7 @@ void initobjects(void) { /////////////////// // l1 addot(OT_S_TRUESTRIKE, "weapon attraction", "Gives the target unerring accuracy, making their attacks always hit.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the amount of strikes before it expires."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); @@ -7110,9 +7207,11 @@ void initobjects(void) { addot(OT_S_GRAVLOWER, "lessen gravity", "Causes the caster to fall very slowly.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); // l3 addot(OT_S_SLOW, "slowness", "Decreases the speed of the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the slowness will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -7120,6 +7219,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); // l4 addot(OT_S_GRAVBOOST, "boost gravity", "Greatly increases gravity around the target, stopping them from moving.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long its effects will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -7128,6 +7228,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); // l5 // l6 @@ -7136,8 +7237,10 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_AIR, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addot(OT_S_HASTE, "haste", "Increases the speed of the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long its effects will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_GRAVITY, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); @@ -7147,12 +7250,13 @@ void initobjects(void) { /////////////////// // l1 addot(OT_S_HEALINGMIN, "minor healing", "Restores 1-8 health to the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell heals an extra 2 damage per power level."); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - addot(OT_S_TURNUNDEAD, "turn undead", "Instills fear in undead creatures. Power depends on caster's skill.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_TURNUNDEAD, "turn undead", "Instills fear in undead creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); // l2 @@ -7169,6 +7273,7 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l3 addot(OT_S_HEALING, "healing", "Restores 10-20 health to the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell heals an extra 2 damage per power level."); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -7203,18 +7308,22 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); // l2 - addot(OT_S_LOWERMETAB, "lower metabolism", "Slow your body's functions, decreasing your rate of hunger but also your speed. At power level V your speed is no longer affected.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_LOWERMETAB, "lower metabolism", "Slow your body's functions, decreasing your rate of hunger but also your movement speed.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power level III: your speed penalty is reduced."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power level V: you no longer suffer a speed penalty."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_TELEKINESIS, "telekinesis", "Mentally move or manipulate nearby objects.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell can lift up to 10kg per power level."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); // l3 addot(OT_S_PSYARMOUR, "psychic armour", "Mentally block incoming attacks.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The psychic armour's Armour Rating is ^bpower*4^n."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_VARPOWER, B_TRUE, NA, NA, NULL); @@ -7222,6 +7331,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); // TODO: hardcode how ai casts this addot(OT_S_PACIFY, "pacify", "Induces calmness in another, preventing them from attacking.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -7234,12 +7344,14 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 addot(OT_S_SLEEP, "sleep", "Puts the target creature to sleep.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how long the sleep effect will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_HEALINGMAJ, "major healing", "Restores 20-30 health to the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell heals an extra 2 damage per power level."); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); @@ -7247,6 +7359,7 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l5 addot(OT_S_CHARM, "charm", "Causes another lifeform to temporary become friendly.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability and duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); @@ -7264,12 +7377,15 @@ void initobjects(void) { addot(OT_S_INSCRIBE, "inscribe", "Creates a magical inscription viewable to anyone standing nearby.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); - addot(OT_S_KNOCK, "knock", "Magically opens doors or other such barriers.\nAt Power VII, this spell will also knock back living creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_KNOCK, "knock", "Magically opens doors or other such barriers.\n", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power VII, this spell will also knock back living creatures."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); - addot(OT_S_LIGHT, "light area", "Creates a temporary light source centred on the caster.\nAt Power III, you can control where the light appears.\nAt Power VIII, the light becomes permenant.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_LIGHT, "light area", "Creates a temporary light source centred on the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power III, you can control where the light appears."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power VIII, the light becomes permenant."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); @@ -7278,21 +7394,25 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); // l2 addot(OT_S_DARKNESS, "darkness", "Permenantly darkens the area around the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power III, you can control where the darkness appears."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power VIII, the darkness becomes permenant."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); - addot(OT_S_MENDING, "mending", "Repairs minor damage to objects.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_MENDING, "mending", "Repairs minor damage to objects (1d6 + ^bpower^n).", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addot(OT_S_GREASE, "grease", "Creates a large pool of greasy oil.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines the size of the grease pool."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); // l3 addot(OT_S_INVISIBILITY, "invisibility", "Temporarily renders the target invisible.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the invisibility will last."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -7301,6 +7421,7 @@ void initobjects(void) { //addflag(lastot->flags, F_XPVAL, 50, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_PASSWALL, "passwall", "Allows the caster to temporarily walk through a single wall.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines how long the caster can wait before entering a wall."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); @@ -7314,10 +7435,14 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); // l6 addot(OT_S_PETRIFY, "petrify", "Causes a living creature to turn into stone.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); - addot(OT_S_POLYMORPH, "polymorph", "Transmutes the target into a new living race.\nBecomes semi-controlled at Power V, and fully-controlled at power VIII.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_POLYMORPH, "polymorph", "Transmutes the target into a new living race.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power V, you can polymorph other creatures."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power VIII, you can choose what kind of creature to polymorph into."); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, 10, NA, NULL); @@ -7328,12 +7453,13 @@ void initobjects(void) { /////////////////// // l1 addot(OT_S_FLOATINGDISC, "floating disc", "Creates a disc of energy to carry your equipment.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power level determines how much wight the disc can carry."); addflag(lastot->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 6, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); - addot(OT_S_SUMMONWEAPON, "summon weapon", "Summons a blade of pure magic into your hands. Deals 1-power damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_SUMMONWEAPON, "summon weapon", "Summons a blade of pure magic into your hands. Deals 1-^bpower^n damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 6, NA, NA, NULL); @@ -7341,7 +7467,9 @@ void initobjects(void) { addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); // l2 - addot(OT_S_CREATEMONSTER, "create monster", "Summons a (probably hostile) monster to a nearby location.\nAt Power V you can control where the monster appears.\nAt Power VII, you can control the type of monster created.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_CREATEMONSTER, "create monster", "Summons a (probably hostile) monster to a nearby location.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power V you can control where the monster appears."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power VII you can control the type of monster created."); addflag(lastot->flags, F_SPELLSCHOOL, SS_SUMMONING, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); @@ -7350,13 +7478,15 @@ void initobjects(void) { // translocation /////////////////// // l2 - addot(OT_S_BLINK, "blink", "Teleports the caster to a random location within view. Becomes controlled at power VI.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_BLINK, "blink", "Teleports the caster to a random location within view.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power VI you can choose where to blink to."); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 6, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); addot(OT_S_SUCK, "suck", "Sucks the target lifeform towards the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -7368,19 +7498,22 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 - addot(OT_S_TELEPORT, "teleportation", "Teleports the caster (and Power-1 adjacent allies) to a new location within the same level.\nBecomes semi-controlled at Power V, and fully controlled at Power VII.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_TELEPORT, "teleportation", "Teleports the caster (and Power-1 adjacent allies) to a new location within the same level.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power V, you can choose the general direction to teleport in."); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power VII, you can choose exactly where to teleport to."); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addot(OT_S_DISPERSAL, "dispersal", "Scatters everything in the target cell around the area.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l5 - addot(OT_S_GATE, "gate", "Creates a portal to a different dungeon level.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_GATE, "gate", "Creates a portal to a different dungeon level (within ^bpower^n*2 levels).", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); // l6 @@ -7414,17 +7547,20 @@ void initobjects(void) { addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); // l3 - addot(OT_S_ENERGYBLAST, "energy blast", "Causes a ring of energy to expand from the caster (radius based on power), causing 2-6 damage to anything in sight.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addot(OT_S_ENERGYBLAST, "energy blast", "Causes a ring of energy to expand from the caster, causing 2-6 damage to anything in sight.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the radius of the blast."); addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); addot(OT_S_FLASH, "flash", "Causes a very bright flash, stunning anyone who sees it.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the size of the flash."); addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); // l6 addot(OT_S_DETONATE, "detonate", "Causes a given area to explode with massive force.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the size of the explosion."); addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); @@ -7479,6 +7615,9 @@ void initobjects(void) { addot(OT_A_DARKWALK, "darkwalk", "Step between the shadows.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); // ai will cast this spell in a special manner... + addot(OT_A_COMBOSTRIKE, "combination strike", "Perform a combination of blows which continues each time you defeat an enemy.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); addot(OT_A_DISARM, "disarm", "Attempt to disable a known trap.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_FEIGNDEATH, "feign death", "Pretend to be dead.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); @@ -7526,12 +7665,16 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); addflag(lastot->flags, F_RANGE, 1, NA, NA, NULL); + addot(OT_A_STUDYSCROLL, "study scroll", "Attempt to learn a spell directly from a scroll.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_SUCKBLOOD, "suck blood", "You can suck the blood from enemies after attaching to them.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); addot(OT_A_SWOOP, "swoop", "You can attack an enemy while flying past them.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addot(OT_A_TRAIN, "train skills", "Start training to gain a new experience level or enhance skill.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_TUMBLE, "tumble", "You can tumble across the ground.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_WARCRY, "warcry", "Inspire fear in your enemies with a mighty war cry.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); @@ -9112,6 +9255,15 @@ void initobjects(void) { addflag(lastot->flags, F_NUMAPPEAR, 1, 10, NA, ""); addflag(lastot->flags, F_CANHAVEOBMOD, OM_POISONED, 17, NA, NULL); + addot(OT_NEEDLE, "needle", "A tiny pointed needle.", MT_METAL, 0.02, OC_MISSILE, SZ_TINY); + addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_THROWMISSILE, B_TRUE, NA, NA, ""); + addflag(lastot->flags, F_MISSILEDAM, 1, NA, NA, ""); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, ""); + addflag(lastot->flags, F_NUMAPPEAR, 1, 3, NA, ""); + addflag(lastot->flags, F_CANHAVEOBMOD, OM_POISONED, 25, NA, NULL); + addflag(lastot->flags, F_RODSHAPED, B_TRUE, NA, NA, NULL); + addot(OT_JAVELIN, "javelin", "A long, sharp missile weapon.", MT_METAL, 6, OC_MISSILE, SZ_MEDIUM); addflag(lastot->flags, F_STACKABLE, B_TRUE, NA, NA, ""); addflag(lastot->flags, F_THROWMISSILE, B_TRUE, NA, NA, ""); @@ -9626,6 +9778,12 @@ void initobjects(void) { addflag(lastot->flags, F_ACCURACY, 500, NA, NA, NULL); addflag(lastot->flags, F_UNIQUE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL); + + addot(OT_ICESHIELD, "ice crystal shield", "A summoned shield made of ice crystals.", MT_ICE, 0, OC_ARMOUR, SZ_SMALL); + addflag(lastot->flags, F_SHIELD, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_GOESON, BP_SECWEAPON, NA, NA, NULL); + addflag(lastot->flags, F_ARMOURRATING, 1, NA, NA, NULL); // will be replaced when summoned + addflag(lastot->flags, F_OBHP, 5, 5, NA, NULL); // will be replaced when summoned } // returns the 'armourrating' flag @@ -10688,7 +10846,7 @@ object_t *real_moveob(object_t *src, obpile_t *dst, int howmany, int stackok) { if (o) { - //if (dst->owner && isplayer(dst->owner) && lfhasflag(dst->owner, F_DETECTAURAS)) { + if (dst->owner && isplayer(dst->owner) && isblessknown(o)) { o->blessknown = B_TRUE; } @@ -10743,6 +10901,17 @@ object_t *real_moveob(object_t *src, obpile_t *dst, int howmany, int stackok) { if (isplayer(dst->owner)) { statdirty = B_TRUE; } + } else if (dst->where) { + object_t *oo,*nextoo; + // object hit the ground? + + // triggered a trap? + for (oo = dst->where->obpile->first ; oo ; oo = nextoo ) { + nextoo = oo->next; + if ((oo != o) && hasflag(oo->flags, F_TRAP)) { + triggertrap(NULL, o, oo, dst->where); + } + } } //o = newobeffects(o); @@ -13270,13 +13439,22 @@ int readsomething(lifeform_t *lf, object_t *o) { if (isplayer(lf)) msg("You already know how to cast this spell!"); } else { enum SPELLSCHOOL school; - enum SKILL skill; + enum SKILLLEVEL slev; school = getspellschool(linkspell->id); - skill = getschoolskill(school); + slev = getspellskill(lf, linkspell->id); - if (getskill(lf, skill)) { - // learn it - addflag(lf->flags, F_CANCAST, linkspell->id, NA, NA, NULL); + // can we learn this? + if (slev) { + // try to learn it + int difficulty, mod; + difficulty = 15 + (getspelllevel(linkspell->id)*3); + mod = slev * 2; + if (skillcheck(lf, SC_LEARNMAGIC, difficulty, mod)) { + // learn it + addflag(lf->flags, F_CANCAST, linkspell->id, NA, NA, NULL); + } else { + msg("You fail to learn %s.",linkspell->name); + } } else { msg("You are not yet skilled in %s.",getschoolnameshort(school)); } @@ -14509,13 +14687,34 @@ int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, if (youhit && !willcatch && !isprone(target)) { // can the victim see the thrower? if (thrower && cansee(target, thrower)) { + int catchmod,dodgemod; + enum LFSIZE sz; + sz = getobsize(o); + // smaller things are harder to catch, but easier to dodge + if (sz >= SZ_HUMAN) { + catchmod = 4; + dodgemod = -3; + } else if (sz == SZ_MEDIUM) { + catchmod = 4; + dodgemod = -1; + } else if (sz == SZ_SMALL) { + catchmod = 0; + dodgemod = 0; + } else if (sz == SZ_TINY) { + catchmod = -3; + dodgemod = 1; + } else if (sz <= SZ_MINI) { + catchmod = -6; + dodgemod = 2; + } // first check to see if you can catch it if (!lfhasflag(target, F_NOPACK) && hasbp(target, BP_HANDS) && lfhasflag(target, F_HUMANOID) && + canpickup(target, o, o->amt) && !isimmobile(target) && - skillcheck(target, SC_DEX, 15*speed, 0)) { + skillcheck(target, SC_DEX, 15*speed, catchmod)) { willcatch = B_TRUE; - } else if (!lfhasflag(target, F_CASTINGSPELL) && skillcheck(target, SC_DODGE, 10*speed, 0)) { + } else if (!lfhasflag(target, F_CASTINGSPELL) && skillcheck(target, SC_DODGE, 10*speed, dodgemod)) { // then check if we dodge it... youhit = B_FALSE; } @@ -15133,92 +15332,107 @@ void timeeffectsob(object_t *o) { // both trapob and oid are passed, because trapob might be NULL if // coming from a door/chest trap. -void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf) { +void trapeffects(object_t *trapob, enum OBTYPE oid, cell_t *c) { + lifeform_t *lf = NULL; char lfname[BUFLEN]; - int avoided; + int avoided = B_FALSE; enum CHECKTYPE ct; objecttype_t *temp = NULL; - getlfname(lf, lfname); + + lf = c->lf; + if (lf) getlfname(lf, lfname); + temp = findot(oid); - // saving throw - if (temp) { + // saving throw? + if (temp && lf) { flag_t *f; f = hasflag(temp->flags, F_TRAP); if (f && (f->val[2] != NA)) { if (isplayer(lf) && hasflag(temp->flags, F_SECRET)) { avoided = B_FALSE; } else { + int mod = 0; switch (oid) { case OT_TRAPTRIP: ct = SC_FALL; break; default: ct = SC_DODGE; break; } - avoided = skillcheck(lf, ct, f->val[2], lfhasflag(lf, F_SNEAK) ? 5 : 0); + // easier to avoid if you're sneaking + if (lfhasflag(lf, F_SNEAK)) mod += 5; + mod += getskill(lf, SK_TRAPS); + avoided = skillcheck(lf, ct, f->val[2], mod); } } } if (oid == OT_TRAPWIND) { // can't be dodged - dospelleffects(NULL, OT_S_GUSTOFWIND, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + dospelleffects(NULL, OT_S_GUSTOFWIND, 10, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE); if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards } else if (oid == OT_TRAPNEEDLEP) { - if (isplayer(lf)) { - msg("A needle shoots out %s", avoided ? "at you, but misses." : "and hits you!"); - } else if (cansee(player, lf)) { - if (avoided) { - msg("A needle shoots out at %s, but misses.",lfname); - } else { - msg("A needle shoots out and hits %s!",lfname); + if (lf) { + if (isplayer(lf)) { + msg("A needle shoots out %s", avoided ? "at you, but misses." : "and hits you!"); + } else if (cansee(player, lf)) { + if (avoided) { + msg("A needle shoots out at %s, but misses.",lfname); + } else { + msg("A needle shoots out and hits %s!",lfname); + } + } + if (!avoided) { + poison(lf, rnd(10,20), P_VENOM, 1, "a needle trap"); + } + } else { + if (haslos(player, c)) { + // TODO: "...shoots out of xxx". need to pass obfrom to this. + msg("A poisoned needle fires, then falls to the ground."); + addob(c->obpile, "poisoned needle"); } - } - if (!avoided) { - poison(lf, rnd(10,20), P_VENOM, 1, "a needle trap"); } if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards } else if (oid == OT_TRAPROCK) { - if (haslos(player, lf->cell)) { - msg("A heavy rock drops onto %s%s", lfname, - avoided ? ", but misses." : "!"); + if (lf) { + if (haslos(player, c)) { + msg("A heavy rock drops onto %s%s", lfname, + avoided ? ", but misses." : "!"); + } + if (!avoided) { + losehp(lf, roll("1d4"), DT_BASH, NULL, "a falling rock trap"); + } + } else { + if (haslos(player, c)) { + msg("A heavy rock drops from the ceiling."); + } } - if (!avoided) { - losehp(lf, roll("1d4"), DT_BASH, NULL, "a falling rock trap"); - } - addob(lf->cell->obpile, "stone"); + addob(c->obpile, "stone"); } else if (oid == OT_TRAPTELEPORT) { - cell_t *newc; - // move somewhere else! - newc = getrandomcelloftype(lf->cell->map, lf->cell->map->habitat->emptycelltype); - if (newc) { - teleportto(lf, newc, B_TRUE); - } - } else if (oid == OT_TRAPWIND) { - // can't be dodged - dospelleffects(NULL, OT_S_GUSTOFWIND, 10, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); - if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards + dospelleffects(NULL, OT_S_DISPERSAL, 10, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE); } else if (oid == OT_TRAPPIT) { cell_t *escapeto = NULL; - addob(lf->cell->obpile, "hole in the ground"); + addob(c->obpile, "hole in the ground"); if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards - if (avoided) { - escapeto = getrandomadjcell(lf->cell, WE_WALKABLE, B_NOEXPAND); - if (!escapeto) avoided = B_FALSE; - } - - if (avoided) { - if (isplayer(lf)) { - msg("You leap away from the pit!"); - } else if (cansee(player, lf)) { - msg("%s leaps away from a pit!", lfname); + if (lf) { + if (avoided) { + escapeto = getrandomadjcell(lf->cell, WE_WALKABLE, B_NOEXPAND); + if (!escapeto) avoided = B_FALSE; + } + + if (avoided) { + if (isplayer(lf)) { + msg("You leap away from the pit!"); + } else if (cansee(player, lf)) { + msg("%s leaps away from a pit!", lfname); + } + movelf(lf, escapeto); } - movelf(lf, escapeto); } } else if (oid == OT_TRAPALARM) { - noise(lf->cell, NULL, NC_OTHER, 10, "a blaring siren!", NULL); + noise(c, NULL, NC_OTHER, 10, "a blaring siren!", NULL); } else if ((oid == OT_TRAPARROW) || (oid == OT_TRAPARROWP)) { int dir,bestdir = D_NONE; cell_t *src; @@ -15226,15 +15440,15 @@ void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf) { // get furthest wall for (dir = DC_N; dir <= DC_NW; dir++) { - cell_t *c,*prevc; + cell_t *cc,*prevc; int thisdist = 0; prevc = NULL; - c = lf->cell; - c = getcellindir(c, dir); - while (!c->type->solid) { + cc = c; + cc = getcellindir(cc, dir); + while (!cc->type->solid) { thisdist++; - prevc = c; - c = getcellindir(c, dir); + prevc = cc; + cc = getcellindir(cc, dir); } if (thisdist > maxdist) { maxdist = thisdist; @@ -15252,49 +15466,52 @@ void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf) { if (o) { // dodge check will happen in fireat(). ignore results of the // one above. - fireat(NULL, o, 1, lf->cell, 5, NULL); + fireat(NULL, o, 1, c, 5, NULL); } else { - msg("failed."); + msg("ERROR: arrow trap failed."); + dblog("ERROR: arrow trap failed."); } } } else if (oid == OT_TRAPEBLAST) { - if (haslos(player, lf->cell)) { + if (haslos(player, c)) { msg("A blast of energy comes out of nowhere!"); } // can't be dodged - dospelleffects(NULL, OT_S_ENERGYBLAST, 1, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + dospelleffects(NULL, OT_S_ENERGYBLAST, 1, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE); if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards } else if (oid == OT_TRAPFIRE) { // can't be dodged - dospelleffects(NULL, OT_S_FLAMEPILLAR, 3, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + dospelleffects(NULL, OT_S_FLAMEPILLAR, 3, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE); if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards } else if (oid == OT_TRAPGAS) { // can't be dodged - dospelleffects(NULL, OT_S_CLOUDKILL, 3, lf, NULL, lf->cell, B_UNCURSED, NULL, B_TRUE); + dospelleffects(NULL, OT_S_CLOUDKILL, 3, NULL, NULL, c, B_UNCURSED, NULL, B_TRUE); if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards } else if (oid == OT_TRAPMINE) { // can't be dodged - explodecells(lf->cell, roll("2d6"), B_FALSE, trapob, 1, DT_ORTH, B_TRUE); + explodecells(c, roll("2d6"), B_FALSE, trapob, 1, DT_ORTH, B_TRUE); if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards } else if (oid == OT_TRAPSUMMON) { - cell_t *c; + cell_t *cc; // can't be dodged - c = getrandomadjcell(lf->cell, WE_WALKABLE, B_ALLOWEXPAND); - if (c) { - summonmonster(NULL, c, R_SPECIFIED, "random", PERMENANT, B_FALSE); + cc = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND); + if (cc) { + summonmonster(NULL, cc, R_SPECIFIED, "random", PERMENANT, B_FALSE); } if (trapob) removeob(trapob, trapob->amt); // trap dies afterwards } else if (oid == OT_TRAPTRIP) { - if (avoided) { - if (isplayer(lf)) { - msg("You retain your balance."); - } else if (cansee(player, lf)) { - msg("%s retains its balance.",lfname); + if (lf) { + if (avoided) { + if (isplayer(lf)) { + msg("You retain your balance."); + } else if (cansee(player, lf)) { + msg("%s retains its balance.",lfname); + } + } else { + fall(lf, NULL, B_TRUE); } - } else { - fall(lf, NULL, B_TRUE); - } + } } } diff --git a/objects.h b/objects.h index 05842fc..5848a40 100644 --- a/objects.h +++ b/objects.h @@ -233,7 +233,8 @@ int takedamage(object_t *o, unsigned int howmuch, int damtype); int real_takedamage(object_t *o, unsigned int howmuch, int damtype, int wantannounce); int fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int speed, object_t *firearm); void timeeffectsob(object_t *o); -void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf); +//void trapeffects(object_t *trapob, enum OBTYPE oid, lifeform_t *lf); +void trapeffects(object_t *trapob, enum OBTYPE oid, cell_t *c); void turnoff(lifeform_t *lf, object_t *o); void turnon(lifeform_t *lf, object_t *o); int uncurseob(object_t *o, int *seen); diff --git a/spell.c b/spell.c index 3eb1ba5..eaec35c 100644 --- a/spell.c +++ b/spell.c @@ -416,9 +416,9 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } if (trapflag->id == F_TRAP) { - trapeffects(trapob, trapob->type->id, user); + trapeffects(trapob, trapob->type->id, user->cell); } else { - trapeffects(NULL, trapflag->val[0], user); + trapeffects(NULL, trapflag->val[0], user->cell); } } else { getobname(trapob, buf, 1); @@ -986,6 +986,59 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // cause ongoing pain for 2 turns addtempflag(target->flags, F_PAIN, DT_ACID, NA, NA, damstr, 2); taketime(user, getactspeed(user)); + } else if (abilid == OT_A_STUDYSCROLL) { + object_t *o; + int difficulty, mod; + flag_t *f; + + // only players can do this + if (!isplayer(user)) { + return B_TRUE; + } + if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { + if (isplayer(user)) msg("That wouldn't be a good idea while swimming."); + return B_TRUE; + } + if (!haslos(user, user->cell)) { + msg("You can't study anything, since you can't see!"); + return B_TRUE; + } + + // ask what to inspect + initprompt(&prompt, "Study which scroll"); + for (o = user->pack->first ; o ; o = o->next) { + if ((o->type->obclass->id == OC_SCROLL) && isknown(o)) { + f = hasflag(o->flags, F_LINKSPELL); + if (f && !cancast(user, f->val[0], NULL)) { + getobname(o, buf, o->amt); + addchoice(&prompt, o->letter, buf, NULL, f); + } + } + } + getchoice(&prompt); + f = (flag_t *)prompt.result; + if (!f) { + msg("Cancelled"); + return B_TRUE; + } + o = f->pile->ob; + + // try to transcribe it... + difficulty = 20 + (getspelllevel(f->val[0])*3); + + mod = getspellskill(user, f->val[0]) * 2; + + if (skillcheck(user, SC_LEARNMAGIC, difficulty, mod)) { + addflag(user->flags, F_CANCAST, f->val[0], NA, NA, NULL); + } else { + // failed! + msg("You fail to comprehend this spell."); + } + taketime(user, getactspeed(user)); + + // now kill the scroll + msg("The scroll crumbles to dust."); + removeob(o, 1); } else if (abilid == OT_A_SUCKBLOOD) { int dam = 0; @@ -1135,6 +1188,23 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef if (haslos(player, origcell)) { redraw(); } + } else if (abilid == OT_A_TRAIN) { + // can we train? + if (!user->skillpoints && !lfhasflag(user, F_HASNEWLEVEL)) { + if(isplayer(user)) { + msg("You are not ready for any training right now."); + } + return B_TRUE; + } + + // safe to train? + if (check_rest_ok(user)) return B_TRUE; + + // start training! + if (!startresting(user, B_TRUE)) { + // do the first one right away + rest(user, B_TRUE); + } } else if (abilid == OT_A_TUMBLE) { cell_t *origcell,*c; cell_t *retcell[MAXRETCELLS]; @@ -1341,6 +1411,91 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } msg("all blinded!"); return B_FALSE; + } else if (abilid == OT_A_COMBOSTRIKE) { + object_t *wep; + skill_t *wepsk = NULL; + int keepgoing = B_TRUE,nhits = 0; + enum SKILLLEVEL slev; + + if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) { + if (isplayer(user)) msg("You cannot perform a combination attack while swimming."); + return B_TRUE; + } + + wep = getweapon(user); + if (wep) { + wepsk = getobskill(wep); + } else { + wepsk = findskill(SK_UNARMED); + } + if (wepsk) { + slev = getskill(user, wepsk->id); + } else { + slev = PR_INEPT; + } + if (slev < PR_MASTER) { + if (isplayer(user)) msg("You must have Mastered your weapon to perform a combination attack."); + return B_TRUE; + } + if (!getadjenemies(user, NULL, NULL)) { + if (isplayer(user)) msg("You cannot see anyone nearby to attack!"); + return B_TRUE; + } + + // take time + taketime(user, getattackspeed(user)); + // remember that we are doing a combo. this will stop taketime() from being + // called during our attacks. + addflag(user->flags, F_COMBOSTRIKE, B_TRUE, NA, NA, NULL); + while (keepgoing) { + char dirch; + cell_t *c; + keepgoing = B_FALSE; + + // ask direction + c = NULL; + while (!c) { + char ques[BUFLEN]; + sprintf(ques,"%s combination in which direction (- to cancel)", nhits ? "Continue" : "Start"); + dirch = askchar("%s combination in which direction (- to cancel)", "yuhjklbn-","-", B_FALSE); + if (dirch == '-') { + break; + } else { + int dir; + dir = chartodir(dirch); + c = getcellindir(user->cell, dir); + } + } + + // attack in that dir + if (c && c->lf) { + attackcell(user, c, B_TRUE); + + // if the lf there died, keep going. + if (!c->lf || isdead(c->lf)) { + keepgoing = B_TRUE; + } + + // other reasons to stop the combo now? + if (keepgoing) { + if (isdead(user) || (getweapon(user) != wep)) { // dead or lost our weapon? + keepgoing = B_FALSE; + } else if (!getadjenemies(user, NULL, NULL)) { // noone left to attack + keepgoing = B_FALSE; + } + } + } else { + msg("Combination ended."); + keepgoing = B_FALSE; + } + // process lf deaths, then redraw the screen + checkdeath(); + needredraw = B_TRUE; + drawscreen(); + more(); // clear msgs + nhits++; + } + killflagsofid(user->flags, F_COMBOSTRIKE); } else if (abilid == OT_A_DEBUG) { cell_t *where; where = askcoords("Debug who?", "Debug->",TT_MONSTER, user, UNLIMITED, LOF_DONTNEED, B_FALSE); @@ -1430,7 +1585,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // ask for direction if (!targcell) { - dirch = askchar("Attack in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE); + dirch = askchar("Heavy blow in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE); if (dirch == '.') { // yourself! targcell = user->cell; @@ -1475,7 +1630,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // ask for direction if (!targcell) { - dirch = askchar("Attack in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE); + dirch = askchar("Quivering Palm in which direction (- to cancel)", "yuhjklbn.-","-", B_FALSE); if (dirch == '.') { // yourself! targcell = user->cell; @@ -1731,29 +1886,34 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // ask what to inspect - o = askobject(user->pack, "Inspect which object", NULL, AO_NOTKNOWN); + + initprompt(&prompt, "Inspect which object"); + for (o = user->pack->first ; o ; o = o->next) { + int classok = B_FALSE; + // can only inspect certain types of things + switch (o->type->obclass->id) { + case OC_SCROLL: + case OC_BOOK: + case OC_WAND: + case OC_RING: + classok = B_TRUE; + break; + default: + break; + } + if (classok && !isknown(o)) { + char buf[BUFLEN]; + getobname(o, buf, o->amt); + addchoice(&prompt, o->letter, buf, NULL, f); + } + } + getchoice(&prompt); + o = (object_t *)prompt.result; if (!o) { msg("Cancelled"); return B_TRUE; } - if (isknown(o)) { - msg("You already know what that is!"); - return B_TRUE; - } - - // can only inspect certain types of things - switch (o->type->obclass->id) { - case OC_SCROLL: - case OC_BOOK: - case OC_WAND: - case OC_RING: - break; - default: - msg("Your knowledge doesn't extend to this kind of item."); - return B_TRUE; - } - // failed this already? if (lfhasflagval(user, F_FAILEDINSPECT, o->type->id, NA, NA, NULL)) { msg("You won't be able to recognise this until you are more experienced."); @@ -2712,6 +2872,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_CHILL) { char lfname[BUFLEN]; + int exposedlimbs,dam; if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power, frompot)) return B_TRUE; target = targcell->lf; if (!target) { @@ -2720,18 +2881,23 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } getlfname(target, lfname); + // how many body parts are impacted? + exposedlimbs = getexposedlimbs(target); + + dam = rnd(1,exposedlimbs); + if (isplayer(target)) { if (isimmuneto(target->flags, DT_COLD)) { msg("You feel mildly chilly."); } else { - msg("You feel very cold!"); + msg("You feel cold!"); } if (seenbyplayer) *seenbyplayer = B_TRUE; } else if (cansee(player, target)) { if (isimmuneto(target->flags, DT_COLD)) { msg("%s looks mildly chilly.", lfname); } else { - msg("%s looks very cold!", lfname); + msg("%s looks cold!", lfname); } if (seenbyplayer) *seenbyplayer = B_TRUE; } @@ -2739,7 +2905,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // target takes magical damage // always hit if (!isimmuneto(target->flags, DT_COLD)) { - losehp(target, roll("1d3"), DT_COLD, caster, "a frostbite spell"); + losehp(target, dam, DT_COLD, caster, "a chill spell"); } } else if (spellid == OT_S_COLDBURST) { int range = 1; @@ -2950,6 +3116,53 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } + } else if (spellid == OT_S_CRYSTALSHIELD) { + object_t *o; + targcell = caster->cell; + target = caster; + if (getequippedob(target->pack, BP_SECWEAPON)) { + fizzle(caster); + stopspell(caster, spellid); + return B_FALSE; + } + o = addob(target->pack, "ice crystal shield"); + if (o) { + if (canwear(target, o, BP_NONE)) { + flag_t *f; + enum LFSIZE sz; + if (power <= 2) { + sz = SZ_SMALL; + } else if (power <= 4) { + sz = SZ_MEDIUM; + } else { + sz = SZ_LARGE; + } + // announce + if (isplayer(target)) { + msg("A %s shield of shimmering ice forms in your hand!", getsizetext(sz)); + } else if (cansee(player, target)) { + msg("A %s shield of shimmering ice forms in %s%s hand!", getsizetext(sz), castername, + getpossessive(castername)); + } + wear(target, o); + // set its values + f = hasflag(o->flags, F_ARMOURRATING); + if (f) f->val[0] = power; + f = hasflag(o->flags, F_OBHP); + if (f) { + f->val[0] = power*5; + f->val[1] = power*5; + } + } else { + killob(o); + fizzle(caster); + stopspell(caster, spellid); + } + } else { + fizzle(caster); + stopspell(caster, spellid); + return B_FALSE; + } } else if (spellid == OT_S_CUREPOISON) { if (!validatespellcell(caster, &targcell,TT_ALLY, spellid, power, frompot)) return B_TRUE; target = targcell->lf; @@ -3233,6 +3446,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } nseen++; removeob(o, ALL); + } else if (o->material->id == MT_ICE) { + takedamage(o, roll("2d6"), DT_FIRE); } } } @@ -3388,7 +3603,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ amt = rnd(1,6) + power; gainhp(target, amt); // caster loses it - amt = losehp(caster, rnd(1,6) + power, DT_NECROTIC, caster, "lifeforce drain"); + amt = losehp(caster, amt, DT_NECROTIC, caster, "lifeforce drain"); } else { // target loses hp amt = losehp(target, rnd(1,6) + power, DT_NECROTIC, caster, "lifeforce drain"); @@ -4029,7 +4244,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // target takes magical damage // always hit if (!isimmuneto(target->flags, DT_COLD)) { - losehp(target, dam, DT_COLD, caster, "a chill spell"); + losehp(target, dam, DT_COLD, caster, "a frostbite spell"); } } else if (spellid == OT_S_GASEOUSFORM) { if (!target) target = caster; @@ -4050,6 +4265,21 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } setrace(target, R_GASCLOUD, B_TRUE); } + } else if (spellid == OT_S_GLACIATE) { + object_t *o,*nexto; + int donesomething = B_FALSE; + if (!validatespellcell(caster, &targcell,TT_OBJECT | TT_MONSTER, spellid, power, frompot)) return B_TRUE; + + if (haslos(player, targcell)) { + msg("A puff of vapour appears."); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + + for (o = targcell->obpile->first ; o ; o = nexto) { + nexto = o->next; + takedamage(o, 1, DT_COLD); + donesomething = B_TRUE; + } } else if (spellid == OT_S_GREASE) { int radius; if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; @@ -5663,7 +5893,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ 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!"); + msg("A gust of wind whips up from nowhere!"); } // chance of blowing each ob away @@ -5687,6 +5917,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ needredraw = B_TRUE; } else if (spellid == OT_S_MIST) { object_t *o; + lifeform_t *l; + targcell = caster->cell; if (haslos(player, targcell)) { @@ -5708,6 +5940,17 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f->val[1] += power; } } + // hack - make all monsters who can see you stop targetting you. + for (l = caster->cell->map->lf ; l ; l = l->next) { + flag_t *f; + if ((l != caster) && (gethitdice(l) <= gethitdice(caster)) ) { + f = lfhasflagval(l, F_TARGETLF, caster->id, NA, NA, NULL); + if (f) killflag(f); + f = lfhasflagval(l, F_TARGETCELL, caster->cell->x, caster->cell->y, NA, NULL); + if (f) killflag(f); + } + } + } else if (spellid == OT_S_MENDING) { object_t *o; int donesomething = B_FALSE; @@ -6149,6 +6392,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seen) { if (seenbyplayer) *seenbyplayer = B_TRUE; + if (isplayer(caster)) { + needredraw = B_TRUE; + } } else { if (isplayer(caster)) { nothinghappens(); @@ -6249,6 +6495,21 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (failed) { fizzle(caster); } + } else if (spellid == OT_S_SLIDE) { + flag_t *f; + target = caster; + if (lfhasflag(target, F_ICESLIDE)) { + fizzle(target); + return B_TRUE; + } + f = addtempflag(target->flags, F_ICESLIDE, power, NA, NA, NULL, FROMSPELL); + f->obfrom = spellid; + f = addtempflag(target->flags, F_AUTOCREATEOB, 0, NA, NA, "sheet of ice", FROMSPELL); + f->obfrom = spellid; + f = addtempflag(target->flags, F_FASTMOVE, 10, NA, NA, "sheet of ice", FROMSPELL); + f->obfrom = spellid; + if (cansee(player, target) && seenbyplayer) if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (spellid == OT_S_SLOW) { int howlong = 15; if (!validatespellcell(caster, &targcell, TT_MONSTER, spellid, power, frompot)) return B_TRUE; @@ -6294,6 +6555,63 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // use direct damage rather than holy, because otherwise it might be increased // due to vulnerabilities losehp(target, rnd(1,power*2), DT_DIRECT, caster, "a smiting"); + } else if (spellid == OT_S_SNOWBALL) { + int failed = B_FALSE; + // ask for a target cell + if (!validatespellcell(caster, &targcell,TT_MONSTER, spellid, power, frompot)) return B_TRUE; + if (targcell) { + if (!targcell->type->solid || hasflag(targcell->type->material->flags, F_FLAMMABLE)) { + int dir; + cell_t *c; + // centre snowball here... + if (isplayer(caster)) { + msg("You launch a huge snowball!"); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (cansee(player, caster)) { + msg("%s launches a huge snowball!",castername); + if (seenbyplayer) *seenbyplayer = B_TRUE; + } else if (haslos(player, targcell)) { + msg("An enormous ball of fire explodes!"); + } + + anim(caster->cell, targcell, '^', C_WHITE); + animradialorth(targcell, 1, '}', C_WHITE); + + redrawpause(); + // add snow as follows (3 = medium, 2 = medium, 1 = smell) + // o + // ooo + // o + if (targcell->lf) { + losehp(targcell->lf, 1, DT_COLD, caster, "a snowball"); + } else { + addob(targcell->obpile, "small puddle of water"); + } + for (dir = D_N; dir <= D_W; dir++) { + c = getcellindir(targcell, dir); + if (c && !c->type->solid ) { + if (c->lf) { + losehp(c->lf, 1, DT_COLD, caster, "a snowball"); + } else { + addob(c->obpile, "small puddle of water"); + } + } + } + redrawresume(); + + } else { + failed = B_TRUE; + } + } else { + if (isplayer(caster)) { + msg("You have no line of fire to there!"); + } + failed = B_TRUE; + } + + if (failed) { + fizzle(caster); + } } else if (spellid == OT_S_SOFTENEARTH) { int seen = B_FALSE; int ndone = 0; @@ -7950,131 +8268,6 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { return power; } -/* -ooooooo old oooooooo -int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { - int power = 0; - int statmod; - int spelllev; - enum SKILLLEVEL spellskill,schoolskill; - enum SPELLSCHOOL school; - int db = B_FALSE; - int max = 10; - flag_t *f; - objecttype_t *ot; - - ot = findot(spellid); - - if (db) dblog("getspellpower for lf %s, spell %s", lf->race->name, ot->name); - - // first: can we WILL this to occur? if so, we might have a set - // spellpower - f = lfhasflagval(lf, F_CANWILL, spellid, NA, NA, NULL); - if (f && strlen(f->text)) { - texttospellopts(f->text, &power, NULL, NULL, NULL); - if (power > 0) { - if (db) { - dblog("-->power = %d (from canwill)", power); - } - return power; - } - } - - // player can only ever cast spells up to your level. - if (isplayer(lf) && !hasjob(lf, J_GOD)) { - if (getspelllevel(spellid) > lf->level) { - if (db) { - dblog("-->power = 0 (spell level > lf level)", power); - } - return 0; - } - } - - // statmod is -1 to 1 - statmod = getstatmod(lf, A_IQ); - if (statmod >= 40) { - statmod = 1; - } else if (statmod <= -40) { - statmod = -1; - } else { - statmod = 0; - } - - - school = getspellschoolknown(lf, spellid); - spellskill = getskill(lf, SK_SPELLCASTING); - if (hasjob(lf, J_DRUID) && (school == SS_NATURE)) { - // druid doesn't use spellcasting skill for nature spells - } else { - // dont need spellcasting skill for mental/allomancy - switch (school) { - case SS_ALLOMANCY: - case SS_MENTAL: - break; - default: - if (spellskill == PR_INEPT) { - if (db) { - dblog("-->power = 0 (inept in sorcery)"); - } - return 0; - } - break; - } - } - - if (hasjob(lf, J_DRUID)) { - power = (lf->level/3) + statmod; - if (db) dblog("-->(druid) basepower from level+iq is %d", power); - } else { - // every 6 levels you get 1 more power - // ie. at level 30 you get +5 power - power = (lf->level/6) + statmod; - if (db) dblog("-->basepower from level+iq is %d", power); - } - - switch (school) { - case SS_ALLOMANCY: - case SS_MENTAL: - break; - default: - if (db) dblog("--> + %d from skill in Sorcery", spellskill); - power += spellskill; - break; - } - - - spelllev = getspelllevel(spellid); - if (spelllev > 0) { - int divamt; - if ((school == SS_ALLOMANCY) || (school == SS_MENTAL)) { - divamt = (spelllev/2); - } else { - divamt = spelllev; - } - - if (divamt > 0) { - if (db) dblog("--> divided by %d (based on spelllevel %d)", divamt, spelllev); - power /= divamt; - } - } - - // specialised school skill - apply this AFTER dividing by spell level - schoolskill = getskill(lf, getschoolskill(school)); - if (schoolskill != PR_INEPT) { - float addamt; - addamt = (float)schoolskill * 1.5; - if (db) dblog("--> + %0.1f from skill in %s", addamt, getschoolname(school)); - power += addamt; - } - - // enforce maximum - max = getspellmaxpower(spellid); - if (power > max) power = max; - if (db) dblog("==> final power: %d", power); - return power; -} -*/ - enum SPELLSCHOOL getspellschool(enum OBTYPE spellid) { flag_t *f; objecttype_t *ot; @@ -8154,6 +8347,14 @@ enum SPELLSCHOOL getspellschoolknown(lifeform_t *lf, enum OBTYPE spellid) { 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(enum OBTYPE spellid, int power) { objecttype_t *st; int range = UNLIMITED; @@ -8363,7 +8564,18 @@ void stopspell(lifeform_t *caster, enum OBTYPE spellid) { // remove any other specific effects based on spell type. for (o = caster->pack->first; o ; o = nexto) { nexto = o->next; - if (o->type->id == OT_ENERGYBLADE) { + if ((o->type->id == OT_ENERGYBLADE) && (spellid == OT_S_SUMMONWEAPON)) { + if (cansee(player, caster)) { + char obname[BUFLEN]; + char lfname[BUFLEN]; + getobname(o, obname, 1); + getlfname(caster, lfname); + msg("%s%s %s vanishes.", lfname, getpossessive(lfname), + obname); + } + killob(o); + } + if ((o->type->id == OT_ICESHIELD) && (spellid == OT_S_CRYSTALSHIELD)) { if (cansee(player, caster)) { char obname[BUFLEN]; char lfname[BUFLEN]; @@ -8375,6 +8587,7 @@ void stopspell(lifeform_t *caster, enum OBTYPE spellid) { killob(o); } } + if ((spellid == OT_S_BARKSKIN) && (getlfmaterial(caster) == MT_WOOD)) { setlfmaterial(caster, caster->race->material->id); } else if (spellid == OT_S_FLOATINGDISC) { diff --git a/spell.h b/spell.h index 208d1f4..3f3be2b 100644 --- a/spell.h +++ b/spell.h @@ -18,6 +18,7 @@ char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf); int getspellpower(lifeform_t *lf, enum OBTYPE spellid); enum SPELLSCHOOL getspellschool(enum OBTYPE spellid); enum SPELLSCHOOL getspellschoolknown(lifeform_t *lf, enum OBTYPE spellid); +enum SKILLLEVEL getspellskill(lifeform_t *lf, enum OBTYPE spellid); int getspellrange(enum OBTYPE spellid, int power); char *getvarpowerspelldesc(enum OBTYPE spellid, int power, char *buf); void pullobto(object_t *o, lifeform_t *lf);