1851 lines
45 KiB
C
1851 lines
45 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 lifeform_t *player;
|
|
|
|
extern enum GAMEMODE gamemode;
|
|
|
|
vlegend_t *addlegend(vault_t *v, int ch, enum VAULTTHING tt, int pct, char *what, enum VAULTTHING tt2, char *what2) {
|
|
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);
|
|
// alternatives...
|
|
l->tt2 = tt2;
|
|
if (what2) {
|
|
l->what2 = strdup(what2);
|
|
} else {
|
|
l->what2 = strdup("");
|
|
}
|
|
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);
|
|
} else if (l->tt2 != VT_NONE) {
|
|
addvaultthing(c, v, l->tt2, l->what2);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// add vault contents based on flags
|
|
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_TRUE, 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 && hasflag(v->flags, F_MONSTERSSTAY)) {
|
|
if (!lfhasflag(lf, F_STAYINROOM)) {
|
|
addflag(lf->flags, F_STAYINROOM, getroomid(c), B_MAYCHASE, NA, NULL);
|
|
}
|
|
}
|
|
break;
|
|
case VT_CELL:
|
|
if (streq(what, "EMPTY")) {
|
|
ct = findcelltype(getmapempty(c->map));
|
|
} else if (streq(what, "SOLID")) {
|
|
ct = findcelltype(getmapsolid(c->map));
|
|
} else {
|
|
ct = findcelltypebyname(what);
|
|
}
|
|
setcelltype(c, ct ? ct->id : getmapempty(c->map));
|
|
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_FREQUENT) {
|
|
// give up.
|
|
break;
|
|
} 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;
|
|
int hasfire = B_FALSE;
|
|
|
|
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 (strchr(arg[0], ' ')) {
|
|
dblog("Vault names may not contain spaces: '%s'.", arg[0]);
|
|
return B_TRUE;
|
|
} else 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,tt2;
|
|
char what[BUFLEN],what2[BUFLEN];
|
|
|
|
// default is no alternative
|
|
tt2 = VT_NONE;
|
|
strcpy(what2, "");
|
|
|
|
// get ob specification
|
|
tt = strtovt(arg[0]);
|
|
strcpy(what, arg[1]);
|
|
|
|
if (nargs >= 3) { // ie have a percentage
|
|
pct = atoi(arg[2]);
|
|
} else {
|
|
pct = 100;
|
|
}
|
|
|
|
if (nargs == 5) { // have alternative value if percentage fails
|
|
tt2 = strtovt(arg[3]);
|
|
strcpy(what2, arg[4]);
|
|
}
|
|
|
|
if (nargs == 4) {
|
|
dblog("alternative obtype given but no alternative ob text");
|
|
} else if (tt == VT_NONE) {
|
|
dblog("invalid type in legend definition");
|
|
} else {
|
|
int error = B_FALSE;
|
|
if (!real_vaultthingok(tt, what, &hasfire)) {
|
|
dblog("invalid legend definition: '%s'", line);
|
|
error = B_TRUE;
|
|
}
|
|
if ((tt2 != VT_NONE) && !real_vaultthingok(tt2,what2, &hasfire)) {
|
|
dblog("invalid legend alternative definition: '%s'", line);
|
|
error = B_TRUE;
|
|
}
|
|
if (!error) {
|
|
// validated ok.
|
|
addlegend(v, command[0], tt, pct, what, tt2, what2);
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
} else { // special legend...
|
|
if (streq(arg[0], "exit")) {
|
|
addlegend(v, command[0], VT_EXIT, 100, "", VT_NONE, NULL);
|
|
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 (real_vaultthingok(vt, thingname, &hasfire)) {
|
|
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 (real_vaultthingok(vt, thingname, &hasfire)) {
|
|
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 (real_vaultthingok(thingtype, thingname, &hasfire)) {
|
|
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 (real_vaultthingok(thingtype, thingname, &hasfire)) {
|
|
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 (real_vaultthingok(thingtype, thingname, &hasfire)) {
|
|
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, "cellempty")) {
|
|
char *p;
|
|
p = line + strlen("cellempty");
|
|
if (*p == ':') {
|
|
char buf[BUFLEN];
|
|
celltype_t *ct;
|
|
p++;
|
|
p = readuntil(buf, p, ','); // eol
|
|
// validate celltype name
|
|
ct = findcelltypebyname(buf);
|
|
if (ct) {
|
|
addflag(v->flags, F_CELLTYPEEMPTY, ct->id, NA, NA, NULL);
|
|
ok = B_TRUE;
|
|
} else {
|
|
dblog("invalid celltype specified in cellempty definition: '%s'", line);
|
|
}
|
|
} else {
|
|
dblog("syntax error in cellempty definition: '%s'", line);
|
|
}
|
|
} else if (strstarts(line, "cellsolid")) {
|
|
char *p;
|
|
p = line + strlen("cellsolid");
|
|
if (*p == ':') {
|
|
char buf[BUFLEN];
|
|
celltype_t *ct;
|
|
p++;
|
|
p = readuntil(buf, p, ','); // eol
|
|
// validate celltype name
|
|
ct = findcelltypebyname(buf);
|
|
if (ct) {
|
|
addflag(v->flags, F_CELLTYPESOLID, ct->id, NA, NA, NULL);
|
|
ok = B_TRUE;
|
|
} else {
|
|
dblog("invalid celltype specified in cellsolid definition: '%s'", line);
|
|
}
|
|
} else {
|
|
dblog("syntax error in cellsolid definition: '%s'", line);
|
|
}
|
|
} 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, "keepmonsinroom")) {
|
|
addflag(v->flags, F_STAYINROOM, NA, B_MAYCHASE, NA, NULL);
|
|
ok = B_TRUE;
|
|
} else if (streq(line, "maintainedge")) {
|
|
addflag(v->flags, F_MAINTAINEDGE, B_TRUE, NA, NA, NULL);
|
|
ok = B_TRUE;
|
|
} else if (strstarts(line, "nostairs")) {
|
|
addflag(v->flags, F_NOSTAIRS, 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, "monstersstay")) {
|
|
addflag(v->flags, F_MONSTERSSTAY, B_TRUE, NA, NA, NULL);
|
|
ok = B_TRUE;
|
|
} 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// free args
|
|
if (command) free(command);
|
|
|
|
for (i = 0; i < nargs; i++) {
|
|
free(arg[i]);
|
|
}
|
|
|
|
if (ok) {
|
|
// remember if the vault has objects made of fire
|
|
if (hasfire && !hasflag(v->flags, F_VAULTHASFIRE)) {
|
|
addflag(v->flags, F_VAULTHASFIRE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
} else {
|
|
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);
|
|
if (l->what2) free(l->what2);
|
|
|
|
// 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);
|
|
if (v->filename) free(v->filename);
|
|
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 *dir, char *filename) {
|
|
FILE *f;
|
|
vault_t *v;
|
|
char line[BUFLEN];
|
|
char fullname[BUFLEN];
|
|
int goterrors = B_FALSE;
|
|
|
|
snprintf(fullname, BUFLEN, "%s/%s", dir, filename);
|
|
dblog("Loading vault file '%s'", fullname);
|
|
|
|
f = fopen(fullname,"rt");
|
|
if (!f) {
|
|
dblog("error opening vault file '%s/%s'", dir,fullname);
|
|
return NULL;
|
|
}
|
|
|
|
v = addvault();
|
|
if (!v) {
|
|
dblog("error allocating new vault");
|
|
return NULL;
|
|
}
|
|
asprintf(&v->filename, "%s/%s",dir,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) {
|
|
char *vaultdir[2];
|
|
int i;
|
|
|
|
//
|
|
vaultdir[0] = strdup(VAULTDIR);
|
|
vaultdir[1] = strdup(BONESDIR);
|
|
for (i = 0; i < 2; i++) {
|
|
int nvaults = 0;
|
|
DIR *dir;
|
|
struct dirent *ent;
|
|
dir = opendir(vaultdir[i]);
|
|
if (!dir) {
|
|
dblog("Could not open vault directory '%s'",vaultdir[i]);
|
|
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(vaultdir[i], ent->d_name)) {
|
|
nvaults++;
|
|
} else {
|
|
dblog("Errors found in vaultfile %s - loading cancelled.",ent->d_name);
|
|
}
|
|
}
|
|
}
|
|
closedir(dir);
|
|
if (nvaults) {
|
|
dblog("Successfully loaded %d %s definition%s.", nvaults,
|
|
(i == 0) ? "fixed vault" : "bones vault",
|
|
(nvaults == 1) ? "" : "s");
|
|
} else {
|
|
dblog("No (valid) vault definitions found.", nvaults);
|
|
}
|
|
}
|
|
free(vaultdir[0]);
|
|
free(vaultdir[1]);
|
|
|
|
// 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) {
|
|
return real_vaultthingok(vt, what, NULL);
|
|
}
|
|
|
|
int real_vaultthingok(enum VAULTTHING vt, char *what, int *hasfire) {
|
|
celltype_t *ct;
|
|
object_t *o;
|
|
race_t *r;
|
|
obpile_t *op;
|
|
|
|
// default
|
|
if (hasfire) *hasfire = B_FALSE;
|
|
|
|
switch (vt) {
|
|
case VT_LF:
|
|
if (streq(what, "random")) {
|
|
return B_TRUE;
|
|
} else {
|
|
r = findracebyname(what);
|
|
if (r) {
|
|
if (r->material->id == MT_FIRE) {
|
|
*hasfire = B_TRUE;
|
|
}
|
|
return B_TRUE;
|
|
} else {
|
|
enum RACE rid;
|
|
rid = parserace(what, NULL, NULL, NULL);
|
|
if (rid) {
|
|
if (hasfire) {
|
|
r = findrace(rid);
|
|
if (r && (r->material->id == MT_FIRE)) {
|
|
*hasfire = B_TRUE;
|
|
}
|
|
}
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case VT_OB:
|
|
if (streq(what, "random")) {
|
|
return B_TRUE;
|
|
} else {
|
|
op = addobpile(NULL, NULL, NULL);
|
|
// disable linking of holes
|
|
o = addobject(op, what, B_TRUE, B_FALSE, OT_NONE);
|
|
if (o) {
|
|
if (o->material->id == MT_FIRE) {
|
|
*hasfire = B_TRUE;
|
|
}
|
|
killobpile(op);
|
|
return B_TRUE;
|
|
}
|
|
killobpile(op);
|
|
}
|
|
break;
|
|
case VT_CELL:
|
|
if (streq(what, "EMPTY") || streq(what, "SOLID")) {
|
|
return B_TRUE;
|
|
}
|
|
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;
|
|
celltype_t *floortype;
|
|
// 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;
|
|
}
|
|
|
|
// if the map's floor is flammable don't make vaults which have objects made of fire
|
|
floortype = findcelltype(getmapempty(m));
|
|
if (hasflag(floortype->material->flags, F_FLAMMABLE)) {
|
|
if (hasflag(v->flags, F_VAULTHASFIRE)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
// dont make bones vaults while debugging.
|
|
if ((gamemode == GM_GAMESTARTED) && hasjob(player, J_GOD)) {
|
|
if (hasflagval(v->flags, F_VAULTTAG, NA, NA, NA, "bones")) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|