001package squidpony.squidgrid.mapping; 002 003import squidpony.annotation.Beta; 004import squidpony.squidmath.Coord; 005import squidpony.squidmath.LightRNG; 006import squidpony.squidmath.PerlinNoise; 007import squidpony.squidmath.StatefulRNG; 008 009import java.util.LinkedList; 010import java.util.List; 011 012import static java.lang.Math.round; 013 014/** 015 * A map generation factory using perlin noise to make island chain style maps. 016 * 017 * Based largely on work done by Metsa from #rgrd 018 * 019 * @author Eben Howard - http://squidpony.com - howard@squidpony.com 020 */ 021@Beta 022public class MetsaMapFactory { 023 //HEIGHT LIMITS 024 025 //BIOMESTUFF 026 private final double POLARLIMIT = 0.5, DESERTLIMIT = 0.1; 027 028 //SHADOW 029 private final double SHADOWLIMIT = 0.01; 030//COLORORDER 031/* 032 0 = deepsea 033 1 = beach 034 2 = low 035 3 = high 036 4 = mountain 037 5 = snowcap 038 6 = lowsea 039 */ 040// new SColor[]{SColor.DARK_SLATE_GRAY, SColor.SCHOOL_BUS_YELLOW, SColor.YELLOW_GREEN, 041// SColor.GREEN_BAMBOO, SColorFactory.lighter(SColor.LIGHT_BLUE_SILK), SColor.ALICE_BLUE, SColor.AZUL}; 042// new SColor[]{SColor.DARK_SLATE_GRAY, SColor.SCHOOL_BUS_YELLOW, SColor.YELLOW_GREEN, 043// SColor.GREEN_BAMBOO, SColorFactory.lighter(SColor.LIGHT_BLUE_SILK), SColor.ALICE_BLUE, SColor.AZUL}; 044 045 private int width; 046 private int height; 047 private int CITYAMOUNT = 14; 048 049 private List<Coord> cities = new LinkedList<>(); 050 private StatefulRNG rng; 051 private double maxPeak = 0; 052 053 public MetsaMapFactory() 054 { 055 rng = new StatefulRNG(); 056 width = 1000; 057 height = 600; 058 } 059 public MetsaMapFactory(int width, int height) 060 { 061 rng = new StatefulRNG(); 062 this.width = width; 063 this.height = height; 064 } 065 public MetsaMapFactory(int width, int height, long rngSeed) 066 { 067 rng = new StatefulRNG(new LightRNG(rngSeed)); 068 this.width = width; 069 this.height = height; 070 } 071 072 public int getShadow(int x, int y, double[][] map) { 073 if (x >= width - 1 || y <= 0) { 074 return 0; 075 } 076 double upRight = map[x + 1][y - 1]; 077 double right = map[x + 1][y]; 078 double up = map[x][y - 1]; 079 double cur = map[x][y]; 080 if (cur <= 0) { 081 return 0; 082 } 083 double slope = cur - (upRight + up + right) / 3; 084 if (slope < SHADOWLIMIT && slope > -SHADOWLIMIT) { 085 return 0; 086 } 087 if (slope >= SHADOWLIMIT) { 088 return -1; //"alpha" 089 } 090 if (slope <= -SHADOWLIMIT) { 091 return 1; 092 } else { 093 return 0; 094 } 095 } 096 097 /** 098 * Finds and returns the closest point containing a city to the given point. 099 * Does not include provided point as a possible city location. 100 * 101 * If there are no cities, null is returned. 102 * 103 * @param point 104 * @return 105 */ 106 public Coord closestCity(Coord point) { 107 double dist = 999999999, newdist; 108 Coord closest = null; 109 for (Coord c : cities) { 110 if (c.equals(point)) { 111 continue;//skip the one being tested for 112 } 113 newdist = Math.pow(point.x - c.x, 2) + Math.pow(point.y - c.y, 2); 114 if (newdist < dist) { 115 dist = newdist; 116 closest = c; 117 } 118 } 119 return closest; 120 } 121 122 public double[][] makeHeightMap() { 123 double[][] map = MapFactory.heightMap(width, height, rng); 124 125 for (int x = 0; x < width / 8; x++) { 126 for (int y = 0; y < height; y++) { 127 map[x][y] = map[x][y] - 1.0 + x / ((width - 1) * 0.125); 128 if (map[x][y] > maxPeak) { 129 maxPeak = map[x][y]; 130 } 131 } 132 } 133 134 for (int x = width / 8; x < 7 * width / 8; x++) { 135 for (int y = 0; y < height; y++) { 136 map[x][y] = map[x][y]; 137 if (map[x][y] > maxPeak) { 138 maxPeak = map[x][y]; 139 } 140 } 141 } 142 143 for (int x = 7 * width / 8; x < width; x++) { 144 for (int y = 0; y < height; y++) { 145 map[x][y] = map[x][y] - 1.0 + (width - 1 - x) / ((width - 1) * 0.125); 146 if (map[x][y] > maxPeak) { 147 maxPeak = map[x][y]; 148 } 149 } 150 } 151 152 return map; 153 } 154 155 public int[][] makeBiomeMap(double[][] map) { 156 //biomes 0 normal 1 snow 157 int biomeMap[][] = new int[width][height]; 158 for (int x = 0; x < width; x++) { 159 for (int y = 0; y < height; y++) { 160 biomeMap[x][y] = 0; 161 double distanceFromEquator = Math.abs(y - height / 2.0) / (height / 2.0); 162 distanceFromEquator += PerlinNoise.noise(x / 32, y / 32) / 8 + map[x][y] / 32; 163 if (distanceFromEquator > POLARLIMIT) { 164 biomeMap[x][y] = 1; 165 } 166 if (distanceFromEquator < DESERTLIMIT) { 167 biomeMap[x][y] = 2; 168 } 169 if (distanceFromEquator > POLARLIMIT + 0.25) { 170 biomeMap[x][y] = 3; 171 } 172 } 173 } 174 return biomeMap; 175 } 176 177 public int[][] makeNationMap(double[][] map) { 178 // nationmap, 4 times less accurate map used for nations -1 no nation 179 int nationMap[][] = new int[width][height]; 180 for (int i = 0; i < width / 4; i++) { 181 for (int j = 0; j < height / 4; j++) { 182 if (map[i * 4][j * 4] < 0) { 183 nationMap[i][j] = -1; 184 } else { 185 nationMap[i][j] = 0; 186 } 187 } 188 } 189 return nationMap; 190 } 191 192 public double[][] makeWeightedMap(double[][] map) { 193 //Weighted map for road 194 double weightedMap[][] = new double[width][height]; 195 double SEALEVEL = 0; 196 double BEACHLEVEL = 0.05; 197 double PLAINSLEVEL = 0.3; 198 for (int i = 0; i < width / 4; i++) { 199 for (int j = 0; j < height / 4; j++) { 200 weightedMap[i][j] = 0; 201 if (map[i * 4][j * 4] > BEACHLEVEL) { 202 weightedMap[i][j] = 2 + (map[i * 4][j * 4] - PLAINSLEVEL) * 8; 203 } 204 if (map[i][j] <= BEACHLEVEL && map[i * 4][j * 4] >= SEALEVEL) { 205 weightedMap[i][j] = 2 - (map[i * 4][j * 4]) * 2; 206 } 207 } 208 } 209 210 for (int i = 0; i < CITYAMOUNT; i++) { 211 int px = rng.between(0, width); 212 int py = rng.between(0, height); 213 while (map[px][py] < SEALEVEL || map[px][py] > BEACHLEVEL) { 214 px = rng.between(0, width); 215 py = rng.between(0, height); 216 } 217 cities.add(Coord.get(4 * round(px / 4f), 4 * round(py / 4f))); 218 } 219 return weightedMap; 220 } 221 222 public List<Coord> getCities() { 223 return cities; 224 } 225 226 public double getMaxPeak() { 227 return maxPeak; 228 } 229}