1 /*
2  *  This file is part of Dune Legacy.
3  *
4  *  Dune Legacy is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Dune Legacy is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Dune Legacy.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <Tile.h>
19 
20 #include <globals.h>
21 
22 #include <FileClasses/GFXManager.h>
23 
24 #include <sand.h>
25 #include <Game.h>
26 #include <Map.h>
27 #include <House.h>
28 #include <SoundPlayer.h>
29 #include <ScreenBorder.h>
30 #include <ConcatIterator.h>
31 #include <Explosion.h>
32 
33 #include <structures/StructureBase.h>
34 #include <units/InfantryBase.h>
35 #include <units/AirUnit.h>
36 
Tile()37 Tile::Tile() {
38     type = Terrain_Sand;
39 
40     for(int i = 0; i < NUM_TEAMS; i++) {
41         explored[i] = currentGame->getGameInitSettings().getGameOptions().startWithExploredMap;
42         lastAccess[i] = 0;
43     }
44 
45     fogColor = COLOR_BLACK;
46 
47     owner = INVALID;
48     sandRegion = NONE_ID;
49 
50     spice = 0;
51 
52     sprite = pGFXManager->getObjPic(ObjPic_Terrain);
53 
54     for(int i=0; i < NUM_ANGLES; i++) {
55         tracksCreationTime[i] = 0;
56     }
57 
58     location.x = 0;
59     location.y = 0;
60 
61     destroyedStructureTile = DestroyedStructure_None;
62 }
63 
64 
~Tile()65 Tile::~Tile() {
66 }
67 
load(InputStream & stream)68 void Tile::load(InputStream& stream) {
69     type = stream.readUint32();
70 
71     stream.readBools(&explored[0], &explored[1], &explored[2], &explored[3], &explored[4], &explored[5], &explored[6]);
72 
73     bool bLastAccess[NUM_TEAMS];
74     stream.readBools(&bLastAccess[0], &bLastAccess[1], &bLastAccess[2], &bLastAccess[3], &bLastAccess[4], &bLastAccess[5], &bLastAccess[6]);
75 
76     for(int i=0;i<NUM_TEAMS;i++) {
77         if(bLastAccess[i] == true) {
78             lastAccess[i] = stream.readUint32();
79         }
80     }
81 
82     fogColor = stream.readUint32();
83 
84     owner = stream.readSint32();
85     sandRegion = stream.readUint32();
86 
87     spice = stream.readFixPoint();
88 
89     bool bHasDamage, bHasDeadUnits, bHasAirUnits, bHasInfantry, bHasUndergroundUnits, bHasNonInfantryGroundObjects;
90     stream.readBools(&bHasDamage, &bHasDeadUnits, &bHasAirUnits, &bHasInfantry, &bHasUndergroundUnits, &bHasNonInfantryGroundObjects);
91 
92     if(bHasDamage) {
93         Uint32 numDamage = stream.readUint32();
94         for(Uint32 i=0; i<numDamage; i++) {
95             DAMAGETYPE newDamage;
96             newDamage.damageType = stream.readUint32();
97             newDamage.tile = stream.readSint32();
98             newDamage.realPos.x = stream.readSint32();
99             newDamage.realPos.y = stream.readSint32();
100 
101             damage.push_back(newDamage);
102         }
103     }
104 
105     if(bHasDeadUnits) {
106         Uint32 numDeadUnits = stream.readUint32();
107         for(Uint32 i=0; i<numDeadUnits; i++) {
108             DEADUNITTYPE newDeadUnit;
109             newDeadUnit.type = stream.readUint8();
110             newDeadUnit.house = stream.readUint8();
111             newDeadUnit.onSand = stream.readBool();
112             newDeadUnit.realPos.x = stream.readSint32();
113             newDeadUnit.realPos.y = stream.readSint32();
114             newDeadUnit.timer = stream.readSint16();
115 
116             deadUnits.push_back(newDeadUnit);
117         }
118     }
119 
120     destroyedStructureTile = stream.readSint32();
121 
122     bool bTrackCounter[NUM_ANGLES];
123     stream.readBools(&bTrackCounter[0], &bTrackCounter[1], &bTrackCounter[2], &bTrackCounter[3], &bTrackCounter[4], &bTrackCounter[5], &bTrackCounter[6], &bTrackCounter[7]);
124 
125     for(int i=0; i < NUM_ANGLES; i++) {
126         if(bTrackCounter[i] == true) {
127             tracksCreationTime[i] = stream.readUint32();
128         }
129     }
130 
131     if(bHasAirUnits) {
132         assignedAirUnitList = stream.readUint32List();
133     }
134 
135     if(bHasInfantry) {
136         assignedInfantryList = stream.readUint32List();
137     }
138 
139     if(bHasUndergroundUnits) {
140         assignedUndergroundUnitList = stream.readUint32List();
141     }
142 
143     if(bHasNonInfantryGroundObjects) {
144         assignedNonInfantryGroundObjectList = stream.readUint32List();
145     }
146 }
147 
save(OutputStream & stream) const148 void Tile::save(OutputStream& stream) const {
149     stream.writeUint32(type);
150 
151     stream.writeBools(explored[0], explored[1], explored[2], explored[3], explored[4], explored[5], explored[6]);
152 
153     stream.writeBools((lastAccess[0] != 0), (lastAccess[1] != 0), (lastAccess[2] != 0), (lastAccess[3] != 0), (lastAccess[4] != 0), (lastAccess[5] != 0), (lastAccess[6] != 0));
154     for(int i=0;i<NUM_TEAMS;i++) {
155         if(lastAccess[i] != 0) {
156             stream.writeUint32(lastAccess[i]);
157         }
158     }
159 
160     stream.writeUint32(fogColor);
161 
162     stream.writeUint32(owner);
163     stream.writeUint32(sandRegion);
164 
165     stream.writeFixPoint(spice);
166 
167     stream.writeBools(  !damage.empty(), !deadUnits.empty(), !assignedAirUnitList.empty(),
168                         !assignedInfantryList.empty(), !assignedUndergroundUnitList.empty(), !assignedNonInfantryGroundObjectList.empty());
169 
170     if(!damage.empty()) {
171         stream.writeUint32(damage.size());
172         for(const DAMAGETYPE& damageItem : damage) {
173             stream.writeUint32(damageItem.damageType);
174             stream.writeSint32(damageItem.tile);
175             stream.writeSint32(damageItem.realPos.x);
176             stream.writeSint32(damageItem.realPos.y);
177         }
178     }
179 
180     if(!deadUnits.empty()) {
181         stream.writeUint32(deadUnits.size());
182         for(const DEADUNITTYPE& deadUnit : deadUnits) {
183             stream.writeUint8(deadUnit.type);
184             stream.writeUint8(deadUnit.house);
185             stream.writeBool(deadUnit.onSand);
186             stream.writeSint32(deadUnit.realPos.x);
187             stream.writeSint32(deadUnit.realPos.y);
188             stream.writeSint16(deadUnit.timer);
189         }
190     }
191 
192     stream.writeSint32(destroyedStructureTile);
193 
194     // clean-up tracksCreationTime to save space in the save game
195     Uint32 tracksCreationTimeToSave[NUM_ANGLES];
196     for(int i=0; i < NUM_ANGLES; i++) {
197         tracksCreationTimeToSave[i] = (tracksCreationTime[i] + TRACKSTIME < currentGame->getGameCycleCount()) ? 0 : tracksCreationTime[i];
198     }
199 
200     stream.writeBools(  (tracksCreationTimeToSave[0] != 0), (tracksCreationTimeToSave[1] != 0), (tracksCreationTimeToSave[2] != 0), (tracksCreationTimeToSave[3] != 0),
201                         (tracksCreationTimeToSave[4] != 0), (tracksCreationTimeToSave[5] != 0), (tracksCreationTimeToSave[6] != 0), (tracksCreationTimeToSave[7] != 0));
202     for(int i=0; i < NUM_ANGLES; i++) {
203         if(tracksCreationTimeToSave[i] != 0) {
204             stream.writeUint32(tracksCreationTimeToSave[i]);
205         }
206     }
207 
208     if(!assignedAirUnitList.empty()) {
209         stream.writeUint32List(assignedAirUnitList);
210     }
211 
212     if(!assignedInfantryList.empty()) {
213         stream.writeUint32List(assignedInfantryList);
214     }
215 
216     if(!assignedUndergroundUnitList.empty()) {
217         stream.writeUint32List(assignedUndergroundUnitList);
218     }
219 
220     if(!assignedNonInfantryGroundObjectList.empty()) {
221         stream.writeUint32List(assignedNonInfantryGroundObjectList);
222     }
223 }
224 
assignAirUnit(Uint32 newObjectID)225 void Tile::assignAirUnit(Uint32 newObjectID) {
226     assignedAirUnitList.push_back(newObjectID);
227 }
228 
assignNonInfantryGroundObject(Uint32 newObjectID)229 void Tile::assignNonInfantryGroundObject(Uint32 newObjectID) {
230     assignedNonInfantryGroundObjectList.push_back(newObjectID);
231 }
232 
assignInfantry(Uint32 newObjectID,Sint8 currentPosition)233 int Tile::assignInfantry(Uint32 newObjectID, Sint8 currentPosition) {
234     Sint8 newPosition = currentPosition;
235 
236     if(currentPosition < 0) {
237         bool used[NUM_INFANTRY_PER_TILE]{ false };
238 
239         for(Uint32 objectID : assignedInfantryList) {
240             InfantryBase* pInfantry = static_cast<InfantryBase*>(currentGame->getObjectManager().getObject(objectID));
241             if(pInfantry == nullptr) {
242                 continue;
243             }
244 
245             int pos = pInfantry->getTilePosition();
246             if ((pos >= 0) && (pos < NUM_INFANTRY_PER_TILE)) {
247                 used[pos] = true;
248             }
249         }
250 
251         for (newPosition = 0; newPosition < NUM_INFANTRY_PER_TILE; newPosition++) {
252             if (used[newPosition] == false) {
253                 break;
254             }
255         }
256 
257         newPosition = std::max((Sint8) 0, std::min(newPosition, (Sint8) NUM_INFANTRY_PER_TILE));
258     }
259 
260     assignedInfantryList.push_back(newObjectID);
261     return newPosition;
262 }
263 
264 
assignUndergroundUnit(Uint32 newObjectID)265 void Tile::assignUndergroundUnit(Uint32 newObjectID) {
266     assignedUndergroundUnitList.push_back(newObjectID);
267 }
268 
blitGround(int xPos,int yPos)269 void Tile::blitGround(int xPos, int yPos) {
270     int tileIndex = getTerrainTile();
271     int indexX = tileIndex % NUM_TERRAIN_TILES_X;
272     int indexY = tileIndex / NUM_TERRAIN_TILES_X;
273     SDL_Rect source = { indexX*world2zoomedWorld(TILESIZE), indexY*world2zoomedWorld(TILESIZE), world2zoomedWorld(TILESIZE), world2zoomedWorld(TILESIZE) };
274     SDL_Rect drawLocation = { xPos, yPos, world2zoomedWorld(TILESIZE), world2zoomedWorld(TILESIZE) };
275 
276     if((hasANonInfantryGroundObject() == false) || (getNonInfantryGroundObject()->isAStructure() == false)) {
277 
278         //draw terrain
279         if(destroyedStructureTile == DestroyedStructure_None || destroyedStructureTile == DestroyedStructure_Wall) {
280             SDL_RenderCopy(renderer, sprite[currentZoomlevel], &source, &drawLocation);
281         }
282 
283         if(destroyedStructureTile != DestroyedStructure_None) {
284             SDL_Texture** pDestroyedStructureSurface = pGFXManager->getObjPic(ObjPic_DestroyedStructure);
285             SDL_Rect source2 = { destroyedStructureTile*world2zoomedWorld(TILESIZE), 0, world2zoomedWorld(TILESIZE), world2zoomedWorld(TILESIZE) };
286             SDL_RenderCopy(renderer, pDestroyedStructureSurface[currentZoomlevel], &source2, &drawLocation);
287         }
288 
289         if(!isFogged(pLocalHouse->getHouseID())) {
290             // tracks
291             SDL_Texture* pTracks = pGFXManager->getObjPic(ObjPic_Terrain_Tracks)[currentZoomlevel];
292             for(int i=0;i<NUM_ANGLES;i++) {
293 
294                 int tracktime = currentGame->getGameCycleCount() - tracksCreationTime[i];
295                 if((tracksCreationTime[i] != 0) && (tracktime < TRACKSTIME)) {
296                     source.x = ((10-i)%8)*world2zoomedWorld(TILESIZE);
297                     SDL_SetTextureAlphaMod(pTracks, std::min(255, 256*(TRACKSTIME-tracktime)/TRACKSTIME));
298                     SDL_RenderCopy(renderer, pTracks, &source, &drawLocation);
299                 }
300             }
301 
302             // damage
303             for(const DAMAGETYPE& damageItem : damage) {
304                 source.x = damageItem.tile*world2zoomedWorld(TILESIZE);
305                 SDL_Rect dest = {   screenborder->world2screenX(damageItem.realPos.x) - world2zoomedWorld(TILESIZE)/2,
306                                     screenborder->world2screenY(damageItem.realPos.y) - world2zoomedWorld(TILESIZE)/2,
307                                     world2zoomedWorld(TILESIZE),
308                                     world2zoomedWorld(TILESIZE) };
309 
310                 if(damageItem.damageType == Terrain_RockDamage) {
311                     SDL_RenderCopy(renderer, pGFXManager->getObjPic(ObjPic_RockDamage)[currentZoomlevel], &source, &dest);
312                 } else {
313                     SDL_RenderCopy(renderer, pGFXManager->getObjPic(ObjPic_SandDamage)[currentZoomlevel], &source, &drawLocation);
314                 }
315             }
316         }
317     }
318 
319 }
320 
blitStructures(int xPos,int yPos)321 void Tile::blitStructures(int xPos, int yPos) {
322     if (hasANonInfantryGroundObject() && getNonInfantryGroundObject()->isAStructure()) {
323         //if got a structure, draw the structure, and dont draw any terrain because wont be seen
324         bool    done = false;   //only draw it once
325         StructureBase* structure = static_cast<StructureBase*>(getNonInfantryGroundObject());
326 
327         for(int i = structure->getX(); (i < structure->getX() + structure->getStructureSizeX()) && !done;  i++) {
328             for(int j = structure->getY(); (j < structure->getY() + structure->getStructureSizeY()) && !done;  j++) {
329                 if(screenborder->isTileInsideScreen(Coord(i,j))
330                     && currentGameMap->tileExists(i, j) && (currentGameMap->getTile(i, j)->isExplored(pLocalHouse->getHouseID()) || debug))
331                 {
332 
333                     structure->setFogged(isFogged(pLocalHouse->getHouseID()));
334 
335                     if ((i == location.x) && (j == location.y)) {
336                         //only this tile will draw it, so will be drawn only once
337                         structure->blitToScreen();
338                     }
339 
340                     done = true;
341                 }
342             }
343         }
344     }
345 }
346 
blitUndergroundUnits(int xPos,int yPos)347 void Tile::blitUndergroundUnits(int xPos, int yPos) {
348     if(hasAnUndergroundUnit() && !isFogged(pLocalHouse->getHouseID())) {
349         UnitBase* current = getUndergroundUnit();
350 
351         if(current->isVisible(pLocalHouse->getTeam())) {
352             if(location == current->getLocation()) {
353                 current->blitToScreen();
354             }
355         }
356     }
357 }
358 
blitDeadUnits(int xPos,int yPos)359 void Tile::blitDeadUnits(int xPos, int yPos) {
360     if(!isFogged(pLocalHouse->getHouseID())) {
361         for(const DEADUNITTYPE& deadUnit : deadUnits) {
362             SDL_Rect source = { 0, 0, world2zoomedWorld(TILESIZE), world2zoomedWorld(TILESIZE) };
363             SDL_Texture** pTexture = nullptr;
364             switch(deadUnit.type) {
365                 case DeadUnit_Infantry: {
366                     pTexture = pGFXManager->getObjPic(ObjPic_DeadInfantry, deadUnit.house);
367                     source.x = (deadUnit.timer < 1000 && deadUnit.onSand) ? world2zoomedWorld(TILESIZE) : 0;
368                 } break;
369 
370                 case DeadUnit_Infantry_Squashed1: {
371                     pTexture = pGFXManager->getObjPic(ObjPic_DeadInfantry, deadUnit.house);
372                     source.x = 4 * world2zoomedWorld(TILESIZE);
373                 } break;
374 
375                 case DeadUnit_Infantry_Squashed2: {
376                     pTexture = pGFXManager->getObjPic(ObjPic_DeadInfantry, deadUnit.house);
377                     source.x = 5 * world2zoomedWorld(TILESIZE);
378                 } break;
379 
380                 case DeadUnit_Carrall: {
381                     pTexture = pGFXManager->getObjPic(ObjPic_DeadAirUnit, deadUnit.house);
382                     if(deadUnit.onSand) {
383                         source.x = (deadUnit.timer < 1000) ? 5*world2zoomedWorld(TILESIZE) : 4*world2zoomedWorld(TILESIZE);
384                     } else {
385                         source.x = 3*world2zoomedWorld(TILESIZE);
386                     }
387                 } break;
388 
389                 case DeadUnit_Ornithopter: {
390                     pTexture = pGFXManager->getObjPic(ObjPic_DeadAirUnit, deadUnit.house);
391                     if(deadUnit.onSand) {
392                         source.x = (deadUnit.timer < 1000) ? 2*world2zoomedWorld(TILESIZE) : world2zoomedWorld(TILESIZE);
393                     } else {
394                         source.x = 0;
395                     }
396                 } break;
397 
398                 default: {
399                     pTexture = nullptr;
400                 } break;
401             }
402 
403             if(pTexture != nullptr) {
404                 SDL_Rect dest = {   screenborder->world2screenX(deadUnit.realPos.x) - world2zoomedWorld(TILESIZE)/2,
405                                     screenborder->world2screenY(deadUnit.realPos.y) - world2zoomedWorld(TILESIZE)/2,
406                                     world2zoomedWorld(TILESIZE),
407                                     world2zoomedWorld(TILESIZE) };
408                 SDL_RenderCopy(renderer, pTexture[currentZoomlevel], &source, &dest);
409             }
410         }
411     }
412 }
413 
blitInfantry(int xPos,int yPos)414 void Tile::blitInfantry(int xPos, int yPos) {
415     if(!isFogged(pLocalHouse->getHouseID())) {
416         for(Uint32 objectID : assignedInfantryList) {
417             InfantryBase* pInfantry = static_cast<InfantryBase*>(currentGame->getObjectManager().getObject(objectID));
418             if(pInfantry == nullptr) {
419                 continue;
420             }
421 
422             if(pInfantry->isVisible(pLocalHouse->getTeam())) {
423                 if(location == pInfantry->getLocation()) {
424                     pInfantry->blitToScreen();
425                 }
426             }
427         }
428     }
429 }
430 
blitNonInfantryGroundUnits(int xPos,int yPos)431 void Tile::blitNonInfantryGroundUnits(int xPos, int yPos) {
432     if(!isFogged(pLocalHouse->getHouseID())) {
433         for(Uint32 objectID : assignedNonInfantryGroundObjectList) {
434             ObjectBase* pObject = currentGame->getObjectManager().getObject(objectID);
435             if(pObject->isAUnit() && pObject->isVisible(pLocalHouse->getTeam())) {
436                 if(location == pObject->getLocation()) {
437                     pObject->blitToScreen();
438                 }
439             }
440         }
441     }
442 }
443 
444 
blitAirUnits(int xPos,int yPos)445 void Tile::blitAirUnits(int xPos, int yPos) {
446     for(Uint32 objectID : assignedAirUnitList) {
447         AirUnit* pAirUnit = static_cast<AirUnit*>(currentGame->getObjectManager().getObject(objectID));
448         if(pAirUnit == nullptr) {
449             continue;
450         }
451 
452         if(!isFogged(pLocalHouse->getHouseID()) || pAirUnit->getOwner() == pLocalHouse) {
453             if(pAirUnit->isVisible(pLocalHouse->getTeam())) {
454                 if(location == pAirUnit->getLocation()) {
455                     pAirUnit->blitToScreen();
456                 }
457             }
458         }
459     }
460 }
461 
blitSelectionRects(int xPos,int yPos)462 void Tile::blitSelectionRects(int xPos, int yPos) {
463     auto blitObjectSelectionRect =  [&](Uint32 objectID) {
464                                         ObjectBase* pObject = currentGame->getObjectManager().getObject(objectID);
465                                         if(pObject == nullptr) {
466                                             return;
467                                         }
468 
469                                         // possibly draw selection rectangle multiple times, e.g. for structures
470                                         if(pObject->isVisible(pLocalHouse->getTeam())) {
471                                             if(pObject->isSelected()) {
472                                                 pObject->drawSelectionBox();
473                                             }
474 
475                                             if(pObject->isSelectedByOtherPlayer()) {
476                                                 pObject->drawOtherPlayerSelectionBox();
477                                             }
478                                         }
479                                     };
480 
481     // draw underground selection rectangles
482     if(!isFogged(pLocalHouse->getHouseID())) {
483         std::for_each(  assignedUndergroundUnitList.begin(),
484                         assignedUndergroundUnitList.end(),
485                         blitObjectSelectionRect);
486 
487         std::for_each(  assignedInfantryList.begin(),
488                         assignedInfantryList.end(),
489                         blitObjectSelectionRect);
490 
491         std::for_each(  assignedNonInfantryGroundObjectList.begin(),
492                         assignedNonInfantryGroundObjectList.end(),
493                         blitObjectSelectionRect);
494 
495         std::for_each(  assignedAirUnitList.begin(),
496                         assignedAirUnitList.end(),
497                         blitObjectSelectionRect);
498     }
499 }
500 
501 
clearTerrain()502 void Tile::clearTerrain() {
503     damage.clear();
504     deadUnits.clear();
505 }
506 
507 
setTrack(Uint8 direction)508 void Tile::setTrack(Uint8 direction) {
509     if(type == Terrain_Sand || type == Terrain_Dunes || type == Terrain_Spice || type == Terrain_ThickSpice) {
510         tracksCreationTime[direction] = currentGame->getGameCycleCount();
511     }
512 }
513 
514 
selectAllPlayersUnits(int houseID,ObjectBase ** lastCheckedObject,ObjectBase ** lastSelectedObject)515 void Tile::selectAllPlayersUnits(int houseID, ObjectBase** lastCheckedObject, ObjectBase** lastSelectedObject) {
516     ConcatIterator<Uint32> iterator;
517     iterator.addList(assignedInfantryList);
518     iterator.addList(assignedNonInfantryGroundObjectList);
519     iterator.addList(assignedUndergroundUnitList);
520     iterator.addList(assignedAirUnitList);
521 
522     while(!iterator.isIterationFinished()) {
523         *lastCheckedObject = currentGame->getObjectManager().getObject(*iterator);
524         if (((*lastCheckedObject)->getOwner()->getHouseID() == houseID)
525             && !(*lastCheckedObject)->isSelected()
526             && (*lastCheckedObject)->isAUnit()
527             && ((*lastCheckedObject)->isRespondable())) {
528 
529             (*lastCheckedObject)->setSelected(true);
530             currentGame->getSelectedList().insert((*lastCheckedObject)->getObjectID());
531             currentGame->selectionChanged();
532             *lastSelectedObject = *lastCheckedObject;
533         }
534         ++iterator;
535     }
536 }
537 
538 
selectAllPlayersUnitsOfType(int houseID,int itemID,ObjectBase ** lastCheckedObject,ObjectBase ** lastSelectedObject)539 void Tile::selectAllPlayersUnitsOfType(int houseID, int itemID, ObjectBase** lastCheckedObject, ObjectBase** lastSelectedObject) {
540     ConcatIterator<Uint32> iterator;
541     iterator.addList(assignedInfantryList);
542     iterator.addList(assignedNonInfantryGroundObjectList);
543     iterator.addList(assignedUndergroundUnitList);
544     iterator.addList(assignedAirUnitList);
545 
546     while(!iterator.isIterationFinished()) {
547         *lastCheckedObject = currentGame->getObjectManager().getObject(*iterator);
548         if (((*lastCheckedObject)->getOwner()->getHouseID() == houseID)
549             && !(*lastCheckedObject)->isSelected()
550             && ((*lastCheckedObject)->getItemID() == itemID)) {
551 
552             (*lastCheckedObject)->setSelected(true);
553             currentGame->getSelectedList().insert((*lastCheckedObject)->getObjectID());
554             currentGame->selectionChanged();
555             *lastSelectedObject = *lastCheckedObject;
556         }
557         ++iterator;
558     }
559 }
560 
561 
unassignAirUnit(Uint32 objectID)562 void Tile::unassignAirUnit(Uint32 objectID) {
563     assignedAirUnitList.remove(objectID);
564 }
565 
566 
unassignNonInfantryGroundObject(Uint32 objectID)567 void Tile::unassignNonInfantryGroundObject(Uint32 objectID) {
568     assignedNonInfantryGroundObjectList.remove(objectID);
569 }
570 
unassignUndergroundUnit(Uint32 objectID)571 void Tile::unassignUndergroundUnit(Uint32 objectID) {
572     assignedUndergroundUnitList.remove(objectID);
573 }
574 
unassignInfantry(Uint32 objectID,int currentPosition)575 void Tile::unassignInfantry(Uint32 objectID, int currentPosition) {
576     assignedInfantryList.remove(objectID);
577 }
578 
unassignObject(Uint32 objectID)579 void Tile::unassignObject(Uint32 objectID) {
580     unassignInfantry(objectID,-1);
581     unassignUndergroundUnit(objectID);
582     unassignNonInfantryGroundObject(objectID);
583     unassignAirUnit(objectID);
584 }
585 
586 
setType(int newType)587 void Tile::setType(int newType) {
588     type = newType;
589     destroyedStructureTile = DestroyedStructure_None;
590 
591     if (type == Terrain_Spice) {
592         spice = currentGame->randomGen.rand(RANDOMSPICEMIN, RANDOMSPICEMAX);
593     } else if (type == Terrain_ThickSpice) {
594         spice = currentGame->randomGen.rand(RANDOMTHICKSPICEMIN, RANDOMTHICKSPICEMAX);
595     } else if (type == Terrain_Dunes) {
596     } else {
597         spice = 0;
598         if (isRock()) {
599             sandRegion = NONE_ID;
600             if (hasAnUndergroundUnit()) {
601                 std::list<Uint32>::const_iterator iter;
602                 iter = assignedUndergroundUnitList.begin();
603 
604                 do {
605                     ObjectBase* current = currentGame->getObjectManager().getObject(*iter);
606                     ++iter;
607 
608                     if(current == nullptr)
609                         continue;
610 
611                     unassignUndergroundUnit(current->getObjectID());
612                     current->destroy();
613                 } while(iter != assignedUndergroundUnitList.end());
614 
615             }
616 
617             if(type == Terrain_Mountain) {
618                 if(hasANonInfantryGroundObject()) {
619                     std::list<Uint32>::const_iterator iter;
620                     iter = assignedNonInfantryGroundObjectList.begin();
621 
622                     do {
623                         ObjectBase* current = currentGame->getObjectManager().getObject(*iter);
624                         ++iter;
625 
626                         if(current == nullptr)
627                             continue;
628 
629                         unassignNonInfantryGroundObject(current->getObjectID());
630                         current->destroy();
631                     } while(iter != assignedNonInfantryGroundObjectList.end());
632                 }
633             }
634         }
635     }
636 
637     for (int i=location.x; i <= location.x+3; i++) {
638         for (int j=location.y; j <= location.y+3; j++) {
639             if (currentGameMap->tileExists(i, j)) {
640                 currentGameMap->getTile(i, j)->clearTerrain();
641             }
642         }
643     }
644 }
645 
646 
squash()647 void Tile::squash() {
648     if(hasInfantry()) {
649         std::list<Uint32>::const_iterator iter;
650         iter = assignedInfantryList.begin();
651 
652         do {
653             InfantryBase* current = static_cast<InfantryBase*>(currentGame->getObjectManager().getObject(*iter));
654             ++iter;
655 
656             if(current == nullptr)
657                 continue;
658 
659             current->squash();
660         } while(iter != assignedInfantryList.end());
661     }
662 }
663 
664 
getInfantryTeam()665 int Tile::getInfantryTeam() {
666     int team = INVALID;
667     if (hasInfantry())
668         team = getInfantry()->getOwner()->getTeam();
669     return team;
670 }
671 
672 
harvestSpice()673 FixPoint Tile::harvestSpice() {
674     FixPoint oldSpice = spice;
675 
676     if((spice - HARVESTSPEED) >= 0) {
677         spice -= HARVESTSPEED;
678     } else {
679         spice = 0;
680     }
681 
682     if(oldSpice >= RANDOMTHICKSPICEMIN && spice < RANDOMTHICKSPICEMIN) {
683         setType(Terrain_Spice);
684     }
685 
686     if(oldSpice > 0 && spice == 0) {
687         setType(Terrain_Sand);
688     }
689 
690     return (oldSpice - spice);
691 }
692 
693 
setSpice(FixPoint newSpice)694 void Tile::setSpice(FixPoint newSpice) {
695     if(newSpice <= 0) {
696         type = Terrain_Sand;
697     } else if(newSpice >= RANDOMTHICKSPICEMIN) {
698         type = Terrain_ThickSpice;
699     } else {
700         type = Terrain_Spice;
701     }
702     spice = newSpice;
703 }
704 
705 
getAirUnit()706 AirUnit* Tile::getAirUnit() {
707     return dynamic_cast<AirUnit*>(currentGame->getObjectManager().getObject(assignedAirUnitList.front()));
708 }
709 
getGroundObject()710 ObjectBase* Tile::getGroundObject() {
711     if (hasANonInfantryGroundObject())
712         return getNonInfantryGroundObject();
713     else if (hasInfantry())
714         return getInfantry();
715     else
716         return nullptr;
717 }
718 
getInfantry()719 InfantryBase* Tile::getInfantry() {
720     return dynamic_cast<InfantryBase*>(currentGame->getObjectManager().getObject(assignedInfantryList.front()));
721 }
722 
getNonInfantryGroundObject()723 ObjectBase* Tile::getNonInfantryGroundObject() {
724     return currentGame->getObjectManager().getObject(assignedNonInfantryGroundObjectList.front());
725 }
726 
getUndergroundUnit()727 UnitBase* Tile::getUndergroundUnit() {
728     return dynamic_cast<UnitBase*>(currentGame->getObjectManager().getObject(assignedUndergroundUnitList.front()));
729 }
730 
731 
732 /*ObjectBase* Tile::getInfantry(int i)
733 {
734     int count;
735     InfantryBase* infantry;
736     assignedInfantry.reset();
737     while (assignedInfantry.currentNotNull())
738     {
739         ((InfantryBase*)assignedInfantry.getCurrent())->squash();
740         assignedInfantry.nextLink();
741     }
742     return assignedInfantry.removeElement();
743 }*/
744 
745 
getObject()746 ObjectBase* Tile::getObject() {
747     ObjectBase* temp = nullptr;
748     if (hasAnAirUnit())
749         temp = getAirUnit();
750     else if (hasANonInfantryGroundObject())
751         temp = getNonInfantryGroundObject();
752     else if (hasInfantry())
753         temp = getInfantry();
754     else if (hasAnUndergroundUnit())
755         temp = getUndergroundUnit();
756     return temp;
757 }
758 
759 
getObjectAt(int x,int y)760 ObjectBase* Tile::getObjectAt(int x, int y) {
761     ObjectBase* pObject = nullptr;
762     if (hasAnAirUnit()) {
763         pObject = getAirUnit();
764     } else if (hasANonInfantryGroundObject()) {
765         pObject = getNonInfantryGroundObject();
766     } else if (hasInfantry()) {
767         FixPoint closestDistance = FixPt_MAX;
768         Coord atPos(x,y);
769 
770         for(Uint32 objectID : assignedInfantryList) {
771             InfantryBase* pInfantry = dynamic_cast<InfantryBase*>(currentGame->getObjectManager().getObject(objectID));
772             if(pInfantry == nullptr) {
773                 continue;
774             }
775 
776             Coord centerPoint = pInfantry->getCenterPoint();
777             FixPoint distance = distanceFrom(atPos, centerPoint);
778             if(distance < closestDistance) {
779                 closestDistance = distance;
780                 pObject = pInfantry;
781             }
782         }
783     } else if (hasAnUndergroundUnit()) {
784         pObject = getUndergroundUnit();
785     }
786 
787     return pObject;
788 }
789 
790 
getObjectWithID(Uint32 objectID)791 ObjectBase* Tile::getObjectWithID(Uint32 objectID) {
792     for(Uint32 curObjectID : assignedInfantryList) {
793         if(curObjectID == objectID) {
794             return currentGame->getObjectManager().getObject(curObjectID);
795         }
796     }
797 
798     for(Uint32 curObjectID : assignedNonInfantryGroundObjectList) {
799         if(curObjectID == objectID) {
800             return currentGame->getObjectManager().getObject(curObjectID);
801         }
802     }
803 
804     for(Uint32 curObjectID : assignedUndergroundUnitList) {
805         if(curObjectID == objectID) {
806             return currentGame->getObjectManager().getObject(curObjectID);
807         }
808     }
809 
810     for(Uint32 curObjectID : assignedAirUnitList) {
811         if(curObjectID == objectID) {
812             return currentGame->getObjectManager().getObject(curObjectID);
813         }
814     }
815 
816     return nullptr;
817 }
818 
triggerSpiceBloom(House * pTrigger)819 void Tile::triggerSpiceBloom(House* pTrigger) {
820     if (isSpiceBloom()) {
821         //a spice bloom
822         soundPlayer->playSoundAt(Sound_Bloom, getLocation());
823         screenborder->shakeScreen(18);
824         if(pTrigger == pLocalHouse) {
825             soundPlayer->playVoice(BloomLocated, pLocalHouse->getHouseID());
826         }
827 
828         setType(Terrain_Spice); // Set this tile to spice first
829         currentGameMap->createSpiceField(location, 5);
830 
831         Coord realLocation = location*TILESIZE + Coord(TILESIZE/2, TILESIZE/2);
832 
833         if(damage.size() < DAMAGE_PER_TILE) {
834             DAMAGETYPE newDamage;
835             newDamage.tile = SandDamage1;
836             newDamage.damageType = Terrain_SandDamage;
837             newDamage.realPos = realLocation;
838 
839             damage.push_back(newDamage);
840         }
841 
842         currentGame->getExplosionList().push_back(new Explosion(Explosion_SpiceBloom,realLocation,pTrigger->getHouseID()));
843     }
844 }
845 
triggerSpecialBloom(House * pTrigger)846 void Tile::triggerSpecialBloom(House* pTrigger) {
847     if(isSpecialBloom()) {
848         setType(Terrain_Sand);
849 
850         switch(currentGame->randomGen.rand(0,3)) {
851             case 0: {
852                 // the player gets an randomly choosen amount of credits between 150 and 400
853                 pTrigger->addCredits(currentGame->randomGen.rand(150, 400),false);
854             } break;
855 
856             case 1: {
857                 // The house gets a Trike for free. It spawns beside the special bloom.
858                 UnitBase* pNewUnit = pTrigger->createUnit(Unit_Trike);
859                 if(pNewUnit != nullptr) {
860                     Coord spot = currentGameMap->findDeploySpot(pNewUnit, location, currentGame->randomGen);
861                     pNewUnit->deploy(spot);
862                 }
863             } break;
864 
865             case 2: {
866                 // One of the AI players on the map (one that has at least one unit) gets a Trike for free. It spawns beside the special bloom.
867                 int numCandidates = 0;
868                 for(int i=0;i<NUM_HOUSES;i++) {
869                     House* pHouse = currentGame->getHouse(i);
870                     if(pHouse != nullptr && pHouse->getTeam() != pTrigger->getTeam() && pHouse->getNumUnits() > 0) {
871                         numCandidates++;
872                     }
873                 }
874 
875                 if(numCandidates == 0) {
876                     break;
877                 }
878 
879                 int candidate = currentGame->randomGen.rand(0, numCandidates-1);
880 
881                 House* pEnemyHouse = nullptr;
882                 for(int i=0;i<NUM_HOUSES;i++) {
883                     House* pHouse = currentGame->getHouse(i);
884                     if(pHouse != nullptr && pHouse->getTeam() != pTrigger->getTeam() && pHouse->getNumUnits() > 0) {
885                         if(candidate == 0) {
886                             pEnemyHouse = pHouse;
887                             break;
888                         }
889                         candidate--;
890                     }
891                 }
892 
893                 UnitBase* pNewUnit = pEnemyHouse->createUnit(Unit_Trike);
894                 if(pNewUnit != nullptr) {
895                     Coord spot = currentGameMap->findDeploySpot(pNewUnit, location, currentGame->randomGen);
896                     pNewUnit->deploy(spot);
897                 }
898 
899             } break;
900 
901             case 3:
902             default: {
903                 // One of the AI players on the map (one that has at least one unit) gets an Infantry unit (3 Soldiers) for free. The spawn beside the special bloom.
904                 int numCandidates = 0;
905                 for(int i=0;i<NUM_HOUSES;i++) {
906                     House* pHouse = currentGame->getHouse(i);
907                     if(pHouse != nullptr && pHouse->getTeam() != pTrigger->getTeam() && pHouse->getNumUnits() > 0) {
908                         numCandidates++;
909                     }
910                 }
911 
912                 if(numCandidates == 0) {
913                     break;
914                 }
915 
916                 int candidate = currentGame->randomGen.rand(0, numCandidates-1);
917 
918                 House* pEnemyHouse = nullptr;
919                 for(int i=0;i<NUM_HOUSES;i++) {
920                     House* pHouse = currentGame->getHouse(i);
921                     if(pHouse != nullptr && pHouse->getTeam() != pTrigger->getTeam() && pHouse->getNumUnits() > 0) {
922                         if(candidate == 0) {
923                             pEnemyHouse = pHouse;
924                             break;
925                         }
926                         candidate--;
927                     }
928                 }
929 
930                 for(int i=0;i<3;i++) {
931                     UnitBase* pNewUnit = pEnemyHouse->createUnit(Unit_Soldier);
932                     if(pNewUnit != nullptr) {
933                         Coord spot = currentGameMap->findDeploySpot(pNewUnit, location, currentGame->randomGen);
934                         pNewUnit->deploy(spot);
935                     }
936                 }
937             } break;
938         }
939     }
940 }
941 
hasAStructure() const942 bool Tile::hasAStructure() const {
943     if(!hasANonInfantryGroundObject()) {
944         return false;
945     }
946 
947     ObjectBase* pObject = currentGame->getObjectManager().getObject(assignedNonInfantryGroundObjectList.front());
948     return ( (pObject != nullptr) && pObject->isAStructure() );
949 }
950 
isFogged(int houseID)951 bool Tile::isFogged(int houseID) {
952     if(debug)
953         return false;
954 
955     if(currentGame->getGameInitSettings().getGameOptions().fogOfWar == false) {
956         return false;
957     } else if((currentGame->getGameCycleCount() - lastAccess[houseID]) >= MILLI2CYCLES(10*1000)) {
958         return true;
959     } else {
960         return false;
961     }
962 }
963 
getRadarColor(House * pHouse,bool radar)964 Uint32 Tile::getRadarColor(House* pHouse, bool radar) {
965     if(isExplored(pHouse->getHouseID()) || debug) {
966         if(isFogged(pHouse->getHouseID()) && radar) {
967             return fogColor;
968         } else {
969             ObjectBase* pObject = getObject();
970             if(pObject != nullptr) {
971                 Uint32 color;
972 
973                 if(pObject->getItemID() == Unit_Sandworm) {
974                     color = COLOR_WHITE;
975                 } else {
976                     switch(pObject->getOwner()->getHouseID()) {
977                         case HOUSE_HARKONNEN:   color = SDL2RGB(palette[PALCOLOR_HARKONNEN]);  break;
978                         case HOUSE_ATREIDES:    color = SDL2RGB(palette[PALCOLOR_ATREIDES]);   break;
979                         case HOUSE_ORDOS:       color = SDL2RGB(palette[PALCOLOR_ORDOS]);      break;
980                         case HOUSE_FREMEN:      color = SDL2RGB(palette[PALCOLOR_FREMEN]);     break;
981                         case HOUSE_SARDAUKAR:   color = SDL2RGB(palette[PALCOLOR_SARDAUKAR]);  break;
982                         case HOUSE_MERCENARY:   color = SDL2RGB(palette[PALCOLOR_MERCENARY]);  break;
983                         default:                color = COLOR_BLACK;                           break;
984                     }
985                 }
986 
987                 if(pObject->isAUnit()) {
988                     fogColor = getColorByTerrainType(getType());
989                 } else {
990                     fogColor = color;
991                 }
992 
993                 // units and structures of the enemy are not visible if no radar
994                 if(!radar && !debug && (pObject->getOwner()->getTeam() != pHouse->getTeam())) {
995                     return COLOR_BLACK;
996                 } else {
997                     return color;
998                 }
999             } else {
1000                 fogColor = getColorByTerrainType(getType());
1001 
1002                 if(!radar && !debug) {
1003                     return COLOR_BLACK;
1004                 } else {
1005                     return fogColor;
1006                 }
1007             }
1008         }
1009     } else {
1010         return COLOR_BLACK;
1011     }
1012 }
1013 
getTerrainTile() const1014 int Tile::getTerrainTile() const {
1015     Uint32 terrainType = type;
1016     if(terrainType == Terrain_ThickSpice) {
1017         // check if we are surrounded by spice/thick spice
1018         bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isSpice() == true);
1019         bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isSpice() == true);
1020         bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isSpice() == true);
1021         bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isSpice() == true);
1022 
1023         if(!up || !right || !down || !left) {
1024             // to avoid graphical glitches (there is no tile for thick spice next to a non-spice tile) we draw this tile as normal spice
1025             terrainType = Terrain_Spice;
1026         }
1027     }
1028 
1029     switch(terrainType) {
1030         case Terrain_Slab: {
1031             return TerrainTile_Slab;
1032         } break;
1033 
1034         case Terrain_Sand: {
1035             return TerrainTile_Sand;
1036         } break;
1037 
1038         case Terrain_Rock: {
1039             // determine which surrounding tiles are rock
1040             bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isRock() == true);
1041             bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isRock() == true);
1042             bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isRock() == true);
1043             bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isRock() == true);
1044 
1045             return TerrainTile_Rock + (((int)up) | (right << 1) | (down << 2) | (left << 3));
1046         } break;
1047 
1048         case Terrain_Dunes: {
1049             // determine which surrounding tiles are dunes
1050             bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->getType() == Terrain_Dunes);
1051             bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->getType() == Terrain_Dunes);
1052             bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->getType() == Terrain_Dunes);
1053             bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->getType() == Terrain_Dunes);
1054 
1055             return TerrainTile_Dunes + (((int)up) | (right << 1) | (down << 2) | (left << 3));
1056         } break;
1057 
1058         case Terrain_Mountain: {
1059             // determine which surrounding tiles are mountains
1060             bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isMountain() == true);
1061             bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isMountain() == true);
1062             bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isMountain() == true);
1063             bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isMountain() == true);
1064 
1065             return TerrainTile_Mountain + (((int)up) | (right << 1) | (down << 2) | (left << 3));
1066         } break;
1067 
1068         case Terrain_Spice: {
1069             // determine which surrounding tiles are spice
1070             bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isSpice() == true);
1071             bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isSpice() == true);
1072             bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isSpice() == true);
1073             bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isSpice() == true);
1074 
1075             return TerrainTile_Spice + (((int)up) | (right << 1) | (down << 2) | (left << 3));
1076         } break;
1077 
1078         case Terrain_ThickSpice: {
1079             // determine which surrounding tiles are thick spice
1080             bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->getType() == Terrain_ThickSpice);
1081             bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->getType() == Terrain_ThickSpice);
1082             bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->getType() == Terrain_ThickSpice);
1083             bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->getType() == Terrain_ThickSpice);
1084 
1085             return TerrainTile_ThickSpice + (((int)up) | (right << 1) | (down << 2) | (left << 3));
1086         } break;
1087 
1088         case Terrain_SpiceBloom: {
1089             return TerrainTile_SpiceBloom;
1090         } break;
1091 
1092         case Terrain_SpecialBloom: {
1093             return TerrainTile_SpecialBloom;
1094         } break;
1095 
1096         default: {
1097             THROW(std::runtime_error, "Tile::getTerrainTile(): Invalid terrain type");
1098         } break;
1099     }
1100 }
1101 
getHideTile(int houseID) const1102 int Tile::getHideTile(int houseID) const {
1103 
1104     // are all surrounding tiles explored?
1105     if( ((currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isExplored(houseID) == true))
1106         && ((currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isExplored(houseID) == true))
1107         && ((currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isExplored(houseID) == true))
1108         && ((currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isExplored(houseID) == true))) {
1109         return 0;
1110     }
1111 
1112     // determine what tiles are unexplored
1113     bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isExplored(houseID) == false);
1114     bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isExplored(houseID) == false);
1115     bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isExplored(houseID) == false);
1116     bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isExplored(houseID) == false);
1117 
1118     return (((int)up) | (right << 1) | (down << 2) | (left << 3));
1119 }
1120 
getFogTile(int houseID) const1121 int Tile::getFogTile(int houseID) const {
1122 
1123     // are all surrounding tiles fogged?
1124     if( ((currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isFogged(houseID) == false))
1125         && ((currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isFogged(houseID) == false))
1126         && ((currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isFogged(houseID) == false))
1127         && ((currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isFogged(houseID) == false))) {
1128         return 0;
1129     }
1130 
1131     // determine what tiles are fogged
1132     bool up = (currentGameMap->tileExists(location.x,location.y-1) == false) || (currentGameMap->getTile(location.x, location.y-1)->isFogged(houseID) == true);
1133     bool right = (currentGameMap->tileExists(location.x+1,location.y) == false) || (currentGameMap->getTile(location.x+1, location.y)->isFogged(houseID) == true);
1134     bool down = (currentGameMap->tileExists(location.x,location.y+1) == false) || (currentGameMap->getTile(location.x, location.y+1)->isFogged(houseID) == true);
1135     bool left = (currentGameMap->tileExists(location.x-1,location.y) == false) || (currentGameMap->getTile(location.x-1, location.y)->isFogged(houseID) == true);
1136 
1137     return (((int)up) | (right << 1) | (down << 2) | (left << 3));
1138 }
1139