1 #include <INIMap/INIMapPreviewCreator.h>
2 
3 #include <FileClasses/FileManager.h>
4 
5 #include <MapSeed.h>
6 #include <RadarView.h>
7 
8 #include <misc/draw_util.h>
9 #include <misc/format.h>
10 #include <misc/exceptions.h>
11 
12 #include <mmath.h>
13 #include <sand.h>
14 #include <globals.h>
15 
16 #include <stdio.h>
17 #include <algorithm>
18 
INIMapPreviewCreator(std::shared_ptr<INIFile> & pINIFile)19 INIMapPreviewCreator::INIMapPreviewCreator(std::shared_ptr<INIFile>& pINIFile)
20  : INIMap(pINIFile)
21 {
22 
23 }
24 
~INIMapPreviewCreator()25 INIMapPreviewCreator::~INIMapPreviewCreator() {
26 }
27 
28 /**
29     This method is used to create a mini map of a map file before the map is being played (e.g. in the map selection menu).
30     The surface is 128x128 pixels plus 2*borderWidth and the map is scaled appropriately.
31     \param  borderWidth the width of the border
32     \param  borderColor the color of the border
33     \return the minimap of size (128+2*borderWidth)x(128+2*borderWidth)
34 */
createMinimapImageOfMap(int borderWidth,Uint32 borderColor)35 SDL_Surface* INIMapPreviewCreator::createMinimapImageOfMap(int borderWidth, Uint32 borderColor) {
36     checkFeatures();
37 
38     SDL_Surface* pMinimap;
39     // create surface
40     if((pMinimap = SDL_CreateRGBSurface(0, 128+2*borderWidth, 128+2*borderWidth, SCREEN_BPP, RMASK, GMASK, BMASK, AMASK)) == nullptr) {
41         return nullptr;
42     }
43     SDL_FillRect(pMinimap, nullptr, borderColor);
44     SDL_Rect dest = { borderWidth, borderWidth, pMinimap->w - 2*borderWidth, pMinimap->h - 2*borderWidth};
45     SDL_FillRect(pMinimap, &dest, COLOR_BLACK);
46 
47     int version = inifile->getIntValue("BASIC", "Version", 1);
48 
49     int offsetX = 0;
50     int offsetY = 0;
51     int scale = 1;
52     int sizeX = 64;
53     int sizeY = 64;
54     int logicalSizeX = 64;
55     int logicalSizeY = 64;
56     int logicalOffsetX = 0;
57     int logicalOffsetY = 0;
58 
59     if(version < 2) {
60         // old map format with seed value
61         int SeedNum = inifile->getIntValue("MAP","Seed",-1);
62 
63         if(SeedNum == -1) {
64             SDL_FreeSurface(pMinimap);
65             logError("Cannot read Seed in this map!");
66         }
67 
68         int mapscale = inifile->getIntValue("BASIC","MapScale",0);
69 
70         switch(mapscale) {
71             case 0: {
72                 scale = 2;
73                 sizeX = 62;
74                 sizeY = 62;
75                 offsetX = 2;
76                 offsetY = 2;
77                 logicalOffsetX = 1;
78                 logicalOffsetY = 1;
79             } break;
80 
81             case 1: {
82                 scale = 4;
83                 sizeX = 32;
84                 sizeY = 32;
85                 logicalOffsetX = 16;
86                 logicalOffsetY = 16;
87             } break;
88 
89             case 2: {
90                 scale = 5;
91                 sizeX = 21;
92                 sizeY = 21;
93                 offsetX = 11;
94                 offsetY = 11;
95                 logicalOffsetX = 11;
96                 logicalOffsetY = 11;
97             } break;
98 
99             default: {
100                 SDL_FreeSurface(pMinimap);
101                 logError("Unknown MapScale!");
102             } break;
103         }
104 
105         logicalSizeX = 64;
106         logicalSizeY = 64;
107 
108         offsetX += borderWidth;
109         offsetY += borderWidth;
110 
111         Uint16 SeedMap[64*64];
112         createMapWithSeed(SeedNum,SeedMap);
113 
114         // "draw" spice fields into SeedMap
115         std::string FieldString = inifile->getStringValue("MAP","Field");
116         if(FieldString != "") {
117             std::vector<std::string> FieldPositions  = splitString(FieldString);
118 
119             for(unsigned int i=0; i < FieldPositions.size();i++) {
120                 // set bloom
121                 int FieldPos;
122                 if(parseString(FieldPositions[i], FieldPos)) {
123                     int xpos = FieldPos % logicalSizeX;
124                     int ypos = FieldPos / logicalSizeX;
125 
126                     for(int x = -5; x <= 5; x++) {
127                         for(int y = -5; y <= 5; y++) {
128 
129                             if(xpos+x >= 0 && xpos+x < logicalSizeX && ypos+y >= 0 && ypos+y < logicalSizeY) {
130                                 if(((SeedMap[64*(ypos+y) + (xpos+x)] >> 4) == 0x7)
131                                     && (distanceFrom(xpos, ypos, xpos + x, ypos + y) <= 5)) {
132 
133                                     SeedMap[64*(ypos+y) + (xpos+x)] = (x==0 && y==0) ? 0xC0 : 0xB0;
134                                 }
135                             }
136                         }
137                     }
138 
139                 } else {
140                     SDL_FreeSurface(pMinimap);
141                     logError(inifile->getKey("MAP", "Field")->getLineNumber(), "Invalid value for key Field: " + FieldString);
142                 }
143             }
144         }
145 
146         for(int y = 0; y < sizeY; y++) {
147             for(int x = 0; x < sizeX; x++) {
148                 Uint32 color = COLOR_BLACK;
149                 unsigned char seedmaptype = SeedMap[(y+logicalOffsetY)*64+x+logicalOffsetX] >> 4;
150                 switch(seedmaptype) {
151 
152                     case 0x7: {
153                         // Normal sand
154                         color = COLOR_DESERTSAND;
155                     } break;
156 
157                     case 0x2:
158                     case 0x8: {
159                         // Rock or building
160                         color = COLOR_ROCK;
161                     } break;
162 
163                     case 0x9: {
164                         // Sand dunes
165                         color = COLOR_DESERTSAND;
166                     } break;
167 
168                     case 0xa: {
169                         // Mountain
170                         color = COLOR_MOUNTAIN;
171                     } break;
172 
173                     case 0xb: {
174                         // Spice
175                         color = COLOR_SPICE;
176                     } break;
177 
178                     case 0xc: {
179                         // Thick spice
180                         color = COLOR_THICKSPICE;
181                     } break;
182                 }
183 
184                 for(int i=0;i<scale;i++) {
185                     for(int j=0;j<scale;j++) {
186                         putPixel(pMinimap, x*scale + i + offsetX, y*scale + j + offsetY, color);
187                     }
188                 }
189             }
190         }
191 
192         // draw spice blooms
193         std::string BloomString = inifile->getStringValue("MAP","Bloom");
194         if(BloomString != "") {
195             std::vector<std::string> BloomPositions  = splitString(BloomString);
196 
197             for(unsigned int i=0; i < BloomPositions.size();i++) {
198                 // set bloom
199                 int BloomPos;
200                 if(parseString(BloomPositions[i], BloomPos)) {
201                     int xpos = BloomPos % logicalSizeX - logicalOffsetX;
202                     int ypos = BloomPos / logicalSizeX - logicalOffsetY;
203                     if(xpos >= 0 && xpos < sizeX && ypos >= 0 && ypos < sizeY) {
204                         for(int i=0;i<scale;i++) {
205                             for(int j=0;j<scale;j++) {
206                                 putPixel(pMinimap, xpos*scale + i + offsetX, ypos*scale + j + offsetY, COLOR_BLOOM);
207                             }
208                         }
209                     }
210                 } else {
211                     SDL_FreeSurface(pMinimap);
212                     logError(inifile->getKey("MAP", "Bloom")->getLineNumber(), "Invalid value for key Bloom: " + BloomString);
213                 }
214             }
215         }
216 
217         // draw special blooms
218         std::string SpecialString = inifile->getStringValue("MAP","Special");
219         if(SpecialString != "") {
220             std::vector<std::string> SpecialPositions  = splitString(SpecialString);
221 
222             for(unsigned int i=0; i < SpecialPositions.size();i++) {
223                 // set bloom
224                 int SpecialPos;
225                 if(parseString(SpecialPositions[i], SpecialPos)) {
226                     int xpos = SpecialPos % logicalSizeX - logicalOffsetX;
227                     int ypos = SpecialPos / logicalSizeX - logicalOffsetY;
228                     if(xpos >= 0 && xpos < sizeX && ypos >= 0 && ypos < sizeY) {
229                         for(int i=0;i<scale;i++) {
230                             for(int j=0;j<scale;j++) {
231                                 putPixel(pMinimap, xpos*scale + i + offsetX, ypos*scale + j + offsetY, COLOR_BLOOM);
232                             }
233                         }
234                     }
235                 } else {
236                     SDL_FreeSurface(pMinimap);
237                     logError(inifile->getKey("MAP", "Special")->getLineNumber(), "Invalid value for key Special: " + SpecialString);
238                 }
239             }
240         }
241 
242 
243     } else {
244         // new map format with saved map
245 
246         if((inifile->hasKey("MAP","SizeX") == false) || (inifile->hasKey("MAP","SizeY") == false)) {
247             SDL_FreeSurface(pMinimap);
248             logError("SizeX and SizeY must be specified!");
249         }
250 
251         sizeX = inifile->getIntValue("MAP","SizeX", 0);
252         sizeY = inifile->getIntValue("MAP","SizeY", 0);
253 
254         logicalSizeX = sizeX;
255         logicalSizeY = sizeY;
256 
257         RadarView::calculateScaleAndOffsets(sizeX, sizeY, scale, offsetX, offsetY);
258 
259         offsetX += borderWidth;
260         offsetY += borderWidth;
261 
262         for(int y=0;y<sizeY;y++) {
263             std::string rowKey = fmt::sprintf("%.3d", y);
264 
265             if(inifile->hasKey("MAP", rowKey) == false) {
266                 SDL_FreeSurface(pMinimap);
267                 logError(inifile->getSection("MAP").getLineNumber(), "Map row " + stringify(y) + " does not exist!");
268             }
269 
270             std::string rowString = inifile->getStringValue("MAP",rowKey);
271             for(int x=0;x<sizeX;x++) {
272                 Uint32 color = COLOR_BLACK;
273                 switch(rowString.at(x)) {
274                     case '-': {
275                         // Normal sand
276                         color = COLOR_DESERTSAND;
277                     } break;
278 
279                     case '^': {
280                         // Sand dunes
281                         color = COLOR_DESERTSAND;
282                     } break;
283 
284                     case '~': {
285                         // Spice
286                         color = COLOR_SPICE;
287                     } break;
288 
289                     case '+': {
290                         // Thick spice
291                         color = COLOR_THICKSPICE;
292                     } break;
293 
294                     case '%': {
295                         // Rock
296                         color = COLOR_ROCK;
297                     } break;
298 
299                     case '@': {
300                         // Mountain
301                         color = COLOR_MOUNTAIN;
302                     } break;
303 
304                     case 'O':
305                     case 'Q': {
306                         // Spice Bloom and Special Bloom
307                         color = COLOR_BLOOM;
308                     } break;
309 
310                     default: {
311                         SDL_FreeSurface(pMinimap);
312                         logError(inifile->getKey("MAP", rowKey)->getLineNumber(), std::string("Unknown map tile type '") + rowString.at(x) + "' in map tile (" + stringify(x) + ", " + stringify(y) + ")!");
313                     } break;
314                 }
315 
316                 for(int i=0;i<scale;i++) {
317                     for(int j=0;j<scale;j++) {
318                         putPixel(pMinimap, x*scale + i + offsetX, y*scale + j + offsetY, color);
319                     }
320                 }
321             }
322         }
323     }
324 
325     // draw structures
326     if(inifile->hasSection("STRUCTURES")) {
327         for(const INIFile::Key& key : inifile->getSection("STRUCTURES")) {
328             std::string tmpkey = key.getKeyName();
329             std::string tmp = key.getStringValue();
330 
331             if(tmpkey.compare(0,3,"GEN") == 0) {
332                 // Gen Object/Structure
333 
334                 std::string PosStr = tmpkey.substr(3,tmpkey.size()-3);
335                 int pos;
336                 if(!parseString(PosStr, pos)) {
337                     continue;
338                 }
339 
340                 std::string HouseStr, BuildingStr;
341                 splitString(tmp,2,&HouseStr,&BuildingStr);
342 
343                 int house = getHouseByName(HouseStr);
344                 Uint32 color = COLOR_WHITE;
345                 if(house != HOUSE_INVALID) {
346                     color = SDL2RGB(palette[houseToPaletteIndex[house]]);
347                 } else {
348                     convertToLower(HouseStr);
349                     if(HouseStr.length() == 7 && HouseStr.substr(0,6) == "player") {
350                         int playernum = HouseStr.at(6)-'0';
351 
352                         if(playernum >= 1 && playernum <= 6) {
353                             int val = 32*(playernum - 1) + 32;
354                             color = COLOR_RGB(val, val, val);
355                         }
356                     } else {
357                         SDL_FreeSurface(pMinimap);
358                         logError(key.getLineNumber(), "Invalid house string: '" + HouseStr + "'!");
359                     }
360                 }
361 
362                 if(BuildingStr == "Concrete") {
363                     // nothing
364                 } else if(BuildingStr == "Wall") {
365                     int x = pos % logicalSizeX - logicalOffsetX;
366                     int y = pos / logicalSizeX - logicalOffsetY;
367 
368                     if(x >= 0 && x < sizeX && y >= 0 && y < sizeY) {
369                         for(int i=0;i<scale;i++) {
370                             for(int j=0;j<scale;j++) {
371                                 putPixel(pMinimap, x*scale + i + offsetX, y*scale + j + offsetY, color);
372                             }
373                         }
374                     }
375                 }
376             } else if(key.getKeyName().find("ID") == 0) {
377                 // other structure
378                 std::string HouseStr, BuildingStr, health, PosStr;
379                 splitString(tmp,6,&HouseStr,&BuildingStr,&health,&PosStr);
380 
381                 int pos;
382                 if(!parseString(PosStr, pos)) {
383                     continue;
384                 }
385 
386                 int house = getHouseByName(HouseStr);
387                 Uint32 color = COLOR_WHITE;
388                 if(house != HOUSE_INVALID) {
389                     color = SDL2RGB(palette[houseToPaletteIndex[house]]);
390                 } else {
391                     convertToLower(HouseStr);
392                     if(HouseStr.length() == 7 && HouseStr.substr(0,6) == "player") {
393                         int playernum = HouseStr.at(6)-'0';
394 
395                         if(playernum >= 1 && playernum <= 6) {
396                             int val = 32*(playernum - 1) + 32;
397                             color = COLOR_RGB(val, val, val);
398                         }
399                     } else {
400                         SDL_FreeSurface(pMinimap);
401                         logError(key.getLineNumber(), "Invalid house string: '" + HouseStr + "'!");
402                     }
403                 }
404 
405                 Coord size = getStructureSize(getItemIDByName(BuildingStr));
406 
407                 int posX = pos % logicalSizeX - logicalOffsetX;
408                 int posY = pos / logicalSizeX - logicalOffsetY;
409                 for(int x = posX; x < posX + size.x; x++) {
410                     for(int y = posY; y < posY + size.y; y++) {
411                         if(x >= 0 && x < sizeX && y >= 0 && y < sizeY) {
412                             for(int i=0;i<scale;i++) {
413                                 for(int j=0;j<scale;j++) {
414                                     putPixel(pMinimap, x*scale + i + offsetX, y*scale + j + offsetY, color);
415                                 }
416                             }
417                         }
418                     }
419                 }
420             }
421         }
422     }
423 
424     return pMinimap;
425 }
426