├── 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 |
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 | }
--------------------------------------------------------------------------------