#include #include #include #include #include #include #include #include "astar.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 void *rdata; 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 branch_t *firstbranch,*lastbranch; 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; extern condset_t ccwalkable; extern condset_t ccwalkableroom; extern condset_t ccroom; 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); killcell(&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->temperature = m->habitat->temperature; 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 = KG_UNKNOWN; cell->knowntime = 0; cell->knownglyph.ch = ' '; cell->knownglyph.colour = C_GREY; cell->visited = B_FALSE; cell->filled = B_FALSE; cell->isroomwall = D_NONE; cell->reason = NULL; cell->lockedreason = NULL; 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, int stairsinrooms, enum TEMPERATURE temp) { 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; a->stairsinrooms = stairsinrooms; a->temperature = getmidtemp(temp); a->monflags = addflagpile(NULL, NULL); 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) { if ((f->id == F_HOMEOB) && pctchance(f->val[0])) { object_t *o = NULL; cell_t *c; o = addob(homeobloc->obpile, f->text); if (o && (homeobloc == lf->cell) && isimpassableob(o, lf, SZ_ANY)) { c = real_getrandomadjcell(lf->cell, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); if (c) { homeobloc = c; // future obs will go here too. moveob(o, homeobloc->obpile, o->amt); } } 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); } } else if ((f->id == F_HOMELEVOB) && dolevelobs) { int i,amt,range; cell_t *retcell[MAXCANDIDATES]; int nretcells; amt = rnd(f->val[0],f->val[1]); range = f->val[2]; if (range != NA) { getradiuscells(lf->cell, range, DT_COMPASS, B_FALSE, LOF_NEED, B_FALSE, retcell, &nretcells, B_FALSE); } for (i = 0; i < amt; i++) { object_t *o = NULL; cell_t *c; //condset_t cs; if (range == NA) { // ie. anywhere on level // pick new EMPTY random spot. try rooms first. //c = getrandomroomcell(lf->cell->map, ANYROOM, WE_WALKABLE); c = getcell_cond(lf->cell->map, &ccwalkableroom); 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); } } else { // within 'range' cells c = retcell[rnd(0,nretcells - 1)]; 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 allowextras, int *nadded) { lifeform_t *lf = NULL; race_t *r; int db = B_FALSE; flagpile_t *wantflags = NULL; enum JOB wantjob = J_NONE; enum BEHAVIOUR wantbehaviour = BH_NONE; enum ERROR why; if (nadded) *nadded = 0; /*if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); */ // ie. don't create mosnters on closed doors! if (!cellwalkable(NULL, c, &why)) { object_t *o; o = (object_t *)rdata; if ((why == E_OBINWAY) && o && hasflag(o->flags, F_ISMONSTER)) { } else { 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, NULL); } else { r = findrace(rid); } } else { condset_t cs; //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging rid = parserace(racename, wantflags, &cs, &wantjob, &wantbehaviour); //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging if (rid == R_RANDOM) { r = getrandomrace(c, NA, &cs); } 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); dblog(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) dblog("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) dblog("finished lf addition"); //if gamemode == GM_GAMESTARTED checkallflags(player->cell->map); // debugging if (lf) { flag_t *f; if (nadded) (*nadded)++; if (db) dblog("checking for job"); lf->born = B_FALSE; // special case for vaults if (c->map->habitat->id == H_MASTERVAULTS) { if (hasflagval(lf->flags, F_STARTJOB, NA, J_GUARD, NA, NULL)) { wantjob = J_GUARD; } } 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 (rnd(1,100) <= f->val[0]) { //job_t *j; if (f->val[1] == J_RANDOM) { job_t *j; j = getrandomjob(B_TRUE); wantjob = j->id; } else { wantjob = f->val[1]; } givejob(lf, wantjob); break; } } } } else { givejob(lf, wantjob); } 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) dblog("handling groups"); if (autogen && allowextras) { 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, &ccwalkable, 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) dblog("handling minions"); if (autogen && allowextras) { 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; condset_t cs; adjcell = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_WALLSTOP, NULL, NULL); if (!adjcell) break; newrid = parserace(f->text, NULL, &cs, NULL, NULL); newr = findrace(newrid); if (!newr) break; newlf = addlf(adjcell, newr->id, getrandommonlevel(newr, adjcell->map)); if (!newlf) break; if (nadded) (*nadded)++; newlf->born = B_FALSE; // 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) dblog("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) dblog("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) dblog("finished addmonster"); return lf; } object_t *addrandomob(cell_t *c) { char buf[BUFLEN]; int db = B_FALSE; condset_t cs; object_t *o = NULL; if (c->type->solid) { return NULL; } initcond(&cs); if (real_getrandomob(c, buf, NA, c->habitat->id, RR_NONE, B_FALSE, &cs)) { 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, B_ALLOWEXTRA, nadded); if (lf) { rv = TT_MONSTER; if (c->habitat) { // ensure it has proper resistances copyflags(lf->flags, c->habitat->monflags, NA); } } //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; // set visrange based on light level maxrange = MAXVISRANGE; switch (m->illumination) { case IL_FULLLIT: break; case IL_WELLLIT: maxrange -= 3; break; case IL_DIM: maxrange -= 5; break; case IL_SHADOWY: maxrange -= 7; break; default: // ie. fulldark maxrange = 1; break; } // 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); } } 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 // if 'includefixed' is passed then walls in vaults with maintainedge ARE included. 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 includefixed) { 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 (!includefixed && cellisfixedvaultwall(c)) continue; 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 (!includefixed && cellisfixedvaultwall(c)) continue; 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 (!includefixed && cellisfixedvaultwall(c)) continue; 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 (!includefixed && cellisfixedvaultwall(c)) continue; if (!onlywantsolid || c->type->solid) { retcell[*ncells] = getcellat(map, x, y); (*ncells)++; } } } } // if outlineid is -1, it's automatically assigned region_t *addregion(enum BRANCH rtype, region_t *parent, int outlineid, int depthmod, int createdby) { region_t *a; regionoutline_t *ro,*poss[MAXCANDIDATES]; int nposs = 0; int id; branch_t *rt; rt = findbranch(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 = findbranch(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 BRANCH 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 = findbranch(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; } branch_t *addbranch(enum BRANCH id, char *name, int pluralname, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major, int depthmod, int addparentdepth) { branch_t *a; // add to the end of the list if (firstbranch == NULL) { firstbranch = malloc(sizeof(branch_t)); a = firstbranch; a->prev = NULL; } else { // go to end of list a = lastbranch; a->next = malloc(sizeof(branch_t)); a->next->prev = a; a = a->next; } lastbranch = 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; a->addparentdepth = addparentdepth; return a; } void adjustcellglyph(cell_t *c, glyph_t *g, enum CELLADJUSTTYPE how) { if (how == CA_NONE) return; if (g->ch == ' ') return; if ((how == CA_COL) || (how == CA_BOTH)) { if (c->type->altcol != C_NONE) { if ((c->x + c->y) % 2) g->colour = c->type->altcol; } } if ((how == CA_CH) || (how == CA_BOTH)) { // for certain cell types, select glyph based on surrounding cells of same type if (g->ch == UNI_DYNAMIC) { int adj = 0,i,ndirslinked = 0,n; cell_t *c2; for (n = 0; ((ndirslinked == 0) && (n < 2)); n++) { for (i = D_N; i <= D_W; i++) { int this = 0; int typematches = B_FALSE; c2 = getcellindir(c,i); if (c2) { if (n == 0) { // first pass if (c2->type->id == c->type->id) { typematches = B_TRUE; } } else { // second pass if (issolid(c2)) { typematches = B_TRUE; } } // TODO: if it's a secret door, pretend it's a wall // if you don't know it. //if (c2->known && (typematches || hasdoor(c2)) ) { if (typematches || hasdoor(c2)) { this = 1; ndirslinked++; // we want: // N E S W // 8 4 2 1 if (i == D_N) this <<= 3; else if (i == D_E) this <<= 2; else if (i == D_S) this <<= 1; } } adj |= this; } } switch (adj) { case 1: // left case 4: // right case 5: // horz g->ch = 0x2500; break; case 2: // down case 10: // vert case 8: // up g->ch = 0x2502; break; case 3: // down left g->ch = 0x2510; break; case 6: // down right g->ch = 0x250c; break; case 7: // T down g->ch = 0x252c; break; case 9: // up left g->ch = 0x2518; break; case 11: // T left g->ch = 0x2524; break; case 12: // up right g->ch = 0x2514; break; case 13: // T up g->ch = 0x2534; break; case 14: // T right g->ch = 0x251c; break; case 15: // cross g->ch = 0x253c; break; default: if (c->known) { g->ch = c->knownglyph.ch; } else { g->ch = 0x2500; } break; } } } /* 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: default: 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_FALSE; 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, B_FALSE); 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 ndoors,maxdoors,nposs = 0; int sel; // for (d = D_N; d <= D_W; d++) { used[d] = B_FALSE; } // no possible door locations - add a random number maxdoors = rnd(1,4); ndoors = 0; for (i = 0; i < maxdoors; 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[ndoors++] = poss[sel]; } // actually make the doors for (i = 0; i < ndoors; i++) { int sel; d = dodoor[i]; getroomedge(map, roomid, minx, miny, maxx, maxy, d, cell, &ncells, B_TRUE, B_FALSE); 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, char *why, ...) { celltype_t *origtype; int roomwall; char buf[BUFLEN]; va_list args; if (why) { va_start(args, why); vsnprintf( buf, BUFLEN, why, args ); va_end(args); } origtype = c->type; roomwall = isroom(c); setcelltype(c, getcellempty(c)); if (why) { setcellreason(c, "%s", buf); } if (origtype->solid && roomwall && onein(10)) { switch (origtype->material->id) { case MT_STONE: addob(c->obpile, "1-10 stones"); break; case MT_BRICK: addob(c->obpile, "1-5 bricks"); break; case MT_GLASS: addob(c->obpile, "1-10 pieces of broken glass"); break; case MT_WOOD: addob(c->obpile, "1-10 shards of wood"); 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) { object_t *oo; // 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 oo = hasobwithflag(cell->obpile, F_BLOCKSVIEW); if (oo) { if (cell == c1) { flag_t *f; f = hasflag(oo->flags, F_BLOCKSVIEW); if (f && (f->val[1] == B_TRUE)) { // ok. } else { return B_FALSE; } } else { 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) && (dir != D_NONE) && (c->isroomwall != D_NONE) && (c->isroomwall != diropposite(dir))) { // different room and hits wrong wall. } else if (cellisfixedvaultwall(c) && (!streq(c->lockedreason, TEMPVAULTLOCK))) { } 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; } */ int cellmeetscondition(cell_t *c, enum CELLCONDITION cond, int arg, int value) { int ok = B_FALSE,vok = B_FALSE; object_t *o; lifeform_t *lf; assert(c); switch (cond) { case CC_CELLTYPE: if (value && (c->type->id == arg)) { ok = B_TRUE; } else if (!value && (c->type->id != arg)) { ok = B_TRUE; } break; case CC_DANGEROUSFOR: lf = findlf(NULL, arg); if (value == B_TRUE) { if (celldangerous(lf, c, B_FALSE, NULL)) { ok = B_TRUE; } } else { if (!celldangerous(lf, c, B_FALSE, NULL)) { ok = B_TRUE; } } break; case CC_EMPTY: if (value && isempty(c)) { ok = B_TRUE; } else if (!value && !isempty(c)) { ok = B_TRUE; } break; case CC_OKFORPORTAL: if (value == B_TRUE) { if (cellwalkable(NULL, c, NULL) && !hasenterableobject(c) && !hasobwithflag(c->obpile, F_DOOR)) { ok = B_TRUE; } } else { if (!cellwalkable(NULL, c, NULL) || hasenterableobject(c) || hasobwithflag(c->obpile, F_DOOR)) { ok = B_TRUE; } } break; case CC_OKFORSTAIRS: if (getcellvault(c) && hasflag(c->room->vault->flags, F_NOSTAIRS)) { vok = B_FALSE; } else { vok = B_TRUE; } if (value == B_TRUE) { if (vok && cellwalkable(NULL, c, NULL) && !hasenterableobject(c) && !hasobwithflag(c->obpile, F_DOOR) && !hasobwithflag(c->obpile, F_DEEPWATER) ) { ok = B_TRUE; } } else { if (!vok || !cellwalkable(NULL, c, NULL) || hasenterableobject(c) || hasobwithflag(c->obpile, F_DOOR) || hasobwithflag(c->obpile, F_DEEPWATER)) { ok = B_TRUE; } } break; case CC_HASLF: if (value && c->lf) { ok = B_TRUE; } else if (!value && !c->lf) { ok = B_TRUE; } break; case CC_HASMATERIAL: if (value && (c->type->material->id == arg)) { ok = B_TRUE; } else if (!value && (c->type->material->id != arg)) { ok = B_TRUE; } break; case CC_HASOBTYPE: o = hasob(c->obpile, arg); if ((o && value) || (!o && !value)) { ok = B_TRUE; } break; case CC_HASROOMID: if (value && isroom(c) && (getroomid(c) == arg)) { ok = B_TRUE; } else if (!value && (!isroom(c) || (getroomid(c) != arg)) ) { ok = B_TRUE; } break; case CC_IMPASSABLE: if (value) { if (c->type->solid || hasobwithflag(c->obpile, F_IMPASSABLE)) { ok = B_TRUE; } } else { if (!c->type->solid && !hasobwithflag(c->obpile, F_IMPASSABLE)) { ok = B_TRUE; } } break; case CC_ISROOM: if (value) { if (isroom(c)) ok = B_TRUE; } else { if (!isroom(c)) ok = B_TRUE; } break; case CC_SOLID: if (value) { if (issolid(c)) ok = B_TRUE; } else { if (!issolid(c)) ok = B_TRUE; } break; case CC_WALKABLE: if (value == B_TRUE) { if (cellwalkable(NULL, c, NULL)) { ok = B_TRUE; } } else { if (!cellwalkable(NULL, c, NULL)) ok = B_TRUE; } break; case CC_WALKABLEFOR: lf = findlf(NULL, arg); if (value == B_TRUE) { if (cellwalkable(lf, c, NULL)) { ok = B_TRUE; } } else { if (!cellwalkable(lf, c, NULL)) ok = B_TRUE; } break; case CC_NONE: ok = B_TRUE; break; default: break; } return ok; } int cellmeets(cell_t *c, condset_t *cs) { int i; if (!cs) return B_TRUE; for (i = 0; i < cs->nconds; i++ ){ if (!cellmeetscondition(c, cs->cond[i], cs->arg[i], cs->val[i])) { return B_FALSE; } } return B_TRUE; } // returns B_TRUE, B_FALSE or B_MAYBE int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom, char *why) { int db = B_FALSE,d; cell_t *c2; if (why) strcpy(why, "ok."); if (c->locked) { //db = B_TRUE; // locked cell. invalied. if (insameroom) *insameroom = B_FALSE; if (db) dblog(" going %s hits locked cell(%s). invalid.", getdirname(dir),c->lockedreason); if (why) sprintf(why, " going %s hits locked cell(%s). invalid.", getdirname(dir),c->lockedreason); 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)); if (why) sprintf(why," going %s hits wall of same room. invalid.", getdirname(dir)); return B_FALSE; } else if (isroom(c) && (getroomid(c) != srcroomid) && (c->isroomwall != D_NONE) && (dir != D_NONE) && (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)); if (why) sprintf(why," going %s hits wrong wall (%s) of different room. invalid.", getdirname(dir), getdirname(c->isroomwall)); return B_FALSE; } else if (cellisfixedvaultwall(c) && !streq(c->lockedreason, TEMPVAULTLOCK)) { // 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)); if (why) sprintf(why," 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)); if (why) sprintf(why," 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)); if (why) sprintf(why," 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. if (why) sprintf(why,"VALID"); return B_TRUE; } } } // adjacent to wall of current room? for (d = D_N; d <= D_W; d++) { c2 = getcellindir(c, d); if (c2 && !cellwalkable(NULL, c2, NULL) && (getroomid(c2) == srcroomid) && !isadjacent(c2, c)) { if (insameroom) *insameroom = B_TRUE; if (db) dblog(" going %s ends up adjacent to wall of start room. invalid.", getdirname(dir)); if (why) sprintf(why," going %s ends up adjacent to wall of start room. invalid.", getdirname(dir)); return B_FALSE; } } if (why) sprintf(why,"maybe..."); return B_MAYBE; } enum CELLTYPE celltypefromvault(cell_t *c) { vault_t *v; enum FLAG lookfor; v = getcellvault(c); if (v) { flag_t *f; if (issolid(c)) { lookfor = F_CELLTYPESOLID; } else { lookfor = F_CELLTYPEEMPTY; } f = hasflag(v->flags, F_CELLTYPESOLID); if (f) return B_TRUE; } return B_FALSE; } // clear and deallocate cell strings void clearcellstrings(cell_t *c) { if (c->writing) { free(c->writing); c->writing = NULL; } if (c->reason) { free(c->reason); c->reason = NULL; } if (c->lockedreason) { free(c->lockedreason); c->lockedreason = NULL; } if (gamemode == GM_GAMESTARTED) { c->known = KG_UNKNOWN; c->knownglyph.ch = ' '; c->knownglyph.colour = C_GREY; } } // kill everything in the given cell (lifeforms && objects) // but DONT remove the cell itself. void clearcell(cell_t *c) { if (c->lf && !isplayer(c->lf)) { killlf(c->lf); } while (c->obpile->first) { killob(c->obpile->first); } clearcellstrings(c); } 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 = KG_UNKNOWN; c->knownglyph.ch = ' '; c->knownglyph.colour = C_GREY; } } // dungeon delving code from http://roguebasin.roguelikedevelopment.org/index.php/Delving_a_connected_cavern // returns # of 'empty' cells created int delve(map_t *map, int neighbourmin, int neighbourmax, int connchance, int chancepct, int newneighbourmin, int newneighbourmax, int newconnchance, enum CELLTYPE empty, enum CELLTYPE solid) { cellstore_t *cs; cell_t *c,*startcell; int cellsdone = 0; int wantcells; int x,y; int ngroups,ncount; int changed = B_FALSE; // calculate how many cells to fill based on parameters /* totcells = map->w * map->h; minmax = neighbourmin + neighbourmax; if (minmax <= 2) { div = 8; } else if (minmax == 3) { div = 7; } else if (minmax == 4) { div = 6; } else if (minmax <= 6) { div = 5; } else if (minmax <= 9) { div = 4; } else if (minmax <= 11) { div = 3; } else { div = 2; } wantcells = totcells / div; */ wantcells = pctof(33,(map->w * map->h)); // start with all walls for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x, y); if (c) { setcelltype(c, solid); } } } // allocate datastore cs = delve_makecellstore(map->w * map->h); // select initial random spot startcell = getcellat(map, map->w/2, map->h/2); dblog("delve: initial cell is %d,%d.", startcell->x, startcell->y); // add initial spot to the store delve_storecell(cs, startcell); dblog("delve: initial cellstore has %d cells.", cs->ncells); // dig the initial 'seed' cells, and place their neighbours in the store. while ( (cellsdone < (2*neighbourmin)) && (cellsdone < wantcells)) { if (!delve_rndpull(cs, &c)) { dblog("delve: rndpull failed during seeding!"); break; } ncount = countadjcellsoftype(c, empty, DT_COMPASS); ngroups = delve_countadjgroups(c, empty); // for the moment, stay close to the origin if (c->type->solid && (getcelldist(c,startcell) <= 2) && (ncount <= neighbourmax) && ((ngroups <= 1) || (pctchance(connchance)) ) ) { int ndone; dblog("delve: trying to seed %d,%d...", c->x, c->y); ndone = delve_digcell(cs, c, empty, solid); if (ndone) { dblog("delve: success! cellstore now has %d cells.", cs->ncells); } else { dblog("delve: failed."); } cellsdone += ndone; } } dblog("delve: seeded %d cells.", cellsdone); // Main delving loop: // // while the wanted FLOOR count isn't reached, and drawing a cell succeedes: // if the drawn cell is within map limits, and // it is a WALL cell, and // it has from ngb_min to ngb_max of FLOOR neighbours, and // making it FLOOR either won't open new connections, or a random // chance (connchance percent) allows opening a new connection, // then the cell is made FLOOR, and its WALL neighbours are put in // store in a random order. dblog("delve: commencing main delve."); while (cellsdone < wantcells) { int xx,yy; cell_t *c2; char line[BUFLEN]; if (!delve_rndpull(cs, &c)) { dblog("delve: rndpull failed during delving!"); break; } ncount = countadjcellsoftype(c, empty, DT_COMPASS); ngroups = delve_countadjgroups(c, empty); dblog("delve: pulled %d,%d (ncount=%d, groupcount=%d):", c->x, c->y, ncount, ngroups); strcpy(line, ""); for (yy = c->y - 1; yy <= c->y + 1; yy++) { for (xx = c->x - 1; xx <= c->x + 1; xx++) { c2 = getcellat(map, xx, yy); if (c2) { if (c2->type->solid) { strcat(line, "#"); } else { strcat(line, "."); } } else { strcat(line, "#"); } } dblog("%s",line); strcpy(line, ""); } dblog("%s",line); strcpy(line, ""); if (c->type->solid && (ncount >= neighbourmin) && (ncount <= neighbourmax) && ((ngroups <= 1) || pctchance(connchance)) ) { int ndone; dblog("delve: trying to delve %d,%d...", c->x, c->y); ndone = delve_digcell(cs, c, empty, solid); cellsdone += ndone; if (ndone) { dblog("delve: success! cellstore now has %d cells. dugcells=%d.", cs->ncells, cellsdone); } else { dblog("delve: failed."); } } // after 50% of cells, chance params. if ((chancepct >= 0) && !changed && (cellsdone >= pctof(chancepct, wantcells) ) ) { changed = B_TRUE; if (newneighbourmin != NA) neighbourmin = newneighbourmin; if (newneighbourmax != NA) neighbourmax = newneighbourmax; if (newconnchance != NA) connchance = newconnchance; } } // clean up delve_killcellstore(cs); dblog("DELVE DEBUG:"); dblog(" PATTERN: ngb_min=%d, ngb_max=%d, connchance=%d", neighbourmin, neighbourmax, connchance); dblog(" wanted cells: %d", wantcells); dblog(" dug cells: %d", cellsdone); dblog("generated map:"); dumpmap(map, B_FALSE, NULL); dblog("END DELVE DEBUG:"); return cellsdone; } // count how many 'groups' of celltype ct surround c. int delve_countadjgroups(cell_t *centre, enum CELLTYPE ct) { const int grouptable[256] = { /********** 0 1 2 3 4 5 6 7 8 9 */ /* 000 */ 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* 010 */ 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, /* 020 */ 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, /* 030 */ 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, /* 040 */ 2, 3, 3, 3, 2, 2, 2, 2, 1, 2, /* 050 */ 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, /* 060 */ 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, /* 070 */ 2, 1, 2, 2, 3, 2, 2, 1, 2, 1, /* 080 */ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, /* 090 */ 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, /* 000 */ 2, 1, 2, 1, 2, 2, 3, 2, 2, 1, /* 110 */ 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, /* 120 */ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, /* 130 */ 2, 1, 2, 1, 2, 1, 2, 2, 3, 2, /* 140 */ 2, 1, 2, 1, 2, 2, 3, 2, 2, 1, /* 150 */ 2, 1, 2, 2, 3, 2, 2, 1, 2, 1, /* 160 */ 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, /* 170 */ 4, 3, 3, 2, 3, 2, 2, 2, 3, 2, /* 180 */ 2, 1, 2, 1, 2, 2, 3, 2, 2, 1, /* 190 */ 2, 1, 1, 1, 2, 1, 2, 1, 2, 1, /* 200 */ 2, 2, 3, 2, 2, 1, 2, 1, 1, 1, /* 210 */ 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, /* 220 */ 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, /* 230 */ 2, 1, 2, 2, 3, 2, 2, 1, 2, 1, /* 240 */ 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, /* 250 */ 2, 1, 1, 1, 1, 1 }; int d; int bitmap = 0; cell_t *c; for (d = DC_N; d <= DC_NW; d++) { bitmap >>= 1; c = getcellindir(centre, d); if (c && (c->type->id == ct)) { bitmap |= 0x80; } } return grouptable[bitmap]; /* int enddir = DC_NW; int groups = 0, ingroup = B_FALSE; cell_t *c; for (d = DC_N; d <= enddir; d++) { c = getcellindir(centre, d); if (c && (c->type->id == ct)) { // special case if first cell checked (ie. N) // matches - go backwards and see where this group // started. if (d == DC_N) { int dd,foundend = B_FALSE; for (dd = DC_NW; dd >= DC_NE; dd--){ cell_t *c2; c2 = getcellindir(centre, dd); // doesn't match? stop here. if (!c2 || (c2->type->id != ct)) { enddir = dd; foundend = B_TRUE; break; } } if (!foundend) { // ie. completely surrounded by // correct type. this means 1 group. return 1; } } if (!ingroup) { groups++; ingroup = B_TRUE; } else { // still in a group... } } else { // not in a group anymore ingroup = B_FALSE; } } return groups; */ } int delve_cuberoot(int num) { int rv,delta; if (!num) return 0; if (num < 1000) rv = 10; else if (num < 1000000) rv = 100; else if (num < 1000000000) rv = 1000; else rv = 1290; do { delta = (num - (rv * rv * rv)) / (2 * rv * rv); rv += delta; } while (delta); if ((rv * rv * rv) > num) { rv--; } return rv; } // fills 'centre' with celltype 'empty', and stores any neighboughts with celltype 'solid' // into the cellstore (in random order). // // returns the number of cells dug (ie. 1 or 0) int delve_digcell(cellstore_t *cs, cell_t *centre, enum CELLTYPE empty, enum CELLTYPE solid) { int dir[8] = { DC_N, DC_NE, DC_E, DC_SE, DC_S, DC_SW, DC_W, DC_NW}; int i; if (!centre || !centre->type->solid) { return 0; } setcelltype(centre, empty); // shuffle directions for (i = 0 ; i < 8; i++) { int newpos; int temp; newpos = rnd(0,7); temp = dir[newpos]; dir[newpos] = dir[i]; dir[i] = temp; } for (i = 0; i < 8; i++) { cell_t *c; c = getcellindir(centre, dir[i]); if (c && c->type->solid) { delve_storecell(cs, c); } } return 1; } cellstore_t *delve_makecellstore(int maxsize) { cellstore_t *cs; cs = malloc(sizeof(cellstore_t)); cs->max = maxsize; cs->ncells = 0; cs->c = malloc(maxsize * sizeof(cell_t)); cs->cutoff = (pctof(2, maxsize)); return cs; } void delve_killcellstore(cellstore_t *cs) { free(cs->c); cs->c = NULL; free(cs); } // pulls a random cell from the datastore // if the datastore has < cs->cutoff cells, return a random cell. // otherwise, return a random cell from within the bottom cells. // if it isn't the topmost cell which is pulled, then move the topmost one to // its place. cell_t *delve_rndpull(cellstore_t *cs, cell_t **c) { int index,min,max; *c = NULL; if (cs->ncells <= 0) return NULL; // *** "fluffy" patterns if (cs->ncells < cs->cutoff) { min = 0; max = cs->ncells -1; } else { //int cuberoot; //cuberoot = delve_cuberoot(cs->ncells); //min = cs->ncells - (5*cuberoot) - 1; //min = pctof(75, cs->ncells); //max = cs->ncells - 1; min = 0; max = pctof(30, cs->ncells); } // compact patterns /* min = 0; max = cs->ncells - 1; */ index = rnd(min,max); *c = cs->c[index]; // not the topmost? topmost moves to here. if (index != (cs->ncells-1)) { cs->c[index] = cs->c[cs->ncells-1]; } cs->ncells -= 1; return *c; } // stores cell position 'c' into the store. // if there's no more space, replace a random cell. void delve_storecell(cellstore_t *cs, cell_t *c) { if (cs->ncells < cs->max) { cs->c[cs->ncells] = c; (cs->ncells)++; } else { cs->c[rnd(0,cs->ncells-1)] = c; } } int damagecell(cell_t *c, int amt, enum DAMTYPE damtype, lifeform_t *fromlf) { 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; enum CELLTYPE origcid; c->hp = 0; // remember cell properties sprintf(cellname, "%s %s", needan(c->type->name) ? "An" : "A", c->type->name); cellmat = c->type->material->id; origcid = c->type->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; } } if (fromlf && (origcid == CT_WALLTREE) && (c->map->region->rtype->id == BH_WOODS)) { magicwoods_angry(fromlf); } } else { if (fromlf && (c->type->id == CT_WALLTREE) && (c->map->region->rtype->id == BH_WOODS)) { magicwoods_warn(fromlf); } } return B_FALSE; } // returns true if something happened int doelementspread(cell_t *c) { //int db = B_FALSE; object_t *fireob = NULL; if (!c || c->type->solid) { return B_FALSE; } // calculate depth of this cell /* DISABLED FOR NOW. if (!hascloseddoor(c)) { float thisdepth; int i; int nsurround = 0; cell_t *surroundcell[8]; 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]; flag_t *damflag; int nretcells,i,nspread = 0; object_t *oo,*nextoo; damflag = hasflagval(fireob->flags, F_WALKDAM, DT_FIRE, NA, NA, NULL); // 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; object_t *hasfire; hasfire = hasobofmaterial(retcell[i]->obpile, MT_FIRE); for (oo = retcell[i]->obpile->first ; oo ; oo = nextoo) { flag_t *f; nextoo = oo->next; // there's already a fire here. // f_onfire flags won't expire if there is fire there. if (hasfire) { 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; } } } else if (issolid(retcell[i]) && damflag) { int dam; dam = (roll(damflag->text) / 2); if (dam) { // cell takes fire damage damagecell(retcell[i], dam, DT_FIRE, NULL); } } } } return B_FALSE; } // returns # cells filled in int dodoorfill(cell_t *c) { int dir,nfilled = 0,db = B_FALSE; cell_t *firstadj = NULL; int firstdir = D_NONE; // fill in the door cell itself c->filled = B_TRUE; // empty all surrounding cells for (dir = DC_N; dir <= DC_NW; dir++) { cell_t *c2; c2 = getcellindir(c, dir); if (c2) { c2->filled = B_FALSE; if (!firstadj && !issolid(c2)) { firstadj = c2; firstdir = dir; } } } if (db) dblog("first dir was %s\n",getdirname(firstdir)); if (!firstadj) return 0; // floodfill from an adacent cell doorfill_r(c, firstadj, &nfilled); // debug... if (db) { int order[9] = { DC_NW, DC_N, DC_NE, DC_W, D_NONE, DC_E, DC_SW, DC_S, DC_SE }; int i; char buf[BUFLEN]; cell_t *c2; strcpy(buf, ""); for (i = 0; i < 9; i++) { dir = order[i]; if (dir == D_NONE) { strcat(buf, "+"); } else { c2 = getcellindir(c, dir); if (c2) { if (c2->filled) { strcat(buf, "F"); } else { if (issolid(c2)) { strcat(buf, "#"); } else { strcat(buf, "."); } } } else { strcat(buf, "x"); } } switch (dir) { case DC_NE: case DC_E: case DC_SE: dblog("%s", buf); strcpy(buf, ""); break; } } } return nfilled; } void doorfill_r(cell_t *startcell, cell_t *c, int *nfilled) { int d; if (c && // not off the map !c->type->solid && // empty cell !c->filled && // not already filled !hasdoor(c) && // not a door (getcelldist(startcell,c) == 1) // adjacent to first cell ) { if (nfilled) (*nfilled)++; c->filled = B_TRUE; } else { return; } for (d = DC_N; d <= DC_NW; d++) { doorfill_r(startcell, getcellindir(c, d), nfilled); // recursive call } } int fix_reachability(map_t *m) { int i,nfixed = 0; int db = B_TRUE; int donesomething; cell_t *unreachcell[MAX_MAPW*MAX_MAPH]; cell_t *reachcell[MAX_MAPW*MAX_MAPH]; 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; donesomething = B_TRUE; while (donesomething) { int nunreach = 0,nreach = 0; donesomething = 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? nunreach = 0; nreach = 0; for (i = 0; i < m->w * m->h; i++) { if (!m->cell[i]->type->solid && !m->cell[i]->filled && ((m->cell[i]->room && m->cell[i]->room->prevault) || !m->cell[i]->locked ) ) { vault_t *v; // cell is unreachable v = getcellvault(m->cell[i]); if (v && hasflag(v->flags, F_VAULTNOLINK)) { // don't need to link it. } else if (!countcellexits(c, DT_COMPASS) && !hasobwithflag(c->obpile, F_STAIRS)) { // completely surrounded by walls. clearcell(c); setcelltype(c,getmapsolid(m)); } else { unreachcell[nunreach++] = m->cell[i]; } } else if (!m->cell[i]->type->solid) { // cell is ok - reachable. reachcell[nreach++] = m->cell[i]; } } // try to fix unreachable areas. if (nunreach) { int nadded = 0,nportals = 0, ndoors = 0; cell_t *ucell; ucell = unreachcell[0]; if (db) dblog(" attempting to fix unreachable area at %d,%d.", ucell->x, ucell->y); donesomething = B_TRUE; // first: try to link up the two areas. ndoors = fix_unreach_via_doors(m); if (!ndoors) { int idx,firstidx = -1,ntries = 0; //,maxtries = 5; // pick one of the unreachable cells idx = rnd(0,nunreach-1); while (idx != firstidx) { if (firstidx == -1) firstidx = idx; ucell = unreachcell[idx]; // try to link it back to a filled cell. //if (db) dblog(" looking for fixes at %d,%d...", ucell->x, ucell->y); if (!linkexit(ucell, B_TRUE, &nadded)) { // fixed. if (db) dblog(" successfully fixed by digging tunnels."); break; } else { //if (db) dblog("failed, trying new cell."); ntries++; // loop around to start if (++idx == nunreach) idx = 0; } } } /* if (!ndoors) { cell_t *rcell; int ptries = 0,maxtries = 5; // failed! try to link via portals. if (db) dblog(" couldn't link via tunnels. trying to link via portals."); // select random REACHABLE cell while (ptries < maxtries) { rcell = reachcell[rnd(0,nreach-1)]; if (!createportallink(ucell,rcell, OT_PORTAL)) { if (db) dblog(" successfully fixed by adding portals."); nportals++; break; } else { ptries++; } } } */ if (ndoors || nadded || nportals) { if (db) dblog(" fixed unreachable area by adding %d doors, %d cells, %d portals.", ndoors, nadded,nportals); } else { // didn't add anything - fail! if (db) dblog(" fix_reachability failed."); //raise(SIGINT); return B_TRUE; } // change floor/wall type in the unreachable area, for variety if (m->habitat->id == H_DUNGEON) { enum CELLTYPE cursol, curemp, newsol,newemp; // get current wall type cursol = getmapsolid(m); curemp = getmapempty(m); newsol = cursol; newemp = curemp; // select new wall/floor types selectcelltypes(m,100, 100, &newsol,&newemp); // change all solid walls adjacent to the unreachable area to this type for (i = 0; i < nunreach; i++) { int dir; // note: we know that all unreachcell[] entries will // be non-solid if (!celltypefromvault(unreachcell[i]) && (unreachcell[i]->type->id == curemp) ) { // chance floor style setcelltype(unreachcell[i], newemp); } // check for surrounding walls for (dir = DC_N; dir <= DC_NW; dir++) { cell_t *c2; c2 = getcellindir(unreachcell[i], dir); if (c2 && issolid(c2) && !celltypefromvault(c2) && !cellisfixedvaultwall(c2)) { if (c2->type->id == cursol) { // chance wall style setcelltype(c2, newsol); } } } } } // now run the test again. // 'c' will be where the next flood will will happen. c = ucell; nfixed++; } } // end while donesomething if (nfixed) { if (db) dblog(" fix_reachability complete. found and fixed %d unreachable areas.", nfixed); } else { if (db) dblog(" fix_reachability complete. no unreachable areas found."); } return B_FALSE; } // returns # doors added int fix_unreach_via_doors(map_t *m) { int i,dir; cell_t *c,*adjcell; for (i = 0; i < m->w*m->h; i++) { c = m->cell[i]; if (issolid(c) && !cellisfixedvaultwall(c)) { int gotreach = B_FALSE,gotunreach = B_FALSE; // is there an adjacent reachable cell? for (dir = DC_N; dir <= DC_NW; dir++) { adjcell = getcellindir(c, dir); if (adjcell && !issolid(adjcell) && adjcell->filled) { gotreach = B_TRUE; break; } } // is there an adjacent unreachable cell? for (dir = DC_N; dir <= DC_NW; dir++) { adjcell = getcellindir(c, dir); if (adjcell && !issolid(adjcell) && !adjcell->filled) { gotunreach = B_TRUE; break; } } if (gotreach && gotunreach) { // this cell links reachable and unreachable areas. setcelltype(c, getmapempty(m)); makedoor(c, 0); setcellreason(c, "making door to link seperated areas"); dblog(" successfully fixed by creating a door at %d,%d.",c->x, c->y); return 1; } } } return 0; } void floodfill(cell_t *startcell) { int d; object_t *o; if (startcell && // not off the map (!startcell->type->solid || (startcell->type->id == CT_WALLGLASS)) && // 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 } // follow portals o = hasob(startcell->obpile, OT_PORTAL); if (o) { flag_t *f; // not using getstairdest cause we don't want to generate // new levels due to unlinked portals. f = hasflag(o->flags, F_MAPLINK); if (f && (f->val[0] == startcell->map->id) && (f->val[1] != NA) && (f->val[2] != NA)) { cell_t *c; c = getcellat(startcell->map, f->val[1], f->val[2]); if (c) { floodfill(c); // recursive call dblog("FLOODFILL THROUGH PORTAL"); } } } } // populates thing & nthings with all "regionthings" with: // whatkind == RT_BRANCHLINK. // OR // whatkind = RT_HABITAT // ie. links to all the map branches. // returns # found int getbranchlinks(regionthing_t **thing, int *nthings, ...) { va_list args; int i; enum REGIONTHING wantthingtype[MAXCANDIDATES]; int ntypes = 0; region_t *r; regionthing_t *temp; va_start(args, nthings); wantthingtype[ntypes] = va_arg(args, enum REGIONTHING); while (wantthingtype[ntypes] != RT_NONE) { ntypes++; wantthingtype[ntypes] = va_arg(args, enum REGIONTHING); } va_end(args); assert(ntypes < MAXCANDIDATES); *nthings = 0; for (r = firstregion ; r ; r = r->next) { if (!r->outline) continue; for (i = 0; i < r->outline->nthings; i++ ){ int n,ok = B_FALSE; // pick a random branchlink thing. temp = &r->outline->thing[i]; // valid ? for (n = 0; n < ntypes; n++) { if (temp->whatkind == wantthingtype[n]) { ok = B_TRUE; } } if (ok) { if (temp->whatkind == RT_BRANCHLINK) { branch_t *rtype; rtype = findbranch(temp->value); if ( (rtype->id != BH_MAINDUNGEON) && (rtype->id != BH_WORLDMAP)) { thing[(*nthings)++] = temp; } } else if (temp->whatkind == RT_HABITAT) { thing[(*nthings)++] = temp; } } } } return *nthings; } cell_t *getcell_cond(map_t *map, condset_t *cs ) { cell_t **poss,*c; int nposs = 0,i; poss = calloc(map->w * map->h, sizeof(cell_t *)); // get a list of all possible cells nposs = 0; for (i = 0; i < map->w*map->h; i++) { c = map->cell[i]; if (cellmeets(c, cs)) { poss[nposs++] = c; } } if (nposs) { c = poss[rnd(0,nposs-1)]; } else { c = NULL; } free(poss); return c; } 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 = 130; break; case MT_DRAGONWOOD: diff = 100; break; case MT_METAL: diff = 100; break; case MT_STONE: diff = 75; break; case MT_WAX: diff = 50; break; case MT_WOOD: diff = 50; break; case MT_PLANT: diff = 40; break; default: diff = 60; break; } // modify for celltype switch (c->type->id) { case CT_WALLBRICK: diff -= 15; 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 += 120; // 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; enum SKILLLEVEL slev; slev = getskill(viewer, SK_CARTOGRAPHY); // 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; //adjustcellglyph(c, g); - this is only for when we can see cell floor. //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; adjustcellglyph(c, g, CA_BOTH); } } else { // draw cell normally //drawcell(cell, x, y); *g = c->type->glyph; adjustcellglyph(c, g, CA_BOTH); } } else { // can't see the cell int tt = TT_NONE; void *thing; tt = isinscanrange(c, &thing, NULL, &tempgl); switch (tt) { 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 // unless it's a certain kind of glyph if ((slev >= PR_BEGINNER) && (c->known == KG_STAIRS)) { ////////// colour ?? } else if ((slev >= PR_ADEPT) && (c->known == KG_DFEATURE)) { } else if ((slev >= PR_EXPERT) && (c->known == KG_OBJECT)) { } else { g->colour = C_VDARKGREY; } } break; } } } enum CELLTYPE getcellempty(cell_t *c) { flag_t *f; vault_t *v; v = getcellvault(c); if (v) { f = hasflag(v->flags, F_CELLTYPEEMPTY); if (f) return f->val[0]; } 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; vault_t *v; v = getcellvault(c); if (v) { f = hasflag(v->flags, F_CELLTYPESOLID); if (f) return f->val[0]; } 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; } int getmidtemp(enum TEMPERATURE temp) { int min,max; gettemprange(temp,&min,&max); return ((min+max)/2); } enum DEPTH getcellwaterdepth(cell_t *c, lifeform_t *lf) { object_t *o; if (!c) return DP_NONE; o = hasobwithflag(c->obpile, F_DEEPWATER); if (o) { return getobdepth(o, lf); } return DP_NONE; } // note that *ncells should be set to 0 before this function is called int getconnectedwatercells(cell_t *c, cell_t **retcell, int *ncells) { int d; int i,found = B_FALSE; for (i = 0; i < *ncells; i++) { if (retcell[i] == c) { found = B_TRUE; break; } } if (c && // not off the map !found && // not already processed !c->type->solid && // empty cell hasobofmaterial(c->obpile, MT_WATER)) { // has water retcell[*ncells] = c; (*ncells)++; } else { return 0; } for (d = DC_N; d <= DC_NW; d++) { getconnectedwatercells(getcellindir(c, d), retcell, ncells); // recursive call } return *ncells; } // 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 70 + (depth*3); } int getdoorsecretdiff(int depth) { //return 70 + (depth / 2); return 40 + (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; if (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); } } } */ // if "stayclose" is non-zero, then room must be within 'stayclose' cells of another room. int calcroompos(map_t *map, int w, int h, int xmargin, int ymargin, int *bx, int *by, int force, int stayclose) { 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; if (stayclose) { int disttoroom = 999; int n; for (n = 1; n <= stayclose; n++) { int startx,starty,endx,endy; startx = x - n; starty = y - n; endx = x+w + (n-1); endy = y+h + (n-1); limit(&startx, 0, map->w-1); limit(&starty, 0, map->h-1); limit(&endx, 0, map->w-1); limit(&endy, 0, map->h-1); for (ry = starty; ry <= endy; ry++) { // check all x vals for top / bottom if ((ry == starty) || (ry == endy)) { for (rx = startx; rx <= endx; rx++) { cell = getcellat(map, rx,ry); // near a room? if (cell && isroom(cell)) { disttoroom = n; break; } } } else { // otherwise just check l/r cell = getcellat(map, startx,ry); if (cell && isroom(cell)) { disttoroom = n; } else { cell = getcellat(map, endx,ry); if (cell && isroom(cell)) { disttoroom = n; } } } if (disttoroom != 999) break; } if (disttoroom != 999) break; } if (disttoroom > stayclose) { // can't do this one. valid = B_FALSE; } } if (valid) { // 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; continue; } // - overlap a cell with an important object if (hasobwithflag(cell->obpile, F_IMPORTANT)) { valid = B_FALSE; continue; } /* // - 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; continue; } // - overlap a 'locked' cell if (cell->locked) { valid = B_FALSE; continue; } // - be on top of an existing staircase if (hasobwithflag(cell->obpile, F_CLIMBABLE)) { valid = B_FALSE; continue; } // is this cell adjacent to an empty cell and not a // corner (ie. a valid door location) if (countcellexits(cell, DT_ORTH)) { score++; if (score >= bestscore) continue; 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; if (score >= bestscore) continue; } // avoid being adjacent to other room walls if (countcellexits(cell, DT_ORTH)) { score++; if (score >= bestscore) continue; } score += (countadjrooms(cell, DT_ORTH)*3); if (score >= bestscore) continue; // overlapping another room? if (isroom(cell)) { if (force) { score += 10; if (score >= bestscore) continue; } else { valid = B_FALSE; continue; } } } // end if includethiscell } // end for rx... } // end for ry... } 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 { // end if room would fit here 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, enum CELLTYPE id, int dirtype) { int d; int count = 0; int start,end; cell_t *newcell; 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->id == id)) { count++; } } return count; } int countadjrooms(cell_t *cell, int dirtype) { int d; int count = 0; cell_t *newcell; int start, end; if (dirtype == DT_COMPASS) { start = DC_N; end = DC_NW; } else { start = D_N; end = D_W; } for (d = start; d <= end; 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 createantnest(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int x,y; cell_t *c; enum CELLTYPE emptycell,solidcell; int ndug = 0; // 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 = getcellat(map, x, y); if (!c->locked) { setcelltype(c, solidcell); } } } map->illumination = IL_FULLLIT; // pick initial random point c = getrandomcell(map); // delve out the ant's nest ndug = 0; while (ndug < 10) { ndug = delve(map, 2, 3, 5, 75, 1, 1, 5, emptycell, solidcell); } // now do a border, just in case. createborder(map, solidcell); } 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); setcelllocked(c, "border"); // s c = getcellat(map, x, map->h-1); clearcell(c); setcelltype(c,solidtype); setcelllocked(c, "border"); } for (y = 1; y < map->h-1; y++) { // w c = getcellat(map, 0, y); clearcell(c); setcelltype(c,solidtype); setcelllocked(c, "border"); // e c = getcellat(map, map->w-1, y); clearcell(c); setcelltype(c,solidtype); setcelllocked(c, "border"); } } void createbyhut(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int x,y; enum CELLTYPE emptycell,solidcell; cell_t *c; vault_t *v; //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 = getcellat(map, x, y); if (!c->locked) setcelltype(c, solidcell); } } // add a random babayaga's hut vault v = findvaultwithtag("byhut"); assert(v); if (createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { dblog("ERROR - couldn't create byhut vault '%s' on map %s", v->id, map->name); msg("ERROR - couldn't create byhut vault '%s' on map %s", v->id, map->name); assert("failed to create babayaga's hut" == 0); } } 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; // 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; } } */ switch (depth) { case 1: map->illumination = IL_FULLLIT; break; case 2: map->illumination = IL_WELLLIT; break; case 3: map->illumination = IL_DIM; break; default: map->illumination = IL_SHADOWY; break; } // 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 = getcellat(map, x, y); if (!c->locked) setcelltype(c, solidcell); } } // pick initial random points for (i = 0; i < numstartpos; i++) { c = getrandomcell(map); if (!c->locked) { 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, NA, NA, DEF_VAULTMARGIN, DEF_VAULTMARGIN, NULL, NULL, NULL, NULL, 50, 25, B_FALSE, 0); //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_DUN; int maxrooms = MAXROOMS_DUN; enum CORRIDORTYPE corridortype = CDT_NORMAL; int minroomw = NA; int minroomh = NA; int maxroomw = NA; int maxroomh = NA; int moved = 0; enum CELLTYPE emptycell,solidcell; //char buf[BUFLEN]; dbtimestart("Creating dungeon"); // override parameters if (map->habitat->id == H_ICECAVE) { // small rooms minroomw = NA; minroomh = NA; maxroomw = minroomw + 4; maxroomh = minroomh + 4; // slightly more twisty turnpct += rnd(5,10); // less sparse. sparseness -= 10; } // select dungeon shape. dbtime("Starting shape selection."); if (onein(3)) { shape = rnd(0,MAXMAPSHAPES-1); } else { shape = MS_NORMAL; } switch (shape) { case MS_NORMAL:// normal break; case MS_HORZ: // horizontal bar // ###### // ###### // // ###### // ###### for (y = 0; y < map->h/4; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x, y); setcelllocked(c, "horzbar"); } } for (y = (map->h/4)*3; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x, y); setcelllocked(c, "horzbar"); } } break; case MS_VERT: // vertical bar // ## ## // ## ## // ## ## // ## ## // ## ## for (x = 0; x < map->w/4; x++) { for (y = 0; y < map->h; y++) { c = getcellat(map, x, y); setcelllocked(c, "vertbar"); } } for (x = (map->w/4)*3; x < map->w; x++) { for (y = 0; y < map->h; y++) { c = getcellat(map, x, y); setcelllocked(c, "vertbar"); } } 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); setcelllocked(c, "cross"); } for (x = map->w - (map->w/3); x < map->w; x++) { c = getcellat(map, x, y); setcelllocked(c, "cross"); } } for (y = map->h-(map->h/3); y < map->h; y++) { for (x = 0; x < map->w/3; x++) { c = getcellat(map, x, y); setcelllocked(c, "cross"); } for (x = map->w - (map->w/3); x < map->w; x++) { c = getcellat(map, x, y); setcelllocked(c, "cross"); } } 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); setcelllocked(c, "turret"); } } 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); setcelllocked(c, "turret"); } } 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); setcelllocked(c, "turret"); } } 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); setcelllocked(c, "circle"); } } } break; } addflag(map->flags, F_MAPSHAPE, shape, NA, NA, NULL); for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x, y); if (c->locked) { c->visited = B_TRUE; } } } dbtime("Finished shape selection."); /* 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,30)-10); // -10 to +20 looppct -= (rnd(0,10)); // subtract 0 - 10 if (shape == MS_NORMAL) { if (onein(2)) { corridortype = CDT_SIMPLE; } } // is the map lit? if (depth < 5) { map->illumination = IL_FULLLIT; } else if (depth < 10) { map->illumination = IL_WELLLIT; } else if (depth < 15) { map->illumination = IL_DIM; } else { map->illumination = IL_SHADOWY; } //map->illumination = IL_FULLLIT; // 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); if (!getcellvault(c) && c->type->solid) { 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; dbtime("Starting initial delve."); 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(); } dbtime("Finished initial delve."); dbtime("Started removing small rooms."); // remove 2x2 dead-end rooms remove_smallrooms(map); dbtime("Finsihed small rooms."); // use sparseness to cut down dead ends dbtime("Started removing dead ends."); remove_deadends(map, sparseness); dbtime("Finished removing dead ends."); // introduce loops dbtime("Starting loop introduction."); 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 (pctchance(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; } } } } } } } dbtime("Finished loop introduction."); } // adjust min/maxrooms based on fixed vaults minrooms -= map->nfixedrooms; maxrooms -= map->nfixedrooms; limit(&minrooms, 0, NA); limit(&maxrooms, 0, NA); dbtime("Starting room creation."); // 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; dbtime("Starting create vault"); v = getvaulttype(map); if (!createvault(map, map->nrooms, v, NULL, NULL, NULL, NULL)) { // success dbtime("Finished createvault (success)"); continue; } else { dbtime("Finished createvault (failed)"); } } // just do a normal room calcposandmakeroom(map, map->nrooms, minroomw, minroomh, maxroomw, maxroomh, DEF_VAULTMARGIN, DEF_VAULTMARGIN, NULL, NULL, NULL, NULL, 50, 25, B_FALSE, 0); //roomvault[i] = B_FALSE; } } dbtime("Finished room creation."); if (corridortype == CDT_NORMAL) { dbtime("Starting 2nd deadend removal."); // now clear up dead ends again. remove_deadends(map, sparseness); dbtime("Finished 2nd deadend removal."); } // link up room exits dbtime("Starting room exit linking."); 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); } } dbtime("Finished room exit linking."); // add pillars & objects & monsters to rooms dbtime("Starting pillars/objects/monsters"); 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); condset_t cs; initcondv(&cs, CC_HASROOMID, B_TRUE, i, CC_EMPTY, B_TRUE, NA, CC_NONE); //c = getrandomroomcell(map, i, WE_EMPTY); c = getcell_cond(map, &cs); 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); c = getcell_cond(map, &ccwalkable); // 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 dbtime("Finished pillars/objects/monsters"); // river? if ((depth >= 4) && pctchance(20)) { dbtime("Starting river creation"); createriver(map); dbtime("Finished river creation"); } // now do a border dbtime("Starting border"); createborder(map, solidcell); dbtime("Finished border"); dbtimeend("Finished dungeon"); } void createfakes(map_t *map, cell_t *cell) { redrawpause(); 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); redrawresume(); } 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 = getcellat(map, x, y); if (!c->locked) 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) { switch (map->habitat->id) { case H_DUNGEON: createdungeon(map, depth, parentmap, exitdir, entryob); break; case H_ANTNEST: createantnest(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_ICECAVE: createicecave(map, depth, parentmap, exitdir, entryob); break; case H_MASTERVAULTS: createmastervaults(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_BYHUT: createbyhut(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 = getcellat(map, x, y); if (!c->locked) 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, B_NOEXTRA, 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++; } } } void createicecave(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); // remove all doors and grates 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) { int willkill = B_FALSE; nexto = o->next; if (isdoor(o, NULL) ) { willkill = B_TRUE; } else if (hasflagval(o->flags, F_PIT, D_DOWN, NA, NA, NULL)) { willkill = B_TRUE; } else if (o->type->id == OT_GRATINGFLOOR) { willkill = B_TRUE; } if (willkill) { killob(o); } } } } // finalisemap() will replace all walls with ice. } /* 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 exitdir = direction you WENT to get to this map. */ 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 nprevaults = 0; int db = B_TRUE; char dbtag[BUFLEN],dbbuf[BIGBUFLEN]; snprintf(dbtag, BUFLEN, "[createmap.c]"); // don't redraw screen during level change calculations redrawpause(); //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 == BH_WORLDMAP) && (depth == 1)) { firstworldmap = B_TRUE; } */ map->beingcreated = B_TRUE; map->depth = depth; map->region = region; getregionname(buf, map, NULL, RF_SHORT); if (db) { dblog("createmap() - Creating new map of region '%s', depth %d",buf, depth); } 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 == BH_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 == BH_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); snprintf(dbbuf, BIGBUFLEN, "%s -about to check outline",dbtag); dblog(dbbuf); // 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..."); 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 == BH_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; case RT_LF: if (db) dblog(" (lifeform)"); 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) { while (failed) { enum CELLTYPE sol = CT_NONE,emp = CT_NONE; failed = B_FALSE; selectcelltypes(map,20, 20,&sol,&emp); if (sol != CT_NONE) { addflag(map->flags, F_CELLTYPESOLID, sol, NA, NA, NULL); } if (emp != CT_NONE) { addflag(map->flags, F_CELLTYPEEMPTY, emp, NA, NA, NULL); } // create initial map cells (they will be solid) for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { addcell(map, x, y); } } map->nrooms = 0; // reset room counts // place forced vaults. if (db) { //dblog(" adding forced things first..."); dblog(" adding forced things first..."); } for (i = 0; i < nthings ;i++) { vault_t *v = NULL; //cell_t *c; switch (thing[i]->whatkind) { 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; default: break; } if (failed) break; // did we make a vault? if (v) { room_t *r; int x,y; // lock all vault's cells so that map generation doesn't // overwrite it. BUT we'll unlock them again just before // doing fix_reachability. r = &map->room[map->nrooms-1]; for (y = r->y1; y <= r->y2; y++) { for (x = r->x1; x <= r->x2; x++) { cell_t *c; c = getcellat(map, x, y); // this exact text is important!! used later. setcelllocked(c, TEMPVAULTLOCK); } } // remember that this room was made before the rest of the map. // it will counteract the fact that we locked this room's cells // when we call fix_reachability(). r->prevault = B_TRUE; nprevaults++; } } // for each remembered thing if (failed) { dblog("********* got errors during forced vault placement - restarting map creation. *********"); unmakemap(map); continue; } if (db) { dblog(" finished forced vault creation (%d placed).", nprevaults); } // build it... //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging if (db) dblog(" creating map habitat."); createhabitat(map, depth, parentmap, exitdir, entryob); if (db) dblog(" finished map habitat."); //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 (db) dblog(" finished home objects."); //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); c = getcell_cond(map, &ccwalkableroom); if (!c) c = getrandomcell(map); c = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); addob(c->obpile, thing[i]->what); break; case RT_LF: if (db) dblog(" adding forced regionthing lifeform: %s", thing[i]->what); //c = getrandomroomcell(map, ANYROOM, WE_WALKABLE); c = getcell_cond(map, &ccwalkableroom); if (!c) c = getrandomcell(map); c = real_getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND, LOF_DONTNEED, NULL, NULL); addmonster(c, R_SPECIFIED, thing[i]->what, B_FALSE, thing[i]->value, B_TRUE, B_ALLOWEXTRA, NULL); break; case RT_BRANCHLINK: if (db) dblog(" adding branchlink"); createbranchlink(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_NONE: break; default: break; } } if (db) dblog(" finished remembered region outline things."); //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging // ensure that starting level has a player start position if ((gamemode == GM_CHARGEN) && (region->rtype->id == BH_MAINDUNGEON)) { cell_t *startpos; startpos = findobinmap(map, OT_PLAYERSTART); if (!startpos) { dblog("ERROR - first level of main dungeon missing player start position."); failed = B_TRUE; } } if (db) dblog(" removing bad doors..."); if (!failed) { remove_baddoors(map); //remove_deadends(map, 5); } if (db) dblog(" finished bad doors."); // unlock temporary locked cells. for (i = 0; i < map->w * map->h; i++) { cell_t *c; c = map->cell[i]; if (c->lockedreason && streq(c->lockedreason, TEMPVAULTLOCK)) { free(c->lockedreason); c->lockedreason = NULL; c->locked = B_FALSE; } } if (db) dblog(" starting reachability fix..."); // ensure there are no unreachable areas if (!failed) { if (fix_reachability(map)) { failed = B_TRUE; } } if (db) dblog(" finished reachability fix."); //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging if (failed) { dblog("********* got errors - restarting map creation. *********"); unmakemap(map); continue; } // } // end while failed) // at this point, we know failed == false. //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); } if (db) { dblog("About to finalise map"); } // add any required stairs, fix doors, etc. if (finalisemap(map, entryob, exitdir)) { failed = B_TRUE; dblog("********* Map finalisation failed. Testarting map creation. *********"); dblog("********* Map finalisation failed. Testarting map creation. *********"); } else { if (db) { dblog("Finalisation finished."); } } } // 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 (db) { dblog("About to autolink holes."); } // link up holes - this will create NEW holes in THIS map connecting to // EXISTING unlinked holes in adjacent maps // // do this BEFORE linking stairs, in case the act of linking holes generates the next map. // if this happens then we want to also join up unlinked stairs. // if we don't do this then when the player tries to use stairs, we will find // that an existing map below/above exists but has no stairs linked, // which isn't meant to happen. i = linkholes(map); if (db) { dblog(" autolinked to %d holes in adjacent maps.",i); } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging //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 on this level..."); } 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 && !hasflag(o->flags, F_PORTAL) && !getstairdestination(o, NULL) && !hasflag(o->flags, F_MAPLINK)) { dblog(" Trying to link '%s'",o->type->name); // 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(" couldn't link stairs: '%s' (probably no lower level yet)",o->type->name); } } } } } if (db) dblog(" linked %d stairs.", nstairslinked); //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 && !hasobwithflag(c->obpile, F_IMPORTANT)) { // add random obs, but not in vaults if (isempty(c)) { if (!getcellvault(c) || (c->habitat->id == H_HEAVEN)) { 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 // put a last fixreachability call in... fix_reachability(map); redrawresume(); 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++) { c = getcellat(map, x, y); if (!c->locked) { //setcelltype(c, getcellempty(c)); setcelltype(c, getcellsolid(c)); } } } 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; // infinite loop here. while (!c || (countadjwalls(c) == 0) || getcellwaterdepth(c, NULL)) { //c = getrandomroomcell(map, ANYROOM, WE_NONE); c = getcell_cond(map, &ccroom); 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; cell_t *c; // fill entire maze with walls for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x, y); if (!c->locked) setcelltype(c, getcellsolid(c)); } } // 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); // remove all doors and grates 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) { int willkill = B_FALSE; nexto = o->next; if (isdoor(o, NULL) ) { willkill = B_TRUE; } else if (hasflagval(o->flags, F_PIT, D_DOWN, NA, NA, NULL)) { // should never happen h_swamp due to no rarity flags. willkill = B_TRUE; } else if (o->type->id == OT_GRATINGFLOOR) { // should never happen h_swamp due to no rarity flags. willkill = B_TRUE; } if (willkill) { killob(o); } } } } // finalisemap() will replace all walls with deep water. } 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, NA, NA, xmargin, ymargin, &minx, &miny, &w, &h, B_NODOORS, 0, B_TRUE, 0)) { 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, 0)) { // 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; map->room[map->nrooms].prevault = B_FALSE; thisroom = &(map->room[map->nrooms]); map->nrooms++; // now make it dbtime("start actually making vault %s", v->id); 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. NOTE: this might _unset_ cell->room // if 'reusable' is listed. addvaultcellcontents(cell, v, x-minx,y-miny, rotation); } } dbtime("finished actually making vault %s", v->id); dbtime("start markroomwalls - vault %s", v->id); markroomwalls(map, thisroom); dbtime("finished markroomwalls - vault %s", v->id); } if (retw) *retw = w; if (reth) *reth = h; if (retx) *retx = minx; if (rety) *rety = miny; dbtime("start addvaultcontents - vault %s", v->id); // 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); dbtime("finished addvaultcontents - vault %s", v->id); // auto add doors if required f = hasflag(v->flags, F_AUTODOORS); if (f) { dbtime("start autodoors - vault %s", v->id); // when adding autodoors to a vault, they are ALWAYS closed. autodoors(map, roomid, minx, miny, maxx, maxy, f->val[0], B_NODOORS); dbtime("finished autodoors - vault %s", v->id); } // link up exits from this vault if (!hasflag(v->flags, F_VAULTNOLINK)) { dbtime("start linkexits - vault %s", v->id); linkexits(map, roomid); dbtime("finished linkexits - vault %s", v->id); } // set vault cell habitat if required. f = hasflag(v->flags, F_VAULTHABITAT); if (f) { habitat_t *h; h = findhabitat(f->val[0]); assert(h); for (y = miny; y <= maxy; y++) { for (x = minx; x <= maxx; x++) { cell_t *c; c = getcellat(map, x, y); c->habitat = h; } } } // lock cells if required dbtime("start locking cells - vault %s", v->id); if (hasflag(v->flags, F_MAINTAINEDGE)) { for (y = miny; y <= maxy; y++) { for (x = minx; x <= maxx; x++) { cell_t *c; c = getcellat(map, x, y); if (getroomid(c) == roomid) { // ie. not marked as reusable setcelllocked(c, "maintainedge vaultcell"); } } } } dbtime("finished locking cells - vault %s", v->id); // 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) { // if (c) { clearcell(*c); killobpile((*c)->obpile); free(*c); *c = NULL; // } } void killbranch(branch_t *b) { branch_t *nextone, *lastone; // free mem free(b->name); // remove from list nextone = b->next; if (nextone != NULL) { nextone->prev = b->prev; } else { /* last */ lastbranch = b->prev; } if (b->prev == NULL) { /* first */ nextone = b->next; free(firstbranch); firstbranch = nextone; } else { lastone = b->prev; free (lastone->next ); lastone->next = nextone; } } 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 killhabitat(habitat_t *h) { habitat_t *nextone, *lastone; // free mem free(h->name); killflagpile(h->monflags); h->monflags = NULL; // remove from list nextone = h->next; if (nextone != NULL) { nextone->prev = h->prev; } else { /* last */ lasthabitat = h->prev; } if (h->prev == NULL) { /* first */ nextone = h->next; free(firsthabitat); firsthabitat = nextone; } else { lastone = h->prev; free (lastone->next ); lastone->next = nextone; } } void killmap(map_t *m) { map_t *nextone, *lastone; int i; // free mem while (m->lf) killlf(m->lf); free(m->name); 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_TRUE; int d, roomid,startd,endd; int poss2[MAXCANDIDATES],nposs2; int dist[MAXDIR_ORTH],hitsedge[MAXDIR_ORTH], sameroom[MAXDIR_ORTH]; int baddir[MAXDIR_ORTH]; cell_t *directendcell[MAXDIR_ORTH]; int mindist = 999,maxdist = -1; cell_t *c; int forcedir = D_NONE; if (ncellsadded) *ncellsadded = 0; node_t open[MAX_PATHFIND_ADJ]; node_t closed[MAX_PATHFIND_ADJ]; roomid = getroomid(startcell); if (db) dblog(" calling linkexit() for cell at %d,%d in roomid %d", startcell->x, startcell->y, roomid); if (db) { dumpmap(startcell->map, -1, NULL); } for (d = D_N; d <= D_W; d++) { hitsedge[d] = B_TRUE; baddir[d] = B_FALSE; 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; dblog(" force start dir = %s", getdirname(forcedir)); } else { startd = D_N; endd = D_W; dblog(" checking all start dirs"); } // 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]), NULL); if (rv == B_FALSE) { if (db) dblog(" %d,%d going %s, cell NOT ok for reachability.", c->x, c->y, getdirname(d)); dist[d] = 999; baddir[d] = B_TRUE; break; } else if (rv == B_TRUE) { directendcell[d] = c; if (db) dblog(" %d,%d can make %s path (hits empty cell at dist %d)", c->x, c->y, getdirname(d), dist[d]); break; } else { // ie. rv == B_MAYBE int perpdir[2],n; cell_t *pcell = NULL; if (db) dblog(" %d,%d going %s maybe ok...", c->x, c->y, getdirname(d)); 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); } 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 (db) dblog(" going %s we can walk %d steps",getdirname(d), dist[d]); if (dist[d] < mindist) mindist = dist[d]; } if (dist[d] > maxdist) maxdist = dist[d]; } } if (mindist == 999) { int roomidx[MAXROOMS]; cell_t *midcell[MAXROOMS]; int donesomething,a,i,foundpath; map_t *m; node_t *solnode = NULL; if (db) dblog("Using modified a* algorithm."); // use modified a* instead. m = startcell->map; // get all other rooms, in order of distance. for (i = 0; i < m->nrooms; i++) { roomidx[i] = i; midcell[i] = getroommidcell(m, i); } // bubblesort donesomething = B_TRUE; while (donesomething) { donesomething = B_FALSE; for (i = 0; i < m->nrooms-1; i++) { if (getcelldist(startcell, midcell[i]) > getcelldist(startcell, midcell[i+1])) { int tempidx; cell_t *tempcell; // swap tempcell = midcell[i+1]; tempidx = roomidx[i+1]; midcell[i+1] = midcell[i]; roomidx[i+1] = roomidx[i]; midcell[i] = tempcell; roomidx[i] = tempidx; donesomething = B_TRUE; } } } if (db) { dblog("Other room distances from room %d:", roomid); for (i = 0;i < m->nrooms; i++) { room_t *r; r = &(m->room[roomidx[i]]); dblog(" Room %d (distance %d)%s", r->id, getcelldist(startcell, midcell[i]), (r->id == roomid) ? " [start room]" : ""); } } // try pathfinding to each room, in order. foundpath = B_FALSE; for (a = 0 ; (a < m->nrooms) && !foundpath; a++) { int destroomid; int done = B_FALSE,i,n; node_t *cur; int nopen = 0,nclosed = 0; int okforreach; destroomid = m->room[roomidx[a]].id; if (destroomid == roomid) continue; // clear open and closed lists. for (i = 0; i < MAX_PATHFIND_ADJ; i++) { clearnode(&open[i]); clearnode(&closed[i]); } nopen = 0; nclosed = 0; if (db) dblog("linkexit() pathfind - finding path from %d,%d (room %d) to room %d.", startcell->x, startcell->y, roomid, destroomid); // add starting cell to open list open[0].c = startcell; open[0].fromstart = 0; open[0].heur = calch_map(startcell, destroomid); open[0].cost = open[0].fromstart + open[0].cost; open[0].parent = NULL; open[0].dirfromparent = D_NONE; nopen = 1; while (!done) { char why[BUFLEN]; // if open list empty? if (!nopen) { // if so, there is NO path. fail. if (db) dblog("linkexit() pathfind - open list is empty. FAILED."); foundpath = B_FALSE; break; } // find lowest COST in open list. this is cur. // move node[cur] to closed list closed[nclosed] = open[0]; cur = &closed[nclosed]; nclosed++; for (i = 0; i < nopen-1; i++) { open[i] = open[i+1]; } nopen--; okforreach = cellokforreachability(startcell, cur->c, roomid, D_NONE, wantfilled, NULL, NULL); // is node[cur] in the target room? xxxxxxxx not sure about dirfromparent here. if ((getroomid(cur->c) == destroomid) && okforreach) { if (db) dblog("success - found a cell in the dest room!."); // if so, we've found a path. now need to populate //if (db) dblog("%s pathfind - at target cell - success!",lfname); foundpath = B_TRUE; done = B_TRUE; solnode = cur; } else if (wantfilled && cur->c->filled && okforreach) { if (db) dblog("success - stumbled upon a filled cell."); foundpath = B_TRUE; done = B_TRUE; solnode = cur; } else if (cellwalkable(NULL, cur->c, NULL) && okforreach) { if (db) dblog("success - stumbled upon a walkable cell."); foundpath = B_TRUE; done = B_TRUE; solnode = cur; } else { if (db) dblog("checking around (%d,%d): ", cur->c->x, cur->c->y); // for "adjcell" in 4 squares around it: for (i = D_N; i <= D_W; i++) { cell_t *adjcell; int found = B_FALSE, ok = B_FALSE; adjcell = getcellindir(cur->c, i); if (!adjcell) continue; //if (db) dblog("%s pathfind - checking %s",lfname, getdirnameshort(i)); // already on closed list? for (n = 0; n < nclosed; n++) { if (closed[n].c == adjcell) { found = B_TRUE; break; } } if (found) { // already on closed list. ignore. if (db) dblog(" %s = on closed list.", getdirname(i)); //if (db) dblog(" %s (%d,%d): already on closed list. ignore.",getdirnameshort(i), // adjcell->x, adjcell->y); continue; } //walkable = cellwalkable(lf, adjcell, &whynot); ok = cellokforreachability(startcell, adjcell, roomid, i, wantfilled, NULL, why); if (ok) { int openpos = -1; // adjcell is walkable. for (n = 0; n < nopen; n++) { if (open[n].c == adjcell) { openpos = n; break; } } if (openpos != -1) { int newfromstart; node_t temp; // adjcell is already on open list if (db) dblog(" %s = already on open list. recalcing.", getdirname(i)); //if (db) dblog(" %s (%d,%d): on open list (position %d).",getdirnameshort(i), // adjcell->x, adjcell->y, openpos); // recalc fromstart of adjcell using node[cur] as a parent. newfromstart = calcg_map(open[openpos].c, cur, i); if (newfromstart < open[openpos].fromstart) { // path through node[cur] is better. //if (db) dblog(" %s (%d,%d): better cost - recalcing.", // getdirnameshort(i), // adjcell->x, adjcell->y); // change parent of adjcell to node[cur] // and recalc new costings (and re-sort list) temp = open[openpos]; temp.parent = cur; temp.dirfromparent = i; assert(isadjacent(temp.c, temp.parent->c)); temp.fromstart = calcg_map(temp.c, temp.parent, i); temp.heur = calch_map(temp.c, destroomid); temp.cost = temp.fromstart + temp.heur; // remove adjcell from open list. for (n = openpos; n < nopen; n++) { open[n] = open[n+1]; } nopen--; // re-add adjcell at correct pos; insert(&temp, open, &nopen); } } else { if (db) dblog(" %s = not on open list. adding.", getdirname(i)); // not on open list node_t temp; //if (db) dblog(" %s (%d,%d): not on openlist. inserting.", // getdirnameshort(i), // adjcell->x, adjcell->y); // calc costs temp.c = adjcell; temp.parent = cur; temp.dirfromparent = i; assert(isadjacent(temp.c, temp.parent->c)); temp.fromstart = calcg_map(temp.c, temp.parent, i); temp.heur = calch_map(adjcell, destroomid); temp.cost = temp.fromstart + temp.heur; insert(&temp, open, &nopen); } } else { // !walkable - ignore it. if (db) dblog(" %s = not usable (%s). ignoring.", getdirname(i), why); //if (db) dblog(" %s (%d,%d): not walkable - ignoring.", // getdirnameshort(i), // adjcell->x, adjcell->y); } } // end for each adj cell }// end "have we reached target room" } } if (foundpath) { int pathlen = 0; char reasontext[BUFLEN]; // work backwards from node[cur] (ie. targcell) following parents. // populate path and return snprintf(reasontext, BUFLEN, "astar pathfind link (from roomid %d)",roomid); while (solnode) { pathlen++; if (pathlen >= MAX_PATHFIND_STEPS) { assert("pathfind - path > MAX_PATHFIND_STEPS - failed." == 0); } // clear solnode->c breakwall(solnode->c, reasontext); if (ncellsadded) (*ncellsadded)++; solnode = solnode->parent; } if (db) dblog("linkexit() - SUCCESS. Path length is %d steps.\n", pathlen); if (db) dumpmap(m, B_TRUE, reasontext); } 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); //} //raise(SIGINT); 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, "direct link (from vault %d)",getroomid(startcell)); if (ncellsadded) (*ncellsadded)++; c = getcellindir(c, whichway); } } // now make sure the START cell is empty too! if (startcell->type->solid && !startcell->locked && !cellisfixedvaultwall(startcell)) { breakwall(startcell, "making sure start cell is empty (from vault %d)",getroomid(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_FALSE; int nadded = 0; int minx = -1, miny = -1, maxx = -1, maxy = -1; int roomidx = -1; vault_t *v; condset_t cs; // 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); initcondv(&cs, CC_HASROOMID, B_TRUE, roomid, CC_NONE); c = getcell_cond(m, &cs); if (!c) return B_FALSE; v = getcellvault(c); if (v) { if (hasflag(v->flags, F_VAULTNOLINK)) { return B_FALSE; } } if (db) { char buf[BUFLEN]; //c = getrandomroomcell(m, roomid, WE_NONE); 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++; } else { // linkexit failed! if (db) dblog(" Linkexits() - linkexit() failed."); //if (db) { // raise(SIGINT); //} } } } // end for each door m->room[roomidx].exitslinked = B_TRUE; if (db) dblog("linkexits complete (%d added).", nadded); return nadded; } void createmastervaults(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int x,y,i; cell_t *c; enum CELLTYPE emptycell,solidcell; int nextrarooms; int minw,minh,maxw,maxh; int xmarg,ymarg; int db = B_TRUE,rv; minw = 5; minh = 5; maxw = 10; maxh = 10; xmarg = 3; ymarg = 3; // 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 = getcellat(map, x, y); if (!c->locked) setcelltype(c, solidcell); } } map->illumination = IL_FULLLIT; // place initial random room. make sure it works! rv = B_TRUE; while (rv) { rv = calcposandmakeroom(map, map->nrooms, minw,minh,maxw,maxh, xmarg,ymarg, NULL, NULL, NULL, NULL, 100, 0, B_TRUE, 0); } // place more rooms, but they must be within 3 cells of each other. nextrarooms = rnd(4,8); for (i = 0; i < nextrarooms; i++) { if (calcposandmakeroom(map, map->nrooms, minw,minh,maxw,maxh, xmarg,ymarg, NULL, NULL, NULL, NULL, 100, 0, B_TRUE, 3)) { // failed. break; } } // clear all solid cells with adjacent rooms for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = getcellat(map, x, y); if (issolid(c) && !isroom(c) && countadjrooms(c, DT_COMPASS)) { setcelltype(c, emptycell); } } } // place chests for (i = 0; i < map->nrooms; i++) { int nchests; condset_t cs; nchests = rnd(1,5); initcondv(&cs, CC_HASROOMID, B_TRUE, i, CC_WALKABLE, B_TRUE, NA, CC_NONE); //c = getrandomroomcell(map, i, WE_WALKABLE); c = getcell_cond(map, &cs); if (c) { addob(c->obpile, "random container"); } } // now do a border, just in case. createborder(map, solidcell); if (db) dblog("linkexits complete (%d rooms added).", map->nrooms); } 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++) { c = getcellat(map, x, y); if (!c->locked) setcelltype(c, getcellsolid(c)); } } // 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 createbranchlink(map_t *m, cell_t *c, object_t *o, char *obname, enum BRANCH newbranch, region_t *parent) { flag_t *f; region_t *r; int basedepth = 0; branch_t *nrt; nrt = findbranch(newbranch); if (nrt->addparentdepth) { basedepth = getmapdifficulty(m); } // create a new region. r = addregion(newbranch, 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; } // return true on error int createportallink(cell_t *src, cell_t *dst, enum OBTYPE portalid) { object_t *dstportal = NULL,*srcportal = NULL; // make sure cells exist if (!src || !dst) { dblog(" createportals(): src or dst cell doesn't exist"); return B_TRUE; } // make sure cells are empty if (issolid(src) || issolid(dst)) { dblog(" createportals(): src or dst cell is solid"); return B_TRUE; } // make sure neither cell has a portal already if (hasob(src->obpile, portalid) || hasob(dst->obpile, portalid)) { dblog(" createportals(): src or dst cell already has a portal"); return B_TRUE; } // add the portals srcportal = addobfast(src->obpile, portalid); if (!srcportal) { dblog(" createportals(): couldn't create source portal."); return B_TRUE; } dstportal = addobfast(dst->obpile, portalid); if (!dstportal) { dblog(" createportals(): couldn't create destination portal."); killob(srcportal); return B_TRUE; } linkportals(srcportal,dstportal); return B_FALSE; } int linkportals(object_t *srcportal, object_t *dstportal) { cell_t *src,*dst; src = getoblocation(srcportal); dst = getoblocation(dstportal); if (!src || !dst) return B_TRUE; // link them addflag_real(srcportal->flags, F_MAPLINK, dst->map->id, dst->x, dst->y, NULL, PERMENANT, B_FALSE, -1); addflag_real(dstportal->flags, F_MAPLINK, src->map->id, src->x, src->y, NULL, PERMENANT, B_FALSE, -1); return B_FALSE; } 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; map->room[map->nrooms].prevault = 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 overridemaxw, int overridemaxh, int xmargin, int ymargin, int *retx, int *rety, int *retw, int *reth, int doorpct, int dooropenchance, int forcewalls, int stayclose) { 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; } if (overridemaxw != NA) { maxroomw = overridemaxw; } if (overridemaxh != NA) { maxroomh = overridemaxh; } // select random width/height w = rnd(minroomw, maxroomw); h = rnd(minroomh, maxroomh); if (retw) *retw = w; if (reth) *reth = h; // find room position dbtime("start calcroompos"); if (calcroompos(map, w, h, xmargin, ymargin, &minx, &miny, B_FALSE, stayclose)) { dblog("** couldn't make room!\n"); return B_TRUE; } dbtime("end calcroompos"); 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. dbtime("start createroom"); createroom(map, roomid, minx,miny,maxx,maxy, forcewalls); dbtime("end createroom"); // add doors if (doorpct) { dbtime("start autodoors"); autodoors(map, roomid, minx, miny, maxx, maxy, doorpct, dooropenchance); dbtime("end autodoors"); } 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; } int doorisvalid(object_t *o) { int dir,nfilled = 0; cell_t *c; c = getoblocation(o); /* // doors must be between two solid cells for (dir = DC_N; dir <= DC_SE; dir++) { cell_t *c1; cell_t *c2; c1 = getcellindir(c, dir); c2 = getcellindir(c, diropposite(dir)); if (!issolid(c1) && !issolid(c2)) { return B_TRUE; } } */ if (!dodoorfill(c)) { // door is surrounded by walls return B_FALSE; } // if there are any empty unfilled cells around the door, it's ok. nfilled = 0; for (dir = DC_N; dir <= DC_NW; dir++) { cell_t *c2; c2 = getcellindir(c, dir); if (c2 && !issolid(c2) && !c2->filled) return B_TRUE; } return B_FALSE; } void dumpmap(map_t *map, int showrooms, char *hilitereason) { 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); if (hilitereason && streq(cell->reason, hilitereason)) { ch = '@'; } else { ch = cell->type->glyph.ch; if (ch == '.') { if (showrooms && isroom(cell)) { ch = '0' + getroomid(cell); } if (ch == '.') { if (cell->filled) { ch = 'o'; } } } else ch = '#'; } 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_BRANCHLINK) { branch_t *rtype; rtype = findbranch(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, lifeform_t *fromwho) { 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, fromwho, B_FALSE); } explosion_knockback(c, range+1, dirtype, dam, fromwho); } void explosion_knockback(cell_t *c, int radius, int dirtype, int dam, lifeform_t *fromwho) { cell_t *retcell[MAXRETCELLS]; int nretcells,i; // lfs up to 1 cell away are knocked back, if no walls in the way getradiuscells(c, radius, 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)) { if (!isimmuneto(cc->lf->flags, DT_EXPLOSIVE, B_FALSE)) { 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, getrandomcorebp(cc->lf, NULL), NULL, 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, fromwho, 200-(mydist*50), B_DOANNOUNCE, B_DODAM); } } } } 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 && !c->locked) { // 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 && !c2->locked && 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, lifeform_t *fromwho, int doknockback) { 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, fromwho, buf); } damageallobs(o, c->obpile, dam, DT_EXPLOSIVE, fromwho); if (killwalls) { if (c->type->solid) { damagecell(c, dam, DT_EXPLOSIVE, fromwho); } } if (doknockback) { explosion_knockback(c, 1, DT_COMPASS, dam, fromwho); } } //returns true on failure int finalisemap(map_t *map, object_t *entryob, int exitdir) { enum OBTYPE upstairtype, downstairtype; int i,d,x,y; int linkedentry = B_FALSE; objecttype_t *entryoppositetype = NULL; //char roomlightob[BUFLEN],corridorlightob[BUFLEN]; //int roomlightchance = 0; //int corridorlightfreq = 0; int nupstairsreq = 0,ndownstairsreq = 0; int db = B_TRUE; int nupstairsneeded = 0,ndownstairsneeded = 0; cell_t *c; object_t *o,*nexto; condset_t okforstairs; if (entryob) { entryoppositetype = getoppositestairs(entryob->type); } initcondv(&okforstairs, CC_EMPTY, B_TRUE, NA, CC_OKFORSTAIRS, B_TRUE, NA, CC_NONE); if (map->habitat->stairsinrooms) { addcond(&okforstairs, CC_ISROOM, B_TRUE, NA); } // 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; nupstairsreq = map->region->rtype->stairsperlev; ndownstairsreq = map->region->rtype->stairsperlev; // override # up stairs for first level of a branch (we only want one up staircase) if (hasflag(map->flags, F_FIRSTINBRANCH)) { nupstairsreq = 1; } nupstairsneeded = nupstairsreq - countmapobs(map, upstairtype); ndownstairsneeded = ndownstairsreq - countmapobs(map, downstairtype); 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); c = getcell_cond(map, &okforstairs); } 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 == BH_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 { if (db) dblog("Need to create %d up stairs.", nupstairsneeded); // up stairs on all other levels for (i = 0; i < nupstairsneeded; i++) { c = NULL; if (!c || countobs(c->obpile, B_TRUE)) { //c = getrandomroomcell(map, ANYROOM, WE_EMPTY); c = getcell_cond(map, &okforstairs); /* while (!c || (getcellvault(c) && hasflag(c->room->vault->flags, F_NOSTAIRS))) { // ANY cell at all, doesn't have to be a room. c = getrandomcell(map); if (!cellwalkable(NULL, c, NULL)) { trytokillobs(c->obpile); } // still not walkable? find something nearby. if (!cellwalkable(NULL, c, NULL)) { c = getrandomadjcell(c, &ccwalkable, B_ALLOWEXPAND); } } */ if (c) { trytokillobs(c->obpile); } else { dblog("ERROR - couldnt find pos for up stairs while making habitat %s.", map->habitat->name); msg("ERROR - couldnt find pos for up stairs while making habitat %s.", map->habitat->name); return B_TRUE; } } o = addobfast(c->obpile, upstairtype); assert(o); if (db) dblog("Created upstairs '%s'", o->type->name); if (entryob && (exitdir == D_DOWN) && !linkedentry && entryoppositetype && (entryoppositetype->id == o->type->id)) { linkstairs(o, entryob); linkedentry = B_TRUE; } else { linkstairs(o, NULL); } } } // make sure we have at least one up stairs if (map->region->rtype->id != BH_WORLDMAP) { assert(findobinmap(map, upstairtype)); } } // DOWN STAIRS if ((downstairtype != OT_NONE) && (map->depth < map->region->rtype->maxdepth)) { if (db) dblog("Need to create %d down stairs.", ndownstairsneeded); for (i = 0; i < ndownstairsneeded; i++) { c = NULL; if (!c || countobs(c->obpile, B_TRUE)) { //c = getrandomroomcell(map, ANYROOM, WE_EMPTY); c = getcell_cond(map, &okforstairs); if (c) { trytokillobs(c->obpile); } else { dblog("ERROR - couldnt find pos for down stairs while making habitat %s.", map->habitat->name); msg("ERROR - couldnt find pos for down stairs while making habitat %s.", map->habitat->name); return B_TRUE; } } o = addobfast(c->obpile, downstairtype); assert(o); if (db) dblog("Created downstairs '%s'", o->type->name); if (entryob && (exitdir == D_UP) && !linkedentry && entryoppositetype && (entryoppositetype->id == o->type->id)) { linkstairs(o, entryob); linkedentry = B_TRUE; } else { linkstairs(o, NULL); } } } // if our up/down stairs were created by a vault, then we now need to link them. if (entryob && !linkedentry) { enum OBTYPE wantoid; if (exitdir == D_DOWN) { wantoid = upstairtype; } else if (exitdir == D_UP) { wantoid = downstairtype; } else { objecttype_t *ot; ot = getoppositestairs(entryob->type); wantoid = ot->id; } if (db) dblog("Still haven't linked to map entry object (%s)",entryob->type->name); for (y = 0; (y < map->h) && !linkedentry; y++) { for (x = 0; (x < map->w) && !linkedentry; x++) { c = getcellat(map, x, y); o = hasob(c->obpile, wantoid); if (o && !hasflag(o->flags, F_MAPLINK) && entryoppositetype && (entryoppositetype->id == o->type->id)) { if (db) dblog("Found candidate: %s",o->type->name); linkstairs(o, entryob); linkedentry = B_TRUE; break; } } } if (!linkedentry) { // no objects of the correct type were found. if (db) dblog("No candidates found. Creating object to link back to entry ob."); // create one. initcondv(&okforstairs, CC_EMPTY, B_TRUE, NA, CC_OKFORSTAIRS, B_TRUE, NA, CC_HASOBTYPE, B_FALSE, entryoppositetype->id, CC_NONE); c = getcell_cond(map, &okforstairs); if (!c) { // relax the conditions initcondv(&okforstairs, CC_OKFORSTAIRS, B_TRUE, NA, CC_NONE); c = getcell_cond(map, &okforstairs); } if (c) { // DONT let addobject create maplinks, because we want to force it to // link to entry object. o = addobject(c->obpile, entryoppositetype->name, B_FALSE, B_FALSE, entryoppositetype->id); assert(o); if (db) dblog("Created '%s'. About to link it.",o->type->name); killflagsofid(o->flags, F_MAPLINK); linkstairs(o, entryob); linkedentry = B_TRUE; } } if (!linkedentry) { dblog("ERROR - couldn't link stairs back to map entry object."); msg("ERROR - couldn't link stairs back to map entry object."); more(); } } // other finalisation tasks... 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; // unlinked stairs? ie ones added from vaults. if so, link them. if (hasflag(o->flags, F_CLIMBABLE) && !hasflag(o->flags, F_MAPLINK)) { if (!hasflag(o->flags, F_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); } } } } } */ // in swamps, replace all walls with deep water if (map->habitat->id == H_SWAMP) { int x,y; cell_t *c; 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"); } } } //} else if (map->habitat->id == H_ICECAVE) { // in ice caves, replace all walls with ice } return B_FALSE; } 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; vault_t *v; object_t *o; 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 (!lfhasflag(lf, F_UNIQUE)) { // might get a random behaviour if one wasn't specified if ((wantbehaviour == BH_NONE) && canhaverandombehaviour(lf) && onein(6)) { wantbehaviour = getrandombehaviour(); } givebehaviour(lf, wantbehaviour); } if (wantflags) { // since we've already called givestartobs(), any new ones // in wantflags need to be handled now. givestartobs(lf, NULL, wantflags); // now remove startob flags from wantflags killflagsofid(wantflags, F_STARTOB); // ...and copy the rest over. 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_A_FLY, NULL) && !isburdened(lf)) { notime = B_TRUE; //castspell(lf, OT_S_FLIGHT, lf, NULL, lf->cell, NULL, NULL); useability(lf, OT_A_FLY, lf, lf->cell); 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); } } // if monster is in a vault, check its flags... v = getcellvault(lf->cell); if (v) { copyflag(lf->flags, v->flags, F_STAYINROOM); } if (lf->race->id == R_HYDRA) { int nextra,i; // they always have at least 5 heads. add more... // grow extra heads nextra = rnd(0,7); for (i = 0; i < nextra; i++) { growhydrahead(lf, B_FALSE); } } // make sure monster has ammo for their weapon for (o = lf->pack->first ; o ; o = o->next) { if (isfirearm(o) && !findammoinobpile(o, lf->pack)) { objecttype_t *ot; ot = getrandomammofor(o, B_FALSE); if (ot) { char ammoname[BUFLEN]; sprintf(ammoname, "%d %s", rnd(2,6),ot->name); addob(lf->pack, ammoname); } } } /* 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]); } } */ autoskill(lf); } 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; } regionoutline_t *findoutlineforbranch(enum BRANCH bid) { regionoutline_t *ro; for (ro = firstregionoutline ;ro ; ro = ro->next) { if (ro->rtype->id == bid) return ro; } return NULL; } branch_t *findrandombranchwithname(char *name) { branch_t *rt,*poss[MAXCANDIDATES]; int nposs = 0; char buf[BUFLEN]; for (rt = firstbranch ; 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 branchs like // rg_worldmap and rg_firstdungeon region_t *findregionbytype(enum BRANCH 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_BRANCHLINK regionthing which links to region type rtid (eg. BH_CAVE) regionthing_t *findbranchlink(enum BRANCH 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_BRANCHLINK) && (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; } branch_t *findbranch(enum BRANCH rtype) { branch_t *rt; for (rt = firstbranch ; rt ; rt = rt->next) { if (rt->id == rtype) return rt; } return NULL; } branch_t *findbranchbyname(char *name) { branch_t *rt; for (rt = firstbranch ; 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 = KG_UNKNOWN; // 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; case D_MYSELF: return cell; default: return NULL; } newx = cell->x + dirtox(dt, dir); newy = cell->y + dirtoy(dt, dir); newcell = getcellat(cell->map, newx,newy); return newcell; } enum TEMPERATURE getcelltemp(cell_t *c, int *actualtemp) { enum TEMPERATURE temp; if (!c || (gamemode != GM_GAMESTARTED)) { if (actualtemp) *actualtemp = getmidtemp(T_NORMAL); return T_NORMAL; } temp = c->temperature; limit(&temp, MIN_TEMPERATURE, MAX_TEMPERATURE); if (actualtemp) *actualtemp = temp; return gettempbracket(temp); } 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, condset_t *cs, int allowexpand) { return real_getrandomadjcell(c, cs, allowexpand, LOF_NEED, NULL, NULL); } cell_t *real_getrandomadjcell(cell_t *c, condset_t *cs, int allowexpand, enum LOFTYPE needlof, cell_t *dontwantcell, 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++) { int gotlof = B_FALSE; new = getcellat(c->map, x, y); if (new && haslof(c, new, needlof, NULL)) { gotlof = B_TRUE; numwithlof++; } if (new && (new != c) && (getcelldist(c,new) == radius) && (new != dontwantcell) && gotlof) { int ok = B_FALSE; ok = cellmeets(new, cs); // obs we dont want? /* if (dontwantob) { for (badoid = dontwantob; (*badoid != OT_NONE) ; badoid++) { if (hasob(new->obpile, *badoid)) { ok = B_FALSE; break; } } } */ /* if (wantmat != MT_NOTHING) { if (new->type->material->id != wantmat) { ok = B_FALSE; } } */ if (ok) { if (preferlos && haslos(preferlos, new)) { losposs[nlosposs++] = new; } poss[nposs++] = new; } } } } // found any possibilities ? if (preferlos && nlosposs) { done = B_TRUE; //} else if (!preferlos && nposs) { } else if (nposs) { done = B_TRUE; } else { if (allowexpand) { if (numwithlof) { // increment radius radius++; // more than map width? fail. if (radius >= MAXOF(MAX_MAPH,MAX_MAPW)) return NULL; } else { if (preferlos) { // start again without preferlos radius = 1; preferlos = NULL; } else { return NULL; } } } else { if (preferlos) { // start again without preferlos radius = 1; preferlos = 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 *getrandomcell_forteleport(map_t *map, lifeform_t *lf) { cell_t *c = NULL; int maxtries = 10,try = 0; while (!c) { int ok = B_TRUE; c = getrandomcell(map); if (!c || c->type->solid || haslf(c) || !cellwalkable(lf, c, NULL) || hasobwithflag(c->obpile, F_CLIMBABLE)) { ok = B_FALSE; } if (!ok) { c = NULL; if (++try > maxtries) { break; } } } return c; } 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; int pctmod = 100; if (slipob) *slipob = NULL; switch (c->type->id) { case CT_MOSSROCK: addition = 15; pctmod += 50; break; case CT_FLOORSNOW: addition = 10; pctmod += 50; break; case CT_FLOORTILE: addition = 5; pctmod += 25; break; case CT_FLOORCARPET: addition = -10; pctmod -= 50; break; default: addition = 0; pctmod = 100; 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 = pctof(pctmod, thisslip); thisslip *= o->amt; totalslip += thisslip; } } totalslip += addition; //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)) { if (hasflag(o->flags, F_PORTAL)) { // ie this is a portal return NULL; } else { if (f->val[1] == NA) { // use same region newregion = obcell->map->region; if (dir == D_UP) newdepth = curmap->depth - 1; else newdepth = curmap->depth + 1; } else { newregion = findregion(f->val[1]); newdepth = 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 *hasdoor(cell_t *c) { object_t *o; for (o = c->obpile->first ; o ; o = o->next) { if (isdoor(o, NULL)) { return o; } } return NULL; } 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; } vault_t *maphasvault(map_t *m, char *id) { int i; for (i = 0; i < m->nrooms; i++) { vault_t *v; v = m->room[i].vault; if (v && streq(v->id, id) ) { return v; } } return NULL; } object_t *hastrailof(obpile_t *op, lifeform_t *lf, int 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) { habitat_t *h; // habitats // thingchance, obchance, vaultchance, maxvisrange, upstiartype, downstairtype, stairs_in_rooms,temp addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 5, 60, 40, 6, OT_STAIRSUP, OT_STAIRSDOWN, B_TRUE, T_NORMAL); h = addhabitat(H_ICECAVE, "ice cavern", CT_CORRIDOR, CT_WALLICE, 10, 45, 20, 6, OT_ICESTAIRSUP, OT_ICESTAIRSDOWN, B_FALSE, T_COLD); addflag(h->monflags, F_DTIMMUNE, DT_COLD, B_TRUE, NA, NULL); addhabitat(H_CAVE, "cave", CT_DIRT, CT_WALLDIRT, 5, 45, 20, 6, OT_TUNNELUP, OT_TUNNELDOWN, B_FALSE, T_NORMAL); addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALLTREE, 5, 75, 0, MAXVISRANGE, OT_TREEUP, OT_TREEDOWN, B_FALSE, T_NORMAL); addhabitat(H_HEAVEN, "heaven", CT_CORRIDOR, CT_WALLGLASS, 5, 0, 0, MAXVISRANGE, OT_NONE, OT_NONE, B_FALSE, T_NORMAL); addhabitat(H_PIT, "pit", CT_CORRIDOR, CT_WALL, 0, 0, 0, 5, OT_NONE, OT_NONE, B_FALSE, T_NORMAL); addhabitat(H_VILLAGE, "village", CT_GRASS, CT_WALL, 5, 70, 0, MAXVISRANGE, OT_NONE, OT_NONE, B_FALSE, T_NORMAL); addhabitat(H_SEWER, "sewer", CT_MOSSROCK, CT_WALL, 10, 50, 0, MAXVISRANGE, OT_GRATINGROOF, OT_NONE, B_FALSE, T_CHILLY); addhabitat(H_STOMACH, "stomach", CT_FLOORFLESH, CT_WALLFLESH, 5, 80, 0, MAXVISRANGE, OT_NONE, OT_NONE, B_FALSE, T_WARM); addhabitat(H_SWAMP, "swamp", CT_CORRIDOR, CT_WALL, 5, 50, 0, MAXVISRANGE, OT_STAIRSUP, OT_STAIRSDOWN, B_FALSE, T_WARM); // "hot+humid" addhabitat(H_BYHUT, "babayaga's hut", CT_FLOORWOOD, CT_WALLDWOOD, 0, 0, 0, MAXVISRANGE, OT_BYHUTDOOR, OT_NONE, B_FALSE, T_NORMAL); addhabitat(H_ANTNEST, "ant nest", CT_DIRT, CT_WALLDIRT, 10, 40, 0, MAXVISRANGE, OT_STAIRSUP, OT_STAIRSDOWN, B_FALSE, T_NORMAL); addhabitat(H_MASTERVAULTS, "master vaults", CT_FLOORDURANITE, CT_WALLDURANITE, 10, 0, 0, MAXVISRANGE, OT_VSTAIRSUP, OT_VSTAIRSDOWN, B_FALSE, T_NORMAL); // cell types - solid // floorheight, hp, volmod, absorbant addcelltype(CT_WALL, "rock wall", UNI_SHADEDARK, C_GREY, NA, B_SOLID, B_OPAQUE, MT_STONE, 0, 50, 0, B_NOABSORB); addcelltype(CT_WALLBRICK, "brick wall", UNI_SHADEDARK, C_BRICK, NA, B_SOLID, B_OPAQUE, MT_BRICK, 0, 40, 0, B_NOABSORB); addcelltype(CT_WALLDIRT, "dirt wall", UNI_SHADEMED, C_BROWN, NA, B_SOLID, B_OPAQUE, MT_STONE, 0, 20, 0, B_NOABSORB); addcelltype(CT_WALLDURANITE, "duranite wall", UNI_SHADEDARK, C_MAGENTA, NA, B_SOLID, B_OPAQUE, MT_DURANITE, 0, 20000, 0, B_NOABSORB); addcelltype(CT_WALLWOOD, "wooden wall", UNI_DYNAMIC, C_DARKBROWN, NA, B_SOLID, B_OPAQUE, MT_WOOD, 0, 30, 0, B_NOABSORB); addcelltype(CT_WALLDWOOD, "wyrmwood wall", UNI_DYNAMIC, C_DARKBROWN, NA, B_SOLID, B_OPAQUE, MT_DRAGONWOOD, 0, 100, 0, B_NOABSORB); addcelltype(CT_WALLFLESH, "flesh wall", UNI_SOLID, C_FLESH, NA, B_SOLID, B_OPAQUE, MT_FLESH, 0, 25, 0, B_NOABSORB); addcelltype(CT_WALLGLASS, "glass wall", UNI_DYNAMIC, C_CYAN, NA, B_SOLID, B_TRANS, MT_GLASS, 0, 20, 0, B_NOABSORB); addcelltype(CT_WALLICE, "ice wall", UNI_SHADEDARK, C_LIGHTCYAN, NA, B_SOLID, B_TRANS, MT_ICE, 0, 30, 0, B_NOABSORB); //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_LIGHTGREEN, NA, B_SOLID, B_OPAQUE, MT_PLANT, 0, 100, 0, B_NOABSORB); addcelltype(CT_WALLMETAL, "metal wall", UNI_DYNAMIC, C_METAL, NA, B_SOLID, B_OPAQUE, MT_METAL, 0, 75, 0, B_NOABSORB); // cell types - non-solid addcelltype(CT_FAKE, "fake cell", '.', C_GREEN, NA, B_EMPTY, B_TRANS, MT_STONE, 0, -1, 0, B_NOABSORB); addcelltype(CT_MOSSROCK, "mossy rock floor", '.', C_MOSS, NA, B_EMPTY, B_TRANS, MT_STONE, 0, -1, 0, B_NOABSORB); addcelltype(CT_CORRIDOR, "rock floor", '.', C_GREY, NA, B_EMPTY, B_TRANS, MT_STONE, 0, -1, 0, B_NOABSORB); addcelltype(CT_LOOPCORRIDOR, "rock floor", 'L', C_GREY, NA, B_EMPTY, B_TRANS, MT_STONE, 0, -1, 0, B_NOABSORB); addcelltype(CT_FLOORCARPET, "carpetted floor", '.', C_CARPET1, C_CARPET2, B_EMPTY, B_TRANS, MT_CLOTH, 0, -1, -1, B_ABSORB); addcelltype(CT_FLOORDURANITE, "duranite floor", '.', C_MAGENTA, NA, B_EMPTY, B_TRANS, MT_DURANITE, 0, -1, 1, B_NOABSORB); addcelltype(CT_FLOORWOOD, "wood floor", '.', C_DARKBROWN, NA, B_EMPTY, B_TRANS, MT_WOOD, 0, -1, 1, B_NOABSORB); addcelltype(CT_FLOORFLESH, "flesh floor", '.', C_FLESH, NA, B_EMPTY, B_TRANS, MT_FLESH, 0, -1, -2, B_NOABSORB); addcelltype(CT_FLOORSHOP, "shop floor", '.', C_DARKBROWN, NA, B_EMPTY, B_TRANS, MT_WOOD, 0, -1, 0, B_NOABSORB); addcelltype(CT_FLOORSNOW, "snow", '.', C_WHITE, NA, B_EMPTY, B_TRANS, MT_SNOW, 0, -1, 0, B_ABSORB); addcelltype(CT_FLOORTILE, "tiled floor", '.', C_CYAN, C_WHITE, B_EMPTY, B_TRANS, MT_METAL, 0, -1, 2, B_NOABSORB); addcelltype(CT_GRASS, "grass", '.', C_GREEN, NA, B_EMPTY, B_TRANS, MT_PLANT, 0, -1, -1, B_ABSORB); addcelltype(CT_DIRT, "dirt", '.', C_DARKYELLOW, C_BROWN, B_EMPTY, B_TRANS, MT_STONE, 0, -1, -1, B_ABSORB); addcelltype(CT_LOWFLOOR, "low rock floor", '.', C_GREY, NA, B_EMPTY, B_TRANS, MT_STONE, -1, -1, 0, B_NOABSORB); addcelltype(CT_VLOWFLOOR, "very low rock floor", '.', C_GREY, NA, B_EMPTY, B_TRANS, MT_STONE, -2, -1, 0, B_NOABSORB); // region types // name, pluralname?, defaulthab, maxdepth stairs stair major? depthmod inherit_parent_depth? // perlev dir addbranch(BH_BABAYAGAHUT, "Baba Yaga's Hut", B_FALSE, H_BYHUT, 0, 0, D_NONE, B_FALSE, 0, B_FALSE); addbranch(BH_WORLDMAP, "The Surface", B_FALSE, H_FOREST, 10, 0, D_NONE, B_TRUE, 20, B_FALSE); addbranch(BH_HEAVEN, "The Realm of Gods", B_FALSE, H_HEAVEN, 1, 0, D_NONE, B_FALSE, 0, B_FALSE); // main branches addbranch(BH_MAINDUNGEON, "The Main Dungeon", B_FALSE, H_DUNGEON, 25, 3, D_DOWN, B_TRUE, 0, B_FALSE); addbranch(BH_CAVE, "The Goblin Caves", B_TRUE, H_CAVE, 3, 1, D_DOWN, B_TRUE, 2, B_FALSE); addbranch(BH_WOODS, "The Sylvan Woods", B_TRUE, H_FOREST, 3, 3, D_DOWN, B_TRUE, 1, B_FALSE); addbranch(BH_MASTERVAULTS, "The Master Vaults", B_TRUE, H_MASTERVAULTS, 3, 1, D_DOWN, B_TRUE, 5, B_TRUE); addbranch(BH_ICECAVERNS, "The Ice Caverns", B_TRUE, H_ICECAVE, 1, 1, D_DOWN, B_TRUE, 10, B_FALSE); // minor branches addbranch(BH_PIT, "A Pit", B_FALSE, H_PIT, 1, 1, D_DOWN, B_FALSE, 0, B_TRUE); addbranch(BH_SEWER, "A Sewer", B_FALSE, H_SEWER, 1, 0, D_NONE, B_FALSE, 2, B_TRUE); addbranch(BH_STOMACH, "A Stomach", B_FALSE, H_STOMACH, 1, 0, D_NONE, B_FALSE, 0, B_FALSE); // special } void initmaplayout(void) { //int vx[4],vy[4]; int i; int lastlevel; branch_t *rt; race_t *r; flagpile_t *uniquelfs = NULL; flag_t *f; // MAPMAPMAPMAP // make a list of unique monsters which will appear uniquelfs = addflagpile(NULL, NULL); for (r = firstrace ; r ; r = r->next) { f = hasflag(r->flags, F_UNIQUE); if (f && (f->val[0] > 0) && pctchance(f->val[0])) { addflag(uniquelfs, f->id, f->val[0], f->val[1], r->id, f->text); } } rt = findbranch(BH_MAINDUNGEON); lastlevel = rt->maxdepth; // region definitions (outlines) addregionoutline(BH_WORLDMAP); // link to first dungeon addregionthing(lastregionoutline, NA, 0, 0, RT_BRANCHLINK, BH_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; */ // *************** MAP OUTLINE *************** addregionoutline(BH_MAINDUNGEON); addregionthing(lastregionoutline, 1, NA, NA, RT_RNDVAULTWITHFLAG, F_VAULTISPLAYERSTART, NULL); // l2-4: sylvan woods addregionthing(lastregionoutline, rnd(2,4), NA, NA, RT_BRANCHLINK, BH_WOODS, "hollow tree leading down"); // l2-5: goblin caves addregionthing(lastregionoutline, rnd(2,4), NA, NA, RT_BRANCHLINK, BH_CAVE, "tunnel leading down"); // l3-7: ice caverns addregionthing(lastregionoutline, rnd(3,7), NA, NA, RT_BRANCHLINK, BH_ICECAVERNS, "icy passage leading down"); // l6: jimbo's lair addregionthing(lastregionoutline, 6, NA, NA, RT_VAULT, NA, "jimbos_lair"); // l7 - 10: ants nest i = rnd(7,10); addregionthing(lastregionoutline, i, NA, NA, RT_HABITAT, H_ANTNEST, NULL); addregionthing(lastregionoutline, i, NA, NA, RT_LF, NA, "queen ant"); // l11 - 14: swamp addregionthing(lastregionoutline, rnd(11,14), NA, NA, RT_HABITAT, H_SWAMP, NULL); // l25: last level addregionthing(lastregionoutline, lastlevel, NA, NA, RT_BRANCHLINK, BH_MASTERVAULTS, "metal hatch leading down"); // 1-3 fixed sewers addregionthing(lastregionoutline, rnd(1,25), NA, NA, RT_BRANCHLINK, BH_SEWER, "drainage grate"); for (i = 0; i < 2; i++) { if (onein(2)) { addregionthing(lastregionoutline, rnd(1,25), NA, NA, RT_BRANCHLINK, BH_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(BH_CAVE); addregionthing(lastregionoutline, 5, NA, NA, RT_RNDVAULTWITHTAG, NA, "caveboss"); addregionoutline(BH_WOODS); addregionthing(lastregionoutline, 5, NA, NA, RT_RNDVAULTWITHTAG, NA, "forestboss"); addregionoutline(BH_MASTERVAULTS); rt = findbranch(BH_MASTERVAULTS); addregionthing(lastregionoutline, rt->maxdepth, NA, NA, RT_RNDVAULTWITHTAG, NA, "shrine"); // godstone on last floor of master vaults. // add initial regions addregion(BH_WORLDMAP, NULL, -1, 0, -1); addregion(BH_HEAVEN, NULL, -1, 0, -1); // add remembered unique lfs for (f = uniquelfs->first ; f ; f = f->next) { regionoutline_t *ro; ro = findoutlineforbranch(f->val[1]); if (ro) { race_t *r; int wantdepth; wantdepth = parserange(f->text); r = findrace(f->val[2]); if (r) { addregionthing(ro, wantdepth, NA, NA, RT_LF, 1, r->name); } } } killflagpile(uniquelfs); } int isadjacent(cell_t *src, cell_t *dst) { if (getcelldist(src, dst) == 1) { return B_TRUE; } return B_FALSE; } int isdark(cell_t *c) { if (hasob(c->obpile, OT_MAGICDARK)) { return TRUE; } /* switch (c->lit) { case L_PERMDARK: case L_NOTLIT: return B_TRUE; default: break; } */ return B_FALSE; } int isdiggable(cell_t *c, enum OBTYPE withwhat) { objecttype_t *ot; ot = findot(withwhat); if (!ot) { // just check whether the cell material // is EVER diggable. switch (c->type->id) { case CT_WALLFLESH: case CT_WALLDIRT: case CT_WALL: case CT_WALLWOOD: // for digging down case CT_CORRIDOR: case CT_MOSSROCK: case CT_LOOPCORRIDOR: case CT_FLOORCARPET: case CT_FLOORFLESH: case CT_FLOORSNOW: case CT_GRASS: case CT_DIRT: case CT_LOWFLOOR: case CT_VLOWFLOOR: //case CT_WALLGLASS: return B_TRUE; default: break; } } else if (ot->id == OT_A_DISMANTLE) { // using dismantle ability - only man-made materials. switch (c->type->material->id) { case MT_BRICK: case MT_WOOD: return B_TRUE; default: break; } } else { flag_t *retflag[MAXCANDIDATES],*f; int nretflags,i; getflags(ot->flags, retflag, &nretflags, F_DIGCELLTYPE, F_DIGCELLMAT, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->id == F_DIGCELLTYPE) && (c->type->id == f->val[0])) { return B_TRUE; } else if ((f->id == F_DIGCELLMAT) && (c->type->material->id == f->val[0])) { return B_TRUE; } } } return B_FALSE; } // if isopen provided, returns whether or not the door is opened int isdoor(object_t *o, int *isopen) { int isdoor = B_FALSE; if (hasflag(o->flags, F_DOOR)) { isdoor = B_TRUE; } else { isdoor = B_FALSE; } if (isdoor) { if (isopen) { if (hasflag(o->flags, F_OPEN)) { *isopen = B_TRUE; } else { *isopen = B_FALSE; } } } return isdoor; } int isempty(cell_t *c) { if (!c) return B_FALSE; if (c->type->solid) return B_FALSE; if (c->lf) return B_FALSE; if (!cellwalkable(NULL, c, NULL)) return B_FALSE; return B_TRUE; } //returns TT_ based on what you can scan there int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) { flag_t *f; 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, NULL)) { // 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; } } f = lfhasflagval(player, F_CANHEARLF, c->lf->id, NA, NA, NULL); if (f) { // can hear them using listen skill? if (f->val[1] == B_TRUE) { set_scanned_glyph(TT_MONSTER, c->lf, " (heard)", desc, glyph); } else { //set_scanned_glyph(TT_MONSTER, c->lf, NULL, NULL, glyph); if (glyph) { glyph->ch = 'X'; glyph->colour = C_GREY; } if (desc) sprintf(desc, "heard: %s", f->text); } *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_LIGHTGREEN; } 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) { int amt = 0; object_t *o; //flag_t *f; /* switch (c->lit) { case L_TEMP: case L_PERMLIGHT: return B_TRUE; default: break; } */ for (o = c->obpile->first ; o ; o = o->next) { if (o->type->id == OT_MAGICDARK) { return 0; } amt += obproduceslight(o); /* f = hasflag(o->flags, F_PRODUCESLIGHT); if (f) { amt += f->val[0]; } */ } if (c->lf) { amt += lfproduceslight(c->lf, NULL); /* for (o = c->lf->pack->first ; o ; o = o->next) { f = hasflag(o->flags, F_PRODUCESLIGHT); if (f) { amt += f->val[0]; } } */ } return amt; } 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; } cell_t *getroommidcell(map_t *m, int roomid) { int midx,midy; cell_t *c; midx = m->room[roomid].x1 + ((m->room[roomid].x2 - m->room[roomid].x1)/2); midy = m->room[roomid].y1 + ((m->room[roomid].y2 - m->room[roomid].y1)/2); c = getcellat(m, midx, midy); assert(c); return c; } void gettemprange(enum TEMPERATURE temp, int *min, int *max) { if (min) *min = -1; if (max) *max = -1; switch (temp) { case T_VCOLD: if (min) *min = MIN_TEMPERATURE; if (max) *max = 0; break; case T_COLD: if (min) *min = 1; if (max) *max = 11; break; case T_CHILLY: if (min) *min = 12; if (max) *max = 18; break; case T_NORMAL: if (min) *min = 19; if (max) *max = 22; break; case T_WARM: if (min) *min = 23; if (max) *max = 29; break; case T_HOT: if (min) *min = 30; if (max) *max = 35; break; case T_VHOT: if (min) *min = 36; if (max) *max = MAX_TEMPERATURE; break; } } enum TEMPERATURE gettempbracket(int temp) { enum TEMPERATURE brack; for (brack = T_VCOLD; brack <= T_VHOT; brack++) { int min,max; gettemprange(brack, &min, &max); if ((temp >= min) && (temp <= max)) return brack; // outside of normal range... if ((brack == T_VCOLD) && (temp < min)) { return brack; } else if ((brack == T_VHOT) && (temp > max)) { return brack; } } // default return T_NORMAL; } enum TIMEPHASE gettimephase(void) { int h,m,s; splittime(&h, &m, &s); if (h == 6) { return TP_SUNRISE; } else if ((h >= 7) && (h <= 9)) { return TP_MORNING; } else if ((h >= 10) && (h < 12)) { return TP_MORNINGLATE; } else if (h == 12) { return TP_NOON; } else if ((h > 12) && (h < 17)) { return TP_AFTERNOON; } else if (h == 17) { return TP_DUSK; } else if (h == 18) { return TP_SUNSET; } else if ((h >= 19) && (h < 22)) { return TP_EVENING; } else if ((h >= 22) && (h <= 23)) { return TP_NIGHT; } else if (h == 0) { return TP_MIDNIGHT; } else if ((h >= 1) && (h < 4)) { return TP_NIGHTLATE; } else if (h == 4) { return TP_DAWN; } // ie. h == 5 return TP_TWILIGHTMORN; } int isnighttime(void) { switch (gettimephase()) { case TP_NIGHT: case TP_MIDNIGHT: case TP_NIGHTLATE: return B_TRUE; default: break; } return B_FALSE; } int isdaytime(void) { switch (gettimephase()) { case TP_MORNING: case TP_MORNINGLATE: case TP_NOON: case TP_AFTERNOON: return B_TRUE; default: break; } 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 == BH_WORLDMAP) { return B_TRUE; } return B_FALSE; } int isroom(cell_t *c) { if (c && (getroomid(c) >= 0)) { return B_TRUE; } return B_FALSE; } int issolid(cell_t *c) { if (!c || c->type->solid) { 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)) { condset_t cs; // this will automatically avoid lifeforms since we're using // we_walkable rather than we_notwall. this saves problems // with someine coming up underneath you! initcondv(&cs, CC_HASLF, B_FALSE, NA, CC_HASOBTYPE, B_FALSE, ot->id, CC_NONE); c2 = real_getrandomadjcell(c2, &cs, B_ALLOWEXPAND, LOF_DONTNEED, NULL, 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 *linkportaltodepth(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 = addobfast(newcell->obpile, srcportal->type->id); assert(dstportal); linkportals(srcportal,dstportal); 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) { int db = B_TRUE; map_t *othermap; map_t *stairmap; cell_t *staircell; flag_t *f; assert(o); // make sure these stairs aren't already linked. assert(!hasflag(o->flags, F_MAPLINK)); staircell = getoblocation(o); stairmap = staircell->map; if (o2) { cell_t *othercell; objecttype_t *ot; othercell = getoblocation(o2); othermap = othercell->map; if (!db) dblog("linkstairs(): was given specific link partner for '%s' (partner = %s)", o->type->name,o2->type->name); ot = getoppositestairs(o->type); if (ot && (ot->id != o2->type->id)) { msg("Hmmm... %s <-> %s = invalid stair link?",o->type->name,o2->type->name); more(); } } else { objecttype_t *otherstairtype; cell_t *c2; int n, dir; object_t *oo = NULL; if (!db) dblog("linkstairs(): trying to find potential link partner for '%s'", o->type->name); // 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) { if (db) dblog("linkstairs(): possibility: '%s'", oo->type->name); // remember all stairs of correct type, for debugging. poss[nposs++] = oo; // does it go nowhere? if (!hasflag(oo->flags, F_MAPLINK)) { if (!db) dblog("linkstairs(): this possibility not linked. using it."); 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; } if (db) { dblog("linkstairs() - linked '%s' to '%s'.", o->type->name, o2->type->name); } } 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)); assert(!cell->type->solid); strcpy(doorbuf, ""); // in master vaults, doors are always locked. if (m->habitat->id == H_MASTERVAULTS) { strcpy(doorbuf, "locked "); } if ((rnd(1,100) + m->depth) >= 66) { strcat(doorbuf, "iron door"); } else { strcat(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 (o->type->id == OT_LEAF) { raise(SIGINT); } 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 makelit(cell_t *c, enum OBTYPE how, int howlong, int power) { object_t *o; o = addobfast(c->obpile, how); if (o) { flag_t *f; if (howlong != PERMENANT) { addflag(o->flags, F_OBHP, howlong, howlong, NA, NULL); addflag(o->flags, F_OBHPDRAIN, 1, DT_DIRECT, NA, NULL); } f = hasflag(o->flags, F_PRODUCESLIGHT); if (f) { f->val[0] = power; } } } void makelitradius(cell_t *c, int radius, enum OBTYPE how, int howlong, int power) { int (*distfunc)(cell_t *, cell_t *); cell_t *retcell[MAXRETCELLS]; int nretcells,i; if (radius <= 0) return; if (radius == 1) { distfunc = getcelldist; } else { distfunc = getcelldistorth; } getradiuscells(c, radius, DT_ORTH, B_FALSE, LOF_NEED, B_TRUE, retcell, &nretcells, 0); for (i = 0; i < nretcells; i++) { if (cellhaslos(c,retcell[i])) { makelit(retcell[i], how,howlong, power); } } /* 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 && (getroomid(c) == r->id)) { 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 && (getroomid(c) == r->id)) { 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 && (getroomid(c) == r->id)) { 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 && (getroomid(c) == r->id)) { 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, B_FALSE); } } } /* // 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); } */ } // if you've been here before... if (m->lastplayervisit != -1) { int nturns; int dowandering = B_FALSE; enteringmap = B_TRUE; // monsters on the new level now get a bunch of turns to simulate them moving about when the player wasn't there. nturns = (curtime - m->lastplayervisit) / TICK_INTERVAL; if (nturns >= 50) { dowandering = B_TRUE; } limit(&nturns, NA, 20); //nturns *= countlfs(m); for (i = 0; i < nturns; i++) { donextturn(m); } // (depth*5)% chance of a wandering monster in each room, as long as the // chosen cell is not near stairs if (dowandering) { int monchance; condset_t cs; monchance = getmapdifficulty(m)*5; for (i = 0; i < m->nrooms; i++) { if (pctchance(monchance)) { initcondv(&cs, CC_HASROOMID, B_TRUE, m->room[i].id, CC_WALKABLE, B_TRUE, NA, CC_NONE); //c = getrandomroomcell(m, m->room[i].id, WE_WALKABLE); c = getcell_cond(m, &cs); if (c && !hasobflagwithin(c, F_STAIRS, 15, DT_COMPASS)) { addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, B_ALLOWEXTRA, NULL); } } } } enteringmap = B_FALSE; } } void modcelltemp(cell_t *c, int howmuch) { c->temperature += howmuch; } void modillumination(map_t *m, int dir) { int donesomething = B_FALSE; if (dir > 0) { if (m->illumination < IL_FULLDARK) { m->illumination++; donesomething = B_TRUE; } } else if (dir < 0) { if (m->illumination > IL_FULLLIT) { m->illumination--; donesomething = B_TRUE; } } if (donesomething) { lifeform_t *lf; for (lf = m->lf ; lf ; lf = lf->next) { setlosdirty(lf); } } } 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, condset_t *cs, enum JOB *wantjob, enum BEHAVIOUR *wantbehaviour) { int donesomething; char *p,*suff; job_t *j; char named[BUFLEN],with[BUFLEN]; char *localname; char *namestart; behaviour_t *b; assert(cs); initcond(cs); // 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, "single ")) { p += strlen("single "); // only ONE monster - no groups/minions. addcond(cs, CC_HASFLAG, B_FALSE, F_MINIONS); addcond(cs, CC_HASFLAG, B_FALSE, F_NUMAPPEAR); donesomething = B_TRUE; } if (strstarts(p, "sleeping ")) { p += strlen("sleeping "); if (wantflags) addflag(wantflags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL); donesomething = B_TRUE; } if (strstarts(p, "hirable ")) { p += strlen("hirable "); addcond(cs, CC_HASFLAG, B_TRUE, F_PLAYABLE); 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 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 = strstr(localname, jobname); if (ep) { // got it if (wantjob) *wantjob = j->id; // now strip the suffix off, starting at the space before it //*ep = '\0'; strcpy(ep, ep+1+strlen(j->name)); break; } } // try removing suffixes for "named xxx" strcpy(named, ""); suff = strstr(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 // strip it off strcpy(suff, suffp); } if (strlen(named)) { if (wantflags) addflag(wantflags, F_NAMED, NA, NA, NA, named); donesomething = B_TRUE; } // try removing suffixes for "with 'xxx'" strcpy(with, ""); suff = strstr(localname, " with '"); if (suff) { char *suffp; char *p2; // extract the name p2 = with; suffp = suff + strlen(" with '"); while (*suffp && (*suffp != '\'')) { *p2 = *suffp; suffp++; p2++; } *p2 = '\0'; // nul-terminate name //*suff = '\0'; // strip off suffix from main string // strip it off strcpy(suff, suffp); } if (strlen(with)) { if (wantflags) addflag(wantflags, F_STARTOB, 100, NA, NA, with); donesomething = B_TRUE; } // try removing suffixes for "with xxx" strcpy(with, ""); suff = strstr(localname, " with "); if (suff) { char *suffp; char *p2; // extract the name p2 = with; suffp = suff + strlen(" with "); while (*suffp && (*suffp != ' ')) { *p2 = *suffp; suffp++; p2++; } *p2 = '\0'; // nul-terminate name //*suff = '\0'; // strip off suffix from main string // strip it off strcpy(suff, suffp); } if (strlen(with)) { if (wantflags) addflag(wantflags, F_STARTOB, 100, NA, NA, with); donesomething = B_TRUE; } // now get raceid if (streq(p, "random")) { free(localname); return R_RANDOM; } else if (strstarts(p, "random ")) { race_t *baser,*r; // ie. "random xxx_baseraceid_xxx" p += strlen("random "); baser = findracebyname(p, NULL); 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) { if (racemeets(r->id, cs)) { poss[nposs++] = r->id; if (nposs == MAXCANDIDATES) break; } } } if (nposs) { free(localname); return poss[rnd(0,nposs-1)]; } } } else { race_t *r; r = findracebyname(p, cs); if (r) { free(localname); return r->id; } } free(localname); 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++) { int thiscount = 0; for (n = 0; n < m->w * m->h; n++) { cell_t *c; c = m->cell[n]; if (!c->room) { int exits; exits = countcellexits(c, DT_ORTH); //if ((countcellexits(c, DT_ORTH) == 1) || // (countcellexits(c, DT_COMPASS) == 1)) { if (exits == 1) { // erase this cell clearcell(c); setcelltype(c, solidcell); setcellreason(c, "dead-end removed."); count++; thiscount++; } else if (exits == 0) { // erase this cell clearcell(c); setcelltype(c, solidcell); setcellreason(c, "zero-exit deadend removed."); count++; thiscount++; } } } if (!thiscount) break; } return count; } // returns # doors removed int remove_baddoors(map_t *m) { int x,y,num=0; object_t *o,*nexto; cell_t *c; // remove useless doors. 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 = nexto) { nexto = o->next; //if (!getcellvault(c)) { if (!cellisfixedvaultwall(c)) { // remove doors which go nowhere (internal to rooms) if (isdoor(o, NULL)) { if (!doorisvalid(o)) { num++; dblog("removed useless door at %d,%d", c->x, c->y); killob(o); // no other obs here? if (!countobs(c->obpile, B_FALSE)) { setcelltype(c, getcellsolid(c)); } continue; } } } } } } return num; } int remove_smallrooms(map_t *m) { int x,y,nremoved = 0; cell_t *c; for (y = 0; y < m->h - 3; y++) { for (x = 0; x < m->w - 3 ; x++) { c = getcellat(m, x, y); if (smallroomat(c)) { int xx,yy; // fill it in. for (yy = y+1; yy <= y+2; yy++) { for (xx = x+1; xx <= x+2; xx++) { c = getcellat(m, xx, yy); if (c) { clearcell(c); setcelltype(c, getmapsolid(m)); setcellreason(c, "small room at %d,%d removed.", xx,yy); } } } nremoved++; // move on. x += 4; } } } return nremoved; } void selectcelltypes(map_t *map, int solchance, int empchance, enum CELLTYPE *sol, enum CELLTYPE *emp) { *sol = CT_NONE; *emp = CT_NONE; if (map->habitat->id == H_DUNGEON) { // random chance of different wall type if (pctchance(solchance)) { switch (rnd(1,3)) { case 1: *sol = CT_WALLBRICK; break; case 2: *sol = CT_WALLMETAL; break; case 3: *sol = CT_WALLWOOD; break; } } // random chance of different floor type if (pctchance(empchance)) { switch (rnd(1,4)) { case 1: *emp = CT_FLOORCARPET; break; case 2: *emp = CT_FLOORTILE; break; case 3: *emp = CT_FLOORWOOD; break; case 4: *emp = CT_DIRT; break; } } } } 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_fake(cell_t *cell, enum CELLTYPE ctid) { celltype_t *ct; ct = findcelltype(ctid); cell->known = KG_MISC; // remember a wall. //ct = findcelltype(getmapsolid(cell->map)); cell->knownglyph = ct->glyph; adjustcellglyph(cell, &(cell->knownglyph), CA_CH); cell->knowntime = PERMENANT; } 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 = KG_MISC; // 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; } adjustcellglyph(cell, &(cell->knownglyph), CA_CH); // high cartography skill lets us remember certain objects... if (slev >= PR_EXPERT) { o = gettopobject(cell, B_TRUE); if (o) { cell->knownglyph = *(getglyph(o)); cell->known = KG_OBJECT; } } 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)); cell->known = KG_DFEATURE; } } } } if (slev >= PR_BEGINNER) { o = hasobwithflag(cell->obpile, F_CLIMBABLE); if (o) { cell->knownglyph = *(getglyph(o)); cell->known = KG_STAIRS; } } if (hasflag(player->flags, F_PHOTOMEM)) { cell->knowntime = PERMENANT; } else if (slev == PR_INEPT) { //cell->knowntime = getattr(player, A_IQ)*2; cell->knowntime = getattr(player, A_IQ)*3; } 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 setcelllocked(cell_t *c, char *why, ...) { char buf[BUFLEN]; va_list args; c->locked = B_TRUE; if (c->lockedreason) { free(c->lockedreason); c->lockedreason = NULL; } va_start(args, why); vsnprintf( buf, BUFLEN, why, args ); va_end(args); c->lockedreason = strdup(buf); } void setcellreason(cell_t *c, char *why, ...) { char buf[BUFLEN]; va_list args; if (c->reason) { free(c->reason); c->reason = NULL; } va_start(args, why); vsnprintf( buf, BUFLEN, why, args ); va_end(args); c->reason = strdup(buf); } 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) { if (cell->obpile->first) { killallobs(cell->obpile); } //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, fromlf); } // 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; } // returns TRUE if we shuffled down. int shuffledown(map_t *map) { lifeform_t *l; int firsttime; firsttime = map->lf->timespent; if (firsttime == 0) return B_FALSE; for (l = map->lf ; l ; l = l->next) { //dblog("shuffling id %d %s timespent=%d -> %d",l->id,l->race->name, l->timespent, l->timespent - firstlftime); l->timespent -= firsttime; assert(l->timespent >= 0); } return B_TRUE; } int smallroomat(cell_t *where) { map_t *m; int x,y,sx,sy; int row = 0,col=0; int gotexit = B_FALSE; int checkagainst[16] = { 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1 }; cell_t *c; m = where->map; sx = where->x; sy = where->y; for (y = sy; y <= sy+3 ; y++,row++) { for (x = sx,col=0; x < sx+3; x++,col++) { int want,matched = B_FALSE; c = getcellat(m,x,y); if (!c) return B_FALSE; want = checkagainst[row*4+col]; if ((want == 0) && !issolid(c)) { matched = B_TRUE; } else if (want == 1) { if (issolid(c)) { matched = B_TRUE; } else { // we can have one exit through the // wall if (!gotexit) { gotexit = B_TRUE; matched = B_TRUE; } } } if (!matched) return B_FALSE; } } return B_TRUE; } 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 unlinkmap='%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 unlinkmap='%s'",unlinkmap->name); return B_FALSE; } void unmakemap(map_t *map) { region_t *r,*nextr; int i; int db = B_TRUE; int nlf = 0,nreg = 0; if (db) dblog("unmaking map."); // remove map lifeforms while (map->lf) { killlf(map->lf); nlf++; } if (db) dblog(" %d lifeforms removed.", nlf); // 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 branchlinks in this map. for (r = firstregion ; r ; r = nextr ){ nextr = r->next; if (r->createdbymapid == map->id) { killregion(r); nreg++; } } if (db) dblog(" %d linked regions removed.", nreg); // free cells on this map for (i = 0; i < (map->w*map->h); i++){ killcell(&(map->cell[i])); } } 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, NULL) && !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; enum RACE rid; condset_t cs; 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_LF: rid = parserace(thing->what, NULL, &cs, NULL, NULL); if (rid == R_NONE) { dblog("Invalid lifeform '%s' specified in regionthing.", thing->what); goterrors = B_TRUE; } else if (thing->value <= 0) { dblog("Invalid lifeform count (%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_BRANCHLINK: if (!findbranch(thing->value)) { dblog("Invalid branchlink to branch %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; case RT_NONE: 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."); } }