1 /*
2 * This file is part of Dune Legacy.
3 *
4 * Dune Legacy is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * Dune Legacy is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Dune Legacy. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <MapEditor/MapGenerator.h>
19
20 #include <globals.h>
21
22 #include <misc/Random.h>
23
24 #include <memory>
25
26
27 #define ROCKFILLER 2 //how many times random generator will try to remove sand "holes" for rock from the map
28 #define SPICEFILLER 2 //for spice
29 #define DUNESFILLER 1
30
31 class MapGenerator {
32
33 public:
34
MapGenerator(int sizeX,int sizeY,int randSeed,int rockfields=ROCKFIELDS,int spicefields=SPICEFIELDS,MirrorMode mirrorMode=MirrorModeNone)35 MapGenerator(int sizeX, int sizeY, int randSeed, int rockfields = ROCKFIELDS, int spicefields = SPICEFIELDS, MirrorMode mirrorMode = MirrorModeNone)
36 : map(sizeX, sizeY), randGen(randSeed), rockfields(rockfields), spicefields(spicefields) {
37 mapMirror = std::shared_ptr<MapMirror>(MapMirror::createMapMirror(mirrorMode, sizeX, sizeY));
38 }
39
getMap()40 MapData& getMap() {
41 return map;
42 }
43
44 /**
45 Creates a random map
46 */
generateMap()47 void generateMap() {
48 // the whole map shall be of type Terrain_Sand
49
50 for(int i = 0; i < rockfields; i++) {
51 int spotX = randGen.rand(0, map.getSizeX()-1);
52 int spotY = randGen.rand(0, map.getSizeY()-1);
53
54 makeSpot(spotX, spotY, Terrain_Rock);
55 }
56
57 for(int i = 0; i < ROCKFILLER; i++) {
58 thickSpots(Terrain_Rock); //SPOT ROCK
59 }
60
61 // Spice fields
62 for(int i = 0; i < spicefields; i++) {
63 int spotX = randGen.rand(0, map.getSizeX()-1);
64 int spotY = randGen.rand(0, map.getSizeY()-1);
65
66 makeSpot(spotX, spotY, Terrain_Spice);
67 }
68
69 for(int i = 0; i < SPICEFILLER; i++) {
70 thickSpots(Terrain_Spice);
71 }
72
73 for(int i = 0; i < SPICEFILLER; i++) {
74 thickThickSpiceSpots();
75 }
76
77 // Spice fields
78 for(int i = 0; i < DUNEFIELDS; i++) {
79 int spotX = randGen.rand(0, map.getSizeX()-1);
80 int spotY = randGen.rand(0, map.getSizeY()-1);
81
82 makeSpot(spotX, spotY, Terrain_Dunes);
83 }
84
85 for(int i = 0; i < DUNESFILLER; i++) {
86 thickSpots(Terrain_Dunes);
87 }
88
89 addRockBits(randGen.rand(0,9));
90 addSpiceBlooms(randGen.rand(0,9));
91
92
93 }
94
95 private:
96
fixTileCoordinate(int & x,int & y)97 bool fixTileCoordinate(int& x, int& y) {
98 bool error = false;
99
100 if(x < 0) {
101 x = 0;
102 error = true;
103 } else if(x >= map.getSizeX()) {
104 x = map.getSizeX() - 1;
105 error = true;
106 }
107
108 if(y < 0) {
109 y = 0;
110 error = true;
111 } else if(y >= map.getSizeY()) {
112 y = map.getSizeY() - 1;
113 error = true;
114 }
115
116 return error;
117 }
118
119 /**
120 Checks if the tile to the left of (x,y) is of type tile
121 \param x x-coordinate in tile coordinates
122 \param y y-coordinate in tile coordinates
123 \param type the tile type to check
124 \return true if of tile type, false otherwise
125 */
onLeft(int x,int y,TERRAINTYPE type)126 bool onLeft(int x, int y, TERRAINTYPE type) {
127 x--;
128 fixTileCoordinate(x, y);
129
130 return (map(x,y) == type);
131 }
132
133 /**
134 Checks if the tile to the right of (x,y) is of type tile
135 \param x x-coordinate in tile coordinates
136 \param y y-coordinate in tile coordinates
137 \param type the tile type to check
138 \return true if of tile type, false otherwise
139 */
onRight(int x,int y,TERRAINTYPE type)140 bool onRight(int x, int y, TERRAINTYPE type) {
141 x++;
142 fixTileCoordinate(x, y);
143
144 return (map(x,y) == type);
145 }
146
147 /**
148 Checks if the tile above (x,y) is of type tile
149 \param x x-coordinate in tile coordinates
150 \param y y-coordinate in tile coordinates
151 \param type the tile type to check
152 \return true if of tile type, false otherwise
153 */
onUp(int x,int y,TERRAINTYPE type)154 bool onUp(int x, int y, TERRAINTYPE type) {
155 y--;
156 fixTileCoordinate(x, y);
157
158 return (map(x,y) == type);
159 }
160
161 /**
162 Checks if the tile below (x,y) is of type tile
163 \param x x-coordinate in tile coordinates
164 \param y y-coordinate in tile coordinates
165 \param type the tile type to check
166 \return true if of tile type, false otherwise
167 */
onDown(int x,int y,TERRAINTYPE type)168 bool onDown(int x, int y, TERRAINTYPE type) {
169 y++;
170 fixTileCoordinate(x, y);
171
172 return (map(x,y) == type);
173 }
174
175 /**
176 Count how many tiles around (x,y) are of type tile
177 \param x x-coordinate in tile coordinates
178 \param y y-coordinate in tile coordinates
179 \param type the tile type to check
180 \return number of surounding tiles of tile type (0 to 4)
181 */
side4(int x,int y,TERRAINTYPE type)182 int side4(int x, int y, TERRAINTYPE type) {
183 // Check at 4 sides for 'tile'
184 int flag = 0;
185
186 if(onLeft(x, y, type)) flag++;
187 if(onRight(x, y, type)) flag++;
188 if(onUp(x, y, type)) flag++;
189 if(onDown(x, y, type)) flag++;
190
191 return flag;
192 }
193
194
195
196 /**
197 Removes holes in rock and spice
198 \param type the type to remove holes from
199 */
thickSpots(TERRAINTYPE type)200 void thickSpots(TERRAINTYPE type) {
201 for(int i = 0; i < map.getSizeX(); i++) {
202 for(int j = 0; j < map.getSizeY(); j++) {
203 if(map(i,j) != type) {
204 // Found something else than what thickining
205
206 if(side4(i, j, type) >= 3) {
207 // Seems enough of the type around it so make this also of this type
208 for(int m=0; m < mapMirror->getSize(); m++) {
209 Coord position = mapMirror->getCoord(Coord(i, j), m);
210 map(position.x,position.y) = type;
211 }
212 }
213
214 if(side4(i, j, type) == 2) {
215 // Gamble, fifty fifty... set this type or not?
216 if(randGen.rand(0,1) == 1) {
217 for(int m=0; m < mapMirror->getSize(); m++) {
218 Coord position = mapMirror->getCoord(Coord(i, j), m);
219 map(position.x,position.y) = type;
220 }
221 }
222 }
223 }
224 }
225 }
226 }
227
228
229 /**
230 Removes holes in thick spice
231 */
thickThickSpiceSpots()232 void thickThickSpiceSpots() {
233 for(int i = 0; i < map.getSizeX(); i++) {
234 for(int j = 0; j < map.getSizeY(); j++) {
235
236 int numSpiceTiles = side4(i,j,Terrain_Spice)+side4(i,j,Terrain_ThickSpice);
237
238 if(map(i,j) != Terrain_ThickSpice && (numSpiceTiles>=4)) {
239 // Found something else than what thickining
240
241 if(side4(i, j, Terrain_ThickSpice) >= 3) {
242 // Seems enough of ThickSpice around it so make this also ThickSpice
243 for(int m=0; m < mapMirror->getSize(); m++) {
244 Coord position = mapMirror->getCoord(Coord(i, j), m);
245 map(position.x,position.y) = Terrain_ThickSpice;
246 }
247 }
248
249 if(side4(i, j, Terrain_ThickSpice) == 2) {
250 // Gamble, fifty fifty... set this to ThickSpice or not?
251 if(randGen.rand(0,1) == 1) {
252 for(int m=0; m < mapMirror->getSize(); m++) {
253 Coord position = mapMirror->getCoord(Coord(i, j), m);
254 map(position.x,position.y) = Terrain_ThickSpice;
255 }
256 }
257 }
258 }
259 }
260 }
261 }
262
263 /**
264 This function creates a spot of type type.
265 \param x the x coordinate in tile coordinates to start making the spot
266 \param y the y coordinate in tile coordinates to start making the spot
267 \param type type of the spot
268 */
makeSpot(int x,int y,TERRAINTYPE type)269 void makeSpot(int x, int y, TERRAINTYPE type) {
270 int spotSize = (640 * map.getSizeX()*map.getSizeY())/(64*64);
271 for(int j = 0; j < spotSize; j++) {
272 int dir = randGen.rand(0,3); // Random Dir
273
274 switch(dir) {
275 case 0 : x--; break;
276 case 1 : x++; break;
277 case 2 : y--; break;
278 case 3 : y++; break;
279 }
280
281 fixTileCoordinate(x, y);
282
283 TERRAINTYPE type2Place = type;
284
285 if(type == Terrain_Spice) {
286 if(map(x,y) == Terrain_Rock) {
287 // Do not place the spice spot, priority is ROCK!
288 continue;
289 } else if((map(x,y) == Terrain_Spice) && ((side4(x,y,Terrain_Spice)+side4(x,y,Terrain_ThickSpice)) >= 4)) {
290 // "upgrade" spice to thick spice
291 type2Place = Terrain_ThickSpice;
292 } else if(map(x,y) == Terrain_ThickSpice) {
293 // do not "downgrade" thick spice to spice
294 type2Place = Terrain_ThickSpice;
295 }
296 } else if(type == Terrain_Dunes) {
297 if(map(x,y) != Terrain_Sand) {
298 // Do not place the dunes spot, priority is ROCK and SPICE!
299 continue;
300 }
301 }
302
303
304
305 for(int m=0; m < mapMirror->getSize(); m++) {
306 Coord position = mapMirror->getCoord(Coord(x, y), m);
307 map(position.x,position.y) = type2Place;
308 }
309 }
310 }
311
312
313 /**
314 Adds amount number of rock tiles to the map
315 \param amount the number of rock tiles to add
316 */
addRockBits(int amount)317 void addRockBits(int amount) {
318 int done = 0;
319 for(int j = 0; (done < amount) && (j < 1000) ; j++) {
320 int spotX = randGen.rand(0, map.getSizeX()-1);
321 int spotY = randGen.rand(0, map.getSizeY()-1);
322
323 if(map(spotX, spotY) == Terrain_Sand) {
324 for(int m=0; m < mapMirror->getSize(); m++) {
325 Coord position = mapMirror->getCoord(Coord(spotX, spotY), m);
326 map(position.x,position.y) = Terrain_Rock;
327 }
328 done++;
329 }
330 }
331 }
332
333 /**
334 Adds amount number of spice blooms to the map
335 \param amount the number of spice blooms to add
336 */
addSpiceBlooms(int amount)337 void addSpiceBlooms(int amount) {
338 int done = 0;
339 for(int j = 0; (done < amount) && (j < 1000) ; j++) {
340 int spotX = randGen.rand(0, map.getSizeX()-1);
341 int spotY = randGen.rand(0, map.getSizeY()-1);
342
343 if(map(spotX, spotY) == Terrain_Sand) {
344 for(int m=0; m < mapMirror->getSize(); m++) {
345 Coord position = mapMirror->getCoord(Coord(spotX, spotY), m);
346 map(position.x,position.y) = Terrain_SpiceBloom;
347 }
348 done++;
349 }
350 }
351 }
352
353 private:
354 MapData map;
355
356 Random randGen;
357
358 int rockfields;
359 int spicefields;
360
361 std::shared_ptr<MapMirror> mapMirror;
362 };
363
364 /**
365 Creates a random map
366 \param sizeX width of the new map (in tiles)
367 \param sizeY height of the new map (in tiles)
368 \param randSeed the seed value for the random generator
369 \param rockfields num rock fields to add
370 \param spicefields num spice fields to add
371 \return the generated map
372 */
generateRandomMap(int sizeX,int sizeY,int randSeed,int rockfields,int spicefields,MirrorMode mirrorMode)373 MapData generateRandomMap(int sizeX, int sizeY, int randSeed, int rockfields, int spicefields, MirrorMode mirrorMode) {
374 MapGenerator mapGenerator(sizeX, sizeY, randSeed, rockfields, spicefields, mirrorMode);
375
376 mapGenerator.generateMap();
377
378 return mapGenerator.getMap();
379 }
380