7898 lines
199 KiB
C
7898 lines
199 KiB
C
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "attack.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 "save.h"
|
|
#include "text.h"
|
|
#include "vault.h"
|
|
|
|
int enteringmap = B_FALSE;
|
|
|
|
extern habitat_t *firsthabitat,*lasthabitat;
|
|
extern job_t *firstjob;
|
|
extern map_t *firstmap,*lastmap;
|
|
extern behaviour_t *firstbehaviour,*lastbehaviour;
|
|
extern region_t *firstregion,*lastregion;
|
|
extern regionoutline_t *firstregionoutline,*lastregionoutline;
|
|
extern regiontype_t *firstregiontype,*lastregiontype;
|
|
extern celltype_t *firstcelltype, *lastcelltype;
|
|
extern objecttype_t *objecttype,*lastobjecttype;
|
|
extern race_t *firstrace;
|
|
extern int viewx,viewy,vieww,viewh;
|
|
extern lifeform_t *player;
|
|
|
|
extern int nextregionthingid;
|
|
|
|
extern lifeform_t *godlf[];
|
|
extern int ngodlfs;
|
|
|
|
extern glyph_t tempglyph;
|
|
|
|
extern enum OBCLASS sortorder[];
|
|
|
|
extern enum ERROR reason;
|
|
|
|
extern enum GAMEMODE gamemode;
|
|
|
|
extern int needredraw;
|
|
extern int noredraw;
|
|
|
|
extern int notime;
|
|
|
|
extern long curtime;
|
|
|
|
cell_t *addcell(map_t *m, int x, int y) {
|
|
cell_t *cell;
|
|
|
|
// already allocated?
|
|
cell = m->cell[(y*m->w)+x];
|
|
if (cell) {
|
|
clearcell(cell);
|
|
free(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;
|
|
cell->habitat = m->habitat;
|
|
cell->obpile = addobpile(NOOWNER, cell, NOOB);
|
|
cell->type = NULL;
|
|
setcelltype(cell, cell->habitat->solidcelltype);
|
|
cell->lf = NULL;
|
|
cell->room = NULL;
|
|
cell->locked = B_FALSE;
|
|
cell->lit = L_NOTLIT;
|
|
cell->origlit = L_NOTLIT;
|
|
cell->littimer = 0;
|
|
cell->origlittimer = 0;
|
|
cell->writing = NULL;
|
|
cell->writinglifetime = -1;
|
|
cell->known = B_FALSE;
|
|
cell->knowntime = 0;
|
|
cell->knownglyph.ch = ' ';
|
|
cell->knownglyph.colour = C_GREY;
|
|
cell->visited = B_FALSE;
|
|
cell->filled = B_FALSE;
|
|
cell->isroomwall = D_NONE;
|
|
return cell;
|
|
}
|
|
|
|
habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell, int thingchance, int obchance, int vaultchance, int maxvisrange, enum OBTYPE upstairtype, enum OBTYPE downstairtype) {
|
|
habitat_t *a;
|
|
// add to the end of the list
|
|
if (firsthabitat == NULL) {
|
|
firsthabitat = malloc(sizeof(habitat_t));
|
|
a = firsthabitat;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lasthabitat;
|
|
a->next = malloc(sizeof(habitat_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lasthabitat = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->name = strdup(name);
|
|
a->emptycelltype = emptycell;
|
|
a->solidcelltype = solidcell;
|
|
a->randthingpct = thingchance;
|
|
a->randobpct = obchance;
|
|
a->randvaultpct = vaultchance;
|
|
a->maxvisrange = maxvisrange;
|
|
a->upstairtype = upstairtype;
|
|
a->downstairtype = downstairtype;
|
|
return a;
|
|
}
|
|
|
|
void addhomeobs(lifeform_t *lf, int dolevelobs) {
|
|
flag_t *f;
|
|
cell_t *homeobloc;
|
|
homeobloc = lf->cell;
|
|
for (f = lf->flags->first ; f ; f = f->next) {
|
|
object_t *o = NULL;
|
|
if ((f->id == F_HOMEOB) && pctchance(f->val[0])) {
|
|
cell_t *c;
|
|
o = addob(homeobloc->obpile, f->text);
|
|
if (o && (homeobloc == lf->cell) && isimpassableob(o, lf, SZ_ANY)) {
|
|
c = real_getrandomadjcell(lf->cell, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL);
|
|
if (c) {
|
|
homeobloc = c; // future obs will go here too.
|
|
moveob(o, homeobloc->obpile, o->amt);
|
|
}
|
|
}
|
|
} else if ((f->id == F_HOMELEVOB) && dolevelobs) {
|
|
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. try rooms first.
|
|
c = getrandomroomcell(lf->cell->map, ANYROOM, WE_WALKABLE);
|
|
if (!c) {
|
|
// if no rooms, get any walkable cell.
|
|
c = getrandomcell(lf->cell->map);
|
|
while (!cellwalkable(NULL, c, NULL)) {
|
|
c = getrandomcell(lf->cell->map);
|
|
}
|
|
}
|
|
if (c) {
|
|
o = addob(c->obpile, f->text);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (o) {
|
|
if (lfhasflagval(lf, F_LIFEOB, o->type->id, NA, NA, NULL)) {
|
|
addflag(o->flags, F_LIFEOBFOR, lf->id, NA, NA, NULL);
|
|
killflagsofid(o->flags, F_OBHPDRAIN);
|
|
}
|
|
addflag(o->flags, F_HOMEOBFOR, lf->id, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
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_MAP; i++) {
|
|
a->nextmap[i] = -1;
|
|
}
|
|
for (i = 0 ; i < MAX_MAPW*MAX_MAPH; i++) {
|
|
a->cell[i] = NULL;
|
|
}
|
|
a->flags = addflagpile(NULL, NULL);
|
|
a->beingcreated = B_TRUE;
|
|
a->illumination = IL_FULLLIT;
|
|
a->habitat = findhabitat(H_DUNGEON); // default!!!
|
|
a->lastplayervisit = -1;
|
|
a->nfixedrooms = 0;
|
|
return a;
|
|
}
|
|
|
|
|
|
// when monsters are made during level generation, autogen will be true. otherwise false;
|
|
// if "rid" R_SPECIFIED, parse racename to get the race.
|
|
// rid can also be R_RANDOM.
|
|
// otherwise just use the given race.
|
|
lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int randomjobok, int amt, int autogen, int *nadded) {
|
|
lifeform_t *lf = NULL;
|
|
race_t *r;
|
|
int db = B_FALSE;
|
|
flagpile_t *wantflags = NULL;
|
|
enum JOB wantjob = J_NONE;
|
|
enum SUBJOB wantsubjob = SJ_NONE;
|
|
enum BEHAVIOUR wantbehaviour = BH_NONE;
|
|
|
|
if (nadded) *nadded = 0;
|
|
|
|
/*if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); */
|
|
|
|
// ie. don't create mosnters on closed doors!
|
|
if (!cellwalkable(NULL, c, NULL)) {
|
|
return NULL;
|
|
}
|
|
|
|
wantflags = addflagpile(NULL, NULL);
|
|
|
|
// override...
|
|
if (streq(racename, "random")) {
|
|
rid = R_RANDOM;
|
|
}
|
|
|
|
if (rid != R_SPECIFIED) {
|
|
if (rid == R_RANDOM) {
|
|
r = getrandomrace(c, NA);
|
|
} else {
|
|
r = findrace(rid);
|
|
}
|
|
} else {
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
rid = parserace(racename, wantflags, &wantjob, &wantsubjob, &wantbehaviour);
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
if (rid == R_RANDOM) {
|
|
r = getrandomrace(c, NA);
|
|
} else {
|
|
r = findrace(rid);
|
|
}
|
|
}
|
|
if (!r) {
|
|
killflagpile(wantflags);
|
|
return NULL;
|
|
}
|
|
|
|
if (db) {
|
|
char buf[BUFLEN];
|
|
snprintf(buf, BUFLEN, "addmonster for '%s'->%s",racename, r->name);
|
|
dbtimestart(buf);
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
//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");
|
|
|
|
if (hasflag(r->flags, F_UNIQUE)) {
|
|
// does it already exist?
|
|
lf = findlfunique(rid);
|
|
|
|
// if so, move it here, then exit.
|
|
if (lf) {
|
|
teleportto(lf, c, B_FALSE);
|
|
killflagpile(wantflags);
|
|
return lf;
|
|
}
|
|
}
|
|
|
|
//if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging
|
|
lf = addlf(c, r->id, getrandommonlevel(r, c->map));
|
|
if (db) dbtime("finished lf addition");
|
|
//if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging
|
|
if (lf) {
|
|
flag_t *f;
|
|
|
|
if (nadded) (*nadded)++;
|
|
|
|
if (db) dbtime("checking for job");
|
|
lf->born = B_FALSE;
|
|
|
|
if (wantjob == J_NONE) {
|
|
if (randomjobok) {
|
|
int nretflags,i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
getflags(lf->flags, retflag, &nretflags, F_STARTJOB, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
// has a job?
|
|
if (f->id == F_STARTJOB) {
|
|
if (rnd(1,100) <= f->val[0]) {
|
|
job_t *j;
|
|
enum SUBJOB wantsubjob;
|
|
if (f->val[1] == J_RANDOM) {
|
|
job_t *j;
|
|
j = getrandomjob(B_TRUE);
|
|
wantjob = j->id;
|
|
} else {
|
|
wantjob = f->val[1];
|
|
}
|
|
// special case: wizards MUST have a subjob.
|
|
if (wantjob == J_WIZARD) {
|
|
wantsubjob = SJ_RANDOM;
|
|
} else {
|
|
wantsubjob = f->val[2];
|
|
}
|
|
givejob(lf, wantjob);
|
|
|
|
j = findjob(wantjob);
|
|
// subjob ?
|
|
if (j && (wantsubjob != NA)) {
|
|
// cope with random
|
|
if (wantsubjob == SJ_RANDOM) {
|
|
// find a subjob which applies
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
|
|
getflags(j->flags, retflag, &nretflags, F_CANHAVESUBJOB, F_NONE);
|
|
if (nretflags) {
|
|
givesubjob(lf, retflag[rnd(0,nretflags-1)]->val[0]);
|
|
}
|
|
} else {
|
|
givesubjob(lf, wantsubjob);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
givejob(lf, wantjob);
|
|
}
|
|
if (wantsubjob != SJ_NONE) {
|
|
givesubjob(lf, wantsubjob);
|
|
}
|
|
|
|
generatealignment(lf);
|
|
//if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging
|
|
|
|
if (autogen) {
|
|
// sometimes start off hiding/asleep in new maps
|
|
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(wantflags, F_HIDING, 0, NA, NA, NULL);
|
|
}
|
|
}
|
|
if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_DEAF) && cansleep(lf) ) {
|
|
// if not already asleep...
|
|
if (hasflag(wantflags, F_ASLEEP)) {
|
|
} else {
|
|
int asleepchance = 70,willsleep = B_FALSE;
|
|
f = lfhasflag(lf, F_STARTASLEEPPCT);
|
|
if (f) {
|
|
asleepchance = f->val[0];
|
|
}
|
|
// TODO: base this on the time, and whether monster is nocturnal
|
|
if (pctchance(asleepchance)) {
|
|
willsleep = B_TRUE;
|
|
} else {
|
|
// might be asleep based on time.
|
|
if (issleepingtimefor(lf)) {
|
|
willsleep = B_TRUE;
|
|
}
|
|
}
|
|
if (willsleep) {
|
|
addflag(wantflags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
finalisemonster(lf, NULL, wantflags, wantbehaviour, 0);
|
|
|
|
// NOTE: because the initial maps (world, heaven, dungeon lev1) are created BEFORE the player,
|
|
// monsters on these maps will not have their hostility adjusted!
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
if (hasequippedobid(player->pack, OT_AMU_VICTIM)) {
|
|
// everything is hostile.
|
|
if (!lfhasflag(lf, F_HOSTILE)) addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
} else {
|
|
// adjust hostility based on player's race
|
|
if ((player->race->id == R_AVIAD) && hasflag(lf->flags, F_AVIAN)) {
|
|
killflagsofid(lf->flags, F_HOSTILE);
|
|
} else if ((player->race->id == R_MAMMOAN) && (lf->race->id == R_ELEPHANT)) {
|
|
killflagsofid(lf->flags, F_HOSTILE);
|
|
} else {
|
|
// adjust hostility based on player's alignment
|
|
switch (getalignment(player)) {
|
|
case AL_GOOD:
|
|
if (getalignment(lf) == AL_GOOD) {
|
|
killflagsofid(lf->flags, F_HOSTILE);
|
|
} else if (getalignment(lf) == AL_EVIL) {
|
|
if (!lfhasflag(lf, F_HOSTILE)) addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
break;
|
|
case AL_EVIL:
|
|
if (getalignment(lf) == AL_GOOD) {
|
|
if (!lfhasflag(lf, F_HOSTILE)) {
|
|
addflag(lf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
} // end if hasequipped amu_victimisation
|
|
}
|
|
|
|
lf->born = B_TRUE;
|
|
//if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging
|
|
|
|
// 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) {
|
|
int idx = 0;
|
|
cell_t *adjcell;
|
|
amt--; // we've already added one
|
|
|
|
//adjcell = c;
|
|
for ( ; amt > 0; amt--, idx++) {
|
|
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, NULL);
|
|
// did we find one?
|
|
if (!adjcell) break;
|
|
|
|
newlf = addlf(adjcell, r->id, getrandommonlevel(r, adjcell->map));
|
|
if (!newlf) {
|
|
break;
|
|
}
|
|
if (nadded) (*nadded)++;
|
|
newlf->born = B_FALSE;
|
|
|
|
|
|
finalisemonster(newlf, lf, wantflags, idx, BH_NONE);
|
|
|
|
newlf->born = B_TRUE;
|
|
|
|
// match alignment
|
|
setalignment(newlf, getalignment(lf));
|
|
// match hostility
|
|
if (lfhasflag(lf, F_HOSTILE)) {
|
|
if (!lfhasflag(newlf, F_HOSTILE)) addflag(newlf->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
|
|
} else {
|
|
killflagsofid(newlf->flags, F_HOSTILE);
|
|
}
|
|
|
|
// initial monster should remember its minions
|
|
addflag(lf->flags, F_MINION, newlf->id, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// 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;
|
|
enum RACE newrid;
|
|
race_t *newr;
|
|
|
|
adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, NULL);
|
|
if (!adjcell) break;
|
|
|
|
newrid = parserace(f->text, NULL, NULL, NULL, NULL);
|
|
newr = findrace(newrid);
|
|
if (!newr) break;
|
|
|
|
newlf = addlf(adjcell, newr->id, getrandommonlevel(newr, adjcell->map));
|
|
if (!newlf) break;
|
|
|
|
if (nadded) (*nadded)++;
|
|
|
|
// wantbehaviour only applies to initial monster
|
|
finalisemonster(newlf, lf, wantflags, 0, BH_NONE);
|
|
|
|
newlf->born = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
|
|
if (db) dbtime("handling random objects");
|
|
// sometimes give the lf random objects (extra monsters through
|
|
// 'numappears' don't get them).
|
|
if (onein(3)) {
|
|
giverandomobs(lf, rnd(1,3));
|
|
while (onein(3)) {
|
|
giverandomobs(lf, 1);
|
|
}
|
|
}
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
/*
|
|
// XXX: temporarily disbled due to slow light code
|
|
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) {
|
|
object_t *o;
|
|
o = addobfast(lf->pack, ot->id);
|
|
if (o) turnon(NULL, o);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
} // end if lf
|
|
} // end if r
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
// free mem
|
|
killflagpile(wantflags);
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
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 (real_getrandomob(c->map, buf, NA, c->habitat->id, SZ_MAX, SK_NONE, B_FALSE, OC_NONE, DT_NONE)) {
|
|
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 (!cellwalkable(NULL, c, NULL) || (rnd(1,100) <= obchance)) {
|
|
object_t *o;
|
|
// object
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
o = addrandomob(c);
|
|
if (o) {
|
|
if (nadded) *nadded = o->amt;
|
|
rv = TT_OBJECT;
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
} else {
|
|
lifeform_t *lf;
|
|
// monster
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
lf = addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, nadded);
|
|
if (lf) {
|
|
rv = TT_MONSTER;
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
map_t *getmapindir(map_t *src, int dir) {
|
|
map_t *other = NULL;
|
|
if (src->nextmap[dir] != -1) {
|
|
other = findmap(src->nextmap[dir]);
|
|
} else {
|
|
if (dir == D_DOWN) {
|
|
other = findregionmap(src->region->id, src->depth+1);
|
|
} else {
|
|
other = findregionmap(src->region->id, src->depth-1);
|
|
}
|
|
}
|
|
return other;
|
|
}
|
|
|
|
//
|
|
int getmapmaxvisrange(map_t *m) {
|
|
int maxrange;
|
|
|
|
maxrange = m->habitat->maxvisrange;
|
|
|
|
// modify for darkness outside ?
|
|
if (isoutdoors(m)) {
|
|
int hours,mins,secs;
|
|
float pct;
|
|
splittime(&hours,&mins,&secs);
|
|
pct = ((float)mins/59.0) * 100.0;
|
|
if (hours == 6) { // ie. 6am - 7am
|
|
// getting lighter. as minutes approach 59,
|
|
// visrange gets closer to maximum.
|
|
maxrange = pctof( pct, maxrange);
|
|
limit(&maxrange, 1, NA);
|
|
} else if (hours == 18) { // ie. 6pm-7pm
|
|
// getting darker. as minutes approach 59,
|
|
// visrange gets closer to zero.
|
|
maxrange = pctof( 100 - pct, maxrange);
|
|
limit(&maxrange, 1, NA);
|
|
}
|
|
} else if (m->habitat->id == H_DUNGEON) {
|
|
// in dungeon, reduce distance based on depth (ie. ambient light)
|
|
maxrange -= m->depth;
|
|
limit(&maxrange, 1, NA);
|
|
}
|
|
|
|
limit(&maxrange, 0, MAXVISRANGE);
|
|
|
|
return maxrange;
|
|
}
|
|
|
|
// populates retcell[] with all cells within given radius of centre
|
|
// if 'scatter' is >0, then not all cells will be returned - as you approach the edge of the radius,
|
|
// chances of getting the cells are lowered
|
|
void getradiuscells(cell_t *centre, int radius, int dirtype, int outsideonly, enum LOFTYPE needlof, int wantcentre, cell_t **retcell, int *ncells, int scatterdensity) {
|
|
int (*distfunc)(cell_t *, cell_t *);
|
|
int x,y;
|
|
cell_t *c;
|
|
|
|
*ncells = 0;
|
|
|
|
if (!centre) {
|
|
return;
|
|
}
|
|
if (dirtype == DT_ORTH) {
|
|
distfunc = getcelldistorth;
|
|
} else {
|
|
distfunc = getcelldist;
|
|
}
|
|
for (y = centre->y - radius; y <= centre->y + radius; y++) {
|
|
for (x = centre->x - radius; x <= centre->x + radius; x++) {
|
|
c = getcellat(centre->map, x,y);
|
|
if (c) {
|
|
int distance,distmatch = B_FALSE;
|
|
distance = distfunc(centre, c);
|
|
if (outsideonly) {
|
|
if (distance == radius) distmatch = B_TRUE;
|
|
} else {
|
|
if (distance <= radius) distmatch = B_TRUE;
|
|
}
|
|
if (distmatch && haslof(centre, c, needlof, NULL) && (wantcentre || (c != centre)) ) {
|
|
int chance;
|
|
if (scatterdensity) {
|
|
chance = 100 - (((float)distance / (float)radius) * scatterdensity);
|
|
} else {
|
|
chance = 100;
|
|
}
|
|
if (pctchance(chance)) {
|
|
retcell[*ncells] = c;
|
|
(*ncells)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int getroomid(cell_t *c) {
|
|
if (c->room) {
|
|
return c->room->id;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// whichside should be D_ORTH
|
|
// for now, this function will NOT include room corners
|
|
void getroomedge(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, int whichside, cell_t **retcell, int *ncells, int onlywantsolid) {
|
|
int x,y;
|
|
cell_t *c;
|
|
*ncells = 0;
|
|
if (whichside == D_N) {
|
|
y = miny;
|
|
for (x = minx+1; x <= maxx-1; x++) {
|
|
c = getcellat(map, x, y);
|
|
if (!onlywantsolid || c->type->solid) {
|
|
retcell[*ncells] = getcellat(map, x, y);
|
|
(*ncells)++;
|
|
}
|
|
}
|
|
} else if (whichside == D_S) {
|
|
y = maxy;
|
|
for (x = minx+1; x <= maxx-1; x++) {
|
|
c = getcellat(map, x, y);
|
|
if (!onlywantsolid || c->type->solid) {
|
|
retcell[*ncells] = getcellat(map, x, y);
|
|
(*ncells)++;
|
|
}
|
|
}
|
|
} else if (whichside == D_W) {
|
|
x = minx;
|
|
for (y = miny+1; y <= maxy-1; y++) {
|
|
c = getcellat(map, x, y);
|
|
if (!onlywantsolid || c->type->solid) {
|
|
retcell[*ncells] = getcellat(map, x, y);
|
|
(*ncells)++;
|
|
}
|
|
}
|
|
} else if (whichside == D_E) {
|
|
x = maxx;
|
|
for (y = miny+1; y <= maxy-1; y++) {
|
|
c = getcellat(map, x, y);
|
|
if (!onlywantsolid || c->type->solid) {
|
|
retcell[*ncells] = getcellat(map, x, y);
|
|
(*ncells)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if outlineid is -1, it's automatically assigned
|
|
region_t *addregion(enum REGIONTYPE rtype, region_t *parent, int outlineid, int depthmod, int createdby) {
|
|
region_t *a;
|
|
regionoutline_t *ro,*poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
int id;
|
|
regiontype_t *rt;
|
|
|
|
rt = findregiontype(rtype);
|
|
if (rt->majorbranch) {
|
|
// check for dupes
|
|
if (findregionbytype(rtype)) {
|
|
dblog("ERROR - trying to add duplicate region for a major branch (%s)", rt->name);
|
|
msg("ERROR - trying to add duplicate region for a major branch (%s)", rt->name);
|
|
more();
|
|
raise(SIGINT); // debug
|
|
}
|
|
}
|
|
|
|
// is there already a region?
|
|
if (lastregion) {
|
|
id = lastregion->id + 1;
|
|
} else {
|
|
id = 0;
|
|
}
|
|
|
|
// add to the end of the list
|
|
if (firstregion == NULL) {
|
|
firstregion = malloc(sizeof(region_t));
|
|
a = firstregion;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastregion;
|
|
a->next = malloc(sizeof(region_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastregion = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->rtype = findregiontype(rtype);
|
|
a->parentregion = parent;
|
|
a->depthmod = depthmod;
|
|
a->createdbymapid = createdby;
|
|
|
|
if (outlineid == -1) {
|
|
// randomly assign a regionoutline
|
|
for (ro = firstregionoutline; ro ; ro = ro->next) {
|
|
if (ro->rtype->id == rtype) {
|
|
poss[nposs++] = ro;
|
|
}
|
|
}
|
|
// make sure we got one...
|
|
if (nposs) {
|
|
a->outline = poss[rnd(0,nposs-1)];
|
|
} else {
|
|
a->outline = NULL;
|
|
}
|
|
} else {
|
|
a->outline = findoutline(outlineid);
|
|
assert(a->outline);
|
|
}
|
|
return a;
|
|
}
|
|
|
|
regionoutline_t *addregionoutline(enum REGIONTYPE rtype) {
|
|
regionoutline_t *a;
|
|
int nextid;
|
|
|
|
// calculate next region outline id
|
|
if (!lastregionoutline) { // ie. this is the first one to be created
|
|
nextid = 0;
|
|
} else {
|
|
nextid = lastregionoutline->id + 1;
|
|
}
|
|
|
|
// add to the end of the list
|
|
if (firstregionoutline == NULL) {
|
|
firstregionoutline = malloc(sizeof(regionoutline_t));
|
|
a = firstregionoutline;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastregionoutline;
|
|
a->next = malloc(sizeof(regionoutline_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastregionoutline = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = nextid;
|
|
a->rtype = findregiontype(rtype);
|
|
a->nthings = 0;
|
|
return a;
|
|
}
|
|
|
|
regionthing_t *addregionthing(regionoutline_t *ro, int depth, int x, int y, enum REGIONTHING whatkind, int value, char *what) {
|
|
regionthing_t *rt;
|
|
rt = &(ro->thing[ro->nthings]);
|
|
rt->id = nextregionthingid;
|
|
nextregionthingid++;
|
|
rt->parent = ro;
|
|
rt->depth = depth;
|
|
rt->x = x;
|
|
rt->y = y;
|
|
rt->whatkind = whatkind;
|
|
rt->value = value;
|
|
if (what) {
|
|
rt->what = strdup(what);
|
|
} else {
|
|
rt->what = strdup("");
|
|
}
|
|
|
|
ro->nthings++;
|
|
|
|
if (ro->nthings >= MAXOUTLINETHINGS) {
|
|
dblog("error - too many outlinethings!");
|
|
printf("error - too many outlinethings!\n");
|
|
exit(1);
|
|
}
|
|
return rt;
|
|
}
|
|
|
|
regiontype_t *addregiontype(enum REGIONTYPE id, char *name, int pluralname, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major, int depthmod) {
|
|
regiontype_t *a;
|
|
|
|
// add to the end of the list
|
|
if (firstregiontype == NULL) {
|
|
firstregiontype = malloc(sizeof(regiontype_t));
|
|
a = firstregiontype;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastregiontype;
|
|
a->next = malloc(sizeof(regiontype_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastregiontype = a;
|
|
a->next = NULL;
|
|
|
|
// props
|
|
a->id = id;
|
|
a->name = strdup(name);
|
|
a->pluralname = pluralname;
|
|
a->defaulthabitat = defaulthabitat;
|
|
a->maxdepth = maxdepth;
|
|
a->stairsperlev = stairsperlev;
|
|
a->deeperdir = deeperdir;
|
|
a->majorbranch = major;
|
|
a->depthmod = depthmod;
|
|
return a;
|
|
}
|
|
|
|
|
|
void adjustcellglyphforlight(cell_t *c, glyph_t *g) {
|
|
if (g->ch == ' ') return;
|
|
switch (c->lit) {
|
|
case L_PERMDARK:
|
|
case L_NOTLIT:
|
|
g->colour = C_BLUE;
|
|
break;
|
|
case L_TEMP: // lit by a light source
|
|
if (g->colour < 8) {
|
|
g->colour = g->colour + 8; // ie. make bold
|
|
if (g->colour >= C_GREY) g->colour = C_WHITE;
|
|
}
|
|
break;
|
|
case L_PERMLIGHT:
|
|
break;
|
|
}
|
|
}
|
|
|
|
int autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, int doorpct, int dooropenchance) {
|
|
int i,d;
|
|
cell_t *poss[MAXCANDIDATES], *cell[MAXCANDIDATES]; // TODO: should this be maxroomw * maxroomh ?
|
|
int possdir[MAXCANDIDATES];
|
|
int ncells = 0, npossible = 0;
|
|
int doorsadded = 0;
|
|
int db = B_TRUE;
|
|
|
|
if (db) dblog("autodoors starting");
|
|
|
|
// for each side, make list of all possible door locations
|
|
// then pick one randomly.
|
|
// BUT if the nearby corridor only has one exit, always
|
|
// place a door.
|
|
for (d = D_N; d <= D_W; d++) {
|
|
npossible = 0;
|
|
getroomedge(map, roomid, minx, miny, maxx, maxy, d, cell, &ncells, B_TRUE);
|
|
for (i = 0; i < ncells; i++) {
|
|
cell_t *newcell;
|
|
|
|
if (cellisfixedvaultwall(cell[i])) continue;
|
|
|
|
// is there empty space on the other side of this wall segment?
|
|
newcell = getcellindir(cell[i], d);
|
|
if (newcell && !newcell->type->solid) {
|
|
int doorcount;
|
|
// if so, make sure there are no other adjacent doors
|
|
doorcount = countadjcellswithflag(cell[i], F_DOOR, DT_ORTH);
|
|
if (doorcount == 0) {
|
|
// if there is only one way out of the adjacent empty cell, and
|
|
// walls to either side of the potential door location, then
|
|
// always add a door
|
|
if ((countcellexits(newcell, DT_ORTH) == 1) && wallstoleftright(newcell, d)) {
|
|
if (rnd(1,100) <= doorpct) {
|
|
makedoor(cell[i], dooropenchance);
|
|
} else {
|
|
setcelltype(cell[i], getcellempty(cell[i]));
|
|
cell[i]->isroomwall = orthdir(d);
|
|
addflag(map->flags, F_ROOMEXIT, roomid, cell[i]->x, cell[i]->y, "from autodoors, only way out");
|
|
}
|
|
} else {
|
|
// otherwise mark this as a _potential_ door location.
|
|
poss[npossible] = cell[i];
|
|
possdir[npossible] = d;
|
|
npossible++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if we have potential door locations, add one somewhere along this wall.
|
|
if (npossible > 0) {
|
|
int sel;
|
|
sel = rnd(0,npossible-1);
|
|
|
|
if (rnd(1,100) <= doorpct) {
|
|
makedoor(poss[sel], dooropenchance);
|
|
} else {
|
|
setcelltype(poss[sel], getcellempty(poss[sel]));
|
|
addflag(map->flags, F_ROOMEXIT, roomid, poss[sel]->x, poss[sel]->y, "from autodoors, potential location");
|
|
}
|
|
poss[sel]->isroomwall = orthdir(possdir[sel]);
|
|
|
|
doorsadded++;
|
|
}
|
|
} // end foreach direction
|
|
|
|
|
|
if (doorsadded == 0) {
|
|
int i,d,used[MAXDIR_ORTH],poss[MAXDIR_ORTH];
|
|
int dodoor[MAXDIR_ORTH];
|
|
int ndodoors = 0;
|
|
int ndoors,nposs = 0;
|
|
|
|
int sel;
|
|
//
|
|
for (d = D_N; d <= D_W; d++) {
|
|
used[d] = B_FALSE;
|
|
}
|
|
// no possible door locations - add a random number
|
|
ndoors = rnd(1,4);
|
|
|
|
for (i = 0; i < ndoors; i++) {
|
|
// find a dir we haven't already used
|
|
nposs = 0;
|
|
for (d = D_N; d <= D_W; d++) {
|
|
if (!used[d]) poss[nposs++] = d;
|
|
}
|
|
if (nposs <= 0) {
|
|
break;
|
|
}
|
|
sel = rnd(0,nposs-1);
|
|
used[poss[sel]] = B_TRUE;
|
|
dodoor[ndodoors] = poss[sel];
|
|
}
|
|
|
|
// actually make the doors
|
|
for (i = 0; i < ndodoors; i++) {
|
|
int sel;
|
|
d = dodoor[i];
|
|
getroomedge(map, roomid, minx, miny, maxx, maxy, d, cell, &ncells, B_TRUE);
|
|
if (ncells) {
|
|
sel = rnd(0,ncells-1);
|
|
if (rnd(1,100) <= doorpct) {
|
|
makedoor(cell[sel], dooropenchance);
|
|
doorsadded++;
|
|
} else {
|
|
setcelltype(cell[sel], getcellempty(cell[sel]));
|
|
addflag(map->flags, F_ROOMEXIT, roomid, cell[sel]->x, cell[sel]->y, "from autodoors, forced at end");
|
|
doorsadded++;
|
|
}
|
|
cell[sel]->isroomwall = orthdir(d);
|
|
}
|
|
}
|
|
}
|
|
if (db) dblog("autodoors() added %d doors to roomid %d", doorsadded, roomid);
|
|
return doorsadded;
|
|
}
|
|
|
|
|
|
// change a wall into an empty cell, and add some rubble
|
|
void breakwall(cell_t *c) {
|
|
celltype_t *origtype;
|
|
int roomwall;
|
|
origtype = c->type;
|
|
roomwall = isroom(c);
|
|
setcelltype(c, getcellempty(c));
|
|
if (origtype->solid && roomwall && onein(2)) {
|
|
switch (origtype->material->id) {
|
|
case MT_STONE: addob(c->obpile, "1-30 stones"); break;
|
|
case MT_GLASS: addob(c->obpile, "1-30 pieces of broken glass"); break;
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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 (c1 == dest) return B_TRUE;
|
|
|
|
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;
|
|
}
|
|
|
|
// is the given cell a wall of a vault with maintain_edge, and not marked as an exit?
|
|
int cellisfixedvaultwall(cell_t *c) {
|
|
if ( getcellvault(c) &&
|
|
c->type->solid &&
|
|
hasflag(c->room->vault->flags, F_MAINTAINEDGE) &&
|
|
!hasflagval(c->map->flags, F_ROOMEXIT, c->room->id, c->x, c->y, NULL) ) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int adjcellokforreachability(cell_t *c, int srcroomid, int dir, int wantfilled) {
|
|
int proomid;
|
|
proomid = getroomid(c);
|
|
if ( ((srcroomid == -1) || (proomid != srcroomid)) &&
|
|
cellwalkable(NULL, c, NULL) && !c->locked) {
|
|
if ((proomid >= 0) && (c->isroomwall != diropposite(dir))) {
|
|
// different room and hits wrong wall.
|
|
} else if (cellisfixedvaultwall(c)) {
|
|
} else {
|
|
if (!wantfilled || c->filled) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int cellmatchescondition(cell_t *c, int wecond) {
|
|
int ok = B_FALSE;
|
|
if (wecond == WE_EMPTY) {
|
|
// make sure it's empty
|
|
if (isempty(c)) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else if (wecond == WE_WALKABLE) {
|
|
if (cellwalkable(NULL, c, NULL)) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else if (wecond == WE_PORTAL) {
|
|
if (cellwalkable(NULL, c, NULL) && !hasenterableobject(c) ) {
|
|
if (!hasobwithflag(c->obpile, F_DOOR)) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
} else if (wecond == WE_NOTWALL) {
|
|
if ((!c->type->solid) && !hasobwithflag(c->obpile, F_IMPASSABLE)) {
|
|
//if (!c->type->solid) {
|
|
ok = B_TRUE;
|
|
}
|
|
} else if (wecond == WE_NOLF) {
|
|
if (!c->lf) ok = B_TRUE;
|
|
} else if (wecond == WE_SOLID) {
|
|
if (c->type->solid) ok = B_TRUE;
|
|
} else {
|
|
// always ok
|
|
ok = B_TRUE;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// returns B_TRUE, B_FALSE or B_MAYBE
|
|
int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom) {
|
|
int db = B_FALSE;
|
|
if (c->locked) {
|
|
// locked cell. invalied.
|
|
if (insameroom) *insameroom = B_FALSE;
|
|
if (db) dblog(" going %s hits locked cell. invalid.", getdirname(dir));
|
|
return B_FALSE;
|
|
} else if ((srcroomid >= 0) && (getroomid(c) == srcroomid) && c->type->solid && startcell->type->solid) {
|
|
// hits a wall of the same room,
|
|
// and start cell NOT one inside the room.
|
|
|
|
// invalid
|
|
if (insameroom) *insameroom = B_TRUE;
|
|
if (db) dblog(" going %s hits wall of same room. invalid.", getdirname(dir));
|
|
return B_FALSE;
|
|
} else if (isroom(c) && (getroomid(c) != srcroomid) && (c->isroomwall != diropposite(dir))) {
|
|
// cell is in a different room, but not the correct edge
|
|
// invalid
|
|
if (insameroom) *insameroom = B_FALSE;
|
|
if (db) dblog(" going %s hits wrong wall (%s) of different room. invalid.", getdirname(dir), getdirname(c->isroomwall));
|
|
return B_FALSE;
|
|
} else if (cellisfixedvaultwall(c)) {
|
|
// cell is a wall of a maintain_edge vault, and not an exit cell
|
|
// invalid
|
|
if (insameroom) *insameroom = B_FALSE;
|
|
if (db) dblog(" going %s hits non-exit wall maintain_edge vault. invalid.", getdirname(dir));
|
|
return B_FALSE;
|
|
} else if (isroom(c) && (getroomid(c) != srcroomid) && c->type->solid && countadjdoors(c) ) {
|
|
// cell is the wall of a different room, and adjacent to a door.
|
|
// invalid
|
|
if (insameroom) *insameroom = B_FALSE;
|
|
if (db) dblog(" going %s hits wall adjacent to door. invalid.", getdirname(dir));
|
|
return B_FALSE;
|
|
} else if (cellwalkable(NULL, c, NULL)) {
|
|
if (getroomid(c) == srcroomid) {
|
|
// invalid
|
|
if (insameroom) *insameroom = B_TRUE;
|
|
if (db) dblog(" going %s hits empty cell of same room. invalid.", getdirname(dir));
|
|
return B_FALSE;
|
|
} else {
|
|
if (!wantfilled || c->filled) {
|
|
// walkable and not in this vault. finished.
|
|
// valid.
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
return B_MAYBE;
|
|
}
|
|
|
|
void clearcell(cell_t *c) {
|
|
// kill everything there - (lifeforms && objects)
|
|
if (c->lf && !isplayer(c->lf)) {
|
|
killlf(c->lf);
|
|
}
|
|
while (c->obpile->first) {
|
|
killob(c->obpile->first);
|
|
}
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
c->known = B_FALSE;
|
|
c->knownglyph.ch = ' ';
|
|
c->knownglyph.colour = C_GREY;
|
|
}
|
|
}
|
|
void clearcell_exceptflags(cell_t *c, ... ) {
|
|
va_list args;
|
|
enum FLAG exception[MAXCANDIDATES];
|
|
int nexceptions = 0,i;
|
|
object_t *o,*nexto;
|
|
va_start(args, c);
|
|
exception[nexceptions] = va_arg(args, enum FLAG);
|
|
while (exception[nexceptions] != F_NONE) {
|
|
nexceptions++;
|
|
exception[nexceptions] = va_arg(args, enum FLAG);
|
|
}
|
|
va_end(args);
|
|
assert(nexceptions < MAXCANDIDATES);
|
|
|
|
if (c->lf && !isplayer(c->lf)) {
|
|
killlf(c->lf);
|
|
}
|
|
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
int exclude = B_FALSE;
|
|
nexto = o->next;
|
|
for (i = 0; i < nexceptions; i++) {
|
|
if (hasflag(o->flags, exception[i])) {
|
|
exclude = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!exclude) killob(o);
|
|
}
|
|
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
c->known = B_FALSE;
|
|
c->knownglyph.ch = ' ';
|
|
c->knownglyph.colour = C_GREY;
|
|
}
|
|
}
|
|
|
|
int damagecell(cell_t *c, int amt, enum DAMTYPE damtype) {
|
|
if (!c->type->solid) return B_TRUE;
|
|
// adjust dam
|
|
adjustdammaterial(&amt, damtype, c->type->material->id);
|
|
if (amt <= 0) return B_TRUE;
|
|
|
|
// wall loses hp
|
|
c->hp -= amt;
|
|
if (c->hp <= 0) {
|
|
char cellname[BUFLEN];
|
|
int shattered = B_FALSE;
|
|
enum MATERIAL cellmat;
|
|
c->hp = 0;
|
|
// remember cell properties
|
|
sprintf(cellname, "%s %s", needan(c->type->name) ? "An" : "A", c->type->name);
|
|
cellmat = c->type->material->id;
|
|
// cell dies (have to do this before calling fragments())
|
|
setcelltype(c, getcellempty(c));
|
|
// announce
|
|
if (haslos(player, c)) {
|
|
msg("%s %s!", cellname, willshatter(cellmat) ? "shatters" : "is destroyed");
|
|
}
|
|
// shatter?
|
|
if (willshatter(cellmat)) {
|
|
char what[BUFLEN];
|
|
shattered = B_TRUE;
|
|
noise(c, NULL, NC_OTHER, SV_CAR, "something shattering.", NULL);
|
|
if (getshardobname(cellmat, what)) {
|
|
fragments(c, what, 3, 3);
|
|
}
|
|
} else {
|
|
switch (cellmat) {
|
|
case MT_STONE:
|
|
addob(c->obpile, "50-100 stones");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
// returns true if something happened
|
|
int doelementspread(cell_t *c) {
|
|
float thisdepth;
|
|
int i;
|
|
int nsurround = 0;
|
|
int db = B_FALSE;
|
|
cell_t *surroundcell[8];
|
|
object_t *fireob = NULL;
|
|
|
|
if (!c || c->type->solid) {
|
|
return B_FALSE;
|
|
}
|
|
|
|
// calculate depth of this cell
|
|
if (!hascloseddoor(c)) {
|
|
thisdepth = getcellwaterdepth(c, NULL);
|
|
|
|
if (thisdepth) {
|
|
// count surrounding cells of lower depth
|
|
for (i = DC_N; i <= DC_NW; i++) {
|
|
cell_t *newc;
|
|
|
|
newc = getcellindir(c, i);
|
|
if (newc && !newc->type->solid && !hascloseddoor(newc)) {
|
|
float newcdepth;
|
|
int ok = B_FALSE;
|
|
|
|
/*
|
|
FIX THIS CODE LATER
|
|
if (newc->type->floorheight == c->type->floorheight) {
|
|
// same height - don't include these???
|
|
ok = B_FALSE;
|
|
//ok = B_TRUE;
|
|
} else if (newc->type->floorheight < c->type->floorheight) {
|
|
// ie. downhill. don't include these
|
|
ok = B_FALSE;
|
|
} else if (newc->type->floorheight > c->type->floorheight) {
|
|
// ie. uphill.
|
|
ok = B_TRUE;
|
|
}
|
|
*/
|
|
if (newc->type->floorheight == c->type->floorheight) {
|
|
ok = B_TRUE;
|
|
} else {
|
|
ok = B_FALSE;
|
|
}
|
|
|
|
if (ok) {
|
|
newcdepth = getcellwaterdepth(newc, NULL);
|
|
|
|
if (newcdepth < thisdepth) {
|
|
surroundcell[nsurround] = newc;
|
|
nsurround++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// if there were any posisble cells around us
|
|
if (nsurround) {
|
|
float newdepth;
|
|
|
|
//newdepth = thisdepth / (pctof(nsurround,50)+1);
|
|
newdepth = thisdepth / (pctof(nsurround,55)+1);
|
|
//newdepth = thisdepth / (nsurround+1);
|
|
limitf(&newdepth,(float)DP_NONE, (float)DP_MAX);
|
|
|
|
if (db) dblog("waterspread: cell %d,%d (%d/%s) spreads to %d cells-->%d/%s",
|
|
c->x, c->y, (int)thisdepth, getwaterdepthname((int)thisdepth),
|
|
nsurround, (int)newdepth, getwaterdepthname((int)newdepth));
|
|
|
|
// SET our cell to new depth
|
|
setwaterdepth(c, (int)newdepth);
|
|
|
|
// ADD water to surrounding cells
|
|
for (i = 0; i < nsurround; i++) {
|
|
cell_t *sc;
|
|
flag_t *f;
|
|
|
|
sc = surroundcell[i];
|
|
|
|
f = hasflagval(sc->map->flags, F_NEWWATERDEPTH, sc->x, sc->y, NA, NULL);
|
|
if (f) {
|
|
f->val[2] += newdepth;
|
|
} else {
|
|
addflag(sc->map->flags, F_NEWWATERDEPTH, sc->x, sc->y, (int)newdepth, NULL);
|
|
}
|
|
}
|
|
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
// fire
|
|
fireob = hasobofmaterial(c->obpile, MT_FIRE);
|
|
if (fireob && (fireob->birthtime != curtime) && !isdeadob(fireob)) {
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcells,i,nspread = 0;
|
|
object_t *oo;
|
|
|
|
// check adjacent cells (and this one) for flammable stuff
|
|
getradiuscells(c, 1, DT_COMPASS, B_FALSE, LOF_DONTNEED, B_TRUE, retcell, &nretcells, B_FALSE);
|
|
for (i = 0; i < nretcells; i++) {
|
|
int celldone = B_FALSE;
|
|
for (oo = retcell[i]->obpile->first ; oo ; oo = oo->next) {
|
|
flag_t *f;
|
|
if (hasobofmaterial(retcell[i]->obpile, MT_FIRE)) {
|
|
// there's already a fire here.
|
|
// f_onfire flags won't expire if there is fire there.
|
|
f = hasflag(oo->flags, F_ONFIRE);
|
|
if (f && (f->lifetime > 0)) {
|
|
f->lifetime++;
|
|
}
|
|
} else if (isflammable(oo)) {
|
|
// no fire here already. if there is a flammable object here, the fire
|
|
// will spread.
|
|
ignite(oo);
|
|
// if object didn't ignite into a fire, add one.
|
|
if (!hasobofmaterial(retcell[i]->obpile, MT_FIRE)) {
|
|
addobfast(retcell[i]->obpile, fireob->type->id);
|
|
}
|
|
// now we regain our full hp
|
|
f = hasflag(fireob->flags, F_OBHP);
|
|
if (f) f->val[0] = f->val[1];
|
|
|
|
nspread++;
|
|
celldone = B_TRUE;
|
|
}
|
|
}
|
|
// lifeforms made out of something flammable? ie. plants
|
|
// don't include flesh even though it's technically flammable
|
|
if (retcell[i]->lf && (retcell[i]->lf->material->id != MT_FLESH)) {
|
|
if (!hasobofmaterial(retcell[i]->obpile, MT_FIRE)) {
|
|
if (lfhasflag(retcell[i]->lf, F_FLAMMABLE)) {
|
|
addobfast(retcell[i]->obpile, fireob->type->id);
|
|
nspread++;
|
|
celldone = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
// flammable cell floor (and no doors, solid walls, etc)
|
|
// just a small chance of spreading.
|
|
if (cellwalkable(NULL, retcell[i], NULL) && !hasobofmaterial(retcell[i]->obpile, MT_FIRE)) {
|
|
if (hasflag(retcell[i]->type->material->flags, F_FLAMMABLE)) {
|
|
if (onein(6)) {
|
|
addobfast(retcell[i]->obpile, fireob->type->id);
|
|
nspread++;
|
|
celldone = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int fix_reachability(map_t *m) {
|
|
int i,keepgoing = B_TRUE, nfixed = 0;
|
|
int db = B_TRUE;
|
|
cell_t *c = NULL;
|
|
|
|
if (db) dblog("fix_reachability starting.");
|
|
|
|
switch (m->habitat->id) {
|
|
case H_FOREST:
|
|
case H_HEAVEN:
|
|
case H_PIT:
|
|
case H_VILLAGE:
|
|
if (db) dblog("fix_reachability not required for this habitat.");
|
|
return B_FALSE;
|
|
default: break;
|
|
}
|
|
|
|
// find first non-empty cell
|
|
for (i = 0; i < m->w * m->h; i++) {
|
|
if (!m->cell[i]->type->solid) {
|
|
c = m->cell[i];
|
|
break;
|
|
}
|
|
}
|
|
// no empty cells in map?
|
|
if (!c) return B_FALSE;
|
|
while (keepgoing) {
|
|
keepgoing = B_FALSE;
|
|
// mark all cells as non-filled
|
|
for (i = 0; i < m->w * m->h; i++) {
|
|
m->cell[i]->filled = FALSE;
|
|
}
|
|
// floodfill
|
|
floodfill(c);
|
|
// any remaining non-filled empty cells?
|
|
for (i = 0; i < m->w * m->h; i++) {
|
|
if (!m->cell[i]->type->solid && !m->cell[i]->filled) {
|
|
vault_t *v;
|
|
v = getcellvault(m->cell[i]);
|
|
if (v && hasflag(v->flags, F_VAULTNOLINK)) {
|
|
// don't need to link it.
|
|
} else {
|
|
int nadded = 0;
|
|
// found an unreachable cell! link it back to a filled cell.
|
|
if (db) dblog(" found unreachable area at %d,%d. will fix it.",
|
|
m->cell[i]->x, m->cell[i]->y);
|
|
|
|
if (linkexit(m->cell[i], B_TRUE, &nadded)) {
|
|
// failed!
|
|
if (db) dblog(" fix_reachability failed - couldn't fix an unreachable area.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (nadded) {
|
|
if (db) dblog(" fixed unreachable area by adding %d cells.", nadded);
|
|
} else {
|
|
// didn't add anything - fail!
|
|
if (db) dblog(" fix_reachability failed.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// now run the test again.
|
|
// 'c' will be where the next flood will will happen.
|
|
keepgoing = B_TRUE;
|
|
c = m->cell[i];
|
|
nfixed++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (db) dblog(" fix_reachability complete. fixed %d unreachable areas.", nfixed);
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
void floodfill(cell_t *startcell) {
|
|
int d;
|
|
if (startcell && // not off the map
|
|
!startcell->type->solid && // empty cell
|
|
!startcell->filled) { // not already filled
|
|
startcell->filled = B_TRUE;
|
|
} else {
|
|
return;
|
|
}
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
floodfill(getcellindir(startcell, d)); // recursive call
|
|
}
|
|
}
|
|
|
|
// populates thing & nthings with all "regionthings" with
|
|
// whatkind == RT_REGIONLINK.
|
|
// ie. links to all the map branches.
|
|
// returns # found
|
|
int getbranchlinks(regionthing_t **thing, int *nthings) {
|
|
int i;
|
|
region_t *r;
|
|
regionthing_t *temp;
|
|
|
|
*nthings = 0;
|
|
for (r = firstregion ; r ; r = r->next) {
|
|
if (!r->outline) continue;
|
|
for (i = 0; i < r->outline->nthings; i++ ){
|
|
// pick a random regionlink thing.
|
|
temp = &r->outline->thing[i];
|
|
if (temp->whatkind == RT_REGIONLINK) {
|
|
regiontype_t *rtype;
|
|
rtype = findregiontype(temp->value);
|
|
if ( (rtype->id != RG_MAINDUNGEON) &&
|
|
(rtype->id != RG_WORLDMAP)) {
|
|
thing[(*nthings)++] = temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return *nthings;
|
|
}
|
|
|
|
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 getcellclimbdifficulty(cell_t *c) {
|
|
int diff = 12;
|
|
switch (c->type->material->id) {
|
|
case MT_GLASS: diff = 26; break;
|
|
case MT_DRAGONWOOD: diff = 20; break;
|
|
case MT_METAL: diff = 20; break;
|
|
case MT_STONE: diff = 15; break;
|
|
case MT_WAX: diff = 10; break;
|
|
case MT_WOOD: diff = 8; break;
|
|
case MT_PLANT: diff = 8; break;
|
|
default: diff = 12; break;
|
|
}
|
|
// modify for celltype
|
|
switch (c->type->id) {
|
|
case CT_WALLBRICK: diff -= 5; break;
|
|
default: break;
|
|
}
|
|
limit(&diff, 0, NA);
|
|
return diff;
|
|
}
|
|
|
|
int getcellclimbdifficultyavg(cell_t *c) {
|
|
int d;
|
|
int diff = 0;
|
|
int nwalls = 0;
|
|
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
cell_t *newcell;
|
|
newcell = getcellindir(c, d);
|
|
if (newcell && newcell->type->solid) {
|
|
diff += getcellclimbdifficulty(newcell);
|
|
} else {
|
|
diff += 30; // ie. v.high
|
|
}
|
|
nwalls++;
|
|
}
|
|
diff /= nwalls;
|
|
return diff;
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
}
|
|
if (lfhasflag(c->lf, F_FROZEN)) {
|
|
g->colour = C_CYAN;
|
|
}
|
|
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;
|
|
}
|
|
// otherwise fall through
|
|
}
|
|
|
|
// objects here?
|
|
if ((countobs(c->obpile, B_FALSE) > 0)) {
|
|
object_t *o;
|
|
|
|
// draw highest object in sort order
|
|
o = gettopobject(c, B_TRUE);
|
|
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;
|
|
adjustcellglyphforlight(c, g);
|
|
}
|
|
} else {
|
|
// draw cell normally
|
|
//drawcell(cell, x, y);
|
|
*g = c->type->glyph;
|
|
adjustcellglyphforlight(c, g);
|
|
}
|
|
} 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 = ' ';
|
|
}
|
|
// out of LOS - show as dark
|
|
// TODO: if terminal supports it, use C_GREY instead.
|
|
g->colour = C_BLUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
enum CELLTYPE getcellempty(cell_t *c) {
|
|
flag_t *f;
|
|
if (c->habitat == c->map->habitat) {
|
|
f = hasflag(c->map->flags, F_CELLTYPEEMPTY);
|
|
if (f) return f->val[0];
|
|
}
|
|
return c->habitat->emptycelltype;
|
|
}
|
|
|
|
enum CELLTYPE getcellsolid(cell_t *c) {
|
|
flag_t *f;
|
|
if (c->habitat == c->map->habitat) {
|
|
f = hasflag(c->map->flags, F_CELLTYPESOLID);
|
|
if (f) return f->val[0];
|
|
}
|
|
return c->habitat->solidcelltype;
|
|
}
|
|
|
|
enum CELLTYPE getmapempty(map_t *m) {
|
|
flag_t *f;
|
|
f = hasflag(m->flags, F_CELLTYPEEMPTY);
|
|
if (f) return f->val[0];
|
|
return m->habitat->emptycelltype;
|
|
}
|
|
|
|
enum CELLTYPE getmapsolid(map_t *m) {
|
|
flag_t *f;
|
|
f = hasflag(m->flags, F_CELLTYPESOLID);
|
|
if (f) return f->val[0];
|
|
return m->habitat->solidcelltype;
|
|
}
|
|
|
|
enum DEPTH getcellwaterdepth(cell_t *c, lifeform_t *lf) {
|
|
object_t *o;
|
|
o = hasobwithflag(c->obpile, F_DEEPWATER);
|
|
if (o) {
|
|
return getobdepth(o, lf);
|
|
}
|
|
return DP_NONE;
|
|
}
|
|
|
|
|
|
// returns the closest cell next to 'dst', when coming from 'src'
|
|
// ie. if you start walking from src to dst, where will you end up?
|
|
cell_t *get_closest_adjcell(cell_t *src, cell_t *dst) {
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcell,i;
|
|
calcbresnham(src->map, src->x, src->y, dst->x, dst->y, retcell, &nretcell);
|
|
for (i = 1; i < nretcell; i++) {
|
|
if (retcell[i] == dst) {
|
|
return retcell[i-1];
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int getdoorlockdiff(int depth) {
|
|
return 19 + depth;
|
|
}
|
|
int getdoorsecretdiff(int depth) {
|
|
return 20 + (depth / 2);
|
|
}
|
|
|
|
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 (isoutdoors(m)) {
|
|
int x,y;
|
|
// depth is distance from 0,0
|
|
getmapcoords(m, &x, &y);
|
|
diff = (abs(x) + abs(y))+1;
|
|
} else {
|
|
diff = m->depth;
|
|
}
|
|
if (m->region) {
|
|
diff += m->region->rtype->depthmod;
|
|
diff += m->region->depthmod;
|
|
}
|
|
} else {
|
|
diff = rnd(1,MAXDEPTH);
|
|
}
|
|
return diff;
|
|
}
|
|
|
|
|
|
// forglyph will be true if we are using this function for the purpose
|
|
// of figuring out which glyph to draw
|
|
object_t *gettopobject(cell_t *where, int forglyph) {
|
|
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 (isdeadob(o)) {
|
|
} else if (hasflag(o->flags, F_SECRET) && !isdoor(o, NULL)) {
|
|
} else if (hasflag(o->flags, F_INVISOB)) {
|
|
} else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) {
|
|
} else if (forglyph && hasflag(o->flags, F_NOGLYPH)) {
|
|
} else {
|
|
f = hasflag(o->flags, F_IMPASSABLE);
|
|
if (f && (f->val[1] > largest)) {
|
|
largest = f->val[1];
|
|
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 (isdeadob(o)) {
|
|
} else if (hasflag(o->flags, F_SECRET)) {
|
|
} else if (hasflag(o->flags, F_INVISOB)) {
|
|
} else if (hasflag(o->flags, F_TRAIL) && !canseeob(player, o)) {
|
|
} else if (forglyph && hasflag(o->flags, F_NOGLYPH)) {
|
|
} else {
|
|
return o;
|
|
}
|
|
}
|
|
}
|
|
c++;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void calclight(map_t *map) {
|
|
int x,y;
|
|
cell_t *c;
|
|
int i;
|
|
|
|
// disable screen redraws
|
|
noredraw = B_TRUE;
|
|
|
|
// 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 (isnighttime()) {
|
|
// ie. nighttime, after 7pm or before 5am
|
|
} else {
|
|
// ie. daytime
|
|
makelit(c, L_PERMLIGHT, -1);
|
|
}
|
|
}
|
|
*/
|
|
//if ((map->depth <= 5) && (c->lit != L_PERMDARK)) {
|
|
if ((map->illumination == IL_FULLLIT) && (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, NULL);
|
|
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);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// re-enable screen redraws
|
|
noredraw = B_FALSE;
|
|
|
|
// did any lit values within player's los change?
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
int dolos = B_FALSE;
|
|
if (!isblind(player)) {
|
|
for (i = 0; i < player->nlos; i++) {
|
|
if (player->los[i]->lastlit != player->los[i]->lit) {
|
|
dolos = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (dolos) {
|
|
setlosdirty(player);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int *by, int force) {
|
|
int x,y,i;
|
|
int bestscore = 9888;
|
|
coord_t coord[MAX_MAPW*MAX_MAPH];
|
|
int coordscore[MAX_MAPW*MAX_MAPH];
|
|
int ncoords = 0;
|
|
coord_t poss[MAX_MAPW*MAX_MAPH];
|
|
int nposs = 0;
|
|
cell_t *cell;
|
|
int sel;
|
|
int db = B_FALSE;
|
|
int foundvalid = B_FALSE;
|
|
|
|
// init coords list
|
|
for (y = 1 + ymargin; y < (map->h - ymargin); y++) {
|
|
for (x = 1 + xmargin; x < (map->w - xmargin); x++) {
|
|
coord[ncoords].x = x;
|
|
coord[ncoords].y = y;
|
|
ncoords++;
|
|
}
|
|
}
|
|
|
|
// try placing room at all positions
|
|
for (i = 0; i < ncoords; i++) {
|
|
int score = 9999;
|
|
x = coord[i].x;
|
|
y = coord[i].y;
|
|
// would the room fit here?
|
|
if ( ((x + w) <= (map->w - xmargin)) &&
|
|
((y + h) <= (map->h - ymargin))) {
|
|
int valid = B_TRUE;
|
|
int rx,ry;
|
|
score = 0;
|
|
// calculate score if we placed the room with its top left corner here.
|
|
for (ry = y; (ry < y+h) && valid; ry++) {
|
|
for (rx = x; (rx < x+w) && valid; rx++) {
|
|
int includethiscell = B_FALSE;
|
|
cell = getcellat(map, rx,ry);
|
|
|
|
// NEVER create a room whcih will:
|
|
// - be on top of the player (normally this can't happen,
|
|
// but debugging via 'create vault' could do it)
|
|
if (cell->lf && isplayer(cell->lf)) {
|
|
valid = B_FALSE;
|
|
}
|
|
// - be on top of an existing staircase
|
|
if (hasobwithflag(cell->obpile, F_CLIMBABLE)) {
|
|
valid = B_FALSE;
|
|
}
|
|
/*
|
|
// - overlap the inside of an existing room
|
|
if (cellwalkable(NULL, cell, NULL) && isroom(cell)) {
|
|
valid = B_FALSE;
|
|
}
|
|
// - overlap any part of an existing vault
|
|
if (cell->room && cell->room->vault) {
|
|
valid = B_FALSE;
|
|
}
|
|
*/
|
|
// - overlap any part of an existing room/vault
|
|
if (cell->room) {
|
|
valid = B_FALSE;
|
|
}
|
|
// - overlap a 'locked' cell
|
|
if (cell->locked) {
|
|
valid = B_FALSE;
|
|
}
|
|
|
|
// is this cell adjacent to an empty cell and not a
|
|
// corner (ie. a valid door location)
|
|
if (countcellexits(cell, DT_ORTH)) {
|
|
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. don't check this cell for scores.
|
|
} else {
|
|
includethiscell = B_TRUE;
|
|
}
|
|
} else {
|
|
// not adjacent to any empty cells. ok.
|
|
includethiscell = B_TRUE;
|
|
}
|
|
if (includethiscell) {
|
|
// is this cell empty itself?
|
|
if (!cell->type->solid) score += 3;
|
|
// avoid being adjacent to other room walls
|
|
if (countcellexits(cell, DT_ORTH)) score++;
|
|
|
|
score += (countadjrooms(cell)*3);
|
|
|
|
// overlapping another room?
|
|
if (isroom(cell)) {
|
|
if (force) {
|
|
score += 10;
|
|
} else {
|
|
valid = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (valid) {
|
|
if (score < bestscore) {
|
|
if (db) dblog("new best score: %d (topleft at %d,%d)",score,x,y);
|
|
bestscore = score;
|
|
foundvalid = B_TRUE;
|
|
}
|
|
} else {
|
|
score = 9999;
|
|
}
|
|
} else {
|
|
if (db) dblog("cell %d,%d - a %dx%d room would not fit here",x,y,w,h);
|
|
}
|
|
coordscore[i] = score;
|
|
if (db) dblog("topleft at %d,%d => score %d",x,y,score);
|
|
}
|
|
|
|
if (foundvalid) {
|
|
// now go through and make a list of all BEST positions
|
|
nposs = 0;
|
|
assert(bestscore != 9888);
|
|
for (i = 0; i < ncoords; i++) {
|
|
if (coordscore[i] == bestscore) {
|
|
poss[nposs++] = coord[i];
|
|
}
|
|
}
|
|
} else {
|
|
nposs = 0;
|
|
}
|
|
|
|
if (nposs == 0) {
|
|
if (db) dblog("calcroompos - zero possibilities!");
|
|
*bx = -1;
|
|
*by = -1;
|
|
return B_TRUE;
|
|
} else {
|
|
// pick a random one
|
|
sel = rnd(0,nposs-1);
|
|
if (db) dblog("calcroompos - %d possibilities (bestscore=%d), selected #%d.", nposs, bestscore, sel);
|
|
|
|
*bx = poss[sel].x;
|
|
*by = poss[sel].y;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
int countadjcellswithflag(cell_t *cell, enum FLAG fid, int dirtype) {
|
|
int d;
|
|
int count = 0;
|
|
int start, end;
|
|
cell_t *newcell;
|
|
if (dirtype == DT_ORTH) {
|
|
start = D_N;
|
|
end = D_W;
|
|
} else {
|
|
start = DC_N;
|
|
end = DC_NW;
|
|
}
|
|
for (d = start; d <= end; d++) {
|
|
newcell = getcellindir(cell, d);
|
|
if (newcell && hasobwithflag(newcell->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 countadjrooms(cell_t *cell) {
|
|
int d;
|
|
int count = 0;
|
|
cell_t *newcell;
|
|
for (d = D_N; d < MAXDIR_ORTH; d++) {
|
|
newcell = getcellindir(cell, d);
|
|
if (newcell && isroom(newcell)) {
|
|
count++;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countadjdoors(cell_t *cell) {
|
|
int d;
|
|
int doors = 0;
|
|
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
cell_t *newcell;
|
|
newcell = getcellindir(cell, d);
|
|
if (newcell && hasobwithflag(newcell->obpile, F_DOOR)) {
|
|
doors++;
|
|
}
|
|
}
|
|
return doors;
|
|
}
|
|
|
|
int countadjwalls(cell_t *cell) {
|
|
int d;
|
|
int walls = 0;
|
|
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
cell_t *newcell;
|
|
newcell = getcellindir(cell, d);
|
|
if (!newcell || newcell->type->solid) {
|
|
walls++;
|
|
}
|
|
}
|
|
return walls;
|
|
}
|
|
|
|
int countcellexits(cell_t *cell, int dirtype) {
|
|
int d;
|
|
int exits = 0;
|
|
int start,end;
|
|
cell_t *newcell;
|
|
assert(cell);
|
|
if (dirtype == DT_ORTH) {
|
|
start = D_N;
|
|
end = D_W;
|
|
} else { // ie. dt_compass
|
|
start = DC_N;
|
|
end = DC_NW;
|
|
}
|
|
|
|
for (d = start; d <= end; d++) {
|
|
newcell = getcellindir(cell, d);
|
|
if (newcell && !newcell->type->solid) {
|
|
exits++;
|
|
}
|
|
}
|
|
return exits;
|
|
}
|
|
|
|
int countcellexitsfor(lifeform_t *lf) {
|
|
int d;
|
|
int exits = 0;
|
|
assert(lf);
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
if (moveclear(lf, d, NULL)) {
|
|
exits++;
|
|
}
|
|
}
|
|
return exits;
|
|
}
|
|
|
|
int countlfs(map_t *map) {
|
|
lifeform_t *lf;
|
|
int count = 0;
|
|
for (lf = map->lf ; lf ; lf = lf->next) {
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countmapobs(map_t *m, enum OBTYPE oid) {
|
|
cell_t *c;
|
|
int count = 0,x,y;
|
|
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
count += countobsoftype(c->obpile, oid);
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countmapobswithflag(map_t *m, enum FLAG flagid) {
|
|
cell_t *c;
|
|
int count = 0,x,y;
|
|
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
count += countobswithflag(c->obpile, flagid);
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countmapobswithflagval(map_t *m, enum FLAG flagid, int val0, int val1, int val2, char *text) {
|
|
cell_t *c;
|
|
int count = 0,x,y;
|
|
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
count += countobswithflagval(c->obpile, flagid, val0, val1, val2, text);
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int countstairs(map_t *m, int dir) {
|
|
int count;
|
|
count = countmapobswithflagval(m, F_CLIMBABLE, dir, NA, NA, NULL);
|
|
// don't include pits
|
|
count -= countmapobswithflagval(m, F_PIT, dir, NA, NA, NULL);
|
|
return count;
|
|
}
|
|
|
|
void createborder(map_t *map, enum CELLTYPE solidtype) {
|
|
int x,y;
|
|
cell_t *c;
|
|
// now do a border
|
|
y = 0;
|
|
for (x = 0; x < map->w; x++) {
|
|
// n
|
|
c = getcellat(map, x, 0);
|
|
clearcell(c);
|
|
setcelltype(c,solidtype);
|
|
c->locked = B_TRUE;
|
|
// s
|
|
c = getcellat(map, x, map->h-1);
|
|
clearcell(c);
|
|
setcelltype(c,solidtype);
|
|
c->locked = B_TRUE;
|
|
}
|
|
for (y = 1; y < map->h-1; y++) {
|
|
// w
|
|
c = getcellat(map, 0, y);
|
|
clearcell(c);
|
|
setcelltype(c,solidtype);
|
|
c->locked = B_TRUE;
|
|
// e
|
|
c = getcellat(map, map->w-1, y);
|
|
clearcell(c);
|
|
setcelltype(c,solidtype);
|
|
c->locked = B_TRUE;
|
|
}
|
|
}
|
|
|
|
void createcave(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
int wantrooms = B_TRUE;
|
|
int x,y,i;
|
|
int numrooms = 0;
|
|
cell_t *c;
|
|
//int entrylinked = B_FALSE;
|
|
//object_t *o;
|
|
//int db = B_TRUE;
|
|
enum CELLTYPE emptycell,solidcell;
|
|
|
|
// parameters
|
|
int minrooms = 0;
|
|
int maxrooms = 3;
|
|
int numstartpos = 5;
|
|
int numpasses = 50;
|
|
|
|
|
|
// fill entire maze with walls
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
addcell(map, x, y);
|
|
}
|
|
}
|
|
|
|
|
|
// is the map lit?
|
|
if (depth == 1) {
|
|
map->illumination = IL_FULLLIT;
|
|
} else {
|
|
int darkchance;
|
|
darkchance = depth*15; // ie. level 2 = 30% chance of being dark, level 6 = 90% chance
|
|
if (pctchance(darkchance)) {
|
|
map->illumination = IL_FULLDARK;
|
|
} else {
|
|
map->illumination = IL_FULLLIT;
|
|
}
|
|
}
|
|
|
|
// what kind of cells will 'empty' ones be?
|
|
emptycell = getmapempty(map);
|
|
solidcell = getmapsolid(map);
|
|
|
|
// pick initial random points
|
|
for (i = 0; i < numstartpos; i++) {
|
|
c = getrandomcell(map);
|
|
setcelltype(c, emptycell);
|
|
}
|
|
|
|
expand_cave(map, numpasses);
|
|
|
|
// adjust min/maxrooms based on fixed vaults
|
|
minrooms -= map->nfixedrooms;
|
|
maxrooms -= map->nfixedrooms;
|
|
limit(&minrooms, 0, NA);
|
|
limit(&maxrooms, 0, NA);
|
|
|
|
// create rooms
|
|
if (wantrooms && (maxrooms > 0)) {
|
|
numrooms = rnd(minrooms, maxrooms);
|
|
|
|
for (i = 0; i < numrooms; i++) {
|
|
// maybe make it a special room
|
|
if (rnd(1,100) <= map->habitat->randvaultpct) {
|
|
vault_t *v;
|
|
v = getvaulttype(map);
|
|
if (!createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) {
|
|
// success
|
|
continue;
|
|
}
|
|
}
|
|
// just do a normal room
|
|
calcposandmakeroom(map, map->nrooms, NA, NA, DEF_VAULTMARGIN, DEF_VAULTMARGIN, NULL, NULL, NULL, NULL, 50, B_FALSE);
|
|
//roomvault[i] = B_FALSE;
|
|
}
|
|
}
|
|
|
|
// link up room exits
|
|
/*
|
|
for (i = 0; i < map->nrooms; i++) {
|
|
int wantlink = B_FALSE;
|
|
if (!map->room[i].exitslinked) {
|
|
if (map->room[i].vault && hasflag(map->room[i].vault->flags, F_VAULTNOLINK)) {
|
|
} else {
|
|
wantlink = B_TRUE;
|
|
}
|
|
}
|
|
if (wantlink) {
|
|
linkexits(map, map->room[i].id);
|
|
}
|
|
}
|
|
*/
|
|
|
|
// now do a border
|
|
createborder(map, solidcell);
|
|
}
|
|
//
|
|
void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
int wantrooms = B_TRUE;
|
|
int d;
|
|
int x,y,w,h;
|
|
enum MAPSHAPE shape = MS_NORMAL;
|
|
int i;
|
|
int done,unused;
|
|
int dir;
|
|
int lastdir;
|
|
int numrooms = 0;
|
|
cell_t *c,*centre;
|
|
//object_t *o;
|
|
int db = B_TRUE;
|
|
int digdb = B_FALSE;
|
|
|
|
// parameters
|
|
int turnpct = DEF_TURNPCT;
|
|
int sparseness = DEF_SPARSENESS;
|
|
int looppct = DEF_LOOPPCT;
|
|
int minrooms = MINROOMS;
|
|
int maxrooms = MAXROOMS;
|
|
enum CORRIDORTYPE corridortype = CDT_NORMAL;
|
|
|
|
int moved = 0;
|
|
|
|
enum CELLTYPE emptycell,solidcell;
|
|
char buf[BUFLEN];
|
|
|
|
// fill entire maze with walls
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
addcell(map, x, y);
|
|
}
|
|
}
|
|
|
|
// select dungeon shape.
|
|
if (onein(3)) {
|
|
shape = rnd(0,MAXMAPSHAPES-1);
|
|
} else {
|
|
shape = MS_NORMAL;
|
|
}
|
|
switch (shape) {
|
|
case MS_NORMAL:// normal
|
|
break;
|
|
case MS_CROSS: // cross
|
|
// ## ##
|
|
// ## ##
|
|
//
|
|
// ## ##
|
|
// ## ##
|
|
for (y = 0; y < map->h/3; y++) {
|
|
for (x = 0; x < map->w/3; x++) {
|
|
c = getcellat(map, x, y); c->locked = B_TRUE;
|
|
}
|
|
for (x = map->w - (map->w/3); x < map->w; x++) {
|
|
c = getcellat(map, x, y); c->locked = B_TRUE;
|
|
}
|
|
}
|
|
for (y = map->h-(map->h/3); y < map->h; y++) {
|
|
for (x = 0; x < map->w/3; x++) {
|
|
c = getcellat(map, x, y); c->locked = B_TRUE;
|
|
}
|
|
for (x = map->w - (map->w/3); x < map->w; x++) {
|
|
c = getcellat(map, x, y); c->locked = B_TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case MS_TURRET: // kind of a reverse cross
|
|
// ##########
|
|
// # #
|
|
// # ## #
|
|
// # ###### #
|
|
// # ###### #
|
|
// # ###### #
|
|
// # ## #
|
|
// # #
|
|
// ##########
|
|
for (y = (map->h/4); y < map->h/3; y++) {
|
|
for (x = map->w/3; x < map->w - (map->w/3); x++) {
|
|
c = getcellat(map, x, y); c->locked = B_TRUE;
|
|
}
|
|
}
|
|
for (y = map->h/3; y < map->h - (map->h/3); y++) {
|
|
for (x = map->w/4; x < map->w - (map->w/4); x++) {
|
|
c = getcellat(map, x, y); c->locked = B_TRUE;
|
|
}
|
|
}
|
|
for (y = map->h-(map->h/3); y < map->h - (map->h/4); y++) {
|
|
for (x = map->w/3; x < map->w - (map->w/3); x++) {
|
|
c = getcellat(map, x, y); c->locked = B_TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case MS_CIRCLE: // circle / ellipse
|
|
centre = getcellat(map, map->w/2, map->h/2);
|
|
w = map->w - 5;
|
|
h = map->h - 4;
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
float val;
|
|
val = (pow(x - centre->x, 2) / pow(w/2, 2)) +
|
|
(pow(y - centre->y, 2) / pow(h/2, 2));
|
|
if (val > 1) {
|
|
c = getcellat(map, x, y);
|
|
c->locked = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
addflag(map->flags, F_MAPSHAPE, shape, NA, NA, NULL);
|
|
|
|
for (y = 0; y < map->h; y++) {
|
|
strcpy(buf, "");
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
if (c->locked) {
|
|
c->visited = B_TRUE;
|
|
}
|
|
if (c->locked) {
|
|
strcat(buf, "X");
|
|
} else {
|
|
strcat(buf, " ");
|
|
}
|
|
}
|
|
dblog("%s",buf);
|
|
}
|
|
|
|
// randomise dungeon parameters
|
|
turnpct += (rnd(0,40)-20); // (-20 to +20)%
|
|
sparseness += (rnd(0,20)-10); // -10 to +10
|
|
looppct -= (rnd(0,10)); // subtrace 0 - 10
|
|
|
|
if (shape == MS_NORMAL) {
|
|
if (onein(2)) {
|
|
corridortype = CDT_SIMPLE;
|
|
}
|
|
}
|
|
|
|
|
|
// is the map lit?
|
|
/*
|
|
|
|
XXX: have removed this code for now as light recalc code is VERY slow.
|
|
|
|
if (depth <= 5) {
|
|
map->illumination = IL_FULLLIT;
|
|
} else {
|
|
int chance;
|
|
chance = (depth - 5) * 10;
|
|
if (pctchance(chance)) {
|
|
map->illumination = rnd(IL_WELLLIT,IL_FULLDARK);
|
|
} else {
|
|
map->illumination = IL_FULLLIT;
|
|
}
|
|
}
|
|
*/
|
|
map->illumination = IL_FULLLIT;
|
|
|
|
// random chance of different wall type
|
|
if (onein(6)) {
|
|
switch (rnd(1,3)) {
|
|
case 1:
|
|
addflag(map->flags, F_CELLTYPESOLID, CT_WALLBRICK, NA, NA, NULL);
|
|
break;
|
|
case 2:
|
|
addflag(map->flags, F_CELLTYPESOLID, CT_WALLMETAL, NA, NA, NULL);
|
|
break;
|
|
case 3:
|
|
addflag(map->flags, F_CELLTYPESOLID, CT_WALLWOOD, NA, NA, NULL);
|
|
break;
|
|
}
|
|
}
|
|
// random chance of different floor type
|
|
if (onein(6)) {
|
|
switch (rnd(1,3)) {
|
|
case 1:
|
|
addflag(map->flags, F_CELLTYPEEMPTY, CT_FLOORCARPET, NA, NA, NULL);
|
|
break;
|
|
case 2:
|
|
addflag(map->flags, F_CELLTYPEEMPTY, CT_FLOORTILE, NA, NA, NULL);
|
|
break;
|
|
case 3:
|
|
addflag(map->flags, F_CELLTYPEEMPTY, CT_FLOORWOOD, NA, NA, NULL);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
// what kind of cells will 'empty' ones be?
|
|
emptycell = getmapempty(map);
|
|
solidcell = getmapsolid(map);
|
|
|
|
// redo all map cells as selected type
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
setcelltype(c, solidcell);
|
|
}
|
|
}
|
|
|
|
// pick initial random spot
|
|
if (corridortype == CDT_NORMAL) {
|
|
c = getrandomcell(map);
|
|
setcelltype(c, emptycell);
|
|
c->visited = B_TRUE;
|
|
if (digdb) printf("- Starting (%d,%d)\n",c->x, c->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) && !done) {
|
|
int badcount;
|
|
if (digdb) printf("- At (%d,%d), moved %d, finding new direction...\n",c->x, c->y, moved);
|
|
|
|
dir = getnewdigdir(c, 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
|
|
c = getrandomcell(map);
|
|
while (!isempty(c)) {
|
|
c = getrandomcell(map);
|
|
}
|
|
if (digdb) printf("--- Couldn't find a valid direction. Jumped to (%d,%d).\n",c->x, c->y);
|
|
// pick a new random dir
|
|
dir = getnewdigdir(c, lastdir, turnpct, &moved);
|
|
}
|
|
if (!done) {
|
|
if (digdb) printf("- Digging %s from (%d,%d).\n",getdirname(dir),c->x, c->y);
|
|
}
|
|
}
|
|
|
|
if (!done) {
|
|
// move to adjacent cell in the given direction
|
|
c = getcellindir(c, dir);
|
|
if (digdb) printf("- Now at (%d,%d)\n",c->x, c->y);
|
|
moved++;
|
|
|
|
// blank it
|
|
setcelltype(c,emptycell);
|
|
c->visited = B_TRUE;
|
|
// mark surrounding cells as visited
|
|
for (d = DC_N; d < MAXDIR_COMPASS; d++) {
|
|
cell_t *thiscell;
|
|
thiscell = getcellindir(c, d);
|
|
if (thiscell) {
|
|
if (digdb) 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;
|
|
}
|
|
}
|
|
|
|
if (digdb) dblog("%d unused cell(s)\n",unused);
|
|
//dumpmap(map);
|
|
//getchar();
|
|
|
|
}
|
|
|
|
// use sparseness to cut down dead ends
|
|
remove_deadends(map, sparseness);
|
|
|
|
// introduce loops
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
if (!c->type->solid && countcellexits(c, DT_ORTH) == 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(c, 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(c, 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(c, 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;
|
|
}
|
|
c = newcell;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// adjust min/maxrooms based on fixed vaults
|
|
minrooms -= map->nfixedrooms;
|
|
maxrooms -= map->nfixedrooms;
|
|
limit(&minrooms, 0, NA);
|
|
limit(&maxrooms, 0, NA);
|
|
|
|
// create rooms
|
|
if (wantrooms && (maxrooms > 0)) {
|
|
numrooms = rnd(minrooms, maxrooms);
|
|
|
|
//printf("using %d rooms\n",numrooms);
|
|
//dblog("Adding %d rooms...\n",numrooms);
|
|
for (i = 0; i < numrooms; i++) {
|
|
// maybe make it a special room
|
|
//roomvault[i] = NULL;
|
|
if (rnd(1,100) <= map->habitat->randvaultpct) {
|
|
vault_t *v;
|
|
v = getvaulttype(map);
|
|
if (!createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) {
|
|
// success
|
|
continue;
|
|
}
|
|
}
|
|
// just do a normal room
|
|
calcposandmakeroom(map, map->nrooms, NA, NA, DEF_VAULTMARGIN, DEF_VAULTMARGIN, NULL, NULL, NULL, NULL, 50, B_FALSE);
|
|
//roomvault[i] = B_FALSE;
|
|
}
|
|
}
|
|
|
|
if (corridortype == CDT_NORMAL) {
|
|
// now clear up dead ends again.
|
|
remove_deadends(map, sparseness);
|
|
}
|
|
|
|
// link up room exits
|
|
for (i = 0; i < map->nrooms; i++) {
|
|
int wantlink = B_FALSE;
|
|
if (!map->room[i].exitslinked) {
|
|
if (map->room[i].vault && hasflag(map->room[i].vault->flags, F_VAULTNOLINK)) {
|
|
} else {
|
|
wantlink = B_TRUE;
|
|
}
|
|
}
|
|
if (wantlink) {
|
|
linkexits(map, map->room[i].id);
|
|
}
|
|
}
|
|
|
|
// add pillars & objects & monsters to rooms
|
|
if (wantrooms && (numrooms > 0)) {
|
|
for (i = 0; i < map->nrooms; i++) {
|
|
if (!map->room[i].vault || hasflag(map->room[i].vault->flags, F_AUTOPOPULATE)) {
|
|
int numobsmin,numobsmax,numobs,n;
|
|
int maxpillars;
|
|
int rw,rh;
|
|
|
|
rw = map->room[i].x2 - map->room[i].x1;
|
|
rh = map->room[i].y2 - map->room[i].y1;
|
|
|
|
//dblog("Adding obs to room %d/%d",i+1,numrooms);
|
|
maxpillars = (rw / 4) + (rh / 4);
|
|
// add pillars first
|
|
if ((maxpillars > 0) && pctchance(PCTCH_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);
|
|
c = getrandomroomcell(map, i, WE_EMPTY);
|
|
|
|
if (c && !countobs(c->obpile, B_TRUE)) {
|
|
setcelltype(c, solidcell);
|
|
}
|
|
}
|
|
}
|
|
|
|
numobsmin = 0;
|
|
numobsmax = MAXOF(rw,rh) / 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;
|
|
done = B_FALSE;
|
|
while (!done) {
|
|
c = getrandomroomcell(map, i, WE_WALKABLE);
|
|
// if nothing there
|
|
if (c) {
|
|
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 = c->habitat->randobpct + 10;
|
|
}
|
|
|
|
if (addrandomthing(c,obchance, &nadded) == TT_MONSTER) {
|
|
nmonsters += nadded;
|
|
}
|
|
done = B_TRUE;
|
|
} else {
|
|
ntries++;
|
|
}
|
|
|
|
if (ntries >= numobs) {
|
|
done = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} // end if !vault
|
|
} // end foreach room
|
|
} // end if wantrooms & nrooms>0
|
|
if (db) dblog("Finished adding stuff to rooms.");
|
|
|
|
|
|
// river?
|
|
if ((depth >= 4) && pctchance(20)) {
|
|
createriver(map);
|
|
}
|
|
|
|
// now do a border
|
|
y = 0;
|
|
for (x = 0; x < map->w; x++) {
|
|
// n
|
|
c = getcellat(map, x, 0);
|
|
clearcell(c);
|
|
setcelltype(c,solidcell);
|
|
c->locked = B_TRUE;
|
|
// s
|
|
c = getcellat(map, x, map->h-1);
|
|
clearcell(c);
|
|
setcelltype(c,solidcell);
|
|
c->locked = B_TRUE;
|
|
}
|
|
for (y = 1; y < map->h-1; y++) {
|
|
// w
|
|
c = getcellat(map, 0, y);
|
|
clearcell(c);
|
|
setcelltype(c,solidcell);
|
|
c->locked = B_TRUE;
|
|
// e
|
|
c = getcellat(map, map->w-1, y);
|
|
clearcell(c);
|
|
setcelltype(c,solidcell);
|
|
c->locked = B_TRUE;
|
|
}
|
|
}
|
|
|
|
void createfakes(map_t *map, cell_t *cell) {
|
|
map->region = NULL;
|
|
map->lf = NULL;
|
|
map->lastlf = NULL;
|
|
map->name = strdup("fake map");
|
|
map->habitat = findhabitat(H_DUNGEON);
|
|
cell->map = map;
|
|
cell->lf = NULL;
|
|
cell->obpile = addobpile(NULL, NULL, NULL);
|
|
cell->room = NULL;
|
|
setcelltype(cell, CT_FAKE);
|
|
}
|
|
|
|
void createforest(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob, int nclearings) {
|
|
int x,y;
|
|
enum CELLTYPE emptycell;
|
|
enum CELLTYPE solidcell;
|
|
int i;
|
|
int ntrees;
|
|
int density;
|
|
cell_t *c;
|
|
char buf[BUFLEN];
|
|
cell_t *retcell[MAXCANDIDATES];
|
|
int nretcells;
|
|
int numrooms = 0;
|
|
//object_t *o;
|
|
|
|
// fill entire maze with emptiness
|
|
emptycell = getmapempty(map);
|
|
solidcell = getmapsolid(map);
|
|
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = addcell(map, x, y);
|
|
setcelltype(c, onein(4) ? CT_DIRT : emptycell );
|
|
}
|
|
}
|
|
|
|
|
|
switch (rnd(1,2)) {
|
|
case 1: // forest type 1: add trees in x% of cells, then add some clearings
|
|
// 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);
|
|
if (onein(2)) {
|
|
killallobs(c->obpile);
|
|
setcelltype(c, solidcell);
|
|
} else {
|
|
setcelltype(c, onein(2) ? emptycell : CT_DIRT);
|
|
switch (rnd(0,1)) {
|
|
default: case 0: strcpy(buf, "tree"); break;
|
|
case 1: strcpy(buf, "shrub"); break;
|
|
}
|
|
addob(c->obpile, buf);
|
|
}
|
|
}
|
|
// clearings
|
|
for (i = 0; i < nclearings; i++) {
|
|
int w,n;
|
|
c = getrandomcell(map);
|
|
w = rnd(MINCLEARINGRADIUS,MAXCLEARINGRADIUS);
|
|
// clear obs in all clearing cells
|
|
getradiuscells(c, w, DT_ORTH, B_FALSE, LOF_DONTNEED, B_TRUE, retcell, &nretcells, B_FALSE);
|
|
for (n = 0; n < nretcells; n++) {
|
|
setcelltype(c, emptycell);
|
|
// kill all obs here
|
|
while (retcell[n]->obpile->first) killob(retcell[n]->obpile->first);
|
|
}
|
|
|
|
// fill some cells with dirt
|
|
getradiuscells(c, w, DT_ORTH,B_FALSE, LOF_DONTNEED, B_TRUE, retcell, &nretcells, 70);
|
|
for (n = 0; n < nretcells; n++) {
|
|
setcelltype(retcell[n], CT_DIRT);
|
|
}
|
|
}
|
|
break;
|
|
case 2: // add clusters of trees
|
|
nclearings = rnd(10,15);
|
|
for (i = 0; i < nclearings; i++) {
|
|
int w,n;
|
|
c = getrandomcell(map);
|
|
while (c->lf) c = getrandomcell(map);
|
|
|
|
w = rnd(MINCLEARINGRADIUS,MAXCLEARINGRADIUS);
|
|
|
|
getradiuscells(c, w, DT_ORTH, B_FALSE, LOF_DONTNEED, B_TRUE, retcell, &nretcells, 80);
|
|
for (n = 0; n < nretcells; n++) {
|
|
if (onein(2)) {
|
|
killallobs(retcell[n]->obpile);
|
|
setcelltype(retcell[n], solidcell);
|
|
} else {
|
|
setcelltype(retcell[n], onein(2) ? emptycell : CT_DIRT);
|
|
switch (rnd(0,1)) {
|
|
default: case 0: strcpy(buf, "tree"); break;
|
|
case 1: strcpy(buf, "shrub"); break;
|
|
}
|
|
addob(retcell[n]->obpile, buf);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
// random vaults
|
|
numrooms = rnd(MINROOMS_WOODS, MAXROOMS_WOODS);
|
|
for (i = 0; i < numrooms; i++) {
|
|
vault_t *v;
|
|
v = getvaulttype(map);
|
|
createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
createborder(map, CT_WALLTREE);
|
|
}
|
|
|
|
|
|
void createhabitat(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
map->nrooms = 0; // reset room counts
|
|
switch (map->habitat->id) {
|
|
case H_DUNGEON:
|
|
createdungeon(map, depth, parentmap, exitdir, entryob);
|
|
break;
|
|
case H_CAVE:
|
|
createcave(map, depth, parentmap, exitdir, entryob);
|
|
break;
|
|
case H_FOREST:
|
|
createforest(map, depth, parentmap, exitdir, entryob, rnd(0,5));
|
|
break;
|
|
case H_VILLAGE:
|
|
createforest(map, depth, parentmap, exitdir, entryob, 0);
|
|
break;
|
|
case H_HEAVEN:
|
|
createheaven(map, depth, parentmap, exitdir, entryob);
|
|
break;
|
|
case H_PIT:
|
|
createpit(map, depth, parentmap, exitdir, entryob);
|
|
break;
|
|
case H_SEWER:
|
|
createsewer(map, depth, parentmap, exitdir, entryob);
|
|
break;
|
|
case H_STOMACH:
|
|
createstomach(map, depth, parentmap, exitdir, entryob);
|
|
break;
|
|
case H_SWAMP:
|
|
createswamp(map, depth, parentmap, exitdir, entryob);
|
|
break;
|
|
case H_ALL:
|
|
dblog("ERROR - createhabitat with invalid habitat!");
|
|
msg("ERROR - createhabitat with invalid habitat!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void createheaven(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
int x,y;
|
|
enum CELLTYPE emptycell,solidcell;
|
|
cell_t *c;
|
|
race_t *r;
|
|
lifeform_t *lf;
|
|
int roomid = 0;
|
|
vault_t *v;
|
|
int i = -1;
|
|
//object_t *o;
|
|
|
|
// what kind of cells will 'empty' ones be?
|
|
emptycell = getmapempty(map);
|
|
solidcell = getmapsolid(map);
|
|
// 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, solidcell);
|
|
}
|
|
}
|
|
// add a random heaven vault
|
|
v = findvaultwithtag("heaven");
|
|
assert(v);
|
|
if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) {
|
|
dblog("ERROR - couldn't create heaven vault '%s' on map %s", v->id, map->name);
|
|
msg("ERROR - couldn't create heaven vault '%s' on map %s", v->id, map->name);
|
|
assert("failed to create realm of gods" == 0);
|
|
}
|
|
|
|
// add one of each god.
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
if (r->raceclass->id == RC_GOD) {
|
|
// find next valid position with a holy circle.
|
|
i++;
|
|
c = map->cell[i];
|
|
while (!hasob(c->obpile, OT_HOLYCIRCLE)) {
|
|
i++;
|
|
if (i >= map->w * map->h) {
|
|
dblog("ERROR - couldn't find home position for god!");
|
|
msg("ERROR - couldn't find home position for god!");
|
|
assert("failed to find home pos for god" == 0);
|
|
}
|
|
c = map->cell[i];
|
|
}
|
|
// place god
|
|
lf = addmonster(c, r->id, NULL, B_FALSE, 1, B_FALSE, NULL);
|
|
assert(lf);
|
|
// add to god list
|
|
godlf[ngodlfs++] = lf;
|
|
if (ngodlfs > MAXGODS) {
|
|
dblog("Error - number of gods(%d) exceeds MAXGODS.",ngodlfs);
|
|
msg("Error - number of gods(%d) exceeds MAXGODS.",ngodlfs); more();
|
|
exit(1);
|
|
}
|
|
roomid++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
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, region_t *region, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
lifeform_t *lf;
|
|
map_t *m;
|
|
char buf[BUFLEN],buf2[BUFLEN];
|
|
int i,x,y;
|
|
enum HABITAT habitat;
|
|
regionthing_t *thing[MAXOUTLINETHINGS];
|
|
int nthings = 0,failed,nstairslinked = 0;
|
|
int db = B_TRUE;
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// determine habitat based on region
|
|
// note: we might override this later based on our region outline
|
|
habitat = region->rtype->defaulthabitat;
|
|
|
|
/*
|
|
if ((region->rtype->id == RG_WORLDMAP) && (depth == 1)) {
|
|
firstworldmap = B_TRUE;
|
|
}
|
|
*/
|
|
map->beingcreated = B_TRUE;
|
|
|
|
map->depth = depth;
|
|
map->region = region;
|
|
|
|
if (db) {
|
|
getregionname(buf, map, NULL, RF_SHORT);
|
|
dblog("Creating new map of region '%s'",buf);
|
|
}
|
|
map->habitat = findhabitat(habitat);
|
|
|
|
map->nrooms = 0;
|
|
|
|
// link to other maps if required.
|
|
// default to no links
|
|
for (i = D_N; i <= D_W; i++) {
|
|
map->nextmap[i] = -1;
|
|
}
|
|
for (i = D_UP; i <= D_DOWN; i++) {
|
|
map->nextmap[i] = -1;
|
|
}
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// look for adjacent maps above/below this one
|
|
if (db) dblog(" look for adjacent maps above/below this one");
|
|
for (i = depth-1; i <= depth+1; i += 2) {
|
|
map_t *othermap;
|
|
othermap = findregionmap(map->region->id, i);
|
|
// TODO: set upmap/downmap for use later on.
|
|
if (othermap) {
|
|
if (i == (depth-1)) {
|
|
map->nextmap[D_UP] = othermap->id;
|
|
if (db) dblog(" found mapin dir d_up: %s", othermap->name);
|
|
} else {
|
|
map->nextmap[D_DOWN] = othermap->id;
|
|
if (db) dblog(" found mapin dir d_down: %s", othermap->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// did we come from a previous map in the same region?
|
|
if (parentmap && (exitdir != D_NONE)) {
|
|
if ((parentmap->region->id == map->region->id) ||
|
|
(map->region->rtype->majorbranch)) {
|
|
if (db) dblog(" linking to parentmap %s in dir %s", parentmap->name, getdirname(diropposite(exitdir)));
|
|
parentmap->nextmap[exitdir] = map->id;
|
|
assert(exitdir >= 0);
|
|
map->nextmap[diropposite(exitdir)] = parentmap->id;
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
/*
|
|
if (map->region->rtype->id == RG_WORLDMAP) {
|
|
map_t *adjmap;
|
|
for (i = D_N; i <= D_W; i++) {
|
|
// is there a map this dir from us???
|
|
adjmap = findmapcoords(, x, y);
|
|
if (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
|
|
// first world map??
|
|
if (map == firstmap) {
|
|
if (db) dblog(" map is the first world map. setting coords to 0,0.");
|
|
addflag(map->flags, F_MAPCOORDS, 0, 0, NA, NULL);
|
|
x = 0;
|
|
y = 0;
|
|
} else {
|
|
// set mapcoords if not already done.
|
|
if (!hasflag(map->flags, F_MAPCOORDS)) {
|
|
x = -999;
|
|
y = -999;
|
|
// set mapcoords based on parent map
|
|
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;
|
|
}
|
|
if (db) dblog(" setting map coords to %d,%d (based on parent map)",x,y);
|
|
} else {
|
|
// set it based on something else...
|
|
if (region->rtype->id == RG_WORLDMAP) {
|
|
// TODO: is this right???????????
|
|
// 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) {
|
|
if (db) dblog(" setting map coords to %d,%d (based on other region map)",x,y);
|
|
x = ff->val[0];
|
|
y = ff->val[1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
x = 0;
|
|
y = 0;
|
|
}
|
|
}
|
|
assert(x != -999);
|
|
assert(y != -999);
|
|
addflag(map->flags, F_MAPCOORDS, x, y, NA, NULL);
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// we now have the map name!
|
|
getregionname(buf2, map, NULL, RF_WITHLEVEL);
|
|
snprintf(buf, BUFLEN, "%s (id #%d)",buf2, map->id);
|
|
map->name = strdup(buf);
|
|
|
|
// get a list of what things are here based on the region's outline
|
|
map->nfixedrooms = 0;
|
|
nthings = 0;
|
|
if (region->outline) {
|
|
if (db) dblog(" checking region outline for things...");
|
|
for (i = 0; i < region->outline->nthings; i++) {
|
|
int matched = B_FALSE;
|
|
if (db) {
|
|
dblog(" Checking outlinething #%d (thing:depth=%d,x=%d,y=%d thismap:depth=%d,x=%d,y=%d ).", i,
|
|
region->outline->thing[i].depth,
|
|
region->outline->thing[i].x,
|
|
region->outline->thing[i].y,
|
|
depth,x,y
|
|
);
|
|
}
|
|
|
|
if ((region->rtype->id == RG_WORLDMAP) &&
|
|
(region->outline->thing[i].depth == NA)) { // match on x/y coords
|
|
if ((region->outline->thing[i].x == x) && (region->outline->thing[i].y == y)) {
|
|
matched = B_TRUE;
|
|
}
|
|
} else { // match on depth
|
|
if (region->outline->thing[i].depth == map->depth) {
|
|
matched = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (matched) {
|
|
// override region
|
|
if (region->outline->thing[i].whatkind == RT_HABITAT) {
|
|
habitat_t *h;
|
|
h = findhabitat(region->outline->thing[i].value);
|
|
if (db) dblog(" setting map habitat to %s based on outlinething.",h->name);
|
|
map->habitat = h;
|
|
} else {
|
|
if (db) dblog(" remembering region thing for later.");
|
|
// remember this thing
|
|
thing[nthings] = ®ion->outline->thing[i];
|
|
switch (thing[nthings]->whatkind) {
|
|
case RT_VAULT:
|
|
case RT_RNDVAULTWITHFLAG:
|
|
case RT_RNDVAULTWITHTAG:
|
|
// this will reduce the amount of random rooms which can
|
|
// be created on this map.
|
|
map->nfixedrooms++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
nthings++;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (db) dblog(" region has no outline.");
|
|
}
|
|
if (db) dblog(" %d things remembered for later.",nthings);
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
failed = B_TRUE;
|
|
while (failed) {
|
|
failed = B_FALSE;
|
|
// build it...
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
if (db) dblog(" creating map habitat.");
|
|
createhabitat(map, depth, parentmap, exitdir, entryob);
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// add home objects
|
|
if (db) dblog(" adding home objects.");
|
|
for (lf = map->lf ; lf ; lf = lf->next) {
|
|
addhomeobs(lf, B_TRUE);
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// add outline things
|
|
if (db) dblog(" adding remembered region outline things...");
|
|
for (i = 0; i < nthings ;i++) {
|
|
vault_t *v;
|
|
cell_t *c;
|
|
// add this thing
|
|
switch (thing[i]->whatkind) {
|
|
case RT_HABITAT: // already handled above
|
|
break;
|
|
case RT_OBJECT:
|
|
if (db) dblog(" adding forced regionthing object: %s", thing[i]->what);
|
|
c = getrandomroomcell(map, ANYROOM, WE_WALKABLE);
|
|
if (!c) c = getrandomcell(map);
|
|
c = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL);
|
|
addob(c->obpile, thing[i]->what);
|
|
break;
|
|
case RT_REGIONLINK:
|
|
if (db) dblog(" adding regionlink");
|
|
createregionlink(map, NULL, NULL, thing[i]->what, thing[i]->value, map->region);
|
|
// ... don't need to do this since we know there won't be anywhere to link to.
|
|
//linkstairs(o);
|
|
break;
|
|
case RT_VAULT:
|
|
if (db) dblog(" adding vault");
|
|
v = findvault(thing[i]->what);
|
|
assert(v);
|
|
if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) {
|
|
dblog("ERROR - couldn't create vault %s on map %s", v->id, map->name);
|
|
failed = B_TRUE;
|
|
}
|
|
break;
|
|
case RT_RNDVAULTWITHFLAG:
|
|
if (db) dblog(" adding rndvaultwithflag");
|
|
v = findvaultwithflag(thing[i]->value);
|
|
assert(v);
|
|
if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) {
|
|
dblog("ERROR - couldn't create rndvaultwithflag %s on map %s", v->id, map->name);
|
|
failed = B_TRUE;
|
|
}
|
|
break;
|
|
case RT_RNDVAULTWITHTAG:
|
|
if (db) dblog(" adding rndvaultwithtag");
|
|
v = findvaultwithtag(thing[i]->what);
|
|
assert(v);
|
|
if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) {
|
|
dblog("ERROR - couldn't create rndvaultwithtag %s on map %s", v->id, map->name);
|
|
failed = B_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// ensure there are no unreachable areas
|
|
if (!failed) {
|
|
if (fix_reachability(map)) {
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
if (failed) {
|
|
region_t *r,*nextr;
|
|
dblog("********* got errors - restarting map creation. *********");
|
|
// remove map flags added during vault creation.
|
|
killflagsofid(map->flags, F_MAPSHAPE);
|
|
killflagsofid(map->flags, F_ROOMEXIT);
|
|
// unlink stairs on adjacent maps
|
|
unlinkstairsto(map);
|
|
// remove regions created by regionlinks in this map.
|
|
for (r = firstregion ; r ; r = nextr ){
|
|
nextr = r->next;
|
|
if (r->createdbymapid == map->id) {
|
|
killregion(r);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
|
|
if (map->habitat->id == H_CAVE) {
|
|
// expand the cave a little more now that we've fixed reachability.
|
|
// this will help make any new corridors look more 'cave-like'.
|
|
expand_cave(map, 2);
|
|
}
|
|
|
|
// add any required stairs
|
|
finalisemap(map, entryob);
|
|
|
|
// special cases
|
|
// village - add town walls and clear it out
|
|
/*
|
|
if (db) dblog(" finalising village creation...");
|
|
if (map->habitat->id == H_VILLAGE) {
|
|
int x1 = 999,y1 = 999,x2 = -1,y2 = -1,x,y;
|
|
int gx[MAXDIR_ORTH],gy[MAXDIR_ORTH];
|
|
int guardx[MAXDIR_ORTH][2], guardy[MAXDIR_ORTH][2];
|
|
int dir;
|
|
cell_t *c;
|
|
// find outermost dimensions of shops
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
if (c && isroom(c)) {
|
|
if (c->x < x1) x1 = c->x;
|
|
if (c->y < y1) y1 = c->y;
|
|
if (c->x > x2) x2 = c->x;
|
|
if (c->y > y2) y2 = c->y;
|
|
}
|
|
}
|
|
}
|
|
|
|
// extend walls a bit more
|
|
x1 -= rnd(2,4);
|
|
y1 -= rnd(2,4);
|
|
x2 += rnd(2,4);
|
|
y2 += rnd(2,4);
|
|
limit(&x1, 0, map->w-1);
|
|
limit(&x2, 0, map->w-1);
|
|
limit(&y1, 0, map->h-1);
|
|
limit(&y2, 0, map->h-1);
|
|
|
|
// decide where the gate will be (not the corner)
|
|
for (dir = D_N; dir <= D_W; dir++) {
|
|
switch (dir) {
|
|
case D_N:
|
|
gx[dir] = rnd(x1+2,x2-2);
|
|
gy[dir] = y1;
|
|
guardx[dir][0] = gx[dir]-1;
|
|
guardy[dir][0] = gy[dir]+1;
|
|
guardx[dir][1] = gx[dir]+1;
|
|
guardy[dir][1] = gy[dir]+1;
|
|
break;
|
|
case D_E:
|
|
gx[dir] = x2;
|
|
gy[dir] = rnd(y1+2,y2-2);
|
|
guardx[dir][0] = gx[dir]-1;
|
|
guardy[dir][0] = gy[dir]-1;
|
|
guardx[dir][1] = gx[dir]-1;
|
|
guardy[dir][1] = gy[dir]+1;
|
|
break;
|
|
case D_S:
|
|
gx[dir] = rnd(x1+2,x2-2);
|
|
gy[dir] = y2;
|
|
guardx[dir][0] = gx[dir]-1;
|
|
guardy[dir][0] = gy[dir]-1;
|
|
guardx[dir][1] = gx[dir]+1;
|
|
guardy[dir][1] = gy[dir]-1;
|
|
break;
|
|
case D_W:
|
|
gx[dir] = x1;
|
|
gy[dir] = rnd(y1+2,y2-2);
|
|
guardx[dir][0] = gx[dir]+1;
|
|
guardy[dir][0] = gy[dir]-1;
|
|
guardx[dir][1] = gx[dir]+1;
|
|
guardy[dir][1] = gy[dir]+1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// fill in town walls and clearing
|
|
for (y = 0; y <= map->h; y++) {
|
|
for (x = 0; x <= map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
if (c) {
|
|
if ((c->x >= x1) && (c->y >= y1) &&
|
|
(c->x <= x2) && (c->y <= y2)) {
|
|
// ie. within the village
|
|
if (!c->type->solid) {
|
|
int i;
|
|
|
|
// mark as different habitat to outside
|
|
c->habitat = findhabitat(H_VILLAGE);
|
|
if (!isroom(c)) {
|
|
// get rid of objects (ie. trees) here
|
|
killallobs(c->obpile);
|
|
// make the ground dirt
|
|
setcelltype(c, CT_DIRT);
|
|
}
|
|
|
|
if ((c->x == x1) || (c->y == y1) ||
|
|
(c->x == x2) || (c->y == y2)) {
|
|
// town perimeter (walls)
|
|
if (!isroom(c)) {
|
|
clearcell(c);
|
|
setcelltype(c, CT_DIRT);
|
|
addob(c->obpile, "wooden fence");
|
|
}
|
|
}
|
|
|
|
for (dir = D_N; dir <= D_W; dir++) {
|
|
// town gate location
|
|
if ((c->x == gx[dir]) && (c->y == gy[dir])) {
|
|
int d2;
|
|
cell_t *c2;
|
|
clearcell(c);
|
|
setcelltype(c, CT_DIRT);
|
|
addob(c->obpile, "wooden gate");
|
|
// also make surrounding forest cells be dirt and no trees,
|
|
for (d2 = DC_N; d2 <= DC_NW; d2++) {
|
|
c2 = getcellindir(c, d2);
|
|
if (c2 && (c2->habitat->id != H_VILLAGE)) {
|
|
clearcell(c2);
|
|
setcelltype(c2, CT_DIRT);
|
|
}
|
|
}
|
|
}
|
|
// village guards
|
|
for (i = 0; i < 1; i++) {
|
|
if ((c->x == guardx[dir][i]) && (c->y == guardy[dir][i])) {
|
|
addmonster(c, R_TOWNGUARD, NULL, B_FALSE, 1, B_TRUE, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// not within the village
|
|
c->habitat = findhabitat(H_FOREST);
|
|
}
|
|
} // end if c
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
// try to join up any unlinked staircases in this map.
|
|
if (db) dblog(" joining unlinked stairs...");
|
|
nstairslinked = 0;
|
|
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 && (o->type->id != OT_PORTAL) && !getstairdestination(o, NULL) && !hasflag(o->flags, F_MAPLINK)) {
|
|
// this will join these stairs to existing ones on
|
|
// existing adjacent maps
|
|
if (!linkstairs(o, NULL)) {
|
|
if (db) {
|
|
cell_t *dst;
|
|
dst = getstairdestination(o, NULL);
|
|
dblog(" linked '%s' to map %s",o->type->name, dst->map->name);
|
|
}
|
|
nstairslinked++;
|
|
} else {
|
|
if (db) {
|
|
dblog(" FAILED to link stairs: '%s'",o->type->name);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (db) dblog(" linked %d stairs.", nstairslinked);
|
|
|
|
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
// link up holes - this will create NEW holes in THIS map connecting to
|
|
// EXISTING unlinked holes in adjacent maps
|
|
i = linkholes(map);
|
|
if (db) {
|
|
if (db) dblog(" autolinked to %d holes in adjacent maps.",i);
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// add random objects and monsters , and remove
|
|
// bad objects
|
|
if (db) dblog(" adding random objects+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) {
|
|
// add random obs, but not in vaults
|
|
if (isempty(c) && !getcellvault(c)) {
|
|
if (rnd(1,100) <= c->habitat->randthingpct) {
|
|
addrandomthing(c, c->habitat->randobpct, NULL);
|
|
}
|
|
}
|
|
// remove bad objects
|
|
if (!c->room) {
|
|
object_t *o,*nexto;
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (hasflag(o->flags, F_ONLYINROOM)) {
|
|
killob(o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
// look for adjacent maps
|
|
if (db) dblog(" linking to 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)) && (thisx == x)) {
|
|
map->nextmap[D_N] = m->id;
|
|
if (db) dblog(" linked to map %d (dir N)",m->id);
|
|
}
|
|
}
|
|
if (map->nextmap[D_E] == -1) {
|
|
if ((thisx == (x + 1)) && (thisy == y)) {
|
|
map->nextmap[D_E] = m->id;
|
|
if (db) dblog(" linked to map %d (dir E)",m->id);
|
|
}
|
|
}
|
|
if (map->nextmap[D_S] == -1) {
|
|
if ((thisy == (y + 1)) && (thisx == x)) {
|
|
map->nextmap[D_S] = m->id;
|
|
if (db) dblog(" linked to map %d (dir S)",m->id);
|
|
}
|
|
}
|
|
if (map->nextmap[D_W] == -1) {
|
|
if ((thisx == (x - 1)) && (thisy == y)) {
|
|
map->nextmap[D_W] = m->id;
|
|
if (db) dblog(" linked to map %d (dir W)",m->id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
|
|
|
|
map->beingcreated = B_FALSE;
|
|
if (db) dblog(" Map creation finished.");
|
|
}
|
|
|
|
void createsewer(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
object_t *o,*nexto;
|
|
cell_t *c;
|
|
flag_t *f;
|
|
int i;
|
|
int startx,starty,x,y,rx,ry;
|
|
int maxroomsx,maxroomsy;
|
|
int roomcountx,roomcounty;
|
|
//int corridorsizex = 3;
|
|
//int corridorsizey = 2;
|
|
int corridorsize; // length of corridors between rooms
|
|
int roomfullsize; // room size including walls
|
|
int roomsize; // room size without walls
|
|
int sectionsize; // a section is a room+corridor
|
|
int *roomon,*rowfirst,*rowlast,*colfirst,*collast;
|
|
int numon = 0;
|
|
char buf[BUFLEN];
|
|
|
|
corridorsize = rnd(2,3);
|
|
roomfullsize = rnd(5,9); if ((roomfullsize % 2) == 0) roomfullsize++;
|
|
sectionsize = corridorsize + roomfullsize;
|
|
roomsize = roomfullsize - 2; // don't include outer walls.
|
|
|
|
|
|
// select how many very/horz rooms
|
|
// we are being over cautious here since "sectionsize"
|
|
// is a room + corridor.
|
|
// (x-1) corridors. ie:
|
|
// Room --- Room --- Room xxx
|
|
// | | |
|
|
// | | |
|
|
// Room --- Room --- Room xxx
|
|
// x x x
|
|
// x x x
|
|
//
|
|
// == Across: 3 rooms, 2 corridors
|
|
// == Down: 2 rooms, 1 corridor
|
|
/// x = left over sectionsize
|
|
maxroomsx = ((map->w - 2) ) / (sectionsize);
|
|
maxroomsy = ((map->h - 2) ) / (sectionsize);
|
|
roomcountx = maxroomsx;
|
|
roomcounty = maxroomsy;
|
|
|
|
|
|
// fill entire maze with walls
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
addcell(map, x, y);
|
|
}
|
|
}
|
|
roomon = malloc((roomcountx * roomcounty) * sizeof(int));
|
|
rowfirst = malloc(roomcounty * sizeof(int));
|
|
rowlast = malloc(roomcounty * sizeof(int));
|
|
colfirst = malloc(roomcountx * sizeof(int));
|
|
collast = malloc(roomcountx * sizeof(int));
|
|
numon = 0;
|
|
while (numon == 0) {
|
|
for (ry = 0; ry < roomcounty; ry++) {
|
|
for (rx = 0; rx < roomcountx; rx++) {
|
|
i = (ry*roomcountx)+rx;
|
|
|
|
if (pctchance(66)) {
|
|
roomon[i] = B_TRUE;
|
|
numon++;
|
|
} else {
|
|
roomon[i] = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now figure first/last rooms in each row
|
|
for (ry = 0; ry < roomcounty; ry++) {
|
|
rowfirst[ry] = -1;
|
|
for (rx = 0; rx < roomcountx; rx++) {
|
|
i = (ry*roomcountx)+rx;
|
|
if (roomon[i]) {
|
|
if (rowfirst[ry] == -1) {
|
|
rowfirst[ry] = rx;
|
|
}
|
|
rowlast[ry] = rx;
|
|
}
|
|
}
|
|
}
|
|
// now figure first/last rooms in each column
|
|
for (rx = 0; rx < roomcountx; rx++) {
|
|
colfirst[rx] = -1;
|
|
for (ry = 0; ry < roomcounty; ry++) {
|
|
i = (ry*roomcountx)+rx;
|
|
if (roomon[i]) {
|
|
if (colfirst[rx] == -1) {
|
|
colfirst[rx] = ry;
|
|
}
|
|
collast[rx] = ry;
|
|
}
|
|
}
|
|
}
|
|
|
|
dblog("SEWER debug:");
|
|
dblog(" sectionsize is %d (room=%d, corridor=%d)", sectionsize, roomfullsize, corridorsize);
|
|
dblog(" roomcountx is %d, roomcounty is %d", roomcountx, roomcounty);
|
|
for (ry = 0; ry < roomcounty; ry++) {
|
|
char buf2[BUFLEN];
|
|
strcpy(buf, "");
|
|
for (rx = 0; rx < roomcountx; rx++) {
|
|
i = (ry*roomcountx)+rx;
|
|
if (roomon[i]) {
|
|
strcat(buf, "#");
|
|
} else {
|
|
strcat(buf, "-");
|
|
}
|
|
}
|
|
sprintf(buf2, " (rowfirst is %d, rowlast is %d)", rowfirst[ry], rowlast[ry]);
|
|
dblog("%s%s", buf, buf2);
|
|
}
|
|
|
|
|
|
// start at top left corner of top left room
|
|
startx = 1;
|
|
starty = 1;
|
|
x = startx;
|
|
y = starty;
|
|
// add square rooms in a gridlike layout
|
|
for (ry = 0; ry < roomcounty; ry++) {
|
|
for (rx = 0; rx < roomcountx; rx++) {
|
|
i = (ry*roomcountx)+rx;
|
|
if (roomon[i]) {
|
|
int thisid;
|
|
int x2,y2;
|
|
thisid = map->nrooms;
|
|
// add a room here
|
|
x2 = x+roomfullsize-1;
|
|
y2 = y+roomfullsize-1;
|
|
createroom(map, thisid, x, y, x2, y2, B_TRUE);
|
|
// define exit locations
|
|
if (ry != colfirst[rx]) addflag(map->flags, F_ROOMEXIT, thisid, x+(roomfullsize/2), y, NULL); // N
|
|
if (ry != collast[rx]) addflag(map->flags, F_ROOMEXIT, thisid, x+(roomfullsize/2), y2, NULL); // S
|
|
if (rx != rowfirst[ry]) addflag(map->flags, F_ROOMEXIT, thisid, x, y+(roomfullsize/2), NULL); // W
|
|
if (rx != rowlast[ry]) addflag(map->flags, F_ROOMEXIT, thisid, x2, y+(roomfullsize/2), NULL); // E
|
|
}
|
|
// move on to next room position
|
|
x += sectionsize;
|
|
assert(x < map->w);
|
|
}
|
|
x = startx;
|
|
y += sectionsize;
|
|
assert(y < map->h);
|
|
}
|
|
free(roomon);
|
|
free(rowfirst);
|
|
free(rowlast);
|
|
free(colfirst);
|
|
free(collast);
|
|
dblog("END SEWER debug");
|
|
|
|
// link rooms with corridors
|
|
for (i = 0; i < map->nrooms; i++) {
|
|
linkexits(map, map->room[i].id);
|
|
}
|
|
|
|
|
|
// replace all empty cells with slime (except the exit one)
|
|
for (i = 0; i < (map->w*map->h); i++){
|
|
c = map->cell[i];
|
|
if (!c->type->solid) {
|
|
setcelltype(c, CT_LOWFLOOR);
|
|
addob(c->obpile, "waist-deep slime");
|
|
}
|
|
}
|
|
|
|
// any solid cell with orthogonally-adjacent water (except the exit one)
|
|
// now turns into a walkway
|
|
for (i = 0; i < (map->w*map->h); i++){
|
|
cell_t *c2;
|
|
c = map->cell[i];
|
|
if (c->type->solid) {
|
|
int dir,makewalkway = B_FALSE;
|
|
for (dir = D_N; dir <= D_W; dir++) {
|
|
c2 = getcellindir(c, dir);
|
|
if (c2 && (c2->type->id == CT_LOWFLOOR)) {
|
|
makewalkway = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (makewalkway) setcelltype(c, getcellempty(c));
|
|
} else if (c->type->id == CT_LOWFLOOR) {
|
|
// any water cell orthogonally surrounded by water becomes very deep
|
|
int dir,surrounded = B_TRUE;
|
|
for (dir = D_N; dir <= D_W; dir++) {
|
|
c2 = getcellindir(c, dir);
|
|
if (c2 && (c2->type->id != CT_LOWFLOOR)) {
|
|
surrounded = B_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (surrounded) {
|
|
setcelltype(c, CT_VLOWFLOOR);
|
|
setwaterdepth(c, DP_MAX);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// now move all objects out of the water
|
|
for (i = 0; i < (map->w*map->h); i++){
|
|
c = map->cell[i];
|
|
if (c->type->id == CT_LOWFLOOR) {
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (o->type->id != OT_SLIMEPOOL) {
|
|
cell_t *c2;
|
|
c2 = getrandomcelloftype(map, getcellempty(c));
|
|
moveob(o, c2->obpile, ALL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add the exit. make sure it's next to a wall so it's possible to climb to.
|
|
// also ensure it's not over water.
|
|
c = NULL;
|
|
while (!c || (countadjwalls(c) == 0) || getcellwaterdepth(c, NULL)) {
|
|
c = getrandomroomcell(map, ANYROOM, WE_NONE);
|
|
assert(c);
|
|
}
|
|
o = addobfast(c->obpile, OT_GRATINGROOF);
|
|
assert(o);
|
|
// have to force these stairs to go back to a different region.
|
|
f = hasflag(o->flags, F_CLIMBABLE);
|
|
assert(f);
|
|
f->val[1] = map->region->parentregion->id;
|
|
linkstairs(o, entryob);
|
|
}
|
|
|
|
void createstomach(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
vault_t *v;
|
|
int x,y;
|
|
|
|
// fill entire maze with walls
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
addcell(map, x, y);
|
|
}
|
|
}
|
|
// add a random worm vault
|
|
v = findvaultwithtag("stomach");
|
|
assert(v);
|
|
if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) {
|
|
dblog("ERROR - couldn't create stomach vault '%s' on map %s", v->id, map->name);
|
|
msg("ERROR - couldn't create stomach vault '%s' on map %s", v->id, map->name);
|
|
}
|
|
|
|
// add an empty maplink to the exits - this will be filled in by OT_A_SWALLOW code in spell.c
|
|
// also stop acid from disappearing
|
|
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 = hasob(c->obpile, OT_STOMACHEXIT);
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (o->material->id == MT_ACID) {
|
|
killflagsofid(o->flags, F_OBHPDRAIN);
|
|
}
|
|
if (o->type->id == OT_STOMACHEXIT) {
|
|
killflagsofid(o->flags, F_MAPLINK);
|
|
addflag(o->flags, F_MAPLINK, parentmap->id, NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void createswamp(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
int x,y;
|
|
cell_t *c;
|
|
object_t *o, *nexto;
|
|
|
|
// first create a normal dungeon
|
|
createdungeon(map, depth, parentmap, exitdir, entryob);
|
|
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
// replace all walls with water
|
|
if (c->type->solid) {
|
|
setcelltype(c, CT_LOWFLOOR);
|
|
addob(c->obpile, "very deep water");
|
|
}
|
|
// remove all doors
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (isdoor(o, NULL)) {
|
|
killob(o);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int createvault(map_t *map, int roomid, vault_t *v, int *retw, int *reth, int *retx, int *rety) {
|
|
int w,h,x,y;
|
|
int xmargin = DEF_VAULTMARGIN,ymargin = DEF_VAULTMARGIN; // defaults
|
|
int minx,miny,maxx,maxy;
|
|
int db = B_FALSE;
|
|
int rotation = 0;
|
|
room_t *thisroom = NULL;
|
|
flag_t *f;
|
|
|
|
// default
|
|
if (retw) *retw = -1;
|
|
if (reth) *reth = -1;
|
|
|
|
if (!v) {
|
|
return B_TRUE;
|
|
}
|
|
|
|
f = hasflag(v->flags, F_KEEPMARGIN);
|
|
if (f) {
|
|
xmargin = f->val[0];
|
|
ymargin = f->val[1];
|
|
}
|
|
|
|
// handle rotation.
|
|
rotation = rnd(0,v->nmaps-1);
|
|
|
|
f = hasflag(v->flags, F_VAULTRANDOMMAP);
|
|
if (f) {
|
|
int minw,minh;
|
|
// just make a normal room to start with. but make sure :
|
|
// - it is surrounded with walls!
|
|
// - we don't do autodoors (will handle this further down) -------------
|
|
minw = f->val[0];
|
|
minh = f->val[1];
|
|
if (calcposandmakeroom(map, roomid, minw, minh, xmargin, ymargin, &minx, &miny, &w, &h, B_NODOORS, B_TRUE)) {
|
|
return B_TRUE;
|
|
}
|
|
if (db) dblog("made random vault %s at pos %d,%d on map %s", v->id, minx, miny, map->name);
|
|
maxx = minx + w - 1;
|
|
maxy = miny + h - 1;
|
|
} else {
|
|
// get width/height from vault
|
|
//w = v->w;
|
|
//h = v->h;
|
|
getvaultwh(v, &w, &h, rotation);
|
|
// find vault position
|
|
if (calcroompos(map, w, h, xmargin, ymargin, &minx, &miny, B_TRUE)) {
|
|
// forced calcroompos should never fail since it's
|
|
// allowed to overlap other rooms.
|
|
dblog("** couldn't find position for vault %s (roomid %d)!\n", v->id, roomid);
|
|
//msg("** ALERT: couldn't make vault room!\n");
|
|
return B_TRUE;
|
|
}
|
|
maxx = minx + (w-1);
|
|
maxy = miny + (h-1);
|
|
|
|
map->room[map->nrooms].id = roomid;
|
|
map->room[map->nrooms].x1 = minx;
|
|
map->room[map->nrooms].y1 = miny;
|
|
map->room[map->nrooms].x2 = maxx;
|
|
map->room[map->nrooms].y2 = maxy;
|
|
map->room[map->nrooms].vault = v;
|
|
map->room[map->nrooms].exitslinked = B_FALSE;
|
|
thisroom = &(map->room[map->nrooms]);
|
|
map->nrooms++;
|
|
|
|
// now make it
|
|
if (db) dblog("making vault %s at pos %d,%d on map %s", v->id, minx, miny, map->name);
|
|
for (y = miny; y <= maxy; y++) {
|
|
for (x = minx; x <= maxx; x++) {
|
|
cell_t *cell;
|
|
celltype_t *ct;
|
|
cell = getcellat(map, x, y);
|
|
|
|
clearcell(cell);
|
|
|
|
// set cell type
|
|
ct = getvaultcelltype(v, x-minx,y-miny, rotation);
|
|
setcelltype(cell, ct ? ct->id : getcellempty(cell));
|
|
// set roomid
|
|
cell->room = thisroom;
|
|
// add objects
|
|
addvaultcellcontents(cell, v, x-minx,y-miny, rotation);
|
|
}
|
|
}
|
|
|
|
markroomwalls(map, thisroom);
|
|
}
|
|
|
|
if (retw) *retw = w;
|
|
if (reth) *reth = h;
|
|
if (retx) *retx = minx;
|
|
if (rety) *rety = miny;
|
|
|
|
// add other stuff to the vault based on flags
|
|
// this will also set cell->vault for all cells.
|
|
addvaultcontents(map, v, minx, miny, maxx, maxy, rotation);
|
|
|
|
// auto add doors if required
|
|
f = hasflag(v->flags, F_AUTODOORS);
|
|
if (f) {
|
|
// when adding autodoors to a vault, they are ALWAYS closed.
|
|
autodoors(map, roomid, minx, miny, maxx, maxy, f->val[0], B_NODOORS);
|
|
}
|
|
|
|
// link up exits from this vault
|
|
if (!hasflag(v->flags, F_VAULTNOLINK)) {
|
|
linkexits(map, roomid);
|
|
}
|
|
|
|
// remove bones vault files after creation.
|
|
if (hasflagval(v->flags, F_VAULTTAG, NA, NA, NA, "bones")) {
|
|
removevaultfile(v->filename);
|
|
// also mark it as invalid, otherwise we might try
|
|
// to create it again, in which case the second call
|
|
// to removevaultfile() will fail.
|
|
v->valid = B_FALSE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
void killcell(cell_t *c) {
|
|
killobpile(c->obpile);
|
|
if (c->writing) free(c->writing);
|
|
}
|
|
|
|
void killcelltype(celltype_t *ct) {
|
|
celltype_t *nextone, *lastone;
|
|
if (ct->name) free(ct->name);
|
|
if (ct->flags) killflagpile(ct->flags);
|
|
|
|
// remove from list
|
|
nextone = ct->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = ct->prev;
|
|
} else { /* last */
|
|
lastcelltype = ct->prev;
|
|
}
|
|
|
|
if (ct->prev == NULL) {
|
|
/* first */
|
|
nextone = ct->next;
|
|
free(firstcelltype);
|
|
firstcelltype = nextone;
|
|
} else {
|
|
lastone = ct->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
void killfakes(map_t *map, cell_t *cell) {
|
|
killobpile(cell->obpile);
|
|
free(map->name);
|
|
}
|
|
|
|
void killmap(map_t *m) {
|
|
map_t *nextone, *lastone;
|
|
int i;
|
|
// free mem
|
|
while (m->lf) killlf(m->lf);
|
|
|
|
killflagpile(m->flags);
|
|
m->flags = NULL;
|
|
|
|
for (i = 0; i < (m->w*m->h); i++){
|
|
killcell(m->cell[i]);
|
|
}
|
|
|
|
// remove from list
|
|
nextone = m->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = m->prev;
|
|
} else { /* last */
|
|
lastmap = m->prev;
|
|
}
|
|
|
|
if (m->prev == NULL) {
|
|
/* first */
|
|
nextone = m->next;
|
|
free(firstmap);
|
|
firstmap = nextone;
|
|
} else {
|
|
lastone = m->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
void killregion(region_t *r) {
|
|
region_t *nextone, *lastone;
|
|
// remove from list
|
|
nextone = r->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = r->prev;
|
|
} else { /* last */
|
|
lastregion = r->prev;
|
|
}
|
|
|
|
if (r->prev == NULL) {
|
|
/* first */
|
|
nextone = r->next;
|
|
free(firstregion);
|
|
firstregion = nextone;
|
|
} else {
|
|
lastone = r->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
// link a single cell up to the rest of the map.
|
|
// make sure it links to an empty cell of a DIFFERENT roomid.
|
|
// if 'wantfilled' is set, only link to "filled" cells.
|
|
// return TRUE on failure.
|
|
int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) {
|
|
int db = B_FALSE;
|
|
int d, roomid,startd,endd;
|
|
int poss2[MAXCANDIDATES],nposs2;
|
|
int dist[MAXDIR_ORTH],hitsedge[MAXDIR_ORTH], sameroom[MAXDIR_ORTH];
|
|
cell_t *directendcell[MAXDIR_ORTH];
|
|
int mindist = 999,maxdist = -1;
|
|
cell_t *c;
|
|
int forcedir = D_NONE;
|
|
|
|
if (ncellsadded) *ncellsadded = 0;
|
|
|
|
roomid = getroomid(startcell);
|
|
if (db) dblog(" calling linkexit() for cell at %d,%d in roomid %d", startcell->x, startcell->y, roomid);
|
|
|
|
for (d = D_N; d <= D_W; d++) {
|
|
hitsedge[d] = B_TRUE;
|
|
directendcell[d] = NULL;
|
|
}
|
|
|
|
// link it.
|
|
// if our cell is marked specifically as a room exit, our first direction MUST be out the
|
|
// door.
|
|
if (startcell->isroomwall != D_NONE) {
|
|
forcedir = startcell->isroomwall;
|
|
startd = forcedir;
|
|
endd = forcedir;
|
|
} else {
|
|
startd = D_N; endd = D_W;
|
|
}
|
|
|
|
// otherwise, starting from the door, count the number of cells in
|
|
// each direction until we hit an empty (walkable) cell which isn't a room.
|
|
|
|
// if we hit a cell of this roomid, mark this dir as invalid.
|
|
for (d = startd; d <= endd; d++) {
|
|
dist[d] = 0;
|
|
hitsedge[d] = B_FALSE;
|
|
sameroom[d] = B_FALSE;
|
|
c = getcellindir(startcell, d);
|
|
while (c) {
|
|
int rv;
|
|
dist[d]++;
|
|
|
|
rv = cellokforreachability(startcell, c, roomid, d, wantfilled, &(sameroom[d]));
|
|
if (rv == B_FALSE) {
|
|
dist[d] = 999;
|
|
break;
|
|
} else if (rv == B_TRUE) {
|
|
directendcell[d] = c;
|
|
if (db) dblog(" can make %s path (hits empty cell at dist %d)", getdirname(d), dist[d]);
|
|
} else { // ie. rv == B_MAYBE
|
|
int perpdir[2],n;
|
|
cell_t *pcell = NULL;
|
|
perpdir[0] = d - 1; if (perpdir[0] < D_N) perpdir[0] = D_W;
|
|
perpdir[1] = d + 1; if (perpdir[1] > D_W) perpdir[1] = D_N;
|
|
// is there an adjacent walkable cell in a perpendicular direction
|
|
// which isn't from the starting room?
|
|
for (n = 0; n <= 1; n++) {
|
|
pcell = getcellindir(c, perpdir[n]);
|
|
if (pcell && adjcellokforreachability(pcell, roomid, perpdir[n], wantfilled)) {
|
|
// finished.
|
|
directendcell[d] = c;
|
|
if (db) dblog(" can make %s path (hits adjacent empty cell at dist %d)", getdirname(d), dist[d]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// check next cell
|
|
c = getcellindir(c, d); // getting the same cell!
|
|
}
|
|
if (!c) {
|
|
if (db) dblog(" going %s hits edge of map.", getdirname(d));
|
|
hitsedge[d] = B_TRUE;
|
|
}
|
|
if (dist[d] != 999) {
|
|
if (!hitsedge[d]) {
|
|
if (dist[d] < mindist) mindist = dist[d];
|
|
}
|
|
if (dist[d] > maxdist) maxdist = dist[d];
|
|
}
|
|
}
|
|
|
|
if (mindist == 999) {
|
|
cell_t *turncell = NULL,*endcell = NULL;
|
|
cell_t *perpcell[MAX_MAPW*MAX_MAPH];
|
|
cell_t *perpturncell1[MAX_MAPW*MAX_MAPH];
|
|
int perpturndir1[MAX_MAPW*MAX_MAPH];
|
|
int nperpcells = 0;
|
|
int perpdir[2];
|
|
int startdir = D_NONE;
|
|
int turndir = D_NONE;
|
|
int startdist = 0;
|
|
int maxdist2 = -1;
|
|
int startposs[MAXDIR_ORTH];
|
|
int nstartposs = 0;
|
|
// no good directions.
|
|
if (db) dblog(" No directions lead to valid cells. Trying turns.");
|
|
|
|
|
|
if (forcedir != D_NONE) {
|
|
startdir = forcedir;
|
|
} else {
|
|
// starting at the LONGEST distance, traverse up each dir,
|
|
// branching off looking for rooms.
|
|
|
|
// if start cell is inside a room, find longest distance which
|
|
// doesn't go through the same room
|
|
for (d = D_N; d <= D_W; d++) {
|
|
if (isroom(startcell)) {
|
|
if (!sameroom[d] && (dist[d] > maxdist2)) {
|
|
maxdist2 = dist[d];
|
|
}
|
|
} else {
|
|
maxdist2 = dist[d];
|
|
}
|
|
}
|
|
// pick one randomly
|
|
for (d = D_N; d <= D_W; d++) {
|
|
if (dist[d] == maxdist2) {
|
|
startposs[nstartposs++] = d;
|
|
}
|
|
}
|
|
if (nstartposs) {
|
|
startdir = startposs[rnd(0,nstartposs-1)];
|
|
}
|
|
}
|
|
|
|
//assert((startdir >= D_N) && (startdir <= D_W));
|
|
if ((startdir < D_N) || (startdir > D_W)) {
|
|
// couldn't find any valid start dirs!
|
|
if (db) dblog(" Cannot find a valid startdir.");
|
|
return B_TRUE;
|
|
}
|
|
|
|
// figure out perpendicular dirs
|
|
perpdir[0] = startdir - 1; if (perpdir[0] < D_N) perpdir[0] = D_W;
|
|
perpdir[1] = startdir + 1; if (perpdir[1] > D_W) perpdir[1] = D_N;
|
|
|
|
assert(perpdir[0] != startdir);
|
|
assert(perpdir[1] != startdir);
|
|
|
|
if (db) dblog(" Will walk %s (dist %d), checking %s and %s.", getdirname(startdir), maxdist,
|
|
getdirname(perpdir[0]), getdirname(perpdir[1]));
|
|
|
|
// check in startdir
|
|
c = getcellindir(startcell, startdir);
|
|
while (c && !turncell) {
|
|
int n;
|
|
cell_t *c2;
|
|
startdist++;
|
|
// check left/right from this cell for rooms
|
|
for (n = 0; n <= 1; n++) {
|
|
int turndist = 0;
|
|
c2 = getcellindir(c, perpdir[n]);
|
|
|
|
while (c2) {
|
|
int gotsolution = B_FALSE;
|
|
int rv;
|
|
turndist++;
|
|
|
|
perpcell[nperpcells] = c2; // this will be used if we need to make 2 turns
|
|
perpturncell1[nperpcells] = c;
|
|
perpturndir1[nperpcells] = perpdir[n];
|
|
nperpcells++;
|
|
|
|
rv = cellokforreachability(startcell, c2, roomid, perpdir[n], wantfilled, NULL);
|
|
|
|
if (rv == B_FALSE) {
|
|
break;
|
|
} else if (rv == B_TRUE) {
|
|
gotsolution = B_TRUE;
|
|
} else if (turndist > 1) {
|
|
// check l/r too
|
|
int perpdir2[2],nn;
|
|
cell_t *pcell = NULL;
|
|
perpdir2[0] = perpdir[n] - 1; if (perpdir2[0] < D_N) perpdir2[0] = D_W;
|
|
perpdir2[1] = perpdir[n] + 1; if (perpdir2[1] > D_W) perpdir2[1] = D_N;
|
|
for (nn = 0; nn <= 1; nn++) {
|
|
pcell = getcellindir(c2, perpdir2[nn]);
|
|
if (pcell && adjcellokforreachability(pcell, roomid, perpdir2[nn], wantfilled)) {
|
|
// finished.
|
|
if (db) dblog(" Got to an empty cell next to us.");
|
|
gotsolution = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (gotsolution) {
|
|
if (db) dblog(" Solution found: Walk %d %s, then %d %s.",
|
|
startdist, getdirname(startdir),
|
|
turndist, getdirname(perpdir[n]));
|
|
// walkable and not in this roomid. ok!
|
|
turncell = c;
|
|
endcell = c2;
|
|
turndir = perpdir[n];
|
|
break;
|
|
}
|
|
// check next cell
|
|
c2 = getcellindir(c2, perpdir[n]);
|
|
}
|
|
if (turncell) break;
|
|
}
|
|
// now keep going in main direction.
|
|
c = getcellindir(c, startdir);
|
|
}
|
|
|
|
if (turncell) {
|
|
// make a path up to the turn point.
|
|
if (db) dblog(" Making path from vault to corner, initdir=%s", getdirname(startdir));
|
|
c = getcellindir(startcell, startdir);
|
|
while (c != turncell) {
|
|
breakwall(c);
|
|
if (ncellsadded) (*ncellsadded)++;
|
|
c = getcellindir(c, startdir);
|
|
}
|
|
// clear the corner cell
|
|
breakwall(c);
|
|
if (db) dblog(" Making path from corner to rest of map, turndir=%s", getdirname(turndir));
|
|
// now turn and clear up to the next room/empty cell
|
|
c = getcellindir(c, turndir);
|
|
while (c != endcell) {
|
|
breakwall(c);
|
|
if (ncellsadded) (*ncellsadded)++;
|
|
c = getcellindir(c, turndir);
|
|
}
|
|
} else {
|
|
// We need to make 2 turns.
|
|
// for each perpcell[], look in startdir + diropposite(startdir)
|
|
int dir3[2],i,n;
|
|
cell_t *turncell2 = NULL;
|
|
int turndir2;
|
|
dir3[0] = startdir;
|
|
dir3[1] = diropposite(startdir);
|
|
if (db) dblog(" Need to make two turns. Searching %s and %s from each perpcell.", getdirname(dir3[0]),
|
|
getdirname(dir3[1]));
|
|
for (i = 0; i < nperpcells; i++) {
|
|
for (n = 0; n < 2; n++) {
|
|
cell_t *c2;
|
|
int turndist = 0;
|
|
|
|
if (db) dblog_nocr("looking %s from %d,%d: ", getdirname(dir3[n]),
|
|
perpcell[i]->x, perpcell[i]->y);
|
|
|
|
c2 = getcellindir(perpcell[i], dir3[n]);
|
|
while (c2) {
|
|
int gotsolution = B_FALSE;
|
|
int rv;
|
|
turndist++;
|
|
if (db) dblog_nocr("(%d,%d)",c2->x,c2->y);
|
|
|
|
rv = cellokforreachability(startcell, c2, roomid, perpdir[n], wantfilled, NULL);
|
|
|
|
if (rv == B_FALSE) {
|
|
break;
|
|
} else if (rv == B_TRUE) {
|
|
gotsolution = B_TRUE;
|
|
} else if (turndist > 1) {
|
|
// check l/r too
|
|
int perpdir2[2],nn;
|
|
cell_t *pcell = NULL;
|
|
perpdir2[0] = perpdir[n] - 1; if (perpdir2[0] < D_N) perpdir2[0] = D_W;
|
|
perpdir2[1] = perpdir[n] + 1; if (perpdir2[1] > D_W) perpdir2[1] = D_N;
|
|
for (nn = 0; nn <= 1; nn++) {
|
|
pcell = getcellindir(c2, perpdir2[nn]);
|
|
if (pcell && adjcellokforreachability(pcell, roomid, perpdir2[nn], wantfilled)) {
|
|
// finished.
|
|
if (db) dblog(" Got to an empty cell next to us.");
|
|
gotsolution = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (gotsolution) {
|
|
turncell = perpturncell1[i];
|
|
turndir = perpturndir1[i];
|
|
turncell2 = perpcell[i];
|
|
turndir2 = dir3[n];
|
|
|
|
endcell = c2;
|
|
break;
|
|
}
|
|
// check next cell
|
|
c2 = getcellindir(c2, dir3[n]);
|
|
} // end while c2
|
|
if (turncell2) break;
|
|
|
|
if (db) dblog("");
|
|
} // end for n=1-2
|
|
if (turncell2) break;
|
|
} // end foreach perpcell
|
|
|
|
// if we find a solution, fill in turncell2 and make path.
|
|
if (turncell2) {
|
|
if (db) dblog(" Twoturn solution found: Walk %s, then %s, then %s.",
|
|
getdirname(startdir), getdirname(turndir), getdirname(turndir2));
|
|
|
|
// make a path up to the turn point.
|
|
if (db) dblog(" Making path from vault to first corner, initdir=%s", getdirname(startdir));
|
|
c = getcellindir(startcell, startdir);
|
|
while (c != turncell) {
|
|
breakwall(c);
|
|
if (ncellsadded) (*ncellsadded)++;
|
|
c = getcellindir(c, startdir);
|
|
}
|
|
// clear the corner cell
|
|
breakwall(c);
|
|
// now turn and clear up to the next turn
|
|
if (db) dblog(" Making path from 1st corner to 2nd corner, turndir=%s", getdirname(turndir));
|
|
c = getcellindir(c, turndir);
|
|
while (c != turncell2) {
|
|
breakwall(c);
|
|
if (ncellsadded) (*ncellsadded)++;
|
|
c = getcellindir(c, turndir);
|
|
}
|
|
|
|
// now turn and clear up to the next room/empty cell
|
|
if (db) dblog(" Making path from 2nd corner to rest of map, turndir=%s", getdirname(turndir2));
|
|
c = getcellindir(c, turndir2);
|
|
while (c != endcell) {
|
|
breakwall(c);
|
|
if (ncellsadded) (*ncellsadded)++;
|
|
c = getcellindir(c, turndir2);
|
|
}
|
|
} else {
|
|
if (db) dblog(" Cannot find a way to link up.");
|
|
// debugging - a failure here during fix_reachability is fatal.
|
|
/*
|
|
if (wantfilled) {
|
|
assert(0 == 1);
|
|
}
|
|
*/
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
} else { // we found a way to go without needing to turn.
|
|
int whichway,sel;
|
|
int mindist2 = 999;
|
|
// now pick the shortest one which doesn't hit the edge
|
|
// get list of all the minimums and randomly tie-break
|
|
nposs2 = 0;
|
|
for (d = D_N; d <= D_W; d++) {
|
|
if (!hitsedge[d] && (dist[d] < mindist2)) {
|
|
mindist2 = dist[d];
|
|
}
|
|
}
|
|
|
|
for (d = D_N; d <= D_W; d++) {
|
|
if (dist[d] == mindist2) {
|
|
poss2[nposs2++] = d;
|
|
}
|
|
}
|
|
sel = rnd(0,nposs2-1);
|
|
whichway = poss2[sel];
|
|
|
|
// now create a path
|
|
if (db) dblog(" Linking directly %s (distance %d).", getdirname(whichway), mindist);
|
|
c = getcellindir(startcell, whichway);
|
|
//while (c && !cellwalkable(NULL, c, NULL)) {
|
|
while (c && c != directendcell[whichway]) {
|
|
breakwall(c);
|
|
if (ncellsadded) (*ncellsadded)++;
|
|
c = getcellindir(c, whichway);
|
|
}
|
|
}
|
|
|
|
// now make sure the START cell is empty too!
|
|
if (startcell->type->solid) {
|
|
breakwall(startcell);
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
// make sure exits/doors in a given room link up to the rest of the map.
|
|
int linkexits(map_t *m, int roomid) {
|
|
int x,y,i;
|
|
cell_t *poss[MAXCANDIDATES],*c;
|
|
int nposs = 0;
|
|
int db = B_TRUE;
|
|
int nadded = 0;
|
|
int minx = -1, miny = -1, maxx = -1, maxy = -1;
|
|
int roomidx = -1;
|
|
|
|
// figure out room coords
|
|
for (i = 0; i < m->nrooms; i++) {
|
|
if (m->room[i].id == roomid) {
|
|
minx = m->room[i].x1;
|
|
miny = m->room[i].y1;
|
|
maxx = m->room[i].x2;
|
|
maxy = m->room[i].y2;
|
|
roomidx = i;
|
|
break;
|
|
}
|
|
}
|
|
assert(roomidx != -1);
|
|
|
|
// does this roomid actually exist??
|
|
c = getrandomroomcell(m, roomid, WE_NONE);
|
|
if (!c) return B_FALSE;
|
|
|
|
if (db) {
|
|
char buf[BUFLEN];
|
|
vault_t *v;
|
|
c = getrandomroomcell(m, roomid, WE_NONE);
|
|
v = getcellvault(c);
|
|
snprintf(buf, BUFLEN, "*** linkexits for roomid %d (%s) [%d,%d-%d,%d]", roomid,
|
|
v ? v->id : "novault",
|
|
minx,miny,maxx,maxy);
|
|
dblog("%s", buf);
|
|
}
|
|
|
|
// find all doors or f_roomexits
|
|
for (y = miny; y <= maxy; y++) {
|
|
for (x = minx; x <= maxx; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (!c) continue;
|
|
|
|
if (hasobwithflag(c->obpile, F_DOOR)) {
|
|
if ((x == minx) || (x == maxx) || (y == miny) || (y == maxy) ) {
|
|
if (db) dblog("found wall door at %d,%d",x,y);
|
|
poss[nposs++] = c;
|
|
}
|
|
} else if (hasflagval(m->flags, F_ROOMEXIT, roomid, x, y, NULL)) {
|
|
if (db) dblog("found roomexit at %d,%d",x,y);
|
|
poss[nposs++] = c;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (db) dblog("%d exit positions found.",nposs);
|
|
|
|
// for each exit position, make sure it links to at least one cell which isn't
|
|
// part of this room
|
|
for (i = 0; i < nposs; i++) {
|
|
int ncorridors = 0,d;
|
|
|
|
if (db) dblog("exit #%d at %d,%d: (%s)",i, poss[i]->x, poss[i]->y,
|
|
(poss[i]->isroomwall != D_NONE) ? getdirname(poss[i]->isroomwall) : "nodir");
|
|
// if exit is solid and COMPLETELY surrounded by solid, ignore it.
|
|
if (poss[i]->type->solid && (countcellexits(poss[i], DT_ORTH) == 0)){
|
|
if (db) dblog("cell is solid and surrounded by solids. ignoring.");
|
|
continue;
|
|
}
|
|
|
|
for (d = D_N; d <= D_W; d++) {
|
|
c = getcellindir(poss[i], d);
|
|
if (c && cellwalkable(NULL, c, NULL) && (getroomid(c) != roomid)) {
|
|
ncorridors++;
|
|
}
|
|
}
|
|
if (db) dblog(" This exit leads to %d corridors. ", ncorridors);
|
|
|
|
// no corridors?
|
|
if (ncorridors == 0) {
|
|
if (db) dblog(" Need to link.");
|
|
if (!linkexit(poss[i], B_FALSE, NULL)) {
|
|
nadded++;
|
|
}
|
|
}
|
|
} // end for each door
|
|
|
|
m->room[roomidx].exitslinked = B_TRUE;
|
|
|
|
if (db) dblog("linkexits complete (%d added).", nadded);
|
|
return nadded;
|
|
}
|
|
|
|
void createpit(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) {
|
|
object_t *o;
|
|
cell_t *c;
|
|
int x,y;
|
|
// fill entire maze with walls
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
addcell(map, x, y);
|
|
}
|
|
}
|
|
// select random spot
|
|
c = getrandomcell(map);
|
|
// clear it
|
|
setcelltype(c, CT_CORRIDOR);
|
|
// put an exit here
|
|
o = addobject(c->obpile, NULL, B_FALSE, B_FALSE, OT_HOLEINROOF);
|
|
assert(o);
|
|
// link it
|
|
linkstairs(o, entryob);
|
|
}
|
|
|
|
// only need to provide either obname _OR_ o
|
|
void createregionlink(map_t *m, cell_t *c, object_t *o, char *obname, enum REGIONTYPE newregiontype, region_t *parent) {
|
|
flag_t *f;
|
|
region_t *r;
|
|
int basedepth = 0;
|
|
if (newregiontype != RG_MAINDUNGEON) {
|
|
basedepth = getmapdifficulty(m);
|
|
}
|
|
// create a new region.
|
|
r = addregion(newregiontype, m->region, -1, basedepth, m->id);
|
|
// add stairs going to the new region, if required
|
|
if (!c) {
|
|
c = NULL;
|
|
while (!c || !cellwalkable(NULL, c, NULL) || getcellwaterdepth(c, NULL)) {
|
|
c = getrandomcell(m);
|
|
}
|
|
}
|
|
|
|
// create object if requested
|
|
if (!o) {
|
|
o = addob(c->obpile, obname);
|
|
assert(o);
|
|
}
|
|
|
|
// link stairs to the new region
|
|
f = hasflag(o->flags, F_CLIMBABLE);
|
|
assert(f);
|
|
f->val[1] = r->id;
|
|
}
|
|
|
|
void createriver(map_t *m) {
|
|
int dir,width,startcentre,endcentre;
|
|
cell_t *retcell[MAX_MAPW*MAX_MAPH];
|
|
cell_t *dirtcell[MAX_MAPW*MAX_MAPH];
|
|
int nretcell,ndirtcells,i;
|
|
dblog("Creating river on map %s (depth %d)!", m->name, m->depth);
|
|
// pick direction
|
|
dir = rnd(B_VERT,B_HORZ);
|
|
// pick random river width
|
|
width = rnd(0,2);
|
|
// pick random spot along top && bottom ( x >= width and x <= w-width-1)
|
|
// then bresnham between spots to make centrelist
|
|
if (dir == B_VERT) {
|
|
startcentre = rnd(1,m->w - width - 1);
|
|
endcentre = rnd(1,m->w - width - 1);
|
|
calcbresnham(m, startcentre, 0, endcentre, m->h - 1, retcell, &nretcell);
|
|
} else {
|
|
startcentre = rnd(1,m->h - width - 1);
|
|
endcentre = rnd(1,m->h - width - 1);
|
|
calcbresnham(m, 0, startcentre, m->w - 1, endcentre, retcell, &nretcell);
|
|
}
|
|
|
|
assert(nretcell < (MAX_MAPW*MAX_MAPH));
|
|
|
|
// for each centrelist
|
|
for (i = 0; i < nretcell; i++) {
|
|
int pos,start,end,n;
|
|
// make a second list of rivercells (rivercell->x - wid) to (rivercell->x + wid)
|
|
if (dir == B_VERT) {
|
|
start = retcell[i]->x;
|
|
end = retcell[i]->x + width;
|
|
} else {
|
|
start = retcell[i]->y;
|
|
end = retcell[i]->y + width;
|
|
}
|
|
limit(&start, 0, m->w-1);
|
|
limit(&end, 0, m->w-1);
|
|
// foreach cell across the river's width
|
|
for (pos = start; pos <= end; pos++) {
|
|
cell_t *c;
|
|
object_t *o;
|
|
if (dir == B_VERT) {
|
|
c = getcellat(m, pos, retcell[i]->y);
|
|
} else {
|
|
c = getcellat(m, retcell[i]->x, pos);
|
|
}
|
|
// move any staircases here to somewhere else
|
|
while ((o = hasobwithflag(c->obpile, F_CLIMBABLE)) != NULL) {
|
|
moveobtoclearcell(o);
|
|
}
|
|
// clear cell, convert to low rock floor, add water
|
|
clearcell(c);
|
|
setcelltype(c, CT_LOWFLOOR);
|
|
addobfast(c->obpile, OT_WATERDEEP);
|
|
// suround with dirt
|
|
getradiuscells(c, 7, DT_ORTH, B_FALSE, LOF_NEED, B_FALSE, dirtcell, &ndirtcells, 70);
|
|
for (n = 0; n < ndirtcells; n++) {
|
|
if (!dirtcell[n]->type->solid && (dirtcell[n]->type->id != CT_LOWFLOOR)) {
|
|
setcelltype(dirtcell[n], CT_DIRT);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void createroom(map_t *map, int roomid, int x1, int y1, int x2, int y2, int forcewalls) {
|
|
int x,y;
|
|
room_t *thisroom = NULL;
|
|
cell_t *cell;
|
|
|
|
map->room[map->nrooms].id = roomid;
|
|
map->room[map->nrooms].x1 = x1;
|
|
map->room[map->nrooms].y1 = y1;
|
|
map->room[map->nrooms].x2 = x2;
|
|
map->room[map->nrooms].y2 = y2;
|
|
map->room[map->nrooms].vault = NULL;
|
|
map->room[map->nrooms].exitslinked = B_FALSE;
|
|
thisroom = &(map->room[map->nrooms]);
|
|
map->nrooms++;
|
|
|
|
for (y = y1; y <= y2; y++) {
|
|
for (x = x1; x <= x2; x++) {
|
|
cell = getcellat(map, x, y);
|
|
if (cell) {
|
|
// kill contents, EXCEPT for staircases!
|
|
clearcell_exceptflags(cell, F_CLIMBABLE, F_NONE);
|
|
// make it a border or room
|
|
if ((y == y1) || (y == y2) ||
|
|
(x == x1) || (x == x2)) {
|
|
// ie. if you haven't forced walls then if this room overlaps
|
|
// with another one, no walls will be created.
|
|
if (forcewalls || (!forcewalls && cell->type->solid)) {
|
|
setcelltype(cell, getcellsolid(cell));
|
|
}
|
|
//}
|
|
} else {
|
|
setcelltype(cell, getcellempty(cell));
|
|
}
|
|
cell->room = thisroom;
|
|
}
|
|
}
|
|
}
|
|
markroomwalls(map, thisroom);
|
|
}
|
|
|
|
// room w/h are returned in *w and *h if given.
|
|
int calcposandmakeroom(map_t *map, int roomid, int overrideminw, int overrideminh, int xmargin, int ymargin, int *retx, int *rety, int *retw, int *reth, int doorpct, int forcewalls) {
|
|
int minx,miny;
|
|
int maxx,maxy;
|
|
int w,h;
|
|
int minroomw = MIN_ROOMW;
|
|
int minroomh = MIN_ROOMH;
|
|
int maxroomw = MAX_ROOMW;
|
|
int maxroomh = MAX_ROOMH;
|
|
|
|
if (overrideminw != NA) {
|
|
minroomw = overrideminw;
|
|
}
|
|
if (overrideminh != NA) {
|
|
minroomh = overrideminh;
|
|
}
|
|
|
|
// select random width/height
|
|
w = rnd(minroomw, maxroomw);
|
|
h = rnd(minroomh, maxroomh);
|
|
|
|
if (retw) *retw = w;
|
|
if (reth) *reth = h;
|
|
|
|
// find room position
|
|
if (calcroompos(map, w, h, xmargin, ymargin, &minx, &miny, B_FALSE)) {
|
|
dblog("** couldn't make room!\n");
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (retx) *retx = minx;
|
|
if (rety) *rety = miny;
|
|
|
|
// we now have the room position - fill it in
|
|
|
|
//printf("trying to create room at (%d,%d) w=%d,h=%d\n", minx, miny, w, h);
|
|
|
|
maxx = minx + (w-1);
|
|
maxy = miny + (h-1);
|
|
|
|
// actually make the room now.
|
|
createroom(map, roomid, minx,miny,maxx,maxy, forcewalls);
|
|
|
|
// add doors
|
|
if (doorpct) {
|
|
autodoors(map, roomid, minx, miny, maxx, maxy, doorpct, 25);
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
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 showrooms) {
|
|
int x,y;
|
|
cell_t *cell;
|
|
char ch;
|
|
dblog("dump of map '%s' (%d x %d):\n",map->name, map->w, map->h);
|
|
|
|
// top reference row
|
|
dblog_nocr(" ");
|
|
for (x = 0; x < map->w; x++) {
|
|
dblog_nocr("%d",x % 10);
|
|
}
|
|
dblog_nocr("\n");
|
|
|
|
for (y = 0; y < map->h; y++) {
|
|
dblog_nocr("%d",y % 10);
|
|
for (x = 0; x < map->w; x++) {
|
|
cell = getcellat(map, x, y);
|
|
ch = cell->type->glyph.ch;
|
|
if (ch == '.') {
|
|
if (showrooms && isroom(cell)) {
|
|
ch = '0' + getroomid(cell);
|
|
}
|
|
|
|
if (ch == '.') {
|
|
if (cell->filled) {
|
|
ch = 'o';
|
|
}
|
|
}
|
|
}
|
|
dblog_nocr("%c",ch);
|
|
}
|
|
dblog_nocr("\n");
|
|
}
|
|
}
|
|
|
|
void dumpoutlines(void) {
|
|
region_t *r;
|
|
int i;
|
|
for (r = firstregion ; r ; r = r->next) {
|
|
dblog("region:%s",r->rtype->name);
|
|
if (r->outline) {
|
|
for (i = 0; i < r->outline->nthings; i++ ){
|
|
regionthing_t *rt;
|
|
char loctext[BUFLEN];
|
|
rt = &r->outline->thing[i];
|
|
if (rt->depth == NA) {
|
|
snprintf(loctext, BUFLEN, "%d,%d",rt->x, rt->y);
|
|
} else {
|
|
snprintf(loctext, BUFLEN, "depth %d",rt->depth);
|
|
}
|
|
if (rt->whatkind == RT_REGIONLINK) {
|
|
regiontype_t *rtype;
|
|
rtype = findregiontype(rt->value);
|
|
dblog(" at %s: link to %s (%s)",loctext, rtype->name, rt->what);
|
|
} else if (rt->whatkind == RT_HABITAT) {
|
|
habitat_t *h;
|
|
h = findhabitat(rt->value);
|
|
dblog(" at %s: %s",loctext, h->name);
|
|
} else if (rt->whatkind == RT_VAULT) {
|
|
vault_t *v;
|
|
v = findvault(rt->what);
|
|
dblog(" at %s: %s",loctext, v->id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// 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) {
|
|
char buf[BUFLEN];
|
|
cell_t *retcell[MAXRETCELLS];
|
|
int nretcells,i;
|
|
if (wantannounce) {
|
|
sprintf(buf, "You see %s explosion!", (range > 0) ? "a huge" : "an");
|
|
}
|
|
|
|
if (dirtype == DT_COMPASS) {
|
|
animradial(c, range, '}', C_RED, DT_COMPASS, wantannounce ? buf : NULL, wantannounce ? buf : NULL);
|
|
} else { // ie. DT_ORTH
|
|
animradial(c, range, '}', C_RED, DT_ORTH, wantannounce ? buf : NULL, wantannounce ? buf : NULL);
|
|
}
|
|
|
|
if (!haslos(player, c)) {
|
|
noise(c, NULL, NC_OTHER, (range > 0) ? SV_PLANE : SV_TRUCK, "an explosion!", NULL);
|
|
}
|
|
|
|
getradiuscells(c, range, dirtype, B_FALSE, killwalls ? LOF_DONTNEED : LOF_WALLSTOP, B_TRUE, retcell, &nretcells, B_FALSE);
|
|
for (i = 0; i < nretcells; i++) {
|
|
explodesinglecell(retcell[i], dam, killwalls, o, c);
|
|
}
|
|
|
|
// lfs up to 1 cell away are knocked back, if no walls in the way
|
|
getradiuscells(c, range+1, dirtype, B_FALSE, LOF_WALLSTOP, B_TRUE, retcell, &nretcells, B_FALSE);
|
|
for (i = 0; i < nretcells; i++) {
|
|
cell_t *cc;
|
|
int mydist;
|
|
cc = retcell[i];
|
|
mydist = getcelldist(c,cc);
|
|
if (cc->lf && !isdead(cc->lf)) {
|
|
int critchance;
|
|
// critical hit? 100% chance in middle, 60 at one cell, 20 at two cells
|
|
critchance = 100 - (mydist*40);
|
|
if (pctchance(critchance)) {
|
|
criticalhit(NULL, cc->lf, BP_HANDS, pctof(critchance, dam), DT_EXPLOSIVE);
|
|
}
|
|
// move away from centre of explosion
|
|
knockback(cc->lf, getdiraway(cc, c, NULL, B_FALSE, DT_COMPASS, B_FALSE), 2, NULL, 40-(mydist*10), B_TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void expand_cave(map_t *map, int numpasses) {
|
|
int n,x,y,dir;
|
|
cell_t *c;
|
|
int chancetoclear;
|
|
chancetoclear = rnd(15,25);
|
|
for (n = 0; n < numpasses; n++) {
|
|
// for each dug map cell, 25% chance of clearing each adjacent cell
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
if (!c->type->solid && !c->visited) { // cell is empty and not processed already?
|
|
cell_t *c2;
|
|
// check for surrounding solid cells
|
|
for (dir = DC_N; dir <= DC_NW; dir++) {
|
|
c2 = getcellindir(c, dir);
|
|
if (c2 && c2->type->solid && pctchance(chancetoclear)) {
|
|
setcelltype(c2, getmapempty(map));
|
|
}
|
|
}
|
|
// mark cell as processed
|
|
c->visited = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
getobnametrue(o, obname, 1);
|
|
snprintf(buf, BUFLEN, "an exploding %s",strchr(obname, ' ')+1);
|
|
} else {
|
|
snprintf(buf, BUFLEN, "an explosion");
|
|
}
|
|
|
|
// take damage
|
|
losehp(c->lf, dam, DT_EXPLOSIVE, NULL, buf);
|
|
|
|
}
|
|
|
|
damageallobs(o, c->obpile, dam, DT_EXPLOSIVE, NULL);
|
|
|
|
if (killwalls) {
|
|
if (c->type->solid) {
|
|
damagecell(c, dam, DT_EXPLOSIVE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void finalisemap(map_t *map, object_t *entryob) {
|
|
enum OBTYPE upstairtype, downstairtype;
|
|
int i,d,x,y;
|
|
int linkedentry = B_FALSE;
|
|
char roomlightob[BUFLEN],corridorlightob[BUFLEN];
|
|
int roomlightchance = 0;
|
|
int corridorlightfreq = 0;
|
|
int nupstairsneeded = 0,ndownstairsneeded = 0;
|
|
cell_t *c;
|
|
object_t *o,*nexto;
|
|
// make sure this map has sufficient up/down staircases as defined by its
|
|
// region type.
|
|
//
|
|
// first dungeon level is a special case. it has 1 up stairs, 3 down.
|
|
|
|
upstairtype = map->habitat->upstairtype;
|
|
downstairtype = map->habitat->downstairtype;
|
|
|
|
nupstairsneeded = map->region->rtype->stairsperlev - countmapobs(map, upstairtype);
|
|
ndownstairsneeded = map->region->rtype->stairsperlev - countmapobs(map, downstairtype);
|
|
|
|
// override # up stairs for first level of a branch.
|
|
if (hasflag(map->flags, F_FIRSTINBRANCH)) {
|
|
nupstairsneeded = 1;
|
|
}
|
|
|
|
|
|
if ( (nupstairsneeded && (upstairtype == OT_NONE)) ||
|
|
(ndownstairsneeded && (downstairtype == OT_NONE)) ) {
|
|
dblog("ERROR - need up/down stairs, but no stairtype defined for habitat %s!", map->habitat->name);
|
|
msg("ERROR - need up/down stairs, but no stairtype defined for habitat %s!", map->habitat->name); more();
|
|
}
|
|
|
|
// UP STAIRS
|
|
if (upstairtype != OT_NONE) {
|
|
// SPECIAL CASE for first level:
|
|
if ((map->habitat->id == H_DUNGEON) && (map->depth == 1)) {
|
|
flag_t *f;
|
|
// first dungeon level. just one exit stairs
|
|
c = NULL;
|
|
while (!c || countobs(c->obpile, B_TRUE)) {
|
|
c = getrandomroomcell(map, ANYROOM, WE_EMPTY);
|
|
}
|
|
o = addobfast(c->obpile, upstairtype);
|
|
if (entryob) {
|
|
linkstairs(o, entryob);
|
|
} else {
|
|
// have to force these stairs to go back to a different region.
|
|
f = hasflag(o->flags, F_CLIMBABLE);
|
|
f->val[1] = map->region->parentregion->id;
|
|
linkstairs(o, NULL);
|
|
}
|
|
|
|
// special case: first dungeon level has barriers over the exit stairs
|
|
if (map->region->rtype->id == RG_MAINDUNGEON) {
|
|
if (c->lf) killlf(c->lf);
|
|
addobfast(c->obpile, OT_MAGICBARRIER);
|
|
// also clear all the cells around it to prevent reachability errors
|
|
for (d = DC_N; d <= DC_NW; d++) {
|
|
cell_t *c2;
|
|
c2 = getcellindir(c, d);
|
|
if (c2) setcelltype(c2, getmapempty(map));
|
|
}
|
|
}
|
|
} else {
|
|
// up stairs on all other levels
|
|
for (i = 0; i < nupstairsneeded; i++) {
|
|
c = NULL;
|
|
while (!c || countobs(c->obpile, B_TRUE)) {
|
|
c = getrandomroomcell(map, ANYROOM, WE_EMPTY);
|
|
if (!c) {
|
|
// ANY cell at all, doesn't have to be a room.
|
|
c = getrandomcell(map);
|
|
c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND);
|
|
}
|
|
}
|
|
o = addobfast(c->obpile, upstairtype);
|
|
assert(o);
|
|
if (entryob && !linkedentry) {
|
|
linkstairs(o, entryob);
|
|
linkedentry = B_TRUE;
|
|
} else {
|
|
linkstairs(o, NULL);
|
|
}
|
|
}
|
|
}
|
|
// make sure we have at least one up stairs
|
|
if (map->region->rtype->id != RG_WORLDMAP) {
|
|
assert(findobinmap(map, upstairtype));
|
|
}
|
|
}
|
|
|
|
// DOWN STAIRS
|
|
if ((downstairtype != OT_NONE) && (map->depth < map->region->rtype->maxdepth)) {
|
|
for (i = 0; i < ndownstairsneeded; i++) {
|
|
c = NULL;
|
|
while (!c || countobs(c->obpile, B_TRUE)) {
|
|
c = getrandomroomcell(map, ANYROOM, WE_EMPTY);
|
|
if (!c) {
|
|
// ANY cell at all, doesn't have to be a room.
|
|
c = getrandomcell(map);
|
|
c = getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND);
|
|
}
|
|
}
|
|
o = addobfast(c->obpile, downstairtype);
|
|
assert(o);
|
|
linkstairs(o, NULL);
|
|
}
|
|
}
|
|
|
|
for (y = 0; y < map->h; y++) {
|
|
for (x = 0; x < map->w; x++) {
|
|
c = getcellat(map, x, y);
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (c->room && c->room->vault) {
|
|
} else {
|
|
// doors which go nowhere?
|
|
if (isdoor(o, NULL)) {
|
|
int dir, ok = B_FALSE;
|
|
cell_t *c2;
|
|
// check all directions for a cell which isn't
|
|
// part of this room.
|
|
for (dir = DC_N; dir <= DC_NW; dir++) {
|
|
c2 = getcellindir(c, dir);
|
|
if (c2 && cellwalkable(NULL, c2, NULL) && getroomid(c2) != getroomid(c)) {
|
|
ok = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!ok) {
|
|
killob(o);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
// unlinked stairs? ie ones added from vaults. if so, link them.
|
|
if (hasflag(o->flags, F_CLIMBABLE) && !hasflag(o->flags, F_MAPLINK)) {
|
|
if (o->type->id != OT_PORTAL) {
|
|
linkstairs(o, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if map if not fully lit, scatter light sources around.
|
|
switch (map->illumination) {
|
|
case IL_FULLLIT: break;
|
|
case IL_WELLLIT:
|
|
strcpy(roomlightob, "patch of sun moss");
|
|
strcpy(corridorlightob, "patch of moon moss");
|
|
roomlightchance = 100;
|
|
corridorlightfreq = 4;
|
|
break;
|
|
case IL_DIM:
|
|
strcpy(roomlightob, "patch of moon moss");
|
|
strcpy(corridorlightob, "patch of moon moss");
|
|
roomlightchance = 100;
|
|
corridorlightfreq = 6;
|
|
break;
|
|
case IL_SHADOWY:
|
|
strcpy(roomlightob, "patch of moon moss");
|
|
strcpy(corridorlightob, "patch of moon moss");
|
|
roomlightchance = 50;
|
|
corridorlightfreq = 8;
|
|
break;
|
|
case IL_FULLDARK:
|
|
strcpy(roomlightob, "patch of moon moss");
|
|
strcpy(corridorlightob, "patch of moon moss");
|
|
roomlightchance = 33;
|
|
corridorlightfreq = 0;
|
|
break;
|
|
}
|
|
|
|
if (roomlightchance) {
|
|
for (i = 0; i < map->nrooms; i++) {
|
|
if (pctchance(roomlightchance)) {
|
|
c = getrandomroomcell(map, i, WE_WALKABLE);
|
|
addob(c->obpile, roomlightob);
|
|
}
|
|
}
|
|
}
|
|
if (corridorlightfreq) {
|
|
for (y = 1; y < map->h-1; y += corridorlightfreq) {
|
|
for (x = 1; x < map->w-1; x += corridorlightfreq) {
|
|
c = getcellat(map, x, y);
|
|
if (c && !isroom(c)) {
|
|
cell_t *c2;
|
|
c2 = getrandomadjcell(c, WE_NOTWALL, B_ALLOWEXPAND);
|
|
if (c2) {
|
|
addob(c2->obpile, corridorlightob);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags, enum BEHAVIOUR wantbehaviour, int idx) {
|
|
flag_t *f;
|
|
enum FLAG noflag[MAXCANDIDATES];
|
|
int nnoflags = 0,i;
|
|
int mapdiff;
|
|
|
|
mapdiff = getmapdifficulty(lf->cell->map)/2;
|
|
|
|
if (leader) {
|
|
// if leader is asleep:
|
|
// for unintelligent monsters, minions will also be asleep
|
|
// for intelligent monsters, all but the FIRST minion will also be asleep
|
|
if (lfhasflag(leader, F_ASLEEP)) {
|
|
enum ATTRBRACKET iqb,wisb;
|
|
int keepguard = B_FALSE;
|
|
iqb = getattrbracket(getattr(lf, A_IQ), A_IQ, NULL);
|
|
wisb = getattrbracket(getattr(lf, A_WIS), A_WIS, NULL);
|
|
if ((iqb > IQ_ANIMAL) && (wisb >= AT_AVERAGE)) {
|
|
keepguard = B_TRUE;
|
|
}
|
|
if (keepguard && (idx == 0)) {
|
|
// first minion will stay awake
|
|
noflag[nnoflags++] = F_ASLEEP;
|
|
} else {
|
|
if (wantflags && hasflag(wantflags, F_ASLEEP)) {
|
|
// already going to be asleep?
|
|
} else {
|
|
addflag(wantflags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((lf->cell->map->illumination != IL_FULLLIT) && !lfhasflag(lf, F_SEEINDARK)) {
|
|
if ((getraceclass(lf) != RC_HUMANOID) && pctchance(75)) {
|
|
addflag(wantflags, F_SEEINDARK, rnd(3,5), NA, NA, NULL);
|
|
}
|
|
}
|
|
|
|
// minions never have certain flags.
|
|
killflagsofid(lf->flags, F_DEMANDSBRIBE);
|
|
}
|
|
|
|
// random monster behaviours
|
|
if ((wantbehaviour == BH_NONE) && canhaverandombehaviour(lf) && onein(6)) {
|
|
wantbehaviour = getrandombehaviour();
|
|
}
|
|
givebehaviour(lf, wantbehaviour);
|
|
|
|
if (wantflags) {
|
|
copyflags(lf->flags, wantflags, NA);
|
|
}
|
|
|
|
for (i = 0; i < nnoflags; i++ ){
|
|
killflagsofid(lf->flags, noflag[i]);
|
|
}
|
|
|
|
if (lfhasflag(lf, F_ASLEEP)) {
|
|
killflagsofid(lf->flags, F_FLYING);
|
|
killflagsofid(lf->flags, F_HIDING);
|
|
} else {
|
|
if (hasflag(lf->race->flags, F_NATURALFLIGHT) && !lfhasflag(lf, F_FLYING)) {
|
|
if (cancast(lf, OT_S_FLIGHT, NULL)) {
|
|
notime = B_TRUE;
|
|
castspell(lf, OT_S_FLIGHT, lf, NULL, lf->cell, NULL, NULL);
|
|
notime = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// automatic spells
|
|
f = hasflag(lf->flags, F_RNDSPELLCOUNT);
|
|
if (f) {
|
|
autospells(lf, f->val[0]);
|
|
}
|
|
|
|
addhomeobs(lf, B_TRUE);
|
|
|
|
// if monster can shapeshift, it will sometimes start in its alternate form.
|
|
if (cancast(lf, OT_S_SHAPESHIFT, NULL)) {
|
|
if (onein(4)) {
|
|
castspell(lf, OT_S_SHAPESHIFT, lf, NULL, lf->cell, NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
getflags(lf->flags, retflag, &nretflags, F_LIFEOB, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
if (retflag[i]->val[0] != NA) {
|
|
addobfast(lf->cell->obpile, retflag[i]->val[0]);
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
celltype_t *findcelltype(enum CELLTYPE cid) {
|
|
celltype_t *ct;
|
|
for (ct = firstcelltype ; ct ; ct = ct->next) {
|
|
if (ct->id == cid) return ct;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
celltype_t *findcelltypebyname(char *name) {
|
|
celltype_t *ct;
|
|
for (ct = firstcelltype ; ct ; ct = ct->next) {
|
|
if (!strcmp(ct->name, name)) return ct;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
habitat_t *findhabitat(enum HABITAT id) {
|
|
habitat_t *h;
|
|
for (h = firsthabitat ; h ; h = h->next) {
|
|
if (h->id == id) return h;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
habitat_t *findhabitatbyname(char *name) {
|
|
habitat_t *h;
|
|
for (h = firsthabitat ; h ; h = h->next) {
|
|
if (streq(h->name, name)) return h;
|
|
}
|
|
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;
|
|
}
|
|
|
|
object_t *findmapobwithflag(map_t *m, enum FLAG flagid) {
|
|
cell_t *c;
|
|
object_t *o;
|
|
int x,y;
|
|
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
o = hasobwithflag(c->obpile, flagid);
|
|
if (o) return o;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
object_t *findmapobwithflagval(map_t *m, enum FLAG flagid, int val0, int val1, int val2, char *text) {
|
|
cell_t *c;
|
|
object_t *o;
|
|
int x,y;
|
|
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
o = hasobwithflagval(c->obpile, flagid, val0, val1, val2, text);
|
|
if (o) return o;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
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;
|
|
default:
|
|
dblog("error: invalid side given to mapentrypoint.");
|
|
msg("error: invalid side given to mapentrypoint.");
|
|
return NULL;
|
|
}
|
|
|
|
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 OBTYPE 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;
|
|
}
|
|
|
|
regionoutline_t *findoutline(int id) {
|
|
regionoutline_t *ro;
|
|
for (ro = firstregionoutline ;ro ; ro = ro->next) {
|
|
if (ro->id == id) return ro;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
regiontype_t *findrandomregiontypewithname(char *name) {
|
|
regiontype_t *rt,*poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
char buf[BUFLEN];
|
|
for (rt = firstregiontype ; rt ; rt = rt->next) {
|
|
if (streq(buf, rt->name)) {
|
|
poss[nposs++] = rt;
|
|
}
|
|
}
|
|
if (nposs) {
|
|
return poss[rnd(0,nposs-1)];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
region_t *findregion(int regionid) {
|
|
region_t *r;
|
|
for (r = firstregion ; r ; r = r->next) {
|
|
if (r->id == regionid) return r;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// this will objviously only work for unique regiontypes like
|
|
// rg_worldmap and rg_firstdungeon
|
|
region_t *findregionbytype(enum REGIONTYPE rtid) {
|
|
region_t *r;
|
|
for (r = firstregion ; r ; r = r->next) {
|
|
if (r->rtype->id == rtid) return r;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
map_t *findregionmap(int regionid, int depth) {
|
|
map_t *m;
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
if ((m->depth == depth) && (m->region->id == regionid)) return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// returns the RT_REGIONLINK regionthing which links to region type rtid (eg. RG_CAVE)
|
|
regionthing_t *findregionlink(enum REGIONTYPE rtid) {
|
|
region_t *r;
|
|
regionthing_t *rt;
|
|
int i;
|
|
for (r = firstregion ; r ; r = r->next) {
|
|
if (!r->outline) continue;
|
|
for (i = 0; i < r->outline->nthings; i++ ){
|
|
rt = &r->outline->thing[i];
|
|
if ((rt->whatkind == RT_REGIONLINK) && (rt->value == rtid)) {
|
|
return rt;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// find the region thing with the given id.
|
|
// if optional 'retregion' is supplied, fill it in with the region which contains the thing.
|
|
regionthing_t *findregionthing(int id, region_t **retregion) {
|
|
region_t *r;
|
|
regionthing_t *rt;
|
|
int i;
|
|
for (r = firstregion ; r ; r = r->next) {
|
|
if (!r->outline) continue;
|
|
for (i = 0; i < r->outline->nthings; i++ ){
|
|
rt = &r->outline->thing[i];
|
|
if (rt->id == id) {
|
|
if (retregion) *retregion = r;
|
|
return rt;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
regiontype_t *findregiontype(enum REGIONTYPE rtype) {
|
|
regiontype_t *rt;
|
|
for (rt = firstregiontype ; rt ; rt = rt->next) {
|
|
if (rt->id == rtype) return rt;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
regiontype_t *findregiontypebyname(char *name) {
|
|
regiontype_t *rt;
|
|
for (rt = firstregiontype ; rt ; rt = rt->next) {
|
|
if (!strcasecmp(rt->name, name)) return rt;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
room_t *findroom(map_t *m, int roomid) {
|
|
int i;
|
|
for (i = 0; i < m->nrooms; i++) {
|
|
if (m->room[i].id == roomid) {
|
|
return &(m->room[i]);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
map_t *findsurfaceexitmap(map_t *m) {
|
|
object_t *o;
|
|
cell_t *c;
|
|
int x,y;
|
|
// find up stairs on current map
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (c) {
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (hasflagval(o->flags, F_CLIMBABLE, D_UP, NA, NA, NULL)) {
|
|
cell_t *newc;
|
|
newc = getstairdestination(o, NULL);
|
|
if (newc) {
|
|
return newc->map;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
|
|
vault_t *getcellvault(cell_t *c) {
|
|
if (c->room) {
|
|
return c->room->vault;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
cell_t *getclosestroomcell(lifeform_t *lf, int roomid) {
|
|
int i;
|
|
cell_t *c,*best = NULL;
|
|
int closest = 9999;
|
|
for (i = 0; i < lf->cell->map->w * lf->cell->map->h; i++) {
|
|
c = lf->cell->map->cell[i];
|
|
if ((getroomid(c) == roomid) & cellwalkable(lf, c, NULL)) {
|
|
if (getcelldist(lf->cell, c) < closest) {
|
|
best = c;
|
|
}
|
|
}
|
|
}
|
|
return best;
|
|
}
|
|
|
|
// 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) dblog("--- Trying %s...\n",getdirname(dir));
|
|
if (tried[dir] == B_TRUE) { // already found this dir to be invalid
|
|
lastdir = D_UNKNOWN;
|
|
if (db) dblog("--- 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) dblog("--- %s %s!\n",getdirname(dir), err);
|
|
tried[dir] = B_TRUE;
|
|
lastdir = D_UNKNOWN;
|
|
numtries++;
|
|
} else if (!isnewcellok(newcell1, err)) {
|
|
if (db) dblog("--- %s %s!\n",getdirname(dir), err);
|
|
tried[dir] = B_TRUE;
|
|
lastdir = D_UNKNOWN;
|
|
numtries++;
|
|
} else if (!isnewcellok(newcell2, err)) {
|
|
if (db) dblog("--- %s %s!\n",getdirname(dir), err);
|
|
tried[dir] = B_TRUE;
|
|
lastdir = D_UNKNOWN;
|
|
numtries++;
|
|
} else { // ok
|
|
if (db) dblog("--- %s %s!\n",getdirname(dir), err);
|
|
foundvaliddir = B_TRUE;
|
|
}
|
|
} else {
|
|
if (db) dblog("--- %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;
|
|
}
|
|
|
|
|
|
|
|
cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand) {
|
|
return real_getrandomadjcell(c, wantempty, allowexpand, LOF_NEED, NULL, NULL);
|
|
}
|
|
|
|
cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LOFTYPE needlof, enum OBTYPE *dontwantob, lifeform_t *preferlos) {
|
|
int radius = 1;
|
|
int x,y;
|
|
cell_t *poss[MAXCANDIDATES];
|
|
cell_t *losposs[MAXCANDIDATES];
|
|
int nposs = 0,nlosposs = 0;
|
|
cell_t *new,*sel = NULL;
|
|
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++;
|
|
|
|
ok = cellmatchescondition(new, wantempty);
|
|
|
|
// obs we dont want?
|
|
if (dontwantob) {
|
|
for (badoid = dontwantob; (*badoid != OT_NONE) ; badoid++) {
|
|
if (hasob(new->obpile, *badoid)) {
|
|
ok = B_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
if (preferlos && haslos(preferlos, new)) {
|
|
losposs[nlosposs++] = new;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// select a random cell
|
|
if (preferlos && nlosposs) {
|
|
sel = losposs[rnd(0,nlosposs-1)];
|
|
} else {
|
|
sel = poss[rnd(0,nposs-1)];
|
|
}
|
|
return sel;
|
|
}
|
|
|
|
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, enum CELLTYPE id) {
|
|
cell_t *cell;
|
|
cell = getrandomcell(map);
|
|
while (cell->type->id != id) {
|
|
cell = getrandomcell(map);
|
|
}
|
|
return cell;
|
|
}
|
|
|
|
int compassdir(int orthdir) {
|
|
switch (orthdir) {
|
|
case D_N:
|
|
return DC_N;
|
|
case D_S:
|
|
return DC_S;
|
|
case D_E:
|
|
return DC_E;
|
|
case D_W:
|
|
return DC_W;
|
|
}
|
|
return D_NONE;
|
|
}
|
|
|
|
|
|
|
|
int getrandomdir(int dirtype) {
|
|
if (dirtype == DT_ORTH) {
|
|
return rnd(D_N, D_W);
|
|
} else { // ie. DT_COMPASS
|
|
return rnd(DC_N, DC_NW);
|
|
}
|
|
|
|
}
|
|
int getrandomdirexcept(int dirtype, int exception) {
|
|
int dir;
|
|
dir = exception;
|
|
while (dir == exception) {
|
|
if (dirtype == DT_ORTH) {
|
|
dir = rnd(D_N, D_W);
|
|
} else { // ie. DT_COMPASS
|
|
dir = rnd(DC_N, DC_NW);
|
|
}
|
|
}
|
|
return dir;
|
|
}
|
|
|
|
cell_t *getrandomroomcell(map_t *map, int roomid, int wantempty) {
|
|
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?
|
|
// using c->type->solid in case we have a room
|
|
// filled with boulders
|
|
//if (c && cellwalkable(NULL, c, NULL)) {
|
|
if (c && !c->type->solid) {
|
|
int ok = B_FALSE;
|
|
if (getroomid(c) == roomid) {
|
|
ok = B_TRUE;
|
|
} else if (roomid == ANYROOM) {
|
|
if (isroom(c)) {
|
|
ok = B_TRUE;
|
|
}
|
|
}
|
|
|
|
if (ok && cellmatchescondition(c, wantempty)) {
|
|
poss[npossible] = c;
|
|
npossible++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (npossible <= 0) {
|
|
free(poss);
|
|
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 && (getroomid(c) == 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;
|
|
int addition = 0;
|
|
if (slipob) *slipob = NULL;
|
|
|
|
switch (c->type->id) {
|
|
case CT_FLOORTILE: addition = 2; break;
|
|
case CT_FLOORCARPET: addition = -2; break;
|
|
default: addition = 0; break;
|
|
}
|
|
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
int thisslip = 0;
|
|
sumflags(o->flags, F_SLIPPERY, &thisslip, NULL, NULL);
|
|
limit(&thisslip, 0, NA);
|
|
if (thisslip > 0) {
|
|
if (thisslip > bestslip) {
|
|
bestob = o;
|
|
bestslip = thisslip;
|
|
}
|
|
thisslip += addition;
|
|
thisslip *= o->amt;
|
|
totalslip += thisslip;
|
|
}
|
|
}
|
|
|
|
totalslip *= 2;
|
|
|
|
if (slipob) {
|
|
*slipob = bestob;
|
|
}
|
|
|
|
return totalslip;
|
|
}
|
|
|
|
// if madenewmap is passed, then we'll try to make a new map for stairs with no endpoint
|
|
cell_t *getstairdestination(object_t *o, int *madenewmap) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
if (!newcell && madenewmap) {
|
|
cell_t *obcell = NULL;
|
|
map_t *newmap,*curmap;
|
|
region_t *newregion = NULL;
|
|
int dir,newdepth;
|
|
|
|
obcell = getoblocation(o);
|
|
curmap = obcell->map;
|
|
|
|
f = hasflag(o->flags, F_CLIMBABLE);
|
|
if (!f) return NULL;
|
|
|
|
dir = getstairdirection(o);
|
|
if ((dir != D_UP) && (dir != D_DOWN)) {
|
|
// ie this is a portal
|
|
return NULL;
|
|
} else {
|
|
if (dir == D_UP) newdepth = curmap->depth - 1;
|
|
else newdepth = curmap->depth + 1;
|
|
if (f->val[1] == NA) {
|
|
// use same region
|
|
newregion = obcell->map->region;
|
|
} else {
|
|
newregion = findregion(f->val[1]);
|
|
}
|
|
}
|
|
|
|
// is there already a level of the correct depth?
|
|
newmap = findregionmap(newregion->id, newdepth);
|
|
if (newmap) {
|
|
dblog("ERROR - unlinked stairs! should have been linked during map creation.\n");
|
|
msg("ERROR - unlinked stairs! should have been linked during map creation.\n");
|
|
return NULL;
|
|
} else {
|
|
// generate a new map! this will fill in the destination of our stairs
|
|
newmap = addmap();
|
|
// first map of a newly created region?
|
|
if (newregion->id != curmap->region->id) {
|
|
newdepth = 1;
|
|
addflag(newmap->flags, F_FIRSTINBRANCH, B_TRUE, NA, NA, NULL);
|
|
}
|
|
createmap(newmap, newdepth, newregion, curmap, dir, o);
|
|
// at this point, stairs should have a destination (map creation will
|
|
// fill it in)
|
|
newcell = getstairdestination(o, NULL); // recursive call
|
|
*madenewmap = B_TRUE;
|
|
}
|
|
|
|
}
|
|
return newcell;
|
|
}
|
|
|
|
object_t *hasenterableobject(cell_t *c) {
|
|
return hasobwithflag(c->obpile, F_CLIMBABLE);
|
|
}
|
|
|
|
object_t *hascloseddoor(cell_t *c) {
|
|
object_t *o;
|
|
int isopen;
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (isdoor(o, &isopen)) {
|
|
if (!isopen) {
|
|
return o;
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int hascrushableob(cell_t *c, lifeform_t *lf) {
|
|
object_t *o;
|
|
if (lfhasflag(lf, F_CAREFULMOVE)) {
|
|
return B_FALSE;
|
|
}
|
|
for (o = c->obpile->first ; o; o = o->next) {
|
|
if (cancrush(lf, o)) return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
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;
|
|
// default
|
|
if (tflag) *tflag = NULL;
|
|
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) {
|
|
if ((f->val[0] == lf->race->id) && (atoi(f->text) == lf->id)) {
|
|
if (tflag) {
|
|
*tflag = f;
|
|
}
|
|
return o;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void initmap(void) {
|
|
// habitats
|
|
// thingchance, obchance, vaultchance, maxvisrange, upstiartype, downstairtype
|
|
addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 3, 50, 30, 6, OT_STAIRSUP, OT_STAIRSDOWN);
|
|
addhabitat(H_CAVE, "cave", CT_DIRT, CT_WALLDIRT, 5, 65, 10, 6, OT_TUNNELUP, OT_TUNNELDOWN);
|
|
addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALLTREE, 3, 75, 0, MAXVISRANGE, OT_TREEUP, OT_TREEDOWN);
|
|
addhabitat(H_HEAVEN, "heaven", CT_CORRIDOR, CT_WALLGLASS, 5, 0, 0, MAXVISRANGE, OT_NONE, OT_NONE);
|
|
addhabitat(H_PIT, "pit", CT_CORRIDOR, CT_WALL, 0, 0, 0, 5, OT_NONE, OT_NONE);
|
|
addhabitat(H_VILLAGE, "village", CT_GRASS, CT_WALL, 3, 70, 0, MAXVISRANGE, OT_NONE, OT_NONE);
|
|
addhabitat(H_SEWER, "sewer", CT_CORRIDOR, CT_WALL, 5, 50, 0, MAXVISRANGE, OT_NONE, OT_NONE);
|
|
addhabitat(H_STOMACH, "stomach", CT_FLOORFLESH, CT_WALLFLESH, 5, 80, 0, MAXVISRANGE, OT_NONE, OT_NONE);
|
|
addhabitat(H_SWAMP, "swamp", CT_CORRIDOR, CT_WALL, 3, 50, 0, MAXVISRANGE, OT_STAIRSUP, OT_STAIRSDOWN);
|
|
|
|
// cell types - solid
|
|
// floorheight, hp
|
|
addcelltype(CT_WALL, "rock wall", UNI_SHADEDARK, C_GREY, B_SOLID, B_OPAQUE, MT_STONE, 0, 50);
|
|
addcelltype(CT_WALLBRICK, "brick wall", UNI_SHADEDARK, C_ORANGE, B_SOLID, B_OPAQUE, MT_STONE, 0, 40);
|
|
addcelltype(CT_WALLDIRT, "dirt wall", UNI_SHADEDARK, C_BROWN, B_SOLID, B_OPAQUE, MT_STONE, 0, 20);
|
|
addcelltype(CT_WALLWOOD, "wooden wall", UNI_SOLID, C_BROWN, B_SOLID, B_OPAQUE, MT_WOOD, 0, 30);
|
|
addcelltype(CT_WALLFLESH, "flesh wall", UNI_SOLID, C_RED, B_SOLID, B_OPAQUE, MT_FLESH, 0, 25);
|
|
addcelltype(CT_WALLGLASS, "glass wall", UNI_SOLID, C_CYAN, B_SOLID, B_TRANS, MT_GLASS, 0, 20);
|
|
//addcelltype(CT_WALLTREE, "dense bushland", UNI_SHADEDARK, C_GREEN, B_SOLID, B_OPAQUE, MT_PLANT, 0, 100);
|
|
addcelltype(CT_WALLTREE, "dense bushland", UNI_TREELOTS, C_GREEN, B_SOLID, B_OPAQUE, MT_PLANT, 0, 100);
|
|
addcelltype(CT_WALLMETAL, "metal wall", UNI_SOLID, C_WHITE, B_SOLID, B_OPAQUE, MT_METAL, 0, 75);
|
|
// cell types - non-solid
|
|
addcelltype(CT_FAKE, "fake cell", '.', C_GREEN, B_EMPTY, B_TRANS, MT_STONE, 0, -1);
|
|
addcelltype(CT_CORRIDOR, "rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0, -1);
|
|
addcelltype(CT_LOOPCORRIDOR, "rock floor", 'L', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0, -1);
|
|
addcelltype(CT_FLOORCARPET, "carpetted floor", '.', C_RED, B_EMPTY, B_TRANS, MT_CLOTH, 0, -1);
|
|
addcelltype(CT_FLOORWOOD, "wood floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0, -1);
|
|
addcelltype(CT_FLOORFLESH, "flesh floor", '.', C_RED, B_EMPTY, B_TRANS, MT_FLESH, 0, -1);
|
|
addcelltype(CT_FLOORSHOP, "shop floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0, -1);
|
|
addcelltype(CT_FLOORTILE, "tiled floor", '.', C_CYAN, B_EMPTY, B_TRANS, MT_METAL, 0, -1);
|
|
addcelltype(CT_GRASS, "grass", '.', C_GREEN, B_EMPTY, B_TRANS, MT_PLANT, 0, -1);
|
|
addcelltype(CT_DIRT, "dirt", '.', C_BROWN, B_EMPTY, B_TRANS, MT_STONE, 0, -1);
|
|
addcelltype(CT_LOWFLOOR, "low rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, -1, -1);
|
|
addcelltype(CT_VLOWFLOOR, "very low rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, -2, -1);
|
|
|
|
// region types
|
|
// name, pluralname?, defaulthab, maxdepth stairs stair major? depthmod
|
|
// perlev dir
|
|
addregiontype(RG_WORLDMAP, "The Surface", B_FALSE, H_FOREST, 10, 0, D_NONE, B_TRUE, 0);
|
|
addregiontype(RG_HEAVEN, "The Realm of Gods", B_FALSE, H_HEAVEN, 1, 0, D_NONE, B_FALSE, 0);
|
|
// main branches
|
|
addregiontype(RG_MAINDUNGEON, "The Main Dungeon", B_FALSE, H_DUNGEON, 25, 3, D_DOWN, B_TRUE, 0);
|
|
addregiontype(RG_CAVE, "The Goblin Caves", B_TRUE, H_CAVE, 5, 1, D_DOWN, B_TRUE, 2);
|
|
addregiontype(RG_WOODS, "The Sylvan Woods", B_TRUE, H_FOREST, 5, 3, D_DOWN, B_TRUE, 1);
|
|
// minor branches
|
|
addregiontype(RG_PIT, "A Pit", B_FALSE, H_PIT, 1, 1, D_DOWN, B_FALSE, 0);
|
|
addregiontype(RG_SEWER, "A Sewer", B_FALSE, H_SEWER, 1, 0, D_NONE, B_FALSE, 2);
|
|
addregiontype(RG_STOMACH, "A Stomach", B_FALSE, H_STOMACH, 1, 0, D_NONE, B_FALSE, 0);
|
|
|
|
}
|
|
|
|
|
|
void initmaplayout(void) {
|
|
//int vx[4],vy[4];
|
|
int i;
|
|
// MAPMAPMAPMAP
|
|
// region definitions (outlines)
|
|
addregionoutline(RG_WORLDMAP);
|
|
// link to first dungeon
|
|
addregionthing(lastregionoutline, NA, 0, 0, RT_REGIONLINK, RG_MAINDUNGEON, "staircase going down");
|
|
/*
|
|
// four villages
|
|
for (i = 0; i < 4; i++) {
|
|
vx[i] = 0; vy[i] = 0;
|
|
while ((vx[i] == 0) && (vy[i] == 0)) {
|
|
int n, distfromcentre;
|
|
distfromcentre = 2 + (i*2);
|
|
vx[i] = rnd(-distfromcentre,distfromcentre);
|
|
vy[i] = rnd(-distfromcentre,distfromcentre);
|
|
// check for other villages in this map...
|
|
for (n = 0; n < i; n++) {
|
|
if ((vx[n] == vx[i]) && (vy[n] == vy[i])) {
|
|
vx[i] = 0; vy[i] = 0; // invalidate!
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
addregionthing(lastregionoutline, NA, vx[i], vy[i], RT_HABITAT, H_VILLAGE, NULL);
|
|
addregionthing(lastregionoutline, NA, vx[i], vy[i], RT_VAULT, NA, "food_shop");
|
|
addregionthing(lastregionoutline, NA, vx[i], vy[i], RT_VAULT, NA, "pub");
|
|
addregionthing(lastregionoutline, NA, vx[i], vy[i], RT_RNDVAULTWITHFLAG, F_VAULTISSHOP, NULL);
|
|
addregionthing(lastregionoutline, NA, vx[i], vy[i], RT_RNDVAULTWITHFLAG, F_VAULTISSHOP, NULL);
|
|
addregionthing(lastregionoutline, NA, vx[i], vy[i], RT_RNDVAULTWITHFLAG, F_VAULTISSHOP, NULL);
|
|
}
|
|
//vx = 0; vy = -1;
|
|
*/
|
|
|
|
addregionoutline(RG_MAINDUNGEON);
|
|
addregionthing(lastregionoutline, 1, NA, NA, RT_RNDVAULTWITHFLAG, F_VAULTISPLAYERSTART, NULL);
|
|
// l2-4: sylvan woods
|
|
addregionthing(lastregionoutline, rnd(2,4), NA, NA, RT_REGIONLINK, RG_WOODS, "hollow tree leading down");
|
|
// l2-5: goblin caves
|
|
addregionthing(lastregionoutline, rnd(2,4), NA, NA, RT_REGIONLINK, RG_CAVE, "tunnel leading down");
|
|
// l6: jimbo's lair
|
|
addregionthing(lastregionoutline, 6, NA, NA, RT_VAULT, NA, "jimbos_lair");
|
|
// l7 - 10: swamp
|
|
addregionthing(lastregionoutline, rnd(7,10), NA, NA, RT_HABITAT, H_SWAMP, NULL);
|
|
// l25: last level
|
|
addregionthing(lastregionoutline, 25, NA, NA, RT_RNDVAULTWITHFLAG, F_VAULTISSHRINE, NULL); // godstone on last floor
|
|
|
|
// 1-3 fixed sewers
|
|
addregionthing(lastregionoutline, rnd(1,25), NA, NA, RT_REGIONLINK, RG_SEWER, "drainage grate");
|
|
for (i = 0; i < 2; i++) {
|
|
if (onein(2)) {
|
|
addregionthing(lastregionoutline, rnd(1,25), NA, NA, RT_REGIONLINK, RG_SEWER, "drainage grate");
|
|
}
|
|
}
|
|
|
|
|
|
// forced shops:
|
|
addregionthing(lastregionoutline, rnd(2,4), NA, NA, RT_OBJECT, NA, "random building");
|
|
addregionthing(lastregionoutline, rnd(5,7), NA, NA, RT_OBJECT, NA, "random building");
|
|
addregionthing(lastregionoutline, rnd(8,10), NA, NA, RT_OBJECT, NA, "random building");
|
|
addregionthing(lastregionoutline, rnd(11,13), NA, NA, RT_OBJECT, NA, "random building");
|
|
addregionthing(lastregionoutline, rnd(14,16), NA, NA, RT_OBJECT, NA, "random building");
|
|
addregionthing(lastregionoutline, rnd(17,19), NA, NA, RT_OBJECT, NA, "random building");
|
|
addregionthing(lastregionoutline, rnd(20,22), NA, NA, RT_OBJECT, NA, "random building");
|
|
addregionthing(lastregionoutline, rnd(23,25), NA, NA, RT_OBJECT, NA, "random building");
|
|
|
|
addregionoutline(RG_CAVE);
|
|
addregionthing(lastregionoutline, 5, NA, NA, RT_RNDVAULTWITHTAG, NA, "caveboss");
|
|
|
|
// add initial regions
|
|
addregion(RG_WORLDMAP, NULL, -1, 0, -1);
|
|
addregion(RG_HEAVEN, NULL, -1, 0, -1);
|
|
}
|
|
|
|
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_WALLFLESH:
|
|
case CT_WALLDIRT:
|
|
case CT_WALL:
|
|
return B_TRUE;
|
|
default:
|
|
break;
|
|
}
|
|
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;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags;
|
|
int i;
|
|
// handle scanner
|
|
if (c->lf) {
|
|
if (areallies(player, c->lf) && !isplayer(c->lf) && canhear(player, c, SV_SHOUT)) {
|
|
// assume that allies will keep in contact with the player, as long
|
|
// as they're not asleep
|
|
if (!lfhasflag(c->lf, F_ASLEEP)) {
|
|
set_scanned_glyph(TT_MONSTER, c->lf, " (heard ally)", desc, glyph);
|
|
*thing = c->lf;
|
|
return TT_MONSTER;
|
|
}
|
|
}
|
|
|
|
f = lfhasflag(player, F_DETECTLIFE);
|
|
if (f) {
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
*thing = c->lf;
|
|
if (f->val[1] == B_TRUE) {
|
|
set_scanned_glyph(TT_MONSTER, c->lf, " (detected)", desc, glyph);
|
|
} else {
|
|
if (desc) {
|
|
char *p;
|
|
p = getsizetext(getlfsize(c->lf));
|
|
snprintf(desc, BUFLEN, "%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_ENHANCESMELL);
|
|
if (f && issmellablelf(c->lf)) {
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
set_scanned_glyph(TT_MONSTER, c->lf, " (smelled)", desc, glyph);
|
|
*thing = c->lf;
|
|
return TT_MONSTER;
|
|
}
|
|
}
|
|
if (lfhasflagval(player, F_CANHEARLF, c->lf->id, NA, NA, NULL)) {
|
|
// can hear them using master level listen skill?
|
|
set_scanned_glyph(TT_MONSTER, c->lf, " (heard)", desc, glyph);
|
|
*thing = c->lf;
|
|
return TT_MONSTER;
|
|
}
|
|
f = lfhasflagval(player, F_GRABBEDBY, c->lf->id, NA, NA, NULL);
|
|
if (!f) {
|
|
f = lfhasflagval(c->lf, F_ATTACHEDTO, player->id, NA, NA, NULL);
|
|
}
|
|
if (!f) {
|
|
f = lfhasflagval(c->lf, F_UNSEENATTACKER, player->id, NA, NA, NULL);
|
|
}
|
|
if (f) {
|
|
if (glyph) {
|
|
glyph->ch = 'X';
|
|
glyph->colour = C_GREY;
|
|
}
|
|
*thing = c->lf;
|
|
if (desc) {
|
|
getlfname(c->lf, desc);
|
|
switch (f->id) {
|
|
case F_GRABBEDBY:
|
|
strcat(desc, " (holding you)");
|
|
break;
|
|
case F_ATTACHEDTO:
|
|
strcat(desc, " (attached to you)");
|
|
break;
|
|
case F_UNSEENATTACKER:
|
|
strcat(desc, " (unseen attacker)");
|
|
break;
|
|
default:
|
|
// should never happen
|
|
strcat(desc, " (detected)");
|
|
break;
|
|
}
|
|
}
|
|
return TT_MONSTER;
|
|
}
|
|
}
|
|
|
|
f = lfhasflag(player, F_DETECTMETAL);
|
|
if (f) {
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
if (c->lf && ismetal(getlfmaterial(c->lf)) ) {
|
|
*thing = c->lf;
|
|
if (glyph) {
|
|
glyph->ch = '*';
|
|
glyph->colour = C_GREY;
|
|
}
|
|
if (desc) snprintf(desc, BUFLEN, "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) snprintf(desc, BUFLEN, "something metallic");
|
|
return TT_OBJECT;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end if hasflag detectmetal
|
|
|
|
|
|
// get list of all detected ob ids.
|
|
getflags(player->flags, retflag, &nretflags, F_DETECTOBS, F_ENHANCESMELL, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
object_t *o;
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if ((f->id == F_DETECTOBS) && ((f->val[1] == NA) || (o->type->id == f->val[1]))) {
|
|
if (!hasflag(o->flags, F_NOPICKUP) && !hasflag(o->flags, F_DOOR)) {
|
|
*thing = o;
|
|
if (glyph) {
|
|
glyph->ch = '*';
|
|
glyph->colour = C_BOLDGREEN;
|
|
}
|
|
if (desc) {
|
|
if (f->val[1] == NA) {
|
|
strcpy(desc, "a detected object");
|
|
} else {
|
|
getobname(o, desc, o->amt);
|
|
}
|
|
}
|
|
return TT_OBJECT;
|
|
}
|
|
} else if ((f->id == F_ENHANCESMELL) && issmellableob(o)) {
|
|
if (getcelldistorth(player->cell, c) <= f->val[0]) {
|
|
*thing = o;
|
|
set_scanned_glyph(TT_OBJECT, o, " (smelled)", desc, glyph);
|
|
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) snprintf(err, BUFLEN,"goes off the map.");
|
|
return B_FALSE;
|
|
} else if ( cell->locked ) { // locked
|
|
if (err) snprintf(err, BUFLEN,"locked.");
|
|
return B_FALSE;
|
|
} else if ( !cell->type->solid) { // already an empty space there
|
|
if (err) snprintf(err, BUFLEN,"goes to an empty space (%d,%d)",cell->x,cell->y);
|
|
return B_FALSE;
|
|
}
|
|
|
|
// ok!
|
|
if (err) snprintf(err, BUFLEN, "OK!");
|
|
return B_TRUE;
|
|
}
|
|
|
|
int isnighttime(void) {
|
|
int hours,mins,secs;
|
|
splittime(&hours,&mins,&secs);
|
|
if ((hours < 7) || (hours >= 19)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
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 && m->region->rtype->id == RG_WORLDMAP) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isroom(cell_t *c) {
|
|
if (c && (getroomid(c) >= 0)) {
|
|
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;
|
|
}
|
|
|
|
// 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.
|
|
// returns then umber of holes linked.
|
|
int linkholes(map_t *map) {
|
|
map_t *adjmap;
|
|
cell_t *c;
|
|
object_t *o, *newob;
|
|
int x,y;
|
|
int dir;
|
|
int nlinked = 0;
|
|
|
|
for (dir = D_UP ; dir <= D_DOWN; dir++) {
|
|
adjmap = getmapindir(map, dir);
|
|
if (adjmap) {
|
|
for (y = 0; y < adjmap->h; y++) {
|
|
for (x = 0; x < adjmap->w; x++) {
|
|
c = getcellat(adjmap, x, y);
|
|
if (c) {
|
|
// does the adjacent map have an unlinked pit going to us?
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (hasflagval(o->flags, F_PIT, diropposite(dir), NA, NA, NULL) &&
|
|
!hasflag(o->flags, F_MAPLINK)) {
|
|
cell_t *c2;
|
|
objecttype_t *ot;
|
|
ot = getoppositestairs(o->type);
|
|
// make a link to it in this map, as close as possible to same pos
|
|
c2 = getcellat(map, x, y);
|
|
if (c2->lf || hasobid(c2->obpile, ot->id)) {
|
|
// this will automatically avoid lifeforms since we're using
|
|
// we_walkable rather than we_notwall. this saves problems
|
|
// with someine coming up underneath you!
|
|
c2 = real_getrandomadjcell(c2, WE_NOLF, B_ALLOWEXPAND, LOF_DONTNEED, &ot->id, NULL);
|
|
}
|
|
// clear out the cell if required
|
|
if (c2->type->solid) {
|
|
setcelltype(c2, getcellempty(c2));
|
|
}
|
|
// note we specifically say DONT link the new hole, to avoid an infinite
|
|
// loop!
|
|
newob = addobject(c2->obpile, NULL, B_FALSE, B_FALSE, ot->id);
|
|
// link holes manually now.
|
|
linkstairs(newob, o);
|
|
|
|
// objects above fall down
|
|
if (dir == D_UP) {
|
|
obsfallthrough(c, o);
|
|
} else {
|
|
obsfallthrough(c2, newob);
|
|
}
|
|
nlinked++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nlinked;
|
|
}
|
|
|
|
object_t *linkportal(object_t *srcportal, int wantdepth) {
|
|
cell_t *srcloc,*newcell;
|
|
map_t *newmap = NULL;
|
|
object_t *dstportal = NULL;
|
|
|
|
srcloc = getoblocation(srcportal);
|
|
newmap = findregionmap(srcloc->map->region->id, wantdepth);
|
|
if (!newmap) {
|
|
// create new map
|
|
newmap = addmap();
|
|
createmap(newmap, wantdepth, srcloc->map->region, NULL, D_NONE, NULL);
|
|
}
|
|
|
|
// find a random cell there
|
|
newcell = getrandomcell(newmap);
|
|
while (!cellwalkable(NULL, newcell, NULL) || hasenterableobject(newcell) || getcellwaterdepth(newcell, NULL)) {
|
|
newcell = getrandomcell(newmap);
|
|
}
|
|
// add the dst portal
|
|
dstportal = addob(newcell->obpile, "magic portal");
|
|
assert(dstportal);
|
|
|
|
// link the dst portal
|
|
addflag_real(dstportal->flags, F_MAPLINK, srcloc->map->id, srcloc->x, srcloc->y, NULL, PERMENANT, B_FALSE, -1);
|
|
|
|
// link the source portal
|
|
addflag_real(srcportal->flags, F_MAPLINK, newmap->id, newcell->x, newcell->y, NULL, PERMENANT, B_FALSE, -1);
|
|
|
|
return dstportal;
|
|
}
|
|
|
|
// link the staircase 'o' to a free one in adjacent maps.
|
|
// o2 is optional. if o2 isn't provided, we will try to find
|
|
// something to link to ourself.
|
|
// returns TRUE if it failed because othermap doesn't exist.
|
|
int linkstairs(object_t *o, object_t *o2) {
|
|
map_t *othermap;
|
|
map_t *stairmap;
|
|
cell_t *staircell;
|
|
flag_t *f;
|
|
|
|
assert(o);
|
|
|
|
staircell = getoblocation(o);
|
|
stairmap = staircell->map;
|
|
|
|
if (o2) {
|
|
cell_t *othercell;
|
|
othercell = getoblocation(o2);
|
|
othermap = othercell->map;
|
|
} else {
|
|
objecttype_t *otherstairtype;
|
|
cell_t *c2;
|
|
int n, dir;
|
|
object_t *oo;
|
|
// find a valid other end
|
|
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);
|
|
}
|
|
|
|
// do stairs go to a particular region?
|
|
if (f->val[1] != NA) {
|
|
// if so, find the first map in that region (ie depth 1)
|
|
othermap = findregionmap(f->val[1], 1);
|
|
} else {
|
|
othermap = getmapindir(stairmap, f->val[0]);
|
|
}
|
|
if (othermap) {
|
|
int found = B_FALSE;
|
|
object_t *poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
// find an empty staircase of the correct type in the other map
|
|
for (n = 0; n < othermap->w*othermap->h; n++) {
|
|
c2 = othermap->cell[n];
|
|
oo = hasob(c2->obpile, otherstairtype->id);
|
|
if (oo) {
|
|
// remember all stairs of correct type, for debugging.
|
|
poss[nposs++] = oo;
|
|
// does it go nowhere?
|
|
if (!hasflag(oo->flags, F_MAPLINK)) {
|
|
o2 = oo;
|
|
found = B_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!found) {
|
|
dblog("ERROR - stairs should link to existing map ('%s', depth %d), but it has no free stairs.",
|
|
othermap->name, othermap->depth);
|
|
msg("ERROR - stairs should link to existing map ('%s', depth %d), but it has no free stairs.",
|
|
othermap->name, othermap->depth);
|
|
more();
|
|
raise(SIGINT); // debug
|
|
}
|
|
} // end if othermap
|
|
} // end if !o2
|
|
|
|
|
|
if (o2) {
|
|
char obid[BUFLEN];
|
|
// link it to here!
|
|
snprintf(obid, BUFLEN, "%ld", o->id);
|
|
addflag(o2->flags, F_MAPLINK, stairmap->id, NA, NA, obid);
|
|
// link me to there
|
|
snprintf(obid, BUFLEN, "%ld", o2->id);
|
|
addflag(o->flags, F_MAPLINK, othermap->id, NA, NA, obid);
|
|
// now mark that we are not a new staircase to a new region anymore
|
|
f = hasflag(o->flags, F_CLIMBABLE);
|
|
if (f) {
|
|
f->val[1] = NA;
|
|
}
|
|
} else {
|
|
return B_TRUE;
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
void makedoor(cell_t *cell, int openchance) {
|
|
object_t *o;
|
|
map_t *m;
|
|
char doorbuf[BUFLEN];
|
|
|
|
// can't have more than one door in a cell
|
|
if (hasobwithflag(cell->obpile, F_DOOR)) {
|
|
return;
|
|
}
|
|
|
|
m = cell->map;
|
|
|
|
setcelltype(cell, getcellempty(cell));
|
|
|
|
if ((rnd(1,100) + m->depth) >= 66) {
|
|
strcpy(doorbuf, "iron door");
|
|
} else {
|
|
strcpy(doorbuf, "wooden door");
|
|
}
|
|
|
|
// addob will determine whether the door is locked. if so,
|
|
// don't open it!
|
|
o = addob(cell->obpile, doorbuf);
|
|
if (o) {
|
|
if (!hasflag(o->flags, F_LOCKED) && (rnd(1,100) <= openchance)) {
|
|
opendoor(NULL, o);
|
|
} else {
|
|
int chance;
|
|
// door might be secret?
|
|
chance = rolldie(1,6) - (m->depth / 10);
|
|
|
|
// difficulty:
|
|
// l1 = 20
|
|
// l10 = 25
|
|
// l20 = 30
|
|
if (chance <= 1) {
|
|
addflag(o->flags, F_SECRET, getdoorsecretdiff(m->depth), NA, NA, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void makelit(cell_t *c, enum LIGHTLEV how, int howlong) {
|
|
enum LIGHTLEV origlit;
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
origlit = c->lit;
|
|
|
|
if (howlong > 0) {
|
|
// TODO: use a stack here instead
|
|
c->origlit = c->lit;
|
|
c->origlittimer = c->littimer;
|
|
c->littimer = howlong;
|
|
}
|
|
|
|
if (how != origlit) {
|
|
//if ((gamemode == GM_GAMESTARTED) && (c->lit != how)) {
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
lifeform_t *lf;
|
|
for (lf = c->map->lf ; lf ; lf = lf->next) {
|
|
//if (haslos(lf, c) || haslosdark(lf, c)) {
|
|
if ((lf->cell == c) || haslos(lf, c)) {
|
|
setlosdirty(lf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now setlos dirty for lfs who can see any of the cells.
|
|
// while makelit() does this as well, we need to do it
|
|
// again here because makelit also contains calls to "haslos",
|
|
// which clears lf->losdirty again. This is fine when we're only
|
|
// changing one cell's light level, but when changing multiple
|
|
// cells we need to go through again afterwards.
|
|
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
lifeform_t *lf;
|
|
for (lf = c->map->lf ; lf ; lf = lf->next) {
|
|
//OLD: if (haslos(lf, c) || haslosdark(lf, c)) {
|
|
// need to verify lf->cell because it might be NULL if we're
|
|
// swapping places with someone else.
|
|
if (lf->cell && (distfunc(lf->cell,c) <= (radius + MAXVISRANGE))) {
|
|
setlosdirty(lf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void markroomwalls(map_t *m, room_t *r) {
|
|
int x,y;
|
|
cell_t *c,*c2;
|
|
// N edge
|
|
for (x = r->x1+1; x <= r->x2-1; x++) {
|
|
for (y = r->y1; y <= r->y2; y++) {
|
|
c = getcellat(m, x, y);
|
|
if (c->type->solid) {
|
|
c2 = getcellindir(c, DC_S);
|
|
if (c2 && !c2->type->solid) {
|
|
c->isroomwall = D_N;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// W edge
|
|
for (y = r->y1+1; y <= r->y2-1; y++) {
|
|
for (x = r->x2; x >= r->x1; x--) {
|
|
c = getcellat(m, x, y);
|
|
if (c->type->solid) {
|
|
c2 = getcellindir(c, DC_W);
|
|
if (c2 && !c2->type->solid) {
|
|
c->isroomwall = D_E;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// S edge
|
|
for (x = r->x1+1; x <= r->x2-1; x++) {
|
|
for (y = r->y2; y >= r->y1; y--) {
|
|
c = getcellat(m, x, y);
|
|
if (c->type->solid) {
|
|
c2 = getcellindir(c, DC_N);
|
|
if (c2 && !c2->type->solid) {
|
|
c->isroomwall = D_S;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// W edge
|
|
for (y = r->y1+1; y <= r->y2-1; y++) {
|
|
for (x = r->x1; x <= r->x2; x++) {
|
|
c = getcellat(m, x, y);
|
|
if (c->type->solid) {
|
|
c2 = getcellindir(c, DC_E);
|
|
if (c2 && !c2->type->solid) {
|
|
c->isroomwall = D_W;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void mapentereffects(map_t *m) {
|
|
int i;
|
|
cell_t *c;
|
|
//flag_t *f;
|
|
for (i = 0; i < m->w * m->h; i++) {
|
|
vault_t *v;
|
|
c = m->cell[i];
|
|
v = getcellvault(c);
|
|
|
|
// teleport monsters back to their lairs
|
|
if (c->lf && !isplayer(c->lf)) {
|
|
flag_t *f;
|
|
f = lfhasflag(c->lf, F_STAYINROOM);
|
|
if (f && (f->val[0] != NA)) {
|
|
cell_t *where;
|
|
int roomid;
|
|
roomid = f->val[0];
|
|
// find the closest cell of my lair
|
|
where = getclosestroomcell(c->lf, roomid);
|
|
if (where) {
|
|
movelf(c->lf, where);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
// replace people in the Inn
|
|
if (v && streq(v->id, "inn") && c->lf && (c->lf->race->id == R_HUMAN)) {
|
|
lifeform_t *lf;
|
|
killlf(c->lf);
|
|
lf = addmonster(c, R_HUMAN, NULL, B_TRUE, 1, B_FALSE, NULL);
|
|
addflag(lf->flags, F_STAYINROOM, getroomid(c), B_MAYCHASE, NA, NULL);
|
|
}
|
|
*/
|
|
}
|
|
|
|
// monsters on the new level now get a bunch of turns to simulate them moving about when the player wasn't there.
|
|
if (m->lastplayervisit != -1) {
|
|
int nturns;
|
|
|
|
enteringmap = B_TRUE;
|
|
|
|
nturns = (curtime - m->lastplayervisit) / TICK_INTERVAL;
|
|
limit(&nturns, NA, 20);
|
|
//nturns *= countlfs(m);
|
|
for (i = 0; i < nturns; i++) {
|
|
donextturn(m);
|
|
}
|
|
enteringmap = B_FALSE;
|
|
}
|
|
}
|
|
|
|
void moveobtoclearcell(object_t *o) {
|
|
cell_t *c,*startcell;
|
|
startcell = getoblocation(o);
|
|
|
|
c = startcell;
|
|
while ((c == startcell) ||
|
|
!cellwalkable(NULL, c, NULL) ||
|
|
hasobwithflag(c->obpile, F_CLIMBABLE) ||
|
|
hasobwithflag(c->obpile, F_DEEPWATER) ) {
|
|
c = getrandomcell(startcell->map);
|
|
}
|
|
|
|
moveob(o, c->obpile, ALL);
|
|
}
|
|
|
|
int orthdir(int compassdir) {
|
|
switch (compassdir) {
|
|
case DC_N:
|
|
return D_N;
|
|
case DC_S:
|
|
return D_S;
|
|
case DC_E:
|
|
return D_E;
|
|
case DC_W:
|
|
return D_W;
|
|
}
|
|
return D_NONE;
|
|
}
|
|
|
|
enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum SUBJOB *wantsubjob, enum BEHAVIOUR *wantbehaviour) {
|
|
int donesomething;
|
|
char *p,*suff;
|
|
job_t *j;
|
|
char named[BUFLEN];
|
|
char *localname;
|
|
char *namestart;
|
|
behaviour_t *b;
|
|
|
|
// get params
|
|
donesomething = B_TRUE;
|
|
|
|
if (wantbehaviour) *wantbehaviour = BH_NONE;
|
|
|
|
|
|
// take a local copy so we can strip suffixes off it
|
|
if (strstarts(name, "the ")) {
|
|
namestart = name + strlen("the ");
|
|
} else if (strstarts(name, "an ")) {
|
|
namestart = name + strlen("an ");
|
|
} else if (strstarts(name, "a ")) {
|
|
namestart = name + strlen("a ");
|
|
} else {
|
|
namestart = name;
|
|
}
|
|
localname = strdup(namestart);
|
|
|
|
|
|
p = localname;
|
|
while (donesomething) {
|
|
donesomething = B_FALSE;
|
|
if (strstarts(p, "sleeping ")) {
|
|
p += strlen("sleeping ");
|
|
if (wantflags) addflag(wantflags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL);
|
|
donesomething = B_TRUE;
|
|
}
|
|
if (wantbehaviour && (*wantbehaviour != BH_NONE)) {
|
|
} else {
|
|
// try removing prefixes for behaviours
|
|
for (b = firstbehaviour ; b; b = b->next) {
|
|
char lookfor[BUFLEN];
|
|
sprintf(lookfor, "%s ", b->name);
|
|
if (strstarts(p, lookfor)) {
|
|
p += strlen(lookfor);
|
|
if (wantbehaviour) *wantbehaviour = b->id;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// try removing suffixes for "named xxx"
|
|
strcpy(named, "");
|
|
suff = strends(localname, " named ");
|
|
if (suff) {
|
|
char *suffp;
|
|
char *p2;
|
|
// extract the name
|
|
p2 = named;
|
|
suffp = suff + strlen(" named ");
|
|
while (*suffp && (*suffp != ' ')) {
|
|
*p2 = *suffp;
|
|
suffp++;
|
|
p2++;
|
|
}
|
|
*p2 = '\0'; // nul-terminate name
|
|
*suff = '\0'; // strip off suffix from main string
|
|
}
|
|
if (strlen(named)) {
|
|
if (wantflags) addflag(wantflags, F_NAMED, NA, NA, NA, named);
|
|
donesomething = B_TRUE;
|
|
}
|
|
|
|
// try removing suffixes for jobs
|
|
for (j = firstjob ; j ; j = j->next) {
|
|
char *ep;
|
|
char jobname[BUFLEN];
|
|
snprintf(jobname, BUFLEN, " %s", j->name);
|
|
jobname[1] = tolower(jobname[1]);
|
|
ep = strends(localname, jobname);
|
|
if (ep) {
|
|
// got it
|
|
if (wantjob) *wantjob = j->id;
|
|
// now strip the suffix off, starting at the space before it
|
|
*ep = '\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!j) {
|
|
int foundsubjob = B_FALSE;
|
|
// search for subjob names
|
|
char *ep;
|
|
ep = strends(localname, "Skymage");
|
|
if (ep && !foundsubjob) {
|
|
if (wantjob) *wantjob = J_WIZARD;
|
|
if (wantsubjob) *wantsubjob = SJ_AIRMAGE;
|
|
*ep = '\0';
|
|
foundsubjob = B_TRUE;
|
|
}
|
|
ep = strends(localname, "Icemage");
|
|
if (ep && !foundsubjob) {
|
|
if (wantjob) *wantjob = J_WIZARD;
|
|
if (wantsubjob) *wantsubjob = SJ_ICEMAGE;
|
|
*ep = '\0';
|
|
foundsubjob = B_TRUE;
|
|
}
|
|
ep = strends(localname, "Firemage");
|
|
if (ep && !foundsubjob) {
|
|
if (wantjob) *wantjob = J_WIZARD;
|
|
if (wantsubjob) *wantsubjob = SJ_FIREMAGE;
|
|
*ep = '\0';
|
|
foundsubjob = B_TRUE;
|
|
}
|
|
ep = strends(localname, "Necromancer");
|
|
if (ep && !foundsubjob) {
|
|
if (wantjob) *wantjob = J_WIZARD;
|
|
if (wantsubjob) *wantsubjob = SJ_NECROMANCER;
|
|
*ep = '\0';
|
|
foundsubjob = B_TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
free(localname);
|
|
|
|
// now get raceid
|
|
if (streq(p, "random")) {
|
|
return R_RANDOM;
|
|
} else if (strstarts(p, "random ")) {
|
|
race_t *baser,*r;
|
|
// ie. "random xxx_baseraceid_xxx"
|
|
p += strlen("random ");
|
|
baser = findracebyname(p);
|
|
if (baser) {
|
|
enum RACE poss[MAXCANDIDATES];
|
|
int nposs = 0;
|
|
// find all races with this baseid
|
|
for (r = firstrace ; r ; r = r->next) {
|
|
if (r->baseid == baser->id) {
|
|
poss[nposs++] = r->id;
|
|
if (nposs == MAXCANDIDATES) break;
|
|
}
|
|
}
|
|
if (nposs) {
|
|
return poss[rnd(0,nposs-1)];
|
|
}
|
|
}
|
|
} else {
|
|
race_t *r;
|
|
r = findracebyname(p);
|
|
if (r) {
|
|
return r->id;
|
|
}
|
|
}
|
|
return R_NONE;
|
|
}
|
|
|
|
// returns # of dead ends removed.
|
|
int remove_deadends(map_t *m, int howmuch) {
|
|
enum CELLTYPE solidcell;
|
|
int i,n,count = 0;
|
|
|
|
solidcell = getmapsolid(m);
|
|
|
|
for (i = 0; i < howmuch; i++) {
|
|
for (n = 0; n < m->w * m->h; n++) {
|
|
cell_t *c;
|
|
c = m->cell[n];
|
|
if (!c->room && (countcellexits(c, DT_ORTH) == 1)) {
|
|
// erase this cell
|
|
clearcell(c);
|
|
setcelltype(c, solidcell);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
void set_scanned_glyph(int targettype, void *what, char *descappend, char *desc, glyph_t *glyph) {
|
|
if (targettype == TT_MONSTER) {
|
|
if (desc) {
|
|
real_getlfnamea((lifeform_t *)what, desc, NULL, B_NOSHOWALL, B_CURRACE);
|
|
strcat(desc, descappend);
|
|
}
|
|
if (glyph) {
|
|
*glyph = *(getlfglyph((lifeform_t *) what));
|
|
}
|
|
} else if (targettype == TT_OBJECT) {
|
|
object_t *o;
|
|
o = (object_t *)what;
|
|
if (desc) {
|
|
getobname(o, desc, o->amt);
|
|
strcat(desc, descappend);
|
|
}
|
|
if (glyph) {
|
|
*glyph = *(getglyph(o));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void setcellknown(cell_t *cell, int forcelev) {
|
|
enum SKILLLEVEL slev;
|
|
object_t *o;
|
|
if (forcelev > 0) {
|
|
slev = forcelev;
|
|
} else {
|
|
slev = getskill(player, SK_CARTOGRAPHY);
|
|
}
|
|
|
|
cell->known = B_TRUE;
|
|
// default to remembering the cell's glyph, or a wall if there's a secret door there
|
|
o = hassecretdoor(cell->obpile);
|
|
if (o) {
|
|
celltype_t *ct;
|
|
//ct = findcelltype(getcellsolid(cell));
|
|
ct = findcelltype(getmapsolid(cell->map));
|
|
cell->knownglyph = ct->glyph;
|
|
} else {
|
|
cell->knownglyph = cell->type->glyph;
|
|
}
|
|
|
|
// high cartography skill lets us remember certain objects...
|
|
if (slev >= PR_EXPERT) {
|
|
o = gettopobject(cell, B_TRUE);
|
|
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));
|
|
}
|
|
}
|
|
|
|
if (hasflag(player->flags, F_PHOTOMEM)) {
|
|
cell->knowntime = PERMENANT;
|
|
} else if (slev == PR_INEPT) {
|
|
cell->knowntime = getattr(player, A_IQ)*2;
|
|
} else {
|
|
cell->knowntime = PERMENANT;
|
|
}
|
|
//getcellglyph(&(cell->knownglyph), cell, player);
|
|
}
|
|
|
|
void setcellknownradius(cell_t *centre, int forcelev, int radius, int dirtype) {
|
|
cell_t *cell[MAXCANDIDATES];
|
|
int ncells,i;
|
|
getradiuscells(centre, radius, dirtype, B_FALSE, LOF_DONTNEED, B_TRUE, cell, &ncells, B_FALSE);
|
|
for (i = 0; i < ncells; i++) {
|
|
cell_t *c;
|
|
c = cell[i];
|
|
setcellknown(c, forcelev);
|
|
}
|
|
}
|
|
|
|
void setcelltype(cell_t *cell, enum CELLTYPE id) {
|
|
assert(cell);
|
|
cell->type = findcelltype(id);
|
|
assert(cell->type);
|
|
cell->hp = cell->type->hp;
|
|
if (cell->type->solid) {
|
|
assert(!cell->obpile->first);
|
|
}
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
|
|
// digging out of a stomach
|
|
if (cell->map &&
|
|
!cell->map->beingcreated &&
|
|
!cell->type->solid &&
|
|
(cell->map->habitat->id == H_STOMACH)) {
|
|
object_t *o,*exit;
|
|
cell_t *exitcell;
|
|
// find an existing exit to copy
|
|
exitcell = findobinmap(cell->map, OT_STOMACHEXIT);
|
|
exit = hasob(exitcell->obpile, OT_STOMACHEXIT);
|
|
assert(exit);
|
|
// add a new exit
|
|
o = addobfast(cell->obpile, OT_STOMACHEXIT);
|
|
copyflag(o->flags, exit->flags, F_MAPLINK);
|
|
}
|
|
if (haslos(player, cell)) {
|
|
needredraw = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// returns true if something happened
|
|
int shattercell(cell_t *c, lifeform_t *fromlf, char *damstring) {
|
|
lifeform_t *target;
|
|
char targetname[BUFLEN];
|
|
object_t *o, *nexto;
|
|
int rv = B_FALSE;
|
|
|
|
target = c->lf;
|
|
if (target) {
|
|
getlfname(target, targetname);
|
|
}
|
|
// shatter solid glass cells
|
|
if (c->type->solid && willshatter(c->type->material->id)) {
|
|
damagecell(c, 9999, DT_DIRECT);
|
|
}
|
|
|
|
// shatter lifeform-owned objects
|
|
if (target) {
|
|
for (o = target->pack->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (willshatter(o->material->id)) {
|
|
if (shatter(o, B_TRUE, damstring, fromlf)) {
|
|
rv = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// shatter cell objects
|
|
for (o = c->obpile->first ; o ; o = nexto) {
|
|
nexto = o->next;
|
|
if (willshatter(o->material->id)) {
|
|
if (shatter(o, B_TRUE, damstring, fromlf)) {
|
|
rv = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
int unlinkstairsto(map_t *unlinkmap) {
|
|
map_t *m;
|
|
object_t *o;
|
|
cell_t *c;
|
|
long badid[MAXCANDIDATES];
|
|
int x,y,nbadids = 0,nretflags,i;
|
|
int db = B_TRUE;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
|
|
if (db) dblog("starting unlinkstairsto for unlikmap='%s'",unlinkmap->name);
|
|
// get a list of all stair object ids on this unlinkmap.
|
|
for (y = 0; y < unlinkmap->h; y++) {
|
|
for (x = 0; x < unlinkmap->w; x++) {
|
|
c = getcellat(unlinkmap, x,y);
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
if (hasflag(o->flags, F_MAPLINK)) {
|
|
badid[nbadids++] = o->id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (db) dblog(" found %d stairs on unlinkmap.",nbadids);
|
|
|
|
// go through all maps and remove any stairs which link to unlinkmap
|
|
if (db) dblog(" searching other maps for stairs linking to unlinkmap.");
|
|
for (m = firstmap ; m ; m = m->next) {
|
|
for (y = 0; y < m->h; y++) {
|
|
for (x = 0; x < m->w; x++) {
|
|
c = getcellat(m, x,y);
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
getflags(o->flags, retflag, &nretflags, F_MAPLINK, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
int killit = B_FALSE,n;
|
|
if (retflag[i]->val[0] == unlinkmap->id) {
|
|
|
|
if (db) dblog(" on map '%s': found '%s' linking to unlinkmap's id.", m->name,o->type->name);
|
|
killit = B_TRUE;
|
|
} else {
|
|
for (n = 0; n < nbadids; n++) {
|
|
if (atol(retflag[i]->text) == badid[n]) {
|
|
killit = B_TRUE;
|
|
if (db) dblog(" on map '%s': found '%s' linking to obid of stairs on unlinkmap.", m->name,o->type->name);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (killit) {
|
|
killflag(retflag[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// now remove all links from stairs on unlinkmap
|
|
if (db) dblog(" removing maplinks from stairs on unlinkmap.");
|
|
for (y = 0; y < unlinkmap->h; y++) {
|
|
for (x = 0; x < unlinkmap->w; x++) {
|
|
c = getcellat(unlinkmap, x,y);
|
|
for (o = c->obpile->first ; o ; o = o->next) {
|
|
killflagsofid(o->flags, F_MAPLINK);
|
|
}
|
|
}
|
|
}
|
|
if (db) dblog("finished unlinkstairsto for unlikmap='%s'",unlinkmap->name);
|
|
return B_FALSE;
|
|
}
|
|
|
|
|
|
void updateknowncells(void) {
|
|
//int x,y;
|
|
//map_t *map;
|
|
//object_t *wep;
|
|
//int seenundead = B_FALSE;
|
|
int i;
|
|
|
|
// you don't remember cells when you're flying, unless you
|
|
// have a magic map or photographic memory.
|
|
if (isairborne(player) && !lfhasflag(player, F_PHOTOMEM)) {
|
|
return;
|
|
}
|
|
if (lfhasflag(player, F_RAGE)) {
|
|
return;
|
|
}
|
|
for (i = 0; i < player->nlos; i++) {
|
|
setcellknown(player->los[i], B_FALSE);
|
|
}
|
|
}
|
|
|
|
int validateregions(void) {
|
|
regionoutline_t *ro;
|
|
int failed = B_FALSE;
|
|
// check outlines
|
|
for (ro = firstregionoutline ; ro ; ro = ro->next) {
|
|
int i;
|
|
for (i = 0 ; i < ro->nthings; i++) {
|
|
if (validateregionthing(&ro->thing[i])) {
|
|
failed = B_TRUE;
|
|
}
|
|
}
|
|
}
|
|
return failed;
|
|
}
|
|
|
|
int validateregionthing(regionthing_t *thing) {
|
|
cell_t fakecell;
|
|
map_t fakemap;
|
|
int goterrors = B_FALSE;
|
|
object_t *o;
|
|
createfakes(&fakemap, &fakecell);
|
|
switch (thing->whatkind) {
|
|
case RT_HABITAT:
|
|
if (!findhabitat(thing->value)) {
|
|
dblog("Invalid habitat %d specified in regionthing.", thing->value);
|
|
goterrors = B_TRUE;
|
|
}
|
|
break;
|
|
case RT_OBJECT:
|
|
o = addob(fakecell.obpile, thing->what);
|
|
if (!o) {
|
|
dblog("Invalid object '%s' specified in regionthing.", thing->what);
|
|
goterrors = B_TRUE;
|
|
}
|
|
break;
|
|
case RT_REGIONLINK:
|
|
if (!findregiontype(thing->value)) {
|
|
dblog("Invalid regionlink to regiontype %d specified in regionthing.", thing->value);
|
|
goterrors = B_TRUE;
|
|
}
|
|
break;
|
|
case RT_VAULT:
|
|
if (!findvault(thing->what)) {
|
|
dblog("Invalid rt_vault to vaulttype '%s' specified in regionthing.", thing->what);
|
|
goterrors = B_TRUE;
|
|
}
|
|
break;
|
|
case RT_RNDVAULTWITHFLAG:
|
|
if (!findvaultwithflag(thing->value)) {
|
|
dblog("Invalid rt_rndvaultwithflag specified in regionthing.");
|
|
goterrors = B_TRUE;
|
|
}
|
|
break;
|
|
case RT_RNDVAULTWITHTAG:
|
|
if (!findvaultwithtag(thing->what)) {
|
|
dblog("Invalid rt_rndvaultwithtag specified in regionthing.");
|
|
goterrors = B_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
killfakes(&fakemap, &fakecell);
|
|
return goterrors;
|
|
}
|
|
|
|
int wallstoleftright(cell_t *c, int dir) {
|
|
int d1,d2;
|
|
switch (dir) {
|
|
case D_N:
|
|
case D_S:
|
|
d1 = D_E; d2 = D_W; break;
|
|
case D_E:
|
|
case D_W:
|
|
d1 = D_N; d2 = D_S; break;
|
|
default:
|
|
return B_FALSE;
|
|
}
|
|
if (iswallindir(c,d1) && iswallindir(c,d2)) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void writetextonground(lifeform_t *lf, cell_t *c, char *buf, int howlong) {
|
|
char *p;
|
|
if (lf && isblind(lf)) {
|
|
// if blind, garble text somewhat
|
|
for (p = buf ; *p; p++) {
|
|
if (*p != ' ') {
|
|
if (onein(4)) {
|
|
if (onein(2)) {
|
|
*p = rnd('a','z');
|
|
} else {
|
|
*p = rnd('A','Z');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (c->writing) {
|
|
free(c->writing);
|
|
}
|
|
c->writing = strdup(buf);
|
|
c->writinglifetime = howlong;
|
|
|
|
if (haslos(player, c)) {
|
|
if (c == player->cell) {
|
|
msg("Magical writing appears around you!");
|
|
} else {
|
|
msg("Magical writing appears nearby!");
|
|
}
|
|
} else if (c == player->cell) {
|
|
msg("You feel something stirring in the air around you.");
|
|
}
|
|
}
|