diff --git a/attack.c b/attack.c index 2bbf828..d2a95c4 100644 --- a/attack.c +++ b/attack.c @@ -761,8 +761,16 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) if (isunarmed) { f = lfhasflag(lf, F_FREEZINGTOUCH); if (f) { - // victim turns to ice for a while! - freezelf(victim, lf, rnd(5,10)); + int diff; + diff = f->val[2]; + if (isimmuneto(victim->flags, DT_COLD) || skillcheck(victim, SC_RESISTMAG, diff, 0)) { + if (isplayer(victim)) { + msg("You feel mildly chilly."); + } + } else { + // victim turns to ice for a while! + freezelf(victim, lf, rnd(5,10)); + } killflag(f); } } @@ -1074,6 +1082,24 @@ int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { return B_FALSE; } +int damtypecausesbleed(enum DAMTYPE dt) { + switch (dt) { + case DT_PIERCE: + case DT_SLASH: + case DT_BASH: + case DT_BITE: + case DT_CHOP: + case DT_PROJECTILE: + case DT_EXPLOSIVE: + case DT_UNARMED: + case DT_FALL: + return B_TRUE; + default: + break; + } + return B_FALSE; +} + // returns the amount of damage the armour blocked... int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype) { int reduceamt = 0; diff --git a/attack.h b/attack.h index 627b392..6cb1bf4 100644 --- a/attack.h +++ b/attack.h @@ -6,6 +6,7 @@ int attackcell(lifeform_t *lf, cell_t *c, int force); int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag); int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag); void confereffects(flagpile_t *fp, lifeform_t *victim); +int damtypecausesbleed(enum DAMTYPE dt); int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype); char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp); //object_t *getattackwep(lifeform_t *lf, obpile_t **unarmedpile, flag_t **unarmedflag); diff --git a/defs.h b/defs.h index e502cad..3c576cb 100644 --- a/defs.h +++ b/defs.h @@ -43,6 +43,7 @@ enum SKILL { SK_BACKSTAB, SK_CARTOGRAPHY, SK_CHANNELING, + SK_CLIMBING, SK_COOKING, SK_FIRSTAID, SK_LISTEN, @@ -90,7 +91,7 @@ enum SKILL { SK_SS_TRANSLOCATION, SK_SS_WILD, }; -#define MAXSKILLS 49 +#define MAXSKILLS 50 // proficiency levels enum SKILLLEVEL { @@ -145,6 +146,7 @@ enum CHECKTYPE { SC_IQ, SC_CON, ////////// + SC_CLIMB, SC_DISARM, SC_DODGE, SC_SHIELDBLOCK, @@ -1089,6 +1091,7 @@ enum OBTYPE { OT_LOCKPICK, OT_PANPIPES, OT_PICKAXE, + OT_ROPE, OT_TORCH, OT_TOWEL, // tech @@ -1106,6 +1109,7 @@ enum OBTYPE { OT_MOTIONSCANNER, OT_NVGOGGLES, OT_PAPERCLIP, + OT_PORTLADDER, OT_SLEEPINGBAG, OT_TELEPAD, OT_TENT, @@ -1518,6 +1522,7 @@ enum FLAG { F_LIGHTSOURCE, // a light source like a torch, lantern etc F_CHARGELOWMSG, // text = msg when charges are nearly out F_CHARGEOUTMSG, // text = msg when charges are gone + F_HELPSCLIMB, // object gives v0 bonus to sc_climb checks. // technology flags F_TECHLEVEL, // v0 is a PR_xxx enum for tech usage skill // what can you do with this object? @@ -1751,6 +1756,7 @@ enum FLAG { // v0 is spell id F_AVOIDCURSEDOB, // for AI animals - they will avoid walking on obid 'text' // (text is a long) + F_FALLDISTANCE, // how many floors this lf has fallen through. // ABILITY/SPELL FLAGS / ability flags / spell flags F_FAILEDINSPECT, // lf has failed an inspect check for item id v0 F_BOOSTSPELL, // v0 is active boost spell, v1 is ongoing mpcost, v2 is power @@ -1822,7 +1828,6 @@ enum FLAG { // this lf's spell casting at all. F_NODEATHANNOUNCE, // don't say 'the xx dies' if this lf dies F_BEHEADED, // use special corpse drop code - F_SILENTMOVE, // lf makes no noise when walking/flying F_MOVESPEED, // override default move speed F_ACTIONSPEED, // override default action speed F_SPELLSPEED, // override default spellcast speed (ie. movespeed) @@ -1925,6 +1930,7 @@ enum FLAG { // v1 = power // text = what from.eg'a bad egg' F_FREEZINGTOUCH,// next thing touched turns to ice! + // v2 is save difficulty F_GRABBEDBY,// you've been grabbed by lf id v0 F_GRABBING, // you are grabbing lf id v0 F_HEAVYBLOW, // next attack is a heavy blow @@ -1958,11 +1964,12 @@ enum FLAG { // text must have at least TWO words F_RISEASGHOST, // become a ghost when you die. F_SEEINDARK, // nightvis range is val0 - F_TREMORSENSE, // doesn't need eyes to see, can see in dark with v0 - F_TRUESTRIKE, // your attacks ALWAYS hit. turnsleft=v0 F_SEEINVIS, // can see invisible things + F_SILENTMOVE, // lf makes no noise when walking/flying F_STABILITY, // doesn't slip over F_STENCH, // creatures within v0 gain f_nauseated = v1 + F_TREMORSENSE, // doesn't need eyes to see, can see in dark with v0 + F_TRUESTRIKE, // your attacks ALWAYS hit. turnsleft=v0 F_PRODUCESLIGHT, // produces light of val0 radius. // (but not for obs in pack) // if val2 is true, will only make light if ob @@ -2670,6 +2677,7 @@ enum BRAND { BR_SHADOWS, BR_SLOTH, BR_SPEED, + BR_STEALTH, BR_POWER, BR_SWIFTNESS, BR_TELEKINESIS, diff --git a/io.c b/io.c index 7f4797e..7847f71 100644 --- a/io.c +++ b/io.c @@ -1417,6 +1417,12 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; } break; + case F_SILENTMOVE: + if (isplayer(lf)) { // don't know if monsters get it + msg("You now move silently."); + donesomething = B_TRUE; + } + break; case F_SLOWACT: msg("%s %s",lfname, isplayer(lf) ? "feel sluggish." : "looks sluggish."); donesomething = B_TRUE; @@ -1902,6 +1908,12 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; } break; + case F_SILENTMOVE: + if (isplayer(lf)) { // don't know if monsters lose it + msg("You no longer move silently."); + donesomething = B_TRUE; + } + break; case F_SLOWACT: msg("%s %s",lfname, isplayer(lf) ? "no longer feel sluggish." : "no longer looks sluggish."); donesomething = B_TRUE; @@ -2849,47 +2861,49 @@ void describeob(object_t *o) { // weight y = 4; - if (o->material->id == MT_FOOD) { - sprintf(buf, "%s food product%s, ",(o->amt == 1) ? "It is a" : "They are", - (o->amt == 1) ? "" : "s"); - } else { - sprintf(buf, "%s made from %s, ",(o->amt == 1) ? "It is" : "They are", o->material->name); - } - - if (o->amt == 1) { - char wbuf[BUFLEN]; - getweighttext(getobweight(o), wbuf); - sprintf(buf2, "and weighs %s.",wbuf); - strcat(buf, buf2); - } else { - char wbuf[BUFLEN]; - char wbuf2[BUFLEN]; - getweighttext(getobweight(o), wbuf); - getweighttext(getobunitweight(o), wbuf2); - sprintf(buf2, "and weigh %s (%s each).",wbuf, wbuf2); - strcat(buf, buf2); - } - mvwprintw(mainwin, y, 0, "%s",buf); - y++; - - throwrange = getmaxthrowrange(player, o); - if (throwrange >= 1) { - mvwprintw(mainwin, y, 0, " You could throw %s %d metres.",(o->amt == 1) ? "it" : "one", throwrange); - } else { - mvwprintw(mainwin, y, 0, " It is too heavy for you to throw."); - } - y++; - - f = hasflag(o->flags, F_THROWMISSILE); - if (f) { - if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT)) { - int dam; - dam = getthrowdam(o); - mvwprintw(mainwin, y, 0, " %s good for throwing [base damage %d].",(o->amt == 1) ? "It is" : "They are", dam); + if (o->material->id != MT_NOTHING) { + if (o->material->id == MT_FOOD) { + sprintf(buf, "%s food product%s, ",(o->amt == 1) ? "It is a" : "They are", + (o->amt == 1) ? "" : "s"); } else { - mvwprintw(mainwin, y, 0, " %s good for throwing.", (o->amt == 1) ? "It is" : "They are"); + sprintf(buf, "%s made from %s, ",(o->amt == 1) ? "It is" : "They are", o->material->name); + } + + if (o->amt == 1) { + char wbuf[BUFLEN]; + getweighttext(getobweight(o), wbuf); + sprintf(buf2, "and weighs %s.",wbuf); + strcat(buf, buf2); + } else { + char wbuf[BUFLEN]; + char wbuf2[BUFLEN]; + getweighttext(getobweight(o), wbuf); + getweighttext(getobunitweight(o), wbuf2); + sprintf(buf2, "and weigh %s (%s each).",wbuf, wbuf2); + strcat(buf, buf2); + } + mvwprintw(mainwin, y, 0, "%s",buf); + y++; + + throwrange = getmaxthrowrange(player, o); + if (throwrange >= 1) { + mvwprintw(mainwin, y, 0, " You could throw %s %d metres.",(o->amt == 1) ? "it" : "one", throwrange); + } else { + mvwprintw(mainwin, y, 0, " It is too heavy for you to throw."); } y++; + + f = hasflag(o->flags, F_THROWMISSILE); + if (f) { + if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT)) { + int dam; + dam = getthrowdam(o); + mvwprintw(mainwin, y, 0, " %s good for throwing [base damage %d].",(o->amt == 1) ? "It is" : "They are", dam); + } else { + mvwprintw(mainwin, y, 0, " %s good for throwing.", (o->amt == 1) ? "It is" : "They are"); + } + y++; + } } if (isedible(o)) { @@ -3049,6 +3063,13 @@ void describeob(object_t *o) { y++; } + f = hasflag(o->flags, F_HELPSCLIMB); + if (f) { + mvwprintw(mainwin, y, 0, "It can be used to assist in climbing."); + y++; + } + + // skip line y++; @@ -3364,6 +3385,9 @@ void describeob(object_t *o) { case F_SEEINDARK: mvwprintw(mainwin, y, 0, "%s allows you to see in the dark.", buf); y++; break; + case F_SILENTMOVE: + mvwprintw(mainwin, y, 0, "%s allows you to move silently.", buf); y++; + break; case F_SLOWACT: mvwprintw(mainwin, y, 0, "%s will slow down your actions.", buf); y++; break; @@ -3952,7 +3976,7 @@ void dovendingmachine(lifeform_t *lf, object_t *vm) { ch = f->val[0]; if (strlen(f->text)) { - o = addobject(op, f->text, B_FALSE); // no stacking! + o = addobject(op, f->text, B_FALSE, B_FALSE); // no stacking! // remember letter o->letter = ch; // make object fully known @@ -8325,6 +8349,11 @@ void showlfstats(lifeform_t *lf, int showall) { y++; } + f = lfhasflag(lf, F_SILENTMOVE); + if (f && (f->known)) { + mvwprintw(mainwin, y, 0, "%s move%s silently.", you(lf), isplayer(lf) ? "" : "s"); + y++; + } f = lfhasflag(lf, F_STABILITY); if (f && (f->known)) { diff --git a/lf.c b/lf.c index 15dea54..e0f41a4 100644 --- a/lf.c +++ b/lf.c @@ -49,11 +49,13 @@ extern int obdb; extern enum ERROR reason; + // for xplist race_t **raceposs; int *xpposs; int xplistlen; +int notime = B_FALSE; // prevent taketime from doing anything void autoweild(lifeform_t *lf) { object_t *bestwep,*bestfirearm; @@ -1923,6 +1925,15 @@ int digdown(lifeform_t *lf, object_t *o) { // TODO: check if the floor is solid? + if (o) { + if (lfhasflag(lf, F_LEVITATING)) { + if (isplayer(lf)) { + msg("You can't reach the ground from up here!"); + } + return B_TRUE; + } + } + if (isplayer(lf)) { msg("You dig a hole in the floor."); } else if (cansee(player, lf)) { @@ -1932,7 +1943,9 @@ int digdown(lifeform_t *lf, object_t *o) { addob(lf->cell->obpile, "hole in the ground"); // takes a lot of time - taketime(lf, getactspeed(lf) * 9); + if (o) { + taketime(lf, getactspeed(lf) * 9); + } return B_FALSE; } @@ -1948,13 +1961,14 @@ int digup(lifeform_t *lf, object_t *o) { return B_TRUE; } - // TODO: check if the roof is solid? - - if (!isairborne(lf)) { - if (isplayer(lf)) { - msg("You can't reach the roof!"); + // if digging with an object, you must be able to reach the roof + if (o) { + if (!isairborne(lf)) { + if (isplayer(lf)) { + msg("You can't reach the roof!"); + } + return B_TRUE; } - return B_TRUE; } if (isplayer(lf)) { @@ -1969,7 +1983,9 @@ int digup(lifeform_t *lf, object_t *o) { addob(lf->cell->obpile, "hole in the roof"); // takes a LOT of time since gravity is against us - taketime(lf, getactspeed(lf) * 18); + if (o) { + taketime(lf, getactspeed(lf) * 18); + } return B_FALSE; } @@ -3170,8 +3186,8 @@ int getactspeed(lifeform_t *lf) { return speed; } -// only include allies which will follow you up/down stairs etc -void getadjallies(lifeform_t *lf, lifeform_t **adjally, int *nadjallies) { +// include allies or enemies which will follow you up/down stairs etc +void getadjallies(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *nadjallies) { int d; for (d = DC_N; d <= DC_NW; d++) { cell_t *c; @@ -3179,6 +3195,13 @@ void getadjallies(lifeform_t *lf, lifeform_t **adjally, int *nadjallies) { if (c && c->lf) { if (areallies(lf, c->lf) || areenemies(lf, c->lf)) { if (!isimmobile(c->lf) && cansee(c->lf, lf)) { + int ok = B_TRUE; + // if this was a pit, only flying things will follow + if (stairob && hasflag(stairob->flags, F_PIT)) { + if (!lfhasflag(c->lf, F_FLYING)) { + ok = B_FALSE; + } + } adjally[*nadjallies] = c->lf; (*nadjallies)++; } @@ -6565,6 +6588,11 @@ int haslos(lifeform_t *viewer, cell_t *dest) { if (!islit(dest)) { return B_FALSE; } + } else { + // inside our nightvis range and magically dark + if (dest->lit == L_PERMDARK) { + return B_FALSE; + } } } @@ -6723,6 +6751,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LOCKPICKING, PR_NOVICE, NA, NULL); mayusespellschool(lastjob->flags, SS_ALLOMANCY, F_CANCAST); 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_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_STEALTH, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_TECHUSAGE, NA, NA, NULL); @@ -6752,6 +6781,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LORE_NATURE, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_TRACKING, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CARTOGRAPHY, 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_LISTEN, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_LOCKPICKING, NA, NA, NULL); @@ -6797,6 +6827,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_LORE_HUMANOID, PR_SKILLED, NA, NULL); 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_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_LISTEN, NA, NA, NULL); @@ -6845,6 +6876,7 @@ void initjobs(void) { addflag(lastjob->flags, F_CANWILL, OT_A_JUMP, 3, 3, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SWIMMING, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_ATHLETICS, NA, NA, NULL); + addflag(lastjob->flags, F_CANLEARN, SK_CLIMBING, NA, 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_TECHUSAGE, NA, NA, NULL); @@ -6901,6 +6933,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "5 potions of rum"); // skills addflag(lastjob->flags, F_STARTSKILL, SK_CARTOGRAPHY, PR_SKILLED, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_CLIMBING, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LONGBLADES, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_UNARMED, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SWIMMING, PR_ADEPT, NA, NULL); @@ -6927,6 +6960,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "5 lockpicks"); addflag(lastjob->flags, F_MPDICE, 1, NA, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_CARTOGRAPHY, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_CLIMBING, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_STEALTH, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_LISTEN, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_BACKSTAB, PR_BEGINNER, NA, NULL); @@ -10622,16 +10656,8 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml } // occasionally drop blood - switch (damtype) { - case DT_POISON: - case DT_POISONGAS: - case DT_WATER: - break; - default: - if (onein(3)) { - bleed(lf); - } - break; + if (damtypecausesbleed(damtype) && onein(3)) { + bleed(lf); } if (hasflag(lf->flags, F_DEBUG)) { @@ -12339,6 +12365,7 @@ void initskills(void) { addskill(SK_BACKSTAB, "Backstab", "Lets you inflict massive damage with stabs when unseen.", 50); addskill(SK_CARTOGRAPHY, "Cartography", "Your ability to create and interpret maps.", 0); // untrainable addskill(SK_CHANNELING, "Channeling", "Lets you make better use of magical items.", 0); // untrainable + addskill(SK_CLIMBING, "Climbing", "Helps you to climb walls, mountains or other terrain.", 50); addskill(SK_COOKING, "Cooking", "Your ability to combine foods into nutritious meals.", 50); addskill(SK_FIRSTAID, "First Aid", "Increases your healing rate and reduces duration of poison.", 0); // untrainable addskill(SK_LISTEN, "Listen", "How good you are at hearing and interpreting sounds.", 100); @@ -12507,6 +12534,9 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r attrib += (getattr(lf, A_DEX)/4); } break; + case SC_CLIMB: + attrib = (getskill(lf, SK_CLIMBING)*2); + break; case SC_DODGE: // getevasion returns 0-100 (ie. pct chance) // convert this to 0-20 @@ -12535,7 +12565,15 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r levmod = (lf->level / 3); // other modifiers - if (ct == SC_SLIP) { + if (ct == SC_CLIMB) { + object_t *o; + for (o = lf->pack->first ; o ; o = o->next) { + f = hasflag(o->flags, F_HELPSCLIMB); + if (f && isknown(o)) { + othermod += f->val[0]; + } + } + } else if (ct == SC_SLIP) { if (getequippedob(lf->pack, BP_FEET)) { othermod += 5; } @@ -13001,8 +13039,7 @@ void taketime(lifeform_t *lf, long howlong) { int db = B_FALSE; map_t *map; - - if (lfhasflag(lf, F_NOTIME)) { + if (notime || lfhasflag(lf, F_NOTIME)) { return; } @@ -13061,6 +13098,10 @@ int throwat(lifeform_t *thrower, object_t *o, cell_t *where) { void timeeffectslf(lifeform_t *lf) { object_t *o, *nexto; flag_t *f,*nextf; + int dir; + + // make SURE we don't take any time! + notime = B_TRUE; // decrement flags timeeffectsflags(lf->flags); @@ -13082,6 +13123,7 @@ void timeeffectslf(lifeform_t *lf) { } if (isdead(lf)) { + killflagsofid(lf->flags, F_NOTIME); return; } @@ -13121,19 +13163,78 @@ void timeeffectslf(lifeform_t *lf) { } // holes in the floor/roof - o = hasobwithflagval(lf->cell->obpile, F_PIT, D_DOWN, NA, NA, NULL); - if (o) { - if (!isairborne(lf)) { - usestairs(lf, o, B_FALSE); - } - } - o = hasobwithflagval(lf->cell->obpile, F_PIT, D_UP, NA, NA, NULL); - if (o) { - if (lfhasflag(lf, F_LEVITATING)) { - usestairs(lf, o, B_FALSE); + for (dir = D_UP; dir <= D_DOWN; dir++) { + int donesomething = B_TRUE; + o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL); + while (o && donesomething) { + int willfall = B_FALSE; + donesomething = B_FALSE; + if ((dir == D_DOWN) && !isairborne(lf)) { + willfall = B_TRUE; + } else if ((dir == D_UP) && lfhasflag(lf, F_LEVITATING)) { + willfall = B_TRUE; + } + + if (willfall) { + usestairs(lf, o, B_FALSE); + donesomething = B_TRUE; + o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL); + } } } + notime = B_FALSE; +} + +// return B_TRUE on failure. +int tryclimb(lifeform_t *lf, cell_t *where, char *towhat) { + // if you have a rope or there's an adjacent wall, you can try + // to climb up + int adjwalls; + char lfname[BUFLEN]; + getlfname(lf, lfname); + adjwalls = countadjwalls(where); + if (adjwalls || hasobwithflag(lf->pack, F_HELPSCLIMB)) { + if (isplayer(lf)) { + msg("You start climbing..."); + } else if (cansee(player, lf)) { + msg("%s starts climbing...", lfname); + } + + taketime(lf, getactspeed(lf)); + + // base difficulty of 20 + if (skillcheck(lf, SC_CLIMB, 20, (countadjwalls(where)+1)/2)) { + // you made it! + if (isplayer(lf)) { + msg("You reach %s.", towhat); + } else if (cansee(player, lf)) { + msg("%s reaches %s.", towhat); + } + // train climbing + practice(lf, SK_CLIMBING, 1); + // continue... + + } else { + // you fall. + if (isplayer(lf)) { + msg("You fall to the ground!"); + } else if (cansee(player, lf)) { + msg("%s falls to the ground!", lfname); + } + fall(lf, NULL, B_FALSE); // this will take some time. + losehp(lf, roll("1d6"), DT_FALL, NULL, "a fall while climbing"); + return B_TRUE; + } + } else { // no rope or adjacent walls + if (isplayer(lf)) { + msg("You can't reach the roof!"); + } + return B_TRUE; + } + + // success + return B_FALSE; } ////////////////////////////////// @@ -13967,6 +14068,7 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { flag_t *f; map_t *curmap; map_t *newmap; + cell_t *obcell; cell_t *newcell; int dir; int newdepth; @@ -13985,20 +14087,21 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { getlfname(lf, lfname); getobname(o, obname, 1); + obcell = getoblocation(o); if (initiatemove(lf, NULL, NULL)) { // failed? return B_FALSE; } - curmap = lf->cell->map; + curmap = obcell->map; f = hasflag(o->flags, F_CLIMBABLE); assert(f); dir = f->val[0]; if (f->val[1] == NA) { // use same region - newregion = lf->cell->map->region; + newregion = obcell->map->region; } else { newregion = findregion(f->val[1]); } @@ -14032,11 +14135,13 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { // can only go up if you have a rope or are flying/levitating if (lfhasflag(lf, F_LEVITATING) || lfhasflag(lf, F_FLYING)) { // ok. - } else { // TODO: if has rope??? - if (isplayer(lf)) { - msg("You can't reach the roof!"); + } else { + char buf[BUFLEN]; + sprintf(buf, "the %s", noprefix(obname)); + if (tryclimb(lf, obcell, buf)) { + // failed + return B_TRUE; } - return B_TRUE; } } } @@ -14070,9 +14175,9 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { } // find adjacent allies or enemies which will follow you - // (but not into/out of pits) - if (isplayer(lf) || !hasflag(o->flags, F_PIT)) { - getadjallies(lf, adjally, &nadjallies); + // (getadjallies will handle following through pits) + if (isplayer(lf)) { + getadjallies(lf, o, adjally, &nadjallies); } // do stairs go somewhere? @@ -14100,9 +14205,11 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { // previous/next levels in the same region. // if not, we need to call linkstairs() on the staircase first. + /* if (newmap->region->id != curmap->region->id) { linkstairs(o); } +*/ // at this point, stairs should have a destination newcell = getstairdestination(o); } @@ -14112,10 +14219,29 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { curs_set(0); if (newcell) { int n; + + // if we just climbed up through a hole, and are not flying, we want to + // end up adjacent to the hole in the ground. otherwise we'll just fall + // straight back down! + if (hasflag(o->flags, F_PIT) && (dir == D_UP) && !isairborne(lf)) { + cell_t *noholecell; + noholecell = real_getrandomadjcell(newcell, WE_WALKABLE, B_ALLOWEXPAND, LOF_NEED, NULL); + if (noholecell) { + // go here instead + newcell = noholecell; + } else { + // alert + if (isplayer(lf)) { + msg("You can't find anywhere safe to get out."); + } + } + + } + // check noone is in the way if (newcell->lf) { cell_t *c; - // if they are, move them + // if they are, find somewhere to move them. c = getrandomadjcell(newcell, WE_WALKABLE, B_ALLOWEXPAND); if (c) { // move them there @@ -14168,24 +14294,56 @@ int usestairs(lifeform_t *lf, object_t *o, int onpurpose) { if (falling) { if (dir == D_DOWN) { - if (isplayer(lf)) { - msg("You slam into the ground!"); - } else if (cansee(player, lf)){ - msg("%s slams into the ground!", lfname); + if (hasobwithflagval(lf->cell->obpile, F_PIT, D_DOWN, NA, NA, NULL)) { + flag_t *ff; + // inc fall distance + ff = lfhasflag(lf, F_FALLDISTANCE); + if (ff) { + ff->val[0]++; + } else { + addflag(lf->flags, F_FALLDISTANCE, 1, NA, NA, NULL); + } + } else { + int howfar; + if (isplayer(lf)) { + msg("You slam into the ground!"); + } else if (cansee(player, lf)){ + msg("%s slams into the ground!", lfname); + } + // how far did you fall? + sumflags(lf->flags, F_FALLDISTANCE, &howfar, NULL, NULL); + howfar++; + // take fall damage. 2d6 per level. + losehp(lf, rolldie(howfar*2, 6), DT_FALL, NULL, "falling"); + killflagsofid(lf->flags, F_FALLDISTANCE); + // fall over + fall(lf, NULL, B_FALSE); } - // take fall damage - losehp(lf, roll("2d6"), DT_FALL, NULL, "falling"); - // fall over - fall(lf, NULL, B_FALSE); } else { // TODO: if you are outside, DIE! - if (isplayer(lf)) { - msg("You slam into the roof!"); - } else if (cansee(player, lf)){ - msg("%s slams into the roof!", lfname); + if (hasobwithflagval(lf->cell->obpile, F_PIT, D_UP, NA, NA, NULL)) { + flag_t *ff; + // inc fall distance + ff = lfhasflag(lf, F_FALLDISTANCE); + if (ff) { + ff->val[0]++; + } else { + addflag(lf->flags, F_FALLDISTANCE, 1, NA, NA, NULL); + } + } else { + int howfar; + if (isplayer(lf)) { + msg("You slam into the roof!"); + } else if (cansee(player, lf)){ + msg("%s slams into the roof!", lfname); + } + // how far did you fall? + sumflags(lf->flags, F_FALLDISTANCE, &howfar, NULL, NULL); + howfar++; + // take hitting roof damage (less than floor). 1d4 per level. + losehp(lf, rolldie(howfar, 4), DT_FALL, NULL, "slamming into the roof"); + killflagsofid(lf->flags, F_FALLDISTANCE); } - // take hitting roof damage - losehp(lf, roll("1d4"), DT_FALL, NULL, "slamming into the roof"); } } diff --git a/lf.h b/lf.h index 9ae42de..3918119 100644 --- a/lf.h +++ b/lf.h @@ -79,7 +79,7 @@ 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, lifeform_t **adjally, int *nadjallies); +void getadjallies(lifeform_t *lf, object_t *stairob, lifeform_t **adjally, int *nadjallies); enum ALLEGIENCE getallegiance(lifeform_t *lf); int getallouterarmour(lifeform_t *lf, object_t **ob, int *nobs); object_t *getarmour(lifeform_t *lf, enum BODYPART bp); @@ -295,6 +295,7 @@ int takeoff(lifeform_t *lf, object_t *o); void taketime(lifeform_t *lf, long howlong); int throwat(lifeform_t *thrower, object_t *o, cell_t *where); void timeeffectslf(lifeform_t *lf); +int tryclimb(lifeform_t *lf, cell_t *where, char *towhat); void turneffectslf(lifeform_t *lf); int touch(lifeform_t *lf, object_t *o); void unpoison(lifeform_t *lf); diff --git a/map.c b/map.c index 591546f..d934a11 100644 --- a/map.c +++ b/map.c @@ -389,6 +389,20 @@ int addrandomthing(cell_t *c, int obchance, int *nadded) { return rv; } +map_t *getmapindir(map_t *src, int dir) { + map_t *other = NULL; + if (src->nextmap[dir] != -1) { + other = findmap(src->nextmap[dir]); + } else { + if (dir == D_DOWN) { + other = findregionmap(src->region->id, src->depth+1); + } else { + other = findregionmap(src->region->id, src->depth-1); + } + } + return other; +} + // populates retcell[] with all cells within given radius of centre void getradiuscells(cell_t *centre, int radius, int dirtype, enum LOFTYPE needlof, int wantcentre, cell_t **retcell, int *ncells) { int (*distfunc)(cell_t *, cell_t *); @@ -1276,6 +1290,20 @@ int countadjcellsoftype(cell_t *cell, int id) { return count; } +int countadjwalls(cell_t *cell) { + int d; + int walls = 0; + + for (d = DC_N; d <= DC_NW; d++) { + cell_t *newcell; + newcell = getcellindir(cell, d); + if (!newcell || newcell->type->solid) { + walls++; + } + } + return walls; +} + int countcellexits(cell_t *cell) { int d; int exits = 0; @@ -1751,7 +1779,6 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex map_t *m; char buf[BUFLEN]; int i,x,y; - int firstworldmap = B_FALSE; enum HABITAT habitat; regionthing_t *thing[MAXOUTLINETHINGS]; int nthings = 0; @@ -1760,10 +1787,11 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex // note: we might override this later based on our region outline habitat = region->rtype->defaulthabitat; + /* if ((region->rtype->id == RG_WORLDMAP) && (depth == 1)) { firstworldmap = B_TRUE; } - + */ map->beingcreated = B_TRUE; map->depth = depth; @@ -1777,6 +1805,9 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex // link to other maps if required. // default to no links + for (i = D_N; i <= D_W; i++) { + map->nextmap[i] = -1; + } for (i = D_UP; i <= D_DOWN; i++) { map->nextmap[i] = -1; } @@ -1795,17 +1826,27 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex } } + // did we come from a previous map? if (parentmap) { parentmap->nextmap[exitdir] = map->id; - for (i = 0; i < MAXDIR_ORTH; i++) { - if (parentmap && (i == diropposite(exitdir)) ) { + map->nextmap[diropposite(exitdir)] = parentmap->id; + } + + /* + if (map->region->rtype->id == RG_WORLDMAP) { + map_t *adjmap; + for (i = D_N; i <= D_W; i++) { + // is there a map this dir from us??? + adjmap = findmapcoords(, x, y); + if (i == diropposite(exitdir) ) { map->nextmap[i] = parentmap->id; } else { map->nextmap[i] = -1; } } } + */ map->w = MAX_MAPW; map->h = MAX_MAPH; @@ -1818,7 +1859,8 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex srand(map->seed); // set map coords - if (firstworldmap) { + // first world map?? + if (map == firstmap) { addflag(map->flags, F_MAPCOORDS, 0, 0, NA, NULL); } else { // set mapcoords if not already done. @@ -1947,7 +1989,8 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex } } - // link up holes - this will add matching holes on adjacent maps + // link up holes - this will create NEW holes in THIS map connecting to + // EXISTING unlinked holes in adjacent maps linkholes(map); // add random objects and monsters @@ -2175,8 +2218,8 @@ void createpit(map_t *map, int depth, map_t *parentmap, int exitdir) { c = getrandomcell(map); // clear it setcelltype(c, CT_CORRIDOR); - // put an exit here - o = addob(c->obpile, "hole in the roof"); + // put an exit here - don't link it yet - this will happen back in createmap(). + o = addobject(c->obpile, "hole in the roof", B_FALSE, B_FALSE); assert(o); } @@ -2622,10 +2665,10 @@ map_t *findsurfaceexitmap(map_t *m) { if (c) { for (o = c->obpile->first ; o ; o = o->next) { if (hasflagval(o->flags, F_CLIMBABLE, D_UP, NA, NA, NULL)) { - cell_t *c; - c = getstairdestination(o); - if (c) { - return c->map; + cell_t *newc; + newc = getstairdestination(o); + if (newc) { + return newc->map; } } } @@ -3449,34 +3492,42 @@ int iswallindir(cell_t *cell, int dir) { return B_FALSE; } -// search for unlinked pits/holes in roof in adjacent maps -// if we find any, add a matching end as close as we can in this map. +// search for unlinked pits/holes in roof in ADJACENT maps +// if we find any, add a matching end as close as we can in THIS map. void linkholes(map_t *map) { map_t *adjmap; cell_t *c; object_t *o, *newob; int x,y; - // check previous map for pits. - adjmap = findregionmap(map->region->id, map->depth-1); - if (adjmap) { - for (y = 0; y < adjmap->h; y++) { - for (x = 0; x < adjmap->w; x++) { - c = getcellat(adjmap, x, y); - if (c) { - for (o = c->obpile->first ; o ; o = o->next) { - if (hasflagval(o->flags, F_PIT, D_DOWN, NA, NA, NULL) && - !hasflag(o->flags, F_MAPLINK)) { - cell_t *c2; - objecttype_t *ot; - ot = getoppositestairs(o->type); - // make a link to it in this map, as close as possible to same pos - c2 = getcellat(map, x, y); - if (c2->type->solid) { - c2 = real_getrandomadjcell(c2, WE_NOTWALL, B_ALLOWEXPAND, LOF_DONTNEED, &ot->id); + int dir; + + for (dir = D_UP ; dir <= D_DOWN; dir++) { + adjmap = getmapindir(map, dir); + if (adjmap) { + for (y = 0; y < adjmap->h; y++) { + for (x = 0; x < adjmap->w; x++) { + c = getcellat(adjmap, x, y); + if (c) { + // does the adjacent map have an unlinked pit going to us? + for (o = c->obpile->first ; o ; o = o->next) { + if (hasflagval(o->flags, F_PIT, diropposite(dir), NA, NA, NULL) && + !hasflag(o->flags, F_MAPLINK)) { + cell_t *c2; + objecttype_t *ot; + ot = getoppositestairs(o->type); + // make a link to it in this map, as close as possible to same pos + c2 = getcellat(map, x, y); + if (c2->type->solid) { + // this will automatically avoid lifeforms since we're using + // we_walkable rather than we_notwall. this saves problems + // with someine coming up underneath you! + c2 = real_getrandomadjcell(c2, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, &ot->id); + } + // note we specifically say DONT link the new hole! + newob = addobject(c2->obpile, ot->name, B_FALSE, B_FALSE); + // link them! + linkstairs(newob); } - newob = addob(c2->obpile, ot->name); - // link them! - linkstairs(newob); } } } @@ -3489,7 +3540,6 @@ void linkholes(map_t *map) { // returns TRUE if it failed because othermap doesn't exist. int linkstairs(object_t *o) { map_t *othermap; - int othermapid; object_t *o2; map_t *stairmap; cell_t *staircell; @@ -3522,13 +3572,7 @@ int linkstairs(object_t *o) { // if so, find the first map in that region (ie depth 1) othermap = findregionmap(f->val[1], 1); } else { - othermapid = stairmap->nextmap[f->val[0]]; - othermap = findmap(othermapid); - - if (!othermap) { - // find next map based on depth... - othermap = findregionmap(stairmap->region->id, stairmap->depth+dir); - } + othermap = getmapindir(stairmap, f->val[0]); } if (othermap) { // find an empty staircase in other map @@ -3536,33 +3580,19 @@ int linkstairs(object_t *o) { c2 = othermap->cell[n]; o2 = hasob(c2->obpile, otherstairtype->id); if (o2) { - int ok = B_FALSE; // does it go nowhere? if (!hasflag(o2->flags, F_MAPLINK)) { - if (othermap->region->id == stairmap->region->id) { - ok = B_TRUE; - } else { - /* - // handle cross-region stairs! - f = hasflag(o2->flags, F_CLIMBABLE); - if (f->val[1] == stairmap->region->id) { - ok = B_TRUE; - } - */ - ok = B_TRUE; - } - - if (ok) { - char obid[BUFLEN]; - // link it to here! - sprintf(obid, "%ld", o->id); - addflag(o2->flags, F_MAPLINK, stairmap->id, NA, NA, obid); - // link me to there - sprintf(obid, "%ld", o2->id); - addflag(o->flags, F_MAPLINK, othermap->id, NA, NA, obid); - found = B_TRUE; - break; - } + char obid[BUFLEN]; + // link it to here! + sprintf(obid, "%ld", o->id); + addflag(o2->flags, F_MAPLINK, stairmap->id, NA, NA, obid); + // link me to there + sprintf(obid, "%ld", o2->id); + addflag(o->flags, F_MAPLINK, othermap->id, NA, NA, obid); + found = B_TRUE; + // now mark that we are not a new staircase to a new region anymore + f->val[1] = NA; + break; } } } @@ -3883,3 +3913,8 @@ int wallstoleftright(cell_t *c, int dir) { } return B_FALSE; } + + + + + diff --git a/map.h b/map.h index a2c0426..d74cb1d 100644 --- a/map.h +++ b/map.h @@ -24,6 +24,7 @@ int getdoorlockdiff(int depth); int getdoorsecretdiff(int depth); flag_t *getmapcoords(map_t *m, int *x, int *y); int getmapdifficulty(map_t *m); +map_t *getmapindir(map_t *src, int dir); void getradiuscells(cell_t *centre, int radius, int dirtype, enum LOFTYPE needlof, int wantcentre, cell_t **retcell, int *ncells); void getroomedge(map_t *m, int roomid, int minx, int miny, int maxx, int maxy, int whichside, cell_t **retcell, int *ncells, int onlywantsolid); object_t *gettopobject(cell_t *where, int forglyph); @@ -31,6 +32,7 @@ void calclight(map_t *map); int calcroompos(map_t *map, int w, int h, int *bx, int *by, int force); int countadjcellsoftype(cell_t *cell, int id); int countadjcellswithflag(cell_t *cell, enum FLAG fid); +int countadjwalls(cell_t *cell); int countcellexits(cell_t *cell); void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir); void createforest(map_t *map, int depth, map_t *parentmap, int exitdir); diff --git a/move.c b/move.c index 53a99a1..f11fc34 100644 --- a/move.c +++ b/move.c @@ -168,13 +168,24 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err for (o = cell->obpile->first ; o ; o = o->next) { - if (hasflag(o->flags, F_TRAP) && canseeob(lf, o)) { + if (onlyifknown && !canseeob(lf, o)) continue; + if (hasflag(o->flags, F_TRAP)) { if (error) { *error = E_AVOIDOB; rdata = o; } return B_TRUE; } + f = hasflag(o->flags, F_PIT); + if (f && (f->val[0] == D_DOWN)) { + if (!isairborne(lf)) { + if (error) { + *error = E_AVOIDOB; + rdata = o; + } + return B_TRUE; + } + } f = hasflag(o->flags, F_DEEPWATER); if (f) { // non swimming creature in water? @@ -340,6 +351,10 @@ int diropposite(int dir) { return DC_E; case DC_NW: return DC_SE; + case D_UP: + return D_DOWN; + case D_DOWN: + return D_UP; } // should never happen! return dir; @@ -582,6 +597,39 @@ int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, in return bestdir; } +int getwalkoffdir(lifeform_t *lf, int dir) { + switch (dir) { + case DC_NE: + if (lf->cell->y == 0) { + return DC_N; + } else if ( lf->cell->x == (lf->cell->map->w-1)) { + return DC_E; + } + break; + case DC_SE: + if (lf->cell->y == (lf->cell->map->h-1)) { + return DC_S; + } else if ( lf->cell->x == (lf->cell->map->w-1)) { + return DC_E; + } + break; + case DC_SW: + if (lf->cell->y == (lf->cell->map->h-1)) { + return DC_S; + } else if ( lf->cell->x == 0) { + return DC_W; + } + break; + case DC_NW: + if (lf->cell->y == 0) { + return DC_N; + } else if ( lf->cell->x == 0) { + return DC_W; + } + break; + } + return D_NONE; +} // use 'n/a' for zero chance of falling. 0 means 'calculate based on distance' int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallcheckdiff) { @@ -2018,15 +2066,22 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { if (isswimming(lf)) { practice(lf, SK_SWIMMING, 1); } - } else { + } else { // ie !moveok object_t *inway = NULL; int door, dooropen; reason = errcode; switch (errcode) { case E_OFFMAP: - if ((lf->cell->map->region == RG_WORLDMAP) && (isorthogonal(dir))) { - // we are allowed to walk off the edge - return walkoffmap(lf, dir, B_TRUE); + if (lf->cell->map->region->rtype->id == RG_WORLDMAP) { + // cope with nonorthogonal! + // ie. ne counts as n if we are at the top. + if (!isorthogonal(dir)) { + dir = getwalkoffdir(lf, dir); + } + if (dir != D_NONE) { + // we are allowed to walk off the edge + return walkoffmap(lf, dir, B_TRUE); + } } // otherwise fall through... case E_WALLINWAY: @@ -2264,7 +2319,7 @@ int walkoffmap(lifeform_t *lf, int dir, int onpurpose) { if (onpurpose) { // get list of adjacent allies if (isplayer(lf)) { - getadjallies(lf, adjally, &nadjallies); + getadjallies(lf, NULL, adjally, &nadjallies); } } diff --git a/move.h b/move.h index 1b71066..7624bd1 100644 --- a/move.h +++ b/move.h @@ -10,6 +10,7 @@ int diropposite(int dir); int dorandommove(lifeform_t *lf, int badmovesok, int restonfail); int getdiraway(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int dirtype); int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck, int dirtype); +int getwalkoffdir(lifeform_t *lf, int dir); int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallcheckdiff); int moveawayfrom(lifeform_t *lf, cell_t *dst, int dirtype); int moveclear(lifeform_t *lf, int dir, enum ERROR *error); diff --git a/nexus.c b/nexus.c index 6d7be35..a89f29c 100644 --- a/nexus.c +++ b/nexus.c @@ -192,7 +192,7 @@ int main(int argc, char **argv) { // create first dungeon dregion = findregionbytype(RG_FIRSTDUNGEON); dmap = addmap(); - createmap(dmap, 1, dregion, NULL, D_NONE); + createmap(dmap, 1, dregion, firstmap, D_DOWN); } // find staircase diff --git a/objects.c b/objects.c index f49cd0a..f87f856 100644 --- a/objects.c +++ b/objects.c @@ -390,7 +390,7 @@ void addocnoun(objectclass_t *oc, char *text) { // create a new object, stacking ok object_t *addob(obpile_t *where, char *name) { - return addobject(where, name, B_TRUE); + return addobject(where, name, B_TRUE, B_TRUE); } // create a new object @@ -399,7 +399,7 @@ object_t *addob(obpile_t *where, char *name) { // object with o->amt set, instead of // creating new obejct entries. // NOTE: This function MUST return number of obs created via global "nretobs" -object_t *addobject(obpile_t *where, char *name, int canstack) { +object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes) { objecttype_t *ot; object_t *o = NULL; char *p,*nsp,*p2; @@ -981,52 +981,25 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { addflag(o->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, selhn->text); } + if (o && hasflag(o->flags,F_PIT)) { + dblog("added pit"); + } - // link holes to adjacent maps - if (o && hasflag(o->flags, F_PIT)) { + // create linked holes in adjacent maps + if (wantlinkholes && o && hasflag(o->flags, F_PIT)) { cell_t *c; map_t *adjmap = NULL; f = hasflag(o->flags, F_PIT); c = getoblocation(o); - if ((c->map->region->rtype->id == RG_WORLDMAP) && (f->val[0] == D_DOWN)) { - // ie. going down from the surface. MUST be down because holes - // going up make no sense! + adjmap = getmapindir(c->map, f->val[0]); + if ((c->map->region->rtype->id == RG_WORLDMAP) && !adjmap) { + // ie. going down from the surface, and no dungeon below. + // ( MUST be down because holes going up make no sense! ) createregionlink(c->map, c, o, NULL, RG_PIT, c->map->region); } else { - // create linked holes on any existing adjacent maps - if (f->val[0] == D_DOWN) { - adjmap = findregionmap(c->map->region->id, c->map->depth+1); - } else if (f->val[0] == D_UP) { - if ((c->map->region->rtype->id != RG_WORLDMAP) && - (c->map->depth == 1) && - (c->map->region->rtype->deeperdir == D_DOWN)) { - cell_t *newcell; - object_t *newob; - char buf[BUFLEN]; - // ie. digging up to the surface - adjmap = findsurfaceexitmap(c->map); - // make a hole here. don't use linkholes since it can't - // cross regions. - newcell = getcellat(adjmap, c->x, c->y); - if (newcell->type->solid) { - newcell = real_getrandomadjcell(newcell, WE_NOTWALL, B_ALLOWEXPAND, LOF_DONTNEED, &(o->type->id)); - } - - newob = addob(newcell->obpile, "hole in the ground"); - sprintf(buf, "%ld", o->id); - addflag(newob->flags, F_MAPLINK, c->map->id, NA, NA, buf); - - sprintf(buf, "%ld", newob->id); - addflag(o->flags, F_MAPLINK, adjmap->id, NA, NA, buf); - - // don't call linkholes - adjmap = NULL; - } else { - adjmap = findregionmap(c->map->region->id, c->map->depth-1); - } - } + // create linked holes on the map at the other end of this one. if (adjmap) { linkholes(adjmap); } @@ -5043,6 +5016,8 @@ void initobjects(void) { addflag_real(lastbrand->flags, F_EQUIPCONFER, F_FASTMOVE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1); addbrand(BR_SLOTH, "of sloth", BP_FEET); addflag_real(lastbrand->flags, F_EQUIPCONFER, F_SLOWMOVE, 5, NA, NULL, PERMENANT, B_UNKNOWN, -1); + addbrand(BR_STEALTH, "of stealth", BP_FEET); + addflag_real(lastbrand->flags, F_EQUIPCONFER, F_SILENTMOVE, B_TRUE, NA, NULL, PERMENANT, B_UNKNOWN, -1); // hands addbrand(BR_POWER, "of power", BP_HANDS); @@ -6131,7 +6106,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); // l2 - addot(OT_S_FREEZEOB, "freezing touch", "Permenantly changes the next object touched into solid ice.", MT_NOTHING, 0, OC_SPELL); + 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); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -6709,7 +6684,7 @@ void initobjects(void) { addot(OT_WAND_DIGGING, "wand of digging", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND); addflag(lastot->flags, F_RARITY, H_DUNGEON, 80, RR_RARE, NULL); addflag(lastot->flags, F_LINKSPELL, OT_S_DIG, NA, NA, NULL); - addflag(lastot->flags, F_OPERNEEDTARGET, TT_NONE, NA, NA, NULL); + //addflag(lastot->flags, F_OPERNEEDTARGET, TT_NONE, NA, NA, NULL); addot(OT_WAND_COLD, "wand of cold", "A limited-use magical wand which casts the imbued spell.", MT_METAL, 0.5, OC_WAND); addflag(lastot->flags, F_RARITY, H_DUNGEON, 73, RR_RARE, NULL); addflag(lastot->flags, F_LINKSPELL, OT_S_COLDRAY, NA, NA, NULL); @@ -6846,6 +6821,11 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_DUNGEON, 65, NA, NULL); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); + addot(OT_ROPE, "rope", "A long length of strong rope.", MT_CLOTH, 5, OC_TOOLS); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 75, NA, NULL); + addflag(lastot->flags, F_RARITY, H_FOREST, 75, NA, NULL); + addflag(lastot->flags, F_HELPSCLIMB, 3, NA, NA, NULL); + addot(OT_TORCH, "torch", "A metre-long wooden rod with a flammable end.", MT_WOOD, 2, OC_TOOLS); addflag(lastot->flags, F_RARITY, H_DUNGEON, 85, NA, NULL); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); @@ -6973,7 +6953,28 @@ void initobjects(void) { addflag(lastot->flags, F_TECHLEVEL, PR_BEGINNER, NA, NA, NULL); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); + // tech - l3 + addot(OT_INFOVISOR, "infovisor", "Sleek looking metal visor which displays info directly into the retina.", MT_METAL, 0.2, OC_TECH); + addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_EXTRAINFO, B_TRUE, NA, NULL); + addflag(lastot->flags, F_EQUIPCONFER, F_ENHANCESEARCH, 10, NA, NULL); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); + addflag(lastot->flags, F_TECHLEVEL, PR_ADEPT, NA, NA, NULL); + addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); + addot(OT_LOCKHACKER, "lock hacker", "A sophisticated machine to manipulate physical locks.", MT_METAL, 3, OC_TECH); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); + addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_TECHLEVEL, PR_ADEPT, NA, NA, NULL); + addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); + addot(OT_PORTLADDER, "portable ladder", "A lightweight two metre ladder which automatically folds down to pocket size.", MT_METAL, 2, OC_TECH); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); + addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); + addflag(lastot->flags, F_TECHLEVEL, PR_ADEPT, NA, NA, NULL); + addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); + + // tech - l4 addot(OT_JETPACK, "jet pack", "A portable ion-thruster which allows the wearer to fly.", MT_METAL, 10, OC_TECH); addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); @@ -6982,21 +6983,6 @@ void initobjects(void) { addflag(lastot->flags, F_REFILLWITH, OT_POT_OIL, NA, NA, NULL); addflag(lastot->flags, F_ACTIVATECONFER, F_FLYING, B_TRUE, NA, NULL); addflag(lastot->flags, F_ACTIVATECONFER, F_PRODUCESLIGHT, 1, NA, NULL); - addflag(lastot->flags, F_TECHLEVEL, PR_ADEPT, NA, NA, NULL); - addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); - - // tech - l4 - addot(OT_INFOVISOR, "infovisor", "Sleek looking metal visor which displays info directly into the retina.", MT_METAL, 0.2, OC_TECH); - addflag(lastot->flags, F_GOESON, BP_EYES, NA, NA, NULL); - addflag(lastot->flags, F_EQUIPCONFER, F_EXTRAINFO, B_TRUE, NA, NULL); - addflag(lastot->flags, F_EQUIPCONFER, F_ENHANCESEARCH, 10, NA, NULL); - addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); - addflag(lastot->flags, F_TECHLEVEL, PR_SKILLED, NA, NA, NULL); - addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); - addot(OT_LOCKHACKER, "lock hacker", "A sophisticated machine to manipulate physical locks.", MT_METAL, 3, OC_TECH); - addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); - addflag(lastot->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_TECHLEVEL, PR_SKILLED, NA, NA, NULL); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); @@ -9888,12 +9874,8 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { sprintf(buf, "Where will you aim %s?",obname); where = askcoords(buf, subprompt, ttype, lf, UNLIMITED, LOF_NEED, B_TRUE); if (!haslos(lf, where)) { - // exception - wand of digging doesn't need los - if (isknown(o) && (o->type->id == OT_WAND_DIGGING)) { - } else { - msg("You can't see there!"); - return B_TRUE; - } + msg("You can't see there!"); + return B_TRUE; } } } else { @@ -10001,7 +9983,6 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { msg("%s zaps %s.",lfname,obname); } - f = hasflag(o->flags, F_LINKSPELL); if (f) { enum OBTYPE spelltocast; @@ -11997,7 +11978,7 @@ object_t *splitob(object_t *o) { // doesn't matter if it goes down to zero, as we will put it back up soon. o->amt--; // give new object - newob = addobject(o->pile, o->type->name, B_NOSTACK); + newob = addobject(o->pile, o->type->name, B_NOSTACK, B_FALSE); // restore count o->amt++; if (newob) { diff --git a/objects.h b/objects.h index b2bbcd2..c0a018d 100644 --- a/objects.h +++ b/objects.h @@ -10,7 +10,7 @@ material_t *addmaterial(enum MATERIAL id, char *name, float weightrating); objectclass_t *addoc(enum OBCLASS id, char *name, char *desc, char glyph, int glyphcolour); void addocnoun(objectclass_t *oc, char *text); object_t *addob(obpile_t *where, char *name); -object_t *addobject(obpile_t *where, char *name, int canstack); +object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes); int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fromlf, enum LOFTYPE needlof); obmod_t *addobmod(enum OBMOD id, char *prefix); obpile_t *addobpile(lifeform_t *owner, cell_t *where); diff --git a/save.c b/save.c index c69f3cc..11b7eea 100644 --- a/save.c +++ b/save.c @@ -498,7 +498,7 @@ int loadob(FILE *f, obpile_t *op, long *id) { return B_TRUE; } // create the object - o = addobject(op, ot->name, B_NOSTACK); // no stacking! + o = addobject(op, ot->name, B_NOSTACK, B_FALSE); // no stacking! // overwrite ob parameters o->id = obid; diff --git a/spell.c b/spell.c index 3eb3062..c2c0e89 100644 --- a/spell.c +++ b/spell.c @@ -2584,29 +2584,49 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else if (spellid == OT_S_DIG) { int numseen = 0; - cell_t *retcell[MAXRETCELLS]; - int nretcell,i; + cell_t *c; int ndigs; + char ch; + int dir; - // don't need line of fire OR sight! - if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power, frompot)) return B_TRUE; + if (targcell) { + dir = getdirtowards(caster->cell, targcell, NULL, B_FALSE, DT_COMPASS); + } else { + // don't need line of fire OR sight! + //if (!validatespellcell(caster, &targcell, TT_NONE, spellid, power, frompot)) return B_TRUE; + ch = askchar("Dig in which direction (- to cancel)", "yuhjklbn.-<>","-", B_FALSE); + if ((ch == '.') || (ch == '-')) { + fizzle(caster); + return B_TRUE; + } else if (ch == '<') { + return digup(caster, NULL); + } else if (ch == '>') { + return digdown(caster, NULL); + } else { + dir = chartodir(ch); + } + } - // calculate a line from caster to the target cell - calcbresnham(caster->cell->map, caster->cell->x, caster->cell->y, - targcell->x, targcell->y, retcell, &nretcell); + if (dir == DT_NONE) { + fizzle(caster); + return B_TRUE; + } ndigs = 0; // get rid of rock in the cells... - for (i = 0; i < nretcell && (ndigs <= power) ; i++) { + c = getcellindir(caster->cell, dir); + while (c && (ndigs <= power)) { int seenthiscell = B_FALSE; - if (haslos(player, retcell[i])) seenthiscell = B_TRUE; - if (retcell[i]->type->solid) { + + if (haslos(player, c)) seenthiscell = B_TRUE; + + if (c->type->solid) { // can dig through stone, but nothing else. - if (retcell[i]->type->material->id == MT_STONE) { - setcelltype(retcell[i], retcell[i]->map->habitat->emptycelltype); + if (c->type->material->id == MT_STONE) { + setcelltype(c, c->map->habitat->emptycelltype); + ndigs++; if (seenthiscell) { - ndigs++; numseen++; if (seenbyplayer) *seenbyplayer = B_TRUE; } @@ -2616,7 +2636,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else { object_t *o; - for (o = retcell[i]->obpile->first ; o ; o = o->next) { + for (o = c->obpile->first ; o ; o = o->next) { if (hasflag(o->flags, F_IMPASSABLE)) { char obname[BUFLEN]; // destroy this object then stop. @@ -2632,6 +2652,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } } + ndigs++; } // announce destruction of any walls seen @@ -3429,7 +3450,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } else { // next thing touched - addflag(caster->flags, F_FREEZINGTOUCH, 1, NA, NA, NULL); + addflag(caster->flags, F_FREEZINGTOUCH, 1, NA, 10+power, NULL); if (isplayer(caster)) { msg("Your hands begin to glow blue!"); if (seenbyplayer) *seenbyplayer = B_TRUE;