nexus/vault.c

1708 lines
41 KiB
C

#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#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->legend = NULL;
v->lastlegend = 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;
}
// return a random vault with the given tag (ie. f_vaulttag "xxx").
// don't care about rarity.
vault_t *findvaultwithtag(char *tag) {
vault_t *v;
vault_t *poss[MAXCANDIDATES];
int nposs = 0;
for (v = firstvault ; v ; v = v->next) {
if (!v->valid) continue;
if (hasflagval(v->flags, F_VAULTTAG, NA, NA, NA, tag)) poss[nposs++] = v;
}
if (nposs) {
v = poss[rnd(0,nposs-1)];
} else {
v = NULL;
}
return v;
}
// generate vault map 1 as x-flipped map0.
// remember offsets into map[0]
void generatevaultflipsx(vault_t *v) {
int x,y;
v->map[1].mlen = 0;
for (y = 0 ; y < v->map[0].h; y++) {
for (x = v->map[0].w-1; x >= 0; x--) {
int offset;
getvaultchar(v, x, y, 0, &offset);
v->map[1].data[v->map[1].mlen] = offset;
v->map[1].mlen++;
}
}
v->map[1].w = v->map[0].w;
v->map[1].h = v->map[0].h;
v->nmaps = 2;
}
// generate vault map 1 as y-flipped map0.
// remember offsets into map[0]
void generatevaultflipsy(vault_t *v) {
int x,y;
v->map[1].mlen = 0;
for (y = v->map[0].h-1; y >= 0; y--) {
for (x = 0 ; x < v->map[0].w; x++) {
int offset;
getvaultchar(v, x, y, 0, &offset);
v->map[1].data[v->map[1].mlen] = offset;
v->map[1].mlen++;
}
}
v->map[1].w = v->map[0].w;
v->map[1].h = v->map[0].h;
v->nmaps = 2;
}
// generate vault maps 1-3 with offsets into map[0]
void generatevaultrotations(vault_t *v) {
int i;
int x,y;
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 (streq(line, "maintainedge")) {
addflag(v->flags, F_MAINTAINEDGE, B_TRUE, NA, NA, NULL);
ok = B_TRUE;
} 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 (hasflag(v->flags, F_VAULTMAYFLIPX) || hasflag(v->flags, F_VAULTMAYFLIPY)) {
dblog("vault can only have one of mayrotate / mayflipx / mayflipy.");
} else 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, "mayflipx")) {
if (hasflag(v->flags, F_VAULTMAYROTATE) || hasflag(v->flags, F_VAULTMAYFLIPY)) {
dblog("vault can only have one of mayrotate / mayflipx / mayflipy.");
} else if (v->nmaps == 1) {
addflag(v->flags, F_VAULTMAYFLIPX, B_TRUE, NA, NA, NULL);
// now generate rotated versions
generatevaultflipsx(v);
ok = B_TRUE;
} else {
dblog("vault flag mayflipx isnt valid for vaults with random maps.");
}
} else if (streq(line, "mayflipy")) {
if (hasflag(v->flags, F_VAULTMAYROTATE) || hasflag(v->flags, F_VAULTMAYFLIPX)) {
dblog("vault can only have one of mayrotate / mayflipx / mayflipy.");
} else if (v->nmaps == 1) {
addflag(v->flags, F_VAULTMAYFLIPY, B_TRUE, NA, NA, NULL);
// now generate rotated versions
generatevaultflipsy(v);
ok = B_TRUE;
} else {
dblog("vault flag mayflipy 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 (strstarts(line, "tag:")) {
char *p;
p = line + 4;
addflag(v->flags, F_VAULTTAG, B_TRUE, NA, NA, p);
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);
v->flags = NULL;
// 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;
}