├── utils.js ├── index.html ├── README.md ├── renderers.js └── maps.js /utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Jim Saunders 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | function randomRange(min, max) 24 | { 25 | return Math.round(min+(Math.random()*(max-min))); 26 | } 27 | 28 | function arraySearch(val, array) 29 | { 30 | var count = array.length; 31 | for(var i=0; i < count; ++i) 32 | if(array[i] == val)return i; 33 | return -1; 34 | } 35 | 36 | function clampValue(min, max, val) 37 | { 38 | return Math.min(Math.max(val,min),max); 39 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jimdoescode/jenysis @ GitHub 6 | 28 | 29 | 30 | 31 |
32 | [Create Terrain] 33 | [Create Erosion] 34 | [Create Rivers] 35 | [Temperature] 36 | [GitHub] 37 |
38 | 39 | 40 | 41 | 42 | 43 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jenysis Javascript World Generator 2 | ================================== 3 | 4 | This is a collection of javascript libraries that will let you generate a realistic world. The goal 5 | of this project is to emulate the complex world generation of games like Dwarf Fortress including 6 | terrain, weather, temperature, biomes, civilizations and history. My hope is that upon completion of 7 | these goals I will continue developing this project into a game similar to the adventure mode of 8 | Dwarf Fortress. 9 | 10 | Please note that this is the very very early stage of this project. Many things are not correct in terms of proper 11 | world generation. Please bare with me as I continue to work on and improve this project. 12 | 13 | 14 | Instantiation 15 | ------------- 16 | 17 | A new world is created by instantiating the World object and passing in a world size and water level. 18 | 19 | var world = new World(257, 90); 20 | 21 | World sizes should be (x^2)+1 as midpoint displacement is used to generate many of the features of 22 | the world. 23 | 24 | Water level should be a value between 0-255. 25 | 26 | 27 | To display the world on an HTML canvas element, you must instantiate the WorldRender object. Pass a 28 | handle to the canvas element you wish to render on as well as elevation values for beaches, forrest, 29 | and mountains. Elevation values should be between 0-255. 30 | 31 | var renderer = new WorldRenderer(document.getElementById('map'), 105, 180, 240); 32 | 33 | NOTE: Currently biome data is determined by elevation. This should be changed very soon, at which 34 | time you will no longer need to specify elevations for biome data. 35 | 36 | 37 | Usage 38 | ----- 39 | 40 | Once you have created your world object and rendering object now you can begin to generate your 41 | world. The following calls will create different aspects of the world. 42 | 43 | You must always generate some terrain prior to any other operation. 44 | 45 | world.createTerrain(); 46 | 47 | Once the initial terrain is created then you can perform other operations that affect the terrain. 48 | 49 | --------------------------------------------------------------------------------------------------- 50 | 51 | Erosion will cut paths through the terrain, starting at random points within the specified elevation 52 | percentage. 53 | 54 | world.createErosion(0.15, 20); //Run 20 erosion paths starting in the top 15 percent of elevation 55 | 56 | --------------------------------------------------------------------------------------------------- 57 | 58 | Rivers will behave similarly to erosion, except water will be set as the terrain type. 59 | 60 | world.createRivers(0.2, 10); //Run 10 rivers start in the top 20 percent of elevation. 61 | 62 | --------------------------------------------------------------------------------------------------- 63 | 64 | Temperatures can also be generated. They go from coldest at the top to warmest at the bottom. 65 | 66 | world.createTemperatures(); 67 | 68 | --------------------------------------------------------------------------------------------------- 69 | 70 | After each call you can use the renderer to view the changes to the world. 71 | 72 | renderer.drawTiled(world, 5); //Draw the world on the canvas each square should be 5 pixels. -------------------------------------------------------------------------------- /renderers.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Jim Saunders 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | function WorldRenderer(canvas, beach, forest, mountain) 24 | { 25 | WorldRenderer.prototype.drawArray = function(mapObj) 26 | { 27 | var str = ''; 28 | for(var i=0; i < mapObj.size*mapObj.size; ++i) 29 | { 30 | if(i%mapObj.size == 0)str += '
' 31 | str += (mapObj.map[i] === undefined ? '_':mapObj.map[i])+', '; 32 | } 33 | document.write(str); 34 | } 35 | 36 | WorldRenderer.prototype.drawPixelated = function(mapObj) 37 | { 38 | canvas.width = mapObj.size; 39 | canvas.height = mapObj.size; 40 | var ctx = canvas.getContext('2d'); 41 | var imgd = ctx.getImageData(0, 0, mapObj.size, mapObj.size); 42 | var pixm = imgd.data; 43 | for(var y=0; y < mapObj.size; ++y) 44 | { 45 | for(var x=0; x < mapObj.size; ++x) 46 | { 47 | var c = mapObj.map[(y*mapObj.size)+x]; 48 | var incl = y < (mapObj.size-1) ? (c - mapObj.map[((y+1)*mapObj.size)+x])*3 : 0; 49 | var wc = Math.floor(Math.max(Math.min(c-incl, 255), 0)); 50 | var rgb = [0, 0, 0]; 51 | if(c < water)rgb = [0, 0, c+92]; 52 | else if(c >= water && c < beach)rgb = [255,(c+110),c+50]; 53 | else if(c >= beach && c < forest)rgb = [0, wc/1.5, 0]; 54 | else if(c >= forest && c < mountain)rgb = [wc/1.2, wc/1.2, wc/1.2]; 55 | else if(c >= mountain)rgb = [wc, wc, wc]; 56 | var koo = y*mapObj.size*4 + (x*4); 57 | pixm[koo + 0] = rgb[0]; 58 | pixm[koo + 1] = rgb[1]; 59 | pixm[koo + 2] = rgb[2]; 60 | pixm[koo + 3] = 255; 61 | } 62 | } 63 | ctx.putImageData(imgd, 0, 0); 64 | } 65 | 66 | WorldRenderer.prototype.drawTiled = function(worldObj, tileSize) 67 | { 68 | var size = worldObj.terrain.size; 69 | canvas.width = size*tileSize; 70 | canvas.height = size*tileSize; 71 | var ctx = canvas.getContext('2d'); 72 | for(var y=0; y < size; ++y) 73 | { 74 | for(var x=0; x < size; ++x) 75 | { 76 | var pt = worldObj.getSquare(x, y); 77 | //var pt = mapObj.map[(y*mapObj.size)+x]; 78 | 79 | var c = Math.floor(pt.elevation); 80 | var incl = y < (size-1) ? (pt.elevation - worldObj.getSquare(x, y+1).elevation)*3 : 0; 81 | 82 | var rgb = [0,0,0]; 83 | 84 | if(pt.temperature !== false && pt.temperature < 1) 85 | { 86 | var clr = 255 - pt.temperature*10; 87 | rgb = [clr,clr,255]; 88 | } 89 | else if(pt.type === 'ocean' || pt.type === 'edge' || pt.type === 'water')rgb = [0,0,clampValue(130, 160, c+92)]; 90 | else 91 | { 92 | var wc = Math.floor(Math.max(Math.min(pt.elevation-incl, 255), 0)); 93 | 94 | if(pt.elevation < beach)rgb = [255,clampValue(180, 240, c+110),clampValue(180, 250, c+50)]; 95 | else if(pt.elevation >= beach && pt.elevation < forest)rgb = [0,clampValue(60, 110, Math.floor(wc/1.5)),0]; 96 | else if(pt.elevation >= forest && pt.elevation < mountain) 97 | { 98 | var val = clampValue(100, 210, Math.floor(wc/1.2)); 99 | rgb = [val,val,val]; 100 | } 101 | else if(pt.elevation >= mountain)rgb = [wc,wc,wc]; 102 | } 103 | ctx.fillStyle = 'rgb('+rgb.join(',')+')'; 104 | ctx.fillRect(x*tileSize, y*tileSize, tileSize, tileSize); 105 | } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /maps.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 by Jim Saunders 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | function Map(size) 24 | { 25 | var self = this; 26 | 27 | this.size = size; 28 | this.map = []; 29 | this.min = false; 30 | this.max = false; 31 | 32 | this.normalize = function(ceil) 33 | { 34 | if(this.min !== false && this.max !== false) 35 | { 36 | var diff = this.max-this.min; 37 | var size = this.map.length; 38 | for(var i=0; i < size; ++i) 39 | this.map[i] = Math.round((this.map[i]-this.min) / diff * ceil); 40 | } 41 | } 42 | 43 | this.normalizeValue = function(i, ceil) 44 | { 45 | var value = this.map[i]; 46 | if(this.min !== false && this.max !== false) 47 | { 48 | var diff = this.max-this.min; 49 | value = Math.round((value-this.min) / diff * ceil); 50 | } 51 | return value; 52 | } 53 | 54 | this.average = function() 55 | { 56 | var size = this.map.length - this.size; 57 | for(var i=this.size; i < size; ++i) 58 | this.map[i] = (this.map[i+1]+this.map[i-1]+this.map[i-this.size]+this.map[i+this.size])/4; 59 | } 60 | 61 | this.midpoint = function(size, disp) 62 | { 63 | function fill(stl, size, dh) 64 | { 65 | if(size > 1) 66 | { 67 | var subsize = size/2; 68 | var str = stl+size; //Find the top right of the square 69 | var sbl = stl+(size*self.size);//Find the bottom left of the square 70 | var sbr = sbl+size;//Find the bottom right of the square. 71 | var sc = stl+subsize+(self.size*subsize);//Find the center of the square 72 | 73 | var d2 = dh/2; 74 | 75 | var dt = stl+subsize; //Find the top corner of the diamond 76 | var dl = sc-subsize; //Find the left corner of the diamond 77 | var dr = sc+subsize; //Find the right corner of the diamond 78 | var db = sbl+subsize //Find the bottom corner of the diamond 79 | 80 | //Calculate the middle value of the square 81 | self.map[sc] = Math.abs((self.map[stl] + self.map[str] + self.map[sbl] + self.map[sbr])/4 + Math.random() * dh - d2); 82 | if(self.map[sc] < self.min || self.min === false)self.min = self.map[sc]; 83 | if(self.map[sc] > self.max || self.max === false)self.max = self.map[sc]; 84 | 85 | //Calculate the corner values of the diamond 86 | if(self.map[dt] === undefined) 87 | { 88 | self.map[dt] = Math.abs((self.map[stl] + self.map[str])/2 + Math.random() * dh - d2); 89 | if(self.map[dt] < self.min || self.min === false)self.min = self.map[dt]; 90 | if(self.map[dt] > self.max || self.max === false)self.max = self.map[dt]; 91 | } 92 | if(self.map[db] === undefined) 93 | { 94 | self.map[db] = Math.abs((self.map[sbl] + self.map[sbr])/2 + Math.random() * dh - d2); 95 | if(self.map[db] < self.min || self.min === false)self.min = self.map[db]; 96 | if(self.map[db] > self.max || self.max === false)self.max = self.map[db]; 97 | } 98 | if(self.map[dl] === undefined) 99 | { 100 | self.map[dl] = Math.abs((self.map[stl] + self.map[sbl])/2 + Math.random() * dh - d2); 101 | if(self.map[dl] < self.min || self.min === false)self.min = self.map[dl]; 102 | if(self.map[dl] > self.max || self.max === false)self.max = self.map[dl]; 103 | } 104 | if(self.map[dr] === undefined) 105 | { 106 | self.map[dr] = Math.abs((self.map[str] + self.map[sbr])/2 + Math.random() * dh - d2); 107 | if(self.map[dr] < self.min || self.min === false)self.min = self.map[dr]; 108 | if(self.map[dr] > self.max || self.max === false)self.max = self.map[dr]; 109 | } 110 | 111 | var nh = dh/disp; 112 | 113 | fill(stl, subsize, nh); 114 | fill(str-subsize, subsize, nh); 115 | fill(sc-subsize, subsize, nh); 116 | fill(sc, subsize, nh); 117 | } 118 | } 119 | for(var i=0; i < size; ++i) 120 | { 121 | this.map[i] = 0; 122 | this.map[i*size] = 0; 123 | this.map[(i*size)+(size-1)] = 0; 124 | this.map[(size*(size-1))+i] = 0; 125 | } 126 | fill(0, size-1, 1); 127 | } 128 | 129 | this.sortIndexes = function(type) 130 | { 131 | function swap(array, indexes, a, b) 132 | { 133 | var tmp = array[a]; 134 | array[a] = array[b]; 135 | array[b] = tmp; 136 | 137 | tmp = indexes[a]; 138 | indexes[a] = indexes[b]; 139 | indexes[b] = tmp; 140 | } 141 | 142 | function partition(array, indexes, begin, end, pivot) 143 | { 144 | var piv = array[pivot][type]; 145 | swap(array, indexes, pivot, end-1); 146 | var store = begin; 147 | for(var i = begin; i < end-1; ++i) 148 | { 149 | if(array[i][type] <= piv) 150 | { 151 | swap(array, indexes, store, i); 152 | ++store; 153 | } 154 | } 155 | swap(array, indexes, end-1, store); 156 | return store; 157 | } 158 | 159 | function qsort(array, indexes, begin, end) 160 | { 161 | if(end-1 > begin) 162 | { 163 | var pivot = begin + Math.floor(Math.random() * (end-begin)); 164 | pivot = partition(array, indexes, begin, end, pivot); 165 | 166 | qsort(array, indexes, begin, pivot); 167 | qsort(array, indexes, pivot+1, end); 168 | } 169 | } 170 | 171 | this.sortedIndexes = []; 172 | var size = this.map.length; 173 | for(var i=0; i < size; ++i) 174 | this.sortedIndexes[i] = i; 175 | 176 | //Copy the map so that we can sort it with out touching the original 177 | var sortMap = this.map.slice(); 178 | qsort(sortMap, this.sortedIndexes, 0, this.map.length); 179 | 180 | sortMap = []; //Attempt to clean up the large sorted map that we don't need 181 | } 182 | } 183 | 184 | Drainage.prototype = new Map; 185 | function Drainage(size) 186 | { 187 | Map.call(this, size); 188 | this.midpoint(size, 1.5); 189 | this.normalize(100); 190 | } 191 | 192 | Temperature.prototype = new Map; 193 | function Temperature(terrain, oceanLevel) 194 | { 195 | var size = terrain.size; 196 | Map.call(this, size); 197 | 198 | for(var lat=0; lat < size; ++lat) 199 | { 200 | var base = (lat/size)*255; 201 | for(var lon=0; lon < size; ++lon) 202 | { 203 | var scale = (terrain.map[lon*size+lat].elevation)/2; 204 | var temp = Math.round(base - scale); 205 | if(temp < this.min || this.min === false)this.min = temp; 206 | if(temp > this.max || this.max === false)this.max = temp; 207 | this.map[lon*size+lat] = temp; 208 | } 209 | } 210 | } 211 | 212 | Terrain.prototype = new Map; 213 | function Terrain(drainage, seaLevel) 214 | { 215 | Map.call(this, drainage.size); 216 | this.midpoint(drainage.size, 1.5); 217 | this.average(); 218 | this.normalize(255); 219 | this.seaLevel = seaLevel; 220 | 221 | var self = this; 222 | 223 | var size = this.map.length; 224 | for(var i=0; i < size; ++i) 225 | { 226 | var elevation = this.map[i]; 227 | //Put into vars for efficiency 228 | var imod = i % this.size; 229 | var sizeminone = this.size-1; 230 | if(i < this.size || i > (this.size*(sizeminone)) || imod == 0 || imod == sizeminone)this.map[i] = {type: 'edge', elevation: 0, drainage: 0, name: 'The Edge of the World'}; 231 | else if(elevation < seaLevel)this.map[i] = {type: 'ocean', elevation: elevation, drainage: 0, name: 'The Great Sea'}; 232 | else this.map[i] = {type: 'land', elevation: elevation, drainage: drainage.map[i], name: 'land'}; 233 | } 234 | 235 | //Returns the index of the lowest elevation point that is adjacent 236 | //to the specified index and not in the list of closed indexes. If 237 | //there are no lower elevations then it will lower the elevation of 238 | //one of the adjacent indexes. 239 | function getLowestElevationIndex(index, closed, clamp) 240 | { 241 | var lowest = self.map[index].elevation; 242 | 243 | var newindex = index; 244 | var next = [index-self.size, index+self.size, index-1, index+1]; //top, bottom, left, right 245 | for(var i=0; i < 4; ++i) 246 | { 247 | if(lowest > self.map[next[i]].elevation && arraySearch(next[i], closed) < 0) 248 | { 249 | newindex = next[i]; 250 | lowest = self.map[next[i]].elevation; 251 | } 252 | } 253 | //If we couldn't find a lower elevation 254 | if(newindex === index) 255 | { 256 | //Find the lowest elevation that hasn't already been used. 257 | var smallest = next[randomRange(0, 3)]; 258 | var found = false; 259 | for(var i=0; i < 4; ++i) 260 | { 261 | if(self.map[smallest].elevation > self.map[next[i]].elevation && arraySearch(next[i], closed) < 0) 262 | { 263 | smallest = next[i]; 264 | found = true; 265 | } 266 | } 267 | newindex = smallest; 268 | 269 | //Adjust the elevation so that it is lower. 270 | //Unless it is at the minimum then we just keep it at the minimum. 271 | if(clamp && lowest <= self.seaLevel)self.map[newindex].elevation = self.seaLevel; 272 | else self.map[newindex].elevation = lowest - randomRange(0, 1); 273 | } 274 | closed.push(newindex); 275 | return newindex; 276 | } 277 | 278 | function createLake(index, threshold, moves) 279 | { 280 | if(moves < 0 || index < self.size || index > self.map.length-self.size)return; 281 | var type = self.map[index].type; 282 | var elevation = self.map[index].elevation + threshold; 283 | if(type === 'water' || type === 'ocean' || type === 'edge')return; 284 | 285 | self.map[index].type = 'water'; 286 | self.map[index].drainage = 0; 287 | 288 | if(self.map[index-1].elevation < elevation)createLake(index-1, threshold, moves-1); 289 | if(self.map[index+1].elevation < elevation)createLake(index+1, threshold, moves-1); 290 | if(self.map[index-self.size].elevation < elevation)createLake(index-self.size, threshold, moves-1); 291 | if(self.map[index+self.size].elevation < elevation)createLake(index+self.size, threshold, moves-1); 292 | } 293 | 294 | this.runErosion = function(elevationPercent, pathCount) 295 | { 296 | //Sort the map indexes by elevation 297 | this.sortIndexes('elevation'); 298 | //Determine the index of the highest elevation 299 | var max = this.sortedIndexes.length-1; 300 | //Determine the index of the lowest elevation within our range percentage 301 | var min = max - Math.round(max*elevationPercent); 302 | //If a path count wasn't set determine one based on the size of the map 303 | if(pathCount == undefined)pathCount = Math.sqrt(this.size-1)*2; 304 | //This list of indexes already visited 305 | var closed = []; 306 | var index = -1; 307 | 308 | for(var ctr=0; ctr < pathCount; ++ctr) 309 | { 310 | closed = []; 311 | index = this.sortedIndexes[randomRange(min, max)]; 312 | while(index >= 0 && self.map[index].type !== 'ocean') 313 | index = getLowestElevationIndex(index, closed, true); 314 | } 315 | }; 316 | 317 | this.runRivers = function(elevationPercent, riverCount) 318 | { 319 | //Sort the map indexes by elevation 320 | this.sortIndexes('elevation'); 321 | //Determine the index of the highest elevation 322 | var max = this.sortedIndexes.length-1; 323 | //Determine the index of the lowest elevation within our range percentage 324 | var min = max - Math.round(max*elevationPercent); 325 | //If a river count wasn't set determine one based on the size of the map 326 | if(riverCount == undefined)riverCount = Math.sqrt(this.size-1)*4; 327 | //This list of indexes already visited 328 | var closed = []; 329 | 330 | function waterPerimeter(index, name) 331 | { 332 | var waterCtr = 0; 333 | if(self.map[index].type !== 'edge') 334 | { 335 | if(self.map[index-1].name === name)waterCtr++; 336 | if(self.map[index+1].name === name)waterCtr++; 337 | if(self.map[index-self.size].name === name)waterCtr++; 338 | if(self.map[index+self.size].name === name)waterCtr++; 339 | } 340 | return waterCtr; 341 | } 342 | 343 | function river(index, name) 344 | { 345 | if(self.map[index].type !== 'ocean') 346 | { 347 | self.map[index].type = 'water'; 348 | self.map[index].name = name; 349 | self.map[index].drainage = 0; 350 | } 351 | if(waterPerimeter(index-1, name) > 1)closed.push(index-1); 352 | if(waterPerimeter(index+1, name) > 1)closed.push(index+1); 353 | if(waterPerimeter(index-self.size, name) > 1)closed.push(index-self.size); 354 | if(waterPerimeter(index+self.size, name) > 1)closed.push(index+self.size); 355 | 356 | var newindex = getLowestElevationIndex(index, closed, (self.map[index].type !== 'ocean')); 357 | 358 | //if new index is less than zero maybe we should create a lake there. 359 | if(newindex < 0)return; 360 | else if(self.map[newindex].type === 'edge')return; 361 | else if(self.map[newindex].type === 'water' && self.map[newindex].name !== name) 362 | { 363 | //trace the water source that we found and 364 | //try to find where it hits the ocean 365 | return; 366 | } 367 | else 368 | { 369 | //Average points around the index if they aren't the new index. 370 | if(index-1 != newindex)self.map[index-1].elevation = Math.round((self.map[index-1].elevation+self.map[index].elevation)/2); 371 | if(index+1 != newindex)self.map[index+1].elevation = Math.round((self.map[index+1].elevation+self.map[index].elevation)/2); 372 | if(index+self.size != newindex)self.map[index+self.size].elevation = Math.round((self.map[index+self.size].elevation+self.map[index].elevation)/2); 373 | if(index-self.size != newindex)self.map[index-self.size].elevation = Math.round((self.map[index-self.size].elevation+self.map[index].elevation)/2); 374 | } 375 | river(newindex, name); 376 | } 377 | 378 | for(var ctr=0; ctr < riverCount; ++ctr) 379 | { 380 | var riverid = 'river'+ctr; 381 | var index = this.sortedIndexes[randomRange(min, max)]; 382 | closed = []; 383 | //Generate a name 384 | river(index, riverid); 385 | } 386 | } 387 | } 388 | 389 | function World(size, seaLevel) 390 | { 391 | createAndFireEvent('beginWorldCreate'); 392 | 393 | //... Create the terrain 394 | this.createTerrain = function() 395 | { 396 | this.terrain = new Terrain(new Drainage(size), seaLevel); 397 | createAndFireEvent('landCreated'); 398 | }; 399 | 400 | //... Run some paths to simulate erotion 401 | this.createErosion = function(eleRange, pathCount) 402 | { 403 | this.terrain.runErosion(eleRange, pathCount); 404 | createAndFireEvent('erosionCreated'); 405 | }; 406 | 407 | //... Create river paths. 408 | this.createRivers = function(eleRange, pathCount) 409 | { 410 | this.terrain.runRivers(eleRange, pathCount); //Should rivers be run after rain fall is determined? 411 | createAndFireEvent('riversCreated'); 412 | }; 413 | 414 | //... Determine Rain fall 415 | //createAndFireEvent('weatherCreated'); 416 | 417 | //... Determine temperatures 418 | this.createTemperatures = function() 419 | { 420 | this.temperature = new Temperature(this.terrain, seaLevel); 421 | createAndFireEvent('temperaturesCreated'); 422 | } 423 | 424 | //... Determine Biomes and name areas 425 | //createAndFireEvent('biomesCreated'); 426 | 427 | function createAndFireEvent(eventName) 428 | { 429 | var evt = document.createEvent("Event"); 430 | evt.initEvent(eventName,true,true); 431 | document.dispatchEvent(evt); 432 | } 433 | 434 | this.getSquare = function(x, y) 435 | { 436 | var index = x*size+y; 437 | return { 438 | type: this.terrain ? this.terrain.map[index].type : false, 439 | drainage: this.terrain ? this.terrain.map[index].drainage : false, 440 | elevation: this.terrain ? this.terrain.map[index].elevation : false, 441 | temperature: this.temperature ? this.temperature.map[index] : false 442 | } 443 | } 444 | } --------------------------------------------------------------------------------