From c27ad12c499266a1c7c096263b55e44c009d4f29 Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Wed, 30 Nov 2011 02:07:19 +0000 Subject: [PATCH] added vault code to svn! --- vault.c | 1619 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ vault.h | 27 + 2 files changed, 1646 insertions(+) create mode 100644 vault.c create mode 100644 vault.h diff --git a/vault.c b/vault.c new file mode 100644 index 0000000..066c7aa --- /dev/null +++ b/vault.c @@ -0,0 +1,1619 @@ +#include +#include +#include +#include +#include +#include +#include +#include "defs.h" +#include "flag.h" +#include "io.h" +#include "move.h" +#include "nexus.h" +#include "lf.h" +#include "map.h" +#include "objects.h" +#include "text.h" +#include "vault.h" + +vault_t *firstvault = NULL, *lastvault = NULL; + +extern enum GAMEMODE gamemode; + +vlegend_t *addlegend(vault_t *v, int ch, enum VAULTTHING tt, int pct, char *what) { + vlegend_t *l; + + // add to the end of the list + if (v->legend == NULL) { + v->legend = malloc(sizeof(vlegend_t)); + l = v->legend; + l->prev = NULL; + } else { + // go to end of list + l = v->lastlegend; + l->next = malloc(sizeof(vlegend_t)); + l->next->prev = l; + l = l->next; + } + v->lastlegend = l; + l->next = NULL; + l->vault = v; + + // props + l->ch = ch; + l->tt = tt; + l->pct = pct; + l->what = strdup(what); + return l; +} + +vault_t *addvault(void) { + vault_t *v; + + if (firstvault == NULL) { + firstvault = malloc(sizeof(vault_t)); + v = firstvault; + v->prev = NULL; + } else { + // go to end of list + v = lastvault; + v->next = malloc(sizeof(vault_t)); + v->next->prev = v; + v = v->next; + } + lastvault = v; + v->next = NULL; + + // props + v->filename = NULL; // will be filled in during load. + v->id = NULL; + if (v == firstvault) { + v->numid = 0; + } else { + v->numid = lastvault->numid+1; + } + v->valid = B_TRUE; + v->state = VS_ALLOCATED; + v->flags = addflagpile(NULL, NULL); + v->map[0].mlen = 0; + v->map[0].w = 0; + v->map[0].h = 0; + v->nmaps = 0; + return v; +} + +// add content to vault based on legend +void addvaultcellcontents(cell_t *c, vault_t *v, int x, int y, int rotation) { + char ch; + vlegend_t *l; + + // set cell's vault id before adding anything + // this is needed for things like shopkeepers + // and shop items. + c->room->vault = v; + + ch = getvaultchar(v, x, y, rotation, NULL); + // does this char give us a monster/object? + for (l = v->legend ; l ; l = l->next) { + if (l->ch == ch) { + if (rnd(1,100) <= l->pct) { + addvaultthing(c, v, l->tt, l->what); + } + } + } + +} + +void addvaultcontents(map_t *m, vault_t *v, int minx, int miny, int maxx, int maxy, int rotation) { + flag_t *f; + char buf[BUFLEN]; + int db = B_FALSE; + int x,y; + cell_t *c; + + if (db) dblog("addvaultcontenets started for vaulttype %s, version %d",v->id, rotation); + + for (f = v->flags->first ; f ; f = f->next) { + if ((f->id == F_VAULTATOB) || (f->id == F_VAULTATLF) || (f->id == F_VAULTATCELL)) { + if (rnd(1,100) <= f->val[2]) { + cell_t *c; + int x,y; + getadjustedcoords(v, f->val[0], f->val[1], minx, miny, maxx, maxy, rotation, &x, &y); + c = getcellat(m, x, y); + if (c) { + enum VAULTTHING vt; + switch (f->id) { + default: + case F_VAULTATOB: vt = VT_OB; break; + case F_VAULTATLF: vt = VT_LF; break; + case F_VAULTATCELL: vt = VT_CELL; break; + } + addvaultthing(c, v, vt, f->text); + } + } + } else if (f->id == F_VAULTATONEOF) { + if (rnd(1,100) <= f->val[1]) { + int x[10],y[10]; + int nposs = 0,sel; + int selx,sely; + char *p; + enum VAULTTHING vt; + // get a list of all of the possible locations for this object + p = f->text; + while (*p == '(') { + // go past '(' + p++; + // get x + p = readuntil(buf, p, ','); + x[nposs] = atoi(buf); + // get y + p = readuntil(buf, p, ')'); + y[nposs] = atoi(buf); + nposs++; + } + // go past whitespace - we'll end up on the thing name + while (isspace(*p)) p++; + // select random position + sel = rnd(0,nposs-1); + getadjustedcoords(v, x[sel], y[sel], minx, miny, maxx, maxy, rotation, &selx, &sely); + c = getcellat(m, selx, sely); + // get thing type + vt = f->val[0]; + // add thing + addvaultthing(c, v, vt, p); + } + } else if (f->id == F_VAULTEXIT) { + cell_t *c; + int x,y; + getadjustedcoords(v, f->val[0], f->val[1], minx, miny, maxx, maxy, rotation, &x, &y); + c = getcellat(m, x, y); + if (c) { + addflag(c->map->flags, F_ROOMEXIT, getroomid(c), c->x, c->y, "from f_vaultexit"); + } + } else if ( (f->id == F_VAULTBOX) && (rnd(1,100) <= f->val[1]) ) { + char *p; + char thingname[BUFLEN]; + int x1,y1,x2,y2,x,y; + // get relative range + p = f->text; + p = readuntil(buf, p, ','); + x1 = atoi(buf); + p = readuntil(buf, p, ','); + y1 = atoi(buf); + + /* + if (x1 < 0) x1 = maxx + x1 + 1; + else x1 += minx; + + if (y1 < 0) y1 = maxy + y1 + 1; + else y1 += miny; + */ + + getadjustedcoords(v, x1, y1, minx, miny, maxx, maxy, rotation, &x1, &y1); + + p = readuntil(buf, p, ','); + x2 = atoi(buf); + p = readuntil(buf, p, ','); + y2 = atoi(buf); + + /* + if (x2 < 0) x2 = maxx + x2 + 1; + else x2 += minx; + + if (y2 < 0) y2 = maxy + y2 + 1; + else y2 += miny; + */ + getadjustedcoords(v, x2, y2, minx, miny, maxx, maxy, rotation, &x2, &y2); + + // now figure out the top left and bottom right..... + getboundingbox(x1, y1, x2, y2, &x1, &y1, &x2, &y2); + + p = readuntil(buf, p, ','); + strcpy(thingname,buf); + // fill in + for (y = y1; y <= y2; y++) { + for (x = x1; x <= x2; x++) { + int doit = B_FALSE; + if (f->val[2] == B_TRUE) { // ie. fill + doit = B_TRUE; + } else { // ie. box + if ((x == x1) || (x == x2) || (y == y1) || (y == y2)) { + doit = B_TRUE; + } + } + if (doit) { + cell_t *c; + c = getcellat(m, x, y); + if (c) { + // add the thing + addvaultthing(c, v, f->val[0], thingname); + } + } + } + } // end foreach x/y + } else if ( (f->id == F_VAULTSCATTER) && (rnd(1,100) <= f->val[1]) ) { + char *p; + char thingname[BUFLEN]; + cell_t *poss[MAXCANDIDATES]; // TODO: should be maxroomsize + int nposs = 0; + int totcells = 0; + int i,nplaced; + int x1,y1,x2,y2,x,y; + int countmin,countmax,count; + + if (db) dblog("room is %d,%d-%d,%d (%dx%d)",minx,miny,maxx,maxy,maxx-minx,maxy-miny); + // get relative range + p = f->text; + p = readuntil(buf, p, ','); + x1 = atoi(buf); + p = readuntil(buf, p, ','); + y1 = atoi(buf); + + /* + if (x1 < 0) x1 = maxx + x1 + 1; + else x1 += minx; + + if (y1 < 0) y1 = maxy + y1 + 1; + else y1 += miny; + */ + getadjustedcoords(v, x1, y1, minx, miny, maxx, maxy, rotation, &x1, &y1); + + p = readuntil(buf, p, ','); + x2 = atoi(buf); + p = readuntil(buf, p, ','); + y2 = atoi(buf); + + /* + if (x2 < 0) x2 = maxx + x2 + 1; + else x2 += minx; + + if (y2 < 0) y2 = maxy + y2 + 1; + else y2 += miny; + */ + getadjustedcoords(v, x2, y2, minx, miny, maxx, maxy, rotation, &x2, &y2); + + p = readuntil(buf, p, '-'); + countmin = atoi(buf); + p = readuntil(buf, p, ','); + countmax = atoi(buf); + + p = readuntil(buf, p, ','); + strcpy(thingname,buf); + + // now figure out the top left and bottom right..... + getboundingbox(x1, y1, x2, y2, &x1, &y1, &x2, &y2); + + if (db) dblog("subregion is %d,%d-%d,%d (%dx%d)",x1,y1,x2,y2,x2-x1,y2-y1); + + // get list of cells + nposs = 0; + totcells = 0; + for (y = y1; y <= y2; y++) { + for (x = x1; x <= x2; x++) { + cell_t *c; + c = getcellat(m, x, y); + if (c) { + int valid = B_TRUE; + totcells++; + if (f->val[0] == VT_LF) { + // make sure it's walkable + if (!cellwalkable(NULL, c, NULL)) valid = B_FALSE; + } else if (f->val[0] == VT_OB) { + // make sure it's not solid + if (c->type->solid) valid = B_FALSE; + } + if (valid) { + poss[nposs++] = c; + } + } + } + } // end foreach x/y + + if (db) dblog("cells counted=%d",totcells); + + if (totcells == 0) { + dblog("PROBLEM: couldn't find place to put vaultscatter thing!"); + msg("PROBLEM: couldn't find place to put vaultscatter thing!"); + } + + // figure out how many to place + if (countmax == PCT) { + count = pctof(countmin, nposs); + } else { + count = rnd(countmin, countmax); + } + + if (db) dblog("nposs=%d, count=%d\n", nposs,count); + // now fill in the right amount + nplaced = 0; + for (i = 0; (i < count) && (nposs > 0); i++) { + cell_t *c; + int sel,n; + // get random cell + sel = rnd(0,nposs-1); + c = poss[sel]; + // put the thing there + addvaultthing(c, v, f->val[0], thingname); + nplaced++; + // remove this cell + for (n = sel; n < nposs-1; n++) { + poss[n] = poss[n+1]; + } + nposs--; + } + if (db) dblog("placed %d\n", nplaced); + } + } + + // set vault id for each cell + for (y = miny; y <= maxy; y++) { + for (x = minx; x <= maxx; x++) { + c = getcellat(m, x, y); + if (c) { + c->room->vault = v; + } + } + } +} + +int addvaultthing(cell_t *c, vault_t *v, enum VAULTTHING vt, char *what) { + celltype_t *ct; + object_t *o; + lifeform_t *lf; + int rv = B_FALSE; + switch (vt) { + case VT_EXIT: + // mark this position as a room exit + addflag(c->map->flags, F_ROOMEXIT, getroomid(c), c->x, c->y, "from addvaultthing"); + break; + case VT_OB: + if (streq(what, "random")) { + o = addrandomob(c); + if (!o) { + rv = B_TRUE; + } + } else { + int placeob = B_TRUE; + // special case: ot_playerstart will only be placed if: + // - game is not already in progress + // - there isn't currently one on the level + if (streq(what, "playerstart")) { + if ((gamemode == GM_GAMESTARTED) || findobinmap(c->map, OT_PLAYERSTART)) { + placeob = B_FALSE; + } + } + + if (placeob) { + o = addob(c->obpile, what); + if (!o) { + rv = B_TRUE; + } + } + } + + break; + case VT_LF: + lf = addmonster(c, R_SPECIFIED, what, B_TRUE, 1, B_FALSE, NULL); + if (!lf) { + dblog("invalid racename '%s' in vault", what); + rv = B_TRUE; + } + // first lifeform in a shop is the shopkeeper + if (lf && hasflag(v->flags, F_VAULTISSHOP)) { + if (!findshopkeeper(c->map, getroomid(c))) { + givejob(lf, J_SHOPKEEPER); + } + } + if (lf && streq(v->id, "inn") ) { + addflag(lf->flags, F_STAYINROOM, getroomid(c), NA, NA, NULL); + } + break; + case VT_CELL: + ct = findcelltypebyname(what); + setcelltype(c, ct ? ct->id : c->map->habitat->emptycelltype); + break; + default: + break; + } + return rv; +} + +void dumpvault(char *name, int rotation) { + int x,y; + char buf[BUFLEN]; + char *p; + vault_t *v; + v = findvault(name); + + if (v) { + if (rotation >= v->nmaps) { + dblog("vault dump for '%s' failed. vault exists but no such rotation %d (nmaps=%d).",name, rotation, v->nmaps); + } + + dblog("start vault dump(%s,rot=%d):",v->id, rotation); + + /* + // build up a temp map + for (y = 0; y < v->map[0].h; y++) { + for (x = 0; x < v->map[0].w; x++) { + int offset; + int ch; + ch = getvaultchar(v, x, y, rotation, &offset); + temp[offset] = ch; + } + } + + + // dump the temp map + x = 0; + y = 0; + strcpy(buf, ""); + p = buf; + for (i = 0; i < v->map[rotation].mlen; i++) { + *p = temp[i]; + + x++; + p++; + if (x >= v->map[rotation].w) { + x = 0; + y++; + *p = '\0'; + dblog("%s",buf); + strcpy(buf, ""); + p = buf; + } + } + */ + + strcpy(buf, ""); + p = buf; + for (y = 0; y < v->map[rotation].h; y++) { + for (x = 0; x < v->map[rotation].w; x++) { + *p = getvaultchar(v, x, y, rotation, NULL); + p++; + } + *p = '\0'; + dblog("%s",buf); + strcpy(buf, ""); + p = buf; + } + dblog("end vault dump"); + } else { + dblog("vault dump for '%s' failed. no such vault.",name); + } +} + +vault_t *findvault(char *id) { + vault_t *v; + for (v = firstvault ; v ; v = v->next) { + if (!v->valid) continue; + if (streq(v->id, id)) return v; + } + return NULL; +} + +vault_t *findvaultbyid(int id) { + vault_t *v; + for (v = firstvault ; v ; v = v->next) { + if (!v->valid) continue; + if (v->numid == id) return v; + } + return NULL; +} + +// return a random vault with the given flag. +// don't care about rarity. +vault_t *findvaultwithflag(enum FLAG fid) { + vault_t *v; + vault_t *poss[MAXCANDIDATES]; + int nposs = 0; + for (v = firstvault ; v ; v = v->next) { + if (!v->valid) continue; + if (hasflag(v->flags, fid)) poss[nposs++] = v; + } + if (nposs) { + v = poss[rnd(0,nposs-1)]; + } else { + v = NULL; + } + return v; +} + +// generate vault maps 1-3 with offsets into map[0] +void generatevaultrotations(vault_t *v) { + int i; + int x,y; + int db = B_FALSE; + if (streq(v->id, "jimbos_lair")) { + db = B_TRUE; + } + for (i = 1; i <= 3; i++) { + // rotate 90degrees from previous + v->map[i].mlen = 0; + for (x = 0; x < v->map[i-1].w; x++) { + for (y = v->map[i-1].h-1; y >= 0; y--) { + if (i == 1) { // ie. first rotation + int offset; + getvaultchar(v, x, y, 0, &offset); + v->map[i].data[v->map[i].mlen] = offset; + } else { + v->map[i].data[v->map[i].mlen] = v->map[i-1].data[ y * v->map[i-1].w + x ]; + } + v->map[i].mlen++; + } + } + + v->map[i].w = v->map[i-1].h; + v->map[i].h = v->map[i-1].w; + v->nmaps++; + } + assert(v->nmaps == 4); +} + +void getadjustedcoords(vault_t *v, int origx, int origy, int minx, int miny, int maxx, int maxy, int rotation, int *retx, int *rety) { + int x,y; + int db = B_FALSE; + int w,h; + + if (v->map[0].w) { + w = v->map[0].w; + } else { + w = maxx - minx + 1; + } + + if (v->map[0].h) { + h = v->map[0].h; + } else { + h = maxy - miny + 1; + } + + if (db) dblog("adjustcoords: %d,%d with mapcoord base %d,%d",origx,origy,minx,miny); + // first get coords if this was map #0 (unrotated). + if (origx >= 0) { + x = origx; + } else { + // offset from right. -1 is rightmost. + x = w + origx; + } + if (origy >= 0) { + y = origy; + } else { + // offset from bottom. -1 is bottommost. + y = h + origy; + } + + if (db) dblog("adjustcoords: %d,%d translates to vaultcoords %d,%d (vault w=%d,h=%d)",origx,origy,x,y, w, h); + + // now rotate them + rotatecoords(&x, &y, v, rotation, NULL); + + if (db) dblog("adjustcoords: -> rotated to vaultcoords %d,%d (rotation=%d)",x, y,rotation ); + + // now change them to map coords rather than vault coords + x += minx; + y += miny; + + if (db) dblog("adjustcoords: -> translated to mapcoords %d,%d",x, y); + + // return values + *retx = x; + *rety = y; +} + +void getboundingbox(int x1, int y1, int x2, int y2, int *retleft, int *rettop, int *retright, int *retbottom) { + int left,right,top,bottom; + if (x1 < x2) { + left = x1; + right = x2; + } else { + left = x2; + right = x1; + } + if (y1 < y2) { + top = y1; + bottom = y2; + } else { + top = y2; + bottom = y1; + } + if (retleft) *retleft = left; + if (retright) *retright = right; + if (rettop) *rettop = top; + if (retbottom) *retbottom = bottom; +} + +enum RARITY getvaultrarity(vault_t *v) { + flag_t *f; + f = hasflag(v->flags, F_VAULTRARITY); + if (f) { + return f->val[0]; + } + return RR_COMMON; +} + +void getvaultwh(vault_t *v, int *w, int *h, int rotation) { + assert(rotation < v->nmaps); + *w = v->map[rotation].w; + *h = v->map[rotation].h; +} + +char *getvstatename(enum VAULTSTATE vs) { + switch (vs) { + case VS_ALLOCATED: + return "allocated"; + case VS_NOID: + return "no ID"; + case VS_LOADING: + return "loading (toplevel)"; + case VS_LOADINGMAP: + return "loading map"; + case VS_LOADINGLEGEND: + return "loading legend"; + case VS_LOADINGFLAGS: + return "loading flags"; + default: + break; + } + return "?unknown?"; +} + +celltype_t *getvaultcelltype(vault_t *v, int x, int y, int rotation) { + char ch; + celltype_t *ct = NULL; + vlegend_t *l; + ch = getvaultchar(v, x, y, rotation, NULL); + // does this char give us a cell? + for (l = v->legend ; l ; l = l->next) { + if ((l->ch == ch) && (l->tt == VT_CELL)) { + ct = findcelltypebyname(l->what); + break; + } + } + + return ct; +} + +// return that character at position x/y on give map rotation +char getvaultchar(vault_t *v, int x, int y, int rotation, int *map0offset) { + char ch; + int offset; + + //rotatecoords(&x, &y, v, rotation, &offset); + assert(rotation < v->nmaps); + + offset = y * v->map[rotation].w + x; + + if (rotation == 0) { + ch = v->map[0].data[offset]; + } else { + ch = v->map[0].data[v->map[rotation].data[offset]]; + } + if (map0offset) *map0offset = offset; + + return ch; +} + +// select a random vault type for the given map +vault_t *getvaulttype(map_t *m) { + vault_t *v; + vault_t *poss[MAXCANDIDATES]; + int nposs = 0; + enum RARITY rr = RR_COMMON; + + // select random rarity + rr = pickrr(TT_NONE); + + while (!nposs) { + for (v = firstvault ; v ; v = v->next) { + if (!v->valid) continue; + if (hasflag(v->flags, F_NORANDOM)) continue; + if (getvaultrarity(v) != rr) continue; + + // can this vault go in this map? + if (vaultokformap(v, m)) { + poss[nposs++] = v; + } + } + if (nposs > 0) { + return poss[rnd(0,nposs-1)]; + } + // if we have no possibilities, lower rarity and try again + if (rr == RR_COMMON) { + // give up. + } else { + rr--; + } + } + return NULL; +} + +int handleline(vault_t *v, char *line) { + int ok = B_FALSE; + char localline[BUFLEN]; + char *command = NULL,*temp; + char *dummy; + char *arg[MAXVAULTARGS]; + int nargs = 0; + int i; + + if (!strlen(line)) return B_FALSE; + if (line[0] == '!') return B_FALSE; + + strcpy(localline, line); + + if (v->state == VS_LOADINGMAP) { + if (streq(localline, "@end")) { + // go to toplevel loading state + v->state = VS_LOADING; + ok = B_TRUE; + } else if (strstarts(localline, "random(")) { + char *p; + int minw,minh; + p = localline + 7; + if (*p) { + char buf[BUFLEN]; + p = readuntil(buf, p, ','); + minw = atoi(buf); + p = readuntil(buf, p, ')'); + minh = atoi(buf); + addflag(v->flags, F_VAULTRANDOMMAP, minw, minh, NA, NULL); + } else { + dblog("error - random() map data is missing minw/minh values."); + return B_TRUE; + } + addflag(v->flags, F_VAULTRANDOMMAP, minw, minh, NA, NULL); + ok = B_TRUE; + } else { + // map line definition + if (hasflag(v->flags, F_VAULTRANDOMMAP)) { + dblog("error - map data found after randommap."); + return B_TRUE; + } + + // check length + if ((v->map[0].w != 0) && (strlen(localline) != v->map[0].w)) { + dblog("incorrect map line length! %d != %d", strlen(localline), v->map[0].w); + } else { + char *p; + // read a new map line. we already read into map[0]. + // the others will be populated automatically if/when we + // add f_vaultmayrotate + for (p = localline ; *p; p++) { + v->map[0].data[v->map[0].mlen] = *p; + v->map[0].mlen++; + } + ok = B_TRUE; + // increase width/height + if (!v->map[0].w) { + v->map[0].w = strlen(localline); + } + v->map[0].h++; + } + } + } else { + // split into command and args + if (strchr(localline, ':')) { + temp = strtok_r(localline, ":", &dummy); + command = strdup(temp); + temp = strtok_r(NULL, ":", &dummy); + while (temp) { + arg[nargs] = strdup(temp); + nargs++; + temp = strtok_r(NULL, ":", &dummy); + } + } else { + command = strdup(localline); + nargs = 0; + } + + // process command and args + if (v->state == VS_NOID) { // haven't for our id yet + if (streq(command, "@id")) { + if (!v->id) { + if (nargs == 1) { + if (findvault(arg[0])) { + dblog("Duplicate vault id %s", arg[0]); + return B_TRUE; + } + v->id = strdup(arg[0]); + v->state = VS_LOADING; + ok = B_TRUE; + } else { + dblog("invalid syntax for @id"); + } + } else { + dblog("@id command found but we already have an id"); + } + } + } else if (v->state == VS_LOADING) { // toplevel + if (streq(line, "@map")) { + v->nmaps = 1; + v->state = VS_LOADINGMAP; + ok = B_TRUE; + } else if (streq(line, "@legend")) { + v->state = VS_LOADINGLEGEND; + ok = B_TRUE; + } else if (streq(line, "@flags")) { + v->state = VS_LOADINGFLAGS; + ok = B_TRUE; + } + } else if (v->state == VS_LOADINGLEGEND) { + if (streq(line, "@end")) { + v->state = VS_LOADING; + ok = B_TRUE; + } else { + // legend definition + if (nargs >= 2) { + int pct; + enum VAULTTHING tt; + char what[BUFLEN]; + if (streq(arg[0], "ob")) { + tt = VT_OB; + } else if (streq(arg[0], "mon")) { + tt = VT_LF; + } else if (streq(arg[0], "cell")) { + tt = VT_CELL; + } else { + tt = VT_NONE; + } + + strcpy(what, arg[1]); + + + if (nargs == 3) { // ie have a percentage + pct = atoi(arg[2]); + } else { + pct = 100; + } + + if (tt == VT_NONE) { + dblog("invalid type in legend definition"); + } else if (vaultthingok(tt, what)) { + // validated ok. + addlegend(v, command[0], tt, pct, what); + ok = B_TRUE; + } else { + dblog("invalid legend definition: '%s'", line); + } + } else { // special legend... + if (streq(arg[0], "exit")) { + addlegend(v, command[0], VT_EXIT, 100, ""); + ok = B_TRUE; + } else { + dblog("invalid syntax for legend value"); + } + } + } + } else if (v->state == VS_LOADINGFLAGS) { + if (streq(line, "@end")) { + v->state = VS_LOADING; + ok = B_TRUE; + } else { + // handle flags + if (strstarts(line, "at(")) { + // at(x,y) type:what[:pct] + int x,y; + enum FLAG flagtype; + char *p; + char buf[BUFLEN]; + char thingname[BUFLEN]; + enum VAULTTHING vt = VT_NONE; + int pct; + // get x + p = line + 3; + p = readuntil(buf, p, ','); + x = atoi(buf); + // get y + p = readuntil(buf, p, ')'); + y = atoi(buf); + + // go past whitespace + while (isspace(*p)) p++; + // get type + p = readuntil(buf, p, ':'); + if (streq(buf, "ob")) { + vt = VT_OB; + flagtype = F_VAULTATOB; + } else if (streq(buf, "mon") || streq(buf, "lf")) { + vt = VT_LF; + flagtype = F_VAULTATLF; + } else { // ie. cell + vt = VT_CELL; + flagtype = F_VAULTATCELL; + } + // get thing name + p = readuntil(buf, p, ':'); + strcpy(thingname, buf); + // optional percent chance + if (*p) { + p = readuntil(buf, p, ':'); + pct = atoi(buf); + } else { + pct = 100; + } + + if (vaultthingok(vt, thingname)) { + addflag(v->flags, flagtype, x, y, pct, thingname); + ok = B_TRUE; + } else { + dblog("invalid at() definition: '%s'", line); + } + } else if (strstarts(line, "atoneof(")) { + // atoneof(x,y)(x,y)(x,y)...(x,y) type:what[:pct] + // can have up to 10 x,y pairs. + int x[10],y[10]; + int nposs = 0; + char *p; + char buf[BUFLEN]; + char thingname[BUFLEN]; + enum VAULTTHING vt = VT_NONE; + int pct; + p = line + 7; + + while (*p == '(') { + // go past '(' + p++; + // get x + p = readuntil(buf, p, ','); + x[nposs] = atoi(buf); + // get y + p = readuntil(buf, p, ')'); + y[nposs] = atoi(buf); + nposs++; + } + + // go past whitespace + while (isspace(*p)) p++; + // get type + p = readuntil(buf, p, ':'); + if (streq(buf, "ob")) { + vt = VT_OB; + } else if (streq(buf, "mon") || streq(buf, "lf")) { + vt = VT_LF; + } else { // ie. cell + vt = VT_CELL; + } + // get thing name + p = readuntil(buf, p, ':'); + strcpy(thingname, buf); + // optional percent chance + if (*p) { + p = readuntil(buf, p, ':'); + pct = atoi(buf); + } else { + pct = 100; + } + + if (nposs) { + if (vaultthingok(vt, thingname)) { + int i; + char locbuf[BUFLEN]; + + // construct string for all the possible positions + strcpy(buf, ""); + for (i = 0; i < nposs; i++) { + snprintf(locbuf, BUFLEN, "(%d,%d)",x[i],y[i]); + strcat(buf, locbuf); + } + snprintf(locbuf, BUFLEN, " %s",thingname); + strcat(buf, locbuf); + addflag(v->flags, F_VAULTATONEOF, vt, pct, NA, buf); + ok = B_TRUE; + } else { + dblog("invalid vaultthing in atoneof() definition: '%s'", line); + } + } else { + dblog("no x/y coords found in atoneof() definition: '%s'", line); + } + } else if (strstarts(line, "exitat(")) { // mark a vault exit + // exitat(x,y) + int x,y; + char *p; + char buf[BUFLEN]; + // get x + p = line + 3; + p = readuntil(buf, p, ','); + x = atoi(buf); + // get y + p = readuntil(buf, p, ')'); + y = atoi(buf); + + addflag(v->flags, F_VAULTEXIT, x, y, NA, NULL); + ok = B_TRUE; + } else if (strstarts(line, "box(")) { // outline region + // box(x,y,x2,y2) type:what[:pct] + // if x2/y2 are negative, count back from w/h. ie x=-1 means 'rightmost cell' + int x1,y1,x2,y2; + enum VAULTTHING thingtype; + char *p; + char buf[BUFLEN]; + char thingname[BUFLEN]; + int pct; + p = line + 4; + // get x/y + p = readuntil(buf, p, ','); + x1 = atoi(buf); + p = readuntil(buf, p, ','); + y1 = atoi(buf); + // get x2/y2 + p = readuntil(buf, p, ','); + x2 = atoi(buf); + p = readuntil(buf, p, ')'); + y2 = atoi(buf); + + // go past whitespace + while (isspace(*p)) p++; + // get type + p = readuntil(buf, p, ':'); + if (streq(buf, "ob")) { + thingtype = VT_OB; + } else if (streq(buf, "mon") || streq(buf, "lf")) { + thingtype = VT_LF; + } else { // ie. cell + thingtype = VT_CELL; + } + // get thing name + p = readuntil(buf, p, ':'); + strcpy(thingname, buf); + // optional percent chance + if (*p) { + p = readuntil(buf, p, ':'); + pct = atoi(buf); + } else { + pct = 100; + } + + + if (vaultthingok(thingtype, thingname)) { + snprintf(buf, BUFLEN, "%d,%d,%d,%d,%s",x1,y1,x2,y2,thingname); + addflag(v->flags, F_VAULTBOX, thingtype, pct, B_FALSE, buf); + ok = B_TRUE; + } else { + dblog("invalid box() definition: '%s'", line); + } + + } else if (strstarts(line, "fill(")) { // fill region + // fill(x,y,x2,y2) type:what[:pct] + // if x2/y2 are negative, count back from w/h. ie x=-1 means 'rightmost cell' + int x1,y1,x2,y2; + enum VAULTTHING thingtype; + char *p; + char buf[BUFLEN]; + char thingname[BUFLEN]; + int pct; + p = line + 5; + // get x/y + p = readuntil(buf, p, ','); + x1 = atoi(buf); + p = readuntil(buf, p, ','); + y1 = atoi(buf); + // get x2/y2 + p = readuntil(buf, p, ','); + x2 = atoi(buf); + p = readuntil(buf, p, ')'); + y2 = atoi(buf); + + // go past whitespace + while (isspace(*p)) p++; + // get type + p = readuntil(buf, p, ':'); + if (streq(buf, "ob")) { + thingtype = VT_OB; + } else if (streq(buf, "mon") || streq(buf, "lf")) { + thingtype = VT_LF; + } else { // ie. cell + thingtype = VT_CELL; + } + // get thing name + p = readuntil(buf, p, ':'); + strcpy(thingname, buf); + // optional percent chance + if (*p) { + p = readuntil(buf, p, ':'); + pct = atoi(buf); + } else { + pct = 100; + } + + if (vaultthingok(thingtype, thingname)) { + snprintf(buf, BUFLEN, "%d,%d,%d,%d,%s",x1,y1,x2,y2,thingname); + addflag(v->flags, F_VAULTBOX, thingtype, pct, B_TRUE, buf); + ok = B_TRUE; + } else { + dblog("invalid box() definition: '%s'", line); + } + } else if (strstarts(line, "scatter(")) { // fill region + // scatter(x,y,x2,y2) type:what:howmany[:pct] + // if x2/y2 are negative, count back from w/h. ie x=-1 means 'rightmost cell' + int x1,y1,x2,y2; + enum VAULTTHING thingtype; + char *p; + char buf[BUFLEN]; + char thingname[BUFLEN]; + int pct; + int countmin,countmax; + p = line + 8; + // get x/y + p = readuntil(buf, p, ','); + x1 = atoi(buf); + p = readuntil(buf, p, ','); + y1 = atoi(buf); + // get x2/y2 + p = readuntil(buf, p, ','); + x2 = atoi(buf); + p = readuntil(buf, p, ')'); + y2 = atoi(buf); + + // go past whitespace + while (isspace(*p)) p++; + // get type + p = readuntil(buf, p, ':'); + if (streq(buf, "ob")) { + thingtype = VT_OB; + } else if (streq(buf, "mon") || streq(buf, "lf")) { + thingtype = VT_LF; + } else { // ie. cell + thingtype = VT_CELL; + } + // get thing name + p = readuntil(buf, p, ':'); + strcpy(thingname, buf); + // get count + p = readuntil(buf, p, ':'); + // is this a range? + if (strchr(buf, '-') && strchr(buf, '%')) { + dblog("error - scatter() count contains both range and percentage."); + return B_TRUE; + } else if (strchr(buf, '-')) { + char *p2; + char buf2[BUFLENSMALL]; + p2 = readuntil(buf2, buf, '-'); + countmin = atoi(buf2); + p2 = readuntil(buf2, p2, ':'); + countmax = atoi(buf2); + } else if (strchr(buf, '%')) { + char *p2; + p2 = strchr(buf, '%'); // replace % with nul + *p2 = '\0'; + countmin = atoi(buf); + countmax = PCT; + } else { + countmin = atoi(buf); + countmax = countmin; + } + // optional percent chance + if (*p) { + p = readuntil(buf, p, ':'); + pct = atoi(buf); + } else { + pct = 100; + } + + if (vaultthingok(thingtype, thingname)) { + snprintf(buf, BUFLEN, "%d,%d,%d,%d,%d-%d,%s",x1,y1,x2,y2,countmin,countmax,thingname); + addflag(v->flags, F_VAULTSCATTER, thingtype, pct, NA, buf); + ok = B_TRUE; + } else { + dblog("invalid scatter() definition: '%s'", line); + } + } else if (strstarts(line, "autodoors")) { + char *p; + int pct = 100; + p = line + 9; + if (*p == ':') { + char buf[BUFLEN]; + p++; + p = readuntil(buf, p, ','); // really jsut want EOL + pct = atoi(buf); + } + addflag(v->flags, F_AUTODOORS, pct, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(line, "autopop")) { + addflag(v->flags, F_AUTOPOPULATE, B_TRUE, NA, NA, NULL); + ok = B_TRUE; + } else if (strstarts(line, "dlevmax")) { + // dlevmax:max + char *p; + p = line + 7; + if (*p == ':') { + int lev; + char buf[BUFLEN]; + p++; + p = readuntil(buf, p, ','); // eol + lev = atoi(buf); + addflag(v->flags, F_VAULTDLEVMAX, lev, NA, NA, NULL); + ok = B_TRUE; + } else { + dblog("invalid dlevmax definition: '%s'", line); + } + } else if (strstarts(line, "dlevmin")) { + // dlevmin:min + char *p; + p = line + 7; + if (*p == ':') { + int lev; + char buf[BUFLEN]; + p++; + p = readuntil(buf, p, ','); // eol + lev = atoi(buf); + addflag(v->flags, F_VAULTDLEVMIN, lev, NA, NA, NULL); + ok = B_TRUE; + } else { + dblog("invalid dlevmin definition: '%s'", line); + } + } else if (strstarts(line, "entertext")) { + char *p; + p = line + 9; + if (*p == ':') { + char buf[BUFLEN]; + p++; + p = readuntil(buf, p, ','); // really jsut want EOL + if (strlen(buf)) { + addflag(v->flags, F_VAULTENTERTEXT, NA, NA, NA, buf); + ok = B_TRUE; + } else { + dblog("invalid entertext definition: '%s' (arglen is 0)",line); + } + } else { + dblog("missing entertext definition: '%s'", line); + } + } else if (strstarts(line, "goesin")) { + char *p; + p = line + 6; + if (*p == ':') { + habitat_t *h; + char buf[BUFLEN]; + p++; + p = readuntil(buf, p, ','); // really jsut want EOL + h = findhabitatbyname(buf); + if (h) { + addflag(v->flags, F_VAULTGOESIN, h->id, NA, NA, NULL); + ok = B_TRUE; + } else { + dblog("invalid goesin() definition: '%s' [BAD HABITAT]", line); + } + } else { + dblog("invalid goesin() definition: '%s'", line); + } + } else if (strstarts(line, "margin")) { + char *p; + p = line + 6; + if (*p == ':') { + char buf[BUFLEN]; + int xmargin,ymargin; + p++; + p = readuntil(buf, p, ','); + xmargin = atoi(buf); + p = readuntil(buf, p, ','); // really EOL + ymargin = atoi(buf); + addflag(v->flags, F_KEEPMARGIN, xmargin, ymargin, NA, NULL); + ok = B_TRUE; + } else { + dblog("invalid margin flag: '%s'", line); + } + } else if (streq(line, "mayrotate")) { + if (v->nmaps == 1) { + addflag(v->flags, F_VAULTMAYROTATE, B_TRUE, NA, NA, NULL); + // now generate rotated versions + generatevaultrotations(v); + ok = B_TRUE; + } else { + dblog("vault flag mayrotate isnt valid for vaults with random maps."); + } + } else if (streq(line, "nolink")) { + addflag(v->flags, F_VAULTNOLINK, B_TRUE, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(line, "norandom")) { + addflag(v->flags, F_NORANDOM, B_TRUE, NA, NA, NULL); + addflag(v->flags, F_VAULTRARITY, RR_NEVER, NA, NA, NULL); + ok = B_TRUE; + } else if (strstarts(line, "rarity")) { + char *p; + p = line + 6; + if (*p == ':') { + char buf[BUFLEN]; + p++; + p = readuntil(buf, p, ','); // really EOL + if (streq(buf, "frequent")) { + addflag(v->flags, F_VAULTRARITY, RR_FREQUENT, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(buf, "common")) { + addflag(v->flags, F_VAULTRARITY, RR_COMMON, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(buf, "uncommon")) { + addflag(v->flags, F_VAULTRARITY, RR_UNCOMMON, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(buf, "rare")) { + addflag(v->flags, F_VAULTRARITY, RR_RARE, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(buf, "vrare") || streq(buf, "veryrare")) { + addflag(v->flags, F_VAULTRARITY, RR_VERYRARE, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(buf, "never")) { + addflag(v->flags, F_VAULTRARITY, RR_NEVER, NA, NA, NULL); + ok = B_TRUE; + } else { + dblog("invalid rarity value: '%s'", line); + } + } else { + dblog("invalid rarity flag: '%s'", line); + } + } else if (streq(line, "playerstart")) { + addflag(v->flags, F_VAULTISPLAYERSTART, B_TRUE, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(line, "shop")) { + addflag(v->flags, F_VAULTISSHOP, B_TRUE, NA, NA, NULL); + ok = B_TRUE; + } else if (streq(line, "shrine")) { // a godstone shrine + addflag(v->flags, F_VAULTISSHRINE, B_TRUE, NA, NA, NULL); + ok = B_TRUE; + } + } + } + } + + // free args + if (command) free(command); + + for (i = 0; i < nargs; i++) { + free(arg[i]); + } + + if (!ok) { + dblog("[vault:%s/%s] Invalid line: [%s]", + (v->state == VS_NOID) ? "no_id" : v->id, + getvstatename(v->state), line); + return B_TRUE; + } + return B_FALSE; +} + +void killlegend(vlegend_t *l) { + vlegend_t *nextone,*lastone; + if (l->what) free(l->what); + + // deallocate + nextone = l->next; + if (nextone != NULL) { + nextone->prev = l->prev; + } else { /* last */ + l->vault->lastlegend = l->prev; + } + + if (l->prev == NULL) { + /* first */ + nextone = l->next; + l->vault->legend = nextone; + free(l); + } else { + lastone = l->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + +void killvault(vault_t *v) { + vault_t *nextone,*lastone; + + if (!v) return; + if (v->id) free(v->id); + while (v->legend) { + killlegend(v->legend); + } + killflagpile(v->flags); + + // deallocate + nextone = v->next; + if (nextone != NULL) { + nextone->prev = v->prev; + } else { /* last */ + lastvault = v->prev; + } + + if (v->prev == NULL) { + /* first */ + nextone = v->next; + firstvault = nextone; + free(v); + } else { + lastone = v->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + +vault_t *loadvault(char *filename) { + FILE *f; + vault_t *v; + char line[BUFLEN]; + char fullname[BUFLEN]; + int goterrors = B_FALSE; + + snprintf(fullname, BUFLEN, "%s/%s", VAULTDIR, filename); + dblog("Loading vault file '%s'", fullname); + + f = fopen(fullname,"rt"); + if (!f) { + dblog("error opening vault file '%s'", fullname); + return NULL; + } + + v = addvault(); + if (!v) { + dblog("error allocating new vault"); + return NULL; + } + v->filename = strdup(filename); + + v->state = VS_NOID; + + // read a line + fgets(line, BUFLEN, f); + while (!feof(f)) { + // strip newline + line[strlen(line)-1] = '\0'; + // handle it + if (handleline(v, line)) { + goterrors = B_TRUE; + break; + } + // read next line + fgets(line, BUFLEN, f); + } + + fclose(f); + + if (goterrors && v) { + // mark vault as invalid + v->valid = B_FALSE; + v = NULL; + } else { + //dumpvault(v); + } + return v; +} + +void loadvaults(void) { + int nvaults = 0; + DIR *dir; + struct dirent *ent; + // + dir = opendir(VAULTDIR); + if (!dir) { + dblog("Could not open vault directory '%s'",VAULTDIR); + return; + } + nvaults = 0; + while ((ent = readdir(dir)) != NULL) { + char *p; + // ie. start of 4 char prefix + p = ent->d_name + strlen(ent->d_name) - 4; + if (!strcmp(p, ".vlt") ) { + if (loadvault(ent->d_name)) { + nvaults++; + } else { + dblog("Errors found in vaultfile %s - loading cancelled.",ent->d_name); + } + } + } + closedir(dir); + if (nvaults) { + dblog("Successfully loaded %d vault definition%s.", nvaults,(nvaults == 1) ? "" : "s"); + } else { + dblog("No (valid) vault definitions found.", nvaults); + } + + // debugging + //dumpvault("jimbos_lair", 0); + //dumpvault("jimbos_lair", 1); + //dumpvault("jimbos_lair", 2); + //dumpvault("jimbos_lair", 3); +} + +// translate coords on map0 into coords of the same position on map1-3 +void rotatecoords(int *x, int *y, vault_t *v, int rotation, int *retoffset) { + int offset,newoffset; + + // get offset in map 0 + offset = (*y * (v->map[0].w) + *x); + if (hasflag(v->flags, F_VAULTRANDOMMAP)) { + // don't need to rotate in random maps + if (retoffset) *retoffset = offset; + return; + } + + if (rotation == 0) { + // leave offset (and therefore x/y) unchanged + newoffset = offset; + } else { + int i; + newoffset = -1; + // find translated offset + for (i = 0; i < v->map[rotation].mlen; i++) { + if (v->map[rotation].data[i] == offset) { + newoffset = i; + break; + } + } + assert(newoffset != -1); + } + + // get new x/y + *x = newoffset % v->map[rotation].w; + *y = newoffset / v->map[rotation].w; + if (retoffset) *retoffset = newoffset; +} + +int vaultthingok(enum VAULTTHING vt, char *what) { + celltype_t *ct; + object_t *o; + race_t *r; + obpile_t *op; + + switch (vt) { + case VT_LF: + if (streq(what, "random")) { + return B_TRUE; + } else { + r = findracebyname(what); + if (r) { + return B_TRUE; + } else { + if (parserace(what, NULL, NULL)) { + return B_TRUE; + } + } + } + break; + case VT_OB: + if (streq(what, "random")) { + return B_TRUE; + } else { + op = addobpile(NULL, NULL, NULL); + o = addob(op, what); + if (o) { + killobpile(op); + return B_TRUE; + } + killobpile(op); + } + break; + case VT_CELL: + ct = findcelltypebyname(what); + if (ct) return B_TRUE; + break; + default: + return B_TRUE; + break; + } + return B_FALSE; +} + +int vaultokformap(vault_t *v, map_t *m) { + flag_t *f; + // check HABITAT + if (hasflag(v->flags, F_VAULTGOESIN)) { + int ok = B_FALSE; + // check which habitats are valid + for (f = v->flags->first ; f ; f = f->next) { + if (f->id == F_VAULTGOESIN) { + if (f->val[0] == m->habitat->id) { + ok = B_TRUE; + break; + } + } + } + if (!ok) return B_FALSE; + } + + f = hasflag(v->flags, F_VAULTDLEVMIN); + if (f && (getmapdifficulty(m) < f->val[0])) { + return B_FALSE; + } + f = hasflag(v->flags, F_VAULTDLEVMAX); + if (f && (getmapdifficulty(m) > f->val[0])) { + return B_FALSE; + } + + return B_TRUE; +} diff --git a/vault.h b/vault.h new file mode 100644 index 0000000..4700178 --- /dev/null +++ b/vault.h @@ -0,0 +1,27 @@ +#include "defs.h" + +vlegend_t *addlegend(vault_t *v, int ch, enum VAULTTHING tt, int pct, char *what); +vault_t *addvault(void); +void addvaultcellcontents(cell_t *c, vault_t *v, int x, int y, int rotation); +void addvaultcontents(map_t *m, vault_t *v, int minx, int miny, int maxx, int maxy, int rotationmapnum); +int addvaultthing(cell_t *c, vault_t *v, enum VAULTTHING vt, char *what); +void dumpvault(char *name, int rotation); +vault_t *findvault(char *id); +vault_t *findvaultbyid(int id); +vault_t *findvaultwithflag(enum FLAG fid); +void generatevaultrotations(vault_t *v); +void getadjustedcoords(vault_t *v, int origx, int origy, int minx, int miny, int maxx, int maxy, int rotation, int *retx, int *rety); +void getboundingbox(int x1, int y1, int x2, int y2, int *retleft, int *rettop, int *retright, int *retbottom); +enum RARITY getvaultrarity(vault_t *v); +void getvaultwh(vault_t *v, int *w, int *h, int rotation); +char *getvstatename(enum VAULTSTATE vs); +celltype_t *getvaultcelltype(vault_t *v, int x, int y, int rotation); +char getvaultchar(vault_t *v, int x, int y, int rotation, int *map0offset); +vault_t *getvaulttype(map_t *m); +int handleline(vault_t *v, char *line); +void killvault(vault_t *v); +vault_t *loadvault(char *filename); +void loadvaults(void); +void rotatecoords(int *x, int *y, vault_t *v, int rotation, int *retoffset); +int vaultthingok(enum VAULTTHING vt, char *what); +int vaultokformap(vault_t *v, map_t *m);