1 // Copyright (C) 2007, 2008, 2009, 2014, 2015 Ben Asselstine
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16 // 02110-1301, USA.
17
18 #include <iostream>
19 #include <sstream>
20 #include <string>
21
22 #include "FogMap.h"
23 #include "SightMap.h"
24
25 #include "playerlist.h"
26 #include "xmlhelper.h"
27 #include "GameScenarioOptions.h"
28
29 Glib::ustring FogMap::d_tag = "fogmap";
30
31 //#define debug(x) {std::cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<std::endl<<flush;}
32 #define debug(x)
33
34
FogMap(int width,int height)35 FogMap::FogMap(int width, int height)
36 {
37 debug("FogMap()");
38 d_width = width;
39 d_height = height;
40
41 d_fogmap = new FogType[d_width*d_height];
42 shademap = new ShadeType[d_width*d_height];
43
44 fill(OPEN);
45 }
46
FogMap(XML_Helper * helper)47 FogMap::FogMap(XML_Helper* helper)
48 {
49 Glib::ustring t;
50
51 helper->getData(d_width, "width");
52 helper->getData(d_height, "height");
53 helper->getData(t, "map");
54 std::string types = t.raw();
55 types.erase (std::remove(types.begin(), types.end(), '\n'), types.end());
56 types.erase (std::remove(types.begin(), types.end(), '\r'), types.end());
57
58 //create the map
59 d_fogmap = new FogType[d_width*d_height];
60
61 for (int y = 0; y < d_height; y++)
62 {
63 for (int x = 0; x < d_width; x++)
64 {
65 //due to the circumstances, types is a long stream of
66 //numbers, so read it character for character (no \n's or so)
67 d_fogmap[y*d_width + x] = FogType(types[y*d_width + x] - '0');
68 }
69 }
70 shademap = new ShadeType[d_width*d_height];
71 calculateShadeMap();
72 }
73
FogMap(const FogMap & fogmap)74 FogMap::FogMap(const FogMap& fogmap)
75 :d_width(fogmap.d_width), d_height(fogmap.d_height)
76 {
77 //create the map
78 d_fogmap = new FogType[d_width*d_height];
79
80 for (int y = 0; y < d_height; y++)
81 {
82 for (int x = 0; x < d_width; x++)
83 {
84 d_fogmap[y*d_width + x] = fogmap.d_fogmap[y*d_width + x];
85 }
86 }
87 shademap = new ShadeType[d_width*d_height];
88 for (int y = 0; y < d_height; y++)
89 {
90 for (int x = 0; x < d_width; x++)
91 {
92 shademap[y*d_width + x] = fogmap.shademap[y*d_width + x];
93 }
94 }
95 }
96
~FogMap()97 FogMap::~FogMap()
98 {
99 delete[] d_fogmap;
100 delete[] shademap;
101 }
102
fill(FogType type)103 bool FogMap::fill(FogType type)
104 {
105 for (int i = 0; i < d_width*d_height; i++)
106 d_fogmap[i] = type;
107
108 calculateShadeMap();
109 return true;
110 }
111
save(XML_Helper * helper) const112 bool FogMap::save(XML_Helper* helper) const
113 {
114 bool retval = true;
115
116 retval &= helper->openTag(FogMap::d_tag);
117 retval &= helper->saveData("width", d_width);
118 retval &= helper->saveData("height", d_height);
119
120 std::stringstream types;
121 types << std::endl;
122 for (int y = 0; y < d_height; y++)
123 {
124 for (int x = 0; x < d_width; x++)
125 {
126 types << static_cast<int>(d_fogmap[y*d_width + x]);
127 }
128 types << std::endl;
129 }
130
131 retval &= helper->saveData("map", types.str());
132 retval &= helper->closeTag();
133
134 return retval;
135 }
136
getFogTile(Vector<int> pos) const137 FogMap::FogType FogMap::getFogTile(Vector<int> pos) const
138 {
139 return d_fogmap[pos.y * d_width + pos.x];
140 }
141
getShadeTile(Vector<int> pos) const142 FogMap::ShadeType FogMap::getShadeTile(Vector<int> pos) const
143 {
144 return shademap[pos.y * d_width + pos.x];
145 }
146
alterFogRadius(Vector<int> pt,int radius,FogType new_type)147 void FogMap::alterFogRadius(Vector<int> pt, int radius, FogType new_type)
148 {
149 if (GameScenarioOptions::s_hidden_map == false)
150 return;
151 // this doesn't draw a circle, it draws a square
152 // it isn't a bug, except for being badly named
153 int x = pt.x - radius;
154 int y = pt.y - radius;
155 int size = 2 * radius + 1;
156 for (int i = 0; i < size; i++)
157 {
158 for (int j = 0; j < size; j++)
159 {
160 if ((x+i) < 0 || (y+j) < 0 || (x+i) >= d_width || (y+j) >= d_height)
161 continue;
162 d_fogmap[(y+j)*d_width + (x+i)] = new_type;
163 }
164 }
165 calculateShadeMap();
166 }
167
alterFogRectangle(Vector<int> pt,int height,int width,FogType new_type)168 void FogMap::alterFogRectangle(Vector<int> pt, int height, int width, FogType new_type)
169 {
170 if (GameScenarioOptions::s_hidden_map == false)
171 return;
172 int x = pt.x;
173 int y = pt.y;
174 for (int i = 0; i < height; i++)
175 {
176 for (int j = 0; j < width; j++)
177 {
178 if ((x+i) < 0 || (y+j) < 0 || (x+i) >= d_width || (y+j) >= d_height)
179 continue;
180 d_fogmap[(y+j)*d_width + (x+i)] = new_type;
181 }
182 }
183 calculateShadeMap();
184 }
185
isCompletelyObscuredFogTile(Vector<int> pos) const186 bool FogMap::isCompletelyObscuredFogTile(Vector<int> pos) const
187 {
188 if (shademap[pos.y * d_width + pos.x] == ALL)
189 return true;
190 else
191 return false;
192 return false;
193 }
194
isLoneFogTile(Vector<int> pos)195 bool FogMap::isLoneFogTile(Vector<int> pos)
196 {
197 bool west_open = false;
198 bool east_open = false;
199 //are east-west adjacent squares open?
200 if (pos.x + 1 >= d_width || d_fogmap[pos.y*d_width + pos.x + 1] == OPEN)
201 west_open = true;
202 if (pos.x - 1 < 0 || d_fogmap[pos.y*d_width + pos.x - 1] == OPEN)
203 east_open = true;
204 bool north_open = false;
205 bool south_open = false;
206 //are north-south adjacent squares open?
207 if (pos.y + 1 >= d_height || d_fogmap[(pos.y+1)*d_width + pos.x] == OPEN)
208 south_open = true;
209 if (pos.y - 1 < 0 || d_fogmap[(pos.y-1)*d_width + pos.x] == OPEN)
210 north_open = true;
211 if (east_open && west_open)
212 return true;
213 if (north_open && south_open)
214 return true;
215 return false;
216 }
217
smooth()218 void FogMap::smooth()
219 {
220 for (int y = 0; y < d_height; y++)
221 {
222 for (int x = 0; x < d_width; x++)
223 {
224 if (d_fogmap[y*d_width + x] == CLOSED)
225 {
226 Vector<int> pos;
227 pos.x = x;
228 pos.y = y;
229 if (isLoneFogTile (pos))
230 d_fogmap[y*d_width + x] = OPEN;
231 }
232 }
233 }
234
235 calculateShadeMap();
236 }
237
isFogged(Vector<int> pos)238 bool FogMap::isFogged(Vector <int> pos)
239 {
240 if (getFogTile(pos) == FogMap::CLOSED)
241 return true;
242
243 if (isLoneFogTile(pos) == true)
244 return false;
245
246 return false;
247 }
248
isClear(Vector<int> pos,Player * player)249 bool FogMap::isClear(Vector <int> pos, Player *player)
250 {
251 //is this tile visible, or not?
252 FogMap *fogmap = player->getFogMap();
253 if (fogmap->getFogTile(pos) == FogMap::OPEN)
254 return true;
255
256 return false;
257 }
258
alterFog(SightMap * sightmap)259 void FogMap::alterFog(SightMap *sightmap)
260 {
261 return alterFogRectangle(sightmap->pos, sightmap->h, sightmap->w, OPEN);
262 }
263
264 /*
265 * fog display algorithm
266 *
267 * smallmap shows fog placement
268 * - it is a peek into the data model
269 *
270 * bigmap shows a rendering of that
271 * if a tile on the bigmap is partially fogged, then it is completely fogged on the small map.
272 *
273 * this means that partially fogged tiles depend on adjacent tiles being fogged
274 * completely fogged tiles depends on adjacent tiles being fogged
275 *
276 * when one tile is fogged and is not surrounded by adjacent fogged tiles it is shown as not fogged on the bigmap, while it is shown as fogged on the small map. it is then marked as defogged at the start of the next turn.
277 *
278 * every tile has 4 faces:
279 * it can connect to an adjacent tile darkly, lightly, or not at all
280 * a dark face means the whole side is black
281 * a light face means the side is a gradient *
282 *
283 *
284 * graphics cache
285 * fog types:
286 * 1 = light corner se: connects lightly to south and east
287 * 2 = light corner sw: connects lightly to south and west
288 * 3 = light corner nw: connects lightly to north and west
289 * 4 = light corner ne: connects lightly to north and east
290 * 5 = dark corner nw: connects darkly to north and west, lightly to south and east
291 * 6 = dark corner ne: connects darkly to north and east, lightly to south and west
292 * 7 = dark corner se: connects darkly to east and south, lightly to north and west
293 * 8 = dark corner sw: connects darkly to south and west, lightly to north and east
294 * 9 = bottom to top: connects darkly to south, connects lightly to east and west
295 * 10 = top to bottom: connects darkly to north, connects lightly to east and west
296 * 11 = right to left: connects darkly to west, connects lightly to north and south
297 * 12 = left to right: connects darkly to east, connects lightly to north and south
298 * 13 = all black: connects darkly to north, south, east and west
299 *
300 * bigmap tile processing algorithm:
301 * for each tile currently being shown, examine each tile in normal order
302 *
303 * here are the cases that we can handle for fogging a tile:
304 * the sets are read as follows:
305 *
306 * 876
307 * 5x4 = (fog tile type)
308 * 321
309 * (bit count)
310 *
311 * the most significant bit is in the 1st position, and the least sigificant bit
312 * is in the 8th position
313 *
314 * we check each position and if it's a fogged tile, then we add a 1 to that
315 * bit position.
316 *
317 * 111
318 * 1x1 = 13
319 * 111
320 * (255) (all 8 bits on is 255)
321 *
322 * 111 111 011 110
323 * 1x1 = 5 1x1 = 6 1x1 = 7 1x1 = 8
324 * 110 011 111 111
325 * (127) (223) (254) (251) (e.g. 251 == 11111011)
326 *
327 * 101 111 111 111
328 * 1x1 = 9 1x0 = 12 1x1 =10 0x1 = 11
329 * 111 111 101 111
330 * (253) (239) (191) (247) (e.g. 247 == 11110111)
331 *
332 * 001 111 100 111
333 * 1x1 = 9 1x1 = 10 1x1 = 9 1x1 = 10
334 * 111 001 111 100
335 * (252) (159) (249) (63)
336 *
337 * 011 111 110 111
338 * 0x1 = 11 0x1 = 11 1x0 =12 1x0 = 12
339 * 111 011 111 110
340 * (246) (215) (235) (111)
341 *
342 * 000 000 110 011
343 * 0x1 = 1 1x0 = 2 1x0 = 3 0x1 = 4
344 * 011 110 000 000
345 * (208) (104) (11) (22)
346 *
347 *
348 * 000 111 011 110
349 * 1x1 = 9 1x1 = 10 0x1 = 11 1x0 = 12
350 * 111 000 011 110
351 * (248) (31) (214) (107)
352 *
353 *
354 * 001 111 100 111
355 * 0x1 = 1 0x1 = 4 1x0 = 2 1x0 = 3
356 * 111 001 111 100
357 * (244) (151) (233) (47)
358 *
359 * 000 011 000 111
360 * 0x1 = 1 0x1 = 4 1x0 = 2 1x0 = 3
361 * 111 001 111 000
362 * (240) (150) (232) (15)
363 *
364 * 100 110 001 111
365 * 1x0 = 2 1x0 = 3 0x1 = 1 0x1 = 4
366 * 110 100 011 000
367 * (105) (43) (232) (15)
368 *
369 * 011 110
370 * 1x1 = 14 1x1 = 15
371 * 110 011
372 * (126) (219)
373 *
374 *special note:
375 *none of these sets contain a so-called "lone" tile.
376 *a lone tile is a fogged tile surrounded by two unfogged tiles on either side.
377 **/
calculateShade(Vector<int> tile)378 FogMap::ShadeType FogMap::calculateShade(Vector<int> tile)
379 {
380 int idx = 0;
381 int count = 0;
382 bool foggyTile;
383 for (int i = tile.x - 1; i <= tile.x + 1; i++)
384 for (int j = tile.y - 1; j <= tile.y + 1; j++)
385 {
386 foggyTile = false;
387 if (i == tile.x && j == tile.y)
388 continue;
389 if (i < 0 || j < 0 || i >= d_width || j >= d_height)
390 foggyTile = true;
391 else
392 {
393 Vector<int> pos;
394 pos.x = i;
395 pos.y = j;
396 foggyTile = isFogged(pos);
397 }
398 if (foggyTile)
399 {
400 switch (count)
401 {
402 case 0: idx += 1; break;
403 case 1: idx += 2; break;
404 case 2: idx += 4; break;
405 case 3: idx += 8; break;
406 case 4: idx += 16; break;
407 case 5: idx += 32; break;
408 case 6: idx += 64; break;
409 case 7: idx += 128; break;
410 }
411 }
412
413 count++;
414 }
415
416 //now idx relates to a particular fog picture
417 ShadeType type = NONE;
418 switch (idx)
419 {
420 case 208: case 212: case 240: case 244: case 242: case 216: case 220: case 210: case 217: case 211: case 218: case 209: type = LIGHTLY_TO_SOUTH_AND_EAST; break;
421 case 104: case 105: case 232: case 233: case 121: case 120: case 110: case 106: case 122: case 124: case 234: case 108: type = LIGHTLY_TO_SOUTH_AND_WEST; break;
422 case 11: case 15: case 43: case 47: case 59: case 27: case 79: case 75: case 155: case 203: case 139: case 91: type = LIGHTLY_TO_NORTH_AND_WEST; break;
423 case 22: case 150: case 151: case 23: case 87: case 86: case 158: case 118:case 94: case 30: case 62: case 54: type = LIGHTLY_TO_NORTH_AND_EAST; break;
424 case 127: type = DARKLY_TO_NORTH_AND_WEST_LIGHTLY_TO_SOUTH_AND_EAST; break;
425 case 223: type = DARKLY_TO_NORTH_AND_EAST_LIGHTLY_TO_SOUTH_AND_WEST; break;
426 case 254: type = DARKLY_TO_SOUTH_AND_EAST_LIGHTLY_TO_NORTH_AND_WEST; break;
427 case 251: type = DARKLY_TO_SOUTH_AND_WEST_LIGHTLY_TO_NORTH_AND_EAST; break;
428 case 248: case 249: case 252: case 253: case 250: type = DARKLY_TO_SOUTH_LIGHTLY_TO_EAST_AND_WEST; break;
429 case 31: case 63: case 159: case 191: case 95: type = DARKLY_TO_NORTH_LIGHTLY_TO_EAST_AND_WEST; break;
430 case 214: case 215: case 246: case 247: case 222: type = DARKLY_TO_WEST_LIGHTLY_TO_NORTH_AND_SOUTH; break;
431 case 107: case 111: case 235: case 239: case 123: type = DARKLY_TO_EAST_LIGHTLY_TO_NORTH_AND_SOUTH; break;
432 case 126: type = DARKLY_TO_SOUTH_AND_WEST_DARKLY_TO_NORTH_AND_EAST; break;
433 case 219: type = DARKLY_TO_NORTH_AND_WEST_DARKLY_TO_SOUTH_AND_EAST; break;
434 case 255: type = ALL; break;
435 }
436 if (type)
437 {
438 switch (type) //fixme: figure out why this flipping is necessary!
439 {
440 case DARKLY_TO_EAST_LIGHTLY_TO_NORTH_AND_SOUTH:
441 type = DARKLY_TO_NORTH_LIGHTLY_TO_EAST_AND_WEST; break;
442 case DARKLY_TO_NORTH_LIGHTLY_TO_EAST_AND_WEST:
443 type = DARKLY_TO_EAST_LIGHTLY_TO_NORTH_AND_SOUTH; break;
444 case DARKLY_TO_SOUTH_LIGHTLY_TO_EAST_AND_WEST:
445 type = DARKLY_TO_WEST_LIGHTLY_TO_NORTH_AND_SOUTH; break;
446 case DARKLY_TO_WEST_LIGHTLY_TO_NORTH_AND_SOUTH:
447 type = DARKLY_TO_SOUTH_LIGHTLY_TO_EAST_AND_WEST; break;
448 case DARKLY_TO_NORTH_AND_EAST_LIGHTLY_TO_SOUTH_AND_WEST:
449 type = DARKLY_TO_SOUTH_AND_WEST_LIGHTLY_TO_NORTH_AND_EAST; break;
450 case DARKLY_TO_SOUTH_AND_WEST_LIGHTLY_TO_NORTH_AND_EAST:
451 type = DARKLY_TO_NORTH_AND_EAST_LIGHTLY_TO_SOUTH_AND_WEST; break;
452 case LIGHTLY_TO_SOUTH_AND_WEST:
453 type = LIGHTLY_TO_NORTH_AND_EAST; break;
454 case LIGHTLY_TO_NORTH_AND_EAST:
455 type = LIGHTLY_TO_SOUTH_AND_WEST; break;
456 default:break;
457 }
458 }
459 return type;
460 }
461
calculateShadeMap()462 void FogMap::calculateShadeMap()
463 {
464 for (int i = 0; i < d_width; i++)
465 for (int j = 0; j < d_height; j++)
466 shademap[j * d_width + i] = calculateShade(Vector<int>(i,j));
467
468 for (int i = 0; i < d_width; i++)
469 for (int j = 0; j < d_height; j++)
470 if (isFogged(Vector<int>(i,j)) == false)
471 shademap[j * d_width + i] = NONE;
472 }
473 // End of file
474