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