#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 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 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); setcelltype(cell, cell->habitat->solidcelltype); cell->lf = NULL; cell->room = NULL; 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; return cell; } habitat_t *addhabitat(enum HABITAT id, char *name, enum CELLTYPE emptycell, enum CELLTYPE solidcell, int thingchance, int obchance, int vaultchance, int maxvisrange) { 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; 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_ORTH; 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->lit = B_TRUE; 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_NONE, paste 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; // ie. don't create mosnters on closed doors! if (!cellwalkable(NULL, c, NULL)) { return NULL; } if (rid != R_SPECIFIED) { if (rid == R_RANDOM) { r = getrandomrace(c, NA); } else { r = findrace(rid); } } else { // get params wantflags = addflagpile(NULL, NULL); rid = parserace(racename, wantflags, &wantjob); if (rid == R_RANDOM) { r = getrandomrace(c, NA); } else { r = findrace(rid); } } if (!r) { return NULL; } if (db) { char buf[BUFLEN]; snprintf(buf, BUFLEN, "addmonster for '%s'->%s",racename, r->name); dbtimestart(buf); } //if (db) dblog("adding rand lf %s to cell %d,%d",r->name,c->x,c->y); if (r) { if (db) dbtime("doing lf addition"); 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); return lf; } } lf = addlf(c, r->id, getrandommonlevel(r, c->map)); if (db) dbtime("finished lf addition"); if (lf) { flag_t *f; if (nadded) (*nadded)++; if (db) dbtime("checking for job"); lf->born = B_FALSE; if (wantjob == J_NONE) { if (jobok) { for (f = lf->flags->first ; f ; f = f->next) { // has a job? if (f->id == F_STARTJOB) { if (rnd(1,100) <= f->val[0]) { 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 (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(lf->flags, F_HIDING, 0, NA, NA, NULL); } } if (!lfhasflag(lf, F_HIDING) && !lfhasflag(lf, F_DEAF) && cansleep(lf) ) { int asleepchance = 70; 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)) { addflag(lf->flags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL); } else { // might be asleep based on time. if (issleepingtimefor(lf)) { addflag(lf->flags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL); } } } // nonhuman monsters who on dark levels can always see in the dark if (!lf->cell->map->lit && !lfhasflag(lf, F_SEEINDARK)) { if (getraceclass(lf) != RC_HUMANOID) { addflag(lf->flags, F_SEEINDARK, rnd(3,5), NA, NA, NULL); } } } // appears in groups? if (db) dbtime("handling groups"); if (autogen) { f = hasflag(lf->flags, F_NUMAPPEAR); if (f) { // override amount amt = rnd(f->val[0], f->val[1]); } if (amt > 1) { cell_t *adjcell; amt--; // we've already added one //adjcell = c; for ( ; amt > 0; amt--) { lifeform_t *newlf; // find an adjacent cell to one of the newly added monsters, // starting with the first one adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL); // did we find one? if (!adjcell) break; newlf = addlf(adjcell, r->id, getrandommonlevel(r, adjcell->map)); if (!newlf) { break; } if (nadded) (*nadded)++; newlf->born = B_FALSE; // initial monster shoudl remember its minions addflag(lf->flags, F_MINION, newlf->id, NA, NA, NULL); // if master is asleep, minions will also be asleep if (lfhasflag(lf, F_ASLEEP)) { addflag(newlf->flags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL); } if (!newlf->cell->map->lit && !lfhasflag(newlf, F_SEEINDARK)) { if (getraceclass(newlf) != RC_HUMANOID) { addflag(newlf->flags, F_SEEINDARK, rnd(3,5), NA, NA, NULL); } } // minions never have certain flags. killflagsofid(newlf->flags, F_DEMANDSBRIBE); newlf->born = B_TRUE; } } } // minons? // appears in groups? if (db) dbtime("handling minions"); if (autogen) { f = hasflag(lf->flags, F_MINIONS); if (f) { if (rnd(1,100) <= f->val[0]) { int n; cell_t *adjcell; int nminions; // override amount nminions = rnd(f->val[1], f->val[2]); for (n = 0; n < nminions; n++) { lifeform_t *newlf; race_t *newr; adjcell = real_getrandomadjcell(c, WE_WALKABLE, B_ALLOWEXPAND, LOF_WALLSTOP, NULL); if (!adjcell) break; newr = findracebyname(f->text); if (!newr) break; newlf = addlf(adjcell, newr->id, getrandommonlevel(newr, adjcell->map)); if (!newlf) break; if (nadded) (*nadded)++; newlf->born = B_FALSE; if (lfhasflag(lf, F_ASLEEP)) addflag(newlf->flags, F_ASLEEP, NA, ST_ASLEEP, NA, NULL); if (!newlf->cell->map->lit && !lfhasflag(newlf, F_SEEINDARK)) { if (getraceclass(newlf) != RC_HUMANOID) { addflag(newlf->flags, F_SEEINDARK, rnd(3,5), NA, NA, NULL); } } newlf->born = B_TRUE; } } } } if (db) dbtime("handling random objects"); // sometimes give the lf random objects (extra monsters through // 'numappears' don't get them. if (lfhasflag(lf, F_HUMANOID) && !lfhasflag(lf, F_NOPACK)) { if (rnd(1,3) == 1) { int nobs = rnd(1,3); char buf[BUFLEN]; int i; for (i = 0; i < nobs; i++) { if (getrandomob(c->map, buf)) { object_t *o; o = addob(lf->pack, buf); if (o && !canpickup(lf, o, o->amt)) { killob(o); } } } } } if (db) dbtime("giving torches"); // humanoids on dark levels which can't see will probably have some // kind of light producing device if (!islit(c) && !hasflag(lf->flags, F_SEEINDARK) && !hasflag(lf->flags, F_TREMORSENSE)) { if (lfhasflag(lf, F_HUMANOID)) { objecttype_t *ot, *poss[MAXCANDIDATES]; int nposs = 0; // get a random light-producing object for (ot = objecttype ; ot ; ot = ot->next) { if (hasflag(ot->flags, F_OPERABLE) && hasflagval(ot->flags, F_ACTIVATECONFER, F_PRODUCESLIGHT, NA, NA, NULL)) { poss[nposs++] = ot; } } if (nposs > 0) { ot = poss[rnd(0,nposs-1)]; } else { ot = NULL; } if (ot) { object_t *o; o = addobfast(lf->pack, ot->id); if (o) turnon(NULL, o); } } } if (wantflags) { // wantflags? copyflags(lf->flags, wantflags, NA); } lf->born = B_TRUE; } // end if lf } // free mem if (wantflags) { killflagpile(wantflags); } 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, 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 o = addrandomob(c); if (o) { if (nadded) *nadded = o->amt; rv = TT_OBJECT; } } else { // monster if (addmonster(c, R_RANDOM, NULL, B_TRUE, 1, B_TRUE, nadded)) { rv = TT_MONSTER; } } 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) { region_t *a; regionoutline_t *ro,*poss[MAXCANDIDATES]; int nposs = 0; int id; // 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; 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; // 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 if (a == firstregionoutline) { a->id = 0; } else { a->id = lastregionoutline->id + 1; } 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->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, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major) { 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->defaulthabitat = defaulthabitat; a->maxdepth = maxdepth; a->stairsperlev = stairsperlev; a->deeperdir = deeperdir; a->majorbranch = major; 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_DARKGREY) 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 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], cell[i]->habitat->emptycelltype); 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]; 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], poss[sel]->habitat->emptycelltype); addflag(map->flags, F_ROOMEXIT, roomid, poss[sel]->x, poss[sel]->y, "from autodoors, potential location"); } 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], cell[sel]->habitat->emptycelltype); addflag(map->flags, F_ROOMEXIT, roomid, cell[sel]->x, cell[sel]->y, "from autodoors, forced at end"); doorsadded++; } } } } if (db) dblog("autodoors() added %d doors to roomid %d", doorsadded, roomid); return doorsadded; } int cellhaslos(cell_t *c1, cell_t *dest) { int deltax, deltay; int numpixels; int d; int dinc1,dinc2,xinc1,xinc2,yinc1,yinc2; int xinc,yinc,dinc; int i; int x1,y1; int x; int y; //int wentuphill = B_FALSE; //int origheight; //int shopwall; int x2,y2; map_t *map; if (!dest) return B_FALSE; // let the player see when dead, otherwise the screen wil // go black when "You die" appears. map = c1->map; x1 = c1->x; y1 = c1->y; x2 = dest->x; y2 = dest->y; deltax = (x2 - x1); if (deltax < 0) deltax = -deltax; deltay = (y2 - y1); if (deltay < 0) deltay = -deltay; // can always see your own cell if ((deltax == 0) && (deltay == 0)) { return B_TRUE; } if (deltax >= deltay) { numpixels = deltax + 1; d = (deltay*2) - deltax; dinc1 = deltay << 1; dinc2 = (deltay-deltax) << 1; xinc1 = 1; xinc2 = 1; yinc1 = 0; yinc2 = 1; } else { numpixels = deltay + 1; d = (deltax*2) - deltay; dinc1 = deltax << 1; dinc2 = (deltax - deltay) << 1; xinc1 = 0; xinc2 = 1; yinc1 = 1; yinc2 = 1; } if (x1 > x2) { xinc1 = - xinc1; xinc2 = - xinc2; } if (y1 > y2) { yinc1 = - yinc1; yinc2 = - yinc2; } x = x1; y = y1; for (i = 0; i < numpixels ; i++) { cell_t *cell; // don't need to move out of the last one if ((x == x2) && (y == y2)) { break; } if (d < 0) { xinc = xinc1; yinc = yinc1; dinc = dinc1; } else { xinc = xinc2; yinc = yinc2; dinc = dinc2; } // you can always see your own cell if (i != 0) { // solid cells stop los - but if you are standing on a solid // cell you can still see out. cell = getcellat(map, x, y); if (!cell->type->transparent) { return B_FALSE; } /* // check for smoke if ((x != x1) || (y != y1)) { // if not in first cell if (hasopaqueobject(viewer, x,y,z) && (getheight(x,y,z) >= origheight)) { if (!hasproplf(viewer, P_SEEINSMOKE)) { return B_FALSE; } } } */ // check for objects which block view if (hasobwithflag(cell->obpile, F_BLOCKSVIEW)) { return B_FALSE; } } // move to next cell d += dinc; x += xinc; y += yinc; } // made it to the target cell! return B_TRUE; } 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; } } // 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; 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++; } } } } 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); linkexit(m->cell[i], B_TRUE, &nadded); 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_WAX: diff = 10; break; case MT_WOOD: diff = 8; break; case MT_PLANT: diff = 8; break; default: diff = 12; break; } 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_DARKGREY instead. g->colour = C_BLUE; } break; } } } 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; } } 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 (hasflag(o->flags, F_SECRET) && !isdoor(o, NULL)) { } 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 (hasflag(o->flags, F_SECRET)) { } 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; // 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->lit && (c->lit != L_PERMDARK)) { makelit(c, L_PERMLIGHT, -1); } } // TODO: has dark producing lf? // TODO: has dark producing object? // has lightproducing lf? (ie.hasflag f_produceslight) if (c->lf) { radius = lfproduceslight(c->lf); if (radius) { makelitradius(c, radius, L_TEMP, -1); } } // has light-producing object on ground? for (o = c->obpile->first ; o ; o = o->next) { radius = obproduceslight(o); if (radius) { makelitradius(c, radius, L_TEMP, -1); } } } } } // did any lit values within player's los change? if (gamemode == GM_GAMESTARTED) { int dolos = B_FALSE; 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 vault 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; } // 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; } } 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) { bestscore = score; foundvalid = B_TRUE; } } else { score = 9999; } } coordscore[i] = score; if (db) dblog("cell %d,%d - score %d",x,y,score); } if (foundvalid) { // now go through and make a list of all BEST positions nposs = 0; for (i = 0; i < ncoords; i++) { if (coordscore[i] == bestscore) { poss[nposs++] = coord[i]; } } } else { nposs = 0; } if (nposs == 0) { *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 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; } // void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int wantrooms = B_TRUE; int d; int x,y; int i; int done,unused; int dir; int lastdir; int numrooms = 0; cell_t *cell, *c; object_t *o; int db = B_FALSE; // parameters int turnpct = DEF_TURNPCT; int sparseness = DEF_SPARSENESS; int looppct = DEF_LOOPPCT; int minrooms = MINROOMS; int maxrooms = MAXROOMS; int moved = 0; enum CELLTYPE emptycell,solidcell; // 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 <= 5) { map->lit = B_TRUE; } else { int chance; chance = (depth - 5) * 10; if (pctchance(chance)) { map->lit = B_FALSE; } else { map->lit = B_TRUE; } } // what kind of cells will 'empty' ones be? emptycell = map->habitat->emptycelltype; solidcell = map->habitat->solidcelltype; // pick initial random spot cell = getrandomcell(map); setcelltype(cell, emptycell); cell->visited = B_TRUE; //if (db) printf("- Starting (%d,%d)\n",cell->x, cell->y); lastdir = D_UNKNOWN; done = B_FALSE; dir = D_NONE; while (!done) { // get random direction based on turnpct dir = D_NONE; while (dir == D_NONE) { int badcount; //if (db) printf("- At (%d,%d), moved %d, finding new direction...\n",cell->x, cell->y, moved); dir = getnewdigdir(cell, lastdir, (moved < 2) ? 0 : turnpct, &moved); badcount = 0; while (dir == D_NONE) { badcount++; if (badcount > 10) { // finish! done = B_TRUE; break; } // pick new EMPTY random spot cell = getrandomcell(map); while (!isempty(cell)) { cell = getrandomcell(map); } //if (db) printf("--- Couldn't find a valid direction. Jumped to (%d,%d).\n",cell->x, cell->y); // pick a new random dir dir = getnewdigdir(cell, lastdir, turnpct, &moved); } if (!done) { //if (db) printf("- Digging %s from (%d,%d).\n",getdirname(dir),cell->x, cell->y); } } if (!done) { // move to adjacent cell in the given direction cell = getcellindir(cell, dir); //if (db) printf("- Now at (%d,%d)\n",cell->x, cell->y); moved++; // blank it setcelltype(cell,emptycell); cell->visited = B_TRUE; // mark surrounding cells as visited for (d = DC_N; d < MAXDIR_COMPASS; d++) { cell_t *thiscell; thiscell = getcellindir(cell, d); if (thiscell) { //if (db) printf("* Marking surrounding cell in dir %d (%d,%d) as visited.\n",d, thiscell->x, thiscell->y); thiscell->visited = B_TRUE; } } // remember last direction lastdir = dir; // check if we have visited all valid cells unused = 0; for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { cell_t *thiscell; thiscell = getcellat(map, x, y); if (!thiscell->visited) { unused++; } } } if (!unused) { done = B_TRUE; } } //printf("%d unused cell(s)\n",unused); //dumpmap(map); //getchar(); } // use sparseness to cut down dead ends remove_deadends(map, sparseness); // introduce loops for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { cell = getcellat(map, x, y); if (!cell->type->solid && countcellexits(cell, 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(cell, D_UNKNOWN,100, &moved); if (dir == D_NONE) { // can't make a loop from here. loopok = B_FALSE; } else { int tries = 0; // if we go this dir, will we hit a // corridor? while (!isloopdirok(cell, dir)) { tries++; // tried every direction? if (tries >= MAXDIR_ORTH) { loopok = B_FALSE; break; } // turn... if (++dir >= MAXDIR_ORTH) { dir = 0; } } } if (loopok) { // keep digging until we hit another corridor while (!connected) { cell_t *newcell; // find adjacent cell in the given direction newcell = getcellindir(cell, dir); if (!newcell) { connected = B_TRUE; } else { // have we hit another corridor yet? if (!newcell->type->solid) { connected = B_TRUE; } else { // blank adjacent cell setcelltype(newcell, emptycell); newcell->visited = B_TRUE; } cell = newcell; } } } } } } } // 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 createroom(map, map->nrooms, NA, NA, DEF_VAULTMARGIN, DEF_VAULTMARGIN, NULL, NULL, NULL, NULL, 50, B_FALSE); //roomvault[i] = B_FALSE; } } // 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 staircases. // first dungeon level has 1 up stairs, 3 down. // subsequent levels always have 3 up and down stairs // UP STAIRS if (map->depth == 1) { flag_t *f; // first region level. just one exit stairs c = NULL; while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { c = getrandomroomcell(map, ANYROOM); } o = addobfast(c->obpile, OT_STAIRSUP); // 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_FIRSTDUNGEON) { 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, map->habitat->emptycelltype); } } } else { for (i = 0; i < map->region->rtype->stairsperlev; i++) { c = NULL; while (!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, OT_STAIRSUP); linkstairs(o, NULL); } } // make sure we have at least one up stairs assert(findobinmap(map, OT_STAIRSUP)); // DOWN STAIRS if (map->depth < map->region->rtype->maxdepth) { for (i = 0; i < map->region->rtype->stairsperlev; i++) { c = NULL; while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { c = getrandomroomcell(map, ANYROOM); } o = addobfast(c->obpile, OT_STAIRSDOWN); linkstairs(o, NULL); } } // 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); cell_t *c; c = getrandomroomcell(map, i); if (c && isempty(c) && !countobs(c->obpile, B_TRUE)) { setcelltype(cell, CT_WALL); } } } 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; cell_t *c; 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); // s c = getcellat(map, x, map->h-1); clearcell(c); setcelltype(c,solidcell); } for (y = 1; y < map->h-1; y++) { // w c = getcellat(map, 0, y); clearcell(c); setcelltype(c,solidcell); // e c = getcellat(map, map->w-1, y); clearcell(c); setcelltype(c,solidcell); } } 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); setcelltype(cell, CT_FAKE); cell->lf = NULL; cell->map = map; cell->obpile = addobpile(NULL, NULL, NULL); } 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 = map->habitat->emptycelltype; 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_FOREST: createforest(map, depth, parentmap, exitdir, entryob, rnd(0,5)); break; case H_SWAMP: createswamp(map, depth, parentmap, exitdir, entryob); 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; default: dblog("ERROR - createhabitat with invalid habitat!"); msg("ERROR - createhabitat with invalid habitat!"); } } void createheaven(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int x,y; enum CELLTYPE emptycell; cell_t *c; race_t *r; lifeform_t *lf; //object_t *o; // what kind of cells will 'empty' ones be? emptycell = map->habitat->emptycelltype; // fill entire maze with corridors for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { c = addcell(map, x, y); setcelltype(c, emptycell); } } // add one of each god. for (r = firstrace ; r ; r = r->next) { if (r->raceclass->id == RC_GOD) { // find a position c = getrandomcell(map); while (!cellwalkable(NULL, c, NULL)) { c = getrandomcell(map); } // 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); exit(1); } } } } /* 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; int db = B_TRUE; // 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, B_FALSE); 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; } // 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); } } } // did we come from a previous map in the same region? if (parentmap) { 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; map->nextmap[diropposite(exitdir)] = parentmap->id; } } /* 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); } } // we now have the map name! getregionname(buf2, map, B_TRUE); 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 == getmapdifficulty(map)) { 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: // 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); failed = B_TRUE; while (failed) { failed = B_FALSE; // build it... if (db) dblog(" creating map habitat."); createhabitat(map, depth, parentmap, exitdir, entryob); // add home objects if (db) dblog(" adding home objects."); for (lf = map->lf ; lf ; lf = lf->next) { addhomeobs(lf); } // 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); 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; } } // ensure there are no unreachable areas if (!failed) { if (fix_reachability(map)) { failed = B_TRUE; } } if (failed) { dblog("********* got errors - restarting map creation. *********"); unlinkstairsto(map); } } // 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 } } } // try to join up any unlinked staircases in this map. if (db) dblog(" joining unlinked stairs..."); for (y = 0; y < map->h; y++) { for (x = 0; x < map->w; x++) { cell_t *c; object_t *o; c = getcellat(map, x, y); o = hasobwithflag(c->obpile, F_CLIMBABLE); if (o && !getstairdestination(o, NULL)) { // 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); } } else { if (db) { dblog(" FAILED to link stiars: '%s'",o->type->name); } } } } } // 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); } // add random objects and monsters 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); // no random obs in vaults if (c && isempty(c) && !getcellvault(c)) { if (rnd(1,100) <= c->habitat->randthingpct) { addrandomthing(c, c->habitat->randobpct, NULL); } } } } // 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); } } } } map->beingcreated = B_FALSE; if (db) dblog(" Map creation finished."); } 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 (createroom(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 : cell->habitat->emptycelltype); // set roomid cell->room = thisroom; // add objects addvaultcellcontents(cell, v, x-minx,y-miny, rotation); } } } 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 killfakes(map_t *map, cell_t *cell) { killobpile(cell->obpile); free(map->name); } // link a single cell up to the rest of the map. // make sure it links to an empty cell of a DIFFERENT roomid. // if 'wantfilled' is set, only link to "filled" cells. // return TRUE on failure. int linkexit(cell_t *startcell, int wantfilled, int *ncellsadded) { int db = B_TRUE; int d, roomid; 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; if (ncellsadded) *ncellsadded = 0; if (db) dblog(" calling linkexit() for cell at %d,%d", startcell->x, startcell->y); roomid = getroomid(startcell); for (d = D_N; d <= D_W; d++) { hitsedge[d] = B_TRUE; directendcell[d] = NULL; } // link it. 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 = D_N; d <= D_W; d++) { dist[d] = 0; hitsedge[d] = B_FALSE; sameroom[d] = B_FALSE; c = getcellindir(startcell, d); while (c) { dist[d]++; if ((roomid >= 0) && getroomid(c) == roomid) { // same room //if (wantfilled && c->type->solid) { if (c->type->solid) { // EXCEPTION: // if startcell is a cell _inside_ the room as opposed to // a cell inside the room's walls. // in this case, we ARE allowed to travel through the room's walls. } else { // mark dir as invalid dist[d] = 999; sameroom[d] = B_TRUE; if (db) dblog(" going %s hits same room. invalid.", getdirname(d)); break; } } else if (cellwalkable(NULL, c, NULL)) { if (!wantfilled || c->filled) { // walkable and not in this vault. finished. directendcell[d] = c; if (db) dblog(" can make %s path (hits empty cell at dist %d)", getdirname(d), dist[d]); break; } } else { 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) { if (((roomid == -1 ) || (getroomid(pcell) != roomid)) && cellwalkable(NULL, pcell, NULL)) { if (!wantfilled || c->filled) { // 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."); // starting at the LONGEST distance, traverse up each dir, // branching off looking for rooms. // find longest distance that doesn't go through same room for (d = D_N; d <= D_W; d++) { if (!sameroom[d] && (dist[d] > maxdist2)) { 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)]; } if (wantfilled) { // if startdir is D_NONE here, it means that we've called // linkexit for a cell internal to a room. // if we called this function from fix_reachable (ie. wantfilled=true) // then this is a bad thing. 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 door for rooms for (n = 0; n <= 1; n++) { int turndist = 0; c2 = getcellindir(c, perpdir[n]); while (c2) { int gotsolution = B_FALSE; turndist++; perpcell[nperpcells] = c2; // this will be used if we need to make 2 turns perpturncell1[nperpcells] = c; perpturndir1[nperpcells] = perpdir[n]; nperpcells++; if ((roomid >= 0) && (getroomid(c2) == roomid)) { if (wantfilled && c2->type->solid) { // see EXCEPTION above. } else { // hits same room, not ok. break; } } else if (cellwalkable(NULL, c2, NULL)) { if (!wantfilled || c2->filled) { if (db) dblog(" Got to an empty cell here."); 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) { if ( ((roomid == -1) || (getroomid(pcell) != roomid)) && cellwalkable(NULL, pcell, NULL)) { if (!wantfilled || pcell->filled) { // 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) { setcelltype(c, c->habitat->emptycelltype); if (ncellsadded) (*ncellsadded)++; c = getcellindir(c, startdir); } // clear the corner cell setcelltype(c, c->habitat->emptycelltype); 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) { setcelltype(c, c->habitat->emptycelltype); 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; turndist++; if (db) dblog_nocr("(%d,%d)",c2->x,c2->y); if ((roomid >= 0) && (getroomid(c2) == roomid)) { if (wantfilled && c2->type->solid) { // see EXCEPTION above. } else { // hits same room, not ok. break; } } else if (cellwalkable(NULL, c2, NULL)) { if (!wantfilled || c2->filled) { if (db) dblog(" Got to an empty cell here."); 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) { if ( ((roomid == -1) || (getroomid(pcell) != roomid)) && cellwalkable(NULL, pcell, NULL)) { if (!wantfilled || pcell->filled) { // 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 // TODO: 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) { setcelltype(c, c->habitat->emptycelltype); if (ncellsadded) (*ncellsadded)++; c = getcellindir(c, startdir); } // clear the corner cell setcelltype(c, c->habitat->emptycelltype); // 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) { setcelltype(c, c->habitat->emptycelltype); 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) { setcelltype(c, c->habitat->emptycelltype); 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]) { setcelltype(c, c->habitat->emptycelltype); if (ncellsadded) (*ncellsadded)++; c = getcellindir(c, whichway); } } 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 doors/exits found.",nposs); // for each door, 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 exit is solid and COMPLETELY surrounded by solid, ignore it. if (c->type->solid && (countcellexits(c, DT_ORTH) == 0)){ if (db) dblog("cell is solid and surrounded by solids. ignoring."); continue; } if (db) dblog("linking exit #%d",i); 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."); nadded += linkexit(poss[i], B_FALSE, NULL); } } // 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; // create a new region r = addregion(newregiontype, m->region, -1); // add stairs to new region if (!c) { c = NULL; while (!c || !cellwalkable(NULL, 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[MAXRETCELLS]; cell_t *dirtcell[MAXRETCELLS]; int nretcell,ndirtcells,i; // 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); } // 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); } } } } } // room w/h are returned in *w and *h if given. int createroom(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 x,y; cell_t *cell; 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; room_t *thisroom = NULL; 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); 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 = NULL; map->room[map->nrooms].exitslinked = B_FALSE; thisroom = &(map->room[map->nrooms]); map->nrooms++; for (y = miny; y <= maxy; y++) { for (x = minx; x <= maxx; 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 == miny) || (y == maxy) || (x == minx) || (x == maxx)) { // 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, CT_ROOMWALL); } //} } else { setcelltype(cell, CT_ROOM); } cell->room = thisroom; } } } // 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; printf("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",loctext, rtype->name); } 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; if (dirtype == DT_COMPASS) { animradial(c, range, '}', C_RED); } else { // ie. DT_ORTH animradialorth(c, range, '}', C_RED); } if (haslos(player, c)) { if (wantannounce) { msg("You see %s explosion!", (range > 0) ? "a huge" : "an"); } } else { noise(c, NULL, NC_OTHER, (range > 0) ? 6 : 5, "an explosion!", NULL); } for (y = c->y - range ; y <= c->y + range ; y++) { for (x = c->x - range ; x <= c->x + range ; x++) { int inrange = B_FALSE; cell_t *cc; cc = getcellat(c->map, x,y); if (cc) { if ((dirtype == DT_COMPASS) && (getcelldist(c,cc) <= range)) { inrange = B_TRUE; } else if ((dirtype == DT_ORTH) && (getcelldistorth(c,cc) <= range)) { inrange = B_TRUE; } if (inrange) { cell_t *stopcell = NULL; // if a door stops the explosion, the door will // take the damage. haslof(c, cc, LOF_WALLSTOP, &stopcell); explodesinglecell(stopcell, 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, getrandomcorebp(cc->lf), DT_EXPLOSION); 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); } } } } } // 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); if (killwalls) { if (c->type->solid) { // make it empty! setcelltype(c, c->habitat->emptycelltype); // add some rubble addob(c->obpile, "10-50 stones"); } } } celltype_t *findcelltype(enum CELLTYPE cid) { celltype_t *ct; for (ct = firstcelltype ; ct ; ct = ct->next) { if (ct->id == cid) return ct; } return NULL; } 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; } 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; } 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; } regiontype_t *findregiontype(enum REGIONTYPE rtype) { regiontype_t *rt; for (rt = firstregiontype ; rt ; rt = rt->next) { if (rt->id == rtype) 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) printf("--- Trying %s...\n",getdirname(dir)); if (tried[dir] == B_TRUE) { // already found this dir to be invalid lastdir = D_UNKNOWN; if (db) printf("--- Already know %s is invalid.\n",getdirname(dir)); } else { // check 1 cell ahead newcell = getcellindir(cell, dir); if (isnewcellok(newcell, err)) { cell_t *newcell1, *newcell2; // check 2 cells ahead and sidewars newcell = getcellindir(newcell, dir); if (newcell) { switch (dir) { case D_N: case D_S: newcell1 = getcellindir(newcell,D_E); newcell2 = getcellindir(newcell,D_W); break; case D_E: case D_W: newcell1 = getcellindir(newcell,D_N); newcell2 = getcellindir(newcell,D_S); break; default: // should never happen newcell1 = NULL; newcell2 = NULL; break; } } else { newcell1 = NULL; newcell2 = NULL; } if (!isnewcellok(newcell, err)) { if (db) printf("--- %s %s!\n",getdirname(dir), err); tried[dir] = B_TRUE; lastdir = D_UNKNOWN; numtries++; } else if (!isnewcellok(newcell1, err)) { if (db) printf("--- %s %s!\n",getdirname(dir), err); tried[dir] = B_TRUE; lastdir = D_UNKNOWN; numtries++; } else if (!isnewcellok(newcell2, err)) { if (db) printf("--- %s %s!\n",getdirname(dir), err); tried[dir] = B_TRUE; lastdir = D_UNKNOWN; numtries++; } else { // ok if (db) printf("--- %s %s!\n",getdirname(dir), err); foundvaliddir = B_TRUE; } } else { if (db) printf("--- %s %s!\n",getdirname(dir), err); tried[dir] = B_TRUE; lastdir = D_UNKNOWN; numtries++; } } } //newcell = getcellindir(cell, dir); //printf("getrandomdigdir() - on cell %d,%d, returning dir %d (-> %d,%d)\n", // cell->x, cell->y, dir, newcell->x, newcell->y); if (turned) *moved = 0; return dir; } char *getregionname(char *buf, map_t *m, int withlevel) { region_t *r; r = m->region; if (withlevel) { flag_t *f; int x,y; f = hasflag(m->flags, F_MAPCOORDS); if (f) { x = f->val[0]; y = f->val[1]; } else { x = NA; y = NA; } switch (r->rtype->id) { case RG_WORLDMAP: snprintf(buf, BUFLEN, "the surface(%d,%d)",x,y); break; case RG_FIRSTDUNGEON: snprintf(buf, BUFLEN, "dungeon L%d", m->depth); break; case RG_HEAVEN: snprintf(buf, BUFLEN, "the realm of gods"); break; case RG_PIT: snprintf(buf, BUFLEN, "a pit L%d", m->depth); break; default: strcpy(buf, "?unknownregiontype??"); break; } } else { switch (r->rtype->id) { case RG_WORLDMAP: strcpy(buf, "the surface"); break; case RG_FIRSTDUNGEON: strcpy(buf, "the dungeon"); break; case RG_HEAVEN: snprintf(buf, BUFLEN, "the realm of gods"); break; case RG_PIT: snprintf(buf, BUFLEN, "a pit"); break; default: strcpy(buf, "?unknownregiontype??"); break; } } return buf; } cell_t *getrandomadjcell(cell_t *c, int wantempty, int allowexpand) { return real_getrandomadjcell(c, wantempty, allowexpand, LOF_NEED, NULL); } cell_t *real_getrandomadjcell(cell_t *c, int wantempty, int allowexpand, enum LOFTYPE needlof, enum OBTYPE *dontwantob) { int radius = 1; int x,y; cell_t *poss[MAXCANDIDATES]; int nposs = 0; cell_t *new; int done = B_FALSE; while (!done) { int numwithlof = 0; for (y = c->y - radius ; y <= c->y + radius ; y++) { for (x = c->x - radius ; x <= c->x + radius ; x++) { new = getcellat(c->map, x, y); if (new && (new != c) && (getcelldist(c,new) == radius) && haslof(c, new, needlof, NULL)) { enum OBTYPE *badoid; int ok = B_FALSE; numwithlof++; if (wantempty == WE_EMPTY) { // make sure it's empty if (isempty(new)) { ok = B_TRUE; } } else if (wantempty == WE_WALKABLE) { if (cellwalkable(NULL, new, NULL)) { ok = B_TRUE; } } else if (wantempty == WE_PORTAL) { if (cellwalkable(NULL, new, NULL) && !hasenterableobject(new) ) { if (!hasobwithflag(new->obpile, F_DOOR)) { ok = B_TRUE; } } } else if (wantempty == WE_NOTWALL) { if ((!new->type->solid) && !hasobwithflag(new->obpile, F_IMPASSABLE)) { //if (!new->type->solid) { ok = B_TRUE; } } else if (wantempty == WE_NOLF) { if (!new->lf) ok = B_TRUE; } else { // always ok ok = B_TRUE; } // obs we dont want? if (dontwantob) { for (badoid = dontwantob; (*badoid != OT_NONE) ; badoid++) { if (hasob(new->obpile, *badoid)) { ok = B_FALSE; break; } } } if (ok) { poss[nposs++] = new; } } } } // found any possibilities ? if (nposs) { done = B_TRUE; } else { if (allowexpand) { if (numwithlof) { // increment radius radius++; } else { return NULL; } } else { return NULL; } } } return poss[rnd(0,nposs-1)]; } cell_t *getrandomcell(map_t *map) { int x,y; cell_t *cell; x = (rand() % map->w); y = (rand() % map->h); cell = getcellat(map, x, y); assert(cell); return cell; } cell_t *getrandomcelloftype(map_t *map, enum CELLTYPE id) { cell_t *cell; cell = getrandomcell(map); while (cell->type->id != id) { cell = getrandomcell(map); } return cell; } int getrandomdir(int dirtype) { if (dirtype == DT_ORTH) { return rnd(D_N, D_W); } else { // ie. DT_COMPASS return rnd(DC_N, DC_NW); } } 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) { 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; if (slipob) *slipob = NULL; for (o = c->obpile->first ; o ; o = o->next) { int thisslip; sumflags(o->flags, F_SLIPPERY, &thisslip, NULL, NULL); if (thisslip > 0) { if (thisslip > bestslip) { bestob = o; bestslip = thisslip; } } thisslip *= o->amt; totalslip += thisslip; } totalslip *= 2; if (slipob) { *slipob = bestob; } return totalslip; } // 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!\n"); msg("ERROR - unlinked stairs!\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; } lifeform_t *haslf(cell_t *c) { if (c->lf && !isdead(c->lf)) { return c->lf; } return NULL; } int hasobject(cell_t *c) { if (c->obpile->first) { return B_TRUE; } return B_FALSE; } int hasknownobject(cell_t *c) { object_t *o; for (o = c->obpile->first ; o ; o = o->next) { if (o && canseeob(player, o)) { return B_TRUE; } } return B_FALSE; } object_t *hastrailof(obpile_t *op, lifeform_t *lf, enum OBTYPE oid, flag_t **tflag, lifeform_t *viewer) { object_t *o; flag_t *f; for (o = op->first ; o ; o = o->next) { if (viewer && !canseeob(viewer, o)) continue; if ((oid == NA) || (o->type->id == oid)) { f = hasflag(o->flags, F_TRAIL); // raceid and lfid must match if (f) { if ((f->val[0] == lf->race->id) && (atoi(f->text) == lf->id)) { if (tflag) { *tflag = f; } return o; } } } } return NULL; } void initmap(void) { int vx[4],vy[4],i; // habitats // thingchance, obchance, vaultchance, maxvisrange addhabitat(H_DUNGEON, "dungeon", CT_CORRIDOR, CT_WALL, 3, 50, 30, 6); addhabitat(H_FOREST, "forest", CT_GRASS, CT_WALL, 3, 75, 0, MAXVISRANGE); addhabitat(H_HEAVEN, "heaven", CT_CORRIDOR, CT_WALL, 0, 0, 0, MAXVISRANGE); addhabitat(H_PIT, "pit", CT_CORRIDOR, CT_WALL, 0, 0, 0, 5); addhabitat(H_VILLAGE, "village", CT_GRASS, CT_WALL, 3, 70, 0, MAXVISRANGE); addhabitat(H_SWAMP, "swamp", CT_CORRIDOR, CT_WALL, 3, 50, 0, MAXVISRANGE); // cell types addcelltype(CT_FAKE, "fake cell", '.', C_GREEN, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_WALL, "rock wall", '#', C_GREY, B_SOLID, B_OPAQUE, MT_STONE, 0); addcelltype(CT_WALLWOOD, "wooden wall", '#', C_BROWN, B_SOLID, B_OPAQUE, MT_WOOD, 0); addcelltype(CT_WALLGLASS, "glass wall", '#', C_CYAN, B_SOLID, B_TRANS, MT_GLASS, 0); addcelltype(CT_WALLMETAL, "metal wall", '#', C_WHITE, B_SOLID, B_OPAQUE, MT_METAL, 0); addcelltype(CT_ROOMWALL, "rock wall", '#', C_GREY, B_SOLID, B_OPAQUE, MT_STONE, 0); addcelltype(CT_CORRIDOR, "rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_LOOPCORRIDOR, "rock floor", 'L', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_FLOORWOOD, "wood floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0); addcelltype(CT_FLOORSHOP, "shop floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0); addcelltype(CT_ROOM, "rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_GRASS, "grass", '.', C_GREEN, B_EMPTY, B_TRANS, MT_PLANT, 0); addcelltype(CT_DIRT, "dirt", '.', C_BROWN, B_EMPTY, B_TRANS, MT_STONE, 0); addcelltype(CT_LOWFLOOR, "low rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, -1); // region types addregiontype(RG_WORLDMAP, "World map", H_FOREST, 10, 0, D_NONE, B_TRUE); addregiontype(RG_FIRSTDUNGEON, "First Dungeon", H_DUNGEON, 10, 3, D_DOWN, B_TRUE); addregiontype(RG_HEAVEN, "Realm of Gods", H_HEAVEN, 1, 0, D_NONE, B_FALSE); addregiontype(RG_PIT, "Pit", H_PIT, 1, 1, D_DOWN, B_FALSE); // MAPMAPMAPMAP // region definitions (outlines) addregionoutline(RG_WORLDMAP); // link to first dungeon addregionthing(lastregionoutline, NA, 0, 0, RT_REGIONLINK, RG_FIRSTDUNGEON, "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_FIRSTDUNGEON); addregionthing(lastregionoutline, 1, NA, NA, RT_RNDVAULTWITHFLAG, F_VAULTISPLAYERSTART, NULL); // 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 // 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"); } int isadjacent(cell_t *src, cell_t *dst) { if (getcelldist(src, dst) == 1) { return B_TRUE; } return B_FALSE; } int isdark(cell_t *c) { switch (c->lit) { case L_PERMDARK: case L_NOTLIT: return B_TRUE; default: break; } return B_FALSE; } int isdiggable(cell_t *c) { switch (c->type->id) { case CT_WALL: return B_TRUE; case CT_ROOMWALL: return B_TRUE; } return B_FALSE; } // if isopen provided, returns whether or not the door is opened int isdoor(object_t *o, int *isopen) { int isdoor = B_FALSE; if (hasflag(o->flags, F_DOOR)) { isdoor = B_TRUE; } else { isdoor = B_FALSE; } if (isdoor) { if (isopen) { if (hasflag(o->flags, F_OPEN)) { *isopen = B_TRUE; } else { *isopen = B_FALSE; } } } return isdoor; } int isempty(cell_t *c) { if (!c) return B_FALSE; if (c->type->solid) return B_FALSE; if (c->lf) return B_FALSE; if (!cellwalkable(NULL, c, NULL)) return B_FALSE; return B_TRUE; } //returns TT_ based on what you can scan there int isinscanrange(cell_t *c, void **thing, char *desc, glyph_t *glyph) { flag_t *f; 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 = 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)) { *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->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); } // clear out the cell if required if (c2->type->solid) { setcelltype(c2, c2->habitat->emptycelltype); } // 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; } // link the staircase 'o' to a free one in adjacent maps. // o2 is options. if not probided, 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; // find an empty staircase in other map for (n = 0; n < othermap->w*othermap->h; n++) { c2 = othermap->cell[n]; oo = hasob(c2->obpile, otherstairtype->id); if (oo) { // does it go nowhere? if (!hasflag(oo->flags, F_MAPLINK)) { o2 = oo; found = B_TRUE; break; } } } if (!found) { dblog("ERROR - stairs link to existing map %d('%s', depth %d), but it has no free stairs.",othermap->id, othermap->name, othermap->depth); msg("ERROR - stairs link to existing map %d('%s', depth %d), but it has no free stairs.",othermap->id, 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, cell->habitat->emptycelltype); if ((rnd(1,100) + m->depth) >= 66) { strcpy(doorbuf, "iron door"); } else { strcpy(doorbuf, "wooden door"); } o = addob(cell->obpile, doorbuf); if (o && (rnd(1,100) <= openchance)) { opendoor(NULL, o); } else { int chance; // door is closed - lock it? // ie. at dungeon lev 10, chance is 2 in 6 // at dungeon lev 20, chance is 3 in 6 // ... // at dungeon lev 50, chance is 5 in 6 chance = rolldie(1,6) - (m->depth / 10); if (chance <= 1) { addflag(o->flags, F_LOCKED, B_TRUE, getdoorlockdiff(m->depth), NA, NULL); } // make it secret? chance = rolldie(1,6) - (m->depth / 10); // difficulty: // l1 = 20 // l10 = 25 // l20 = 30 if (chance <= 1) { addflag(o->flags, F_SECRET, getdoorsecretdiff(m->depth), NA, NA, NULL); } } } void makelit(cell_t *c, enum LIGHTLEV how, int howlong) { // don't override permenant light with temp light! //if ((c->lit == L_PERMLIGHT) && (how == L_TEMP)) { if (how == L_TEMP) { //if ((c->lit == L_PERMLIGHT) || (c->lit == L_PERMDARK)) { if (c->lit == L_PERMLIGHT) { // light sources can't override permenant light return; } if (c->lit == L_PERMDARK) { // light sources can't override darkness spell return; } } if (howlong > 0) { // TODO: use a stack here instead c->origlit = c->lit; c->origlittimer = c->littimer; c->littimer = howlong; } c->lit = how; } void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong) { int x,y; cell_t *c2; int (*distfunc)(cell_t *, cell_t *); if (radius <= 0) return; if (radius == 1) { distfunc = getcelldist; } else { distfunc = getcelldistorth; } for (y = c->y - radius ; y <= c->y + radius; y++) { for (x = c->x - radius ; x <= c->x + radius; x++) { c2 = getcellat(c->map, x, y); if (c2 && (distfunc(c, c2) <= radius)) { if (cellhaslos(c,c2)) { makelit(c2, how,howlong); } } } } } void mapentereffects(map_t *m) { int i; cell_t *c; flag_t *f; for (i = 0; i < m->w * m->h; i++) { vault_t *v; // teleport shopkeepers back to their shops c = m->cell[i]; 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); } } } v = getcellvault(c); // 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 = m->habitat->solidcelltype; for (i = 0; i < howmuch; i++) { for (n = 0; n < m->w * m->h; n++) { cell_t *c; c = m->cell[n]; if (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); 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 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)*5; } 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); if (cell->type->solid) { assert(!cell->obpile->first); } if ((gamemode == GM_GAMESTARTED) && 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], buf[BUFLEN]; object_t *o, *nexto; int seen = B_FALSE; 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)) { rv = B_TRUE; // announce if (haslos(player, c)) { msg("%s %s shatters!",needan(c->type->name) ? "An" : "A", c->type->name); seen = B_TRUE; } else { // very loud noise(c, NULL, NC_OTHER, 7, "shattering glass.", NULL); } if (target) { if (seen) { msg("%s %s showered in %s shards!", targetname, is(target), c->type->material->name); } losehp(target, rnd(1,100), DT_SLASH, fromlf, damstring); // BIG damage } // change cell type setcelltype(c, c->habitat->emptycelltype); // place shards if (c->type->material->id == MT_GLASS) { int numshards; numshards = rnd(50,100); snprintf(buf, BUFLEN, "%d pieces of broken glass",numshards); addob(c->obpile, buf); } else if (c->type->material->id == MT_ICE) { int numshards; numshards = rnd(50,100); snprintf(buf, BUFLEN, "%d chunks of ice",numshards); addob(c->obpile, buf); } } // 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; // 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; } map = player->cell->map; //wep = getweapon(player); for (y = viewy; y < viewy + viewh; y++) { for (x = viewx; x < viewx + vieww; x++) { cell_t *cell; cell = getcellat(map, x, y); if (cell) { //if ((player->cell == cell) || haslos(player, cell)) { if (haslos(player, cell)) { setcellknown(cell, B_FALSE); /* if (cell->lf && lfhasflag(cell->lf, F_UNDEAD)) { seenundead = B_TRUE; } */ } } } } } 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; } 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."); } }