diff --git a/data.c b/data.c index dc45d71..f394a2d 100644 --- a/data.c +++ b/data.c @@ -1246,6 +1246,7 @@ void initobjects(void) { addflag(lastobjectclass->flags, F_UNIQUE, NA, NA, NA, NULL); addflag(lastobjectclass->flags, F_INVULNERABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); + addflag(lastobjectclass->flags, F_OPERWITHOUTID, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addoc(OC_CORPSE, "Corpses", "Dead flesh which was once living.", '%', C_GREY, RR_NEVER); @@ -1296,6 +1297,8 @@ void initobjects(void) { addflag(lastot->flags, F_OBHP, 20, 20, NA, NULL); addflag(lastot->flags, F_DTVULN, DT_BASH, NA, NA, NULL); addflag(lastot->flags, F_DTVULN, DT_CHOP, NA, NA, NULL); + addflag(lastot->flags, F_CANHAVEOBMOD, OM_MASTERWORK, 25, NA, NULL); + addflag(lastot->flags, F_CANHAVEOBMOD, OM_SHODDY, 25, NA, NULL); addot(OT_DOORIRON, "iron door", "A strong iron door.", MT_METAL, 300, OC_DFEATURE, SZ_LARGE); // GLYPH here is a special case in getglyph @@ -1457,7 +1460,8 @@ void initobjects(void) { addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_SHOPMENU, 0, MA_GOTOMENU, SM_PURCHASEITEMS, "a:buy something"); addflag(lastot->flags, F_SHOPMENU, 1, MA_GOTOMENU, SM_DONATE, "d:donate something"); - addflag(lastot->flags, F_SHOPMENU, 2, MA_QUIT, NA, "q:leave"); + addflag(lastot->flags, F_SHOPMENU, 2, MA_GOTOMENU, SM_RESIZE, "r:resize equipment"); + addflag(lastot->flags, F_SHOPMENU, 3, MA_QUIT, NA, "q:leave"); for (i = 0; i < 10; i++) { addflag(lastot->flags, F_STARTOBCLASS, 100, OC_ARMOUR, RANDOM, NULL); } @@ -1523,7 +1527,8 @@ void initobjects(void) { addflag(lastot->flags, F_CONTAINER, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_SHOPMENU, 0, MA_GOTOMENU, SM_PURCHASEITEMS, "a:buy something"); addflag(lastot->flags, F_SHOPMENU, 1, MA_GOTOMENU, SM_DONATE, "d:donate something"); - addflag(lastot->flags, F_SHOPMENU, 2, MA_QUIT, NA, "q:leave"); + addflag(lastot->flags, F_SHOPMENU, 2, MA_GOTOMENU, SM_RESIZE, "r:resize equipment"); + addflag(lastot->flags, F_SHOPMENU, 3, MA_QUIT, NA, "q:leave"); for (i = 0; i < 10; i++) { addflag(lastot->flags, F_STARTOBCLASS, 100, OC_WEAPON, RANDOM, NULL); } @@ -5831,9 +5836,9 @@ void initobjects(void) { addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); addflag(lastot->flags, F_USESSKILL, SK_UNARMED, NA, NA, NULL); addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_ATTACKVERB, NA, 5, NA, "scratch"); - addflag(lastot->flags, F_ATTACKVERB, 6, 15, NA, "scrape"); - addflag(lastot->flags, F_ATTACKVERB, 16, NA, NA, "rake"); + addflag(lastot->flags, F_ATTACKVERB, NA, 2, NA, "scratch"); + addflag(lastot->flags, F_ATTACKVERB, 3, 6, NA, "scrape"); + addflag(lastot->flags, F_ATTACKVERB, 7, NA, NA, "rake"); addot(OT_TEETH, "teeth", "teeth object", MT_BONE, 0, OC_WEAPON, SZ_TINY); @@ -5865,12 +5870,12 @@ void initobjects(void) { addot(OT_CLAWS, "claws", "claws object", MT_BONE, 0, OC_WEAPON, SZ_TINY); addflag(lastot->flags, F_DAM, DT_SLASH, 2, NA, NULL); addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_ATTACKVERB, NA, 5, NA, "scratch"); - addflag(lastot->flags, F_ATTACKVERB, 6, 15, NA, "claw"); - addflag(lastot->flags, F_ATTACKVERB, 16, 30, NA, "tear"); - addflag(lastot->flags, F_ATTACKVERB, 31, 40, NA, "rake"); - addflag(lastot->flags, F_ATTACKVERB, 41, 50, NA, "gouge"); - addflag(lastot->flags, F_ATTACKVERB, 51, NA, NA, "shred"); + addflag(lastot->flags, F_ATTACKVERB, NA, 2, NA, "scratch"); + addflag(lastot->flags, F_ATTACKVERB, 3, 6, NA, "claw"); + addflag(lastot->flags, F_ATTACKVERB, 7, 12, NA, "tear"); + addflag(lastot->flags, F_ATTACKVERB, 13, 18, NA, "rake"); + addflag(lastot->flags, F_ATTACKVERB, 19, 24, NA, "gouge"); + addflag(lastot->flags, F_ATTACKVERB, 25, NA, NA, "shred"); addflag(lastot->flags, F_KILLVERB, 70, NA, NA, "disembowel"); addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL); @@ -5880,7 +5885,8 @@ void initobjects(void) { addot(OT_HOOF, "hooves", "hoof object", MT_BONE, 0, OC_WEAPON, SZ_TINY); addflag(lastot->flags, F_DAM, DT_BASH, 2, NA, NULL); addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "kick"); + addflag(lastot->flags, F_ATTACKVERB, NA, 11, NA, "kick"); + addflag(lastot->flags, F_ATTACKVERB, 12, NA, NA, "trample"); addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL); addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL); addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL); @@ -11365,11 +11371,17 @@ void initskills(void) { addskilldesc(SK_CARTOGRAPHY, PR_MASTER, "^gEvery 50 turns, you can intuitively map a small area around you.^n", B_FALSE); addskill(SK_CHANNELING, "Channeling", "Lets you make better use of magical items.", 0); // untrainable addskilldesc(SK_CHANNELING, PR_NOVICE, "^gThe power level of wands and scrolls is increased by 1.^n", B_FALSE); + addskilldesc(SK_CHANNELING, PR_NOVICE, "^gYou can now recognise when objects are low on charges.^n", B_TRUE); addskilldesc(SK_CHANNELING, PR_BEGINNER, "^gThe power level of wands and scrolls is increased by 2.^n", B_FALSE); + addskilldesc(SK_CHANNELING, PR_BEGINNER, "^gYou now know when objects have a single charge remaining.^n", B_TRUE); addskilldesc(SK_CHANNELING, PR_ADEPT, "^gThe power level of wands and scrolls is increased by 4.^n", B_FALSE); + addskilldesc(SK_CHANNELING, PR_ADEPT, "^gYou now know when objects have less than four charges left.^n", B_TRUE); addskilldesc(SK_CHANNELING, PR_SKILLED, "^gThe power level of wands and scrolls is increased by 6.^n", B_FALSE); + addskilldesc(SK_CHANNELING, PR_SKILLED, "^gYou now know when objects have less than seven charges left.^n", B_TRUE); addskilldesc(SK_CHANNELING, PR_EXPERT, "^gThe power level of wands and scrolls is increased by 8.^n", B_FALSE); + addskilldesc(SK_CHANNELING, PR_EXPERT, "^gUsing any object will now reveal its remaining charges.^n", B_TRUE); addskilldesc(SK_CHANNELING, PR_MASTER, "^gThe power level of wands and scrolls is increased by 10.^n", B_FALSE); + addskilldesc(SK_CHANNELING, PR_MASTER, "^gYou now always know remaining charges for all objects.^n", B_TRUE); addskill(SK_CLIMBING, "Climbing", "Helps you to climb walls, mountains or other terrain.", 50); addskilldesc(SK_CLIMBING, PR_INEPT, "Increases your chances of successfully climbing by 10% per level.", B_FALSE); addskilldesc(SK_CLIMBING, PR_NOVICE, "You gain the 'climb walls' ability. Cannot attack while climbing.", B_FALSE); diff --git a/defs.h b/defs.h index 918e96a..fa3fa96 100644 --- a/defs.h +++ b/defs.h @@ -5,6 +5,7 @@ // MACROS #define MAXOF(a,b) (a > b ? a : b) +#define MINOF(a,b) (a < b ? a : b) #define OB1(o,one,many) (o->amt == 1) ? one : many #define OBS1(o) (o->amt == 1) ? "s" : "" #define OBNOS1(o) (o->amt == 1) ? "" : "s" @@ -1986,6 +1987,7 @@ enum FLAG { F_NONE = 0, // dummy flag // map flags F_MAPCOORDS, // v0+v1 are x/y coords for this map area + F_MAPSHAPE, // v0 = enum MAPSHAPE F_ROOMEXIT, // there is an exit from room v0 at x=v1,y=v2 F_NEWWATERDEPTH, // temp flag for the spread of f_deepwater obs. // v0+1 are x/y, v2 is new depth. @@ -2272,11 +2274,11 @@ enum FLAG { F_SHODDY, // weps do less damage, armour protects less. // weapon flags F_ATTACKVERB, // text=verb for attacking. ie. "hit" "slash" "sting" etc - // if v0/v1 are set, only use this text if dam pct is + // if v0/v1 are set, only use this text if dam AMOUNT (not pct) is // between v0 and v1. // should always be singular F_KILLVERB, // text=verb for a fatal attacking. ie. "kill" "behead" - // if v0/v1 are set, only use this text if dam pct is + // if v0/v1 are set, only use this text if dam PCT (not amount) is // between v0 and v1. // should always be singular F_OBATTACKDELAY, // how long weapon takes to attack @@ -2753,6 +2755,7 @@ enum FLAG { // if v2 is set, means we are sleeping on // purpose and will wake up when at full hp/mp/etc. // ie. "resting" + // text = obid of light source which you turned off. F_ATTACHEDTO, // you are attached to lf id v0, and will move with it F_AWARENESS, // you can see 360 degrees around yourself F_BEINGSTONED,// turn to stone when v0 drops to zero. (drops 1/turn) @@ -3438,6 +3441,19 @@ typedef struct hiddennamewithcol_s { enum COLOUR col; } hiddennamewithcol_t; +enum MAPSHAPE { + MS_NORMAL, + MS_CROSS, + MS_CIRCLE, + MS_TURRET, +}; +#define MAXMAPSHAPES (4) + +enum CORRIDORTYPE { + CDT_NORMAL, // make corridors, remove deadends, add rooms, autolink + CDT_SIMPLE, // add rooms, autolink +}; + typedef struct cell_s { map_t *map; // pointer back to map int x,y; // map coords @@ -3455,12 +3471,15 @@ typedef struct cell_s { char *writing; int writinglifetime; + int locked; // cannot make rooms on top of this. + // lifeform pile struct lifeform_s *lf; // known to player? int known; struct glyph_s knownglyph; int knowntime; + int isroomwall; // FOR CONSTRUCTION int visited; @@ -3527,6 +3546,7 @@ typedef struct lifeform_s { int mp,maxmp; float stamina; int alive; + int lastdamlf; // id of lf who just hurt us char *lastdam; char *killverb; struct material_s *material; @@ -3852,6 +3872,8 @@ enum SHOPMENU { SM_MIRACLE = -6, // motels SM_REST = -7, + // weapon/armour shops + SM_RESIZE = -8, }; enum SHOPRETURN { diff --git a/flag.c b/flag.c index 7fb07f6..896796f 100644 --- a/flag.c +++ b/flag.c @@ -240,6 +240,7 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3, case F_RUNNING: case F_TRAINING: case F_AUTOCMD: + case F_PRODUCESLIGHT: break; case F_ASLEEP: if (f->val[2] == NA) { // ie. sleeping, not resting @@ -972,6 +973,7 @@ void killflag(flag_t *f) { case F_RUNNING: case F_TRAINING: case F_AUTOCMD: + case F_PRODUCESLIGHT: break; case F_ASLEEP: if (f->val[2] != NA) { // ie. resting diff --git a/io.c b/io.c index 6635055..de8ddd3 100644 --- a/io.c +++ b/io.c @@ -5357,42 +5357,6 @@ char *makedesc_ob(object_t *o, char *retbuf) { strncat(retbuf, buf, HUGEBUFLEN); } - // charges remaining - if ((o->type->obclass->id == OC_WAND) || (o->type->id == OT_BATTERY)) { - if (isidentified(o)) { - int charges; - charges = getcharges(o); - if (charges) { - sprintf(buf, "It has %d charges left.\n",charges); - } else { - sprintf(buf, "It has no charges left.\n"); - } - strncat(retbuf, buf, HUGEBUFLEN); - } - } else if (o->type->obclass->id == OC_GODSTONE) { - if (isidentified(o)) { - int charges,max; - getchargeinfo(o, &charges, &max); - if (charges == max) { - sprintf(buf, "It is fully charged.\n"); - } else { - sprintf(buf, "It is depleted.\n"); - } - strncat(retbuf, buf, HUGEBUFLEN); - } - } else if (o->type->id == OT_CREDITCARD) { - if (isidentified(o)) { - int charges; - charges = getcharges(o); - if (charges) { - sprintf(buf, "It has $%d remaining until its limit.\n",charges); - } else { - sprintf(buf, "It is maxed out.\n"); - } - strncat(retbuf, buf, HUGEBUFLEN); - } - } - f = hasflag(o->flags, F_PICKLOCKS); if (f) { sprintf(buf, "You can use it to pick locks.\n"); @@ -5506,6 +5470,43 @@ char *makedesc_ob(object_t *o, char *retbuf) { } } // end if isknown + // charges remaining + if ((o->type->obclass->id == OC_WAND) || (o->type->id == OT_BATTERY)) { + if (isidentified(o) || chargesknown(o)) { + int charges; + charges = getcharges(o); + if (charges) { + sprintf(buf, "It has %d charge%s left.\n",charges, (charges == 1) ? "" : "s"); + } else { + sprintf(buf, "It has no charges left.\n"); + } + strncat(retbuf, buf, HUGEBUFLEN); + } + } else if (o->type->obclass->id == OC_GODSTONE) { + if (isidentified(o)) { + int charges,max; + getchargeinfo(o, &charges, &max); + if (charges == max) { + sprintf(buf, "It is fully charged.\n"); + } else { + sprintf(buf, "It is depleted.\n"); + } + strncat(retbuf, buf, HUGEBUFLEN); + } + } else if (o->type->id == OT_CREDITCARD) { + if (isidentified(o)) { + int charges; + charges = getcharges(o); + if (charges) { + sprintf(buf, "It has $%d remaining until its limit.\n",charges); + } else { + sprintf(buf, "It is maxed out.\n"); + } + strncat(retbuf, buf, HUGEBUFLEN); + } + } + + // skip line strncat(retbuf, "\n", HUGEBUFLEN); diff --git a/lf.c b/lf.c index 52eb9d2..ec570e9 100644 --- a/lf.c +++ b/lf.c @@ -2290,6 +2290,11 @@ void die(lifeform_t *lf) { flag_t *retflag[MAXCANDIDATES]; int nretflags; cell_t *corpsecell; + lifeform_t *killer = NULL; + + if (lf->lastdamlf != -1) { + killer = findlf(lf->cell->map, lf->lastdamlf); + } if (cansee(player, lf)) { needredraw = B_TRUE; @@ -2416,6 +2421,8 @@ void die(lifeform_t *lf) { } } } else { + lifeform_t *minion[MAXCANDIDATES]; + int nminions = 0; // intelligent monsters will say something if (!hasflag(lf->flags, F_NODEATHSPEECH)) { if (ispetof(lf, player)) { @@ -2473,6 +2480,19 @@ void die(lifeform_t *lf) { // mercy god doesn't like killing //angergodmaybe(R_GODMERCY, 1, GA_MURDER); } + + // minions drop morale, and might flee + getminions(lf, minion, &nminions); + for (i = 0; i < nminions; i++) { + f = lfhasflag(minion[i], F_MORALE); + if (f) { + f->val[0] -= 10; + // might flee? + if (killer && (f->val[0] <= 0)) { + scare(minion[i], killer, PERMENANT, 10); + } + } + } } // determine where to drop objects @@ -4269,7 +4289,6 @@ int flee(lifeform_t *lf) { if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; real_getlfname(lf, lfname, B_FALSE); - // are we fleeing? getflags(lf->flags, retflag, &nretflags, F_FLEEFROM, F_NONE); @@ -8723,8 +8742,10 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { addflag(lf->flags, F_PHOTOMEM, B_TRUE, NA, NA, NULL); } if (f->val[1] == PR_MASTER) { - newf = addflag(lf->flags, F_CANWILL, OT_S_MAPPING, 50, 50, "pw:1;"); - newf->lifetime = FROMSKILL; + if (!hasflagval(lf->flags, F_CANWILL, OT_S_MAPPING, NA, NA, NULL)) { + newf = addflag(lf->flags, F_CANWILL, OT_S_MAPPING, 50, 50, "pw:1;"); + newf->lifetime = FROMSKILL; + } } } else if (id == SK_COOKING) { if (f->val[1] == PR_ADEPT) { @@ -9053,8 +9074,6 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { case RANDOM: depthmod = rnd(0,MAXDEPTH); break; default: break; } - -// if (getrandomobofsize(targmap, buf, maxobsize)) { if (real_getrandomob(targmap, buf, targmap->depth + depthmod, NA, maxobsize, SK_NONE, B_TRUE, OC_NONE, DT_NONE)) { if (isshop && strstr(buf, "gold coin")) strcpy(buf, "potion of water"); o = addob(op, buf); @@ -9103,7 +9122,6 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { } else { if (db) snprintf(buf2, BUFLEN, "finished startobwepsk, failed."); } - //assert(strlen(buf) > 0); } } else if (id == F_STARTOBCLASS) { if (rnd(1,100) <= val[0]) { @@ -9120,7 +9138,6 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { } //obdb = B_TRUE; - //if (getrandomobwithclass(targmap, val[1], buf, val[2])) { if (real_getrandomob(targmap, buf, getmapdifficulty(targmap) + depthmod, NA, maxobsize, SK_NONE, B_TRUE, val[1], OC_NONE, DT_NONE)) { if (db) snprintf(buf2, BUFLEN, "finished startobclass, success."); @@ -9130,9 +9147,8 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { //obdb = B_FALSE; if (db) snprintf(buf2, BUFLEN, "finished startobclass. couldnt find an object."); } - //if (strlen(buf) <= 0); } - } + } // end what is fid? // TODO: maybe add object to temp cell, then MOVE it to the destination pile. // this will make sure the right checks happen. @@ -9162,9 +9178,15 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { // not worth any points addflag(o->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); } + } else if (isshop) { + flag_t *f2; + // all flags are known + for (f2 = o->flags->first ; f2; f2 = f2->next) { + f2->known = B_TRUE; + } } - } - } + } // end if o + } // end foreach container flag // now remove startob flags so we don't get them again! @@ -9216,14 +9238,36 @@ void givestartskills(lifeform_t *lf, flagpile_t *fp) { } int gotosleep(lifeform_t *lf, int onpurpose) { + char lightid[BUFLEN]; + strcpy(lightid, ""); if (lfhasflag(lf, F_CAFFEINATED)) { if (isplayer(lf)) { msg("Your caffeine high prevents you from sleeping."); } return B_TRUE; } - if (onpurpose) taketime(lf, getactspeed(lf)); - addflag(lf->flags, F_ASLEEP, B_TRUE, lfhasflag(lf, F_MEDITATES) ? ST_MEDITATING : ST_ASLEEP, onpurpose ? B_TRUE : NA, NULL); + if (onpurpose) { + taketime(lf, getactspeed(lf)); + if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { + object_t *o; + // turn off light sources first + for (o = lf->pack->first ; o ; o = o->next) { + if (hasflag(o->flags, F_LIGHTSOURCE) && hasflag(o->flags, F_ACTIVATED)) { + if (!strlen(lightid)) { + // first one. + if (isplayer(lf)) { + char ch; + ch = askchar("Turn off your light sources before resting?", "yn","y", B_TRUE, B_FALSE); + if (ch != 'y') break; + } + sprintf(lightid, "%ld", o->id); + } + turnoff(lf, o); + } + } + } + } + addflag(lf->flags, F_ASLEEP, B_TRUE, lfhasflag(lf, F_MEDITATES) ? ST_MEDITATING : ST_ASLEEP, onpurpose ? B_TRUE : NA, lightid); return B_FALSE; } @@ -10908,6 +10952,7 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { a->alive = B_TRUE; a->lastdam = strdup("nothing"); a->lastdamtype = DT_NONE; + a->lastdamlf = -1; a->damlastturn = 0; a->mplastturn = 0; a->stamlastturn = 0; @@ -12305,6 +12350,12 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml // fill in lastdam... lf->lastdamtype = damtype; + if (fromlf) { + lf->lastdamlf = fromlf->id; + } else { + lf->lastdamlf = -1; + } + // if they died if (lf->hp <= 0) { //if (!fromlf || (fromlf == player)) { @@ -18167,7 +18218,18 @@ int rest(lifeform_t *lf, int onpurpose) { killflagsofid(lf->flags, F_TRAINING); rf = isresting(lf); - if (rf) killflag(rf); + if (rf) { + // since you've woken up normally, turn your light source back on. + if (strlen(rf->text)) { + object_t *light; + light = hasobid(lf->pack, atol(rf->text)); + if (light) { + turnon(lf, light); + } + } + // kill sleeping flag + killflag(rf); + } wantclearmsg = B_FALSE; } } diff --git a/map.c b/map.c index 91f7ffc..f00ed04 100644 --- a/map.c +++ b/map.c @@ -69,6 +69,7 @@ cell_t *addcell(map_t *m, int x, int y) { setcelltype(cell, cell->habitat->solidcelltype); cell->lf = NULL; cell->room = NULL; + cell->locked = B_FALSE; cell->lit = L_NOTLIT; cell->origlit = L_NOTLIT; cell->littimer = 0; @@ -81,6 +82,7 @@ cell_t *addcell(map_t *m, int x, int y) { cell->knownglyph.colour = C_GREY; cell->visited = B_FALSE; cell->filled = B_FALSE; + cell->isroomwall = D_NONE; return cell; } @@ -1536,7 +1538,8 @@ object_t *gettopobject(cell_t *where, int forglyph) { for (o = where->obpile->first ; o ; o = o->next) { flag_t *f; // ignore hidden traps, but not secret doors - if (hasflag(o->flags, F_SECRET) && !isdoor(o, NULL)) { + if (isdeadob(o)) { + } else if (hasflag(o->flags, F_SECRET) && !isdoor(o, NULL)) { } else if (hasflag(o->flags, F_INVISOB)) { } else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) { } else if (forglyph && hasflag(o->flags, F_NOGLYPH)) { @@ -1560,7 +1563,8 @@ object_t *gettopobject(cell_t *where, int forglyph) { // appear first. for (o = where->obpile->last ; o ; o = o->prev) { if (o->type->obclass->id == sortorder[c]) { - if (hasflag(o->flags, F_SECRET)) { + if (isdeadob(o)) { + } else if (hasflag(o->flags, F_SECRET)) { } else if (hasflag(o->flags, F_INVISOB)) { } else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) { } else if (forglyph && hasflag(o->flags, F_NOGLYPH)) { @@ -1719,6 +1723,11 @@ int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int if (cell->room) { valid = B_FALSE; } + // - overlap a 'locked' cell + if (cell->locked) { + valid = B_FALSE; + } + // is this cell adjacent to an empty cell and not a // corner (ie. a valid door location) @@ -2074,13 +2083,14 @@ void createcave(map_t *map, int depth, map_t *parentmap, int exitdir, object_t * void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int wantrooms = B_TRUE; int d; - int x,y; + int x,y,w,h; + enum MAPSHAPE shape = MS_NORMAL; int i; int done,unused; int dir; int lastdir; int numrooms = 0; - cell_t *cell, *c; + cell_t *c,*centre; //object_t *o; int db = B_TRUE; int digdb = B_FALSE; @@ -2091,10 +2101,12 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ int looppct = DEF_LOOPPCT; int minrooms = MINROOMS; int maxrooms = MAXROOMS; + enum CORRIDORTYPE corridortype = CDT_NORMAL; int moved = 0; enum CELLTYPE emptycell,solidcell; + char buf[BUFLEN]; // fill entire maze with walls for (y = 0; y < map->h; y++) { @@ -2103,6 +2115,110 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } } + // select dungeon shape. + if (onein(3)) { + shape = rnd(0,MAXMAPSHAPES-1); + } else { + shape = MS_NORMAL; + } + switch (shape) { + case MS_NORMAL:// normal + break; + case MS_CROSS: // cross + // ## ## + // ## ## + // + // ## ## + // ## ## + for (y = 0; y < map->h/3; y++) { + for (x = 0; x < map->w/3; x++) { + c = getcellat(map, x, y); c->locked = B_TRUE; + } + for (x = map->w - (map->w/3); x < map->w; x++) { + c = getcellat(map, x, y); c->locked = B_TRUE; + } + } + for (y = map->h-(map->h/3); y < map->h; y++) { + for (x = 0; x < map->w/3; x++) { + c = getcellat(map, x, y); c->locked = B_TRUE; + } + for (x = map->w - (map->w/3); x < map->w; x++) { + c = getcellat(map, x, y); c->locked = B_TRUE; + } + } + break; + case MS_TURRET: // kind of a reverse cross + // ########## + // # # + // # ## # + // # ###### # + // # ###### # + // # ###### # + // # ## # + // # # + // ########## + for (y = (map->h/4); y < map->h/3; y++) { + for (x = map->w/3; x < map->w - (map->w/3); x++) { + c = getcellat(map, x, y); c->locked = B_TRUE; + } + } + for (y = map->h/3; y < map->h - (map->h/3); y++) { + for (x = map->w/4; x < map->w - (map->w/4); x++) { + c = getcellat(map, x, y); c->locked = B_TRUE; + } + } + for (y = map->h-(map->h/3); y < map->h - (map->h/4); y++) { + for (x = map->w/3; x < map->w - (map->w/3); x++) { + c = getcellat(map, x, y); c->locked = B_TRUE; + } + } + break; + case MS_CIRCLE: // circle / ellipse + centre = getcellat(map, map->w/2, map->h/2); + w = map->w - 5; + h = map->h - 4; + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + float val; + val = (pow(x - centre->x, 2) / pow(w/2, 2)) + + (pow(y - centre->y, 2) / pow(h/2, 2)); + if (val > 1) { + c = getcellat(map, x, y); + c->locked = B_TRUE; + } + } + } + break; + } + addflag(map->flags, F_MAPSHAPE, shape, NA, NA, NULL); + + for (y = 0; y < map->h; y++) { + strcpy(buf, ""); + for (x = 0; x < map->w; x++) { + c = getcellat(map, x, y); + if (c->locked) { + c->visited = B_TRUE; + } + if (c->locked) { + strcat(buf, "X"); + } else { + strcat(buf, " "); + } + } + dblog("%s",buf); + } + + // randomise dungeon parameters + turnpct += (rnd(0,40)-20); // (-20 to +20)% + sparseness += (rnd(0,20)-10); // -10 to +10 + looppct -= (rnd(0,10)); // subtrace 0 - 10 + + if (shape == MS_NORMAL) { + if (onein(2)) { + corridortype = CDT_SIMPLE; + } + } + // is the map lit? if (depth <= 5) { @@ -2122,148 +2238,150 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ solidcell = map->habitat->solidcelltype; // pick initial random spot - cell = getrandomcell(map); - setcelltype(cell, emptycell); - cell->visited = B_TRUE; - if (digdb) printf("- Starting (%d,%d)\n",cell->x, cell->y); + if (corridortype == CDT_NORMAL) { + c = getrandomcell(map); + setcelltype(c, emptycell); + c->visited = B_TRUE; + if (digdb) printf("- Starting (%d,%d)\n",c->x, c->y); - lastdir = D_UNKNOWN; - done = B_FALSE; + lastdir = D_UNKNOWN; + done = B_FALSE; - dir = D_NONE; - - while (!done) { - // get random direction based on turnpct dir = D_NONE; - while (dir == D_NONE) { - int badcount; - if (digdb) printf("- At (%d,%d), moved %d, finding new direction...\n",cell->x, cell->y, moved); - - dir = getnewdigdir(cell, lastdir, (moved < 2) ? 0 : turnpct, &moved); - badcount = 0; - while (dir == D_NONE) { - badcount++; - if (badcount > 10) { - // finish! - done = B_TRUE; - break; - } - - // pick new EMPTY random spot - cell = getrandomcell(map); - while (!isempty(cell)) { - cell = getrandomcell(map); - } - if (digdb) printf("--- Couldn't find a valid direction. Jumped to (%d,%d).\n",cell->x, cell->y); - // pick a new random dir - dir = getnewdigdir(cell, lastdir, turnpct, &moved); - } - if (!done) { - if (digdb) printf("- Digging %s from (%d,%d).\n",getdirname(dir),cell->x, cell->y); - } - } - - if (!done) { - // move to adjacent cell in the given direction - cell = getcellindir(cell, dir); - if (digdb) printf("- Now at (%d,%d)\n",cell->x, cell->y); - moved++; + while (!done) { + // get random direction based on turnpct + dir = D_NONE; + while ((dir == D_NONE) && !done) { + int badcount; + if (digdb) printf("- At (%d,%d), moved %d, finding new direction...\n",c->x, c->y, moved); - // blank it - setcelltype(cell,emptycell); - cell->visited = B_TRUE; - // mark surrounding cells as visited - for (d = DC_N; d < MAXDIR_COMPASS; d++) { - cell_t *thiscell; - thiscell = getcellindir(cell, d); - if (thiscell) { - if (digdb) printf("* Marking surrounding cell in dir %d (%d,%d) as visited.\n",d, thiscell->x, thiscell->y); - thiscell->visited = B_TRUE; + dir = getnewdigdir(c, lastdir, (moved < 2) ? 0 : turnpct, &moved); + + badcount = 0; + while (dir == D_NONE) { + badcount++; + if (badcount > 10) { + // finish! + done = B_TRUE; + break; + } + + // pick new EMPTY random spot + c = getrandomcell(map); + while (!isempty(c)) { + c = getrandomcell(map); + } + if (digdb) printf("--- Couldn't find a valid direction. Jumped to (%d,%d).\n",c->x, c->y); + // pick a new random dir + dir = getnewdigdir(c, lastdir, turnpct, &moved); + } + if (!done) { + if (digdb) printf("- Digging %s from (%d,%d).\n",getdirname(dir),c->x, c->y); } } - // remember last direction - lastdir = dir; - // check if we have visited all valid cells - unused = 0; - for (y = 0; y < map->h; y++) { - for (x = 0; x < map->w; x++) { + if (!done) { + // move to adjacent cell in the given direction + c = getcellindir(c, dir); + if (digdb) printf("- Now at (%d,%d)\n",c->x, c->y); + moved++; + + // blank it + setcelltype(c,emptycell); + c->visited = B_TRUE; + // mark surrounding cells as visited + for (d = DC_N; d < MAXDIR_COMPASS; d++) { cell_t *thiscell; - thiscell = getcellat(map, x, y); - if (!thiscell->visited) { - unused++; + thiscell = getcellindir(c, d); + if (thiscell) { + if (digdb) printf("* Marking surrounding cell in dir %d (%d,%d) as visited.\n",d, thiscell->x, thiscell->y); + thiscell->visited = B_TRUE; } } - } + // remember last direction + lastdir = dir; - if (!unused) { - done = B_TRUE; - } - } - - if (digdb) dblog("%d unused cell(s)\n",unused); - //dumpmap(map); - //getchar(); - - } - - // use sparseness to cut down dead ends - remove_deadends(map, sparseness); - - // introduce loops - for (y = 0; y < map->h; y++) { - for (x = 0; x < map->w; x++) { - cell = getcellat(map, x, y); - if (!cell->type->solid && countcellexits(cell, DT_ORTH) == 1) { - // dead end - maybe make loop from here - if (rnd(1,100) <= looppct) { - int connected = B_FALSE; - int loopok = B_TRUE; - int dir; - - // pick a random directory - dir = getnewdigdir(cell, D_UNKNOWN,100, &moved); - - if (dir == D_NONE) { - // can't make a loop from here. - loopok = B_FALSE; - } else { - int tries = 0; - // if we go this dir, will we hit a - // corridor? - while (!isloopdirok(cell, dir)) { - tries++; - // tried every direction? - if (tries >= MAXDIR_ORTH) { - loopok = B_FALSE; - break; - } - // turn... - if (++dir >= MAXDIR_ORTH) { - dir = 0; - } + // check if we have visited all valid cells + unused = 0; + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + cell_t *thiscell; + thiscell = getcellat(map, x, y); + if (!thiscell->visited) { + unused++; } } + } - if (loopok) { - // keep digging until we hit another corridor - while (!connected) { - cell_t *newcell; - // find adjacent cell in the given direction - newcell = getcellindir(cell, dir); - if (!newcell) { - connected = B_TRUE; - } else { - // have we hit another corridor yet? - if (!newcell->type->solid) { + if (!unused) { + done = B_TRUE; + } + } + + if (digdb) dblog("%d unused cell(s)\n",unused); + //dumpmap(map); + //getchar(); + + } + + // use sparseness to cut down dead ends + remove_deadends(map, sparseness); + + // introduce loops + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + c = getcellat(map, x, y); + if (!c->type->solid && countcellexits(c, DT_ORTH) == 1) { + // dead end - maybe make loop from here + if (rnd(1,100) <= looppct) { + int connected = B_FALSE; + int loopok = B_TRUE; + int dir; + + // pick a random directory + dir = getnewdigdir(c, D_UNKNOWN,100, &moved); + + if (dir == D_NONE) { + // can't make a loop from here. + loopok = B_FALSE; + } else { + int tries = 0; + // if we go this dir, will we hit a + // corridor? + while (!isloopdirok(c, dir)) { + tries++; + // tried every direction? + if (tries >= MAXDIR_ORTH) { + loopok = B_FALSE; + break; + } + // turn... + if (++dir >= MAXDIR_ORTH) { + dir = 0; + } + } + } + + if (loopok) { + // keep digging until we hit another corridor + while (!connected) { + cell_t *newcell; + // find adjacent cell in the given direction + newcell = getcellindir(c, dir); + if (!newcell) { connected = B_TRUE; } else { - // blank adjacent cell - setcelltype(newcell, emptycell); - newcell->visited = B_TRUE; + // have we hit another corridor yet? + if (!newcell->type->solid) { + connected = B_TRUE; + } else { + // blank adjacent cell + setcelltype(newcell, emptycell); + newcell->visited = B_TRUE; + } + c = newcell; } - cell = newcell; } } } @@ -2301,8 +2419,10 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } } - // now clear up dead ends again. - remove_deadends(map, sparseness); + if (corridortype == CDT_NORMAL) { + // now clear up dead ends again. + remove_deadends(map, sparseness); + } // link up room exits for (i = 0; i < map->nrooms; i++) { @@ -2339,11 +2459,10 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ if (db) dblog("--> Will add %d pillars",numpillars); for (n = 0; n < numpillars;n++ ) { //dblog("----> Adding pillar %d/%d",n+1,numpillars); - cell_t *c; c = getrandomroomcell(map, i); if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { - setcelltype(cell, CT_WALL); + setcelltype(c, CT_WALL); } } } @@ -2362,7 +2481,6 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ for (n = 0 ; n < numobs; n++) { int ntries = 0; int nmonsters = 0; - cell_t *c; done = B_FALSE; while (!done) { c = getrandomroomcell(map, i); @@ -3556,6 +3674,8 @@ int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *r addvaultcellcontents(cell, v, x-minx,y-miny, rotation); } } + + markroomwalls(map, thisroom); } if (retw) *retw = w; @@ -3687,7 +3807,7 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { if ((roomid >= 0) && getroomid(c) == roomid) { // same room //if (wantfilled && c->type->solid) { if (c->type->solid) { - // EXCEPTION: + // This is an exception: // if startcell is a cell _inside_ the room as opposed to // a cell inside the room's walls. // in this case, we ARE allowed to travel through the room's walls. @@ -3698,6 +3818,13 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { if (db) dblog(" going %s hits same room. invalid.", getdirname(d)); break; } + } else if (isroom(c) && (getroomid(c) != roomid) && (c->isroomwall != diropposite(d))) { + // cell is in a different room, but not the correct edge + // mark dir as invalid + dist[d] = 999; + sameroom[d] = B_FALSE; + if (db) dblog(" going %s hits wrong wall of different room. invalid.", getdirname(d)); + break; } else if (cellwalkable(NULL, c, NULL)) { if (!wantfilled || c->filled) { // walkable and not in this vault. finished. @@ -3715,13 +3842,17 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { for (n = 0; n <= 1; n++) { pcell = getcellindir(c, perpdir[n]); if (pcell) { - if (((roomid == -1 ) || (getroomid(pcell) != roomid)) && - cellwalkable(NULL, pcell, NULL)) { - if (!wantfilled || c->filled) { - // finished. - directendcell[d] = c; - if (db) dblog(" can make %s path (hits adjacent empty cell at dist %d)", getdirname(d), dist[d]); - break; + int proomid; + proomid = getroomid(pcell); + if (((roomid == -1 ) || (proomid != roomid)) && cellwalkable(NULL, pcell, NULL)) { + if ((proomid >= 0) && (pcell->isroomwall != diropposite(perpdir[n]))) { + } else { + if (!wantfilled || c->filled) { + // finished. + directendcell[d] = c; + if (db) dblog(" can make %s path (hits adjacent empty cell at dist %d)", getdirname(d), dist[d]); + break; + } } } } @@ -3797,17 +3928,15 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { int n; cell_t *c2; startdist++; - // check left/right from this door for rooms + // check left/right from this cell for rooms for (n = 0; n <= 1; n++) { int turndist = 0; c2 = getcellindir(c, perpdir[n]); - while (c2) { int gotsolution = B_FALSE; turndist++; - perpcell[nperpcells] = c2; // this will be used if we need to make 2 turns perpturncell1[nperpcells] = c; perpturndir1[nperpcells] = perpdir[n]; @@ -3825,6 +3954,13 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { if (db) dblog(" Got to an empty cell here."); gotsolution = B_TRUE; } + } else if (isroom(c2) && (c2->isroomwall != diropposite(perpdir[n]))) { + // wrong wall of room + // mark dir as invalid + dist[d] = 999; + sameroom[d] = B_FALSE; + if (db) dblog(" going %s hits wrong wall of different room. invalid.", getdirname(d)); + break; } else if (turndist > 1) { // check l/r too int perpdir2[2],nn; @@ -3834,13 +3970,18 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { for (nn = 0; nn <= 1; nn++) { pcell = getcellindir(c2, perpdir2[nn]); if (pcell) { - if ( ((roomid == -1) || (getroomid(pcell) != roomid)) && + int proomid; + proomid = getroomid(pcell); + if ( ((roomid == -1) || (proomid != roomid)) && cellwalkable(NULL, pcell, NULL)) { - if (!wantfilled || pcell->filled) { - // finished. - if (db) dblog(" Got to an empty cell next to us."); - gotsolution = B_TRUE; - break; + if ((proomid >= 0) && (pcell->isroomwall != diropposite(perpdir2[n]))) { + } else { + if (!wantfilled || pcell->filled) { + // finished. + if (db) dblog(" Got to an empty cell next to us."); + gotsolution = B_TRUE; + break; + } } } } @@ -3919,6 +4060,13 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { if (db) dblog(" Got to an empty cell here."); gotsolution = B_TRUE; } + } else if (isroom(c2) && (c2->isroomwall != diropposite(dir3[n]))) { + // wrong wall of room + // mark dir as invalid + dist[d] = 999; + sameroom[d] = B_FALSE; + if (db) dblog(" going %s hits wrong wall of different room. invalid.", getdirname(d)); + break; } else if (turndist > 1) { // check l/r too int perpdir2[2],nn; @@ -3928,13 +4076,19 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { for (nn = 0; nn <= 1; nn++) { pcell = getcellindir(c2, perpdir2[nn]); if (pcell) { - if ( ((roomid == -1) || (getroomid(pcell) != roomid)) && + int proomid; + proomid = getroomid(pcell); + if ( ((roomid == -1) || (proomid != roomid)) && cellwalkable(NULL, pcell, NULL)) { - if (!wantfilled || pcell->filled) { - // finished. - if (db) dblog(" Got to an empty cell next to us."); - gotsolution = B_TRUE; - break; + if ((proomid >= 0) && (pcell->isroomwall != diropposite(perpdir2[nn]))) { + // different room and hits wrong wall. + } else { + if (!wantfilled || pcell->filled) { + // finished. + if (db) dblog(" Got to an empty cell next to us."); + gotsolution = B_TRUE; + break; + } } } } @@ -3959,7 +4113,7 @@ int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { if (turncell2) break; } // end foreach perpcell - // TODO: if we find a solution, fill in turncell2 and make path. + // if we find a solution, fill in turncell2 and make path. if (turncell2) { if (db) dblog(" Twoturn solution found: Walk %s, then %s, then %s.", getdirname(startdir), getdirname(turndir), getdirname(turndir2)); @@ -4287,6 +4441,7 @@ void createroom(map_t *map, int roomid, int x1, int y1, int x2, int y2, int forc } } } + markroomwalls(map, thisroom); } // room w/h are returned in *w and *h if given. @@ -4588,13 +4743,14 @@ void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *c void finalisemap(map_t *map, object_t *entryob) { enum OBTYPE upstairtype, downstairtype; - int i,d; + int i,d,x,y; int linkedentry = B_FALSE; cell_t *c; - object_t *o; - // add staircases. - // first dungeon level has 1 up stairs, 3 down. - // subsequent levels always have 3 up and down stairs + object_t *o,*nexto; + // make sure this map has sufficient up/down staircases as defined by its + // region type. + // + // first dungeon level is a special case. it has 1 up stairs, 3 down. switch (map->habitat->id) { case H_CAVE: upstairtype = OT_TUNNELUP; @@ -4686,6 +4842,37 @@ void finalisemap(map_t *map, object_t *entryob) { } } + for (y = 0; y < map->h; y++) { + for (x = 0; x < map->w; x++) { + c = getcellat(map, x, y); + for (o = c->obpile->first ; o ; o = nexto) { + nexto = o->next; + // doors which go nowhere? + if (isdoor(o, NULL)) { + int dir, ok = B_FALSE; + cell_t *c2; + // check all directions for a cell which isn't + // part of this room. + for (dir = DC_N; dir <= DC_NW; dir++) { + c2 = getcellindir(c, dir); + if (c2 && cellwalkable(NULL, c2, NULL) && getroomid(c2) != getroomid(c)) { + ok = B_TRUE; + break; + } + } + if (!ok) { + killob(o); + continue; + } + } + // unlinked stairs? ie ones added from vaults. if so, link them. + if (hasflag(o->flags, F_CLIMBABLE) && !hasflag(o->flags, F_MAPLINK)) { + linkstairs(o, NULL); + } + } + } + } + } celltype_t *findcelltype(enum CELLTYPE cid) { @@ -5488,8 +5675,8 @@ cell_t *getstairdestination(object_t *o, int *madenewmap) { // 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"); + dblog("ERROR - unlinked stairs! should have been linked during map creation.\n"); + msg("ERROR - unlinked stairs! should have been linked during map creation.\n"); return NULL; } else { // generate a new map! this will fill in the destination of our stairs @@ -5918,9 +6105,12 @@ int isloopdirok(cell_t *cell, int dir) { } int isnewcellok(cell_t *cell, char *err) { - if ( !cell) { // can't go that way + if ( !cell ) { // can't go that way if (err) snprintf(err, BUFLEN,"goes off the map."); return B_FALSE; + } else if ( cell->locked ) { // locked + if (err) snprintf(err, BUFLEN,"locked."); + return B_FALSE; } else if ( !cell->type->solid) { // already an empty space there if (err) snprintf(err, BUFLEN,"goes to an empty space (%d,%d)",cell->x,cell->y); return B_FALSE; @@ -6071,7 +6261,7 @@ object_t *linkportal(object_t *srcportal, int wantdepth) { } // link the staircase 'o' to a free one in adjacent maps. -// o2 is optional. if not probided, we will try to find +// o2 is optional. if o2 isn't provided, we will try to find // something to link to ourself. // returns TRUE if it failed because othermap doesn't exist. int linkstairs(object_t *o, object_t *o2) { @@ -6276,6 +6466,59 @@ void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong) { } } +void markroomwalls(map_t *m, room_t *r) { + int x,y; + cell_t *c,*c2; + // N edge + for (x = r->x1+1; x <= r->x2-1; x++) { + for (y = r->y1; y <= r->y2; y++) { + c = getcellat(m, x, y); + if (c->type->solid) { + c2 = getcellindir(c, DC_S); + if (c2 && !c2->type->solid) { + c->isroomwall = DC_N; + } + } + } + } + // W edge + for (y = r->y1+1; y <= r->y2-1; y++) { + for (x = r->x2; x >= r->x1; x--) { + c = getcellat(m, x, y); + if (c->type->solid) { + c2 = getcellindir(c, DC_W); + if (c2 && !c2->type->solid) { + c->isroomwall = DC_E; + } + } + } + } + // S edge + for (x = r->x1+1; x <= r->x2-1; x++) { + for (y = r->y2; y >= r->y1; y--) { + c = getcellat(m, x, y); + if (c->type->solid) { + c2 = getcellindir(c, DC_N); + if (c2 && !c2->type->solid) { + c->isroomwall = DC_S; + } + } + } + } + // W edge + for (y = r->y1+1; y <= r->y2-1; y++) { + for (x = r->x1; x <= r->x2; x++) { + c = getcellat(m, x, y); + if (c->type->solid) { + c2 = getcellindir(c, DC_E); + if (c2 && !c2->type->solid) { + c->isroomwall = DC_W; + } + } + } + } +} + void mapentereffects(map_t *m) { int i; cell_t *c; diff --git a/map.h b/map.h index 165672e..25c2653 100644 --- a/map.h +++ b/map.h @@ -145,6 +145,7 @@ int linkstairs(object_t *o, object_t *o2); void makedoor(cell_t *cell, int openchance); void makelit(cell_t *c, enum LIGHTLEV how, int howlong); void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong); +void markroomwalls(map_t *m, room_t *r); void mapentereffects(map_t *m); void moveobtoclearcell(object_t *o); enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob); diff --git a/objects.c b/objects.c index 172c944..beb2542 100644 --- a/objects.c +++ b/objects.c @@ -1636,7 +1636,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes } break; case OM_MASTERWORK: - if (isweapon(o) || isarmour(o)) { + if (isweapon(o) || isarmour(o) || isdoor(o, NULL)) { flag_t *f; f = hasflag(o->flags, F_OBHP); if (f) { @@ -1646,7 +1646,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes } break; case OM_SHODDY: - if (isweapon(o) || isarmour(o)) { + if (isweapon(o) || isarmour(o) || isdoor(o, NULL)) { flag_t *f; f = hasflag(o->flags, F_OBHP); if (f) { @@ -4504,7 +4504,13 @@ char *getobextrainfo(object_t *o, char *buf) { // charges f = hasflag(o->flags, F_CHARGES); - if (f && f->known) { + if (f) { + int flagknown = B_FALSE; + if (f->known) { + flagknown = B_TRUE; + } else if (ismagical(o) && (getskill(player, SK_CHANNELING) >= PR_MASTER)) { + flagknown = B_TRUE; + } if (!hasflag(o->flags, F_DONTSHOWCHARGES)) { char chargestr[BUFLEN]; if (o->type->obclass->id == OC_GODSTONE) { @@ -4991,7 +4997,11 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan obmod_t *om; for (om = firstobmod ; om; om = om->next) { if (hasobmod(o, om)) { - strcat(localbuf, om->prefix); + char *p; + p = getobmodprefix(o, om); + if (p && strlen(p)) { + strcat(localbuf, p); + } } } } @@ -8275,17 +8285,55 @@ int operate(lifeform_t *lf, object_t *o, cell_t *where) { // objects with charges... if (hasflag(o->flags, F_OPERUSECHARGE)) { // operating toggles on/off - if (usecharge(o) == -1) { - if (isplayer(lf)) { - nothinghappens(); + if (isplayer(lf)) { + int chargesleft; + chargesleft = usecharge(o); + f = hasflag(o->flags, F_CHARGES); // should always be true! + if (chargesleft == -1) { + if (f) f->known = B_TRUE; + msg("Nothing happens - this item is empty."); // you know it's out - f = hasflag(o->flags, F_CHARGES); - f->known = B_TRUE; taketime(lf, getactspeed(lf)); return B_FALSE; - } - } - } + } else if (ismagical(o) && (o->pile->owner == lf)) { + enum SKILLLEVEL slev; + // depending on your channeling skill, you might + // find out that it is running low. + slev = getskill(lf, SK_CHANNELING); + if (slev == PR_EXPERT) { + if (f) f->known = B_TRUE; + } + if (slev && (chargesleft == 0)) { + if (f) f->known = B_TRUE; + msg("Your %s is now empty.", noprefix(obname)); + } else if (slev && chargesknown(o)) { + if (f) f->known = B_TRUE; + if (chargesleft == 1) { + msg("Your %s only has one charge remaining.", noprefix(obname)); + } else { + msg("Your %s now has %d charges remaining.", noprefix(obname), chargesleft); + } + } else { + switch (slev) { + case PR_INEPT: + break; + case PR_NOVICE: // notify when 1-3 charges left + if (chargesleft <= 3) { + msg("Your %s is running low on charges.", noprefix(obname)); + } + break; + case PR_BEGINNER: // charges known at 1, notify at 1-3 + if (chargesleft <= 3) { + msg("Your %s is running low on charges.", noprefix(obname)); + } + break; + default: + break; + } + } // end if slev& chargesleft is 0 + } // end if chargesleft== -1 / ismagical + } // end if isplayer + } // end if hasflag operusecharge // TODO: change to be based on item type // TODO: move this to the end in case 'operate' fails @@ -10616,6 +10664,16 @@ int removeob(object_t *o,int howmany) { return rv; } +void resizeobject(object_t *o, enum LFSIZE wantsize) { + flag_t *f; + f = hasflag(o->flags, F_ARMOURSIZE); + if (f) { + f->val[0] = wantsize; + } else { + addflag(o->flags, F_ARMOURSIZE, wantsize, NA, NA, NULL); + } +} + void rrtorarity(enum RARITY r, int *minr, int *maxr) { switch (r) { case RR_UNIQUE: @@ -12815,7 +12873,6 @@ void turnon(lifeform_t *lf, object_t *o) { held = B_TRUE; } - f = hasflag(o->flags, F_ACTIVATED); if (f) { // already on @@ -13203,6 +13260,28 @@ int getcritchance(lifeform_t *lf, object_t *o, lifeform_t *victim) { return chance; } +int chargesknown(object_t *o) { + flag_t *f; + int cutoff = -2; + f = hasflag(o->flags, F_CHARGES); + if (f) { + if (f->known) return B_TRUE; + + switch (getskill(player, SK_CHANNELING)) { + case PR_BEGINNER: cutoff = 1; break; + case PR_ADEPT: cutoff = 3; break; + case PR_SKILLED: cutoff = 6; break; + case PR_EXPERT: break; // f->known will be set after use. + case PR_MASTER: cutoff = 9999; break; + default: break; + } + } + if (f->val[0] <= cutoff) { + return B_TRUE; + } + return B_FALSE; +} + int getcritprotection(object_t *o) { flag_t *f; f = hasflag(o->flags, F_CRITPROTECTION); diff --git a/objects.h b/objects.h index 5369b58..5a2bc99 100644 --- a/objects.h +++ b/objects.h @@ -34,6 +34,7 @@ int canseeob(lifeform_t *lf, object_t *o); object_t *canstackob(obpile_t *op, object_t *match); object_t *canstacknewot(obpile_t *op, objecttype_t *match); int changemat(object_t *o, enum MATERIAL mat); +int chargesknown(object_t *o); int getcritprotection(object_t *o); int checkobnames(char *haystack, char *needle); void colourmatchob(object_t *o, lifeform_t *lf); @@ -249,6 +250,7 @@ int readsomething(lifeform_t *lf, object_t *o); void removedeadobs(obpile_t *op); int removeob(object_t *o, int howmany); object_t *relinkob(object_t *src, obpile_t *dst); +void resizeobject(object_t *o, enum LFSIZE wantsize); void rrtorarity(enum RARITY r, int *minr, int *maxr); void setblessed(object_t *o, enum BLESSTYPE wantbless); int sethiddenname(objecttype_t *o, char *text); diff --git a/save.c b/save.c index f0bee29..e3a9aeb 100644 --- a/save.c +++ b/save.c @@ -382,8 +382,8 @@ map_t *loadmap(char *basefile) { c = addcell(m, x, y); // cell info - fscanf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", - &roomid, &celltypeid, &c->known, &c->knowntime, &c->knownglyph.ch, &c->knownglyph.colour, &c->visited, &c->lit, &c->origlit, &c->littimer,&temphab); + fscanf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + &roomid, &celltypeid, &c->known, &c->knowntime, &c->knownglyph.ch, &c->knownglyph.colour, &c->visited, &c->lit, &c->origlit, &c->littimer,&c->locked, &temphab, &c->isroomwall); c->habitat = findhabitat(temphab); c->room = findroom(m, roomid); @@ -948,8 +948,8 @@ int savemap(map_t *m) { cell_t *c; c = getcellat(m, x, y); // cell info - fprintf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", - c->room ? c->room->id : -1, c->type->id, c->known, c->knowntime, c->knownglyph.ch, c->knownglyph.colour, c->visited,c->lit,c->origlit,c->littimer,c->habitat->id ); + fprintf(f, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", + c->room ? c->room->id : -1, c->type->id, c->known, c->knowntime, c->knownglyph.ch, c->knownglyph.colour, c->visited,c->lit,c->origlit,c->littimer,c->locked, c->habitat->id, c->isroomwall ); // cell objects for (o = c->obpile->first ; o ; o = o->next) { fprintf(f, "ob:%ld\n",o->id); diff --git a/shops.c b/shops.c index 6dcdf9b..684ed96 100644 --- a/shops.c +++ b/shops.c @@ -21,6 +21,7 @@ #define DEF_REMCURSECOST 60 #define DEF_BLESSCOST 50 #define DEF_SURCHARGE 15 +#define DEF_RESIZECOST 80 extern enum GAMEMODE gamemode; extern prompt_t prompt; @@ -158,6 +159,9 @@ void shop(lifeform_t *lf, object_t *vm) { } else if (curmenu == SM_REST) { shopfunc = shoprest; shoparg = &ndonated; + } else if (curmenu == SM_RESIZE) { + shopfunc = shopresize; + shoparg = &ndonated; } if (shopfunc) { @@ -747,6 +751,57 @@ enum SHOPRETURN shoppurchase(lifeform_t *lf, object_t *vm, int starty, char *top return SR_CONTINUE; } +enum SHOPRETURN shopresize(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { + object_t *o; + char ch,buf[BUFLEN]; + int y; + int resizecost; + y = starty; + + resizecost = applyshoppricemod(pctof(75, DEF_RESIZECOST), lf); + + mvwprintw(mainwin, y, 0, "For just $%d per item, we can resize weapons or armour to fit you.", resizecost); y ++; + mvwprintw(mainwin, y, 0, "(item quality will not be affected)"); y += 2; + if (countmoney(player->pack) < resizecost) { + msg("Sadly, you cannot afford the resizing fee.", resizecost); + getch(); + return SR_BACK; + } else { + mvwprintw(mainwin, y, 0, "Pay to resize an item (you have $%d) [yn]? ", countmoney(lf->pack)); + } + + ch = getch(); + if (ch != 'y') { + return SR_BACK; + } + + // ask which object + sprintf(buf, "Resize which object (you have $%d)?", countmoney(lf->pack)); + initprompt(&prompt, buf); + for (o = player->pack->first ; o ; o = o->next) { + if (hasflag(o->flags, F_MULTISIZE) && !armourfits(player, o, NULL)) { + char costbuf[BUFLEN]; + getobname(o, buf, o->amt); + sprintf(costbuf, "%-60s($%d)", buf, resizecost); + addchoice(&prompt, o->letter, costbuf, costbuf, o, NULL); + } + } + addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); + ch = getchoice(&prompt); + + o = (object_t *)prompt.result; + if (o) { + msg("You hand over $%d.", resizecost); more(); + givemoney(player, NULL, resizecost); + resizeobject(o, getlfsize(player)); // always go to player's size + getobname(o, buf, 1); + msgnocap("%c - %s", o->letter, buf); + more(); + } + + return SR_CONTINUE; +} + enum SHOPRETURN shoprest(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *ndonated) { char ch,obid[BUFLENSMALL],buf[BUFLEN]; int y,i; diff --git a/shops.h b/shops.h index 9cd3036..360805c 100644 --- a/shops.h +++ b/shops.h @@ -9,5 +9,6 @@ enum SHOPRETURN shopdonate(lifeform_t *lf, object_t *vm, int starty, char *topte enum SHOPRETURN shopdetectcurse(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *npurchased); enum SHOPRETURN shopmiracle(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *npurchased); enum SHOPRETURN shoppurchase(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *npurchased); +enum SHOPRETURN shopresize(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *npurchased); enum SHOPRETURN shoprest(lifeform_t *lf, object_t *vm, int starty, char *toptext, int *npurchased); diff --git a/spell.c b/spell.c index ef94bae..1fe1d68 100644 --- a/spell.c +++ b/spell.c @@ -1613,12 +1613,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef } // resize it! - f = hasflag(o->flags, F_ARMOURSIZE); - if (f) { - f->val[0] = wantsize; - } else { - addflag(o->flags, F_ARMOURSIZE, wantsize, NA, NA, NULL); - } + resizeobject(o, wantsize); f = hasflag(o->flags, F_OBHP); if (f) { diff --git a/text.c b/text.c index fc4683c..2b326c6 100644 --- a/text.c +++ b/text.c @@ -18,6 +18,8 @@ extern long curtime; extern lifeform_t *player; +extern enum GAMEMODE gamemode; + int needan(char *text) { if (isvowel(tolower(text[0]))) { return B_TRUE; @@ -360,7 +362,7 @@ int getaccuracynum(int accpct) { // returns a const char * char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) { - float pct; + //float pct; enum LFSIZE ownersize = SZ_HUMAN; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; @@ -369,7 +371,11 @@ char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam ownersize = getlfsize(lf); } - pct = (int)(((float) dam / (float) maxhp) * 100.0); + //pct = (int)(((float) dam / (float) maxhp) * 100.0); + // base verb on amount of damage, not percentage of target. + // reasoning: just because a monster has lots of hp, doesn't mean + // you can only "scratch" it rather than "slice" it. + //pct = (int)(((float) dam / 50.0) * 100.0); if (wep) { int i; @@ -380,15 +386,15 @@ char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam if ((f->val[0] == NA) && (f->val[1] == NA)) { return f->text; } else if (f->val[0]) { - if (pct >= f->val[0]) { + if (dam >= f->val[0]) { if (f->val[1] == NA) { return f->text; - } else if (pct <= f->val[1]) { + } else if (dam <= f->val[1]) { return f->text; } } } else if (f->val[1]) { - if (pct <= f->val[1]) { + if (dam <= f->val[1]) { return f->text; } } @@ -398,71 +404,75 @@ char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam if (damtype == DT_ACID) { return "burn"; } else if (damtype == DT_BASH) { - if (pct <= 5) { + if (dam <= 2) { return "whack"; - } else if (pct <= 15) { + } else if (dam <= 6) { if (onein(2)) { return "hit"; } else { return "bash"; } - } else if (pct <= 25) { + } else if (dam <= 12) { return "pummel"; - } else if (pct <= 35) { - return "slam"; - } else { - return "clobber"; + } else if (dam <= 18) { + if (onein(2)) { + return "slam"; + } else { + return "clobber"; + } } } else if (damtype == DT_BITE) { if (lf && (ownersize <= SZ_SMALL)) { - if (pct <= 5) { + if (dam <= 2) { return "nip"; } else { return "bite"; } } else { - if (pct <= 5) { + if (dam <= 4) { return "gnaw"; - } else if (pct <= 30) { + } else if (dam <= 8) { return "bite"; } else { return "savage"; } } } else if (damtype == DT_CHOP) { - if (pct <= 5) { + if (dam <= 4) { return "chop"; - } else if (pct <= 15) { + } else if (dam <= 8) { return "hack"; } else { return "cleave"; } } else if (damtype == DT_COLD) { - if (pct <= 10) { + if (dam <= 4) { return "chill"; } else { return "freeze"; } } else if (damtype == DT_CRUSH) { - return "crush"; + return "crush"; } else if (damtype == DT_ELECTRIC) { - if (pct <= 5) { + if (dam <= 2) { return "zap"; - } else if (pct <= 15) { + } else if (dam <= 6) { return "jolt"; - } else if (pct <= 20) { + } else if (dam <= 12) { return "shock"; - } else if (pct <= 30) { + } else if (dam <= 18) { return "electrify"; } else { return "electrocute"; } } else if ((damtype == DT_FIRE) || (damtype == DT_HEAT)) { - if (pct <= 5) { + if (dam <= 2) { return "scorch"; - } else if (pct <= 20) { + } else if (dam <= 6) { + return "singe"; + } else if (dam <= 12) { return "burn"; - } else if (pct <= 40) { + } else if (dam <= 18) { return "scald"; } else { return "incinerate"; @@ -477,21 +487,21 @@ char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam return "purify"; } } else if (damtype == DT_MAGIC) { - if (pct <= 5) { + if (dam <= 2) { return "zap"; - } else if (pct <= 15) { + } else if (dam <= 6) { return "sear"; - } else if (pct <= 40) { + } else { return "blast"; } } else if (damtype == DT_PIERCE) { - if (pct <= 5) { + if (dam <= 2) { return "poke"; - } else if (pct <= 15) { + } else if (dam <= 6) { return "stab"; - } else if (pct <= 30) { + } else if (dam <= 12) { return "pierce"; - } else if (pct <= 40) { + } else if (dam <= 18) { return "spear"; } else { return "deeply stab"; @@ -501,11 +511,11 @@ char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam } else if (damtype == DT_PROJECTILE) { return "hit"; } else if (damtype == DT_SLASH) { - if (pct <= 5) { + if (dam <= 2) { return "scratch"; - } else if (pct <= 15) { + } else if (dam <= 6) { return "hit"; - } else if (pct <= 30) { + } else if (dam <= 12) { return "slash"; } else { return "slice"; @@ -909,6 +919,32 @@ char *getinjurydesc(enum BODYPART where, enum DAMTYPE dt) { return ""; } +char *getobmodprefix(object_t *o, obmod_t *om) { + // masterwork/shoddy doors have names based on material. + if (isdoor(o, NULL)) { + // player perceptive enough to notice? + if (om->id == OM_MASTERWORK) { + if ((gamemode == GM_GAMESTARTED) && (getskill(player, SK_PERCEPTION) < PR_BEGINNER)) { + return NULL; + } + switch (o->material->id) { + case MT_STONE: return "reinforced "; + case MT_METAL: return "reinforced "; + default: return "sturdy "; + } + } else if (om->id == OM_SHODDY) { + if ((gamemode == GM_GAMESTARTED) && (getskill(player, SK_PERCEPTION) < PR_BEGINNER)) { + return NULL; + } + switch (o->material->id) { + case MT_STONE: return "crumbling "; + case MT_METAL: return "rusted "; + default: return "rotted "; + } + } + } + return om->prefix; +} char *getrarityname(enum RARITY rr) { switch (rr) { diff --git a/text.h b/text.h index 9778356..ff81992 100644 --- a/text.h +++ b/text.h @@ -26,6 +26,7 @@ char *getdrunktext(flag_t *drunkflag); char *getinjuredbpname(enum BODYPART bp); char *getinjuryname(enum DAMTYPE dt); char *getinjurydesc(enum BODYPART bp, enum DAMTYPE dt); +char *getobmodprefix(object_t *o, obmod_t *om); char *getrarityname(enum RARITY rr); char *getregionname(char *buf, map_t *m, region_t *r, int withlevel); char *getreldirname(int reldir);