diff --git a/Makefile b/Makefile index 7d03891..9750be2 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,2 @@ -nexus: Makefile defs.h nexus.c nexus.h ai.c ai.h attack.c attack.h flag.c flag.h io.c io.h lf.c lf.h map.c map.h move.c move.h objects.c objects.h text.c text.h save.c save.h spell.c spell.h - gcc -Wall -g -o nexus nexus.c ai.c attack.c flag.c io.c lf.c map.c move.c objects.c text.c save.c spell.c -lncurses +nexus: Makefile defs.h nexus.c nexus.h ai.c ai.h attack.c attack.h flag.c flag.h io.c io.h lf.c lf.h map.c map.h move.c move.h objects.c objects.h text.c text.h save.c save.h spell.c spell.h vault.c vault.h + gcc -Wall -g -o nexus nexus.c ai.c attack.c flag.c io.c lf.c map.c move.c objects.c text.c save.c spell.c vault.c vault.h -lncurses diff --git a/defs.h b/defs.h index b43c373..23cda2f 100644 --- a/defs.h +++ b/defs.h @@ -1,6 +1,8 @@ #ifndef __DEFS_H #define __DEFS_H + + // MACROS #define MAXOF(a,b) (a > b ? a : b) @@ -102,6 +104,7 @@ enum SKILLLEVEL { // save/load #define MAPDIR "data/maps" #define SAVEDIR "data/save" +#define VAULTDIR "vaults" #define DUMMYCELLTYPE 0xabcd // SPECIAL NUMBERS/CONSTANTS @@ -110,10 +113,10 @@ enum SKILLLEVEL { #define NA (-9874) #define NOBODY (-1) #define ALLCONFERRED (-9873) +#define PCT (65432) // must be POSITIVE #define AUTO (-7654) - enum GAMEMODE { GM_FIRST, GM_INIT, @@ -407,8 +410,11 @@ enum LOFTYPE { // Cell types enum CELLTYPE { + CT_NONE = 0, // walls CT_WALL, + CT_WALLGLASS, + CT_WALLMETAL, CT_ROOMWALL, // empty CT_CORRIDOR, @@ -419,12 +425,6 @@ enum CELLTYPE { CT_ROOM, }; -enum SPECIALROOMTYPE { - RT_NONE = 0, - RT_FLOODED, -}; - - enum SPELLSCHOOL { SS_NONE, SS_DIVINE, @@ -573,17 +573,24 @@ enum HABITAT { #define RARITYVARIANCELF (5) #define RARITYVARIANCEOB (10) -/* enum RARITY { - RR_UNIQUE = 7, - RR_NEVER = 6, - RR_VERYRARE = 5, - RR_RARE = 4, - RR_UNCOMMON = 3, - RR_COMMON = 2, - RR_FREQUENT = 1, + RR_UNIQUE = 6, + RR_NEVER = 5, + RR_VERYRARE = 4, + RR_RARE = 3, + RR_UNCOMMON = 2, + RR_COMMON = 1, + RR_NONE = 0, +}; + +// for genericising weapons, etc +enum GOODNESS { + G_NA = 0, + G_AVERAGE, + G_GOOD, + G_GREAT, + G_EXCELLENT, }; -*/ enum RACECLASS { RC_ANY, // not actually ever defined @@ -627,6 +634,7 @@ enum RACE { R_KOBOLD, R_LIZARDMAN, R_LURKINGHORROR, + R_MINOTAUR, R_OGRE, R_OGRESAVAGE, R_OGREWARHULK, @@ -1735,7 +1743,7 @@ enum FLAG { F_HASHIDDENNAME, // whether this object class has a hidden name F_IDENTIFIED, // whether this object is fully identified // bad flags - F_DEEPWATER, // v0 = depth.oooooooooooo + F_DEEPWATER, // v0 = depth. F_WALKDAM, // val0 = damtype, text = dam per sec F_WALKDAMBP, // v0 = bodypart, v1 = damtype, text = dam per sec // if v2 == FALLTHRU, damage falls through to lf if @@ -1805,6 +1813,10 @@ enum FLAG { // calculation F_STARTJOB, // val0 = %chance of starting with it, v1 = jobid F_STARTATT, // val0 = A_xxx, val0 = start bracket (ie. IQ_GENIUS) + // if text is set, it overrides val0. + // text can be: + // x (single number) + // x-y (range) F_STARTHIDDENPCT, // val0 = pct chance auto-generated monster will // start off hidden F_CORPSETYPE, // text field specifies what corpse obtype to leave @@ -2088,7 +2100,20 @@ enum FLAG { F_LEVFLAG, // at level v0, this job gains flagid v1, flagval0=v2, // flagtext = text - // + // vault flags + F_AUTODOORS, // automatically create at least one door + F_AUTOPOPULATE, // fill this vault with obs/mons/pillars like normal rooms + F_VAULTATOB, // v0/1=x/y, v1=pctchance, text=obname + F_VAULTATLF, // v0/1=x/y, v1=pctchance, text=lfname + F_VAULTATCELL, // v0/1=x/y, v1=pctchance, text=cellname + F_VAULTBOX, // v0=thingtype, v1=pctchance, v2=fill?, text=x1,y1,x2,y2,thingname + F_VAULTSCATTER, // v0=thingtype, v1=pctchance + // text=x1,y1,x2,y2,mincount-maxcount,thingname + // if maxcount is PCT, mincount is a percentage + // of the total space. + F_VAULTRANDOMMAP, // v0=minwidth, v1=minheight. this vault's map is + // v0/1 can be NA. + // just a normal random room F_NULL = -1 }; @@ -2325,6 +2350,46 @@ typedef struct map_s { struct map_s *next, *prev; } map_t; //////////////// remember to modify save/load for new props!! +#define MAXVAULTARGS 10 + +enum VAULTSTATE { + VS_ALLOCATED, + VS_NOID, + VS_LOADING, + VS_LOADINGMAP, + VS_LOADINGLEGEND, + VS_LOADINGFLAGS, +}; + +enum VAULTTHING { + VT_NONE, + VT_OB, + VT_LF, + VT_CELL, +}; + +typedef struct vlegend_s { + char ch; + enum VAULTTHING tt; + char *what; + int pct; + struct vault_s *vault; + struct vlegend_s *next, *prev; +} vlegend_t; + +typedef struct vault_s { + char *filename; + char *id; + int valid; + int state; + char map[MAX_MAPW*MAX_MAPH]; + int mlen; + int w,h; + struct vlegend_s *legend, *lastlegend; + struct vault_s *next, *prev; + struct flagpile_s *flags; +} vault_t; + typedef struct glyph_s { int ch; int colour; @@ -2513,10 +2578,13 @@ typedef struct job_s { struct job_s *next, *prev; } job_t; +#define MAXOCNOUNS 5 typedef struct objectclass_s { enum OBCLASS id; char *name; char *desc; + char *noun[MAXOCNOUNS]; + int nnouns; glyph_t glyph; struct flagpile_s *flags; struct objectclass_s *next, *prev; diff --git a/doc/vaults.txt b/doc/vaults.txt new file mode 100644 index 0000000..aa8e17d --- /dev/null +++ b/doc/vaults.txt @@ -0,0 +1,53 @@ +General format: + @id:textual_name + @map + EITHER + map definition + OR + random(minw,minh) + @end + @legend + k:mon:kobold + s:ob:50:short sword + #:cell:stone wall + @end + @flags + ... + @end + +Legend is: + c:type:what[:pct] + + c = any letter + type = ob, mon or cell + pct = optional pct chance of appearing + what = text of what this letter represents + +Flags can be: + at(x,y) type:what[:pct] // put what at position x,y + 'type' can be: ob, mon, cell + coords can be negative ("count back from right/bottom") + + box(x,y,x2,y2) type:what[:pct] // outline box with what + fill(x,y,x2,y2) type:what[:pct] // filled box with what + coords can be negative ("count back from right/bottom") + + scatter(x,y,x2,y2) type:what:howmany[:pct] // scatter what within region + howmany can be: + - a number (x) + - a range (x-y) + - a pct of the total region cells (x%) + coords can be negative ("count back from right/bottom") + + + autodoors // automatically add at least one door to the edges of + // this room. + + autopop // automatically add obs/mons/pillars to this room + +NOTES: + when adding lfs/objects, "random" creates a random one. + + + + diff --git a/io.c b/io.c index 9ea2e38..0d7858d 100644 --- a/io.c +++ b/io.c @@ -5038,8 +5038,12 @@ void drawglyph(glyph_t *g, int x, int y) { col = g->colour; } setcol(gamewin, col); - //mvwprintw(gamewin, y, x, "%lc", g->ch); - mvwaddch(gamewin, y, x, g->ch); + if (g->ch > 255) { + // note: much slower + mvwprintw(gamewin, y, x, "%lc", g->ch); + } else { + mvwaddch(gamewin, y, x, g->ch); + } unsetcol(gamewin, col); } diff --git a/lf.c b/lf.c index 9eb690c..0f05885 100644 --- a/lf.c +++ b/lf.c @@ -6875,7 +6875,7 @@ void initrace(void) { addflag(lastrace->flags, F_NOISETEXT, N_FLY, 1, NA, "^flapping wings"); addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); - addrace(R_GIANTHILL, "hill giant", 160, 'H', C_BROWN, MT_FLESH, RC_HUMANOID); + addrace(R_GIANTHILL, "hill giant", 160, 'H', C_GREY, MT_FLESH, RC_HUMANOID); addflag(lastrace->flags, F_RARITY, H_DUNGEON, 55, NA, NULL); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_HUGE, NA, NA, NULL); @@ -7348,6 +7348,34 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 30, NA, NA, NULL); addflag(lastrace->flags, F_DEAF, B_TRUE, NA, NA, NULL); + + addrace(R_MINOTAUR, "minotaur", 130, 'H', C_BROWN, MT_FLESH, RC_HUMANOID); + addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL); + addflag(lastrace->flags, F_RARITY, H_DUNGEON, 62, NA, NULL); + addflag(lastrace->flags, F_ENHANCESMELL, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HATESRACE, R_GNOLL, NA, NA, NULL); + addflag(lastrace->flags, F_HITDICE, 6, 3, NA, NULL); + addflag(lastrace->flags, F_ARMOURRATING, 12, NA, NA, NULL); + addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL); + addflag(lastrace->flags, F_CARNIVORE, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_IQ, NA, NA, "5-7"); + addflag(lastrace->flags, F_STARTATT, A_DEX, DX_DEXTROUS, NA, NULL); + addflag(lastrace->flags, F_STARTATT, A_STR, ST_TITANIC, NA, NULL); + addflag(lastrace->flags, F_HASATTACK, OT_BUTT, NA, NA, "2d4"); + addflag(lastrace->flags, F_HASATTACK, OT_BUTT, NA, NA, "2d4"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "+2 heavy flail"); + addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "greataxe"); + addflag(lastrace->flags, F_WANTSBETTERWEP, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_HASSKILL, SK_TRACKING, PR_EXPERT, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_GRAB, NA, NA, NULL); + addflag(lastrace->flags, F_CANWILL, OT_A_CHARGE, NA, NA, "range:5;"); + addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "roars^a roar"); + addflag(lastrace->flags, F_SEEINDARK, 5, NA, NA, NULL); + addflag(lastrace->flags, F_MORALE, 20, NA, NA, NULL); + addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL); + + addrace(R_OGRE, "ogre", 160, 'O', C_BROWN, MT_FLESH, RC_HUMANOID); addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_SIZE, SZ_LARGE, NA, NA, NULL); @@ -11391,7 +11419,27 @@ int rollstat(lifeform_t *lf, enum ATTRIB attr) { f = hasflagval(lf->flags, F_STARTATT, attr, NA, NA, NULL); if (f) { - bracket = f->val[1]; + if (strlen(f->text)) { + int val; + if (strchr(f->text, '-')) { + int min,max; + char *p; + char buf[BUFLENSMALL]; + // text is a range + p = readuntil(buf, f->text, '-'); + min = atoi(buf); + p = readuntil(buf, p, '-'); + max = atoi(buf); + val = rnd(min,max); + } else { + val = atoi(f->text); + } + lf->att[attr] = val; + return B_FALSE; + } else { + bracket = f->val[1]; + } + } else { switch (attr) { case A_STR: @@ -13686,6 +13734,11 @@ int validateraces(void) { printf("ERROR in race '%s' - F_HITCONFER, but no HITCONFERVALS defined.\n", r->name); goterror = B_TRUE; } + } else if (f->id == F_STARTATT) { + if (strlen(f->text) && (f->val[1] != NA)) { + printf("ERROR in race '%s' - F_STARTATT has both text range and val1.", r->name); + goterror = B_TRUE; + } } else if (f->id == F_NOISETEXT) { if (f->val[0] == N_FLY) { if (!hasflag(r->flags, F_FLYING) && !hasflag(r->flags, F_LEVITATING)) { diff --git a/map.c b/map.c index 4a73c43..296076c 100644 --- a/map.c +++ b/map.c @@ -12,6 +12,7 @@ #include "map.h" #include "objects.h" #include "text.h" +#include "vault.h" extern map_t *firstmap,*lastmap; extern celltype_t *firstcelltype, *lastcelltype; @@ -169,12 +170,6 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto if (autogen) { // sometimes start off asleep in new maps - if (!lfhasflag(lf, F_DEAF) && cansleep(lf)) { - // TODO: base this on the time, and whether monster is nocturnal - if (rnd(1,2) == 1) { - addflag(lf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL); - } - } f = lfhasflag(lf, F_STARTHIDDENPCT); if (f) { if (rnd(1,100) <= f->val[0]) { @@ -184,6 +179,12 @@ lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int auto addflag(lf->flags, F_HIDING, 0, NA, NA, NULL); } } + if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_DEAF) && cansleep(lf)) { + // TODO: base this on the time, and whether monster is nocturnal + if (rnd(1,2) == 1) { + addflag(lf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL); + } + } } // appears in groups? @@ -358,6 +359,134 @@ int addrandomthing(cell_t *c, int obchance, int *nadded) { return rv; } +// whichside should be D_ORTH +// for now, this function will NOT include room corners +void getroomedge(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, int whichside, cell_t **retcell, int *ncells) { + int x,y; + *ncells = 0; + if (whichside == D_N) { + y = miny; + for (x = minx+1; x <= maxx-1; x++) { + retcell[*ncells] = getcellat(map, x, y); + (*ncells)++; + } + } else if (whichside == D_S) { + y = maxy; + for (x = minx+1; x <= maxx-1; x++) { + retcell[*ncells] = getcellat(map, x, y); + (*ncells)++; + } + } else if (whichside == D_W) { + x = minx; + for (y = miny+1; y <= maxy-1; y++) { + retcell[*ncells] = getcellat(map, x, y); + (*ncells)++; + } + } else if (whichside == D_E) { + x = maxx; + for (y = miny+1; y <= maxy-1; y++) { + retcell[*ncells] = getcellat(map, x, y); + (*ncells)++; + } + } +} + + +void autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy) { + int i,d; + cell_t *poss[MAXCANDIDATES], *cell[MAXCANDIDATES]; // TODO: should this be maxroomw * maxroomh ? + int ncells = 0, npossible = 0; + int doorsadded = 0; + + // for each side, make list of all possible door locations + // then pick one randomly. + // BUT if the nearby corridor only has one exit, always + // place a door. + for (d = D_N; d <= D_W; d++) { + npossible = 0; + getroomedge(map, roomid, minx, miny, maxx, maxy, d, cell, &ncells); + for (i = 0; i < ncells; i++) { + cell_t *newcell; + // is there empty space on the other side of this wall segment? + newcell = getcellindir(cell[i], d); + if (newcell && !newcell->type->solid) { + int doorcount; + // if so, make sure there are no other adjacent doors + doorcount = countadjcellswithflag(cell[i], F_DOOR); + if (doorcount == 0) { + // if there is only one way out of the adjacent empty cell, and + // walls to either side of the potential door location, then + // always add a door + if ((countcellexits(newcell) == 1) && wallstoleftright(newcell, d)) { + if (onein(2)) { + makedoor(cell[i]); + } else { + setcelltype(cell[i], getemptycelltype(map->habitat)); + } + } else { + // otherwise mark this as a _potential_ door location. + poss[npossible] = cell[i]; + npossible++; + } + } + } + } + + // if we have potential door locations, add one somewhere along this wall. + if (npossible > 0) { + int sel; + sel = rnd(0,npossible-1); + makedoor(poss[sel]); + doorsadded++; + } + } // end foreach direction + + + if (doorsadded == 0) { + int i,d,used[MAXDIR_ORTH],poss[MAXDIR_ORTH]; + int dodoor[MAXDIR_ORTH]; + int ndodoors = 0; + int ndoors,nposs = 0; + + int sel; + // + for (d = D_N; d <= D_W; d++) { + used[d] = B_FALSE; + } + // no possible door locations - add a random number + ndoors = rnd(1,4); + + for (i = 0; i < ndoors; i++) { + // find a dir we haven't already used + nposs = 0; + for (d = D_N; d <= D_W; d++) { + if (!used[d]) poss[nposs++] = d; + } + if (nposs <= 0) { + break; + } + sel = rnd(0,nposs-1); + used[poss[sel]] = B_TRUE; + dodoor[ndodoors++] = poss[sel]; + } + + // actually make the doors + for (i = 0; i < ndodoors; i++) { + int sel; + d = dodoor[i]; + getroomedge(map, roomid, minx, miny, maxx, maxy, d, cell, &ncells); + sel = rnd(0,ncells-1); + + if (onein(2)) { + makedoor(cell[sel]); + } else { + setcelltype(cell[sel], getemptycelltype(map->habitat)); + } + } + } +} + + int cellhaslos(cell_t *c1, cell_t *dest) { int deltax, deltay; int numpixels; @@ -579,6 +708,13 @@ void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) { } } +int getdoorlockdiff(int depth) { + return 19 + depth; +} +int getdoorsecretdiff(int depth) { + return 20 + (depth / 2); +} + enum CELLTYPE getemptycelltype(enum HABITAT hab) { switch (hab) { case H_DUNGEON: @@ -870,13 +1006,10 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { int lastdir; int numrooms = 0; int roomw[MAXROOMS],roomh[MAXROOMS]; + vault_t *roomvault[MAXROOMS]; //int roomspecial[MAX_MAPROOMS]; - int minroomw = MIN_ROOMW; - int minroomh = MIN_ROOMH; - int maxroomw = MAX_ROOMW; - int maxroomh = MAX_ROOMH; - int bestx,besty; - int w,h; + //int bestx,besty; + //int w,h; //int startdir,forcex,forcey,ntries; cell_t *cell, *c; object_t *o; @@ -1075,40 +1208,24 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { //printf("using %d rooms\n",numrooms); //dblog("Adding %d rooms...\n",numrooms); for (i = 0; i < numrooms; i++) { - // select random width/height - w = rnd(minroomw, maxroomw); - h = rnd(minroomh, maxroomh); - - roomw[i] = w; - roomh[i] = h; - - if (calcroompos(map, w, h, &bestx, &besty)) { - //printf("** couldn't make room!\n"); - } else { - // we now have the room position - fill it in - createroom(map, bestx,besty, w,h, i); - - /* - if (getrand(1,100) <= CH_SPECIALROOM) { - int curpos; - int roomid; - roomid = getrandomspecialroom(wreck->mazelev[curz].type); - for (y = besty; y <= (besty + (h-1)); y++) { - for (x = bestx; x <= (bestx + (w-1)); x++) { - curpos = y*MAZEW+x; - wreck->mazelev[curz].maze[curpos].floorver = roomid; - } - } - roomspecial[i] = roomid; - } else { - roomspecial[i] = B_FALSE; - } - */ + // maybe make it a special room + roomvault[i] = NULL; + if (rnd(1,100) <= getvaultchance(map)) { + vault_t *v; + v = getvaulttype(map); + if (!createvault(map, i, v, &roomw[i],&roomh[i])) { + // success + roomvault[i] = v; + } + } + if (!roomvault[i]) { + // just do a normal room + createroom(map, i, NA, NA, NULL, NULL, &roomw[i],&roomh[i]); + roomvault[i] = B_FALSE; } } } - // add staircases - dungeons always have an up and down stairs for (i = 0; i < 3; i++) { // add up stairs @@ -1150,84 +1267,76 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) { // add pillars & objects & monsters to rooms if (wantrooms && (numrooms > 0)) { for (i = 0; i < numrooms; i++) { - int numobsmin,numobsmax,numobs,n; - int maxpillars; + if (!roomvault[i] || hasflag(roomvault[i]->flags, F_AUTOPOPULATE)) { + int numobsmin,numobsmax,numobs,n; + int maxpillars; - //dblog("Adding obs to room %d/%d",i+1,numrooms); - maxpillars = (roomw[i] / 4) + (roomh[i] / 4); - // add pillars first - if ((maxpillars > 0) && (rnd(1,100) <= CH_PILLAR)) { - int n; - int numpillars; - numpillars = rnd(1,maxpillars); - 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); + //dblog("Adding obs to room %d/%d",i+1,numrooms); + maxpillars = (roomw[i] / 4) + (roomh[i] / 4); + // add pillars first + if ((maxpillars > 0) && (rnd(1,100) <= CH_PILLAR)) { + int n; + int numpillars; + numpillars = rnd(1,maxpillars); + 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); + if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { + setcelltype(cell, CT_WALL); + } } } - } - /* - if (roomspecial[i]) { - // chance is increased - numobsmin = (roomw[i]*roomh[i]) / 4 ; - numobsmax = (roomw[i]*roomh[i]) / 2 ; - } else { - */ - numobsmin = 0; - //numobsmax = MAXOF(roomw[i],roomh[i]) / 2; - numobsmax = MAXOF(roomw[i],roomh[i]); + numobsmin = 0; + //numobsmax = MAXOF(roomw[i],roomh[i]) / 2; + numobsmax = MAXOF(roomw[i],roomh[i]); - //} + // then objects/monsters + if (numobsmax <= numobsmin) { + numobs = numobsmin; + } else { + numobs = rnd(numobsmin,numobsmax); + } + if (db) dblog("--> Will add %d objects to room %d (of %d)",numobs,i,numrooms); + for (n = 0 ; n < numobs; n++) { + int ntries = 0; + int nmonsters = 0; + cell_t *c; + done = B_FALSE; + while (!done) { + c = getrandomroomcell(map, i); + // if nothing there + if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { + int obchance; + int nadded = 0; - // then objects/monsters - if (numobsmax <= numobsmin) { - numobs = numobsmin; - } else { - numobs = rnd(numobsmin,numobsmax); - } - if (db) dblog("--> Will add %d objects to room %d (of %d)",numobs,i,numrooms); - for (n = 0 ; n < numobs; n++) { - int ntries = 0; - int nmonsters = 0; - cell_t *c; - done = B_FALSE; - while (!done) { - c = getrandomroomcell(map, i); - // if nothing there - if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { - int obchance; - int nadded = 0; + // limit # monster per room to depth+1 + if (nmonsters >= (depth+1)) { + obchance = 100; + } else { + // slightly more chance of objects in rooms + obchance = getobchance(map->habitat) + 10; + } - // limit # monster per room to depth+1 - if (nmonsters >= (depth+1)) { - obchance = 100; + if (addrandomthing(c,obchance, &nadded) == TT_MONSTER) { + nmonsters += nadded; + } + done = B_TRUE; } else { - // slightly more chance of objects in rooms - obchance = getobchance(map->habitat) + 10; + ntries++; } - if (addrandomthing(c,obchance, &nadded) == TT_MONSTER) { - nmonsters += nadded; + if (ntries >= numobs) { + break; } - done = B_TRUE; - } else { - ntries++; - } - - if (ntries >= numobs) { - break; } } - } - - } - } + } // end if !vault + } // end foreach room + } // end if wantrooms & nrooms>0 if (db) dblog("Finished adding objects."); } @@ -1541,12 +1650,194 @@ void createmap(map_t *map, int depth, int region, int habitat, map_t *parentmap, map->beingcreated = B_FALSE; } -void createroom(map_t *map, int minx, int miny, int w, int h, int roomid) { +int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth) { + int w,h,x,y; + int minx,miny,maxx,maxy; + flag_t *f; + if (!v) { + return B_TRUE; + } + + f = hasflag(v->flags, F_VAULTRANDOMMAP); + if (f) { + // just make a normal room to start with. + if (createroom(map, roomid, f->val[0], f->val[1], &minx, &miny, &w, &h)) { + return B_TRUE; + } + maxx = minx + w - 1; + maxy = miny + h - 1; + } else { + // get width/height from vault + w = v->w; + h = v->h; + if (retw) *retw = w; + if (reth) *reth = h; + // find vault position + if (calcroompos(map, w, h, &minx, &miny)) { + dblog("** couldn't make vault room!\n"); + return B_TRUE; + } + maxx = minx + (w-1); + maxy = miny + (h-1); + + // now make it + for (y = miny; y <= maxy; y++) { + for (x = minx; x <= maxx; x++) { + cell_t *cell; + celltype_t *ct; + cell = getcellat(map, x, y); + + // set cell type + ct = getvaultcelltype(v, x-minx,y-miny); + setcelltype(cell, ct ? ct->id : getemptycelltype(cell->map->habitat)); + // set roomid + cell->roomid = roomid; + // add objects + addvaultcellcontents(cell, v, x-minx,y-miny); + } + } + } + + // add other stuff to the vault + addvaultcontents(map, v, minx, miny, maxx, maxy); + + // auto add doors if required + if (hasflag(v->flags, F_AUTODOORS)) { + autodoors(map, roomid, minx, miny, maxx, maxy); + } + + linkdoors(map, roomid, minx, miny, maxx,maxy); + + return B_FALSE; +} + +// make sure doors in a given room link up to the rest of the map. +void linkdoors(map_t *m, int roomid, int minx, int miny, int maxx, int maxy) { + int x,y,i; + cell_t *poss[MAXCANDIDATES],*c; + int nposs = 0; + int db = B_FALSE; + + if (db) dblog("linkdoors for roomid %d", roomid); + + // find all doors // TODO: ...or "exits" + for (y = miny; y <= maxy; y++) { + for (x = minx; x <= maxx; x++) { + c = getcellat(m, x, y); + if (!c) continue; + + if (hasobwithflag(c->obpile, F_DOOR)) { + if (db) dblog("found door at %d,%d",x,y); + poss[nposs++] = c; + } + } + } + + if (db) dblog("%d doors found.",nposs); + + // for each door, make sure it links to at least one cell which isn't + // part of this room + for (i = 0; i < nposs; i++) { + int d; + int ncorridors = 0; + for (d = DC_N; d <= DC_NW; d++) { + c = getcellindir(poss[i], d); + if (c && cellwalkable(NULL, c, NULL) && (c->roomid != roomid)) { + ncorridors++; + } + } + if (db) dblog("Door #%d leads to %d corridors. ",i, ncorridors); + + // no corridors? + if (ncorridors == 0) { + int poss2[MAXCANDIDATES],nposs2; + int dist[MAXDIR_ORTH]; + int bestdist = 999; + if (db) dblog(" Need to link."); + // link it. starting from the door, count the number of cells in + // each direction until we hit an empty (walkable) cell not of this room. + // if we hit a cell of this roomid, mark this dir as invalid. + for (d = D_N; d <= D_W; d++) { + dist[d] = 0; + c = getcellindir(poss[i], d); + while (c) { + dist[d]++; + if (c->roomid == roomid) { + // mark dir as invalid + dist[d] = 999; + break; + } else if (cellwalkable(NULL, c, NULL)) { + // walkable and not in this vault. finished. + break; + } + // check next cell + c = getcellindir(c, d); // getting the same cell! + } + if (dist[d] < bestdist) bestdist = dist[d]; + } + + if (bestdist != 999) { + int whichway,sel; + // now pick the shortest one + // get list of all the maximums and randomly tie-break + nposs2 = 0; + for (d = D_N; d <= D_W; d++) { + if (dist[d] == bestdist) { + poss2[nposs2++] = d; + } + } + sel = rnd(0,nposs2-1); + whichway = poss2[sel]; + + // now create a path + if (db) dblog(" Linking %s (distance %d).", getdirname(whichway), bestdist); + c = getcellindir(poss[i], whichway); + while (c && !cellwalkable(NULL, c, NULL)) { + setcelltype(c, getemptycelltype(c->map->habitat)); + c = getcellindir(poss[i], whichway); + } + } + } // end if ncorridors = 0 + } // end for each door + if (db) dblog("linkdoors complete."); +} + +// room w/h are returned in *w and *h if given. +int createroom(map_t *map, int roomid, int overrideminw, int overrideminh, int *retx, int *rety, int *retw, int *reth) { int x,y; - int poss[MAXOF(MAX_MAPW,MAX_MAPH)]; - int npossible; - cell_t *cell, *newcell; + cell_t *cell; + int minx,miny; int maxx,maxy; + int w,h; + int minroomw = MIN_ROOMW; + int minroomh = MIN_ROOMH; + int maxroomw = MAX_ROOMW; + int maxroomh = MAX_ROOMH; + + if (overrideminw != NA) { + minroomw = overrideminw; + } + if (overrideminh != NA) { + minroomh = overrideminh; + } + + // select random width/height + w = rnd(minroomw, maxroomw); + h = rnd(minroomh, maxroomh); + + if (retw) *retw = w; + if (reth) *reth = h; + + // find room position + if (calcroompos(map, w, h, &minx, &miny)) { + dblog("** couldn't make room!\n"); + return B_TRUE; + } + + if (retx) *retx = minx; + if (rety) *rety = miny; + + // we now have the room position - fill it in //printf("trying to create room at (%d,%d) w=%d,h=%d\n", minx, miny, w, h); @@ -1556,7 +1847,6 @@ void createroom(map_t *map, int minx, int miny, int w, int h, int roomid) { for (y = miny; y <= maxy; y++) { for (x = minx; x <= maxx; x++) { cell = getcellat(map, x, y); - // make it a border or room if ((y == miny) || (y == maxy) || (x == minx) || (x == maxx)) { @@ -1570,172 +1860,13 @@ void createroom(map_t *map, int minx, int miny, int w, int h, int roomid) { } } + // add doors + autodoors(map, roomid, minx, miny, maxx, maxy); - // for each side, make list of all possible door locations - // then pick one randomly. - // BUT if the nearby corridor only has one exit, always - // place a door. - - // N - npossible = 0; - y = miny; - for (x = minx+1; x <= maxx-1; x++) { - cell = getcellat(map, x, y); - newcell = getcellindir(cell, D_N); - if (newcell && !newcell->type->solid) { - int doorcount; - - doorcount = countadjcellswithflag(cell, F_DOOR); - - if (doorcount == 0) { - if ((countcellexits(newcell) == 1) && - (iswallindir(newcell,D_E)) && - (iswallindir(newcell,D_W))) { // always add door - makedoor(cell); - } else { - poss[npossible] = x; - npossible++; - } - } - } - } - - if (npossible > 0) { - int sel = rand() % npossible; - //printf("adding N door at %d\n",poss[sel]); - cell = getcellat(map, poss[sel], y); - makedoor(cell); - } - - - // S - npossible = 0; - y = maxy; - for (x = minx+1; x <= maxx-1; x++) { - cell = getcellat(map, x, y); - newcell = getcellindir(cell, D_S); - if (newcell && !newcell->type->solid) { - int doorcount; - - doorcount = countadjcellswithflag(cell, F_DOOR); - - if (doorcount == 0) { - if ((countcellexits(newcell) == 1) && - (iswallindir(newcell,D_E)) && - (iswallindir(newcell,D_W))) { // always add door - makedoor(cell); - } else { - poss[npossible] = x; - npossible++; - } - } - } - } - - if (npossible > 0) { - int sel = rand() % npossible; - //printf("adding S door at %d\n",poss[sel]); - cell = getcellat(map, poss[sel], y); - makedoor(cell); - } - - - // W - npossible = 0; - x = minx; - for (y = miny+1; y <= maxy-1; y++) { - cell = getcellat(map, x, y); - newcell = getcellindir(cell, D_W); - if (newcell && !newcell->type->solid) { - int doorcount; - doorcount = countadjcellswithflag(cell, F_DOOR); - if (doorcount == 0) { - if ((countcellexits(newcell) == 1) && - (iswallindir(newcell,D_N)) && - (iswallindir(newcell,D_S))) { // always add door - makedoor(cell); - } else { - poss[npossible] = y; - npossible++; - } - } - } - } - - if (npossible > 0) { - int sel = rand() % npossible; - //printf("adding W door at %d\n",poss[sel]); - cell = getcellat(map, x, poss[sel]); - makedoor(cell); - } - - - // E - npossible = 0; - x = maxx; - for (y = miny+1; y <= maxy-1; y++) { - cell = getcellat(map, x, y); - newcell = getcellindir(cell, D_E); - if (newcell && !newcell->type->solid) { - int doorcount; - doorcount = countadjcellswithflag(cell, F_DOOR); - if (doorcount == 0) { - if ((countcellexits(newcell) == 1) && - (iswallindir(newcell,D_N)) && - (iswallindir(newcell,D_S))) { // always add door - makedoor(cell); - } else { - poss[npossible] = y; - npossible++; - } - } - } - } - - if (npossible > 0) { - int sel = rand() % npossible; - //printf("adding E door at %d\n",poss[sel]); - cell = getcellat(map, x, poss[sel]); - makedoor(cell); - } - - // maybe make it a special room - if (rnd(1,100) <= getspecialroomchance(map)) { - enum SPECIALROOMTYPE rt; - rt = getspecialroomtype(map); - if (rt == RT_FLOODED) { - int sx,sy,ex,ey; - char wtype[BUFLEN]; - switch (rnd(1,2)) { - case 1: strcpy(wtype, "deep water"); break; - case 2: strcpy(wtype, "shallow water"); break; - } - switch (rnd(1,2)) { - case 1: // entire room flooded - sx = minx+1; - sy = miny+1; - ex = maxx-1; - ey = maxy-1; - break; - case 2: // small walkway around the edge - sx = minx+2; - sy = miny+2; - ex = maxx-2; - ey = maxy-2; - if (sx > ex) sx = ex; - if (sy > ey) sy = ey; - break; - } - for (y = sy; y <= ey; y++) { - for (x = sx; x <= ex; x++) { - cell = getcellat(map, x, y); - addob(cell->obpile, wtype); - } - } - } - } + return B_FALSE; } + int dirtox(int dt, int dir) { if (dt == DT_ORTH) { switch (dir) { @@ -1897,6 +2028,14 @@ celltype_t *findcelltype(enum CELLTYPE cid) { return NULL; } +celltype_t *findcelltypebyname(char *name) { + celltype_t *ct; + for (ct = firstcelltype ; ct ; ct = ct->next) { + if (!strcmp(ct->name, name)) return ct; + } + return NULL; +} + map_t *findmap(int mid) { map_t *m; for (m = firstmap ; m ; m = m->next) { @@ -2199,10 +2338,10 @@ int getobchance(int habitat) { return 0; } // chance that a room is a 'special' one -int getspecialroomchance(map_t *m) { +int getvaultchance(map_t *m) { switch (m->habitat) { case H_DUNGEON: - return 5; + return 10; default: return 0; } @@ -2210,17 +2349,6 @@ int getspecialroomchance(map_t *m) { return 0; } -enum SPECIALROOMTYPE getspecialroomtype(map_t *m) { - if (m->habitat == H_DUNGEON) { - /* - switch (rnd(1,1)) { - - } - */ - return RT_FLOODED; - } - return RT_NONE; -} // chance of each empty cell in a map has of getting an object/monster @@ -2844,7 +2972,7 @@ void makedoor(cell_t *cell) { chance = rolldie(1,6) - (m->depth / 10); if (chance <= 1) { - addflag(o->flags, F_LOCKED, B_TRUE, 19 + (m->depth), NA, NULL); + addflag(o->flags, F_LOCKED, B_TRUE, getdoorlockdiff(m->depth), NA, NULL); } // make it secret? @@ -2855,7 +2983,7 @@ void makedoor(cell_t *cell) { // l10 = 25 // l20 = 30 if (chance <= 1) { - addflag(o->flags, F_SECRET, 20 + (m->depth / 2), NA, NA, NULL); + addflag(o->flags, F_SECRET, getdoorsecretdiff(m->depth), NA, NA, NULL); } } } @@ -2960,6 +3088,9 @@ void setcelltype(cell_t *cell, int id) { cell->type = findcelltype(id); assert(cell->type); cell->roomid = 0; + if ((gamemode == GM_GAMESTARTED) && haslos(player, cell)) { + needredraw = B_TRUE; + } } @@ -2989,3 +3120,19 @@ void updateknowncells(void) { } } } + +int wallstoleftright(cell_t *c, int dir) { + int d1,d2; + switch (dir) { + case D_N: + case D_S: + d1 = D_E; d2 = D_W; break; + case D_E: + case D_W: + d1 = D_N; d2 = D_S; break; + } + if (iswallindir(c,d1) && iswallindir(c,d2)) { + return B_TRUE; + } + return B_FALSE; +} diff --git a/map.h b/map.h index 8c8f5f6..c1fc103 100644 --- a/map.h +++ b/map.h @@ -6,15 +6,19 @@ map_t *addmap(void); lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen, int *nadded); object_t *addrandomob(cell_t *c); int addrandomthing(cell_t *c, int obchance, int *nadded); +void autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy); int cellhaslos(cell_t *c1, cell_t *dest); cell_t *getcellat(map_t *map, int x, int y); int getcelldist(cell_t *src, cell_t *dst); int getcelldistorth(cell_t *src, cell_t *dst); void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer); +int getdoorlockdiff(int depth); +int getdoorsecretdiff(int depth); enum CELLTYPE getemptycelltype(enum HABITAT hab); enum CELLTYPE getwallcelltype(enum HABITAT hab); flag_t *getmapcoords(map_t *m, int *x, int *y); int getmapdifficulty(map_t *m); +void getroomedge(map_t *m, int roomid, int minx, int miny, int maxx, int maxy, int whichside, cell_t **retcell, int *ncells); object_t *gettopobject(cell_t *where); void calclight(map_t *map); int calcroompos(map_t *map, int w, int h, int *bx, int *by); @@ -24,13 +28,15 @@ 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); void createmap(map_t *map, int depth, int region, int habitat, map_t *parentmap, int exitdir); -void createroom(map_t *map, int minx, int miny, int w, int h, int roomid); +int createroom(map_t *map, int roomid, int overrideminw, int overrideminh, int *retx, int *rety, int *retw, int *reth); +int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth); int dirtox(int dt, int dir); int dirtoy(int dt, int dir); void dumpmap(map_t *map); void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *centre); void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int dirtype, int wantannounce); celltype_t *findcelltype(enum CELLTYPE cid); +celltype_t *findcelltypebyname(char *name); map_t *findmap(int mid); map_t *findmapofdepth(int depth); cell_t *findmapentrypoint(map_t *m, int side, lifeform_t *lf); @@ -41,8 +47,8 @@ void forgetcells(map_t *map, int amt); cell_t *getcellindir(cell_t *cell, int dir); int getnewdigdir(cell_t *cell, int lastdir, int turnpct, int *moved); int getobchance(int habitat); -int getspecialroomchance(map_t *m); -enum SPECIALROOMTYPE getspecialroomtype(map_t *m); +int getvaultchance(map_t *m); +char getvaultchar(vault_t *v, int x, int y); int getthingchance(int habitat); cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand); cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LOFTYPE needlof, enum OBTYPE *dontwantob); @@ -70,6 +76,7 @@ int isnewcellok(cell_t *cell, char *err); int isonmap(map_t *map, int x, int y); int isoutdoors(map_t *m); int iswallindir(cell_t *cell, int dir); +void linkdoors(map_t *m, int roomid, int minx, int miny, int maxx, int maxy); int linkstairs(object_t *o); void makedoor(cell_t *cell); void makelit(cell_t *c, enum LIGHTLEV how, int howlong); @@ -77,3 +84,4 @@ void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong); void setcellknown(cell_t *cell, int forcelev); void setcelltype(cell_t *cell, int id); void updateknowncells(void); +int wallstoleftright(cell_t *c, int dir); diff --git a/move.c b/move.c index be102bd..6843009 100644 --- a/move.c +++ b/move.c @@ -143,6 +143,16 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err // obvious things that you can see if (!onlyifknown || (haslos(lf, cell) && !lfhasflag(lf, F_UNDEAD))) { + // water needing creature out of water? + if (lfhasflag(lf, F_NEEDSWATER)) { + if (!hasobwithflag(cell->obpile, F_DEEPWATER)) { + if (error) { + *error = E_DANGEROUS; + } + return B_TRUE; + } + } + for (o = cell->obpile->first ; o ; o = o->next) { f = hasflag(o->flags, F_DEEPWATER); if (f) { @@ -156,14 +166,6 @@ int celldangerous(lifeform_t *lf, cell_t *cell, int onlyifknown, enum ERROR *err return B_TRUE; } } - } else { - // water needing creature out of water? - if (lfhasflag(lf, F_NEEDSWATER)) { - if (error) { - *error = E_DANGEROUS; - } - return B_TRUE; - } } f = hasflag(o->flags, F_WALKDAM); if (f) { @@ -566,7 +568,6 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc char newlfname[BUFLEN]; int seen; int mightfall = B_TRUE; - cell_t *newcell; lifeform_t *newlf; // calculate chance of falling @@ -600,6 +601,9 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc trymove(lf, dir, B_FALSE); } if (reason != E_OK) { + char buf[BUFLEN]; + cell_t *newcell; + newcell = getcellindir(lf->cell, dir); // failed to move switch (reason) { case E_OFFMAP: @@ -611,8 +615,10 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc } // fall through case E_WALLINWAY: - if (seen) msg("%s slam%s into a wall!",lfname,isplayer(lf) ? "" : "s"); - losehp(lf, rnd(1,6), DT_BASH, pusher, "slamming into a wall"); + if (seen) msg("%s slam%s into a %s!",lfname,isplayer(lf) ? "" : "s", + newcell ? newcell->type->name : "wall"); + sprintf(buf, "slamming into a %s", newcell ? newcell->type->name : "wall"); + losehp(lf, rnd(1,6), DT_BASH, pusher, buf); // stop moving i = howfar; // don't fall @@ -1313,7 +1319,11 @@ int opendoor(lifeform_t *lf, object_t *o) { if (amt < 0) amt = 0; if (lf && isplayer(lf)) { - msg("You yank on the door but it holds fast."); + if (amt > 0) { + msg("The door moves slightly but seems jammed."); + } else { + msg("The door is jammed."); + } } // loosen a bit if (amt) { @@ -1758,10 +1768,14 @@ int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) { // can't teleport on top of something else if (c->lf) { - if (isplayer(lf)) { - msg("You feel a wrenching sensation."); + // go somewhere nearby + c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND); + if (!c) { + if (isplayer(lf)) { + msg("You feel a wrenching sensation."); + } + return B_TRUE; } - return B_TRUE; } if (!isplayer(lf) && cansee(player, lf)) { @@ -1953,25 +1967,31 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { // otherwise fall through... case E_WALLINWAY: if (isplayer(lf)) { - msg("Ouch! You %s into a wall.", getmoveverb(lf)); + msg("Ouch! You %s into a %s.", getmoveverb(lf), + cell ? cell->type->name : "wall"); } else if (cansee(player, lf)) { getlfname(lf, buf); - msg("%s %ss into a wall.", buf, getmoveverb(lf)); + msg("%s %ss into a %s.", buf, getmoveverb(lf), + cell ? cell->type->name : "wall"); } //if (isblind(lf) || !haslos(lf, cell)) { - if (!haslos(lf, cell)) { + if (!cell || !haslos(lf, cell)) { if (isplayer(lf)) { // only take damage if we didn't know about this - if (!cell->known || isdrunk(lf)) { - sprintf(buf, "%sing into a wall", getmoveverb(lf)); + if ((cell && !cell->known) || isdrunk(lf)) { + sprintf(buf, "%sing into a %s", getmoveverb(lf), + cell ? cell->type->name : "wall"); losehp(lf, 1, DT_BASH, NULL, buf); - // we now know there is a wall there. - setcellknown(cell, B_FALSE); + if (cell) { + // we now know there is a wall there. + setcellknown(cell, B_FALSE); + } if (onpurpose) taketime(lf, getmovespeed(lf)); } } else { - sprintf(buf, "%sing into a wall", getmoveverb(lf)); + sprintf(buf, "%sing into a %s", getmoveverb(lf), + cell ? cell->type->name : "wall"); losehp(lf, 1, DT_BASH, NULL, buf); if (onpurpose) taketime(lf, getmovespeed(lf)); } @@ -1997,7 +2017,7 @@ int trymove(lifeform_t *lf, int dir, int onpurpose) { } else { if (cansee(player, lf)) { getlfname(lf, buf); - msg("%s %ss into a wall.", buf, getmoveverb(lf)); + msg("%s %ss into a door.", buf, getmoveverb(lf)); } sprintf(buf, "%sing into a door", getmoveverb(lf)); losehp(lf, 1, DT_BASH, NULL, buf); diff --git a/nexus.c b/nexus.c index f9300f5..4cd9726 100644 --- a/nexus.c +++ b/nexus.c @@ -17,6 +17,7 @@ #include "objects.h" #include "save.h" #include "text.h" +#include "vault.h" material_t *material = NULL,*lastmaterial = NULL; objectclass_t *objectclass = NULL,*lastobjectclass = NULL; @@ -33,6 +34,8 @@ map_t *firstmap = NULL,*lastmap = NULL; knowledge_t *knowledge = NULL, *lastknowledge = NULL; hiddenname_t *firsthiddenname = NULL, *lasthiddenname = NULL; +extern vault_t *firstvault; + glyph_t playerglyph,tempglyph; double startticks,lastticks; @@ -86,6 +89,7 @@ int main(int argc, char **argv) { FILE *playerfile = NULL; int x,y; cell_t *c; + vault_t *v; atexit(cleanup); @@ -117,11 +121,11 @@ int main(int argc, char **argv) { // init graphics initgfx(); - // if no maps, make the initial level - if (!firstmap) { - newworld = B_TRUE; - addmap(); - createmap(firstmap, 1, RG_FIRSTDUNGEON, H_DUNGEON, NULL, D_NONE); + // warn about vault problems. + for (v = firstvault ; v ; v = v->next) { + if (!v->valid) { + msg("warning: invalid vaultfile '%s'",v->filename); more(); + } } if (!knowledge) { @@ -169,6 +173,13 @@ int main(int argc, char **argv) { } } + // if no maps (ie. ALWAYS now that maps aren't persistent), + // make the initial level + if (!firstmap) { + newworld = B_TRUE; + addmap(); + createmap(firstmap, 1, RG_FIRSTDUNGEON, H_DUNGEON, NULL, D_NONE); + } // find staircase where = findobinmap(firstmap, OT_STAIRSUP); @@ -740,6 +751,8 @@ int init(void) { // cell types addcelltype(CT_WALL, "rock wall", '#', C_GREY, B_SOLID, B_OPAQUE, MT_STONE); + addcelltype(CT_WALLGLASS, "glass wall", '#', C_CYAN, B_SOLID, B_TRANS, MT_GLASS); + addcelltype(CT_WALLMETAL, "metal wall", '#', C_WHITE, B_SOLID, B_OPAQUE, MT_METAL); addcelltype(CT_ROOMWALL, "rock wall", '#', C_GREY, B_SOLID, B_OPAQUE, MT_STONE); addcelltype(CT_CORRIDOR, "rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE); addcelltype(CT_LOOPCORRIDOR, "rock floor", 'L', C_GREY, B_EMPTY, B_TRANS, MT_STONE); @@ -759,6 +772,9 @@ int init(void) { logfile = fopen("log.txt","wt"); fprintf(logfile, "\n\n\n====== NEW LOGFILE ====\n"); + // load in vaults + loadvaults(); + return B_FALSE; } diff --git a/objects.c b/objects.c index d2d6f85..e32b26e 100644 --- a/objects.c +++ b/objects.c @@ -73,6 +73,7 @@ enum OBCLASS sortorder[] = { OC_ROCK, OC_FLORA, OC_DFEATURE, + OC_TRAP, OC_MISC, // omitting OC_SPELL and OC_JUMP because it shouldn't ever be displayed OC_NULL @@ -370,6 +371,7 @@ objectclass_t *addoc(enum OBCLASS id, char *name, char *desc, char glyph, int gl // props a->id = id; a->name = strdup(name); + a->nnouns = 0; a->desc = strdup(desc); a->glyph.ch = glyph; a->glyph.colour = glyphcolour; @@ -378,6 +380,12 @@ objectclass_t *addoc(enum OBCLASS id, char *name, char *desc, char glyph, int gl return a; } +void addocnoun(objectclass_t *oc, char *text) { + assert (oc->nnouns < MAXOCNOUNS); + oc->noun[oc->nnouns] = strdup(text); + oc->nnouns++; +} + // create a new object, stacking ok @@ -402,17 +410,27 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { char *localname; int wantblessed = B_UNCURSED; race_t *corpserace = NULL; + int dorandombrand = B_FALSE; + brand_t *wantbrand = NULL; brand_t *br; obmod_t *om; obmod_t *wantom[MAXOBMODS]; int nom = 0; int n; int bookcontents = -1; + int wantrarity = RR_NONE; + int wantgoodness = G_NA; + int donesomething; + object_t *addedob[MAXPILEOBS]; + int nadded = 0; + // for doors + enum FLAG doorflag[5]; + int ndoorflags = 0; + + addedob[0] = NULL; // just in case we don't add any - localname = strdup(name); - // check for premods. eg. "flaming xxx" "frozen xxx" etc for (om = firstobmod ; om ; om = om->next) { if (db) dblog("DB: checking for '%s' in '%s'",om->prefix, localname); @@ -431,6 +449,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { if (where->owner && hasflag(where->owner->flags, F_NOPACK)) { if (db) dblog("error giving ob '%s' - owner isn't allowed to carry objects!", name); + nretobs = 0; return NULL; } @@ -484,24 +503,95 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { } else { howmany = 1; } - - // new objects after the game starts have unknown - // blessed/cursed status, so we can never stack them -// if (gamestarted && !hasflag(player->flags, F_DETECTAURAS)) { - if (strstr(p, "blessed") || strstr(p, "holy water")) { + + + // handle prefixes. strip them off as we go. + donesomething = B_TRUE; + while (donesomething) { + donesomething = B_FALSE; + if (strstarts(p, "blessed ")) { + if (db) dblog("DB: ob is blessed (%s)",p); + wantblessed = B_BLESSED; + p += strlen("blessed "); + donesomething = B_TRUE; + } else if (strstarts(p, "uncursed ")) { + if (db) dblog("DB: ob is uncursed (%s)",p); + wantblessed = B_UNCURSED; + p += strlen("uncursed "); + donesomething = B_TRUE; + } else if (strstarts(p, "cursed ")) { + if (db) dblog("DB: ob is cursed (%s)",p); + wantblessed = B_CURSED; + p += strlen("cursed "); + donesomething = B_TRUE; + // door flags + } else if (strstarts(p, "locked ")) { + doorflag[ndoorflags++] = F_LOCKED; + p += strlen("locked "); + donesomething = B_TRUE; + } else if (strstarts(p, "jammed ")) { + doorflag[ndoorflags++] = F_JAMMED; + p += strlen("jammed "); + donesomething = B_TRUE; + } else if (strstarts(p, "secret ")) { + doorflag[ndoorflags++] = F_SECRET; + p += strlen("secret "); + donesomething = B_TRUE; + // rarity + } else if (strstarts(p, "common ")) { + wantrarity = RR_COMMON; + p += strlen("common "); + donesomething = B_TRUE; + } else if (strstarts(p, "uncommon ")) { + wantrarity = RR_UNCOMMON; + p += strlen("uncommon "); + donesomething = B_TRUE; + } else if (strstarts(p, "rare ")) { + wantrarity = RR_RARE; + p += strlen("rare "); + donesomething = B_TRUE; + } else if (strstarts(p, "very rare ")) { + wantrarity = RR_VERYRARE; + p += strlen("very rare "); + donesomething = B_TRUE; + // weapon "goodness" + } else if (strstarts(p, "average ")) { + wantgoodness = G_AVERAGE; + p += strlen("average "); + donesomething = B_TRUE; + } else if (strstarts(p, "good ")) { + wantgoodness = G_GOOD; + if (onein(4)) wantblessed = B_BLESSED; + p += strlen("good "); + donesomething = B_TRUE; + } else if (strstarts(p, "great ")) { + wantgoodness = G_GREAT; + if (onein(3)) wantblessed = B_BLESSED; + if (onein(10)) dorandombrand = B_TRUE; + p += strlen("great "); + donesomething = B_TRUE; + } else if (strstarts(p, "excellent ")) { + wantgoodness = G_EXCELLENT; + if (onein(2)) wantblessed = B_BLESSED; + if (onein(4)) dorandombrand = B_TRUE; + p += strlen("excellent "); + donesomething = B_TRUE; + // brands + } else if (strstarts(p, "branded ")) { + dorandombrand = B_TRUE; + p += strlen("branded "); + donesomething = B_TRUE; + } + } + + if (strstr(p, "holy water") || strstr(p, "incompetence")) { if (db) dblog("DB: ob is blessed (%s)",p); wantblessed = B_BLESSED; - //canstack = B_FALSE; - } else if (strstr(p, "uncursed")) { - if (db) dblog("DB: ob is uncursed (%s)",p); - wantblessed = B_UNCURSED; - } else if (strstr(p, "cursed") || strstr(p, "incompetence")) { - if (db) dblog("DB: ob is cursed (%s)",p); - wantblessed = B_CURSED; - //canstack = B_FALSE; } -// } + //////////////////////////////////// + // handle special object names + //////////////////////////////////// if (strstr(p, "corpse")) { int len; char racename[BUFLEN]; @@ -556,18 +646,54 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { } } ot = findot(OT_MANUAL); + //////////////////////////////////// + // also handle generic names + //////////////////////////////////// } else { - // make sure we can find the requested object type - if (db) dblog("DB: Looking for object name '%s'", p ); - ot = findotn(p); - if (!ot) { - //if (gamestarted) msg("DB: No match for object name '%s'", p ); - if (db) dblog("DB: No match for object name '%s'", p ); - return NULL; + objectclass_t *oc; + char tempname[BUFLEN]; + int found = B_FALSE; + + // check for things like "weapon" or "random ring" + for (oc = objectclass; oc ; oc = oc->next) { + int i; + int matched = B_FALSE; + for (i = 0; i < oc->nnouns; i++) { + sprintf(tempname, "random %s", oc->noun[i]); + if (strstarts(p, tempname) || streq(p, oc->noun[i])) { + matched = B_TRUE; + } + + if (matched) { + int minrarity,maxrarity; + // want a specific rarity? + rrtorarity(wantrarity, &minrarity, &maxrarity); + + ot = getrandomobofclass(oc->id, minrarity, maxrarity); + if (ot) { + found = B_TRUE; + break; + } + } + } + if (found) break; + } + + if (!found) { + // look up the object name + if (db) dblog("DB: Looking for object name '%s'", p ); + ot = findotn(p); + if (!ot) { + //if (gamestarted) msg("DB: No match for object name '%s'", p ); + if (db) dblog("DB: No match for object name '%s'", p ); + return NULL; + } } } if (db) dblog("DB: FOUND: ot->name = '%s'", ot->name ); + // we now have the objecttype. + // override blessed status from flags... f = hasflag(ot->flags, F_STARTBLESSED); if (f) { @@ -576,11 +702,13 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { // don't give nopickup objects to lifeforms if (hasflag(ot->flags, F_NOPICKUP) && where->owner) { + nretobs = 0; return NULL; } if (ot->obclass->id == OC_SPELL) { if (db) dblog("DB: Cannot give a spell object! object name '%s'", p ); + nretobs = 0; return NULL; } @@ -595,6 +723,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { // does this unique ob already exist? if (obexists(ot->id)) { if (db) dblog("DB: Unique ob %s already exists!", p ); + nretobs = 0; return NULL; } @@ -615,10 +744,22 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { // does the pile already contain one? existob = canstacknewot(where, ot); if (existob) { + int n,found = B_FALSE; if (db) dblog("DB: STACK FOUND (%d x %s). Adding %d obs to existing stack.",existob->amt, existob->type->name, howmany); existob->amt++; added = B_TRUE; o = existob; + + // if this isn't already in our list of added obs, add it + for (n = 0; n < nadded; n++) { + if (addedob[n] == o) { + found = B_TRUE; + break; + } + } + if (!found) { + addedob[nadded++] = o; + } } else { if (db) dblog("DB: No stacks found."); } @@ -715,6 +856,9 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { setblessed(o, wantblessed); } o->blessknown = B_FALSE; + + addedob[nadded++] = o; + if (canstack) { // stop looping through break; @@ -722,422 +866,517 @@ object_t *addobject(obpile_t *where, char *name, int canstack) { } } - // fill in book types - if (o && (o->type->obclass->id == OC_BOOK)) { - hiddenname_t *hn,*selhn = NULL; - int numhiddennames; - int n,sel; - if (bookcontents == -1) { - if (o->type->id == OT_SPELLBOOK) { - bookcontents = getrandomspell(); - while (!schoolappearsinbooks(getspellschool(bookcontents))) { + /* + need to do the below for _all_ objects added! + */ + + for (i = 0; i < nadded; i++) { + o = addedob[i]; + + // fill in door flags + if (ndoorflags && isdoor(o, NULL)) { + int n; + for (n = 0; n < ndoorflags; n++) { + cell_t *c; + int val[3]; + c = getoblocation(o); + // fill in flag vals + switch (doorflag[n]) { + case F_LOCKED: + val[0] = B_TRUE; + val[1] = getdoorlockdiff(c->map->depth); + val[2] = NA; + break; + case F_JAMMED: + val[0] = rnd(1,c->map->depth+3); + val[1] = NA; + val[2] = NA; + break; + case F_SECRET: + val[0] = getdoorsecretdiff(c->map->depth); + val[1] = NA; + val[2] = NA; + break; + default: break; + } + addflag(o->flags, doorflag[n], val[0], val[1], val[2], NULL); + } + } + + // fill in book types + if (o && (o->type->obclass->id == OC_BOOK)) { + hiddenname_t *hn,*selhn = NULL; + int numhiddennames; + int n,sel; + if (bookcontents == -1) { + if (o->type->id == OT_SPELLBOOK) { bookcontents = getrandomspell(); - } - } else { // ie. manual - bookcontents = getrandomskill(); - } - } - - // link - if (o->type->id == OT_SPELLBOOK) { - addflag(o->flags, F_LINKSPELL, bookcontents, NA, NA, NULL); - } else { - addflag(o->flags, F_MANUALOF, bookcontents, NA, NA, NULL); - } - - - // count hidden names - numhiddennames = 0; - for (hn = firsthiddenname ; hn ; hn = hn->next) { - if (hn->obclass == o->type->obclass->id) { - numhiddennames++; - } - } - // assign hidden name - sel = rnd(0,numhiddennames-1); - n = 0; - for (hn = firsthiddenname ; hn ; hn = hn->next) { - if (hn->obclass == o->type->obclass->id) { - if (n == sel) { - selhn = hn; - break; - } - n++; - } - } - addflag(o->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, selhn->text); - } - - - // other special changes we need to make based on what was - // asked for - if ((gamemode != GM_LOADING) && o) { - char *p2; - int bonus = 0; - - // corpses - fill in details - if (o->type->id == OT_CORPSE) { - flag_t *rf, *cf; - - if (!corpserace) { - // random one. - corpserace = getrandomrace(NULL, 0); - } - - o->weight = corpserace->weight; - o->material = corpserace->material; - - // remember the race type - addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL); - - // override ot_corpse nutrition flag based on race's size - rf = hasflag(corpserace->flags, F_SIZE); - if (rf) { - cf = hasflag(o->flags, F_EDIBLE); - if (cf) { - switch (rf->val[0]) { - case SZ_MINI: - cf->val[1] = 1; - break; - case SZ_TINY: - cf->val[1] = 10; - break; - case SZ_SMALL: - cf->val[1] = 25; - break; - case SZ_MEDIUM: - cf->val[1] = 75; - break; - case SZ_HUMAN: - cf->val[1] = 120; - break; - case SZ_LARGE: - cf->val[1] = 150; - break; - case SZ_HUGE: - cf->val[1] = 200; - break; - case SZ_ENORMOUS: - cf->val[1] = 250; - break; + while (!schoolappearsinbooks(getspellschool(bookcontents))) { + bookcontents = getrandomspell(); } + } else { // ie. manual + bookcontents = getrandomskill(); } - } - } else if (o->type->id == OT_STATUE) { - flag_t *f, *rf; - float ratio; + } - if (!corpserace) { - cell_t *where; - where = getoblocation(o); - // select random race - if (where) { - corpserace = getrandomrace(where, 0); - } else { - // ie. vending machine + // link + if (o->type->id == OT_SPELLBOOK) { + addflag(o->flags, F_LINKSPELL, bookcontents, NA, NA, NULL); + } else { + addflag(o->flags, F_MANUALOF, bookcontents, NA, NA, NULL); + } + + + // count hidden names + numhiddennames = 0; + for (hn = firsthiddenname ; hn ; hn = hn->next) { + if (hn->obclass == o->type->obclass->id) { + numhiddennames++; + } + } + // assign hidden name + sel = rnd(0,numhiddennames-1); + n = 0; + for (hn = firsthiddenname ; hn ; hn = hn->next) { + if (hn->obclass == o->type->obclass->id) { + if (n == sel) { + selhn = hn; + break; + } + n++; + } + } + addflag(o->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, selhn->text); + } + + + // other special changes we need to make based on what was + // asked for + if ((gamemode != GM_LOADING) && o) { + char *p2; + int bonus = 0; + + // corpses - fill in details + if (o->type->id == OT_CORPSE) { + flag_t *rf, *cf; + + if (!corpserace) { + // random one. corpserace = getrandomrace(NULL, 0); } - } - - ratio = o->material->weightrating / corpserace->material->weightrating; + + o->weight = corpserace->weight; + o->material = corpserace->material; - o->weight = corpserace->weight * ratio; + // remember the race type + addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL); - // remember the race type - addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL); - - // set impassable size - f = hasflag(o->flags, F_IMPASSABLE); - if (f) { + // override ot_corpse nutrition flag based on race's size rf = hasflag(corpserace->flags, F_SIZE); if (rf) { - f->val[0] = rf->val[0]; - } else { - killflag(f); - } - } + cf = hasflag(o->flags, F_EDIBLE); + if (cf) { + switch (rf->val[0]) { + case SZ_MINI: + cf->val[1] = 1; + break; + case SZ_TINY: + cf->val[1] = 10; + break; + case SZ_SMALL: + cf->val[1] = 25; + break; + case SZ_MEDIUM: + cf->val[1] = 75; + break; + case SZ_HUMAN: + cf->val[1] = 120; + break; + case SZ_LARGE: + cf->val[1] = 150; + break; + case SZ_HUGE: + cf->val[1] = 200; + break; + case SZ_ENORMOUS: + cf->val[1] = 250; + break; + } + } + } + } else if (o->type->id == OT_STATUE) { + flag_t *f, *rf; + float ratio; - // set ob hp - f = hasflag(o->flags, F_OBHP); - if (f) { - rf = hasflag(corpserace->flags, F_HITDICE); + if (!corpserace) { + cell_t *where; + where = getoblocation(o); + // select random race + if (where) { + corpserace = getrandomrace(where, 0); + } else { + // ie. vending machine + corpserace = getrandomrace(NULL, 0); + } + } + + ratio = o->material->weightrating / corpserace->material->weightrating; + + o->weight = corpserace->weight * ratio; + + // remember the race type + addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL); + + // set impassable size + f = hasflag(o->flags, F_IMPASSABLE); + if (f) { + rf = hasflag(corpserace->flags, F_SIZE); + if (rf) { + f->val[0] = rf->val[0]; + } else { + killflag(f); + } + } + + // set ob hp + f = hasflag(o->flags, F_OBHP); + if (f) { + rf = hasflag(corpserace->flags, F_HITDICE); + if (rf) { + int maxhp; + maxhp = (rf->val[0] * 4) + rf->val[1]; + f->val[0] = maxhp; + f->val[1] = maxhp; + } + } + + } else if (o->type->id == OT_HEAD) { + flag_t *rf, *cf; + + assert(corpserace); + + o->weight = corpserace->weight; + o->material = corpserace->material; + + // remember the race type + addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL); + + // override ot_corpse nutrition flag based on race's size + rf = hasflag(corpserace->flags, F_SIZE); if (rf) { - int maxhp; - maxhp = (rf->val[0] * 4) + rf->val[1]; - f->val[0] = maxhp; - f->val[1] = maxhp; - } + cf = hasflag(o->flags, F_EDIBLE); + if (cf) { + switch (rf->val[0]) { + case SZ_MINI: + cf->val[1] = 1; + break; + case SZ_TINY: + cf->val[1] = 2; + break; + case SZ_SMALL: + cf->val[1] = 5; + break; + case SZ_MEDIUM: + cf->val[1] = 25; + break; + case SZ_HUMAN: + cf->val[1] = 50; + break; + case SZ_LARGE: + cf->val[1] = 75; + break; + case SZ_HUGE: + cf->val[1] = 100; + break; + case SZ_ENORMOUS: + cf->val[1] = 150; + break; + } + } + } + } - } else if (o->type->id == OT_HEAD) { - flag_t *rf, *cf; - - assert(corpserace); - - o->weight = corpserace->weight; - o->material = corpserace->material; - - // remember the race type - addflag(o->flags, F_CORPSEOF, corpserace->id, NA, NA, NULL); - - // override ot_corpse nutrition flag based on race's size - rf = hasflag(corpserace->flags, F_SIZE); - if (rf) { - cf = hasflag(o->flags, F_EDIBLE); - if (cf) { - switch (rf->val[0]) { - case SZ_MINI: - cf->val[1] = 1; - break; - case SZ_TINY: - cf->val[1] = 2; - break; - case SZ_SMALL: - cf->val[1] = 5; - break; - case SZ_MEDIUM: - cf->val[1] = 25; - break; - case SZ_HUMAN: - cf->val[1] = 50; - break; - case SZ_LARGE: - cf->val[1] = 75; - break; - case SZ_HUGE: - cf->val[1] = 100; - break; - case SZ_ENORMOUS: - cf->val[1] = 150; - break; - } - } - } - - } - - for (n = 0; n < nom; n++) { - // add flags from obmod - applyobmod(o, wantom[n]); - // other effects... - switch (wantom[n]->id) { - case OM_FLAMING: // flaming weapons are immune to fire - if (o->type->obclass->id == OC_WEAPON) { - if (!isimmuneto(o->flags, DT_FIRE)) { - addflag(o->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); - } + // chance of masterwork based on wantgoodness + switch (wantgoodness) { + case G_GREAT: + if (onein(6)) { + wantom[nom++] = findobmod(OM_MASTERWORK); } break; - case OM_FROZEN: - if (o->material->id != MT_FIRE) { // fire can't be frozen! - // made of ice - // note: not using changemat() here to avoid adding f_frozen twice. - o->material = findmaterial(MT_ICE); + case G_EXCELLENT: + if (onein(3)) { + wantom[nom++] = findobmod(OM_MASTERWORK); } break; - case OM_MASTERWORK: - if (isweapon(o) || isarmour(o)) { - flag_t *f; - f = hasflag(o->flags, F_OBHP); - if (f) { - f->val[0] *= 2; - f->val[1] *= 2; - } - } - if (isweapon(o)) { - flag_t *f; - // always does near high end of damage range - f = hasflag(o->flags, F_DAM); - if (f) { - int ndice,dsides,bonus; - texttodice(f->text, &ndice,&dsides,&bonus); - // half the number of sides on the dice (dsides). - dsides /= 2; if (dsides < 1) dsides = 1; - // increase modifier by (ndice * dsides) - bonus += (ndice * dsides); - if (bonus <= 0) { - bonus = NA; + } + + for (n = 0; n < nom; n++) { + // add flags from obmod + applyobmod(o, wantom[n]); + // other effects... + switch (wantom[n]->id) { + case OM_FLAMING: // flaming weapons are immune to fire + if (o->type->obclass->id == OC_WEAPON) { + if (!isimmuneto(o->flags, DT_FIRE)) { + addflag(o->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); } - dicetotext(ndice,dsides,bonus,NULL,NULL,f->text, NULL); } - } - break; - case OM_SHODDY: - if (isweapon(o) || isarmour(o)) { - flag_t *f; - f = hasflag(o->flags, F_OBHP); - if (f) { - f->val[0] /= 2; if (f->val[0] < 1) f->val[0] = 1; - f->val[1] /= 2; if (f->val[1] < 1) f->val[1] = 1; - + break; + case OM_FROZEN: + if (o->material->id != MT_FIRE) { // fire can't be frozen! + // made of ice + // note: not using changemat() here to avoid adding f_frozen twice. + o->material = findmaterial(MT_ICE); } - } - if (isweapon(o)) { - flag_t *f; - // half the damage - f = hasflag(o->flags, F_DAM); - if (f) { - int ndice, dsides,bonus; - texttodice(f->text, &ndice, &dsides, &bonus); - // half the number of sides on the dice (dsides). - dsides /= 2; - // half modifier - if (bonus > 0) { - bonus /= 2; if (bonus <= 0) bonus = NA; + break; + case OM_MASTERWORK: + if (isweapon(o) || isarmour(o)) { + flag_t *f; + f = hasflag(o->flags, F_OBHP); + if (f) { + f->val[0] *= 2; + f->val[1] *= 2; } - dicetotext(ndice,dsides,bonus,NULL,NULL,f->text, NULL); } - } - break; + if (isweapon(o)) { + flag_t *f; + // always does near high end of damage range + f = hasflag(o->flags, F_DAM); + if (f) { + int ndice,dsides,bonus; + texttodice(f->text, &ndice,&dsides,&bonus); + // half the number of sides on the dice (dsides). + dsides /= 2; if (dsides < 1) dsides = 1; + // increase modifier by (ndice * dsides) + bonus += (ndice * dsides); + if (bonus <= 0) { + bonus = NA; + } + dicetotext(ndice,dsides,bonus,NULL,NULL,f->text, NULL); + } + } + break; + case OM_SHODDY: + if (isweapon(o) || isarmour(o)) { + flag_t *f; + f = hasflag(o->flags, F_OBHP); + if (f) { + f->val[0] /= 2; if (f->val[0] < 1) f->val[0] = 1; + f->val[1] /= 2; if (f->val[1] < 1) f->val[1] = 1; - default: - break; - } - } + } + } + if (isweapon(o)) { + flag_t *f; + // half the damage + f = hasflag(o->flags, F_DAM); + if (f) { + int ndice, dsides,bonus; + texttodice(f->text, &ndice, &dsides, &bonus); + // half the number of sides on the dice (dsides). + dsides /= 2; + // half modifier + if (bonus > 0) { + bonus /= 2; if (bonus <= 0) bonus = NA; + } + dicetotext(ndice,dsides,bonus,NULL,NULL,f->text, NULL); + } + } + break; - - // check for bonuses. eg. "+1" - p2 = strchr(p, '+'); - if (p2) { - char *p3; - char numbuf[BUFLENSMALL]; - p3 = numbuf; - - p2++; - - while (isdigit(*p2)) { - *p3 = *p2; - p2++; - p3++; - } - *p3 = '\0'; - - bonus += atoi(numbuf); - } - // check for penalties. eg. "-1" - p2 = strchr(p, '-'); - if (p2) { - char *p3; - char numbuf[BUFLENSMALL]; - p3 = numbuf; - - p2++; - - while (isdigit(*p2)) { - *p3 = *p2; - p2++; - p3++; - } - *p3 = '\0'; - - bonus -= atoi(numbuf); - } - - if (bonus) { - if (hasflag(o->flags, F_ENCHANTABLE)) { - // for swords, armour etc - addflag_real(o->flags, F_BONUS, bonus, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); - } - } - - // special rings which get randomized... - if (o->type->id == OT_RING_CON) { - flag_t *f; - f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_CON, NA, NULL); - if (f) { - if (bonus) f->val[2] = bonus; - else f->val[2] = rnd(1,3); - } - if (o->blessed == B_CURSED) f->val[2] *= -1; - } else if (o->type->id == OT_RING_DEX) { - flag_t *f; - f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_DEX, NA, NULL); - if (f) { - if (bonus) f->val[2] = bonus; - else f->val[2] = rnd(1,3); - } - if (o->blessed == B_CURSED) f->val[2] *= -1; - } else if (o->type->id == OT_RING_IQ) { - flag_t *f; - f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_IQ, NA, NULL); - if (f) { - if (bonus) f->val[2] = bonus; - else f->val[2] = rnd(1,3); - } - if (o->blessed == B_CURSED) f->val[2] *= -1; - } else if (o->type->id == OT_RING_STR) { - flag_t *f; - f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, NA, NULL); - if (f) { - if (bonus) f->val[2] = bonus; - else f->val[2] = rnd(1,3); - } - - if (o->blessed == B_CURSED) f->val[2] *= -1; - } - - // check for postmods. eg. "xxx of pyromania" - for (br = firstbrand ; br ; br = br->next) { - if (strstr(name, br->suffix)) { - // does this brand apply to this objecttype? - if (brandappliesto(br, o->type)) { - copyflags(o->flags, br->flags, FROMBRAND); - addflag(o->flags, F_HASBRAND, br->id, NA, NA, NULL); - } else { - // doesn't exist! - return NULL; + default: + break; } } + + + // check for bonuses. eg. "+1" + p2 = strchr(p, '+'); + if (p2) { + char *p3; + char numbuf[BUFLENSMALL]; + p3 = numbuf; + + p2++; + + while (isdigit(*p2)) { + *p3 = *p2; + p2++; + p3++; + } + *p3 = '\0'; + + bonus += atoi(numbuf); + } + // check for penalties. eg. "-1" + p2 = strchr(p, '-'); + if (p2) { + char *p3; + char numbuf[BUFLENSMALL]; + p3 = numbuf; + + p2++; + + while (isdigit(*p2)) { + *p3 = *p2; + p2++; + p3++; + } + *p3 = '\0'; + + bonus -= atoi(numbuf); + } + + // if no bonus yet, get one based on 'wantgoodness' + if (!bonus) { + switch (wantgoodness) { + case G_GOOD: // 1 - 2 + bonus = 1; + if (onein(2)) bonus++; + break; + case G_GREAT: // 1 - 3 + bonus = 1; + while (onein(2) && (bonus < 3)) { + bonus++; + } + break; + case G_EXCELLENT: // 1 - 4 + bonus = 1; + while (onein(2) && (bonus < 4)) { + bonus++; + } + break; + default: // no bonus + break; + } + } + + if (bonus) { + if (hasflag(o->flags, F_ENCHANTABLE)) { + // for swords, armour etc + addflag_real(o->flags, F_BONUS, bonus, NA, NA, NULL, PERMENANT, B_UNKNOWN, -1); + } + } + + // special rings which get randomized... + if (o->type->id == OT_RING_CON) { + flag_t *f; + f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_CON, NA, NULL); + if (f) { + if (bonus) f->val[2] = bonus; + else f->val[2] = rnd(1,3); + } + if (o->blessed == B_CURSED) f->val[2] *= -1; + } else if (o->type->id == OT_RING_DEX) { + flag_t *f; + f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_DEX, NA, NULL); + if (f) { + if (bonus) f->val[2] = bonus; + else f->val[2] = rnd(1,3); + } + if (o->blessed == B_CURSED) f->val[2] *= -1; + } else if (o->type->id == OT_RING_IQ) { + flag_t *f; + f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_IQ, NA, NULL); + if (f) { + if (bonus) f->val[2] = bonus; + else f->val[2] = rnd(1,3); + } + if (o->blessed == B_CURSED) f->val[2] *= -1; + } else if (o->type->id == OT_RING_STR) { + flag_t *f; + f = hasflagval(o->flags, F_EQUIPCONFER, F_ATTRMOD, A_STR, NA, NULL); + if (f) { + if (bonus) f->val[2] = bonus; + else f->val[2] = rnd(1,3); + } + + if (o->blessed == B_CURSED) f->val[2] *= -1; + } + + // now apply a random brand if we wanted one + if (dorandombrand) { + wantbrand = getrandombrandfor(ot); + } + + // check for specific brands. eg. "xxx of pyromania" + // NOTE: this will override any random brands from "branded" + for (br = firstbrand ; br ; br = br->next) { + if (strstr(name, br->suffix)) { + // does this brand apply to this objecttype? + if (brandappliesto(br, o->type)) { + wantbrand = br; + break; + } + } + } + + if (wantbrand) { + if (brandappliesto(wantbrand, o->type)) { + copyflags(o->flags, wantbrand->flags, FROMBRAND); + addflag(o->flags, F_HASBRAND, wantbrand->id, NA, NA, NULL); + } + } + } - } + if (where->owner) { + // new owner gains "hold confer" flags conferred by this object + giveobflags(where->owner, o, F_HOLDCONFER); + } + // special cases + if (o->type->id == OT_VENDINGMACHINE) { + char buf[BUFLEN]; + cell_t *loc; + loc = getoblocation(o); + // populate with objects + for (i = 0; i < 10; i++) { + objecttype_t *ot2; + strcpy(buf, ""); + while (!strcmp(buf, "")) { + real_getrandomob(loc->map, buf, RO_NONE, NA, loc->map->depth + rnd(10,15)); + // replace "1 potion" with "a potion" + if (strstr(buf, "1 ") == buf) { + char temp[BUFLEN]; + strcpy(temp, buf); + sprintf(buf, "a %s",temp + 2); + } + // make sure you can hold it + ot2 = findotn(buf); + if (!ot2 || + hasflag(ot2->flags, F_NOPICKUP) || + hasflag(ot2->flags, F_IMPASSABLE) ) { + strcpy(buf, ""); + } + } + addflag(o->flags, F_CONTAINSOB, 'a' + i, NA, NA, buf); + } + } + + if (gamemode == GM_GAMESTARTED) { + if (o && where->where && !hasflag(o->flags, F_NOGLYPH) && haslos(player, where->where) ) { + needredraw = B_TRUE; + } + } + } // end foreach added object + + // don't need the name anymore. free(localname); - if (where->owner) { - // new owner gains "hold confer" flags conferred by this object - giveobflags(where->owner, o, F_HOLDCONFER); - } - - // special cases - if (o->type->id == OT_VENDINGMACHINE) { - char buf[BUFLEN]; - cell_t *loc; - loc = getoblocation(o); - // populate with objects - for (i = 0; i < 10; i++) { - objecttype_t *ot2; - strcpy(buf, ""); - while (!strcmp(buf, "")) { - real_getrandomob(loc->map, buf, RO_NONE, NA, loc->map->depth + rnd(10,15)); - // replace "1 potion" with "a potion" - if (strstr(buf, "1 ") == buf) { - char temp[BUFLEN]; - strcpy(temp, buf); - sprintf(buf, "a %s",temp + 2); - } - // make sure you can hold it - ot2 = findotn(buf); - if (!ot2 || - hasflag(ot2->flags, F_NOPICKUP) || - hasflag(ot2->flags, F_IMPASSABLE) ) { - strcpy(buf, ""); - } - } - - addflag(o->flags, F_CONTAINSOB, 'a' + i, NA, NA, buf); + // populate retobs + for (i = 0; i < nadded; i++) { + if (addedob[i]) { + retobs[i] = addedob[i]; + retobscount[i] = addedob[i]->amt; } } + nretobs = nadded; - if (gamemode == GM_GAMESTARTED) { - if (o && where->where && !hasflag(o->flags, F_NOGLYPH) && haslos(player, where->where) ) { - needredraw = B_TRUE; - } - } - - // return the last object given - return o; + // return the first object given + return addedob[0]; } @@ -1225,7 +1464,6 @@ obpile_t *addobpile(lifeform_t *owner, cell_t *where) { return op; } - void addobsinradius(cell_t *centre, int radius, int dirtype, char *name, int allowdupes) { int x,y; objecttype_t *ot; @@ -2082,6 +2320,29 @@ void damageallobs(object_t *srcob, obpile_t *op, int howmuch, int damtype) { } } +void dumpobrarity(void) { + enum RARITY rr; + objecttype_t *ot; + flag_t *f; + int min,max; + + for (rr = RR_COMMON; rr <= RR_VERYRARE; rr++) { + rrtorarity(rr, &min, &max); + dblog("Obs with rarity %s:", getrarityname(rr)); + for (ot = objecttype ; ot ; ot = ot->next) { + if (ot->obclass->id == OC_ARMOUR) { + int thisrar; + f = hasflag(ot->flags, F_RARITY); + if (!f) continue; + thisrar = f->val[1]; + + if ((thisrar >= min) && (thisrar <= max)) { + dblog("\t%s", ot->name); + } + } + } + } +} void explodeob(object_t *o, flag_t *f, int bigness) { cell_t *c; @@ -2672,15 +2933,15 @@ int getobvalue(object_t *o) { } // one-off magical effects (linkspell) - use spell price if (f->id == F_LINKSPELL) { - // spelllevel^2 * 20 - price += (pow(getspelllevel(f->val[0]), 2) * 20); - // ...and spellbooks then cost triple if (o->type->obclass->id == OC_BOOK) { - price *= 3; + price += (pow(getspelllevel(f->val[0]), 2) * 30); } else if (o->type->obclass->id == OC_SCROLL) { // do nothing + price += (pow(getspelllevel(f->val[0]), 2) * 2); + } else if (o->type->obclass->id == OC_POTION) { + price += (pow(getspelllevel(f->val[0]), 2) * 15); } else if (o->type->obclass->id == OC_WAND) { - price *= 2.25; + price += (pow(getspelllevel(f->val[0]), 2) * 22); } } else if (f->id == F_MANUALOF) { price *= 124; @@ -2756,6 +3017,8 @@ int getobvalue(object_t *o) { // minimum if (price < 1) price = 1; + + price = ((int)price * o->amt); return (int) price; } @@ -2828,6 +3091,72 @@ object_t *getrandomammo(lifeform_t *lf) { return NULL; } +brand_t *getrandombrandfor(objecttype_t *ot) { + brand_t *br, **poss; + brand_t *result = NULL; + int numbr; + int nposs = 0; + + // count number of brands + numbr = 0; + for (br = firstbrand ; br ; br = br->next) { + numbr++; + } + poss = malloc(numbr * sizeof(brand_t *)); + + for (br = firstbrand ; br ; br = br->next) { + if (brandappliesto(br, ot)) { + poss[nposs] = br; + nposs++; + } + } + + if (nposs > 0) { + result = poss[rnd(0,nposs-1)]; + } + free(poss); + return result; +} + +objecttype_t *getrandomobofclass(enum OBCLASS ocid, int minrarity, int maxrarity) { + objecttype_t *ot; + int count = 0,sel,n; + flag_t *f; + + for (ot = objecttype ; ot ; ot = ot->next) { + if ((ot->obclass->id == ocid) && !hasflag(ot->flags, F_UNIQUE)) { + f = hasflag(ot->flags, F_RARITY); + if (f) { + if ( ((minrarity == NA) || (f->val[1] >= minrarity)) && + ((maxrarity == NA) || (f->val[1] <= maxrarity)) ) { + count++; + } + } + } + } + if (count <= 0) { + return NULL; + } + sel = rnd(1,count); + n = 0; + for (ot = objecttype ; ot ; ot = ot->next) { + if ((ot->obclass->id == ocid) && !hasflag(ot->flags, F_UNIQUE)) { + f = hasflag(ot->flags, F_RARITY); + if (f) { + if ( ((minrarity == NA) || (f->val[1] >= minrarity)) && + ((maxrarity == NA) || (f->val[1] <= maxrarity)) ) { + n++; + if (n == sel) { + return ot; + } + } + } + } + } + + return NULL; +} + char *getdamname(enum DAMTYPE damtype) { switch (damtype) { case DT_ALL: return "all damage"; @@ -4041,29 +4370,9 @@ char *real_getrandomob(map_t *map, char *buf, int cond, int cval, int forcedepth // if so... strcpy(brandname, ""); if (rnd(1,100) <= depth) { - brand_t *om, **poss; - int numom; - int nposs = 0; - - // count number of brands - numom = 0; - for (om = firstbrand ; om ; om = om->next) { - numom++; - } - poss = malloc(numom * sizeof(brand_t *)); - - for (om = firstbrand ; om ; om = om->next) { - if (brandappliesto(om, ot)) { - poss[nposs] = om; - nposs++; - } - } - - if (nposs > 0) { - om = poss[rnd(0,nposs-1)]; - strcpy(brandname, om->suffix); - } - free(poss); + brand_t *br; + br = getrandombrandfor(ot); + if (br) strcpy(brandname, br->suffix); } @@ -4101,13 +4410,13 @@ int getobrarity(object_t *o) { if (m) { f = hasflagval(o->flags, F_RARITY,m->habitat, NA, NA, NULL); if (f) { - return f->val[0]; + return f->val[1]; } } // any rarity value at all? f = hasflag(o->flags, F_RARITY); if (f) { - return f->val[0]; + return f->val[1]; } // ie. doesn't randomly appear return 0; @@ -4696,8 +5005,11 @@ void initobjects(void) { // object classes addoc(OC_DFEATURE, "Dungeon Features", "Doors, etc.", '\\', C_GREY); addoc(OC_TERRAIN, "Terrain", "Water, etc.", '\\', C_GREY); + addoc(OC_TRAP, "Trap", "Fiendish traps.", '^', C_GREY); + addocnoun(lastobjectclass, "trap"); addoc(OC_MONEY, "Money", "The standard currency of Nexus.", '$', C_GREY); addoc(OC_SCROLL, "Scrolls", "An arcane roll of parchment, inscribed with many magical glyphs.", '?', C_GREY); + addocnoun(lastobjectclass, "scroll"); addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); @@ -4707,6 +5019,7 @@ void initobjects(void) { //addflag(lastobjectclass->flags, F_MATCONVERTTEXTPL, MT_WATER, NA, NA, "go soggy"); addoc(OC_WAND, "Wands", "A limited-use magical wand which casts the imbued spell.", '/', C_GREY); + addocnoun(lastobjectclass, "wand"); addflag(lastobjectclass->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL); addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_OPERABLE, B_TRUE, NA, NA, NULL); @@ -4714,34 +5027,45 @@ void initobjects(void) { addflag(lastobjectclass->flags, F_RNDCHARGES, 1, 8, NA, NULL); addoc(OC_POTION, "Potions", "A strange concoction contained within a small flask.", '!', C_GREY); + addocnoun(lastobjectclass, "potion"); addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_POURABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_DRINKABLE, B_TRUE, NA, NA, NULL); addoc(OC_RING, "Rings", "A circular band, worn on the finger.", '=', C_GREY); + addocnoun(lastobjectclass, "ring"); addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_GOESON, BP_RIGHTHAND, NA, NA, NULL); addflag(lastobjectclass->flags, F_GOESON, BP_LEFTHAND, NA, NA, NULL); addoc(OC_WEAPON, "Weapons", "An instrument used for the purpose of causing harm or death.", ')', C_GREY); + addocnoun(lastobjectclass, "weapon"); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_CANHAVEOBMOD, OM_MASTERWORK, 17, NA, NULL); addflag(lastobjectclass->flags, F_CANHAVEOBMOD, OM_SHODDY, 34, NA, NULL); addoc(OC_ARMOUR, "Armour/Clothing", "Protective gear.", ']', C_GREY); + addocnoun(lastobjectclass, "armour"); + addocnoun(lastobjectclass, "clothing"); + addocnoun(lastobjectclass, "clothes"); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_CANHAVEOBMOD, OM_MASTERWORK, 17, NA, NULL); addflag(lastobjectclass->flags, F_CANHAVEOBMOD, OM_SHODDY, 34, NA, NULL); addflag(lastobjectclass->flags, F_CANHAVEOBMOD, OM_BLOODSTAINED, 17, NA, NULL); addoc(OC_MISSILE, "Missiles/Ammunition", "An instrument used for the purpose of causing harm or death.", ';', C_GREY); + addocnoun(lastobjectclass, "missile"); + addocnoun(lastobjectclass, "ammo"); + addocnoun(lastobjectclass, "ammunition"); addflag(lastobjectclass->flags, F_ENCHANTABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addoc(OC_FLORA, "Plants", "All kinds of plants and foliage", ',', C_GREEN); + addocnoun(lastobjectclass, "plant"); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addoc(OC_ROCK, "Rocks/Gems", "Boring (or not so boring) rocks or plants.", '*', C_GREY); addoc(OC_FOOD, "Food", "Yum!", '%', C_GREY); + addocnoun(lastobjectclass, "food"); addflag(lastobjectclass->flags, F_STACKABLE, B_TRUE, NA, NA, ""); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addoc(OC_CORPSE, "Corpses", "Dead flesh which was once living.", '%', C_GREY); @@ -4751,9 +5075,12 @@ void initobjects(void) { addflag(lastobjectclass->flags, F_OBHP, 50, 50, NA, NULL); addflag(lastobjectclass->flags, F_OBHPDRAIN, 1, DT_DECAY, NA, NULL); // ie. corpses last for 50 turns addoc(OC_TECH, "Technology", "A strange piece of futuristic technology.", '~', C_GREY); + addocnoun(lastobjectclass, "technology"); + addocnoun(lastobjectclass, "tech"); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addoc(OC_TOOLS, "Tools", "Useful items, from the common to the obscure.", '[', C_GREY); + addocnoun(lastobjectclass, "tool"); addflag(lastobjectclass->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addoc(OC_MISC, "Miscellaneous", "This could be anything.", '\\', C_GREY); @@ -4761,6 +5088,9 @@ void initobjects(void) { addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addoc(OC_BOOK, "Books", "Spellbooks, tomes or manuals.", '+', C_GREY); + addocnoun(lastobjectclass, "spellbook"); + addocnoun(lastobjectclass, "book"); + addocnoun(lastobjectclass, "tome"); addflag(lastobjectclass->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); addflag(lastobjectclass->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addoc(OC_SPELL, "Spells", "A magical spell", '&', C_GREY); // this is a "virtual" object class @@ -4900,7 +5230,7 @@ void initobjects(void) { addflag(lastot->flags, F_REDUCEMOVEMENT, 4, NA, NA, NULL); // traps - addot(OT_TRAPTRIP, "tripwire", "A thin wire at ankle height.", MT_NOTHING, 0, OC_MISC); + addot(OT_TRAPTRIP, "tripwire", "A thin wire at ankle height.", MT_NOTHING, 0, OC_TRAP); addflag(lastot->flags, F_TRAP, 10, B_FALSE, 20, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL); addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); @@ -4908,7 +5238,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); addflag(lastot->flags, F_SECRET, 25, NA, NA, NULL); - addot(OT_TRAPROCK, "falling rock trap", "A pressure plate which causes heavy rocks to drop from the ceiling.", MT_NOTHING, 0, OC_MISC); + addot(OT_TRAPROCK, "falling rock trap", "A pressure plate which causes heavy rocks to drop from the ceiling.", MT_NOTHING, 0, OC_TRAP); addflag(lastot->flags, F_TRAP, 20, B_TRUE, 22, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 90, NA, NULL); addflag(lastot->flags, F_GLYPH, C_GREY, NA, NA, "^"); @@ -4916,7 +5246,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); addflag(lastot->flags, F_SECRET, 25, NA, NA, NULL); - addot(OT_TRAPARROW, "arrow trap", "A pressure plate which causes arrows to shoot at you.", MT_NOTHING, 0, OC_MISC); + addot(OT_TRAPARROW, "arrow trap", "A pressure plate which causes arrows to shoot at you.", MT_NOTHING, 0, OC_TRAP); addflag(lastot->flags, F_TRAP, 25, B_TRUE, NA, NULL); addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 76, NA, NULL); @@ -4925,7 +5255,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); - addot(OT_TRAPARROWP, "poison arrow trap", "A pressure plate which causes poisoned arrows to shoot at you.", MT_NOTHING, 0, OC_MISC); + addot(OT_TRAPARROWP, "poison arrow trap", "A pressure plate which causes poisoned arrows to shoot at you.", MT_NOTHING, 0, OC_TRAP); addflag(lastot->flags, F_TRAP, 25, B_TRUE, NA, NULL); addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 69, NA, NULL); @@ -4934,7 +5264,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); - addot(OT_TRAPGAS, "gas trap", "A pressure plate which releases poisonous gas.", MT_NOTHING, 0, OC_MISC); + addot(OT_TRAPGAS, "gas trap", "A pressure plate which releases poisonous gas.", MT_NOTHING, 0, OC_TRAP); addflag(lastot->flags, F_TRAP, 27, B_TRUE, NA, NULL); addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 69, NA, NULL); @@ -4943,7 +5273,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); - addot(OT_TRAPFIRE, "fire trap", "A pressure plate which fires a pillar of flame.", MT_NOTHING, 0, OC_MISC); + addot(OT_TRAPFIRE, "fire trap", "A pressure plate which fires a pillar of flame.", MT_NOTHING, 0, OC_TRAP); addflag(lastot->flags, F_TRAP, 30, B_TRUE, NA, NULL); addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 59, NA, NULL); @@ -4952,7 +5282,7 @@ void initobjects(void) { addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "."); addflag(lastot->flags, F_SECRET, 30, NA, NA, NULL); - addot(OT_TRAPMINE, "landmine trap", "A buried, pressure-sensitive explosive device.", MT_NOTHING, 0, OC_MISC); + addot(OT_TRAPMINE, "landmine trap", "A buried, pressure-sensitive explosive device.", MT_NOTHING, 0, OC_TRAP); addflag(lastot->flags, F_TRAP, 30, B_TRUE, NA, NULL); addflag(lastot->flags, F_TRAP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_RARITY, H_DUNGEON, 50, NA, NULL); @@ -8761,11 +9091,15 @@ void killobpile(obpile_t *op) { void killoc(objectclass_t *oc) { objectclass_t *nextone, *lastone; + int i; // free mem free(oc->name); free(oc->desc); if (oc->flags) killflagpile(oc->flags); + for (i = 0; i < oc->nnouns; i++) { + free(oc->noun[i]); + } // remove from list nextone = oc->next; @@ -11313,6 +11647,37 @@ int removeob(object_t *o,int howmany) { return o->amt; } +void rrtorarity(enum RARITY r, int *minr, int *maxr) { + switch (r) { + case RR_UNIQUE: + case RR_NEVER: + if (minr) *minr = 0; + if (maxr) *maxr = 0; + break; + case RR_VERYRARE: + if (minr) *minr = 0; + if (maxr) *maxr = 49; + break; + case RR_RARE: + if (minr) *minr = 50; + if (maxr) *maxr = 64; + break; + case RR_UNCOMMON: + if (minr) *minr = 65; + if (maxr) *maxr = 79; + break; + case RR_COMMON: + if (minr) *minr = 80; + if (maxr) *maxr = 100; + break; + default: + if (minr) *minr = 0; + if (maxr) *maxr = 100; // ie. rarity can be anything + break; + + } +} + void setblessed(object_t *o, enum BLESSTYPE wantbless) { o->blessed = wantbless; if (wantbless != B_BLESSED) { diff --git a/objects.h b/objects.h index e6e4ff0..44cd66c 100644 --- a/objects.h +++ b/objects.h @@ -8,6 +8,7 @@ hiddenname_t *addhiddenname(enum OBCLASS obclass, char *text); knowledge_t *addknowledge(enum OBCLASS id, char *hiddenname, int known); 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); int addobburst(cell_t *where, int range, int dirtype, char *name, lifeform_t *fromlf, enum LOFTYPE needlof); @@ -38,6 +39,7 @@ int countnoncosmeticobs(obpile_t *op, int onlyifknown); int curseob(object_t *o); void damageallobs(object_t *srcob, obpile_t *op, int howmuch, int damtype); void dumprandomobs(int amt); +void dumpobrarity(void); void explodeob(object_t *o, flag_t *f, int bigness); void extinguish(object_t *o); material_t *findmaterial(int id); @@ -64,6 +66,8 @@ int getobvalue(object_t *o); char *getaccuracyname(int accpct); object_t *getammo(lifeform_t *lf); object_t *getrandomammo(lifeform_t *lf); +brand_t *getrandombrandfor(objecttype_t *ot); +objecttype_t *getrandomobofclass(enum OBCLASS ocid, int minrarity, int maxrarity); char *getdamname(enum DAMTYPE damtype); char *getdamnamenoun(enum DAMTYPE damtype); char *getfillingname(int nutrition); @@ -193,6 +197,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 rrtorarity(enum RARITY r, int *minr, int *maxr); void setblessed(object_t *o, enum BLESSTYPE wantbless); void setinscription(object_t *o, char *text); void setobcreatedby(object_t *o, lifeform_t *lf); diff --git a/spell.c b/spell.c index 0539ebe..3d4c3ed 100644 --- a/spell.c +++ b/spell.c @@ -31,6 +31,10 @@ extern objecttype_t *objecttype; extern map_t *firstmap; +extern object_t *retobs[MAXPILEOBS+1]; +extern int retobscount[MAXPILEOBS+1]; +extern int nretobs; + extern enum ERROR reason; extern int needredraw, statdirty; @@ -2238,11 +2242,17 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ int seenthiscell = B_FALSE; if (haslos(player, retcell[i])) seenthiscell = B_TRUE; if (retcell[i]->type->solid) { - setcelltype(retcell[i], getemptycelltype(retcell[i]->map->habitat)); - if (seenthiscell) { - ndigs++; - numseen++; - if (seenbyplayer) *seenbyplayer = B_TRUE; + // can dig through stone, but nothing else. + if (retcell[i]->type->material->id == MT_STONE) { + setcelltype(retcell[i], getemptycelltype(retcell[i]->map->habitat)); + if (seenthiscell) { + ndigs++; + numseen++; + if (seenbyplayer) *seenbyplayer = B_TRUE; + } + } else { + // stop. + break; } } else { object_t *o; @@ -5436,7 +5446,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // confirm ch = askchar("Are you sure to want to teleport into the unknown?", "yn", "n", B_TRUE); if (ch != 'y') c = NULL; - } else if (!cellwalkable(caster, c, NULL)) { + } else if (c->type->solid) { // confirm ch = askchar("Are you sure to want to teleport into solid rock?", "yn", "n", B_TRUE); if (ch != 'y') c = NULL; @@ -5544,9 +5554,15 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ targcell = c; - if (!cellwalkable(caster, c, NULL)) { - fizzle(caster); - return B_FALSE; + if (c->type->solid) { + // ok, but you'll die! + } else if (!cellwalkable(caster, c, NULL)) { + // go somewhere nearby... + c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND); + if (!c) { + fizzle(caster); + return B_FALSE; + } } // we can take up to 'power-1' allies with us. @@ -6104,6 +6120,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (isplayer(caster)) { char lfname[BUFLEN]; char question[BUFLEN]; + int i; if (seenbyplayer) *seenbyplayer = B_TRUE; // ask for target if (spellid == OT_S_GIFT) { @@ -6121,34 +6138,40 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ sprintf(question, "For what do you wish"); } askstring(question, '?', buf, BUFLEN, NULL); - o = addob(target->cell->obpile, buf); - if (o) { - char obname[BUFLEN]; - getobname(o, obname, o->amt); - if (!hasflag(o->flags, F_IMPASSABLE) && canpickup(target, o, ALL)) { - relinkob(o, target->pack); // move to pack - if (isplayer(target)) { - msgnocap("%c - %s.", o->letter, obname); - } else { - msg("%s is gifted with %s.", lfname, obname); - } - } else { - // couldn't pick it up - try to place on ground! - // impassable? - if (hasflag(o->flags, F_IMPASSABLE)) { - cell_t *newloc; - // if so, don't put it where the player is! - newloc = getrandomadjcell(target->cell, WE_WALKABLE, B_ALLOWEXPAND); - o = relinkob(o, newloc->obpile); - } - if (o) { - noise(caster->cell, NULL, 1, "something hitting the ground.", NULL); - if (!isblind(caster)) { - msg("%s appear%s on the ground!", obname, (o->amt == 1) ? "s" : ""); + addob(target->cell->obpile, buf); + if (nretobs) { + for (i = 0; i < nretobs; i++) { + char obname[BUFLEN]; + o = retobs[i]; + getobname(o, obname, o->amt); + if (!hasflag(o->flags, F_IMPASSABLE) && canpickup(target, o, ALL)) { + // you gain it. + relinkob(o, target->pack); // move to pack + if (isplayer(target)) { + msgnocap("%c - %s.", o->letter, obname); + } else { + msg("%s is gifted with %s.", lfname, obname); } } else { - // couldn't make it appear - msg("The air in front of %s seems to ripple for a moment.", lfname); + // can't pick this up... + + // impassable? goes in a nearby cell instead of at your feet. + if (hasflag(o->flags, F_IMPASSABLE)) { + cell_t *newloc; + // if so, don't put it where the player is! + newloc = real_getrandomadjcell(target->cell, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL); + o = relinkob(o, newloc->obpile); + } + if (o) { + noise(caster->cell, NULL, 1, "something hitting the ground.", NULL); + if (!isblind(caster)) { + msg("%s appear%s on the ground!", obname, (o->amt == 1) ? "s" : ""); + } + } else { + // ob exists but couldn't make it appear + msg("The air in front of %s seems to ripple for a moment.", lfname); + break; // don't process any more. + } } } } else { diff --git a/text.c b/text.c index 3af8499..2254ec1 100644 --- a/text.c +++ b/text.c @@ -208,6 +208,20 @@ char *getdrunktext(flag_t *drunkflag) { return "??drunk??"; } +char *getrarityname(enum RARITY rr) { + switch (rr) { + case RR_UNIQUE: return "Unique"; + case RR_NEVER: return "Never"; + case RR_VERYRARE: return "Very Rare"; + case RR_RARE: return "Rare"; + case RR_UNCOMMON: return "Uncommon"; + case RR_COMMON: return "Common"; + case RR_NONE: return "None"; + } + + return "?unknownrarity?"; +} + char *getsizetext(enum LFSIZE sz) { switch (sz) { case SZ_ENORMOUS: @@ -458,6 +472,19 @@ char *numtotext(int num, char *buf) { return buf; } +// returns posiiton AFTER end of copied text, or NULL on failure. +char *readuntil(char *retbuf, char *src, char delim) { + char *bp,*p; + bp = retbuf; + for (p=src; *p && (*p != delim); p++) { + *bp = *p; + bp++; + } + p++; // go past delimiter + *bp = '\0'; // nul-terminate buffer + return p; +} + // convert number to roman numerals // only copes with 1-10 char *roman(int num) { @@ -546,6 +573,21 @@ char *dostrrep(char* in, char** out, char* oldtok, char* newtok, int *rv) { return *out; } +int streq(char *a, char *b) { + if (!a || !b) return B_FALSE; + return !strcmp(a,b); +} + +char *strstarts(char *a, char *prefix) { + if (!a || !prefix) return NULL; + + if (strstr(a, prefix) == a) { + return a; + } + + return NULL; +} + int strpixmatch(char *haystack, char *needle) { int matched = B_FALSE; char *hword, *nword, *hcont,*ncont; diff --git a/text.h b/text.h index f3f314f..ae4f072 100644 --- a/text.h +++ b/text.h @@ -10,6 +10,7 @@ char *getattrname(enum ATTRIB att); int gethitconferlifetime(char *text, int *min, int *max); char *getpossessive(char *text); char *getdrunktext(flag_t *drunkflag); +char *getrarityname(enum RARITY rr); char *getsizetext(enum LFSIZE sz); char *gettimetext(char *retbuf); char *gettimetextfuzzy(char *retbuf, int wantpm); @@ -21,11 +22,14 @@ char *makeuppercase(char *text); int needses(char *text); char *noprefix(char *obname); char *numtotext(int num, char *buf); +char *readuntil(char *retbuf, char *src, char delim); char *roman(int num); int speedtokph(int speed); void splittime(int *hours, int *mins, int *secs); char *strrep(char *text, char *oldtok, char *newtok, int *rv); char *dostrrep(char* in, char** out, char* oldtok, char* newtok, int *rv); +int streq(char *a, char *b); +char *strstarts(char *a, char *prefix); int strpixmatch(char *haystack, char *needle); int texttodice(char *text, int *ndice, int *nsides, int *bonus); void texttospellopts(char *text, int *power, char *damstr, int *needgrab, int *range);