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