1 #include "map.h"
2 
3 #include <cmath>
4 #include <stdexcept>
5 
6 using namespace Shared::Util;
7 using namespace std;
8 
9 namespace Glest{ namespace MapEditor{
10 
11 // ===============================================
12 //	class Map
13 // ===============================================
14 
15 // ================== PUBLIC =====================
16 
Map()17 Map::Map(){
18     altFactor= 3;
19     waterLevel= 4;
20     cells= NULL;
21     startLocations= NULL;
22     reset(64, 64, 10.f, 1);
23     resetPlayers(4);
24     title="";
25     desc="";
26     author="";
27 	refAlt= 10;
28 }
29 
~Map()30 Map::~Map(){
31      delete [] startLocations;
32 	 for(int i=0; i<h; i++){
33           delete cells[i];
34 	 }
35      delete cells;
36 }
37 
38 
getHeight(int x,int y) const39 float Map::getHeight(int x, int y) const{
40      return cells[x][y].height;
41 }
42 
getSurface(int x,int y) const43 int Map::getSurface(int x, int y) const{
44      return cells[x][y].surface;
45 }
46 
getObject(int x,int y) const47 int Map::getObject(int x, int y) const{
48      return cells[x][y].object;
49 }
50 
getResource(int x,int y) const51 int Map::getResource(int x, int y) const{
52      return cells[x][y].resource;
53 }
54 
getStartLocationX(int index) const55 int Map::getStartLocationX(int index) const{
56      return startLocations[index].x;
57 }
58 
getStartLocationY(int index) const59 int Map::getStartLocationY(int index) const{
60      return startLocations[index].y;
61 }
62 
get_dist(int delta_x,int delta_y)63 static int get_dist(int delta_x, int delta_y)
64 {
65 	double dx = delta_x;
66 	double dy = delta_y;
67 	return static_cast<int> (sqrt(dx * dx + dy * dy));
68 }
69 
changeHeight(int x,int y,int height,int radius)70 void Map::changeHeight(int x, int y, int height, int radius){
71 
72 	for (int i=x-radius+1; i<x+radius; i++){
73 		for (int j=y-radius+1; j<y+radius; j++){
74 			if (inside(i, j)){
75 				int dist= get_dist(i-x, j-y);
76 				if (radius>dist){
77 					int oldAlt= static_cast<int>(cells[i][j].height);
78 					int altInc= height * (radius-dist-1)/radius;
79 					if(height>0){
80 						altInc++;
81 					}
82 					if(height<0){
83 						altInc--;
84 						}
85 					int newAlt= refAlt + altInc;
86 					if((height>0 && newAlt>oldAlt) || (height<0 && newAlt<oldAlt) || height==0){
87 						if(newAlt>=0 && newAlt<=20){
88 							cells[i][j].height= static_cast<float>(newAlt);
89 						}
90 					}
91 				}
92 			}
93 		}
94 	}
95 }
96 
setRefAlt(int x,int y)97 void Map::setRefAlt(int x, int y){
98 	if(inside(x, y)){
99 		refAlt= static_cast<int>(cells[x][y].height);
100 	}
101 }
102 
flipX()103 void Map::flipX(){
104 	Cell **oldCells= cells;
105 
106 	cells= new Cell*[w];
107 	for (int i=0; i<w; i++){
108 		cells[i]= new Cell[h];
109 		for (int j=0; j<h; j++){
110 			cells[i][j].height= oldCells[w-i-1][j].height;
111 			cells[i][j].object= oldCells[w-i-1][j].object;
112 			cells[i][j].resource= oldCells[w-i-1][j].resource;
113 			cells[i][j].surface= oldCells[w-i-1][j].surface;
114 		}
115 	}
116 
117 	for(int i=0; i<maxPlayers; ++i){
118 		startLocations[i].x= w-startLocations[i].x-1;
119 	}
120 
121 	for (int i=0; i<w; i++){
122 		delete oldCells[i];
123 	}
124 	delete oldCells;
125 }
126 
flipY()127 void Map::flipY(){
128 	Cell **oldCells= cells;
129 
130 	cells= new Cell*[w];
131 	for (int i=0; i<w; i++){
132 		cells[i]= new Cell[h];
133 		for (int j=0; j<h; j++){
134 			cells[i][j].height= oldCells[i][h-j-1].height;
135 			cells[i][j].object= oldCells[i][h-j-1].object;
136 			cells[i][j].resource= oldCells[i][h-j-1].resource;
137 			cells[i][j].surface= oldCells[i][h-j-1].surface;
138 		}
139 	}
140 
141 	for(int i=0; i<maxPlayers; ++i){
142 		startLocations[i].y= h-startLocations[i].y-1;
143 	}
144 
145 	for (int i=0; i<w; i++){
146 		delete oldCells[i];
147 	}
148 	delete oldCells;
149 }
150 
changeSurface(int x,int y,int surface,int radius)151 void Map::changeSurface(int x, int y, int surface, int radius){
152      int i, j;
153      int dist;
154 
155      for (i=x-radius+1; i<x+radius; i++){
156           for (j=y-radius+1; j<y+radius; j++){
157                if (inside(i, j)){
158                     dist= get_dist(i-x, j-y);
159                     if (radius>=dist){
160                          cells[i][j].surface= surface;
161                     }
162                }
163           }
164      }
165 }
166 
changeObject(int x,int y,int object,int radius)167 void Map::changeObject(int x, int y, int object, int radius){
168      int i, j;
169      int dist;
170 
171      for (i=x-radius+1; i<x+radius; i++){
172           for (j=y-radius+1; j<y+radius; j++){
173                if (inside(i, j)){
174                     dist= get_dist(i-x, j-y);
175                     if (radius>=dist){
176                          cells[i][j].object= object;
177                          cells[i][j].resource= 0;
178                     }
179                }
180           }
181      }
182 }
183 
changeResource(int x,int y,int resource,int radius)184 void Map::changeResource(int x, int y, int resource, int radius){
185      int i, j;
186      int dist;
187 
188      for (i=x-radius+1; i<x+radius; i++){
189           for (j=y-radius+1; j<y+radius; j++){
190                if (inside(i, j)){
191                     dist= get_dist(i-x, j-y);
192                     if (radius>=dist){
193                          cells[i][j].resource= resource;
194                          cells[i][j].object= 0;
195                     }
196                }
197           }
198      }
199 }
200 
changeStartLocation(int x,int y,int player)201 void Map::changeStartLocation(int x, int y, int player){
202      if ((player-1)<maxPlayers && inside(x, y)){
203           startLocations[player].x= x;
204           startLocations[player].y= y;
205      }
206 }
207 
inside(int x,int y)208 bool Map::inside(int x, int y){
209      return (x>=0 && x<w && y>=0 && y<h);
210 }
211 
reset(int w,int h,float alt,int surf)212 void Map::reset(int w, int h, float alt, int surf){
213 	if (w<16 || h<16){
214 		throw runtime_error("Size of map must be at least 16x16");
215 		return;
216 	}
217 
218 	if (w>1024 || h>1024){
219 		throw runtime_error("Size of map can be at most 1024x1024");
220 		return;
221 	}
222 
223 	if (alt<0 || alt>20){
224 		throw runtime_error("Height must be in the range 0-20");
225 		return;
226 	}
227 
228 	if (surf<1 || surf>5){
229 		throw runtime_error("Surface must be in the range 1-5");
230 		return;
231 	}
232 
233 	if (cells!=NULL){
234 		for(int i=0; i<this->w; i++){
235 			delete cells[i];
236 		}
237 		delete cells;
238 	}
239 
240 	this->w= w;
241 	this->h= h;
242 	this->maxPlayers= maxPlayers;
243 
244 	cells= new Cell*[w];
245 	for (int i=0; i<w; i++){
246 		cells[i]= new Cell[h];
247 		for (int j=0; j<h; j++){
248 			cells[i][j].height= alt;
249 			cells[i][j].object= 0;
250 			cells[i][j].resource= 0;
251 			cells[i][j].surface= surf;
252 		}
253 	}
254 }
255 
resize(int w,int h,float alt,int surf)256 void Map::resize(int w, int h, float alt, int surf){
257 	if (w<16 || h<16){
258 		throw runtime_error("Size of map must be at least 16x16");
259 		return;
260 	}
261 
262 	if (w>1024 || h>1024){
263 		throw runtime_error("Size of map can be at most 1024x1024");
264 		return;
265 	}
266 
267 	if (alt<0 || alt>20){
268 		throw runtime_error("Height must be in the range 0-20");
269 		return;
270 	}
271 
272 	if (surf<1 || surf>5){
273 		throw runtime_error("Surface must be in the range 1-5");
274 		return;
275 	}
276 
277 	int oldW= this->w;
278 	int oldH= this->h;
279 	this->w= w;
280 	this->h= h;
281 	this->maxPlayers= maxPlayers;
282 
283 	//create new cells
284 	Cell **oldCells= cells;
285 	cells= new Cell*[w];
286 	for (int i=0; i<w; i++){
287 		cells[i]= new Cell[h];
288 		for (int j=0; j<h; j++){
289 			cells[i][j].height= alt;
290 			cells[i][j].object= 0;
291 			cells[i][j].resource= 0;
292 			cells[i][j].surface= surf;
293 		}
294 	}
295 
296 	int wOffset= w<oldW? 0: (w-oldW)/2;
297 	int hOffset= h<oldH? 0: (h-oldH)/2;
298 	//assign old values to cells
299 	for (int i=0; i<oldW; i++){
300 		for (int j=0; j<oldH; j++){
301 			if(i+wOffset<w && j+hOffset<h){
302 				cells[i+wOffset][j+hOffset].height= oldCells[i][j].height;
303 				cells[i+wOffset][j+hOffset].object= oldCells[i][j].object;
304 				cells[i+wOffset][j+hOffset].resource= oldCells[i][j].resource;
305 				cells[i+wOffset][j+hOffset].surface= oldCells[i][j].surface;
306 			}
307 		}
308 	}
309 	for(int i=0; i<maxPlayers; ++i){
310 		startLocations[i].x+= wOffset;
311 		startLocations[i].y+= hOffset;
312 	}
313 
314 	//delete old cells
315 	if (oldCells!=NULL){
316 		for(int i=0; i<oldW; i++)
317 			delete oldCells[i];
318 		delete oldCells;
319 	}
320 }
321 
resetPlayers(int maxPlayers)322 void Map::resetPlayers(int maxPlayers){
323 	if (maxPlayers<1 || maxPlayers>4){
324 		throw runtime_error("Max Players must be in the range 1-4");
325 		return;
326 	}
327 
328 	if (startLocations!=NULL)
329 		delete startLocations;
330 
331 	this->maxPlayers= maxPlayers;
332 
333 	startLocations= new StartLocation[maxPlayers];
334 	for (int i=0; i<maxPlayers; i++){
335 		startLocations[i].x= 0;
336 		startLocations[i].y= 0;
337 	}
338 }
339 
setTitle(const string & title)340 void Map::setTitle(const string &title){
341 	this->title= title;
342 }
343 
setDesc(const string & desc)344 void Map::setDesc(const string &desc){
345 	this->desc= desc;
346 }
347 
setAuthor(const string & author)348 void Map::setAuthor(const string &author){
349 	this->author= author;
350 }
351 
setAdvanced(int altFactor,int waterLevel)352 void Map::setAdvanced(int altFactor, int waterLevel){
353      this->altFactor= altFactor;
354      this->waterLevel= waterLevel;
355 }
356 
getHeightFactor() const357 int Map::getHeightFactor() const{
358      return altFactor;
359 }
360 
getWaterLevel() const361 int Map::getWaterLevel() const{
362      return waterLevel;
363 }
364 
randomizeHeights()365 void Map::randomizeHeights(){
366 	resetHeights(random.randRange(8, 10));
367 	sinRandomize(0);
368 	decalRandomize(4);
369 	sinRandomize(1);
370 }
371 
randomize()372 void Map::randomize(){
373 	randomizeHeights();
374 
375 	int slPlaceFactorX= random.randRange(0, 1);
376 	int slPlaceFactorY= random.randRange(0, 1)*2;
377 
378 	for(int i=0; i<maxPlayers; ++i){
379 		StartLocation sl;
380 		float slNoiseFactor= random.randRange(0.5f, 0.8f);
381 
382 		sl.x= static_cast<int>(w*slNoiseFactor * ((i+slPlaceFactorX)%2) + w*(1.f-slNoiseFactor)/2.f);
383 		sl.y= static_cast<int>(h*slNoiseFactor * (((i+slPlaceFactorY)/2) % 2)+ h*(1.f-slNoiseFactor)/2.f);
384 		startLocations[i]= sl;
385 	}
386 }
387 
switchSurfaces(int surf1,int surf2)388 void Map::switchSurfaces(int surf1, int surf2){
389 	if(surf1>0 && surf1<=5 && surf2>0 && surf2<=5){
390 		for(int i=0; i<w; ++i){
391 			for(int j=0; j<h; ++j){
392 				if(cells[i][j].surface==surf1){
393 					cells[i][j].surface= surf2;
394 				}
395 				else if(cells[i][j].surface==surf2){
396 					cells[i][j].surface= surf1;
397 				}
398 			}
399 		}
400 	}
401 	else{
402 		throw runtime_error("Incorrect surfaces");
403 	}
404 }
405 
loadFromFile(const string & path)406 void Map::loadFromFile(const string &path){
407 
408 	FILE *f1= fopen(path.c_str(), "rb");
409 	if(f1!=NULL){
410 
411 		//read header
412 		MapFileHeader header;
413 		fread(&header, sizeof(MapFileHeader), 1, f1);
414 
415 		altFactor= header.altFactor;
416 		waterLevel= header.waterLevel;
417 		title= header.title;
418 		author= header.author;
419 		desc= header.description;
420 
421 		//read start locations
422 		resetPlayers(header.maxPlayers);
423 		for(int i=0; i<maxPlayers; ++i){
424 			fread(&startLocations[i].x, sizeof(int32), 1, f1);
425 			fread(&startLocations[i].y, sizeof(int32), 1, f1);
426 		}
427 
428 		//read Heights
429 		reset(header.width, header.height, 10, 1);
430 		for(int j=0; j<h; ++j){
431 			for(int i=0; i<w; ++i){
432 				fread(&cells[i][j].height, sizeof(float), 1, f1);
433 			}
434 		}
435 
436 		//read surfaces
437 		for(int j=0; j<h; ++j){
438 			for(int i=0; i<w; ++i){
439 				fread(&cells[i][j].surface, sizeof(int8), 1, f1);
440 			}
441 		}
442 
443 		//read objects
444 		for(int j=0; j<h; ++j){
445 			for(int i=0; i<w; ++i){
446 				int8 obj;
447 				fread(&obj, sizeof(int8), 1, f1);
448 				if(obj<=10){
449 					cells[i][j].object= obj;
450 				}
451 				else{
452 					cells[i][j].resource= obj-10;
453 				}
454 			}
455 		}
456 
457 		fclose(f1);
458 	}
459 	else{
460 		throw runtime_error("error opening map file: "+ path);
461 	}
462 }
463 
464 
saveToFile(const string & path)465 void Map::saveToFile(const string &path){
466 
467 	FILE *f1= fopen(path.c_str(), "wb");
468 	if(f1!=NULL){
469 
470 		//write header
471 		MapFileHeader header;
472 
473 		header.version= 1;
474 		header.maxPlayers= maxPlayers;
475 		header.width= w;
476 		header.height= h;
477 		header.altFactor= altFactor;
478 		header.waterLevel= waterLevel;
479 		strncpy(header.title, title.c_str(), 128);
480 		strncpy(header.author, author.c_str(), 128);
481 		strncpy(header.description, desc.c_str(), 256);
482 
483 		fwrite(&header, sizeof(MapFileHeader), 1, f1);
484 
485 		//write start locations
486 		for(int i=0; i<maxPlayers; ++i){
487 			fwrite(&startLocations[i].x, sizeof(int32), 1, f1);
488 			fwrite(&startLocations[i].y, sizeof(int32), 1, f1);
489 		}
490 
491 		//write Heights
492 		for(int j=0; j<h; ++j){
493 			for(int i=0; i<w; ++i){
494 				fwrite(&cells[i][j].height, sizeof(float32), 1, f1);
495 			}
496 		}
497 
498 		//write surfaces
499 		for(int j=0; j<h; ++j){
500 			for(int i=0; i<w; ++i){
501 				fwrite(&cells[i][j].surface, sizeof(int8), 1, f1);
502 			}
503 		}
504 
505 		//write objects
506 		for(int j=0; j<h; ++j){
507 			for(int i=0; i<w; ++i){
508 				if(cells[i][j].resource==0)
509 					fwrite(&cells[i][j].object, sizeof(int8), 1, f1);
510 				else{
511 					int8 res= cells[i][j].resource+10;
512 					fwrite(&res, sizeof(int8), 1, f1);
513 				}
514 			}
515 		}
516 
517 		fclose(f1);
518 
519 	}
520 	else{
521 		throw runtime_error("Error opening map file: "+ path);
522 	}
523 
524 	void randomHeight(int x, int y, int height);
525 }
526 
527 // ==================== PRIVATE ====================
528 
resetHeights(int height)529 void Map::resetHeights(int height){
530 	for(int i=0; i<w; ++i){
531 		for(int j=0; j<h; ++j){
532 			cells[i][j].height= static_cast<float>(height);
533 		}
534 	}
535 }
536 
sinRandomize(int strenght)537 void Map::sinRandomize(int strenght){
538 	float sinH1= random.randRange(5.f, 40.f);
539 	float sinH2= random.randRange(5.f, 40.f);
540 	float sinV1= random.randRange(5.f, 40.f);
541 	float sinV2= random.randRange(5.f, 40.f);
542 	float ah= static_cast<float>(10 + random.randRange(-2, 2));
543 	float bh= static_cast<float>((maxHeight-minHeight)/random.randRange(2, 3));
544 	float av= static_cast<float>(10 + random.randRange(-2, 2));
545 	float bv= static_cast<float>((maxHeight-minHeight)/random.randRange(2, 3));
546 
547 	for(int i=0; i<w; ++i){
548 		for(int j=0; j<h; ++j){
549 			float normH= static_cast<float>(i)/w;
550 			float normV= static_cast<float>(j)/h;
551 
552 			float sh= (sin(normH*sinH1) + sin(normH*sinH2))/2.f;
553 			float sv= (sin(normV*sinV1) + sin(normV*sinV2))/2.f;
554 
555 			float newHeight= (ah+bh*sh+av+bv*sv)/2.f;
556 			applyNewHeight(newHeight, i, j, strenght);
557 		}
558 	}
559 }
560 
decalRandomize(int strenght)561 void Map::decalRandomize(int strenght){
562 	//first row
563 	int lastHeight= 10;
564 	for(int i=0; i<w; ++i){
565 		lastHeight+= random.randRange(-1, 1);
566 		lastHeight= clamp(lastHeight, minHeight, maxHeight);
567 		applyNewHeight(static_cast<float>(lastHeight), i, 0, strenght);
568 	}
569 
570 	//other rows
571 	for(int j=1; j<h; ++j){
572 		int height= static_cast<int>(cells[0][j-1].height+random.randRange(-1, 1));
573 		applyNewHeight(static_cast<float>(clamp(height, minHeight, maxHeight)), 0, j, strenght);
574 		for(int i=1; i<w; ++i){
575 			height= static_cast<int>((cells[i][j-1].height+cells[i-1][j].height)/2.f+random.randRange(-1, 1));
576 			float newHeight= static_cast<float>(clamp(height, minHeight, maxHeight));
577 			applyNewHeight(newHeight, i, j, strenght);
578 		}
579 	}
580 }
581 
applyNewHeight(float newHeight,int x,int y,int strenght)582 void Map::applyNewHeight(float newHeight, int x, int y, int strenght){
583 	cells[x][y].height= static_cast<float>(((cells[x][y].height*strenght)+newHeight)/(strenght+1));
584 }
585 
586 }}// end namespace
587