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