2992 lines
64 KiB
C
2992 lines
64 KiB
C
#include <assert.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"
|
|
|
|
extern map_t *firstmap,*lastmap;
|
|
extern celltype_t *firstcelltype, *lastcelltype;
|
|
extern objecttype_t *objecttype,*lastobjecttype;
|
|
extern int viewx,viewy,vieww,viewh;
|
|
extern lifeform_t *player;
|
|
|
|
extern glyph_t tempglyph;
|
|
|
|
extern enum OBCLASS sortorder[];
|
|
|
|
extern enum ERROR reason;
|
|
|
|
extern enum GAMEMODE gamemode;
|
|
|
|
extern int needredraw;
|
|
|
|
cell_t *addcell(map_t *m, int x, int y) {
|
|
cell_t *cell;
|
|
m->cell[(y*m->w)+x] = malloc(sizeof(cell_t));
|
|
cell = m->cell[(y*m->w)+x];
|
|
cell->map = m;
|
|
cell->x = x;
|
|
cell->y = y;
|
|
setcelltype(cell, CT_WALL);
|
|
cell->visited = B_FALSE;
|
|
cell->obpile = addobpile(NOOWNER, cell);
|
|
cell->lf = NULL;
|
|
cell->roomid = -1;
|
|
cell->lit = L_NOTLIT;
|
|
cell->origlit = L_NOTLIT;
|
|
cell->littimer = 0;
|
|
cell->origlittimer = 0;
|
|
cell->writing = NULL;
|
|
cell->known = B_FALSE;
|
|
cell->knownglyph.ch = ' ';
|
|
cell->knownglyph.colour = C_GREY;
|
|
return cell;
|
|
}
|
|
|
|
void addhomeobs(lifeform_t *lf) {
|
|
flag_t *f;
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
if (f->id == F_HOMEOB) {
|
|
addob(lf->cell->obpile, f->text);
|
|
} else if (f->id == F_HOMELEVOB) {
|
|
int i,amt;
|
|
amt = rnd(f->val[0],f->val[1]);
|
|
for (i = 0; i < amt; i++) {
|
|
cell_t *c;
|
|
// pick new EMPTY random spot
|
|
c = getrandomcell(lf->cell->map);
|
|
while (!cellwalkable(NULL, c, NULL)) {
|
|
c = getrandomcell(lf->cell->map);
|
|
}
|
|
addob(c->obpile, f->text);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
map_t *addmap(void) {
|
|
map_t *a;
|
|
int id;
|
|
int i;
|
|
// is there already a map?
|
|
if (lastmap) {
|
|
id = lastmap->id + 1;
|
|
} else {
|
|
id = 0;
|
|
}
|
|
|
|
// add to the end of the list
|
|
if (firstmap == NULL) {
|
|
firstmap = malloc(sizeof(map_t));
|
|
a = firstmap;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastmap;
|
|
a->next = malloc(sizeof(map_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastmap = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->lf = NULL;
|
|
for (i = 0; i < MAXDIR_ORTH; i++) {
|
|
a->nextmap[i] = -1;
|
|
}
|
|
a->flags = addflagpile(NULL, NULL);
|
|
a->beingcreated = B_TRUE;
|
|
return a;
|
|
}
|
|
|
|
|
|
// when monsters are made during level generation, autogen will be true. otherwise false;
|
|
lifeform_t *addmonster(cell_t *c, enum RACE raceid, int jobok, int amt, int autogen, int *nadded) {
|
|
lifeform_t *lf = NULL;
|
|
race_t *r;
|
|
int db = B_FALSE;
|
|
|
|
if (nadded) *nadded = 0;
|
|
|
|
// ie. don't create mosnters on closed doors!
|
|
if (!cellwalkable(NULL, c, NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
if ((raceid == R_NONE) || (raceid == R_RANDOM)) {
|
|
r = getrandomrace(c, 0);
|
|
} else {
|
|
r = findrace(raceid);
|
|
}
|
|
if (!r) {
|
|
r = getreallyrandomrace();
|
|
}
|
|
|
|
assert(r);
|
|
|
|
|
|
if (db) {
|
|
char buf[BUFLEN];
|
|
sprintf(buf, "start addmonster for %s",r->name);
|
|
dbtimestart(buf);
|
|
}
|
|
|
|
//if (db) dblog("adding rand lf %s to cell %d,%d",r->name,c->x,c->y);
|
|
|
|
if (r) {
|
|
if (db) dbtime("doing lf addition");
|
|
lf = addlf(c, r->id, getrandommonlevel(r, c->map));
|
|
if (db) dbtime("finished lf addition");
|
|
if (lf) {
|
|
flag_t *f;
|
|
|
|
if (nadded) (*nadded)++;
|
|
|
|
if (db) dbtime("checking for job");
|
|
lf->born = B_FALSE;
|
|
if (jobok) {
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
// has a job?
|
|
if (f->id == F_STARTJOB) {
|
|
if (rnd(1,100) <= f->val[0]) {
|
|
givejob(lf, f->val[1]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (autogen) {
|
|
// sometimes start off asleep in new maps
|
|
if (!lfhasflag(lf, F_DEAF) && cansleep(lf)) {
|
|
// TODO: base this on the time, and whether monster is nocturnal
|
|
if (rnd(1,2) == 1) {
|
|
addflag(lf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
f = lfhasflag(lf, F_STARTHIDDENPCT);
|
|
if (f) {
|
|
if (rnd(1,100) <= f->val[0]) {
|
|
// note: if we start off hidden, we have no hide penalty.
|
|
// this is so we can have monsters which start hidden, but
|
|
// don't have the 'hide' skill.
|
|
addflag(lf->flags, F_HIDING, 0, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
// appears in groups?
|
|
if (db) dbtime("handling groups");
|
|
if (autogen) {
|
|
f = hasflag(lf->flags, F_NUMAPPEAR);
|
|
if (f) {
|
|
// override amount
|
|
amt = rnd(f->val[0], f->val[1]);
|
|
}
|
|
if (amt > 1) {
|
|
cell_t *adjcell;
|
|
amt--; // we've already added one
|
|
|
|
//adjcell = c;
|
|
for ( ; amt > 0; amt--) {
|
|
lifeform_t *newlf;
|
|
// find an adjacent cell to one of the newly added monsters,
|
|
// starting with the first one
|
|
adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL);
|
|
// did we find one?
|
|
if (!adjcell) break;
|
|
|
|
newlf = addlf(c, r->id, getrandommonlevel(r, adjcell->map));
|
|
if (!newlf) {
|
|
break;
|
|
}
|
|
if (nadded) (*nadded)++;
|
|
newlf->born = B_FALSE;
|
|
|
|
// initial monster shoudl remember its minions
|
|
addflag(lf->flags, F_MINION, newlf->id, NA, NA, NULL);
|
|
|
|
// if master is asleep, minions will also be asleep
|
|
if (lfhasflag(lf, F_ASLEEP)) {
|
|
addflag(newlf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL);
|
|
}
|
|
|
|
// minions never have certain flags.
|
|
killflagsofid(newlf->flags, F_DEMANDSBRIBE);
|
|
|
|
newlf->born = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// minons?
|
|
// appears in groups?
|
|
if (db) dbtime("handling minions");
|
|
if (autogen) {
|
|
f = hasflag(lf->flags, F_MINIONS);
|
|
if (f) {
|
|
if (rnd(1,100) <= f->val[0]) {
|
|
int n;
|
|
cell_t *adjcell;
|
|
int nminions;
|
|
// override amount
|
|
nminions = rnd(f->val[1], f->val[2]);
|
|
|
|
for (n = 0; n < nminions; n++) {
|
|
lifeform_t *newlf;
|
|
race_t *newr;
|
|
|
|
adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL);
|
|
if (!adjcell) break;
|
|
|
|
newr = findracebyname(f->text);
|
|
if (!newr) break;
|
|
|
|
newlf = addlf(adjcell, newr->id, getrandommonlevel(newr, adjcell->map));
|
|
if (!newlf) break;
|
|
|
|
if (nadded) (*nadded)++;
|
|
|
|
newlf->born = B_FALSE;
|
|
if (lfhasflag(lf, F_ASLEEP)) addflag(newlf->flags, F_ASLEEP, B_TRUE, NA, NA, NULL);
|
|
newlf->born = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (db) dbtime("handling random objects");
|
|
// sometimes give the lf random objects (extra monsters through
|
|
// 'numappears' don't get them.
|
|
if (lfhasflag(lf, F_HUMANOID) && !lfhasflag(lf, F_NOPACK)) {
|
|
if (rnd(1,3) == 1) {
|
|
int nobs = rnd(1,3);
|
|
char buf[BUFLEN];
|
|
int i;
|
|
for (i = 0; i < nobs; i++) {
|
|
if (getrandomob(c->map, buf)) {
|
|
object_t *o;
|
|
o = addob(lf->pack, buf);
|
|
if (o && !canpickup(lf, o, o->amt)) {
|
|
killob(o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (db) dbtime("giving torches");
|
|
// humanoids on dark levels which can't see will probably have some
|
|
// kind of light producing device
|
|
if (!islit(c) && !hasflag(lf->flags, F_SEEINDARK) && !hasflag(lf->flags, F_TREMORSENSE)) {
|
|
if (lfhasflag(lf, F_HUMANOID)) {
|
|
objecttype_t *ot, *poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
// get a random light-producing object
|
|
for (ot = objecttype ; ot ; ot = ot->next) {
|
|
if (hasflag(ot->flags, F_OPERABLE) &&
|
|
hasflagval(ot->flags, F_ACTIVATECONFER, F_PRODUCESLIGHT, NA, NA, NULL)) {
|
|
poss[nposs++] = ot;
|
|
}
|
|
}
|
|
if (nposs > 0) {
|
|
ot = poss[rnd(0,nposs-1)];
|
|
} else {
|
|
ot = NULL;
|
|
}
|
|
if (ot) {
|
|
addob(lf->pack, ot->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
lf->born = B_TRUE;
|
|
} // end if lf
|
|
}
|
|
|
|
if (db) dbtimeend("finished addmonster");
|
|
|
|
return lf;
|
|
}
|
|
|
|
object_t *addrandomob(cell_t *c) {
|
|
char buf[BUFLEN];
|
|
int db = B_FALSE;
|
|
object_t *o = NULL;
|
|
|
|
if (c->type->solid) {
|
|
return NULL;
|
|
}
|
|
|
|
if (getrandomob(c->map, buf)) {
|
|
if (db) dblog("adding rand obj %s to cell %d,%d",buf,c->x,c->y);
|
|
o = addob(c->obpile, buf);
|
|
}
|
|
return o;
|
|
}
|
|
|
|
int addrandomthing(cell_t *c, int obchance, int *nadded) {
|
|
int rv = TT_NONE;
|
|
// if there's already someone there,
|
|
// then add an object.
|
|
if (c->lf || (rnd(1,100) <= obchance)) {
|
|
object_t *o;
|
|
// object
|
|
o = addrandomob(c);
|
|
if (o) {
|
|
if (nadded) *nadded = o->amt;
|
|
rv = TT_OBJECT;
|
|
}
|
|
} else {
|
|
// monster
|
|
if (addmonster(c, R_RANDOM, B_TRUE, 1, B_TRUE, nadded)) {
|
|
rv = TT_MONSTER;
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int cellhaslos(cell_t *c1, cell_t *dest) {
|
|
int deltax, deltay;
|
|
int numpixels;
|
|
int d;
|
|
int dinc1,dinc2,xinc1,xinc2,yinc1,yinc2;
|
|
int xinc,yinc,dinc;
|
|
int i;
|
|
int x1,y1;
|
|
int x;
|
|
int y;
|
|
//int wentuphill = B_FALSE;
|
|
//int origheight;
|
|
//int shopwall;
|
|
int x2,y2;
|
|
map_t *map;
|
|
|
|
if (!dest) return B_FALSE;
|
|
|
|
// let the player see when dead, otherwise the screen wil
|
|
// go black when "You die" appears.
|
|
map = c1->map;
|
|
|
|
|
|
x1 = c1->x;
|
|
y1 = c1->y;
|
|
x2 = dest->x;
|
|
y2 = dest->y;
|
|
|
|
|
|
|
|
deltax = (x2 - x1);
|
|
if (deltax < 0) deltax = -deltax;
|
|
deltay = (y2 - y1);
|
|
if (deltay < 0) deltay = -deltay;
|
|
|
|
// can always see your own cell
|
|
if ((deltax == 0) && (deltay == 0)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (deltax >= deltay) {
|
|
numpixels = deltax + 1;
|
|
d = (deltay*2) - deltax;
|
|
dinc1 = deltay << 1;
|
|
dinc2 = (deltay-deltax) << 1;
|
|
xinc1 = 1;
|
|
xinc2 = 1;
|
|
yinc1 = 0;
|
|
yinc2 = 1;
|
|
} else {
|
|
numpixels = deltay + 1;
|
|
d = (deltax*2) - deltay;
|
|
dinc1 = deltax << 1;
|
|
dinc2 = (deltax - deltay) << 1;
|
|
xinc1 = 0;
|
|
xinc2 = 1;
|
|
yinc1 = 1;
|
|
yinc2 = 1;
|
|
}
|
|
|
|
if (x1 > x2) {
|
|
xinc1 = - xinc1;
|
|
xinc2 = - xinc2;
|
|
}
|
|
if (y1 > y2) {
|
|
yinc1 = - yinc1;
|
|
yinc2 = - yinc2;
|
|
}
|
|
|
|
x = x1; y = y1;
|
|
|
|
for (i = 0; i < numpixels ; i++) {
|
|
cell_t *cell;
|
|
|
|
// don't need to move out of the last one
|
|
if ((x == x2) && (y == y2)) {
|
|
break;
|
|
}
|
|
|
|
if (d < 0) {
|
|
xinc = xinc1;
|
|
yinc = yinc1;
|
|
dinc = dinc1;
|
|
} else {
|
|
xinc = xinc2;
|
|
yinc = yinc2;
|
|
dinc = dinc2;
|
|
}
|
|
|
|
|
|
// you can always see your own cell
|
|
if (i != 0) {
|
|
// solid cells stop los - but if you are standing on a solid
|
|
// cell you can still see out.
|
|
cell = getcellat(map, x, y);
|
|
if (!cell->type->transparent) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
/*
|
|
// check for smoke
|
|
if ((x != x1) || (y != y1)) { // if not in first cell
|
|
if (hasopaqueobject(viewer, x,y,z) && (getheight(x,y,z) >= origheight)) {
|
|
if (!hasproplf(viewer, P_SEEINSMOKE)) {
|
|
return B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
// check for objects which block view
|
|
if (hasobwithflag(cell->obpile, F_BLOCKSVIEW)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
// move to next cell
|
|
d += dinc;
|
|
x += xinc;
|
|
y += yinc;
|
|
}
|
|
|
|
// made it to the target cell!
|
|
return B_TRUE;
|
|
}
|
|
|
|
cell_t *getcellat(map_t *map, int x, int y) {
|
|
if (!isonmap(map, x, y)) return NULL;
|
|
return map->cell[y*map->w + x];
|
|
}
|
|
|
|
int getcelldist(cell_t *src, cell_t *dst) {
|
|
double xd,yd;
|
|
// use pythag
|
|
xd = abs(dst->x - src->x);
|
|
yd = abs(dst->y - src->y);
|
|
return (int)sqrt(xd*xd + yd*yd);
|
|
}
|
|
|
|
int getcelldistorth(cell_t *src, cell_t *dst) { // add x/y
|
|
return abs(dst->x - src->x) + abs(dst->y - src->y);
|
|
}
|
|
|
|
//populates 'g' with the contects of cell 'c', as seen by 'viewer'
|
|
// if we don't have LOS to there, set g->ch to NUL.
|
|
void getcellglyph(glyph_t *g, cell_t *c, lifeform_t *viewer) {
|
|
glyph_t tempgl;
|
|
|
|
// default
|
|
g->ch = ' ';
|
|
g->colour = C_RED;
|
|
|
|
if (haslos(viewer, c)) {
|
|
// show cell contents
|
|
if (c->lf && cansee(viewer, c->lf)) { // lifeform here which we can see
|
|
// draw the lf's race glyph
|
|
*g = *(getlfglyph(c->lf));
|
|
if (isprone(c->lf)) {
|
|
g->ch = flip(g->ch);
|
|
}
|
|
return;
|
|
} else { // we can see the floor here
|
|
void *thing;
|
|
// scanned lf here?
|
|
if (isinscanrange(c, &thing, NULL, &tempgl) == TT_MONSTER) {
|
|
*g = tempgl;
|
|
//mvwprintw(gamewin, y-viewy, x-viewx, "%c", glyph);
|
|
//drawglyph(&glyph, x, y);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// objects here?
|
|
if ((countobs(c->obpile, B_FALSE) > 0)) {
|
|
object_t *o;
|
|
|
|
// draw highest object in sort order
|
|
o = gettopobject(c);
|
|
if (o && !hasflag(o->flags, F_NOGLYPH)) {
|
|
// return the object's glyph
|
|
*g = *(getglyph(o));
|
|
} else {
|
|
// objects here, but we can't see them. draw the cell.
|
|
*g = c->type->glyph;
|
|
if (!islit(c)) {
|
|
g->colour = C_BLUE;
|
|
}
|
|
}
|
|
} else {
|
|
// draw cell normally
|
|
//drawcell(cell, x, y);
|
|
*g = c->type->glyph;
|
|
if (!islit(c)) {
|
|
g->colour = C_BLUE;
|
|
}
|
|
}
|
|
} else { // can't see the cell
|
|
void *thing;
|
|
//drawscannedcell(cell, x-viewx, y-viewy);
|
|
switch (isinscanrange(c, &thing, NULL, &tempgl)) {
|
|
case TT_MONSTER:
|
|
case TT_OBJECT:
|
|
*g = tempgl;
|
|
break;
|
|
default:
|
|
if (c->known) {
|
|
// copy from cell
|
|
/*
|
|
*g = c->type->glyph;
|
|
*/
|
|
*g = c->knownglyph;
|
|
if (g->ch == '.') {
|
|
g->ch = ' ';
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
enum CELLTYPE getemptycelltype(enum HABITAT hab) {
|
|
switch (hab) {
|
|
case H_DUNGEON:
|
|
return CT_CORRIDOR;
|
|
case H_FOREST:
|
|
return CT_GRASS;
|
|
default:
|
|
break;
|
|
}
|
|
return CT_CORRIDOR;
|
|
}
|
|
|
|
enum CELLTYPE getwallcelltype(enum HABITAT hab) {
|
|
switch (hab) {
|
|
case H_DUNGEON:
|
|
return CT_WALL;
|
|
default:
|
|
break;
|
|
}
|
|
return CT_WALL;
|
|
}
|
|
|
|
flag_t *getmapcoords(map_t *m, int *x, int *y) {
|
|
flag_t *f;
|
|
f = hasflag(m->flags, F_MAPCOORDS);
|
|
if (f) {
|
|
if (x) *x = f->val[0];
|
|
if (y) *y = f->val[1];
|
|
return f;
|
|
}
|
|
if (x) *x = -999;
|
|
if (y) *y = -999;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int getmapdifficulty(map_t *m) {
|
|
int diff = 1;
|
|
if (m) {
|
|
if (m->region == RG_WORLDMAP) {
|
|
int x,y;
|
|
// depth is distance from 0,0
|
|
getmapcoords(m, &x, &y);
|
|
diff = (abs(x) + abs(y))+1;
|
|
} else {
|
|
diff = m->depth;
|
|
}
|
|
} else {
|
|
diff = rnd(1,MAXDEPTH);
|
|
}
|
|
return diff;
|
|
}
|
|
|
|
object_t *gettopobject(cell_t *where) {
|
|
object_t *o,*oo = NULL;
|
|
enum LFSIZE largest = SZ_MINI;
|
|
int c;
|
|
|
|
// get largest known impassable object first
|
|
for (o = where->obpile->first ; o ; o = o->next) {
|
|
flag_t *f;
|
|
// ignore hidden traps, but not secret doors
|
|
if (hasflag(o->flags, F_SECRET) && !isdoor(o, NULL)) {
|
|
} else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) {
|
|
} else {
|
|
f = hasflag(o->flags, F_IMPASSABLE);
|
|
if (f && (f->val[0] > largest)) {
|
|
largest = f->val[0];
|
|
oo = o;
|
|
}
|
|
}
|
|
}
|
|
if (oo) {
|
|
return oo;
|
|
}
|
|
|
|
// otherwise draw highest object in sort order
|
|
c = 0;
|
|
while (sortorder[c] != OC_NULL) {
|
|
// check each object against this ob class
|
|
// count backwards so more recently dropped objects
|
|
// appear first.
|
|
for (o = where->obpile->last ; o ; o = o->prev) {
|
|
if (o->type->obclass->id == sortorder[c]) {
|
|
if (hasflag(o->flags, F_SECRET)) {
|
|
} else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) {
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
}
|
|
c++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void calclight(map_t *map) {
|
|
int x,y;
|
|
cell_t *c;
|
|
int i;
|
|
|
|
// remember lit values for cells in player's los
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
for (i = 0; i < player->nlos; i++) {
|
|
player->los[i]->lastlit = player->los[i]->lit;
|
|
}
|
|
}
|
|
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x,y);
|
|
//if (c && (c->lit == L_PERMLIGHT) && (c->lit != L_PERMDARK)) c->lit = B_FALSE;
|
|
c->lastlit = c->lit;
|
|
if (c && (c->lit != L_PERMLIGHT) && (c->lit != L_PERMDARK)) c->lit = B_FALSE;
|
|
}
|
|
}
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x,y);
|
|
if (c) {
|
|
int radius;
|
|
object_t *o;
|
|
|
|
// lit based on depth
|
|
if (isoutdoors(map)) {
|
|
int hours,mins,secs;
|
|
splittime(&hours,&mins,&secs);
|
|
if ((hours < 5) || (hours >= 19)) {
|
|
// ie. nighttime, after 7pm or before 5am
|
|
} else {
|
|
// ie. daytime
|
|
makelit(c, L_PERMLIGHT, -1);
|
|
}
|
|
} else {
|
|
if ((map->depth <= 5) && (c->lit != L_PERMDARK)) {
|
|
makelit(c, L_PERMLIGHT, -1);
|
|
}
|
|
}
|
|
|
|
// TODO: has dark producing lf?
|
|
// TODO: has dark producing object?
|
|
|
|
// has lightproducing lf? (ie.hasflag f_produceslight)
|
|
if (c->lf) {
|
|
radius = lfproduceslight(c->lf);
|
|
if (radius) {
|
|
makelitradius(c, radius, L_TEMP, -1);
|
|
}
|
|
}
|
|
// has light-producing object on ground?
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
radius = obproduceslight(o);
|
|
if (radius) {
|
|
makelitradius(c, radius, L_TEMP, -1);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
// did any lit values within player's los change?
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
for (i = 0; i < player->nlos; i++) {
|
|
if (player->los[i]->lastlit != player->los[i]->lit) {
|
|
needredraw = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int calcroompos(map_t *map, int w, int h, int *bx, int *by) {
|
|
int x,y;
|
|
int bestx = -1, besty = -1;
|
|
int valid = B_FALSE;
|
|
int bestscore = 9999;
|
|
cell_t *cell;
|
|
|
|
// try placing room at all positions
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
// would the room fit here?
|
|
if ( ((x + (w-1)) <= (map->w-1)) &&
|
|
((y + (h-1)) <= (map->h-1))) {
|
|
int score = 0;
|
|
int rx,ry;
|
|
int notpossible = B_FALSE;
|
|
valid = B_FALSE;
|
|
// calculate score based on cells in room
|
|
for (ry = y; (ry < y+h) && (!notpossible); ry++) {
|
|
for (rx = x; (rx < x+w) && (!notpossible); rx++) {
|
|
cell = getcellat(map, rx,ry);
|
|
// is this cell adjacent to an empty cell and not a
|
|
// corner (ie. a valid door location)
|
|
if (countcellexits(cell)) {
|
|
score++;
|
|
if ( ((ry == y) && (rx == x)) ||
|
|
((ry == y) && (rx == (x+w-1))) ||
|
|
((ry == y+h-1) && (rx == x)) ||
|
|
((ry == y+h-1) && (rx == (x+w-1))) ) {
|
|
// corner.
|
|
} else {
|
|
// not corner
|
|
valid = B_TRUE;
|
|
}
|
|
}
|
|
// is this cell empty itself?
|
|
if (!cell->type->solid) score += 3;
|
|
// avoid being adjacent to other room walls
|
|
if (countcellexits(cell)) score++;
|
|
score += (countadjcellsoftype(cell, CT_ROOMWALL)*3);
|
|
// overlapping another room?
|
|
if (cell->type->id == CT_ROOM) {
|
|
valid = B_FALSE;
|
|
notpossible = B_TRUE;
|
|
}
|
|
if (cell->type->id == CT_ROOMWALL) {
|
|
valid = B_FALSE;
|
|
notpossible = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
if (valid && (score != 0) && (score < bestscore)) {
|
|
bestscore = score;
|
|
bestx = x;
|
|
besty = y;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*bx = bestx;
|
|
*by = besty;
|
|
|
|
if ((bestx == -1) || (besty == -1)) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int countadjcellswithflag(cell_t *cell, enum FLAG fid) {
|
|
int d;
|
|
int count = 0;
|
|
cell_t *newcell;
|
|
for (d = D_N; d < MAXDIR_ORTH; d++) {
|
|
newcell = getcellindir(cell, d);
|
|
if (newcell && hasobwithflag(cell->obpile, fid)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countadjcellsoftype(cell_t *cell, int id) {
|
|
int d;
|
|
int count = 0;
|
|
cell_t *newcell;
|
|
for (d = D_N; d < MAXDIR_ORTH; d++) {
|
|
newcell = getcellindir(cell, d);
|
|
if (newcell && newcell->type->id == id) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countcellexits(cell_t *cell) {
|
|
int d;
|
|
int exits = 0;
|
|
cell_t *newcell;
|
|
assert(cell);
|
|
for (d = D_N; d < MAXDIR_ORTH; d++) {
|
|
newcell = getcellindir(cell, d);
|
|
if (newcell && !newcell->type->solid) {
|
|
exits++;
|
|
}
|
|
}
|
|
return exits;
|
|
}
|
|
|
|
void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir) {
|
|
int wantrooms = B_TRUE;
|
|
int d;
|
|
int x,y;
|
|
int i;
|
|
int done,unused;
|
|
int dir;
|
|
int lastdir;
|
|
int numrooms = 0;
|
|
int roomw[MAXROOMS],roomh[MAXROOMS];
|
|
//int roomspecial[MAX_MAPROOMS];
|
|
int minroomw = MIN_ROOMW;
|
|
int minroomh = MIN_ROOMH;
|
|
int maxroomw = MAX_ROOMW;
|
|
int maxroomh = MAX_ROOMH;
|
|
int bestx,besty;
|
|
int w,h;
|
|
//int startdir,forcex,forcey,ntries;
|
|
cell_t *cell, *c;
|
|
object_t *o;
|
|
int db = B_FALSE;
|
|
|
|
// parameters
|
|
int turnpct = DEF_TURNPCT;
|
|
int sparseness = DEF_SPARSENESS;
|
|
int looppct = DEF_LOOPPCT;
|
|
int minrooms = MINROOMS;
|
|
int maxrooms = MAXROOMS;
|
|
|
|
int moved = 0;
|
|
|
|
enum CELLTYPE emptycell;
|
|
|
|
// fill entire maze with walls
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
addcell(map, x, y);
|
|
}
|
|
}
|
|
|
|
|
|
// what kind of cells will 'empty' ones be?
|
|
emptycell = getemptycelltype(map->habitat);
|
|
|
|
// pick initial random spot
|
|
cell = getrandomcell(map);
|
|
setcelltype(cell, emptycell);
|
|
cell->visited = B_TRUE;
|
|
//if (db) printf("- Starting (%d,%d)\n",cell->x, cell->y);
|
|
|
|
lastdir = D_UNKNOWN;
|
|
done = B_FALSE;
|
|
|
|
dir = D_NONE;
|
|
|
|
while (!done) {
|
|
// get random direction based on turnpct
|
|
dir = D_NONE;
|
|
while (dir == D_NONE) {
|
|
int badcount;
|
|
//if (db) printf("- At (%d,%d), moved %d, finding new direction...\n",cell->x, cell->y, moved);
|
|
|
|
dir = getnewdigdir(cell, lastdir, (moved < 2) ? 0 : turnpct, &moved);
|
|
|
|
badcount = 0;
|
|
while (dir == D_NONE) {
|
|
badcount++;
|
|
if (badcount > 10) {
|
|
// finish!
|
|
done = B_TRUE;
|
|
break;
|
|
}
|
|
|
|
// pick new EMPTY random spot
|
|
cell = getrandomcell(map);
|
|
while (!isempty(cell)) {
|
|
cell = getrandomcell(map);
|
|
}
|
|
//if (db) printf("--- Couldn't find a valid direction. Jumped to (%d,%d).\n",cell->x, cell->y);
|
|
// pick a new random dir
|
|
dir = getnewdigdir(cell, lastdir, turnpct, &moved);
|
|
}
|
|
if (!done) {
|
|
//if (db) printf("- Digging %s from (%d,%d).\n",getdirname(dir),cell->x, cell->y);
|
|
}
|
|
}
|
|
|
|
if (!done) {
|
|
// move to adjacent cell in the given direction
|
|
cell = getcellindir(cell, dir);
|
|
//if (db) printf("- Now at (%d,%d)\n",cell->x, cell->y);
|
|
moved++;
|
|
|
|
// blank it
|
|
setcelltype(cell,emptycell);
|
|
cell->visited = B_TRUE;
|
|
// mark surrounding cells as visited
|
|
for (d = DC_N; d < MAXDIR_COMPASS; d++) {
|
|
cell_t *thiscell;
|
|
thiscell = getcellindir(cell, d);
|
|
if (thiscell) {
|
|
//if (db) printf("* Marking surrounding cell in dir %d (%d,%d) as visited.\n",d, thiscell->x, thiscell->y);
|
|
thiscell->visited = B_TRUE;
|
|
}
|
|
}
|
|
// remember last direction
|
|
lastdir = dir;
|
|
|
|
// check if we have visited all valid cells
|
|
unused = 0;
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
cell_t *thiscell;
|
|
thiscell = getcellat(map, x, y);
|
|
if (!thiscell->visited) {
|
|
unused++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!unused) {
|
|
done = B_TRUE;
|
|
}
|
|
}
|
|
|
|
//printf("%d unused cell(s)\n",unused);
|
|
//dumpmap(map);
|
|
//getchar();
|
|
|
|
}
|
|
|
|
// use sparseness to cut down dead ends
|
|
for (i = 0; i < sparseness; i++) {
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
cell = getcellat(map, x,y);
|
|
if (countcellexits(cell) == 1) {
|
|
// erase this cell
|
|
setcelltype(cell, CT_WALL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// introduce loops
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
cell = getcellat(map, x, y);
|
|
if (!cell->type->solid && countcellexits(cell) == 1) {
|
|
// dead end - maybe make loop from here
|
|
if (rnd(1,100) <= looppct) {
|
|
int connected = B_FALSE;
|
|
int loopok = B_TRUE;
|
|
int dir;
|
|
|
|
// pick a random directory
|
|
dir = getnewdigdir(cell, D_UNKNOWN,100, &moved);
|
|
|
|
if (dir == D_NONE) {
|
|
// can't make a loop from here.
|
|
loopok = B_FALSE;
|
|
} else {
|
|
int tries = 0;
|
|
// if we go this dir, will we hit a
|
|
// corridor?
|
|
while (!isloopdirok(cell, dir)) {
|
|
tries++;
|
|
// tried every direction?
|
|
if (tries >= MAXDIR_ORTH) {
|
|
loopok = B_FALSE;
|
|
break;
|
|
}
|
|
// turn...
|
|
if (++dir >= MAXDIR_ORTH) {
|
|
dir = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (loopok) {
|
|
// keep digging until we hit another corridor
|
|
while (!connected) {
|
|
cell_t *newcell;
|
|
// find adjacent cell in the given direction
|
|
newcell = getcellindir(cell, dir);
|
|
if (!newcell) {
|
|
connected = B_TRUE;
|
|
} else {
|
|
// have we hit another corridor yet?
|
|
if (!newcell->type->solid) {
|
|
connected = B_TRUE;
|
|
} else {
|
|
// blank adjacent cell
|
|
setcelltype(newcell, emptycell);
|
|
newcell->visited = B_TRUE;
|
|
}
|
|
cell = newcell;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// create rooms
|
|
if (wantrooms) {
|
|
numrooms = rnd(minrooms, maxrooms);
|
|
|
|
//printf("using %d rooms\n",numrooms);
|
|
//dblog("Adding %d rooms...\n",numrooms);
|
|
for (i = 0; i < numrooms; i++) {
|
|
// select random width/height
|
|
w = rnd(minroomw, maxroomw);
|
|
h = rnd(minroomh, maxroomh);
|
|
|
|
roomw[i] = w;
|
|
roomh[i] = h;
|
|
|
|
if (calcroompos(map, w, h, &bestx, &besty)) {
|
|
//printf("** couldn't make room!\n");
|
|
} else {
|
|
// we now have the room position - fill it in
|
|
createroom(map, bestx,besty, w,h, i);
|
|
|
|
/*
|
|
if (getrand(1,100) <= CH_SPECIALROOM) {
|
|
int curpos;
|
|
int roomid;
|
|
roomid = getrandomspecialroom(wreck->mazelev[curz].type);
|
|
for (y = besty; y <= (besty + (h-1)); y++) {
|
|
for (x = bestx; x <= (bestx + (w-1)); x++) {
|
|
curpos = y*MAZEW+x;
|
|
wreck->mazelev[curz].maze[curpos].floorver = roomid;
|
|
}
|
|
}
|
|
roomspecial[i] = roomid;
|
|
} else {
|
|
roomspecial[i] = B_FALSE;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// add staircases - dungeons always have an up and down stairs
|
|
for (i = 0; i < 3; i++) {
|
|
// add up stairs
|
|
c = NULL;
|
|
while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) {
|
|
c = getrandomroomcell(map, ANYROOM);
|
|
}
|
|
o = addob(c->obpile, "staircase going up");
|
|
linkstairs(o);
|
|
// first dungeon level has barriers over the exit stairs
|
|
if ((map->region == RG_FIRSTDUNGEON) && (map->depth == 1)) {
|
|
if (c->lf) killlf(c->lf);
|
|
addob(c->obpile, "magical barrier");
|
|
/*
|
|
int d;
|
|
// surround stairs with barriers
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
cell_t *newc;
|
|
newc = getcellindir(c, d);
|
|
|
|
if (newc && !newc->type->solid) {
|
|
// kill lfs there
|
|
if (newc->lf) killlf(newc->lf);
|
|
// add a barrier
|
|
addob(newc->obpile, "magical barrier");
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
c = NULL;
|
|
while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) {
|
|
c = getrandomroomcell(map, ANYROOM);
|
|
}
|
|
o = addob(c->obpile, "staircase going down");
|
|
linkstairs(o);
|
|
}
|
|
|
|
// add pillars & objects & monsters to rooms
|
|
if (wantrooms && (numrooms > 0)) {
|
|
for (i = 0; i < numrooms; i++) {
|
|
int numobsmin,numobsmax,numobs,n;
|
|
int maxpillars;
|
|
|
|
//dblog("Adding obs to room %d/%d",i+1,numrooms);
|
|
maxpillars = (roomw[i] / 4) + (roomh[i] / 4);
|
|
// add pillars first
|
|
if ((maxpillars > 0) && (rnd(1,100) <= CH_PILLAR)) {
|
|
int n;
|
|
int numpillars;
|
|
numpillars = rnd(1,maxpillars);
|
|
if (db) dblog("--> Will add %d pillars",numpillars);
|
|
for (n = 0; n < numpillars;n++ ) {
|
|
//dblog("----> Adding pillar %d/%d",n+1,numpillars);
|
|
cell_t *c;
|
|
c = getrandomroomcell(map, i);
|
|
|
|
if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) {
|
|
setcelltype(cell, CT_WALL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (roomspecial[i]) {
|
|
// chance is increased
|
|
numobsmin = (roomw[i]*roomh[i]) / 4 ;
|
|
numobsmax = (roomw[i]*roomh[i]) / 2 ;
|
|
} else {
|
|
*/
|
|
numobsmin = 0;
|
|
//numobsmax = MAXOF(roomw[i],roomh[i]) / 2;
|
|
numobsmax = MAXOF(roomw[i],roomh[i]);
|
|
|
|
//}
|
|
|
|
// then objects/monsters
|
|
if (numobsmax <= numobsmin) {
|
|
numobs = numobsmin;
|
|
} else {
|
|
numobs = rnd(numobsmin,numobsmax);
|
|
}
|
|
if (db) dblog("--> Will add %d objects to room %d (of %d)",numobs,i,numrooms);
|
|
for (n = 0 ; n < numobs; n++) {
|
|
int ntries = 0;
|
|
int nmonsters = 0;
|
|
cell_t *c;
|
|
done = B_FALSE;
|
|
while (!done) {
|
|
c = getrandomroomcell(map, i);
|
|
// if nothing there
|
|
if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) {
|
|
int obchance;
|
|
int nadded = 0;
|
|
|
|
// limit # monster per room to depth+1
|
|
if (nmonsters >= (depth+1)) {
|
|
obchance = 100;
|
|
} else {
|
|
// slightly more chance of objects in rooms
|
|
obchance = getobchance(map->habitat) + 10;
|
|
}
|
|
|
|
if (addrandomthing(c,obchance, &nadded) == TT_MONSTER) {
|
|
nmonsters += nadded;
|
|
}
|
|
done = B_TRUE;
|
|
} else {
|
|
ntries++;
|
|
}
|
|
|
|
if (ntries >= numobs) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (db) dblog("Finished adding objects.");
|
|
}
|
|
|
|
void createforest(map_t *map, int depth, map_t *parentmap, int exitdir) {
|
|
int x,y;
|
|
enum CELLTYPE emptycell;
|
|
int i;
|
|
int ntrees;
|
|
int density;
|
|
cell_t *c;
|
|
char buf[BUFLEN];
|
|
int nclearings;
|
|
//object_t *o;
|
|
|
|
// what kind of cells will 'empty' ones be?
|
|
emptycell = getemptycelltype(map->habitat);
|
|
// fill entire maze with walls
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = addcell(map, x, y);
|
|
setcelltype(c, emptycell);
|
|
}
|
|
}
|
|
|
|
// determine density
|
|
density = rnd(40,70);
|
|
// add a plant to density percent of cells
|
|
ntrees = (int)(((float)density/100.0) * (float)(map->w*map->h));
|
|
for (i = 0; i < ntrees; i++) {
|
|
c = getrandomcell(map);
|
|
while (c->lf) {
|
|
c = getrandomcell(map);
|
|
}
|
|
switch (rnd(0,1)) {
|
|
default: case 0: strcpy(buf, "tree"); break;
|
|
case 1: strcpy(buf, "shrub"); break;
|
|
}
|
|
addob(c->obpile, buf);
|
|
}
|
|
|
|
// clearings
|
|
nclearings = rnd(0,5);
|
|
for (i = 0; i < nclearings; i++) {
|
|
int w;
|
|
c = getrandomcell(map);
|
|
w = rnd(MINCLEARINGRADIUS,MAXCLEARINGRADIUS);
|
|
for (y = c->y - w; y <= c->y + w; y++) {
|
|
for (x = c->x - w; x <= c->x + w; x++) {
|
|
cell_t *newc;
|
|
int dist = 999;
|
|
newc = getcellat(map, x, y);
|
|
|
|
|
|
if (newc) {
|
|
dist = getcelldistorth(newc, c);
|
|
if (dist <= w) {
|
|
int dirtchance;
|
|
// kill all obs here
|
|
while (newc->obpile->first) killob(newc->obpile->first);
|
|
|
|
// change it into dirt.
|
|
// ie. at centre (dist=0) dirt chance is 100%
|
|
// ie. at max distance, dirt chance is 30%
|
|
dirtchance = 100 - (((float)dist / (float)w) * 70);
|
|
if (rnd(1,100) <= dirtchance) {
|
|
setcelltype(newc, CT_DIRT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add monsters
|
|
}
|
|
|
|
/*
|
|
seed = random number seed
|
|
turnpct = percentage change of a corridor turning
|
|
sparseness = how many times to chop off dead ends
|
|
looppct = percentage change of turning dead-end into loop
|
|
maxrooms = max # of rooms
|
|
*/
|
|
void createmap(map_t *map, int depth, int region, int habitat, map_t *parentmap, int exitdir) {
|
|
lifeform_t *lf;
|
|
map_t *m;
|
|
char buf[BUFLEN];
|
|
int i,x,y;
|
|
int firstworldmap = B_FALSE;
|
|
// determine habitat?
|
|
if (habitat == AUTO) {
|
|
if (depth > 0) {
|
|
habitat = H_DUNGEON;
|
|
} else if (depth == 0) {
|
|
habitat = H_FOREST;
|
|
}
|
|
}
|
|
|
|
if (region == RG_WORLDMAP) {
|
|
int found = B_FALSE;
|
|
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
if ((m != map) && (m->region == RG_WORLDMAP)) {
|
|
found = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
firstworldmap = B_TRUE;
|
|
}
|
|
}
|
|
|
|
map->beingcreated = B_TRUE;
|
|
|
|
map->depth = depth;
|
|
map->region = region;
|
|
|
|
sprintf(buf, "Region %d (#%d)",map->region, map->id);
|
|
map->name = strdup(buf);
|
|
map->habitat = habitat;
|
|
|
|
// link to other maps if required.
|
|
// default to no links
|
|
for (i = D_UP; i <= D_DOWN; i++) {
|
|
map->nextmap[i] = -1;
|
|
}
|
|
|
|
// look for adjacent maps above/below this one
|
|
for (i = depth-1; i <= depth+1; i += 2) {
|
|
map_t *othermap;
|
|
othermap = findregionmap(map->region, i);
|
|
// TODO: set upmap/downmap for use later on.
|
|
if (othermap) {
|
|
if (i == (depth-1)) {
|
|
map->nextmap[D_UP] = othermap->id;
|
|
} else {
|
|
map->nextmap[D_DOWN] = othermap->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
// did we come from a previous map?
|
|
if (parentmap) {
|
|
parentmap->nextmap[exitdir] = map->id;
|
|
for (i = 0; i < MAXDIR_ORTH; i++) {
|
|
if (parentmap && (i == diropposite(exitdir)) ) {
|
|
map->nextmap[i] = parentmap->id;
|
|
} else {
|
|
map->nextmap[i] = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
map->w = MAX_MAPW;
|
|
map->h = MAX_MAPH;
|
|
|
|
// map depth?
|
|
map->depth = depth;
|
|
|
|
// rememebr seed
|
|
map->seed = rand() % 65535;
|
|
srand(map->seed);
|
|
|
|
// set map coords
|
|
if (firstworldmap) {
|
|
addflag(map->flags, F_MAPCOORDS, 0, 0, NA, NULL);
|
|
} else {
|
|
// set mapcoords if not already done.
|
|
if (!hasflag(map->flags, F_MAPCOORDS)) {
|
|
x = -999;
|
|
y = -999;
|
|
if (parentmap) {
|
|
getmapcoords(parentmap, &x, &y);
|
|
assert((x != -999) && (y != -999));
|
|
switch (exitdir) {
|
|
case D_N:
|
|
y--;
|
|
break;
|
|
case D_E:
|
|
x++;
|
|
break;
|
|
case D_S:
|
|
y++;
|
|
break;
|
|
case D_W:
|
|
x--;
|
|
break;
|
|
default:
|
|
// no change
|
|
break;
|
|
}
|
|
addflag(map->flags, F_MAPCOORDS, x, y, NA, NULL);
|
|
} else {
|
|
// set it based on something else...
|
|
if (region == RG_FIRSTDUNGEON) {
|
|
x = 0;
|
|
y = 0;
|
|
} else {
|
|
// find another map of this region and set it.
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
if ((m != map) && (m->region == region)) {
|
|
flag_t *ff;
|
|
ff = hasflag(m->flags, F_MAPCOORDS);
|
|
if (ff) {
|
|
x = ff->val[0];
|
|
y = ff->val[1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
assert(x != -999);
|
|
assert(y != -999);
|
|
addflag(map->flags, F_MAPCOORDS, x, y, NA, NULL);
|
|
}
|
|
}
|
|
|
|
// build it...
|
|
if (habitat == H_DUNGEON) {
|
|
createdungeon(map, depth, parentmap, exitdir);
|
|
} else if (habitat == H_FOREST) {
|
|
createforest(map, depth, parentmap, exitdir);
|
|
}
|
|
|
|
// add home objects
|
|
for (lf = map->lf ; lf ; lf = lf->next) {
|
|
addhomeobs(lf);
|
|
}
|
|
|
|
|
|
|
|
// if this is the first world map, add stairs to the dungeon
|
|
if (firstworldmap) {
|
|
// ie. this is the first forest map
|
|
map_t *firstdungeon;
|
|
|
|
// link to first dungeon map
|
|
firstdungeon = findregionmap(RG_FIRSTDUNGEON, 1);
|
|
assert(firstdungeon);
|
|
map->nextmap[D_DOWN] = firstdungeon->id;
|
|
|
|
// add stairs going down to dungeon
|
|
for (i = 0; i < 3; i++) {
|
|
cell_t *c;
|
|
object_t *o;
|
|
c = NULL;
|
|
while (!c || !cellwalkable(NULL, c, NULL)) {
|
|
c = getrandomcell(map);
|
|
}
|
|
o = addob(c->obpile, "staircase going down");
|
|
linkstairs(o);
|
|
}
|
|
}
|
|
|
|
// join up any unlinked staircases in this map.
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
cell_t *c;
|
|
object_t *o;
|
|
c = getcellat(map, x, y);
|
|
o = hasobwithflag(c->obpile, F_CLIMBABLE);
|
|
if (o && !getstairdestination(o)) {
|
|
linkstairs(o);
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: search for unlinked pits/holes in roof in adjacent maps
|
|
// if we find any, add a matching end as close as we can in this map.
|
|
|
|
// add random objects and monsters
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
cell_t *c;
|
|
c = getcellat(map, x, y);
|
|
if (c && isempty(c)) {
|
|
if (rnd(1,100) <= getthingchance(map->habitat)) {
|
|
addrandomthing(c, getobchance(map->habitat), NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// look for adjacent maps
|
|
getmapcoords(map, &x, &y);
|
|
assert(x != -999);
|
|
assert(y != -999);
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
if ((m != map) && (m->region == map->region)) {
|
|
int thisx,thisy;
|
|
getmapcoords(m, &thisx, &thisy);
|
|
if (map->nextmap[D_N] == -1) {
|
|
if (thisy == (y - 1)) map->nextmap[D_N] = m->id;
|
|
}
|
|
if (map->nextmap[D_E] == -1) {
|
|
if (thisx == (x + 1)) map->nextmap[D_E] = m->id;
|
|
}
|
|
if (map->nextmap[D_S] == -1) {
|
|
if (thisy == (y + 1)) map->nextmap[D_S] = m->id;
|
|
}
|
|
if (map->nextmap[D_W] == -1) {
|
|
if (thisx == (x - 1)) map->nextmap[D_W] = m->id;
|
|
}
|
|
}
|
|
}
|
|
|
|
map->beingcreated = B_FALSE;
|
|
}
|
|
|
|
void createroom(map_t *map, int minx, int miny, int w, int h, int roomid) {
|
|
int x,y;
|
|
int poss[MAXOF(MAX_MAPW,MAX_MAPH)];
|
|
int npossible;
|
|
cell_t *cell, *newcell;
|
|
int maxx,maxy;
|
|
|
|
//printf("trying to create room at (%d,%d) w=%d,h=%d\n", minx, miny, w, h);
|
|
|
|
maxx = minx + (w-1);
|
|
maxy = miny + (h-1);
|
|
|
|
for (y = miny; y <= maxy; y++) {
|
|
for (x = minx; x <= maxx; x++) {
|
|
cell = getcellat(map, x, y);
|
|
|
|
// make it a border or room
|
|
if ((y == miny) || (y == maxy) ||
|
|
(x == minx) || (x == maxx)) {
|
|
if (cell->type->solid) {
|
|
setcelltype(cell, CT_ROOMWALL);
|
|
}
|
|
} else {
|
|
setcelltype(cell, CT_ROOM);
|
|
}
|
|
cell->roomid = roomid;
|
|
}
|
|
}
|
|
|
|
|
|
// for each side, make list of all possible door locations
|
|
// then pick one randomly.
|
|
// BUT if the nearby corridor only has one exit, always
|
|
// place a door.
|
|
|
|
// N
|
|
npossible = 0;
|
|
y = miny;
|
|
for (x = minx+1; x <= maxx-1; x++) {
|
|
cell = getcellat(map, x, y);
|
|
newcell = getcellindir(cell, D_N);
|
|
if (newcell && !newcell->type->solid) {
|
|
int doorcount;
|
|
|
|
doorcount = countadjcellswithflag(cell, F_DOOR);
|
|
|
|
if (doorcount == 0) {
|
|
if ((countcellexits(newcell) == 1) &&
|
|
(iswallindir(newcell,D_E)) &&
|
|
(iswallindir(newcell,D_W))) { // always add door
|
|
makedoor(cell);
|
|
} else {
|
|
poss[npossible] = x;
|
|
npossible++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (npossible > 0) {
|
|
int sel = rand() % npossible;
|
|
//printf("adding N door at %d\n",poss[sel]);
|
|
cell = getcellat(map, poss[sel], y);
|
|
makedoor(cell);
|
|
}
|
|
|
|
|
|
// S
|
|
npossible = 0;
|
|
y = maxy;
|
|
for (x = minx+1; x <= maxx-1; x++) {
|
|
cell = getcellat(map, x, y);
|
|
newcell = getcellindir(cell, D_S);
|
|
if (newcell && !newcell->type->solid) {
|
|
int doorcount;
|
|
|
|
doorcount = countadjcellswithflag(cell, F_DOOR);
|
|
|
|
if (doorcount == 0) {
|
|
if ((countcellexits(newcell) == 1) &&
|
|
(iswallindir(newcell,D_E)) &&
|
|
(iswallindir(newcell,D_W))) { // always add door
|
|
makedoor(cell);
|
|
} else {
|
|
poss[npossible] = x;
|
|
npossible++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (npossible > 0) {
|
|
int sel = rand() % npossible;
|
|
//printf("adding S door at %d\n",poss[sel]);
|
|
cell = getcellat(map, poss[sel], y);
|
|
makedoor(cell);
|
|
}
|
|
|
|
|
|
// W
|
|
npossible = 0;
|
|
x = minx;
|
|
for (y = miny+1; y <= maxy-1; y++) {
|
|
cell = getcellat(map, x, y);
|
|
newcell = getcellindir(cell, D_W);
|
|
if (newcell && !newcell->type->solid) {
|
|
int doorcount;
|
|
doorcount = countadjcellswithflag(cell, F_DOOR);
|
|
if (doorcount == 0) {
|
|
if ((countcellexits(newcell) == 1) &&
|
|
(iswallindir(newcell,D_N)) &&
|
|
(iswallindir(newcell,D_S))) { // always add door
|
|
makedoor(cell);
|
|
} else {
|
|
poss[npossible] = y;
|
|
npossible++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (npossible > 0) {
|
|
int sel = rand() % npossible;
|
|
//printf("adding W door at %d\n",poss[sel]);
|
|
cell = getcellat(map, x, poss[sel]);
|
|
makedoor(cell);
|
|
}
|
|
|
|
|
|
// E
|
|
npossible = 0;
|
|
x = maxx;
|
|
for (y = miny+1; y <= maxy-1; y++) {
|
|
cell = getcellat(map, x, y);
|
|
newcell = getcellindir(cell, D_E);
|
|
if (newcell && !newcell->type->solid) {
|
|
int doorcount;
|
|
doorcount = countadjcellswithflag(cell, F_DOOR);
|
|
if (doorcount == 0) {
|
|
if ((countcellexits(newcell) == 1) &&
|
|
(iswallindir(newcell,D_N)) &&
|
|
(iswallindir(newcell,D_S))) { // always add door
|
|
makedoor(cell);
|
|
} else {
|
|
poss[npossible] = y;
|
|
npossible++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (npossible > 0) {
|
|
int sel = rand() % npossible;
|
|
//printf("adding E door at %d\n",poss[sel]);
|
|
cell = getcellat(map, x, poss[sel]);
|
|
makedoor(cell);
|
|
}
|
|
|
|
// maybe make it a special room
|
|
if (rnd(1,100) <= getspecialroomchance(map)) {
|
|
enum SPECIALROOMTYPE rt;
|
|
rt = getspecialroomtype(map);
|
|
if (rt == RT_FLOODED) {
|
|
int sx,sy,ex,ey;
|
|
char wtype[BUFLEN];
|
|
switch (rnd(1,2)) {
|
|
case 1: strcpy(wtype, "deep water"); break;
|
|
case 2: strcpy(wtype, "shallow water"); break;
|
|
}
|
|
switch (rnd(1,2)) {
|
|
case 1: // entire room flooded
|
|
sx = minx+1;
|
|
sy = miny+1;
|
|
ex = maxx-1;
|
|
ey = maxy-1;
|
|
break;
|
|
case 2: // small walkway around the edge
|
|
sx = minx+2;
|
|
sy = miny+2;
|
|
ex = maxx-2;
|
|
ey = maxy-2;
|
|
if (sx > ex) sx = ex;
|
|
if (sy > ey) sy = ey;
|
|
break;
|
|
}
|
|
for (y = sy; y <= ey; y++) {
|
|
for (x = sx; x <= ex; x++) {
|
|
cell = getcellat(map, x, y);
|
|
addob(cell->obpile, wtype);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int dirtox(int dt, int dir) {
|
|
if (dt == DT_ORTH) {
|
|
switch (dir) {
|
|
case D_E:
|
|
return 1;
|
|
case D_W:
|
|
return -1;
|
|
default:
|
|
return 0;
|
|
}
|
|
} else if (dt == DT_COMPASS) {
|
|
switch (dir) {
|
|
case DC_NE:
|
|
case DC_E:
|
|
case DC_SE:
|
|
return 1;
|
|
case DC_NW:
|
|
case DC_W:
|
|
case DC_SW:
|
|
return -1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int dirtoy(int dt, int dir) {
|
|
if (dt == DT_ORTH) {
|
|
switch (dir) {
|
|
case D_S:
|
|
return 1;
|
|
case D_N:
|
|
return -1;
|
|
default:
|
|
return 0;
|
|
}
|
|
} else if (dt == DT_COMPASS) {
|
|
switch (dir) {
|
|
case DC_SW:
|
|
case DC_S:
|
|
case DC_SE:
|
|
return 1;
|
|
case DC_NE:
|
|
case DC_N:
|
|
case DC_NW:
|
|
return -1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void dumpmap(map_t *map) {
|
|
int x,y;
|
|
cell_t *cell;
|
|
printf("dump of map '%s' (%d x %d):\n",map->name, map->w, map->h);
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
cell = getcellat(map, x, y);
|
|
printf("%c",cell->type->glyph.ch);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
// dirtype of DT_ORTH will give a square explosion
|
|
// dirtype of DT_COMPASS will give a circular explosion
|
|
void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int dirtype, int wantannounce) {
|
|
int x,y;
|
|
|
|
if (dirtype == DT_COMPASS) {
|
|
animradial(c, range, '}', C_RED);
|
|
} else { // ie. DT_ORTH
|
|
animradialorth(c, range, '}', C_RED);
|
|
}
|
|
|
|
if (haslos(player, c)) {
|
|
if (wantannounce) {
|
|
msg("You see %s explosion!", (range > 0) ? "a huge" : "an");
|
|
}
|
|
} else {
|
|
noise(c, NULL, (range > 0) ? 6 : 5, "an explosion!", NULL);
|
|
}
|
|
|
|
for (y = c->y - range ; y <= c->y + range ; y++) {
|
|
for (x = c->x - range ; x <= c->x + range ; x++) {
|
|
int inrange = B_FALSE;
|
|
cell_t *cc;
|
|
cc = getcellat(c->map, x,y);
|
|
if (cc) {
|
|
if ((dirtype == DT_COMPASS) && (getcelldist(c,cc) <= range)) {
|
|
inrange = B_TRUE;
|
|
} else if ((dirtype == DT_ORTH) && (getcelldistorth(c,cc) <= range)) {
|
|
inrange = B_TRUE;
|
|
}
|
|
if (inrange && haslof(c, cc, LOF_WALLSTOP, NULL)) {
|
|
explodesinglecell(cc, dam, killwalls, o, c);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// lfs up to 1 cell away are knocked back
|
|
for (y = c->y - range-1 ; y <= c->y + range+1 ; y++) {
|
|
for (x = c->x - range-1 ; x <= c->x + range+1 ; x++) {
|
|
cell_t *cc;
|
|
int mydist;
|
|
cc = getcellat(c->map, x,y);
|
|
mydist = getcelldist(c,cc);
|
|
if (cc && (mydist <= (range+1))) {
|
|
if (cc->lf && !isdead(cc->lf)) {
|
|
// move away from centre of explosion
|
|
knockback(cc->lf, getdiraway(cc, c, NULL, B_FALSE, DT_COMPASS), 2, NULL, 40-(mydist*10));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// this should never be called directly - only from explodecells().
|
|
// (otherwise knockback effect won't happen)
|
|
void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *centre) {
|
|
char obname[BUFLEN];
|
|
|
|
if (c->lf) {
|
|
char buf[BUFLEN];
|
|
if (o) {
|
|
getobname(o, obname, 1);
|
|
sprintf(buf, "an exploding %s",strchr(obname, ' ')+1);
|
|
} else {
|
|
sprintf(buf, "an explosion");
|
|
}
|
|
|
|
// take damage
|
|
losehp(c->lf, dam, DT_EXPLOSIVE, NULL, buf);
|
|
|
|
}
|
|
|
|
damageallobs(o, c->obpile, dam, DT_EXPLOSIVE);
|
|
|
|
if (killwalls) {
|
|
if (c->type->solid) {
|
|
// make it empty!
|
|
setcelltype(c, getemptycelltype(c->map->habitat));
|
|
// add some rubble
|
|
addob(c->obpile, "10-50 stones");
|
|
}
|
|
}
|
|
}
|
|
|
|
celltype_t *findcelltype(enum CELLTYPE cid) {
|
|
celltype_t *ct;
|
|
for (ct = firstcelltype ; ct ; ct = ct->next) {
|
|
if (ct->id == cid) return ct;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
map_t *findmap(int mid) {
|
|
map_t *m;
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
if (m->id == mid) return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
map_t *findmapofdepth(int depth) {
|
|
map_t *m;
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
if (m->depth == depth) return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
cell_t *findmapentrypoint(map_t *m, int side, lifeform_t *lf) {
|
|
int x,y,xinc,yinc;
|
|
|
|
cell_t *selection = NULL;
|
|
cell_t *bestcell = NULL;
|
|
int closest = 999;
|
|
// oooooo TODO handle side being diagonal
|
|
switch (side) {
|
|
case D_N:
|
|
case DC_N:
|
|
x = 0;
|
|
y = 0;
|
|
xinc = 1;
|
|
yinc = 0;
|
|
bestcell = getcellat(m, lf->cell->x, 0);
|
|
break;
|
|
case D_E:
|
|
case DC_E:
|
|
x = m->w-1;
|
|
y = 0;
|
|
xinc = 0;
|
|
yinc = 1;
|
|
bestcell = getcellat(m, m->w - 1, lf->cell->y);
|
|
break;
|
|
case D_S:
|
|
case DC_S:
|
|
x = 0;
|
|
y = m->h - 1;
|
|
xinc = 1;
|
|
yinc = 0;
|
|
bestcell = getcellat(m, lf->cell->x, m->h - 1);
|
|
break;
|
|
case D_W:
|
|
case DC_W:
|
|
x = 0;
|
|
y = 0;
|
|
xinc = 0;
|
|
yinc = 1;
|
|
bestcell = getcellat(m, 0, lf->cell->y);
|
|
break;
|
|
}
|
|
|
|
for (;(x < m->w) && (y < m->h);) {
|
|
cell_t *c;
|
|
// check cell
|
|
c = getcellat(m, x, y);
|
|
if (c && cellwalkable(lf, c, NULL)) {
|
|
int dist;
|
|
dist = getcelldist(c, bestcell);
|
|
if (dist < closest) {
|
|
selection = c;
|
|
closest = dist;
|
|
}
|
|
}
|
|
// go to next one
|
|
x += xinc;
|
|
y += yinc;
|
|
}
|
|
|
|
return selection;
|
|
}
|
|
|
|
// find the object with id 'id' in map 'm'
|
|
object_t *findobidinmap(map_t *m, long id) {
|
|
cell_t *c;
|
|
int x,y;
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
object_t *o;
|
|
c = getcellat(m, x, y);
|
|
o = findobbyid(c->obpile, id);
|
|
if (o) return o;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// find the cell in 'map' which contains object oid
|
|
cell_t *findobinmap(map_t *m, enum OBCLASS oid) {
|
|
cell_t *c;
|
|
int x,y;
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (hasob(c->obpile, oid)) {
|
|
return c;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
map_t *findregionmap(int region, int depth) {
|
|
map_t *m;
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
if ((m->depth == depth) && (m->region == region)) return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void forgetcells(map_t *map, int amt) {
|
|
int amtleft;
|
|
//int totcells;
|
|
int i;
|
|
cell_t *poss[MAX_MAPW*MAX_MAPH];
|
|
cell_t *c;
|
|
int nposs = 0;
|
|
// how many cells to forget?
|
|
//totcells = (map->w * map->h);
|
|
//amtleft = (int) (((float) pct / 100.0) * (float)totcells);
|
|
amtleft = amt;
|
|
|
|
// get a list of all known cells
|
|
for (i = 0; i < (map->w*map->h); i++){
|
|
c = map->cell[i];
|
|
if (c && c->known && !haslos(player, c)) {
|
|
poss[nposs] = c;
|
|
nposs++;
|
|
}
|
|
|
|
}
|
|
|
|
if (amtleft > nposs) amtleft = nposs;
|
|
|
|
// forget cells...
|
|
for (i = 0; i < amtleft; i++) {
|
|
int n;
|
|
int sel;
|
|
sel = rnd(0,nposs-1);
|
|
poss[sel]->known = B_FALSE;
|
|
// shuffle down
|
|
for (n = i; n < (amtleft-1); n++) {
|
|
poss[n] = poss[n+1];
|
|
}
|
|
nposs--;
|
|
}
|
|
}
|
|
|
|
cell_t *getcellindir(cell_t *cell, int dir) {
|
|
cell_t *newcell;
|
|
int newx,newy;
|
|
int dt;
|
|
switch (dir) {
|
|
case D_N:
|
|
case D_S:
|
|
case D_E:
|
|
case D_W:
|
|
dt = DT_ORTH;
|
|
break;
|
|
case DC_N:
|
|
case DC_E:
|
|
case DC_S:
|
|
case DC_W:
|
|
case DC_NE:
|
|
case DC_SE:
|
|
case DC_SW:
|
|
case DC_NW:
|
|
dt = DT_COMPASS;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
newx = cell->x + dirtox(dt, dir);
|
|
newy = cell->y + dirtoy(dt, dir);
|
|
newcell = getcellat(cell->map, newx,newy);
|
|
|
|
return newcell;
|
|
}
|
|
|
|
// select a new direction (random chance of turnung)
|
|
int getnewdigdir(cell_t *cell, int lastdir, int turnpct, int *moved) {
|
|
int foundvaliddir = B_FALSE;
|
|
int dir;
|
|
int tried[4], numtries;
|
|
int i;
|
|
int turned = B_FALSE;
|
|
cell_t *newcell;
|
|
int db = B_FALSE;
|
|
char err[BUFLEN];
|
|
|
|
// haven't tried any dirs yet
|
|
numtries = 0;
|
|
for (i = 0; i < MAXDIR_ORTH; i++) {
|
|
tried[i] = B_FALSE;
|
|
}
|
|
|
|
while (!foundvaliddir) { // keep going until we get a valid direction
|
|
if (numtries >= MAXDIR_ORTH) { // no valid dirs
|
|
return D_NONE; // (pick a new random spot and refresh tried dirs and current dir)
|
|
}
|
|
|
|
if (lastdir == D_UNKNOWN) {
|
|
// just pick a random dir
|
|
dir = rnd(0, MAXDIR_ORTH-1);
|
|
turned = B_TRUE;
|
|
} else {
|
|
// chance of changing dir
|
|
if (rnd(1,100) <= turnpct) {
|
|
dir = rnd(0, MAXDIR_ORTH-1);
|
|
turned = B_TRUE;
|
|
} else {
|
|
dir = lastdir;
|
|
}
|
|
}
|
|
|
|
// now validate the direction
|
|
if (db) printf("--- Trying %s...\n",getdirname(dir));
|
|
if (tried[dir] == B_TRUE) { // already found this dir to be invalid
|
|
lastdir = D_UNKNOWN;
|
|
if (db) printf("--- Already know %s is invalid.\n",getdirname(dir));
|
|
} else {
|
|
// check 1 cell ahead
|
|
newcell = getcellindir(cell, dir);
|
|
if (isnewcellok(newcell, err)) {
|
|
cell_t *newcell1, *newcell2;
|
|
// check 2 cells ahead and sidewars
|
|
newcell = getcellindir(newcell, dir);
|
|
if (newcell) {
|
|
switch (dir) {
|
|
case D_N:
|
|
case D_S:
|
|
newcell1 = getcellindir(newcell,D_E);
|
|
newcell2 = getcellindir(newcell,D_W);
|
|
break;
|
|
case D_E:
|
|
case D_W:
|
|
newcell1 = getcellindir(newcell,D_N);
|
|
newcell2 = getcellindir(newcell,D_S);
|
|
break;
|
|
default: // should never happen
|
|
newcell1 = NULL;
|
|
newcell2 = NULL;
|
|
break;
|
|
}
|
|
} else {
|
|
newcell1 = NULL;
|
|
newcell2 = NULL;
|
|
}
|
|
|
|
if (!isnewcellok(newcell, err)) {
|
|
if (db) printf("--- %s %s!\n",getdirname(dir), err);
|
|
tried[dir] = B_TRUE;
|
|
lastdir = D_UNKNOWN;
|
|
numtries++;
|
|
} else if (!isnewcellok(newcell1, err)) {
|
|
if (db) printf("--- %s %s!\n",getdirname(dir), err);
|
|
tried[dir] = B_TRUE;
|
|
lastdir = D_UNKNOWN;
|
|
numtries++;
|
|
} else if (!isnewcellok(newcell2, err)) {
|
|
if (db) printf("--- %s %s!\n",getdirname(dir), err);
|
|
tried[dir] = B_TRUE;
|
|
lastdir = D_UNKNOWN;
|
|
numtries++;
|
|
} else { // ok
|
|
if (db) printf("--- %s %s!\n",getdirname(dir), err);
|
|
foundvaliddir = B_TRUE;
|
|
}
|
|
} else {
|
|
if (db) printf("--- %s %s!\n",getdirname(dir), err);
|
|
tried[dir] = B_TRUE;
|
|
lastdir = D_UNKNOWN;
|
|
numtries++;
|
|
}
|
|
}
|
|
}
|
|
//newcell = getcellindir(cell, dir);
|
|
//printf("getrandomdigdir() - on cell %d,%d, returning dir %d (-> %d,%d)\n",
|
|
// cell->x, cell->y, dir, newcell->x, newcell->y);
|
|
if (turned) *moved = 0;
|
|
return dir;
|
|
}
|
|
|
|
// chance of a random thing being an object (as opposed to a monster)
|
|
int getobchance(int habitat) {
|
|
switch (habitat) {
|
|
case H_DUNGEON:
|
|
return 50;
|
|
case H_FOREST:
|
|
return 75;
|
|
}
|
|
// default of no objects
|
|
return 0;
|
|
}
|
|
// chance that a room is a 'special' one
|
|
int getspecialroomchance(map_t *m) {
|
|
switch (m->habitat) {
|
|
case H_DUNGEON:
|
|
return 5;
|
|
default:
|
|
return 0;
|
|
}
|
|
// default of no chance
|
|
return 0;
|
|
}
|
|
|
|
enum SPECIALROOMTYPE getspecialroomtype(map_t *m) {
|
|
if (m->habitat == H_DUNGEON) {
|
|
/*
|
|
switch (rnd(1,1)) {
|
|
|
|
}
|
|
*/
|
|
return RT_FLOODED;
|
|
}
|
|
return RT_NONE;
|
|
}
|
|
|
|
|
|
// chance of each empty cell in a map has of getting an object/monster
|
|
int getthingchance(int habitat) {
|
|
switch (habitat) {
|
|
case H_DUNGEON:
|
|
return 3;
|
|
case H_FOREST:
|
|
return 3;
|
|
}
|
|
// default of no objects
|
|
return 0;
|
|
}
|
|
|
|
cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand) {
|
|
return real_getrandomadjcell(c, wantempty, allowexpand, LOF_NEED, NULL);
|
|
}
|
|
|
|
cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LOFTYPE needlof, enum OBTYPE *dontwantob) {
|
|
int radius = 1;
|
|
int x,y;
|
|
cell_t *poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
cell_t *new;
|
|
int done = B_FALSE;
|
|
|
|
while (!done) {
|
|
int numwithlof = 0;
|
|
for (y = c->y - radius ; y <= c->y + radius ; y++) {
|
|
for (x = c->x - radius ; x <= c->x + radius ; x++) {
|
|
new = getcellat(c->map, x, y);
|
|
if (new &&
|
|
(new != c) &&
|
|
(getcelldist(c,new) == radius) &&
|
|
haslof(c, new, needlof, NULL)) {
|
|
enum OBTYPE *badoid;
|
|
int ok = B_FALSE;
|
|
numwithlof++;
|
|
if (wantempty == WE_EMPTY) {
|
|
// make sure it's empty
|
|
if (isempty(new)) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else if (wantempty == WE_WALKABLE) {
|
|
if (cellwalkable(NULL, new, NULL)) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else if (wantempty == WE_PORTAL) {
|
|
if (cellwalkable(NULL, new, NULL) && !hasenterableobject(new) ) {
|
|
if (!hasobwithflag(new->obpile, F_DOOR)) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
} else if (wantempty == WE_NOTWALL) {
|
|
if ((!new->type->solid) && !hasobwithflag(new->obpile, F_IMPASSABLE)) {
|
|
//if (!new->type->solid) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else {
|
|
// always ok
|
|
ok = B_TRUE;
|
|
}
|
|
|
|
// obs we dont want?
|
|
if (dontwantob) {
|
|
for (badoid = dontwantob; (*badoid != OT_NONE) ; badoid++) {
|
|
if (hasob(new->obpile, *badoid)) {
|
|
ok = B_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
poss[nposs++] = new;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// found any possibilities ?
|
|
if (nposs) {
|
|
done = B_TRUE;
|
|
} else {
|
|
if (allowexpand) {
|
|
if (numwithlof) {
|
|
// increment radius
|
|
radius++;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
return poss[rnd(0,nposs-1)];
|
|
}
|
|
|
|
cell_t *getrandomcell(map_t *map) {
|
|
int x,y;
|
|
cell_t *cell;
|
|
x = (rand() % map->w);
|
|
y = (rand() % map->h);
|
|
cell = getcellat(map, x, y);
|
|
assert(cell);
|
|
return cell;
|
|
}
|
|
|
|
cell_t *getrandomcelloftype(map_t *map, int id) {
|
|
cell_t *cell;
|
|
cell = getrandomcell(map);
|
|
while (cell->type->id != id) {
|
|
cell = getrandomcell(map);
|
|
}
|
|
return cell;
|
|
}
|
|
|
|
|
|
int getrandomdir(int dirtype) {
|
|
if (dirtype == DT_ORTH) {
|
|
return rnd(D_N, D_W);
|
|
} else { // ie. DT_COMPASS
|
|
return rnd(DC_N, DC_NW);
|
|
}
|
|
|
|
}
|
|
|
|
cell_t *getrandomroomcell(map_t *map, int roomid) {
|
|
int npossible = 0;
|
|
int selidx;
|
|
int x,y;
|
|
cell_t *c, **poss;
|
|
|
|
poss = malloc((map->w*map->h) * sizeof(cell_t));
|
|
|
|
npossible = 0;
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
// is this cell in the correct room and not a wall?
|
|
if (c && isempty(c)) {
|
|
int ok = B_FALSE;
|
|
if (c->roomid == roomid) {
|
|
ok = B_TRUE;
|
|
} else if (roomid == ANYROOM) {
|
|
if (c->roomid != -1) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
poss[npossible] = c;
|
|
npossible++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (npossible <= 0) {
|
|
return NULL;
|
|
}
|
|
|
|
selidx = rnd(0, npossible-1);
|
|
c = poss[selidx];
|
|
free(poss);
|
|
return c;
|
|
}
|
|
|
|
// popuplates retcell[] with all cells from room
|
|
void getroomcells(map_t *m, int roomid, cell_t **retcell, int *ncells) {
|
|
int i;
|
|
cell_t *c;
|
|
*ncells = 0;
|
|
for (i = 0; i < m->w*m->h; i++) {
|
|
c = m->cell[i];
|
|
if (c && (c->roomid == roomid)) {
|
|
retcell[*ncells] = c;
|
|
(*ncells)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns how slippery this cell is
|
|
// if slipob is set, return the most slippery object in it.
|
|
int getslipperyness(cell_t *c, object_t **slipob) {
|
|
object_t *o,*bestob = NULL;
|
|
int bestslip = 0;
|
|
int totalslip = 0;
|
|
if (slipob) *slipob = NULL;
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
int thisslip;
|
|
sumflags(o->flags, F_SLIPPERY, &thisslip, NULL, NULL);
|
|
if (thisslip > 0) {
|
|
if (thisslip > bestslip) {
|
|
bestob = o;
|
|
bestslip = thisslip;
|
|
}
|
|
}
|
|
thisslip *= o->amt;
|
|
totalslip += thisslip;
|
|
}
|
|
|
|
totalslip *= 2;
|
|
|
|
if (slipob) {
|
|
*slipob = bestob;
|
|
}
|
|
|
|
return totalslip;
|
|
}
|
|
|
|
cell_t *getstairdestination(object_t *o) {
|
|
flag_t *f;
|
|
cell_t *newcell = NULL;
|
|
|
|
f = hasflag(o->flags, F_MAPLINK);
|
|
if (f) {
|
|
map_t *newmap;
|
|
int newx,newy;
|
|
newmap = findmap(f->val[0]);
|
|
assert(newmap);
|
|
// prefer an object id if we have it
|
|
if (strlen(f->text)) {
|
|
object_t *o2;
|
|
long obid;
|
|
obid = atol(f->text);
|
|
o2 = findobidinmap(newmap, obid);
|
|
if (o2) {
|
|
newcell = getoblocation(o2);
|
|
} else {
|
|
dblog("stairs link to object %ld which no longer exists!",obid);
|
|
msg("stairs link to object %ld which no longer exists!",obid);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
// otherwise look for x/y coors
|
|
newx = f->val[1];
|
|
newy = f->val[2];
|
|
// find dst x/y
|
|
newcell = getcellat(newmap, newx, newy);
|
|
}
|
|
}
|
|
return newcell;
|
|
}
|
|
|
|
object_t *hasenterableobject(cell_t *c) {
|
|
return hasobwithflag(c->obpile, F_CLIMBABLE);
|
|
}
|
|
|
|
|
|
lifeform_t *haslf(cell_t *c) {
|
|
if (c->lf && !isdead(c->lf)) {
|
|
return c->lf;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int hasobject(cell_t *c) {
|
|
if (c->obpile->first) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int hasknownobject(cell_t *c) {
|
|
object_t *o;
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (o && canseeob(player, o)) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
object_t *hastrailof(obpile_t *op, lifeform_t *lf, enum OBTYPE oid, flag_t **tflag, lifeform_t *viewer) {
|
|
object_t *o;
|
|
flag_t *f;
|
|
for (o = op->first ; o ; o = o->next) {
|
|
if (viewer && !canseeob(viewer, o)) continue;
|
|
if ((oid == NA) || (o->type->id == oid)) {
|
|
f = hasflag(o->flags, F_TRAIL);
|
|
// raceid and lfid must match
|
|
if (f && (f->val[0] == lf->race->id) && (atoi(f->text) == lf->id)) {
|
|
if (tflag) {
|
|
*tflag = f;
|
|
}
|
|
return o;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int isadjacent(cell_t *src, cell_t *dst) {
|
|
if (getcelldist(src, dst) == 1) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isdark(cell_t *c) {
|
|
switch (c->lit) {
|
|
case L_PERMDARK:
|
|
case L_NOTLIT:
|
|
return B_TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isdiggable(cell_t *c) {
|
|
switch (c->type->id) {
|
|
case CT_WALL: return B_TRUE;
|
|
case CT_ROOMWALL: return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// if isopen provided, returns whether or not the door is opened
|
|
int isdoor(object_t *o, int *isopen) {
|
|
int isdoor = B_FALSE;
|
|
if (hasflag(o->flags, F_DOOR)) {
|
|
isdoor = B_TRUE;
|
|
} else {
|
|
isdoor = B_FALSE;
|
|
}
|
|
|
|
if (isdoor) {
|
|
if (isopen) {
|
|
if (hasflag(o->flags, F_OPEN)) {
|
|
*isopen = B_TRUE;
|
|
} else {
|
|
*isopen = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
return isdoor;
|
|
}
|
|
|
|
int isempty(cell_t *c) {
|
|
if (!c) return B_FALSE;
|
|
if (c->type->solid) return B_FALSE;
|
|
if (c->lf) return B_FALSE;
|
|
if (!cellwalkable(NULL, c, NULL)) return B_FALSE;
|
|
return B_TRUE;
|
|
}
|
|
|
|
//returns TT_ based on what you can scan there
|
|
int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) {
|
|
flag_t *f;
|
|
// handle scanner
|
|
f = lfhasflag(player, F_DETECTLIFE);
|
|
if (f) {
|
|
if (c->lf) {
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
*thing = c->lf;
|
|
if (f->val[1] == B_TRUE) {
|
|
if (desc) {
|
|
real_getlfnamea(c->lf, desc, B_FALSE);
|
|
strcat(desc, " (detected)");
|
|
}
|
|
if (glyph) {
|
|
*glyph = *(getlfglyph(c->lf));
|
|
}
|
|
} else {
|
|
if (desc) {
|
|
char *p;
|
|
p = getsizetext(getlfsize(c->lf));
|
|
sprintf(desc, "%s %s monster", isvowel(*p) ? "an" : "a", p);
|
|
}
|
|
if (glyph) {
|
|
// select glyph based on size
|
|
glyph->ch = '0' + ((int) getlfsize(c->lf));
|
|
glyph->colour = C_GREY;
|
|
}
|
|
}
|
|
return TT_MONSTER;
|
|
}
|
|
}
|
|
}
|
|
f = lfhasflag(player, F_DETECTMETAL);
|
|
if (f) {
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
if (c->lf && ismetal(c->lf->race->material->id) ) {
|
|
*thing = c->lf;
|
|
if (glyph) {
|
|
glyph->ch = '*';
|
|
glyph->colour = C_GREY;
|
|
}
|
|
if (desc) sprintf(desc, "something metallic");
|
|
return TT_MONSTER;
|
|
} else {
|
|
object_t *o;
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (ismetal(o->material->id)) {
|
|
*thing = o;
|
|
if (glyph) {
|
|
glyph->ch = '*';
|
|
glyph->colour = C_GREY;
|
|
}
|
|
if (desc) sprintf(desc, "something metallic");
|
|
return TT_OBJECT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// can hear them using master level listen skill?
|
|
if (c->lf && lfhasflagval(player, F_CANHEARLF, c->lf->id, NA, NA, NULL)) {
|
|
if (glyph) {
|
|
*glyph = *(getlfglyph(c->lf));
|
|
}
|
|
if (desc) {
|
|
real_getlfnamea(c->lf, desc, B_FALSE);
|
|
strcat(desc, " (heard)");
|
|
}
|
|
|
|
*thing = c->lf;
|
|
return TT_MONSTER;
|
|
}
|
|
|
|
f = lfhasflag(player, F_DETECTOBS);
|
|
if (f) {
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
object_t *o;
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_DOOR)) {
|
|
*thing = o;
|
|
if (glyph) {
|
|
glyph->ch = '*';
|
|
glyph->colour = C_GREY;
|
|
}
|
|
if (desc) sprintf(desc, "an object");
|
|
return TT_OBJECT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int islit(cell_t *c) {
|
|
switch (c->lit) {
|
|
case L_TEMP:
|
|
case L_PERMLIGHT:
|
|
return B_TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
int isloopdirok(cell_t *cell, int dir) {
|
|
int dirok = B_FALSE;
|
|
cell_t *newcell;
|
|
// is there a corridor in this direction?
|
|
newcell = getcellindir(cell, dir);
|
|
while (newcell) {
|
|
// got a corridor?
|
|
if (!newcell->type->solid) {
|
|
dirok = B_TRUE;
|
|
break;
|
|
}
|
|
// keep going
|
|
newcell = getcellindir(newcell, dir);
|
|
}
|
|
|
|
// we've either gone off the map or
|
|
// hit a corridor
|
|
return dirok;
|
|
}
|
|
|
|
int isnewcellok(cell_t *cell, char *err) {
|
|
if ( !cell) { // can't go that way
|
|
if (err) sprintf(err,"goes off the map.");
|
|
return B_FALSE;
|
|
} else if ( !cell->type->solid) { // already an empty space there
|
|
if (err) sprintf(err,"goes to an empty space (%d,%d)",cell->x,cell->y);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// ok!
|
|
if (err) sprintf(err, "OK!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
int isonmap(map_t *map, int x, int y) {
|
|
if ((x < 0) || (y < 0)) {
|
|
return B_FALSE;
|
|
}
|
|
if ((x >= map->w) || (y >= map->h)) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
|
|
int isoutdoors(map_t *m) {
|
|
if (m->region == RG_WORLDMAP) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int iswallindir(cell_t *cell, int dir) {
|
|
cell_t *newcell;
|
|
newcell = getcellindir(cell, dir);
|
|
if (!newcell) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (newcell->type->solid) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
// link the staircase 'o' to a free one in adjacent maps.
|
|
// returns TRUE if it failed because othermap doesn't exist.
|
|
int linkstairs(object_t *o) {
|
|
map_t *othermap;
|
|
int othermapid;
|
|
object_t *o2;
|
|
map_t *stairmap;
|
|
cell_t *staircell;
|
|
cell_t *c2;
|
|
objecttype_t *otherstairtype;
|
|
int n,found = B_FALSE;
|
|
int dir;
|
|
flag_t *f;
|
|
|
|
staircell = getoblocation(o);
|
|
stairmap = staircell->map;
|
|
|
|
otherstairtype = getoppositestairs(o->type);
|
|
|
|
f = hasflag(o->flags, F_CLIMBABLE);
|
|
if (f) {
|
|
if (f->val[0] == D_UP) {
|
|
dir = -1;
|
|
} else {
|
|
dir = 1;
|
|
}
|
|
} else {
|
|
dblog("ERROR: stair object has no f_climbable!");
|
|
msg("ERROR: stair object has no f_climbable!");
|
|
assert(0 == 1);
|
|
}
|
|
|
|
|
|
//othermap = findmapofdepth(stairmap->depth + dir);
|
|
othermapid = stairmap->nextmap[f->val[0]];
|
|
othermap = findmap(othermapid);
|
|
if (!othermap) {
|
|
// find next map based on depth...
|
|
othermap = findregionmap(stairmap->region, stairmap->depth+dir);
|
|
}
|
|
if (othermap) {
|
|
// find an empty staircase in other map
|
|
for (n = 0; n < othermap->w*othermap->h; n++) {
|
|
c2 = othermap->cell[n];
|
|
o2 = hasob(c2->obpile, otherstairtype->id);
|
|
// does it go nowhere?
|
|
if (o2 && !hasflag(o2->flags, F_MAPLINK)) {
|
|
char obid[BUFLEN];
|
|
// link it to here!
|
|
sprintf(obid, "%ld", o->id);
|
|
addflag(o2->flags, F_MAPLINK, stairmap->id, NA, NA, obid);
|
|
// link me to there
|
|
sprintf(obid, "%ld", o2->id);
|
|
addflag(o->flags, F_MAPLINK, othermap->id, NA, NA, obid);
|
|
found = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!found) {
|
|
dblog("ERROR - stairs link to existing map %d, but it has no free stairs.",othermap->id);
|
|
msg("ERROR - stairs link to existing map %d, but it has no free stairs.",othermap->id);
|
|
more();
|
|
assert(0 == 1);
|
|
}
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void makedoor(cell_t *cell) {
|
|
object_t *o;
|
|
map_t *m;
|
|
char doorbuf[BUFLEN];
|
|
|
|
m = cell->map;
|
|
|
|
setcelltype(cell, getemptycelltype(m->habitat));
|
|
|
|
if ((rnd(1,100) + m->depth) >= 66) {
|
|
strcpy(doorbuf, "iron door");
|
|
} else {
|
|
strcpy(doorbuf, "wooden door");
|
|
}
|
|
|
|
o = addob(cell->obpile, doorbuf);
|
|
if (o && (rnd(1,2) == 1)) {
|
|
opendoor(NULL, o);
|
|
} else {
|
|
int chance;
|
|
// door is closed - lock it?
|
|
|
|
// ie. at dungeon lev 10, chance is 2 in 6
|
|
// at dungeon lev 20, chance is 3 in 6
|
|
// ...
|
|
// at dungeon lev 50, chance is 5 in 6
|
|
chance = rolldie(1,6) - (m->depth / 10);
|
|
|
|
if (chance <= 1) {
|
|
addflag(o->flags, F_LOCKED, B_TRUE, 19 + (m->depth), NA, NULL);
|
|
}
|
|
|
|
// make it secret?
|
|
chance = rolldie(1,6) - (m->depth / 10);
|
|
|
|
// difficulty:
|
|
// l1 = 20
|
|
// l10 = 25
|
|
// l20 = 30
|
|
if (chance <= 1) {
|
|
addflag(o->flags, F_SECRET, 20 + (m->depth / 2), NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void makelit(cell_t *c, enum LIGHTLEV how, int howlong) {
|
|
// don't override permenant light with temp light!
|
|
//if ((c->lit == L_PERMLIGHT) && (how == L_TEMP)) {
|
|
if (how == L_TEMP) {
|
|
//if ((c->lit == L_PERMLIGHT) || (c->lit == L_PERMDARK)) {
|
|
if (c->lit == L_PERMLIGHT) {
|
|
// light sources can't override permenant light
|
|
return;
|
|
}
|
|
if (c->lit == L_PERMDARK) {
|
|
// light sources can't override darkness spell
|
|
return;
|
|
}
|
|
}
|
|
if (howlong > 0) {
|
|
// TODO: use a stack here instead
|
|
c->origlit = c->lit;
|
|
c->origlittimer = c->littimer;
|
|
c->littimer = howlong;
|
|
}
|
|
c->lit = how;
|
|
}
|
|
|
|
void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong) {
|
|
int x,y;
|
|
cell_t *c2;
|
|
int (*distfunc)(cell_t *, cell_t *);
|
|
|
|
if (radius <= 0) return;
|
|
|
|
if (radius == 1) {
|
|
distfunc = getcelldist;
|
|
} else {
|
|
distfunc = getcelldistorth;
|
|
}
|
|
|
|
for (y = c->y - radius ; y <= c->y + radius; y++) {
|
|
for (x = c->x - radius ; x <= c->x + radius; x++) {
|
|
c2 = getcellat(c->map, x, y);
|
|
if (c2 && (distfunc(c, c2) <= radius)) {
|
|
if (cellhaslos(c,c2)) {
|
|
makelit(c2, how,howlong);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void setcellknown(cell_t *cell, int forcelev) {
|
|
enum SKILLLEVEL slev;
|
|
object_t *o;
|
|
|
|
if (forcelev > 0) {
|
|
slev = forcelev;
|
|
} else {
|
|
slev = getskill(player, SK_CARTOGRAPHY);
|
|
}
|
|
|
|
// photographic memory counts as novice level
|
|
if ((slev == PR_INEPT) && lfhasflag(player, F_PHOTOMEM)) {
|
|
slev = PR_NOVICE;
|
|
}
|
|
|
|
|
|
if (slev >= PR_NOVICE) {
|
|
cell->known = B_TRUE;
|
|
// default to remembering the cell's glyph
|
|
cell->knownglyph = cell->type->glyph;
|
|
// high cartography skill lets us remember certain objects...
|
|
if (slev >= PR_EXPERT) {
|
|
o = gettopobject(cell);
|
|
if (o) {
|
|
cell->knownglyph = *(getglyph(o));
|
|
}
|
|
}
|
|
if (slev >= PR_ADEPT) {
|
|
for (o = cell->obpile->first ; o ; o = o->next) {
|
|
if ((o->type->obclass->id == OC_DFEATURE) || (o->type->obclass->id == OC_TERRAIN)) {
|
|
if (!issecretdoor(o)) {
|
|
cell->knownglyph = *(getglyph(o));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (slev >= PR_BEGINNER) {
|
|
o = hasobwithflag(cell->obpile, F_CLIMBABLE);
|
|
if (o) {
|
|
cell->knownglyph = *(getglyph(o));
|
|
}
|
|
}
|
|
}
|
|
//getcellglyph(&(cell->knownglyph), cell, player);
|
|
}
|
|
|
|
void setcelltype(cell_t *cell, int id) {
|
|
assert(cell);
|
|
cell->type = findcelltype(id);
|
|
assert(cell->type);
|
|
cell->roomid = 0;
|
|
}
|
|
|
|
|
|
void updateknowncells(void) {
|
|
int x,y;
|
|
map_t *map;
|
|
object_t *wep;
|
|
int seenundead = B_FALSE;
|
|
|
|
|
|
map = player->cell->map;
|
|
wep = getweapon(player);
|
|
|
|
for (y = viewy; y < viewy + viewh; y++) {
|
|
for (x = viewx; x < viewx + vieww; x++) {
|
|
cell_t *cell;
|
|
cell = getcellat(map, x, y);
|
|
if (cell) {
|
|
//if ((player->cell == cell) || haslos(player, cell)) {
|
|
if (haslos(player, cell)) {
|
|
setcellknown(cell, B_FALSE);
|
|
if (cell->lf && lfhasflag(cell->lf, F_UNDEAD)) {
|
|
seenundead = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|