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