diff --git a/data.c b/data.c index ffa3d69..81b4bb8 100644 --- a/data.c +++ b/data.c @@ -1589,6 +1589,9 @@ void initobjects(void) { // potions (sorted by rarity) addot(OT_POT_JUICE, "potion of fruit juice", "Tasty (but not very fresh) fruit juice!", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL); + addot(OT_POT_CANINETRACKING, "potion of canine tracking", "Mimics the effects of a 'canine tracking' spell.", MT_GLASS, 1, OC_POTION, SZ_TINY); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_COMMON, NULL); + addot(OT_POT_HEALINGMIN, "potion of minor healing", "Restores 1-10 health to whoever drinks it.", MT_GLASS, 1, OC_POTION, SZ_TINY); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, NA, NULL); addflag(lastot->flags, F_AIHEALITEM, B_TRUE, NA, NA, NULL); @@ -1690,10 +1693,6 @@ void initobjects(void) { addot(OT_SCR_AWARENESS, "scroll of awareness", "Mimics the effects of a 'heightened awareness' spell.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_UNCOMMON, NULL); - addot(OT_SCR_CANINETRACKING, "scroll of canine tracking", "Mimics the effects of a 'canine tracking' spell.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); - addflag(lastot->flags, F_LINKSPELL, OT_S_CANINETRACKING, NA, NA, NULL); - addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_COMMON, NULL); - addot(OT_SCR_REMOVECURSE, "scroll of remove curse", "Removes curses from all weilded equipment.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_COMMON, NULL); @@ -2876,6 +2875,13 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, 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_INSTANTDISROBE, "instant disrobe", "Transports 1-3 pieces of the target's armour to the air next to them.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); + addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines number of items affected."); + addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); + addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); + addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); + addflag(lastot->flags, F_MAXPOWER, 8, NA, NA, NULL); + addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); // l4 addot(OT_S_BLINKASS, "assassin blink", "Teleports the caster behind an enemy, ready for a sneak attack.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_TRANSLOCATION, NA, NA, NULL); @@ -3007,6 +3013,8 @@ 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_NEEDSGRAB, B_TRUE, NA, NA, NULL); + addot(OT_A_CHECKSTAIRS, "check stairs", "Attempt to determine what lies on the other end of a staircase.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_COOK, "cook", "Combine food and water into a healthy meals.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_DARKWALK, "darkwalk", "Step between the shadows.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); @@ -8664,6 +8672,7 @@ void initskills(void) { addskilldesc(SK_LISTEN, PR_NOVICE, "^gYou now gauge the distance of sounds.^n", B_TRUE); addskilldesc(SK_LISTEN, PR_BEGINNER, "^gYou now more accurately gauge the distance of sounds.^n", B_TRUE); addskilldesc(SK_LISTEN, PR_ADEPT, "^gYou can now determine the direction sounds are coming from.^n", B_TRUE); + addskilldesc(SK_LISTEN, PR_ADEPT, "^gYou can now listen at staircases before descending.^n", B_TRUE); addskilldesc(SK_LISTEN, PR_EXPERT, "^gYou can now identify monsters based on sound.^n", B_TRUE); addskilldesc(SK_LISTEN, PR_MASTER, "^gYou can now locate monsters based on sound.^n", B_TRUE); addskill(SK_LOCKPICKING, "Lockpicking", "Enhances your ability to pick locks.", 50); @@ -8707,6 +8716,7 @@ void initskills(void) { 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.^n", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_NOVICE, "^gYou can now check for trails on staircases before descending.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_BEGINNER, "^gYou can now determine how recently footprints were made.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_ADEPT, "^gYou can now identify creatures from their footprints.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_ADEPT, "^gYour field of vision is now wider.^n", B_TRUE); @@ -8715,6 +8725,7 @@ void initskills(void) { addskilldesc(SK_PERCEPTION, PR_MASTER, "^gYou now have perception of your blind spots.^n", B_TRUE); addskill(SK_STEALTH, "Stealth", "Affects your ability to move silently.", 0); // untrainable? addskilldesc(SK_STEALTH, PR_BEGINNER, "^gYou gain the 'hide' ability.^n", B_FALSE); + addskilldesc(SK_STEALTH, PR_SKILLED, "^gYou can now peek down staircases.^n", B_TRUE); addskilldesc(SK_STEALTH, PR_EXPERT, "^gYou can now hide even when monsters are nearby.^n", B_TRUE); addskill(SK_SWIMMING, "Swimming", "Allows you to safely swim through deep water.", 50); addskilldesc(SK_SWIMMING, PR_NOVICE, "^gYou can now swim.^n", B_TRUE); diff --git a/data/hiscores.db b/data/hiscores.db index cd06d0b..75df3b8 100644 Binary files a/data/hiscores.db and b/data/hiscores.db differ diff --git a/defs.h b/defs.h index 34d3428..2c309d1 100644 --- a/defs.h +++ b/defs.h @@ -1044,6 +1044,7 @@ enum OBTYPE { OT_POT_AMBROSIA, OT_POT_BLOOD, OT_POT_BLOODC, + OT_POT_CANINETRACKING, OT_POT_COFFEE, OT_POT_COMPETENCE, OT_POT_ELEMENTIMMUNE, @@ -1072,7 +1073,6 @@ enum OBTYPE { OT_GRAPHPAPER, OT_SCR_AWARENESS, OT_SCR_NOTHING, - OT_SCR_CANINETRACKING, OT_SCR_CREATEMONSTER, OT_SCR_DETECTAURA, OT_SCR_DETECTLIFE, @@ -1260,6 +1260,7 @@ enum OBTYPE { OT_S_BLINKASS, OT_S_DISPERSAL, OT_S_GATE, + OT_S_INSTANTDISROBE, OT_S_PLANESHIFT, OT_S_SUCK, OT_S_TELEPORT, @@ -1284,6 +1285,7 @@ enum OBTYPE { OT_A_LEVELUP, // abilities OT_A_AIMEDSTRIKE, + OT_A_CHECKSTAIRS, OT_A_COOK, OT_A_DARKWALK, OT_A_DISARM, diff --git a/lf.c b/lf.c index 616fdca..97c97c0 100644 --- a/lf.c +++ b/lf.c @@ -2902,7 +2902,7 @@ int eat(lifeform_t *lf, object_t *o) { addtempflag(lf->flags, F_SEEINDARK, 3, NA, NA, NULL, rnd(20,40)); } - makeknown(o->type->id); + if (isplayer(lf)) makeknown(o->type->id); } // end if fullyeaten // take time @@ -5673,6 +5673,130 @@ int getnightvisrange(lifeform_t *lf) { return range; } +// populates heartext, seetext and volume +int getnoisedetails(lifeform_t *lf, enum NOISETYPE nid, char *heartext,char *seetext, int *volume) { + flag_t *retflag[MAXCANDIDATES],*nflag[MAXCANDIDATES]; + int nretflags, i,nnflags = 0; + + // deafults + if (volume) *volume = 0; + if (heartext) strcpy(heartext, ""); + if (seetext) strcpy(seetext, ""); + + if (lfhasflag(lf, F_FROZEN)) { + // can't make noise if frozen! + return B_TRUE; + } + + if ((nid == N_WALK) || nid == (N_FLY)) { + if (!movecausesnoise(lf)) { + return B_TRUE; + } + } + + getflags(lf->flags, retflag, &nretflags, F_NOISETEXT, F_NONE); + for (i = 0; i < nretflags; i++) { + if (retflag[i]->val[0] == nid) { + nflag[nnflags++] = retflag[i]; + } + } + + if (nnflags) { + flag_t *f; + char verb[BUFLEN], noun[BUFLEN]; + f = nflag[rnd(0,nnflags-1)]; + + if (volume) *volume = f->val[1]; + + if (f->text[0] == '^') { + strcpy(verb, ""); + //noun = strtok_r(f->text, "^", &dummy); + strcpy(noun, f->text + 1); + } else { + char *p; + p = readuntil(verb, f->text, '^'); + readuntil(noun, p, '^'); // ie eol + //verb = strtok_r(f->text, "^", &dummy); + //noun = strtok_r(NULL, "^", &dummy); + } + + if (heartext) { + if (strlen(noun)) { + snprintf(heartext, BUFLEN, "%s.",noun); + } + } + if (seetext) { + if (strlen(verb)) { + strcpy(seetext, verb); + } + } + if (nid == N_WALK) { + *volume += getarmournoise(lf); + } + return B_FALSE; + } else { + // some defaults + if (nid == N_WALK) { + enum LFSIZE sz; + char movetext[BUFLEN]; + strcpy(movetext, ""); + sz = getlfsize(lf); + switch (sz) { + case SZ_MINI: + case SZ_TINY: + if (volume) *volume = 0; + break; + case SZ_SMALL: + if (volume) *volume = 1; + strcpy(movetext, "light footsteps."); + break; + case SZ_MEDIUM: + case SZ_HUMAN: + if (volume) *volume = 2; + strcpy(movetext, "footsteps."); + break; + case SZ_LARGE: + if (volume) *volume = 3; + strcpy(movetext, "heavy footsteps."); + break; + case SZ_HUGE: + if (volume) *volume = 4; + strcpy(movetext, "heavy footsteps."); + break; + case SZ_ENORMOUS: + if (volume) *volume = 5; + strcpy(movetext, "very heavy footsteps."); + break; + case SZ_MAX: + if (volume) *volume = 6; + strcpy(movetext, "extremely loud thumping."); + break; + default: + break; + } + if (strlen(movetext)) { + if (volume) *volume += getarmournoise(lf); + if (heartext) strcpy(heartext, movetext); + return B_FALSE; + } + } else if (nid == N_SONICBOLT) { + + if (volume) *volume = 5; + if (heartext) strcpy(heartext, "a ear-splitting burst of sound!"); + if (seetext) strcpy(seetext, "emits an ear-splitting burst of sound!"); + return B_FALSE; + } else if (nid == N_WARCRY) { + if (volume) *volume = 4; + if (heartext) strcpy(heartext, "a blood-curdling war cry!"); + if (seetext) strcpy(seetext, "shouts a blood-curdling war-cry!"); + return B_FALSE; + } + return B_TRUE; + } + return B_FALSE; +} + + char *getlfconditionname(enum LFCONDITION cond) { switch (cond) { case C_CRITICAL: @@ -7521,10 +7645,10 @@ void giveobflags(lifeform_t *lf, object_t *o, enum FLAG whattype) { // if all conferred flags now known, object is known if (flagsfound && (flagsknown == flagsfound)) { - makeknown(o->type->id); - // in some cases, identify the object fully - // (ie. make +xxx bonuses known too) - if (isplayer(lf)) { + if (isplayer(lf) || cansee(player, lf)) { + makeknown(o->type->id); + // in some cases, identify the object fully + // (ie. make +xxx bonuses known too) if (hasflag(o->flags, F_IDWHENUSED)) { identify(o); } @@ -7633,7 +7757,6 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { makeknown(OT_MUSHROOMTOAD); } } - } else if (id == SK_LORE_ARCANA) { if (f->val[1] == PR_ADEPT) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL); @@ -7642,6 +7765,13 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { newf->lifetime = FROMSKILL; } } + } else if (id == SK_LORE_NATURE) { + if (f->val[1] == PR_ADEPT) { + if (isplayer(lf)) { + makeknown(OT_MUSHROOMSHI); + makeknown(OT_MUSHROOMTOAD); + } + } } else if (id == SK_PERCEPTION) { if ((f->val[1] == PR_ADEPT) || (f->val[1] == PR_MASTER)) { // our FOV gets wider @@ -7659,25 +7789,27 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { newf->lifetime = FROMSKILL; } } else if (id == SK_TECHUSAGE) { - objecttype_t *ot; - // automatically make known all tech <= our skill level - for (ot = objecttype ; ot ; ot = ot->next) { - // if objecttype is not known... - if (!isknownot(ot)) { - flag_t *tf; - tf = hasflag(ot->flags, F_TECHLEVEL); - // if objecttype has a tech level , and it is - // lower (or equal to) our tech knowledge... - if (tf && !isknownot(ot) && (tf->val[0] <= f->val[1])) { - // then make it known! - makeknown(ot->id); - if (isplayer(lf)) { - object_t *o; - o = hasob(lf->pack, ot->id); - if (o) { - char buf[BUFLEN]; - getobname(o, buf, o->amt); - msgnocap("%c - %s", o->letter, buf); + if (isplayer(lf)) { + objecttype_t *ot; + // automatically make known all tech <= our skill level + for (ot = objecttype ; ot ; ot = ot->next) { + // if objecttype is not known... + if (!isknownot(ot)) { + flag_t *tf; + tf = hasflag(ot->flags, F_TECHLEVEL); + // if objecttype has a tech level , and it is + // lower (or equal to) our tech knowledge... + if (tf && !isknownot(ot) && (tf->val[0] <= f->val[1])) { + // then make it known! + makeknown(ot->id); + if (isplayer(lf)) { + object_t *o; + o = hasob(lf->pack, ot->id); + if (o) { + char buf[BUFLEN]; + getobname(o, buf, o->amt); + msgnocap("%c - %s", o->letter, buf); + } } } } @@ -9591,51 +9723,58 @@ void addskilldesc(enum SKILL id, enum SKILLLEVEL lev, char *text, int wantmsg) { sk->nskilldesc++; } -void addtrail(lifeform_t *lf, int dir) { - object_t *footprint, *scent; +object_t *addtrail(lifeform_t *lf, cell_t *where, int dir, int doprints, int doscents) { + object_t *footprint, *scent,*retob = NULL; flag_t *fpflag; // no tracks at all? - if (lf->cell->type->solid) { - return; - } else if (hasobwithflag(lf->cell->obpile, F_DEEPWATER)) { - return; + if (where->type->solid) { + return NULL; + } else if (hasobwithflag(where->obpile, F_DEEPWATER)) { + return NULL; } // footprints first - if (!isairborne(lf) && !lfhasflag(lf, F_NONCORPOREAL)) { - int fpdir; - if (getskill(lf, SK_PERCEPTION) >= PR_EXPERT) { - // no footprints! - return; - } else { - fpdir = dir; - } - - footprint = hastrailof(lf->cell->obpile, lf, OT_FOOTPRINT, &fpflag, NULL); - if (footprint) { - assert(fpflag); - fpflag->lifetime = getfootprinttime(lf); - fpflag->val[1] = fpdir; - } else { - char buf[BUFLENTINY]; - snprintf(buf, BUFLENTINY, "%d", lf->id); - footprint = addobfast(lf->cell->obpile, OT_FOOTPRINT); - addtempflag(footprint->flags, F_TRAIL, lf->race->id, fpdir, S_SIGHT, buf, getfootprinttime(lf)); + if (doprints) { + if (!isairborne(lf) && !lfhasflag(lf, F_NONCORPOREAL)) { + int fpdir; + if (getskill(lf, SK_PERCEPTION) >= PR_EXPERT) { + // no footprints! + return NULL; + } else { + fpdir = dir; + } + + footprint = hastrailof(where->obpile, lf, OT_FOOTPRINT, &fpflag, NULL); + if (footprint) { + assert(fpflag); + fpflag->lifetime = getfootprinttime(lf); + fpflag->val[1] = fpdir; + } else { + char buf[BUFLENTINY]; + snprintf(buf, BUFLENTINY, "%d", lf->id); + footprint = addobfast(where->obpile, OT_FOOTPRINT); + addtempflag(footprint->flags, F_TRAIL, lf->race->id, fpdir, S_SIGHT, buf, getfootprinttime(lf)); + } + retob = footprint; } } // now smell - scent = hastrailof(lf->cell->obpile, lf, OT_SCENT, &fpflag, NULL); - if (scent) { - assert(fpflag); - fpflag->lifetime = TM_SCENT; - } else { - char buf[BUFLENTINY]; - snprintf(buf, BUFLENTINY, "%d", lf->id); - scent = addobfast(lf->cell->obpile, OT_SCENT); - addtempflag(scent->flags, F_TRAIL, lf->race->id, dir, S_SMELL, buf, TM_SCENT); + if (doscents) { + scent = hastrailof(where->obpile, lf, OT_SCENT, &fpflag, NULL); + if (scent) { + assert(fpflag); + fpflag->lifetime = TM_SCENT; + } else { + char buf[BUFLENTINY]; + snprintf(buf, BUFLENTINY, "%d", lf->id); + scent = addobfast(where->obpile, OT_SCENT); + addtempflag(scent->flags, F_TRAIL, lf->race->id, dir, S_SMELL, buf, TM_SCENT); + } + retob = scent; } + return retob; } void adjustspeedforwater(lifeform_t *lf, int *speed) { @@ -10789,96 +10928,15 @@ int makenauseated(lifeform_t *lf, int amt, int howlong) { return B_FALSE; } + void makenoise(lifeform_t *lf, enum NOISETYPE nid) { - flag_t *retflag[MAXCANDIDATES],*nflag[MAXCANDIDATES]; - int nretflags, volume = 1, i,nnflags = 0; - char *verb = NULL, *noun = NULL; + int volume = 1; + char hear[BUFLEN], see[BUFLEN]; - if (lfhasflag(lf, F_FROZEN)) { - // can't make noise if frozen! - return; + if (!getnoisedetails(lf, nid, hear, see, &volume)) { + // success + noise(lf->cell, lf, noisetypetoclass(nid), volume, strlen(hear) ? hear : NULL, strlen(see) ? see : NULL); } - - - getflags(lf->flags, retflag, &nretflags, F_NOISETEXT, F_NONE); - for (i = 0; i < nretflags; i++) { - if (retflag[i]->val[0] == nid) { - nflag[nnflags++] = retflag[i]; - } - } - - if (nnflags) { - flag_t *f; - char *dummy; - char noisetext[BUFLEN]; - f = nflag[rnd(0,nnflags-1)]; - - volume = f->val[1]; - - if (f->text[0] == '^') { - verb = NULL; - noun = strtok_r(f->text, "^", &dummy); - } else { - verb = strtok_r(f->text, "^", &dummy); - noun = strtok_r(NULL, "^", &dummy); - } - - snprintf(noisetext, BUFLEN, "%s.",noun); - if (nid == N_WALK) { - volume += getarmournoise(lf); - } - noise(lf->cell, lf, noisetypetoclass(nid), volume, noisetext, verb); - } else { - // some defaults - if (nid == N_WALK) { - enum LFSIZE sz; - char movetext[BUFLEN]; - strcpy(movetext, ""); - sz = getlfsize(lf); - switch (sz) { - case SZ_MINI: - case SZ_TINY: - volume = 0; - break; - case SZ_SMALL: - volume = 1; - strcpy(movetext, "light footsteps."); - break; - case SZ_MEDIUM: - case SZ_HUMAN: - volume = 2; - strcpy(movetext, "footsteps."); - break; - case SZ_LARGE: - volume = 3; - strcpy(movetext, "heavy footsteps."); - break; - case SZ_HUGE: - volume = 4; - strcpy(movetext, "heavy footsteps."); - break; - case SZ_ENORMOUS: - volume = 5; - strcpy(movetext, "very heavy footsteps."); - break; - case SZ_MAX: - volume = 6; - strcpy(movetext, "extremely loud thumping."); - break; - default: - break; - } - volume += getarmournoise(lf); - if (strlen(movetext)) { - noise(lf->cell, lf, noisetypetoclass(nid), volume, movetext, NULL); - } - } else if (nid == N_SONICBOLT) { - noise(lf->cell, lf, NC_OTHER, 5, "a ear-splitting burst of sound!", "emits an ear-splitting burst of sound!"); - } else if (nid == N_WARCRY) { - noise(lf->cell, lf, NC_OTHER, 4, "a blood-curdling war cry!", "shouts a blood-curdling war-cry!"); - } - } - } void mayusespellschool(flagpile_t *fp, enum SPELLSCHOOL ss, enum FLAG how) { @@ -11203,13 +11261,14 @@ void modstamina(lifeform_t *lf, float howmuch) { } else if (orig == 0) { msg("You feel less exhausted now."); } - } else if (cansee(player, lf)) { + } + /*else if (cansee(player, lf)) { if (getstamina(lf) == 0) { char lfname[BUFLEN]; getlfname(lf, lfname); msg("%s looks exhausted.", lfname); } - } + }*/ } if (getstamina(lf) == 0) { @@ -11217,6 +11276,14 @@ void modstamina(lifeform_t *lf, float howmuch) { } } + +int movecausesnoise(lifeform_t *lf) { + if (lfhasflag(lf, F_SILENTMOVE) || lfhasflag(lf, F_SNEAK)) { + return B_FALSE; + } + return B_TRUE; +} + // if validchars is set, we will populate it with a list of valid // choice letters for asking the player how to rest. int needstorest(lifeform_t *lf, char *validchars) { @@ -11310,6 +11377,9 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, strcat(realseetext, "something"); } msg("%s %s.", lfname, realseetext); + if ((realseetext[0] == '\0') || !strlen(realseetext)) { + dblog("xxx"); + } rv = B_TRUE; } } else if (text && !isdeaf(l) && ((nclass != NC_MOVEMENT) || !lfhasflag(l, F_DONELISTEN))) { @@ -15061,31 +15131,14 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { } } - // do stairs go somewhere? - newcell = getstairdestination(o); - if (!newcell) { + // do stairs go somewhere? generate new maps if required. + newcell = getstairdestination(o, &madenewmap); + if (newcell) { + newmap = newcell->map; + } else { if (isportal) { // unconnected portal if (isplayer(lf)) msg("This portal doesn't seem to go anywhere."); - } else { - // is there already a level of the correct depth? - newmap = findregionmap(newregion->id, newdepth); - if (newmap) { - dblog("ERROR - unlinked stairs!\n"); - msg("ERROR - unlinked stairs!\n"); - } else { - // generate a new map! this will fill in the destination of our stairs - newmap = addmap(); - // first map of a newly created region? - if (newregion->id != curmap->region->id) { - newdepth = 1; - } - createmap(newmap, newdepth, newregion, curmap, dir, o); - // at this point, stairs should have a destination (map creation will - // fill it in) - newcell = getstairdestination(o); - madenewmap = B_TRUE; - } } } @@ -15112,19 +15165,11 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { } // check noone is in the way - if (newcell->lf) { - cell_t *c; - // if they are, find somewhere to move them. - c = getrandomadjcell(newcell, WE_WALKABLE, B_ALLOWEXPAND); - if (c) { - // move them there - movelf(newcell->lf, c); - } else { - // TODO: handle this differently - ie always allow the player - // go there? - if (isplayer(lf)) msg("The stairs seem to be blocked."); - return B_TRUE; - } + if (movelfsoutofway(newcell)) { + // TODO: handle this differently - ie always allow the player + // go there? + if (isplayer(lf)) msg("The stairs seem to be blocked."); + return B_TRUE; } // announce if (isplayer(lf)) { diff --git a/lf.h b/lf.h index 8ae0fef..f421f56 100644 --- a/lf.h +++ b/lf.h @@ -7,7 +7,7 @@ race_t *addrace(enum RACE id, char *name, float weight, char glyph, int glyphcol raceclass_t *addraceclass(enum RACECLASS id, char *name, char *pluralname, enum SKILL skill); skill_t *addskill(enum SKILL id, char *name, char *desc, int traintime); void addskilldesc(enum SKILL id, enum SKILLLEVEL lev, char *text, int wantmsg); -void addtrail(lifeform_t *lf, int dir); +object_t *addtrail(lifeform_t *lf, cell_t *where, int dir, int doprints, int doscents); void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype); void adjustspeedforwater(lifeform_t *lf, int *speed); void age(lifeform_t *lf, int pct); @@ -153,6 +153,7 @@ int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions); int getmiscastchance(lifeform_t *lf); int getmorale(lifeform_t *lf); int getnightvisrange(lifeform_t *lf); +int getnoisedetails(lifeform_t *lf, enum NOISETYPE nid, char *heartext,char *seetext, int *volume); char *getlfconditionname(enum LFCONDITION cond); object_t *getouterequippedob(lifeform_t *lf, enum BODYPART bp); int getowing(lifeform_t *buyer, int shopid, int *retnitems); @@ -318,6 +319,7 @@ void modhunger(lifeform_t *lf, int amt); float modifybystat(float num, lifeform_t *lf, enum ATTRIB att); void modmorale(lifeform_t *lf, int howmuch); void modstamina(lifeform_t *lf, float howmuch); +int movecausesnoise(lifeform_t *lf); int needstorest(lifeform_t *lf, char *validchars); int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nt, int volume, char *text, char *seetext); enum NOISECLASS noisetypetoclass(enum NOISETYPE nt); diff --git a/map.c b/map.c index dd6681e..a92f4c8 100644 --- a/map.c +++ b/map.c @@ -2782,13 +2782,13 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex object_t *o; c = getcellat(map, x, y); o = hasobwithflag(c->obpile, F_CLIMBABLE); - if (o && !getstairdestination(o)) { + if (o && !getstairdestination(o, NULL)) { // this will join these stairs to existing ones on // existing adjacent maps if (!linkstairs(o, NULL)) { if (db) { cell_t *dst; - dst = getstairdestination(o); + dst = getstairdestination(o, NULL); dblog(" linked '%s' to map %s",o->type->name, dst->map->name); } } else { @@ -4077,7 +4077,7 @@ map_t *findsurfaceexitmap(map_t *m) { for (o = c->obpile->first ; o ; o = o->next) { if (hasflagval(o->flags, F_CLIMBABLE, D_UP, NA, NA, NULL)) { cell_t *newc; - newc = getstairdestination(o); + newc = getstairdestination(o, NULL); if (newc) { return newc->map; } @@ -4554,7 +4554,8 @@ int getslipperyness(cell_t *c, object_t **slipob) { return totalslip; } -cell_t *getstairdestination(object_t *o) { +// if madenewmap is passed, then we'll try to make a new map for stairs with no endpoint +cell_t *getstairdestination(object_t *o, int *madenewmap) { flag_t *f; cell_t *newcell = NULL; @@ -4585,6 +4586,55 @@ cell_t *getstairdestination(object_t *o) { newcell = getcellat(newmap, newx, newy); } } + + if (!newcell && madenewmap) { + cell_t *obcell = NULL; + map_t *newmap,*curmap; + region_t *newregion = NULL; + int dir,newdepth; + + obcell = getoblocation(o); + curmap = obcell->map; + + f = hasflag(o->flags, F_CLIMBABLE); + if (!f) return NULL; + + dir = getstairdirection(o); + if ((dir != D_UP) && (dir != D_DOWN)) { + // ie this is a portal + return NULL; + } else { + if (dir == D_UP) newdepth = curmap->depth - 1; + else newdepth = curmap->depth + 1; + if (f->val[1] == NA) { + // use same region + newregion = obcell->map->region; + } else { + newregion = findregion(f->val[1]); + } + } + + // is there already a level of the correct depth? + newmap = findregionmap(newregion->id, newdepth); + if (newmap) { + dblog("ERROR - unlinked stairs!\n"); + msg("ERROR - unlinked stairs!\n"); + return NULL; + } else { + // generate a new map! this will fill in the destination of our stairs + newmap = addmap(); + // first map of a newly created region? + if (newregion->id != curmap->region->id) { + newdepth = 1; + } + createmap(newmap, newdepth, newregion, curmap, dir, o); + // at this point, stairs should have a destination (map creation will + // fill it in) + newcell = getstairdestination(o, NULL); // recursive call + *madenewmap = B_TRUE; + } + + } return newcell; } diff --git a/map.h b/map.h index 1b9a54f..3ee6dc5 100644 --- a/map.h +++ b/map.h @@ -95,7 +95,7 @@ int getrandomdirexcept(int dirtype, int exception); cell_t *getrandomroomcell(map_t *map, int roomid); void getroomcells(map_t *m, int roomid, cell_t **retcell, int *ncells); int getslipperyness(cell_t *c, object_t **slipob); -cell_t *getstairdestination(object_t *o); +cell_t *getstairdestination(object_t *o, int *madenewmap); object_t *hasenterableobject(cell_t *c); object_t *hascloseddoor(cell_t *c); lifeform_t *haslf(cell_t *c); diff --git a/move.c b/move.c index 9769eb1..cfeacf4 100644 --- a/move.c +++ b/move.c @@ -1363,6 +1363,22 @@ int movelf(lifeform_t *lf, cell_t *newcell) { return didmsg; } +int movelfsoutofway(cell_t *newcell) { + // anyone in the way? + if (newcell->lf) { + cell_t *c; + // if they are, find somewhere to move them. + c = getrandomadjcell(newcell, WE_WALKABLE, B_ALLOWEXPAND); + if (c) { + // move them there + movelf(newcell->lf, c); + } else { + return B_TRUE; + } + } + return B_FALSE; +} + // basically this is a warpper for 'movelf' which // does other game things like telling the player // what is here. @@ -1466,13 +1482,11 @@ int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg) { // make some noise // (stealth check to avoid this) - if (!lfhasflag(lf, F_SILENTMOVE) && !lfhasflag(lf, F_SNEAK)) { - if (!skillcheck(lf, SC_STEALTH, 20, 0)) { - if (isairborne(lf)) { - makenoise(lf, N_FLY); - } else { - makenoise(lf, N_WALK); - } + if (!skillcheck(lf, SC_STEALTH, 20, 0)) { + if (isairborne(lf)) { + makenoise(lf, N_FLY); + } else { + makenoise(lf, N_WALK); } } @@ -2373,8 +2387,8 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { killflagsofid(lf->flags, F_LASTDIR); addflag(lf->flags, F_LASTDIR, dir, NA, NA, NULL); - // add footprints in our current cell. - addtrail(lf, dir); + // add footprints/scents in our current cell. + addtrail(lf, lf->cell, dir, B_TRUE, B_TRUE); // do your pets see you move? if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) { diff --git a/move.h b/move.h index c0118bf..7d1984f 100644 --- a/move.h +++ b/move.h @@ -18,6 +18,7 @@ int moveawayfrom(lifeform_t *lf, cell_t *dst, int dirtype, int keepinlof, int st int moveclear(lifeform_t *lf, int dir, enum ERROR *error); int moveeffects(lifeform_t *lf); int movelf(lifeform_t *lf, cell_t *newcell); +int movelfsoutofway(cell_t *newcell); int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose, int dontclearmsg); int movetowards(lifeform_t *lf, cell_t *dst, int dirtype, int strafe); int move_will_hurt(lifeform_t *lf); diff --git a/objects.c b/objects.c index 36eb976..cca6f09 100644 --- a/objects.c +++ b/objects.c @@ -5287,6 +5287,15 @@ float getshopprice(object_t *o, lifeform_t *buyer) { return val; } +int getstairdirection(object_t *o) { + flag_t *f; + f = hasflag(o->flags, F_CLIMBABLE); + if ((f->val[0] == D_UP) || (f->val[0] == D_DOWN)) { + return f->val[0]; + } + return D_NONE; +} + enum SKILLLEVEL gettechlevel(enum OBTYPE oid) { flag_t *f; objecttype_t *ot; @@ -6442,6 +6451,14 @@ void makeknown(enum OBTYPE otid) { flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; + /* + objecttype_t *ot; + ot = findot(otid); + if (ot && (ot->obclass->id == OC_TECH)) { + dblog("xxx"); + } + */ + if (player) { // if player is holding an object of that type with F_CONFER.. IFKNOWN... and isn't known... // then by making the object known, we also need to give them the flag. @@ -6597,7 +6614,7 @@ object_t *real_moveob(object_t *src, obpile_t *dst, int howmany, int stackok) { pit = hasobwithflagval(dst->where->obpile, F_PIT, D_DOWN, NA, NA, NULL); if (pit) { cell_t *newcell; - newcell = getstairdestination(pit); + newcell = getstairdestination(pit, NULL); if (newcell) { if (haslos(player, dst->where)) { char obname[BUFLEN]; @@ -7092,7 +7109,7 @@ int obsfallthrough(cell_t *c, object_t *pit) { char obname[BUFLEN],downholename[BUFLEN],upholename[BUFLEN]; int nfallen = 0; - belowcell = getstairdestination(pit); + belowcell = getstairdestination(pit, NULL); if (!belowcell) return 0; uphole = hasobwithflagval(belowcell->obpile, F_PIT, D_UP, NA, NA, NULL); @@ -8492,6 +8509,9 @@ void potioneffects(lifeform_t *lf, enum OBTYPE oid, object_t *o, enum BLESSTYPE } } break; + case OT_POT_CANINETRACKING: + dospelleffects(lf, OT_S_CANINETRACKING, 5, lf, NULL, lf->cell, potblessed, seen, B_TRUE); + break; case OT_POT_COFFEE: if (isplayer(lf)) { msg("This tastes like coffee."); diff --git a/objects.h b/objects.h index a101175..98693d4 100644 --- a/objects.h +++ b/objects.h @@ -130,6 +130,7 @@ char *getschoolname(enum SPELLSCHOOL sch); char *getschoolnameshort(enum SPELLSCHOOL sch); int getshatterdam(object_t *o); float getshopprice(object_t *o, lifeform_t *buyer); +int getstairdirection(object_t *o); enum SKILLLEVEL gettechlevel(enum OBTYPE oid); int getthrowdam(object_t *o); char *gettopobname(cell_t *c, char *retbuf); diff --git a/spell.c b/spell.c index 054bb68..f8b03af 100644 --- a/spell.c +++ b/spell.c @@ -25,6 +25,7 @@ extern region_t *firstregion; extern knowledge_t *knowledge; extern int needredraw; +extern int noredraw; extern prompt_t prompt; @@ -215,6 +216,344 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // attack attackcell(user, targcell, B_TRUE); } + } else if (abilid == OT_A_CHECKSTAIRS) { + char obname[BUFLEN], buf[BUFLEN],ch; + int madenewmap = B_TRUE; + cell_t *origcell = NULL; + lifeform_t *inway = NULL; + cell_t *c; + object_t *stairs; + int stairdir; + + if (!isplayer(user)) { + return B_FALSE; + } + // are there stairs here? + stairs = hasobwithflag(user->cell->obpile, F_CLIMBABLE); + if (!stairs) { + msg("There are no stairs here!"); + return B_TRUE; + } + stairdir = getstairdirection(stairs); + + if ((stairdir != D_UP) && (stairdir != D_DOWN)) { + // slightly different message from above, for debugging + msg("There are no stairs here to check!"); + return B_TRUE; + } + + // how can we check the stairs? + getobname(stairs, obname, 1); + sprintf(buf, "How will you check %s", obname); + initprompt(&prompt, buf); + if ((getskill(user, SK_LISTEN) >= PR_ADEPT) && !isdeaf(user)) { + addchoice(&prompt, 'l', "Listen for sounds", NULL, NULL, NULL); + } + if ((getskill(user, SK_PERCEPTION)) && !isblind(user)) { + addchoice(&prompt, 'f', "Check for footprints", NULL, NULL, NULL); + } + if (getskill(user, SK_STEALTH) >= PR_SKILLED) { + addchoice(&prompt, 'p', "Peek at the other end", NULL, NULL, NULL); + } + if (lfhasflag(user, F_ENHANCESMELL)) { + addchoice(&prompt, 's', "Sniff for scents", NULL, NULL, NULL); + } + addchoice(&prompt, '-', "(cancel)", NULL, NULL, NULL); + prompt.maycancel = B_TRUE; + if (prompt.nchoices == 1) { + msg("You have no way to check %s.", obname); + return B_TRUE; + } + ch = getchoice(&prompt); + + if ((ch == '-') || (ch == '\0')) { + msg("Cancelled."); + return B_TRUE; + } + c = getstairdestination(stairs, &madenewmap); + if (!c) { + msg("These stairs don't seem to go anywhere!"); + return B_TRUE; + } + + noredraw = B_TRUE; + + // move any lfs at the other end out of the way. + if (movelfsoutofway(c)) { + // can't move them? ie. no adj cells. + inway = c->lf; + } + + if (!inway) { + // temporarily put the player at the other end + // purposely not using movelf() because we don't + // want to trigger map enter effects. + origcell = user->cell; + origcell->lf = NULL; + user->cell = c; + c->lf = user; + } + + // now actually do the check + if (ch == 'l') { + lifeform_t *lf; + enum SKILLLEVEL slev; + char *movetext[MAXCANDIDATES],thismovetext[BUFLEN]; + int nposs = 0,n,movetextcount[MAXCANDIDATES],found,vol; + + slev = getskill(user, SK_LISTEN); + + msg("You listen at %s...",obname); + more(); + + if (inway) { + // just get the lf in the way + if (getnoisedetails(inway, N_WALK, thismovetext, NULL, &vol)) { + // doesn't make noise + nposs = 0; + } else { + if (slev >= PR_EXPERT) { + // overwrite name + real_getlfnamea(inway, thismovetext, B_FALSE); + } + movetext[0] = strdup(thismovetext); + movetextcount[0] = 1; + nposs = 1; + } + } else { + // get all lfs within hearing of the other end + for (lf = c->map->lf ; lf ; lf = lf->next) { + if (lf == user) continue; + // get movement text + if (getnoisedetails(lf, N_WALK, thismovetext, NULL, &vol)) continue; + if (slev >= PR_EXPERT) { + // overwrite name + real_getlfnamea(lf, thismovetext, B_FALSE); + } + if (canhear(user, lf->cell, vol)) { + // already have this text? + found = B_FALSE; + for (n = 0; n < nposs; n++) { + if (streq(movetext[n], thismovetext)) { + movetextcount[n]++; + found = B_TRUE; + break; + } + } + if (!found) { + movetext[nposs] = strdup(thismovetext); + movetextcount[nposs] = 1; + nposs++; + } + } + } + } + // announce + if (nposs) { + for (n = 0; n < nposs; n++) { + char amttext[BUFLEN]; + if (slev >= PR_EXPERT) { // we're hearing monster names + if (movetextcount[n] == 1) { + strcpy(amttext, ""); + } else if (movetextcount[n] <= 3) { + strcpy(amttext, "some "); + } else { + strcpy(amttext, "lots of "); + } + } else { // we're hearing monster sounds + strcpy(amttext, ""); + } + + if (slev >= PR_EXPERT) { + char *newtext; + if (movetextcount[n] > 1) { + newtext = makeplural(movetext[n]); + msg("You can hear %s%s.", amttext, noprefix(newtext)); + free(newtext); + } else { + msg("You can hear %s.", movetext[n]); + } + } else { + msg("You can hear %s%s", amttext, movetext[n]); + } + // free this mem + free(movetext[n]); + } + } else { + msg("You don't hear anything unusual."); + } + } else if (ch == 's') { // smell + lifeform_t *lf; + race_t *smellrace[MAXCANDIDATES]; + int nposs = 0,n,smellcount[MAXCANDIDATES], range = 0; + + msg("You sniff %s...",obname); + more(); + + f = lfhasflag(user, F_ENHANCESMELL); + if (f) { + range = f->val[0]; + } + + if (range) { + // list everyone you can smell. + if (inway) { + // just smell the lf in the way + if (issmellablelf(inway)) { + smellrace[0] = inway->race; + smellcount[0] = 1; + nposs = 1; + } else { + nposs = 0; + } + } else { + // get all lfs you can smell from the other end + for (lf = c->map->lf ; lf ; lf = lf->next) { + int found; + if (lf == user) continue; + if (getcelldist(lf->cell, c) <= range) { + // already have this one? + found = B_FALSE; + for (n = 0; n < nposs; n++) { + if (smellrace[n] == lf->race) { + smellcount[n]++; + found = B_TRUE; + break; + } + } + if (!found) { + smellrace[nposs] = lf->race; + smellcount[nposs] = 1; + nposs++; + } + } + } + } + } + // announce + if (nposs) { + for (n = 0; n < nposs; n++) { + char amttext[BUFLEN]; + char *newtext; + if (smellcount[n] == 1) { + strcpy(amttext, ""); + } else if (smellcount[n] <= 3) { + strcpy(amttext, "some "); + } else { + strcpy(amttext, "lots of "); + } + + if (smellcount[n] > 1) { + newtext = makeplural(smellrace[n]->name); + msg("You can smell %s%s.", amttext, newtext); + free(newtext); + } else { + msg("You can smell %s %s.", needan(smellrace[n]->name) ? "an" : "a", smellrace[n]->name); + } + } + } else { + msg("You don't smell anything unusual."); + } + } else if (ch == 'f') { // footprints + lifeform_t *lf; + object_t *trailob; + char *fpname[MAXCANDIDATES],thisfpname[BUFLEN]; + int nposs = 0,n,fpcount[MAXCANDIDATES]; + + msg("You inspect %s...",obname); + more(); + + if (inway) { + // just do footprints for the lf in the way + trailob = addtrail(inway, c, inway->facing, B_TRUE, B_FALSE); + if (trailob) { + getobname(trailob, thisfpname, 1); + fpname[0] = strdup(thisfpname); + fpcount[0] = 1; + nposs = 1; + killob(trailob); + } else { + nposs = 0; + } + } else { + // get all lfs which make trails from the other end + for (lf = c->map->lf ; lf ; lf = lf->next) { + if (lf == user) continue; + // within lof of stairs? + if (haslof(lf->cell, c, LOF_WALLSTOP, NULL)) { + trailob = addtrail(lf, c, lf->facing, B_TRUE, B_FALSE); + if (trailob) { + if (canseeob(user, trailob)) { + int found = B_FALSE; + // already a trail like this? + getobname(trailob, thisfpname, 1); + for (n = 0; n < nposs; n++) { + if (streq(fpname[n], thisfpname)) { + fpcount[n]++; + found = B_TRUE; + } + } + if (!found) { + fpname[nposs] = strdup(thisfpname); + fpcount[nposs] = 1; + nposs++; + } + } + killob(trailob); + } + } + } + } + // announce + if (nposs) { + for (n = 0; n < nposs; n++) { + char amttext[BUFLEN]; + if (fpcount[n] == 1) { + strcpy(amttext, ""); + } else if (fpcount[n] <= 3) { + strcpy(amttext, "several "); + } else { // 4+ + strcpy(amttext, "lots of "); + } + msg("You find %s%s.", amttext, fpname[n]); + } + } else { + msg("You don't find any unusual tracks."); + } + } else if (ch == 'p') { // peek + flag_t *awareness; + msg("You peek %s the stairs...", getdirname(stairdir)); more(); + // process light sources for the other end (otherwise new + // maps will be dark by default and you might not see anything) + calclight(c->map); + // prevent announcement of the flag we're about to give + user->born = B_FALSE; + // temporarily give the player 360degree sight + awareness = addflag(user->flags, F_AWARENESS, B_TRUE, NA, NA, NULL); + noredraw = B_FALSE; // allow redraws + setlosdirty(user); // this will redraw the screen + askcoords("Peek (ESC when done)->", "Peek (ESC when done)->", TT_NONE, user, UNLIMITED, LOF_DONTNEED, B_FALSE); + killflag(awareness); + user->born = B_TRUE; + } else { + msg("Not implemented yet."); + } // end ways of checking + + if (!inway) { + // move user back + user->cell->lf = NULL; + user->cell = origcell; + origcell->lf = user; + } + noredraw = B_FALSE; + + if (ch == 'p') { + // if we peeked, then we have to redraw now. + setlosdirty(user); + msg("You return to your original position."); + } + + taketime(user, getactspeed(user)*2); } else if (abilid == OT_A_COOK) { object_t *water,*o; race_t *r = NULL; @@ -5975,6 +6314,63 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } else { // monsters can't cast } + } else if (spellid == OT_S_INSTANTDISROBE) { + cell_t *newcell = NULL; + int howmany = (power / 4)+1,ndone = 0; + int i,announcethump = B_FALSE; + object_t *o; + target = targcell->lf; + if (!target) { + fizzle(caster); + return B_TRUE; + } + + // find cell next to caster + newcell = getrandomadjcell(targcell, WE_WALKABLE, B_NOEXPAND); + if (!newcell) { + fizzle(caster); + return B_TRUE; + } + for (i = 0; i < howmany; i++) { + // pick armour + o = getrandomarmour(target); + if (o) { + char obname[BUFLEN]; + // move it + ndone++; + moveob(o, newcell->obpile, o->amt); + if (isplayer(target)) { + getobname(o, obname, o->amt); + if (haslos(player, newcell)) { + msg("Your %s suddenly appears next to you!", noprefix(obname)); + } else { + msg("Your %s suddenly vanishes!", noprefix(obname)); + + announcethump = B_TRUE; + } + } else if (cansee(player, target)) { + char lfname[BUFLEN]; + getlfname(target, lfname); + if (haslos(player, newcell)) { + msg("%s%s %s suddenly appears next to it!", lfname, getpossessive(lfname), + noprefix(buf)); + } else { + msg("%s%s %s suddenly vanishes!", lfname, getpossessive(lfname), + noprefix(buf)); + } + } + } else { + break; + } + } + if (!ndone) { + fizzle(caster); + return B_TRUE; + } + + if (announcethump && !isdeaf(player)) { + msg("You hear something hitting the ground next to you."); + } } else if (spellid == OT_S_INVISIBILITY) { int howlong = 30; int willannounce = B_FALSE;