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