/* * Grid * Brogue * * Created by Brian Walker on 12/7/12. * Copyright 2012. All rights reserved. * * This file is part of Brogue. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "Rogue.h" #include "IncludeGlobals.h" // mallocing two-dimensional arrays! dun dun DUN! short **allocGrid() { short i; short **array = malloc(DCOLS * sizeof(short *)); array[0] = malloc(DROWS * DCOLS * sizeof(short)); for(i = 1; i < DCOLS; i++) { array[i] = array[0] + i * DROWS; } return array; } void freeGrid(short **array) { free(array[0]); free(array); } void copyGrid(short **to, short **from) { short i, j; for(i = 0; i < DCOLS; i++) { for(j = 0; j < DROWS; j++) { to[i][j] = from[i][j]; } } } void fillGrid(short **grid, short fillValue) { short i, j; for(i = 0; i < DCOLS; i++) { for(j = 0; j < DROWS; j++) { grid[i][j] = fillValue; } } } // Highlight the portion indicated by hiliteCharGrid with the hiliteColor at the hiliteStrength -- both latter arguments are optional. void hiliteGrid(short **grid, color *hiliteColor, short hiliteStrength) { short i, j, x, y; color hCol; assureCosmeticRNG; if (hiliteColor) { hCol = *hiliteColor; } else { hCol = yellow; } bakeColor(&hCol); if (!hiliteStrength) { hiliteStrength = 75; } for (i=0; i= findValueMin && grid[i][j] <= findValueMax) { grid[i][j] = fillValue; } } } } // 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; } void drawRectangleOnGrid(short **grid, short x, short y, short width, short height, short value) { short i, j; for (i=x; i < x+width; i++) { for (j=y; j= minPassableArc && count <= maxPassableArc) { grid[i][j] = value; } } } } } short validLocationCount(short **grid, short validValue) { short i, j, count; count = 0; for(i = 0; i < DCOLS; i++) { for(j = 0; j < DROWS; j++) { if (grid[i][j] == validValue) { count++; } } } return count; } short leastPositiveValueInGrid(short **grid) { short i, j, leastPositiveValue = 0; for(i = 0; i < DCOLS; i++) { for(j = 0; j < DROWS; j++) { if (grid[i][j] > 0 && (leastPositiveValue == 0 || grid[i][j] < leastPositiveValue)) { leastPositiveValue = grid[i][j]; } } } return leastPositiveValue; } // Takes a grid as a mask of valid locations, chooses one randomly and returns it as (x, y). // If there are no valid locations, returns (-1, -1). void randomLocationInGrid(short **grid, short *x, short *y, short validValue) { const short locationCount = validLocationCount(grid, validValue); short i, j; if (locationCount <= 0) { *x = *y = -1; return; } short index = rand_range(0, locationCount - 1); for(i = 0; i < DCOLS && index >= 0; i++) { for(j = 0; j < DROWS && index >= 0; j++) { if (grid[i][j] == validValue) { if (index == 0) { *x = i; *y = j; } index--; } } } return; } // Finds the lowest positive number in a grid, chooses one location with that number randomly and returns it as (x, y). // If there are no valid locations, returns (-1, -1). void randomLeastPositiveLocationInGrid(short **grid, short *x, short *y, boolean deterministic) { const short targetValue = leastPositiveValueInGrid(grid); short locationCount; short i, j, index; if (targetValue == 0) { *x = *y = -1; return; } locationCount = 0; for(i = 0; i < DCOLS; i++) { for(j = 0; j < DROWS; j++) { if (grid[i][j] == targetValue) { locationCount++; } } } if (deterministic) { index = locationCount / 2; } else { index = rand_range(0, locationCount - 1); } for(i = 0; i < DCOLS && index >= 0; i++) { for(j = 0; j < DROWS && index >= 0; j++) { if (grid[i][j] == targetValue) { if (index == 0) { *x = i; *y = j; } index--; } } } return; } boolean getQualifyingPathLocNear(short *retValX, short *retValY, short x, short y, boolean hallwaysAllowed, unsigned long blockingTerrainFlags, unsigned long blockingMapFlags, unsigned long forbiddenTerrainFlags, unsigned long forbiddenMapFlags, boolean deterministic) { short **grid, **costMap; short loc[2]; // First check the given location to see if it works, as an optimization. if (!cellHasTerrainFlag(x, y, blockingTerrainFlags | forbiddenTerrainFlags) && !(pmap[x][y].flags & (blockingMapFlags | forbiddenMapFlags)) && (hallwaysAllowed || passableArcCount(x, y) <= 1)) { *retValX = x; *retValY = y; return true; } // Allocate the grids. grid = allocGrid(); costMap = allocGrid(); // Start with a base of a high number everywhere. fillGrid(grid, 30000); fillGrid(costMap, 1); // Block off the pathing blockers. getTerrainGrid(costMap, PDS_FORBIDDEN, blockingTerrainFlags, blockingMapFlags); if (blockingTerrainFlags & (T_OBSTRUCTS_DIAGONAL_MOVEMENT | T_OBSTRUCTS_PASSABILITY)) { getTerrainGrid(costMap, PDS_OBSTRUCTION, T_OBSTRUCTS_DIAGONAL_MOVEMENT, 0); } // Run the distance scan. grid[x][y] = 1; costMap[x][y] = 1; dijkstraScan(grid, costMap, true); findReplaceGrid(grid, 30000, 30000, 0); // Block off invalid targets that aren't pathing blockers. getTerrainGrid(grid, 0, forbiddenTerrainFlags, forbiddenMapFlags); if (!hallwaysAllowed) { getPassableArcGrid(grid, 2, 10, 0); } // Get the solution. randomLeastPositiveLocationInGrid(grid, retValX, retValY, deterministic); // dumpLevelToScreen(); // displayGrid(grid); // if (coordinatesAreInMap(*retValX, *retValY)) { // hiliteCell(*retValX, *retValY, &yellow, 100, true); // } // temporaryMessage("Qualifying path selected:", true); freeGrid(grid); freeGrid(costMap); // Fall back to a pathing-agnostic alternative if there are no solutions. if (*retValX == -1 && *retValY == -1) { if (getQualifyingLocNear(loc, x, y, hallwaysAllowed, NULL, (blockingTerrainFlags | forbiddenTerrainFlags), (blockingMapFlags | forbiddenMapFlags), false, deterministic)) { *retValX = loc[0]; *retValY = loc[1]; return true; // Found a fallback solution. } else { return false; // No solutions. } } else { return true; // Found a primary solution. } } 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 topBlobSize) { // if this blob is a new record topBlobSize = blobSize; topBlobNumber = blobNumber; } blobNumber++; } } } // Figure out the top blob's height and width: // First find the max & min x: for(i=0; i topBlobMaxX) { topBlobMaxX = i; } } } // Then the max & min y: for(j=0; j topBlobMaxY) { topBlobMaxY = j; } } } blobWidth = (topBlobMaxX - topBlobMinX) + 1; blobHeight = (topBlobMaxY - topBlobMinY) + 1; } while (blobWidth < minBlobWidth || blobHeight < minBlobHeight || topBlobNumber == 0); // Replace the winning blob with 1's, and everything else with 0's: for(i=0; i