void cellularAutomataRound(short **grid, char birthParameters[9], char survivalParameters[9]) { short i, j, nbCount, newX, newY; enum directions dir; short **buffer2; buffer2 = allocGrid(); copyGrid(buffer2, grid); // Make a backup of grid in buffer2, so that each generation is isolated. for(i=0; i<DCOLS; i++) { for(j=0; j<DROWS; j++) { nbCount = 0; for (dir=0; dir< DIRECTION_COUNT; dir++) { newX = i + nbDirs[dir][0]; newY = j + nbDirs[dir][1]; if (coordinatesAreInMap(newX, newY) && buffer2[newX][newY]) { nbCount++; } } if (!buffer2[i][j] && birthParameters[nbCount] == 't') { grid[i][j] = 1; // birth } else if (buffer2[i][j] && survivalParameters[nbCount] == 't') { // survival } else { grid[i][j] = 0; // death } } } freeGrid(buffer2); }
// Flood-fills the grid from (x, y) along cells that are within the eligible range. // Returns the total count of filled cells. short floodFillGrid(short **grid, short x, short y, short eligibleValueMin, short eligibleValueMax, short fillValue) { enum directions dir; short newX, newY, fillCount = 1; brogueAssert(fillValue < eligibleValueMin || fillValue > eligibleValueMax); grid[x][y] = fillValue; for (dir = 0; dir < 4; dir++) { newX = x + nbDirs[dir][0]; newY = y + nbDirs[dir][1]; if (coordinatesAreInMap(newX, newY) && grid[newX][newY] >= eligibleValueMin && grid[newX][newY] <= eligibleValueMax) { fillCount += floodFillGrid(grid, newX, newY, eligibleValueMin, eligibleValueMax, fillValue); } } return fillCount; }
// Marks a cell as being a member of blobNumber, then recursively iterates through the rest of the blob short fillContiguousRegion(short **grid, short x, short y, short fillValue) { enum directions dir; short newX, newY, numberOfCells = 1; grid[x][y] = fillValue; // Iterate through the four cardinal neighbors. for (dir=0; dir<4; dir++) { newX = x + nbDirs[dir][0]; newY = y + nbDirs[dir][1]; if (!coordinatesAreInMap(newX, newY)) { break; } if (grid[newX][newY] == 1) { // If the neighbor is an unmarked region cell, numberOfCells += fillContiguousRegion(grid, newX, newY, fillValue); // then recurse. } } return numberOfCells; }
// This is a custom implementation of recursive shadowcasting. void scanOctantFOV(char grid[VIEWWIDTH][VIEWHEIGHT], short xLoc, short yLoc, short octant, float maxRadius, short columnsRightFromOrigin, long startSlope, long endSlope, unsigned long forbiddenTerrain, unsigned long forbiddenFlags, boolean cautiousOnWalls) { if (columnsRightFromOrigin >= maxRadius) return; short i, a, b, iStart, iEnd, x, y, x2, y2; // x and y are temporary variables on which we do the octant transform long newStartSlope, newEndSlope; boolean cellObstructed; newStartSlope = startSlope; a = ((LOS_SLOPE_GRANULARITY / -2 + 1) + startSlope * columnsRightFromOrigin) / LOS_SLOPE_GRANULARITY; b = ((LOS_SLOPE_GRANULARITY / -2 + 1) + endSlope * columnsRightFromOrigin) / LOS_SLOPE_GRANULARITY; iStart = min(a, b); iEnd = max(a, b); // restrict vision to a circle of radius maxRadius if ((columnsRightFromOrigin*columnsRightFromOrigin + iEnd*iEnd) >= maxRadius*maxRadius) { return; } if ((columnsRightFromOrigin*columnsRightFromOrigin + iStart*iStart) >= maxRadius*maxRadius) { iStart = (int) (-1 * sqrt(maxRadius*maxRadius - columnsRightFromOrigin*columnsRightFromOrigin) + FLOAT_FUDGE); } x = xLoc + columnsRightFromOrigin; y = yLoc + iStart; betweenOctant1andN(&x, &y, xLoc, yLoc, octant); boolean currentlyLit = coordinatesAreInMap(x, y) && !(cellHasTerrainFlag(x, y, forbiddenTerrain) || (pmap[x][y].flags & forbiddenFlags)); for (i = iStart; i <= iEnd; i++) { x = xLoc + columnsRightFromOrigin; y = yLoc + i; betweenOctant1andN(&x, &y, xLoc, yLoc, octant); if (!coordinatesAreInMap(x, y)) { // We're off the map -- here there be memory corruption. continue; } cellObstructed = (cellHasTerrainFlag(x, y, forbiddenTerrain) || (pmap[x][y].flags & forbiddenFlags)); // if we're cautious on walls and this is a wall: if (cautiousOnWalls && cellObstructed) { // (x2, y2) is the tile one space closer to the origin from the tile we're on: x2 = xLoc + columnsRightFromOrigin - 1; y2 = yLoc + i; if (i < 0) { y2++; } else if (i > 0) { y2--; } betweenOctant1andN(&x2, &y2, xLoc, yLoc, octant); if (pmap[x2][y2].flags & IN_FIELD_OF_VIEW) { // previous tile is visible, so illuminate grid[x][y] = 1; } } else { // illuminate grid[x][y] = 1; } if (!cellObstructed && !currentlyLit) { // next column slope starts here newStartSlope = (long int) ((LOS_SLOPE_GRANULARITY * (i) - LOS_SLOPE_GRANULARITY / 2) / (columnsRightFromOrigin + 0.5)); currentlyLit = true; } else if (cellObstructed && currentlyLit) { // next column slope ends here newEndSlope = (long int) ((LOS_SLOPE_GRANULARITY * (i) - LOS_SLOPE_GRANULARITY / 2) / (columnsRightFromOrigin - 0.5)); if (newStartSlope <= newEndSlope) { // run next column scanOctantFOV(grid, xLoc, yLoc, octant, maxRadius, columnsRightFromOrigin + 1, newStartSlope, newEndSlope, forbiddenTerrain, forbiddenFlags, cautiousOnWalls); } currentlyLit = false; } } if (currentlyLit) { // got to the bottom of the scan while lit newEndSlope = endSlope; if (newStartSlope <= newEndSlope) { // run next column scanOctantFOV(grid, xLoc, yLoc, octant, maxRadius, columnsRightFromOrigin + 1, newStartSlope, newEndSlope, forbiddenTerrain, forbiddenFlags, cautiousOnWalls); } } }
/* * Given the x and y coordinate, checks the permanent map and returns * true if cell at the coordinates is considered to be in a terrain * loop. * * TDOD: Figure out what this does. * TODO: What is a terrain loop? * TODO: Remove magic numbers in parameters. */ bool checkLoopiness(struct pcell pmap[DCOLS][DROWS], const short x, const short y, const short cDirs[8][2]) { bool inString; short newX, newY, dir, sdir; short numStrings, maxStringLength, currentStringLength; if (!(pmap[x][y].flags & IN_LOOP)) { return false; } // find an unloopy neighbor to start on for (sdir = 0; sdir < 8; sdir++) { newX = x + cDirs[sdir][0]; newY = y + cDirs[sdir][1]; if (!coordinatesAreInMap(newX, newY) || !(pmap[newX][newY].flags & IN_LOOP)) { break; } } if (sdir == 8) { // no unloopy neighbors return false; // leave cell loopy } // starting on this unloopy neighbor, work clockwise and count up (a) the number of strings // of loopy neighbors, and (b) the length of the longest such string. numStrings = maxStringLength = currentStringLength = 0; inString = false; for (dir = sdir; dir < sdir + 8; dir++) { newX = x + cDirs[dir % 8][0]; newY = y + cDirs[dir % 8][1]; if (coordinatesAreInMap(newX, newY) && (pmap[newX][newY].flags & IN_LOOP)) { currentStringLength++; if (!inString) { if (numStrings > 0) { return false; // more than one string here; leave loopy } numStrings++; inString = true; } } else if (inString) { if (currentStringLength > maxStringLength) { maxStringLength = currentStringLength; } currentStringLength = 0; inString = false; } } if (inString && currentStringLength > maxStringLength) { maxStringLength = currentStringLength; } if (numStrings == 1 && maxStringLength <= 4) { pmap[x][y].flags &= ~IN_LOOP; for (dir = 0; dir < 8; dir++) { newX = x + cDirs[dir][0]; newY = y + cDirs[dir][1]; if (coordinatesAreInMap(newX, newY)) { checkLoopiness(pmap, newX, newY, cDirs); } } return true; } else { return false; } }