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