1 /*!
2  * \file   GameMap.cpp
3  * \brief  The central object holding everything that is on the map
4  *
5  *  Copyright (C) 2011-2016  OpenDungeons Team
6  *
7  *  This program is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #ifdef _MSC_VER
22 #define snprintf_is_banned_in_OD_code _snprintf
23 #endif
24 
25 #include "gamemap/GameMap.h"
26 
27 #include "ai/KeeperAIType.h"
28 #include "creatureaction/CreatureAction.h"
29 #include "creaturemood/CreatureMood.h"
30 #include "entities/Creature.h"
31 #include "entities/CreatureDefinition.h"
32 #include "entities/GameEntityType.h"
33 #include "entities/MapLight.h"
34 #include "entities/RenderedMovableEntity.h"
35 #include "entities/Tile.h"
36 #include "entities/Weapon.h"
37 #include "game/Player.h"
38 #include "game/Skill.h"
39 #include "game/SkillType.h"
40 #include "game/Seat.h"
41 #include "gamemap/MapHandler.h"
42 #include "gamemap/Pathfinding.h"
43 #include "gamemap/TileSet.h"
44 #include "goals/Goal.h"
45 #include "modes/ModeManager.h"
46 #include "network/ODServer.h"
47 #include "network/ServerMode.h"
48 #include "network/ServerNotification.h"
49 #include "render/ODFrameListener.h"
50 #include "rooms/Room.h"
51 #include "rooms/RoomManager.h"
52 #include "rooms/RoomPortal.h"
53 #include "rooms/RoomTreasury.h"
54 #include "rooms/RoomType.h"
55 #include "spells/Spell.h"
56 #include "sound/SoundEffectsManager.h"
57 #include "traps/Trap.h"
58 #include "traps/TrapManager.h"
59 #include "utils/ConfigManager.h"
60 #include "utils/Helper.h"
61 #include "utils/LogManager.h"
62 #include "utils/ResourceManager.h"
63 
64 #include <OgreTimer.h>
65 
66 #include <algorithm>
67 #include <cassert>
68 #include <cmath>
69 #include <cstdlib>
70 #include <iostream>
71 #include <sstream>
72 #include <string>
73 
74 const std::string DEFAULT_NICK = "You";
75 
76 using namespace std;
77 
78 /*! \brief A helper class for the A* search in the GameMap::path function.
79 *
80 * This class stores the requisite information about a tile which is placed in
81 * the search queue for the A-star, or A*, algorithm which is used to
82 * calculate paths in the path function.
83 *
84 * The A* description can be found here:
85 * http://en.wikipedia.org/wiki/A*_search_algorithm
86 */
87 class AstarEntry
88 {
89 public:
AstarEntry()90     AstarEntry() :
91         tile    (nullptr),
92         parent  (nullptr),
93         g       (0.0),
94         h       (0.0)
95     {}
96 
AstarEntry(Tile * tile,int x1,int y1,int x2,int y2)97     AstarEntry(Tile* tile, int x1, int y1, int x2, int y2) :
98         mHasBeenProcessed (false),
99         tile    (tile),
100         parent  (nullptr),
101         g       (0.0),
102         h       (0.0)
103     {
104         h = computeHeuristic(x1, y1, x2, y2);
105     }
106 
computeHeuristic(const int & x1,const int & y1,const int & x2,const int & y2)107     static double computeHeuristic(const int& x1, const int& y1, const int& x2, const int& y2)
108     {
109         return fabs(static_cast<double>(x2 - x1)) + fabs(static_cast<double>(y2 - y1));
110     }
111 
setHeuristic(const int & x1,const int & y1,const int & x2,const int & y2)112     void setHeuristic(const int& x1, const int& y1, const int& x2, const int& y2)
113     {
114         h = fabs(static_cast<double>(x2 - x1)) + fabs(static_cast<double>(y2 - y1));
115     }
116 
fCost() const117     inline double fCost() const
118     { return g + h; }
119 
getTile() const120     inline Tile* getTile() const
121     { return tile; }
122 
getHasBeenProcessed() const123     inline bool getHasBeenProcessed() const
124     { return mHasBeenProcessed; }
125 
setHasBeenProcessed()126     inline void setHasBeenProcessed()
127     { mHasBeenProcessed = true; }
128 
setTile(Tile * newTile)129     inline void setTile(Tile* newTile)
130     { tile = newTile; }
131 
getParent() const132     inline AstarEntry* getParent() const
133     { return parent; }
134 
setParent(AstarEntry * newParent)135     inline void setParent(AstarEntry* newParent)
136     { parent = newParent; }
137 
getG() const138     inline const double& getG() const
139     { return g; }
140 
setG(const double & newG)141     inline void setG(const double& newG)
142     { g = newG; }
143 
144 private:
145     bool        mHasBeenProcessed;
146     Tile*       tile;
147     AstarEntry* parent;
148     double      g;
149     double      h;
150 };
151 
152 
GameMap(bool isServerGameMap)153 GameMap::GameMap(bool isServerGameMap) :
154         TileContainer(isServerGameMap ? 15 : 0),
155         mIsServerGameMap(isServerGameMap),
156         mLocalPlayer(nullptr),
157         mLocalPlayerNick(DEFAULT_NICK),
158         mTurnNumber(-1),
159         mIsPaused(false),
160         mTimePayDay(0),
161         mFloodFillEnabled(false),
162         mIsFOWActivated(true),
163         mNumCallsTo_path(0),
164         mAiManager(*this),
165         mTileSet(nullptr)
166 {
167     resetUniqueNumbers();
168 }
169 
~GameMap()170 GameMap::~GameMap()
171 {
172     clearAll();
173 }
174 
serverStr()175 std::string GameMap::serverStr()
176 {
177     if (mIsServerGameMap)
178         return std::string("SERVER - ");
179 
180     return std::string("CLIENT (" + getLocalPlayerNick() + ") - ");
181 }
182 
isInEditorMode() const183 bool GameMap::isInEditorMode() const
184 {
185     if (isServerGameMap())
186         return (ODServer::getSingleton().getServerMode() == ServerMode::ModeEditor);
187 
188     return (ODFrameListener::getSingleton().getModeManager()->getCurrentModeType() == ModeManager::EDITOR);
189 }
190 
loadLevel(const std::string & levelFilepath)191 bool GameMap::loadLevel(const std::string& levelFilepath)
192 {
193     // We reset the creature definitions
194     clearClasses();
195     for(auto it : ConfigManager::getSingleton().getCreatureDefinitions())
196     {
197         addClassDescription(it.second);
198     }
199 
200     // We reset the weapons definitions
201     clearWeapons();
202     const std::vector<const Weapon*>& weapons = ConfigManager::getSingleton().getWeapons();
203     for(const Weapon* def : weapons)
204     {
205         addWeapon(def);
206     }
207 
208     // We add the rogue default seat (seatId = 0 and teamId = 0)
209     Seat* rogueSeat = Seat::createRogueSeat(this);
210     if(!addSeat(rogueSeat))
211     {
212         OD_LOG_ERR("Couldn't add rogue seat");
213         delete rogueSeat;
214     }
215 
216     if (MapHandler::readGameMapFromFile(levelFilepath, *this))
217         setLevelFileName(levelFilepath);
218     else
219         return false;
220 
221     return true;
222 }
223 
createNewMap(int sizeX,int sizeY)224 bool GameMap::createNewMap(int sizeX, int sizeY)
225 {
226     if (!allocateMapMemory(sizeX, sizeY))
227         return false;
228 
229     for (int jj = 0; jj < mMapSizeY; ++jj)
230     {
231         for (int ii = 0; ii < mMapSizeX; ++ii)
232         {
233             Tile* tile = new Tile(this, ii, jj);
234             tile->setName(Tile::buildName(ii, jj));
235             tile->setType(TileType::dirt);
236             addTile(tile);
237         }
238     }
239 
240     mTurnNumber = -1;
241 
242     return true;
243 }
244 
setAllFullnessAndNeighbors()245 void GameMap::setAllFullnessAndNeighbors()
246 {
247     for (int ii = 0; ii < mMapSizeX; ++ii)
248     {
249         for (int jj = 0; jj < mMapSizeY; ++jj)
250         {
251             Tile* tile = getTile(ii, jj);
252             tile->setFullness(tile->getFullness());
253             setTileNeighbors(tile);
254         }
255     }
256 }
257 
clearAll()258 void GameMap::clearAll()
259 {
260     clearCreatures();
261     clearClasses();
262     clearWeapons();
263     clearTraps();
264 
265     clearMapLights();
266     clearRooms();
267     // NOTE : clearRenderedMovableEntities should be called after clearRooms because clearRooms will try to remove the objects from the room
268     clearRenderedMovableEntities();
269     clearSpells();
270 
271     processDeletionQueues();
272 
273     clearTiles();
274     processDeletionQueues();
275 
276     clearGoalsForAllSeats();
277     clearSeats();
278     mLocalPlayer = nullptr;
279     clearPlayers();
280 
281     clearAiManager();
282 
283     mLocalPlayerNick = DEFAULT_NICK;
284     mTurnNumber = -1;
285     resetUniqueNumbers();
286     mIsFOWActivated = true;
287     mTimePayDay = 0;
288 
289     // We check if the different vectors are empty
290     if(!mActiveObjects.empty())
291     {
292         OD_LOG_ERR("mActiveObjects not empty size=" + Helper::toString(static_cast<uint32_t>(mActiveObjects.size())));
293         for(GameEntity* entity : mActiveObjects)
294         {
295             OD_LOG_ERR("entity not removed=" + entity->getName());
296         }
297         mActiveObjects.clear();
298     }
299     if(!mAnimatedObjects.empty())
300     {
301         OD_LOG_ERR("mAnimatedObjects not empty size=" + Helper::toString(static_cast<uint32_t>(mAnimatedObjects.size())));
302         for(GameEntity* entity : mAnimatedObjects)
303         {
304             OD_LOG_ERR("entity not removed=" + entity->getName());
305         }
306         mAnimatedObjects.clear();
307     }
308     if(!mEntitiesToDelete.empty())
309     {
310         OD_LOG_ERR("mEntitiesToDelete not empty size=" + Helper::toString(static_cast<uint32_t>(mEntitiesToDelete.size())));
311         for(GameEntity* entity : mEntitiesToDelete)
312         {
313             OD_LOG_ERR("entity not removed=" + entity->getName());
314         }
315         mEntitiesToDelete.clear();
316     }
317     if(!mGameEntityClientUpkeep.empty())
318     {
319         OD_LOG_ERR("mGameEntityClientUpkeep not empty size=" + Helper::toString(static_cast<uint32_t>(mGameEntityClientUpkeep.size())));
320         for(GameEntity* entity : mGameEntityClientUpkeep)
321         {
322             OD_LOG_ERR("entity not removed=" + entity->getName());
323         }
324         mGameEntityClientUpkeep.clear();
325     }
326 }
327 
clearCreatures()328 void GameMap::clearCreatures()
329 {
330     // We need to work on a copy of mCreatures because removeFromGameMap will remove them from this vector
331     std::vector<Creature*> creatures = mCreatures;
332     for (Creature* creature : creatures)
333     {
334         creature->removeFromGameMap();
335         creature->deleteYourself();
336     }
337 
338     mCreatures.clear();
339 }
340 
clearAiManager()341 void GameMap::clearAiManager()
342 {
343    mAiManager.clearAIList();
344 }
345 
clearClasses()346 void GameMap::clearClasses()
347 {
348     for (std::pair<const CreatureDefinition*,CreatureDefinition*>& def : mClassDescriptions)
349     {
350         if(def.second != nullptr)
351             delete def.second;
352 
353         // On client side, classes are sent by network so they should be deleted
354         if(!isServerGameMap())
355             delete def.first;
356     }
357     mClassDescriptions.clear();
358 }
359 
clearWeapons()360 void GameMap::clearWeapons()
361 {
362     for (std::pair<const Weapon*,Weapon*>& def : mWeapons)
363     {
364         if(def.second != nullptr)
365             delete def.second;
366 
367         // On client side, weapons are sent by network so they should be deleted
368         if(!isServerGameMap())
369             delete def.first;
370     }
371     mWeapons.clear();
372 }
373 
clearRenderedMovableEntities()374 void GameMap::clearRenderedMovableEntities()
375 {
376     // We need to work on a copy of mRenderedMovableEntities because removeFromGameMap will remove them from this vector
377     std::vector<RenderedMovableEntity*> renderedMovableEntities = mRenderedMovableEntities;
378     for (RenderedMovableEntity* obj : renderedMovableEntities)
379     {
380         obj->removeFromGameMap();
381         obj->deleteYourself();
382     }
383 
384     mRenderedMovableEntities.clear();
385 }
386 
clearPlayers()387 void GameMap::clearPlayers()
388 {
389     for (Player* player : mPlayers)
390         delete player;
391 
392     mPlayers.clear();
393 }
394 
resetUniqueNumbers()395 void GameMap::resetUniqueNumbers()
396 {
397     mUniqueNumberCreature = 0;
398     mUniqueNumberMissileObj = 0;
399     mUniqueNumberRoom = 0;
400     mUniqueNumberRenderedMovableEntity = 0;
401     mUniqueNumberTrap = 0;
402     mUniqueNumberMapLight = 0;
403     mUniqueFloodFillValue = 0;
404 }
405 
addClassDescription(const CreatureDefinition * c)406 void GameMap::addClassDescription(const CreatureDefinition *c)
407 {
408     mClassDescriptions.push_back(std::pair<const CreatureDefinition*,CreatureDefinition*>(c, nullptr));
409 }
410 
addWeapon(const Weapon * weapon)411 void GameMap::addWeapon(const Weapon* weapon)
412 {
413     mWeapons.push_back(std::pair<const Weapon*,Weapon*>(weapon, nullptr));
414 }
415 
getWeapon(int index)416 const Weapon* GameMap::getWeapon(int index)
417 {
418     if(index >= static_cast<int>(mWeapons.size()))
419     {
420         OD_LOG_ERR("index=" + Helper::toString(index));
421         return nullptr;
422     }
423 
424     std::pair<const Weapon*,Weapon*>& def = mWeapons[index];
425     if(def.second != nullptr)
426         return def.second;
427 
428     return def.first;
429 }
430 
getWeapon(const std::string & name)431 const Weapon* GameMap::getWeapon(const std::string& name)
432 {
433     for (std::pair<const Weapon*,Weapon*>& def : mWeapons)
434     {
435         if(def.second != nullptr)
436         {
437             if (def.second->getName().compare(name) == 0)
438                 return def.second;
439         }
440         else if(def.first->getName().compare(name) == 0)
441             return def.first;
442     }
443 
444     return nullptr;
445 }
446 
getWeaponForTuning(const std::string & name)447 Weapon* GameMap::getWeaponForTuning(const std::string& name)
448 {
449     for (std::pair<const Weapon*,Weapon*>& def : mWeapons)
450     {
451         if(def.second != nullptr)
452         {
453             if (def.second->getName().compare(name) == 0)
454                 return def.second;
455         }
456         else if(def.first->getName().compare(name) == 0)
457         {
458             // If the definition is not a copy, we make one because we want to keep the original so we are able
459             // to save the changes if the map is saved
460             def.second = new Weapon(*def.first);
461             return def.second;
462         }
463     }
464 
465     // It is a new definition
466     Weapon* def = new Weapon(name);
467     mWeapons.push_back(std::pair<const Weapon*,Weapon*>(nullptr, def));
468     return def;
469 }
470 
numWeapons()471 uint32_t GameMap::numWeapons()
472 {
473     return mWeapons.size();
474 }
475 
saveLevelEquipments(std::ofstream & levelFile)476 void GameMap::saveLevelEquipments(std::ofstream& levelFile)
477 {
478     for (std::pair<const Weapon*,Weapon*>& def : mWeapons)
479     {
480         if(def.second == nullptr)
481             continue;
482 
483         Weapon::writeWeaponDiff(def.first, def.second, levelFile);
484     }
485 }
486 
addCreature(Creature * cc)487 void GameMap::addCreature(Creature *cc)
488 {
489     OD_LOG_INF(serverStr() + "Adding Creature " + cc->getName()
490         + ", seatId=" + (cc->getSeat() != nullptr ? Helper::toString(cc->getSeat()->getId()) : std::string("null")));
491 
492     mCreatures.push_back(cc);
493 }
494 
removeCreature(Creature * c)495 void GameMap::removeCreature(Creature *c)
496 {
497     OD_LOG_INF(serverStr() + "Removing Creature " + c->getName());
498 
499     std::vector<Creature*>::iterator it = std::find(mCreatures.begin(), mCreatures.end(), c);
500     if(it == mCreatures.end())
501     {
502         OD_LOG_ERR("creature name=" + c->getName());
503         return;
504     }
505 
506     mCreatures.erase(it);
507 }
508 
queueEntityForDeletion(GameEntity * ge)509 void GameMap::queueEntityForDeletion(GameEntity *ge)
510 {
511     mEntitiesToDelete.push_back(ge);
512 }
513 
getClassDescription(const string & className)514 const CreatureDefinition* GameMap::getClassDescription(const string &className)
515 {
516     for (std::pair<const CreatureDefinition*,CreatureDefinition*>& def : mClassDescriptions)
517     {
518         if (def.second != nullptr)
519         {
520             if(def.second->getClassName().compare(className) == 0)
521                 return def.second;
522         }
523         else if(def.first->getClassName().compare(className) == 0)
524             return def.first;
525     }
526 
527     return nullptr;
528 }
529 
getClassDescriptionForTuning(const std::string & name)530 CreatureDefinition* GameMap::getClassDescriptionForTuning(const std::string& name)
531 {
532     for (std::pair<const CreatureDefinition*,CreatureDefinition*>& def : mClassDescriptions)
533     {
534         if(def.second != nullptr)
535         {
536             if (def.second->getClassName().compare(name) == 0)
537                 return def.second;
538         }
539         else if(def.first->getClassName().compare(name) == 0)
540         {
541             def.second = new CreatureDefinition(*def.first);
542             return def.second;
543         }
544     }
545 
546     // It is a new definition
547     CreatureDefinition* def = new CreatureDefinition(name);
548     mClassDescriptions.push_back(std::pair<const CreatureDefinition*,CreatureDefinition*>(nullptr, def));
549     return def;
550 }
551 
getCreaturesByAlliedSeat(const Seat * seat) const552 std::vector<Creature*> GameMap::getCreaturesByAlliedSeat(const Seat* seat) const
553 {
554     std::vector<Creature*> tempVector;
555 
556     // Loop over all the creatures in the GameMap and add them to the temp vector if their seat matches the one in parameter.
557     for (Creature* creature : mCreatures)
558     {
559         if (seat->isAlliedSeat(creature->getSeat()) && creature->isAlive())
560             tempVector.push_back(creature);
561     }
562 
563     return tempVector;
564 }
565 
getCreaturesBySeat(const Seat * seat) const566 std::vector<Creature*> GameMap::getCreaturesBySeat(const Seat* seat) const
567 {
568     std::vector<Creature*> tempVector;
569 
570     // Loop over all the creatures in the GameMap and add them to the temp vector if their seat matches the one in parameter.
571     for (Creature* creature : mCreatures)
572     {
573         if (creature->getSeat() == seat && creature->isAlive())
574             tempVector.push_back(creature);
575     }
576 
577     return tempVector;
578 }
579 
getWorkerToPickupBySeat(Seat * seat)580 Creature* GameMap::getWorkerToPickupBySeat(Seat* seat)
581 {
582     // 1 - Take idle worker
583     // 2 - Take fighting/fleeing
584     // 3 - Take claimers
585     // 4 - Take diggers
586     // 5 - Take gold digger/depositers or all the rest
587     // For each, we pickup the highest leveled available
588 
589     uint32_t idleWorkerLevel = 0;
590     Creature* idleWorker = nullptr;
591     uint32_t fightingWorkerLevel = 0;
592     Creature* fightingWorker = nullptr;
593     uint32_t claimerWorkerLevel = 0;
594     Creature* claimerWorker = nullptr;
595     uint32_t diggerWorkerLevel = 0;
596     Creature* diggerWorker = nullptr;
597     uint32_t otherWorkerLevel = 0;
598     Creature* otherWorker = nullptr;
599     std::vector<Creature*> creatures = getCreaturesBySeat(seat);
600     for(Creature* creature : creatures)
601     {
602         if(!creature->getDefinition()->isWorker())
603             continue;
604 
605         // Creatures in containment cannot be picked up like that
606         if(creature->isInContainment())
607             continue;
608 
609         if(!creature->tryPickup(seat))
610             continue;
611 
612         bool isIdle = true;
613         bool isFighting = false;
614         bool isClaiming = false;
615         bool isDigging = false;
616 
617         for(const std::unique_ptr<CreatureAction>& action : creature->getActions())
618         {
619             switch(action.get()->getType())
620             {
621                 case CreatureActionType::fight:
622                 case CreatureActionType::flee:
623                     isIdle = false;
624                     isFighting = true;
625                     break;
626 
627                 case CreatureActionType::searchGroundTileToClaim:
628                 case CreatureActionType::claimGroundTile:
629                     isIdle = false;
630                     isClaiming = true;
631                     break;
632 
633                 case CreatureActionType::searchTileToDig:
634                 case CreatureActionType::digTile:
635                     isIdle = false;
636                     isDigging = true;
637                     break;
638 
639                 case CreatureActionType::walkToTile:
640                     // We do nothing
641                     break;
642 
643                 default:
644                     // Other
645                     isIdle = false;
646                     break;
647             }
648         }
649 
650         if(isIdle)
651         {
652             if(creature->getLevel() > idleWorkerLevel)
653             {
654                 idleWorker = creature;
655                 idleWorkerLevel = creature->getLevel();
656             }
657         }
658         else if(isFighting)
659         {
660             if(creature->getLevel() > fightingWorkerLevel)
661             {
662                 fightingWorker = creature;
663                 fightingWorkerLevel = creature->getLevel();
664             }
665         }
666         else if(isClaiming)
667         {
668             if(creature->getLevel() > claimerWorkerLevel)
669             {
670                 claimerWorker = creature;
671                 claimerWorkerLevel = creature->getLevel();
672             }
673         }
674         else if(isDigging)
675         {
676             if(creature->getLevel() > diggerWorkerLevel)
677             {
678                 diggerWorker = creature;
679                 diggerWorkerLevel = creature->getLevel();
680             }
681         }
682         else
683         {
684             if(creature->getLevel() > otherWorkerLevel)
685             {
686                 otherWorker = creature;
687                 otherWorkerLevel = creature->getLevel();
688             }
689         }
690     }
691     if(idleWorker != nullptr)
692         return idleWorker;
693     else if(fightingWorker != nullptr)
694         return fightingWorker;
695     else if(claimerWorker != nullptr)
696         return claimerWorker;
697     else if(diggerWorker != nullptr)
698         return diggerWorker;
699     else if(otherWorker != nullptr)
700         return otherWorker;
701 
702     return nullptr;
703 }
704 
getFighterToPickupBySeat(Seat * seat)705 Creature* GameMap::getFighterToPickupBySeat(Seat* seat)
706 {
707     // 1 - Take fleeing fighters
708     // 2 - Take idle fighters
709     // 3 - Take busy (working/eating) fighters
710     // 4 - Then take fighting fighters or all the rest
711     // For each, we pickup the highest leveled available
712 
713     uint32_t idleFighterLevel = 0;
714     Creature* idleFighter = nullptr;
715     uint32_t fleeingFighterLevel = 0;
716     Creature* fleeingFighter = nullptr;
717     uint32_t busyFighterLevel = 0;
718     Creature* busyFighter = nullptr;
719     uint32_t otherFighterLevel = 0;
720     Creature* otherFighter = nullptr;
721     std::vector<Creature*> creatures = getCreaturesBySeat(seat);
722     for(Creature* creature : creatures)
723     {
724         if(creature->getDefinition()->isWorker())
725             continue;
726 
727         if(!creature->tryPickup(seat))
728             continue;
729 
730         bool isIdle = true;
731         bool isFleeing = false;
732         bool isBusy = false;
733 
734         for(const std::unique_ptr<CreatureAction>& action : creature->getActions())
735         {
736             switch(action.get()->getType())
737             {
738                 case CreatureActionType::flee:
739                     isIdle = false;
740                     isFleeing = true;
741                     break;
742 
743                 case CreatureActionType::searchFood:
744                 case CreatureActionType::searchJob:
745                 case CreatureActionType::useRoom:
746                     isIdle = false;
747                     isBusy = true;
748                     break;
749 
750                 case CreatureActionType::walkToTile:
751                     // We do nothing
752                     break;
753 
754                 default:
755                     // Other
756                     isIdle = false;
757                     break;
758             }
759         }
760 
761         if(isFleeing)
762         {
763             if(creature->getLevel() > fleeingFighterLevel)
764             {
765                 fleeingFighter = creature;
766                 fleeingFighterLevel = creature->getLevel();
767             }
768         }
769         else if(isIdle)
770         {
771             if(creature->getLevel() > idleFighterLevel)
772             {
773                 idleFighter = creature;
774                 idleFighterLevel = creature->getLevel();
775             }
776         }
777         else if(isBusy)
778         {
779             if(creature->getLevel() > busyFighterLevel)
780             {
781                 busyFighter = creature;
782                 busyFighterLevel = creature->getLevel();
783             }
784         }
785         else
786         {
787             if(creature->getLevel() > otherFighterLevel)
788             {
789                 otherFighter = creature;
790                 otherFighterLevel = creature->getLevel();
791             }
792         }
793     }
794     if(fleeingFighter != nullptr)
795         return fleeingFighter;
796     else if(idleFighter != nullptr)
797         return idleFighter;
798     else if(busyFighter != nullptr)
799         return busyFighter;
800     else if(otherFighter != nullptr)
801         return otherFighter;
802 
803     return nullptr;
804 }
805 
addAnimatedObject(MovableGameEntity * a)806 void GameMap::addAnimatedObject(MovableGameEntity *a)
807 {
808     mAnimatedObjects.push_back(a);
809 }
810 
removeAnimatedObject(MovableGameEntity * a)811 void GameMap::removeAnimatedObject(MovableGameEntity *a)
812 {
813     std::vector<MovableGameEntity*>::iterator it = std::find(mAnimatedObjects.begin(), mAnimatedObjects.end(), a);
814     if(it == mAnimatedObjects.end())
815         return;
816 
817     mAnimatedObjects.erase(it);
818 }
819 
getAnimatedObject(const std::string & name) const820 MovableGameEntity* GameMap::getAnimatedObject(const std::string& name) const
821 {
822     for (MovableGameEntity* mge : mAnimatedObjects)
823     {
824         if (mge->getName().compare(name) == 0)
825             return mge;
826     }
827 
828     return nullptr;
829 }
830 
addRenderedMovableEntity(RenderedMovableEntity * obj)831 void GameMap::addRenderedMovableEntity(RenderedMovableEntity *obj)
832 {
833     OD_LOG_INF(serverStr() + "Adding rendered object " + obj->getName()
834         + ",MeshName=" + obj->getMeshName());
835     mRenderedMovableEntities.push_back(obj);
836 }
837 
removeRenderedMovableEntity(RenderedMovableEntity * obj)838 void GameMap::removeRenderedMovableEntity(RenderedMovableEntity *obj)
839 {
840     OD_LOG_INF(serverStr() + "Removing rendered object " + obj->getName()
841         + ",MeshName=" + obj->getMeshName());
842     std::vector<RenderedMovableEntity*>::iterator it = std::find(mRenderedMovableEntities.begin(), mRenderedMovableEntities.end(), obj);
843     if(it == mRenderedMovableEntities.end())
844     {
845         OD_LOG_ERR("obj name=" + obj->getName());
846         return;
847     }
848 
849     mRenderedMovableEntities.erase(it);
850 }
851 
getRenderedMovableEntity(const std::string & name)852 RenderedMovableEntity* GameMap::getRenderedMovableEntity(const std::string& name)
853 {
854     for(RenderedMovableEntity* renderedMovableEntity : mRenderedMovableEntities)
855     {
856         if(name.compare(renderedMovableEntity->getName()) == 0)
857             return renderedMovableEntity;
858     }
859     return nullptr;
860 }
861 
addActiveObject(GameEntity * a)862 void GameMap::addActiveObject(GameEntity *a)
863 {
864     // Active objects are only used on server side
865     if(!isServerGameMap())
866         return;
867 
868     mActiveObjects.push_back(a);
869 }
870 
removeActiveObject(GameEntity * a)871 void GameMap::removeActiveObject(GameEntity *a)
872 {
873     // Active objects are only used on server side
874     if(!isServerGameMap())
875         return;
876 
877     auto it = std::find(mActiveObjects.begin(), mActiveObjects.end(), a);
878     if(it == mActiveObjects.end())
879     {
880         OD_LOG_ERR("ActiveObject name=" + a->getName());
881         return;
882     }
883 
884     mActiveObjects.erase(it);
885 }
886 
numClassDescriptions()887 unsigned int GameMap::numClassDescriptions()
888 {
889     return mClassDescriptions.size();
890 }
891 
saveLevelClassDescriptions(std::ofstream & levelFile)892 void GameMap::saveLevelClassDescriptions(std::ofstream& levelFile)
893 {
894     for (std::pair<const CreatureDefinition*,CreatureDefinition*>& def : mClassDescriptions)
895     {
896         if(def.second == nullptr)
897             continue;
898 
899         CreatureDefinition::writeCreatureDefinitionDiff(def.first, def.second, levelFile, ConfigManager::getSingleton().getCreatureDefinitions());
900     }
901 }
902 
getClassDescription(int index)903 const CreatureDefinition* GameMap::getClassDescription(int index)
904 {
905     std::pair<const CreatureDefinition*,CreatureDefinition*>& def = mClassDescriptions[index];
906     if(def.second != nullptr)
907         return def.second;
908     else
909         return def.first;
910 }
911 
createAllEntities()912 void GameMap::createAllEntities()
913 {
914     mTileSet = ConfigManager::getSingleton().getTileSet(mTileSetName);
915 
916     if(isServerGameMap())
917     {
918         // Set positions and update active spots
919         for (RenderedMovableEntity* rendered : mRenderedMovableEntities)
920         {
921             rendered->setPosition(rendered->getPosition());
922         }
923 
924         for (Room* room : mRooms)
925         {
926             room->updateActiveSpots();
927         }
928 
929         for (Spell* spell : mSpells)
930         {
931             spell->setPosition(spell->getPosition());
932         }
933 
934         for (Trap* trap : mTraps)
935         {
936             trap->updateActiveSpots();
937         }
938 
939         for (Creature* creature : mCreatures)
940         {
941             //Set up definition for creature. This was previously done in createMesh for some reason.
942             creature->setupDefinition(*this, *ConfigManager::getSingleton().getCreatureDefinitionDefaultWorker());
943             //Set position to update info on what tile the creature is in.
944             creature->setPosition(creature->getPosition());
945             //Doesn't do anything currently.
946             //creature->restoreInitialEntityState();
947         }
948     }
949     else
950     {
951         // On client we create meshes
952         // Create OGRE entities for map tiles
953         for (int jj = 0; jj < getMapSizeY(); ++jj)
954         {
955             for (int ii = 0; ii < getMapSizeX(); ++ii)
956             {
957                 getTile(ii,jj)->createMesh();
958             }
959         }
960 
961         // Create OGRE entities for rendered entities
962         for (RenderedMovableEntity* rendered : mRenderedMovableEntities)
963         {
964             rendered->createMesh();
965             rendered->setPosition(rendered->getPosition());
966         }
967 
968         // Create OGRE entities for the creatures
969         for (Creature* creature : mCreatures)
970         {
971             creature->setupDefinition(*this, *ConfigManager::getSingleton().getCreatureDefinitionDefaultWorker());
972             creature->createMesh();
973             creature->setPosition(creature->getPosition());
974         }
975 
976         // Create OGRE entities for the map lights.
977         for (MapLight* mapLight: mMapLights)
978         {
979             mapLight->createMesh();
980             mapLight->setPosition(mapLight->getPosition());
981         }
982 
983         // Create OGRE entities for the rooms
984         for (Room* room : mRooms)
985         {
986             room->createMesh();
987         }
988 
989         // Create OGRE entities for the rooms
990         for (Trap* trap : mTraps)
991         {
992             trap->createMesh();
993         }
994 
995         // Create OGRE entities for spells
996         for (Spell* spell : mSpells)
997         {
998             spell->createMesh();
999             spell->setPosition(spell->getPosition());
1000         }
1001     }
1002 
1003     for (Room* room : mRooms)
1004     {
1005         room->restoreInitialEntityState();
1006     }
1007 
1008     for (Trap* trap : mTraps)
1009     {
1010         trap->restoreInitialEntityState();
1011     }
1012 
1013     //Doesn't do anything currently
1014     /*
1015     for (Spell* spell : mSpells)
1016     {
1017         spell->restoreInitialEntityState();
1018     }
1019 
1020     for (RenderedMovableEntity* rendered : mRenderedMovableEntities)
1021     {
1022         rendered->restoreInitialEntityState();
1023     }*/
1024 
1025     OD_LOG_INF("entities created");
1026 }
1027 
destroyAllEntities()1028 void GameMap::destroyAllEntities()
1029 {
1030     // Destroy OGRE entities for map tiles
1031     for (int jj = 0; jj < getMapSizeY(); ++jj)
1032     {
1033         for (int ii = 0; ii < getMapSizeX(); ++ii)
1034         {
1035             Tile* tile = getTile(ii,jj);
1036             tile->destroyMesh();
1037         }
1038     }
1039 
1040     // Destroy OGRE entities for the creatures
1041     for (Creature* creature : mCreatures)
1042     {
1043         creature->destroyMesh();
1044     }
1045 
1046     // Destroy OGRE entities for the map lights.
1047     for (MapLight* mapLight : mMapLights)
1048     {
1049         mapLight->destroyMesh();
1050     }
1051 
1052     // Destroy OGRE entities for the rooms
1053     for (Room *currentRoom : mRooms)
1054     {
1055         currentRoom->destroyMesh();
1056     }
1057 
1058     // Destroy OGRE entities for the traps
1059     for (Trap* trap : mTraps)
1060     {
1061         trap->destroyMesh();
1062     }
1063 }
1064 
getCreature(const std::string & cName) const1065 Creature* GameMap::getCreature(const std::string& cName) const
1066 {
1067     for (Creature* creature : mCreatures)
1068     {
1069         if (creature->getName().compare(cName) == 0)
1070             return creature;
1071     }
1072     return nullptr;
1073 }
1074 
doTurn(double timeSinceLastTurn)1075 void GameMap::doTurn(double timeSinceLastTurn)
1076 {
1077     OD_LOG_INF("Computing turn " + Helper::toString(mTurnNumber) + ", timeSinceLastTurn=" + Helper::toString(timeSinceLastTurn));
1078     unsigned int numCallsTo_path_atStart = mNumCallsTo_path;
1079 
1080     uint32_t miscUpkeepTime = doMiscUpkeep(timeSinceLastTurn);
1081 
1082     for (Seat* seat : mSeats)
1083     {
1084         if(seat->getPlayer() == nullptr)
1085             continue;
1086 
1087         seat->getPlayer()->upkeepPlayer(timeSinceLastTurn);
1088     }
1089 
1090     OD_LOG_INF("During this turn there were " + Helper::toString(mNumCallsTo_path - numCallsTo_path_atStart)
1091         + " calls to GameMap::path(), miscUpkeepTime=" + Helper::toString(miscUpkeepTime));
1092 }
1093 
doPlayerAITurn(double timeSinceLastTurn)1094 void GameMap::doPlayerAITurn(double timeSinceLastTurn)
1095 {
1096     mAiManager.doTurn(timeSinceLastTurn);
1097 }
1098 
doMiscUpkeep(double timeSinceLastTurn)1099 unsigned long int GameMap::doMiscUpkeep(double timeSinceLastTurn)
1100 {
1101     Tile *tempTile;
1102     Ogre::Timer stopwatch;
1103     unsigned long int timeTaken;
1104 
1105     // We check if it is pay day
1106     mTimePayDay += timeSinceLastTurn;
1107     if((mTimePayDay >= ConfigManager::getSingleton().getTimePayDay()))
1108     {
1109         mTimePayDay = 0;
1110         // We only notify players with a dungeon temple
1111         for(Player* player : getPlayers())
1112         {
1113             if(!player->getIsHuman())
1114                 continue;
1115             if(player->getHasLost())
1116                 continue;
1117 
1118             // We notify the player if he owns a fighter only
1119             bool isCreatureSeat = false;
1120             for(Creature* creature : mCreatures)
1121             {
1122                 if(creature->getSeat() != player->getSeat())
1123                     continue;
1124 
1125                 if(creature->getDefinition()->isWorker())
1126                     continue;
1127 
1128                 isCreatureSeat = true;
1129                 break;
1130             }
1131 
1132             if(!isCreatureSeat)
1133                 continue;
1134 
1135             ServerNotification *serverNotification = new ServerNotification(
1136                 ServerNotificationType::chatServer, player);
1137             serverNotification->mPacket << "It's pay day !" << EventShortNoticeType::majorGameEvent;
1138             ODServer::getSingleton().queueServerNotification(serverNotification);
1139         }
1140 
1141         for(Creature* creature : mCreatures)
1142         {
1143             creature->itsPayDay();
1144         }
1145     }
1146 
1147     // Loop over all the filled seats in the game and check all the unfinished goals for each seat.
1148     // Add any seats with no remaining goals to the winningSeats vector.
1149     for (Seat* seat : mSeats)
1150     {
1151         if(seat->getPlayer() == nullptr)
1152             continue;
1153 
1154         // Check the previously completed goals to make sure they are still met.
1155         seat->checkAllCompletedGoals();
1156 
1157         // Check the goals and move completed ones to the completedGoals list for the seat.
1158         //NOTE: Once seats are placed on this list, they stay there even if goals are unmet.  We may want to change this.
1159         if (seat->checkAllGoals() == 0 && seat->numFailedGoals() == 0)
1160             addWinningSeat(seat);
1161 
1162         seat->mNumCreaturesFightersMax = getMaxNumberCreatures(seat);
1163 
1164         // Set the creatures count to 0. It will be reset by the next count in doTurn()
1165         seat->mNumCreaturesFighters = 0;
1166         seat->mNumCreaturesWorkers = 0;
1167     }
1168 
1169     // Count how many creatures the player controls
1170     for(Creature* creature : mCreatures)
1171     {
1172         // Check to see if the creature has died.
1173         if (!creature->isAlive())
1174             continue;
1175 
1176         Seat *tempSeat = creature->getSeat();
1177         if(tempSeat == nullptr)
1178             continue;
1179 
1180         // We only count fighters
1181         if (creature->getDefinition()->isWorker())
1182             ++(tempSeat->mNumCreaturesWorkers);
1183         else
1184             ++(tempSeat->mNumCreaturesFighters);
1185     }
1186 
1187     // At each upkeep, we re-compute tiles with vision
1188     for (Seat* seat : mSeats)
1189         seat->clearTilesWithVision();
1190 
1191     for (int jj = 0; jj < getMapSizeY(); ++jj)
1192     {
1193         for (int ii = 0; ii < getMapSizeX(); ++ii)
1194         {
1195             getTile(ii,jj)->clearVision();
1196         }
1197     }
1198 
1199     // Compute vision. We need to compute every seats including AI because
1200     // a human can be allied with an AI and they would share vision
1201     for (int jj = 0; jj < getMapSizeY(); ++jj)
1202     {
1203         for (int ii = 0; ii < getMapSizeX(); ++ii)
1204         {
1205             getTile(ii,jj)->computeVisibleTiles();
1206         }
1207     }
1208 
1209     for (Creature* creature : mCreatures)
1210     {
1211         creature->computeVisibleTiles();
1212     }
1213 
1214     for (Spell* spell : mSpells)
1215     {
1216         spell->computeVisibleTiles();
1217     }
1218 
1219     for (Seat* seat : mSeats)
1220     {
1221         if(!seat->getIsDebuggingVision())
1222             continue;
1223 
1224         seat->refreshSeatVisualDebug();
1225     }
1226 
1227     // We send to each seat the list of tiles he has vision on
1228     for (Seat* seat : mSeats)
1229         seat->sendVisibleTiles();
1230 
1231     // Carry out the upkeep round of all the active objects in the game.
1232     // Here, we work on a copy of the active objects list because they might
1233     // try to remove themselves which would break the iterator
1234     std::vector<GameEntity*> activeObjects = mActiveObjects;
1235     for(GameEntity* ge : activeObjects)
1236         ge->doUpkeep();
1237 
1238     // Carry out the upkeep round for each seat. This means recomputing how much gold is
1239     // available in their treasuries, how much mana they gain/lose during this turn, etc.
1240     for (Seat* seat : mSeats)
1241     {
1242         if(seat->getPlayer() == nullptr)
1243             continue;
1244 
1245         seat->computeSeatBeginTurn();
1246 
1247         // Add the amount of mana this seat accrued this turn if the player has a dungeon temple
1248         if(seat->getNbRooms(RoomType::dungeonTemple) == 0)
1249         {
1250             seat->mManaDelta = 0;
1251             seat->getPlayer()->notifyNoMoreDungeonTemple();
1252         }
1253         else
1254         {
1255             seat->mManaDelta = 50 + seat->getNumClaimedTiles();
1256             seat->mMana += seat->mManaDelta;
1257             double maxMana = ConfigManager::getSingleton().getMaxManaPerSeat();
1258             if (seat->mMana > maxMana)
1259                 seat->mMana = maxMana;
1260         }
1261 
1262         // Update the count on how much gold is available in all of the treasuries claimed by the given seat.
1263         seat->mGold = 0;
1264         seat->mGoldMax = 0;
1265         for (Room* room : getRooms())
1266         {
1267             if(room->getSeat() != seat)
1268                 continue;
1269 
1270             seat->mGold += room->getTotalGoldStored();
1271             seat->mGoldMax += room->getTotalGoldStorage();
1272         }
1273     }
1274 
1275     // Determine the number of tiles claimed by each seat.
1276     // Begin by setting the number of claimed tiles for each seat to 0.
1277     for (Seat* seat : mSeats)
1278         seat->setNumClaimedTiles(0);
1279 
1280     // Now loop over all of the tiles, if the tile is claimed increment the given seats count.
1281     for (int jj = 0; jj < getMapSizeY(); ++jj)
1282     {
1283         for (int ii = 0; ii < getMapSizeX(); ++ii)
1284         {
1285             tempTile = getTile(ii,jj);
1286 
1287             // Check to see if the current tile is claimed by anyone.
1288             if (tempTile->isClaimed())
1289             {
1290                 // Increment the count of the seat who owns the tile.
1291                 tempTile->getSeat()->incrementNumClaimedTiles();
1292             }
1293         }
1294     }
1295 
1296     timeTaken = stopwatch.getMicroseconds();
1297     return timeTaken;
1298 }
1299 
updateAnimations(Ogre::Real timeSinceLastFrame)1300 void GameMap::updateAnimations(Ogre::Real timeSinceLastFrame)
1301 {
1302     if(mIsPaused)
1303         return;
1304 
1305     if(getTurnNumber() <= 0)
1306         return;
1307 
1308     // Update the animations on all AnimatedObjects
1309     for(MovableGameEntity* mge : mAnimatedObjects)
1310         mge->update(timeSinceLastFrame);
1311 }
1312 
playerIsFighting(Player * player,Tile * tile)1313 void GameMap::playerIsFighting(Player* player, Tile* tile)
1314 {
1315     if (player == nullptr)
1316         return;
1317 
1318     // No need to check for inactive players
1319     if (player->getSeat() == nullptr)
1320         return;
1321 
1322     // Get every player's allies
1323     for (Player* ally : mPlayers)
1324     {
1325         // No need to warn AI about music
1326         if (!ally || !ally->getIsHuman())
1327             continue;
1328 
1329         if (ally->getSeat() == nullptr || !ally->getSeat()->isAlliedSeat(player->getSeat()))
1330             continue;
1331 
1332         // Warn the ally about the battle
1333         ally->notifyTeamFighting(player, tile);
1334     }
1335 }
1336 
findBestPath(const Creature * creature,Tile * tileStart,const std::vector<Tile * > possibleDests,Tile * & chosenTile)1337 std::list<Tile*> GameMap::findBestPath(const Creature* creature, Tile* tileStart, const std::vector<Tile*> possibleDests,
1338     Tile*& chosenTile)
1339 {
1340     chosenTile = nullptr;
1341     std::list<Tile*> returnList;
1342     if(possibleDests.empty())
1343         return returnList;
1344 
1345     // We start by sorting the vector
1346     std::vector<Tile*> possibleDestsTmp = possibleDests;
1347     std::sort(possibleDestsTmp.begin(), possibleDestsTmp.end(), [this, &tileStart](Tile* tile1, Tile* tile2){
1348               int d1 = Pathfinding::squaredDistanceTile(*tile1, *tileStart);
1349               int d2 = Pathfinding::squaredDistanceTile(*tile2, *tileStart);
1350               return d1 < d2;
1351         });
1352 
1353     double magic = 2.0;
1354     for(Tile* tile : possibleDestsTmp)
1355     {
1356         if(!pathExists(creature, tileStart, tile))
1357             continue;
1358 
1359         // The first reachable tile is the best by default
1360         Ogre::Real dist = std::hypotf(tile->getX() - tileStart->getX(), tile->getY() - tileStart->getY());
1361         if(chosenTile == nullptr)
1362         {
1363             chosenTile = tile;
1364             returnList = path(tileStart, tile, creature, creature->getSeat(), false);
1365             continue;
1366         }
1367 
1368         // We compute the path only if walkableDist < (dist * magic).
1369         double walkableDist = static_cast<double>(returnList.size());
1370         if(walkableDist < (dist * magic))
1371             continue;
1372 
1373         std::list<Tile*> pathTmp = path(tileStart, tile, creature, creature->getSeat(), false);
1374         if(pathTmp.size() < returnList.size())
1375         {
1376             // The path is shorter
1377             chosenTile = tile;
1378             returnList = pathTmp;
1379         }
1380     }
1381     return returnList;
1382 }
1383 
pathExists(const Creature * creature,Tile * tileStart,Tile * tileEnd)1384 bool GameMap::pathExists(const Creature* creature, Tile* tileStart, Tile* tileEnd)
1385 {
1386     // If floodfill is not enabled, we cannot check if the path exists so we return true
1387     if(!mFloodFillEnabled)
1388         return true;
1389 
1390     // We check if the tile we are heading to is walkable. We don't do the same for the start tile because it might
1391     //not be the case if a creature is on a door tile while it is closed
1392     if(creature == nullptr)
1393         return false;
1394 
1395     FloodFillType floodFill = FloodFillType::ground;
1396     if((creature->getMoveSpeedGround() > 0.0) &&
1397         (creature->getMoveSpeedWater() > 0.0) &&
1398         (creature->getMoveSpeedLava() > 0.0))
1399     {
1400         floodFill = FloodFillType::groundWaterLava;
1401     }
1402     if((creature->getMoveSpeedGround() > 0.0) &&
1403         (creature->getMoveSpeedWater() > 0.0))
1404     {
1405         floodFill = FloodFillType::groundWater;
1406     }
1407     if((creature->getMoveSpeedGround() > 0.0) &&
1408         (creature->getMoveSpeedLava() > 0.0))
1409     {
1410         floodFill = FloodFillType::groundLava;
1411     }
1412 
1413     if(creature->getDefinition()->isWorker())
1414     {
1415         // Workers can go on a tile if and only if the path is open for any creature. If it is closed, that
1416         // means that a door is closed
1417         for(Seat* seat : mSeats)
1418         {
1419             if(tileStart->isSameFloodFill(seat, floodFill, tileEnd))
1420                 continue;
1421 
1422             return false;
1423         }
1424 
1425         return true;
1426     }
1427     else
1428     {
1429         // For fighters, we can test their seat only because if they reach a closed enemy door, they
1430         // will attack it
1431         return tileStart->isSameFloodFill(creature->getSeat(), floodFill, tileEnd);
1432     }
1433 }
1434 
path(int x1,int y1,int x2,int y2,const Creature * creature,Seat * seat,bool throughDiggableTiles)1435 std::list<Tile*> GameMap::path(int x1, int y1, int x2, int y2, const Creature* creature, Seat* seat, bool throughDiggableTiles)
1436 {
1437     ++mNumCallsTo_path;
1438     std::list<Tile*> returnList;
1439 
1440     // If the start tile was not found return an empty path
1441     Tile* start = getTile(x1, y1);
1442     if (start == nullptr)
1443         return returnList;
1444 
1445     // If the end tile was not found return an empty path
1446     Tile* destination = getTile(x2, y2);
1447     if (destination == nullptr)
1448         return returnList;
1449 
1450     if (creature == nullptr)
1451         return returnList;
1452 
1453     // If flood filling is enabled, we can possibly eliminate this path by checking to see if they two tiles are floodfilled differently.
1454     if (!throughDiggableTiles && !pathExists(creature, start, destination))
1455         return returnList;
1456 
1457     AstarEntry *currentEntry = new AstarEntry(start, x1, y1, x2, y2);
1458     AstarEntry neighbor;
1459 
1460     std::vector<AstarEntry*> openList;
1461     openList.push_back(currentEntry);
1462 
1463     // This list will contain the processed and the to process entries
1464     // allowing to quickly know if a tile has been processed or not
1465     std::vector<std::vector<AstarEntry*>> processList(getMapSizeX(), std::vector<AstarEntry*>(getMapSizeY(), nullptr));
1466     processList[currentEntry->getTile()->getX()][currentEntry->getTile()->getY()] = currentEntry;
1467     AstarEntry* destinationEntry = nullptr;
1468     while (true)
1469     {
1470         // if the openList is empty we failed to find a path
1471         if (openList.empty())
1472             break;
1473 
1474         // openList being sorted, the last element is the smallest
1475         auto smallestAstar = openList.rbegin();
1476         openList.pop_back();
1477 
1478         currentEntry = *smallestAstar;
1479         currentEntry->setHasBeenProcessed();
1480 
1481         // We found the path, break out of the search loop
1482         if (currentEntry->getTile() == destination)
1483         {
1484             destinationEntry = currentEntry;
1485             break;
1486         }
1487 
1488         // Check the tiles surrounding the current square
1489         bool areTilesPassable[4] = {false, false, false, false};
1490         // Note : to disable diagonals, process tiles from 0 to 3. To allow them, process tiles from 0 to 7
1491         for (unsigned int i = 0; i < 8; ++i)
1492         {
1493             Tile* neighborTile = nullptr;
1494             switch(i)
1495             {
1496                 // We process the 4 adjacent tiles
1497                 case 0:
1498                     neighborTile = getTile(currentEntry->getTile()->getX() - 1, currentEntry->getTile()->getY());
1499                     break;
1500                 case 1:
1501                     neighborTile = getTile(currentEntry->getTile()->getX() + 1, currentEntry->getTile()->getY());
1502                     break;
1503                 case 2:
1504                     neighborTile = getTile(currentEntry->getTile()->getX(), currentEntry->getTile()->getY() - 1);
1505                     break;
1506                 case 3:
1507                     neighborTile = getTile(currentEntry->getTile()->getX(), currentEntry->getTile()->getY() + 1);
1508                     break;
1509                 // We process the 4 diagonal tiles. We only process a diagonal tile if the 2 tiles adjacent to the original one are
1510                 // passable.
1511                 case 4:
1512                     if(areTilesPassable[0] && areTilesPassable[2])
1513                         neighborTile = getTile(currentEntry->getTile()->getX() - 1, currentEntry->getTile()->getY() - 1);
1514                     break;
1515                 case 5:
1516                     if(areTilesPassable[0] && areTilesPassable[3])
1517                         neighborTile = getTile(currentEntry->getTile()->getX() - 1, currentEntry->getTile()->getY() + 1);
1518                     break;
1519                 case 6:
1520                     if(areTilesPassable[1] && areTilesPassable[2])
1521                         neighborTile = getTile(currentEntry->getTile()->getX() + 1, currentEntry->getTile()->getY() - 1);
1522                     break;
1523                 case 7:
1524                     if(areTilesPassable[1] && areTilesPassable[3])
1525                         neighborTile = getTile(currentEntry->getTile()->getX() + 1, currentEntry->getTile()->getY() + 1);
1526                     break;
1527                 default:
1528                     break;
1529             }
1530             if(neighborTile == nullptr)
1531                 continue;
1532 
1533             neighbor.setTile(neighborTile);
1534 
1535             bool processNeighbor = false;
1536             // We process the tile if the creature can go through. But if it is the first tile that is
1537             // not passable, we also process it. That happens if a door is closed
1538             if((creature->canGoThroughTile(neighbor.getTile())) ||
1539                (neighbor.getTile() == start))
1540             {
1541                 processNeighbor = true;
1542                 // We set passability for the 4 adjacent tiles only
1543                 if(i < 4)
1544                     areTilesPassable[i] = true;
1545              }
1546             else if(throughDiggableTiles && neighbor.getTile()->isDiggable(seat))
1547                 processNeighbor = true;
1548 
1549             if (!processNeighbor)
1550                 continue;
1551 
1552             // See if the neighbor has already been processed
1553             AstarEntry* neighborEntry = processList[neighbor.getTile()->getX()][neighbor.getTile()->getY()];
1554             if ((neighborEntry != nullptr) && (neighborEntry->getHasBeenProcessed()))
1555                 continue;
1556 
1557             // If the neighbor is not in the open list
1558             if (neighborEntry == nullptr)
1559             {
1560                 double weightToParent = AstarEntry::computeHeuristic(neighbor.getTile()->getX(), neighbor.getTile()->getY(),
1561                     currentEntry->getTile()->getX(), currentEntry->getTile()->getY());
1562 
1563                 if(currentEntry->getTile()->getFullness() == 0)
1564                     weightToParent /= creature->getMoveSpeed(currentEntry->getTile());
1565                 else
1566                     weightToParent /= creature->getMoveSpeedGround();
1567                 neighbor.setG(currentEntry->getG() + weightToParent);
1568 
1569                 // Use the manhattan distance for the heuristic
1570                 neighbor.setHeuristic(neighbor.getTile()->getX(), neighbor.getTile()->getY(), x2, y2);
1571                 neighbor.setParent(currentEntry);
1572 
1573                 AstarEntry* entry = new AstarEntry(neighbor);
1574                 auto itr = openList.begin();
1575                 while((itr != openList.end()) &&
1576                       ((*itr)->fCost() > entry->fCost()))
1577                 {
1578                     ++itr;
1579                 }
1580 
1581                 openList.insert(itr, entry);
1582                 processList[neighbor.getTile()->getX()][neighbor.getTile()->getY()] = entry;
1583             }
1584             else
1585             {
1586                 // If this path to the given neighbor tile is a shorter path than the
1587                 // one already given, make this the new parent.
1588                 double weightToParent = AstarEntry::computeHeuristic(neighbor.getTile()->getX(), neighbor.getTile()->getY(),
1589                     currentEntry->getTile()->getX(), currentEntry->getTile()->getY());
1590 
1591                 if(currentEntry->getTile()->getFullness() == 0)
1592                     weightToParent /= creature->getMoveSpeed(currentEntry->getTile());
1593                 else
1594                     weightToParent /= creature->getMoveSpeedGround();
1595 
1596                 if (currentEntry->getG() + weightToParent < neighborEntry->getG())
1597                 {
1598                     neighborEntry->setG(currentEntry->getG() + weightToParent);
1599                     neighborEntry->setParent(currentEntry);
1600 
1601                     // The cost changed. We need to re-order openList
1602                     auto itr = std::find(openList.begin(), openList.end(), neighborEntry);
1603                     if(itr == openList.end())
1604                     {
1605                         OD_LOG_ERR("Unexpected entry not found tileStart=" + Tile::displayAsString(start)
1606                             + ", tileDest=" + Tile::displayAsString(destination)
1607                             + ", tile=" + Tile::displayAsString(neighborEntry->getTile()));
1608                     }
1609                     else
1610                     {
1611                         itr = openList.erase(itr);
1612                         // We look for the new location
1613                         while((itr != openList.end()) &&
1614                               ((*itr)->fCost() > neighborEntry->fCost()))
1615                         {
1616                             ++itr;
1617                         }
1618 
1619                         openList.insert(itr, neighborEntry);
1620                     }
1621                 }
1622             }
1623         }
1624     }
1625 
1626     if (destinationEntry != nullptr)
1627     {
1628         // Follow the parent chain back the the starting tile
1629         AstarEntry* curEntry = destinationEntry;
1630         do
1631         {
1632             if (curEntry->getTile() != nullptr)
1633             {
1634                 returnList.push_front(curEntry->getTile());
1635                 curEntry = curEntry->getParent();
1636             }
1637 
1638         } while (curEntry != nullptr);
1639     }
1640 
1641     // Clean up the memory we allocated by deleting the astarEntries.  Note that
1642     // processList contains all the created entries so it is enough to clean it.
1643     for (std::vector<AstarEntry*>& column : processList)
1644     {
1645         for (AstarEntry* entry : column)
1646             delete entry;
1647     }
1648 
1649     return returnList;
1650 }
1651 
addPlayer(Player * player)1652 bool GameMap::addPlayer(Player* player)
1653 {
1654     mPlayers.push_back(player);
1655     OD_LOG_INF(serverStr() + "Added player: " + player->getNick());
1656     return true;
1657 }
1658 
assignAI(Player & player,KeeperAIType aiType)1659 bool GameMap::assignAI(Player& player, KeeperAIType aiType)
1660 {
1661     std::string typeString = KeeperAITypes::toString(aiType);
1662     if (mAiManager.assignAI(player, aiType))
1663     {
1664         OD_LOG_INF("Assign AI: " + typeString + ", to player: " + player.getNick());
1665         return true;
1666     }
1667 
1668     OD_LOG_INF("Couldn't assign AI: " + typeString + ", to player: " + player.getNick());
1669     return false;
1670 }
1671 
getPlayer(const std::string & pName) const1672 Player* GameMap::getPlayer(const std::string& pName) const
1673 {
1674     for (Player* player : mPlayers)
1675     {
1676         if (player->getNick().compare(pName) == 0)
1677         {
1678             return player;
1679         }
1680     }
1681 
1682     return nullptr;
1683 }
1684 
getPlayerBySeatId(int seatId) const1685 Player* GameMap::getPlayerBySeatId(int seatId) const
1686 {
1687     for (Player* player : mPlayers)
1688     {
1689         if(player->getSeat()->getId() == seatId)
1690             return player;
1691     }
1692     return nullptr;
1693 }
1694 
getPlayerBySeat(Seat * seat) const1695 Player* GameMap::getPlayerBySeat(Seat* seat) const
1696 {
1697     for (Player* player : mPlayers)
1698     {
1699         if(player->getSeat() == seat)
1700             return player;
1701     }
1702     return nullptr;
1703 }
1704 
getVisibleForce(const std::vector<Tile * > & visibleTiles,Seat * seat,bool enemyForce)1705 std::vector<GameEntity*> GameMap::getVisibleForce(const std::vector<Tile*>& visibleTiles, Seat* seat, bool enemyForce)
1706 {
1707     std::vector<GameEntity*> returnList;
1708 
1709     // Loop over the visible tiles
1710     for (Tile* tile : visibleTiles)
1711     {
1712         if(tile == nullptr)
1713         {
1714             OD_LOG_ERR("unexpected null tile");
1715             continue;
1716         }
1717 
1718         if(enemyForce)
1719         {
1720             tile->fillWithEntities(returnList, SelectionEntityWanted::creatureAliveEnemyAttackable, seat->getPlayer());
1721             Building* building = tile->getCoveringBuilding();
1722             if((building != nullptr) &&
1723                (!building->getSeat()->isAlliedSeat(seat)) &&
1724                (building->isAttackable(tile, seat)) &&
1725                (std::find(returnList.begin(), returnList.end(), building) == returnList.end()))
1726             {
1727                 returnList.push_back(building);
1728             }
1729         }
1730         else
1731         {
1732             tile->fillWithEntities(returnList, SelectionEntityWanted::creatureAliveAllied, seat->getPlayer());
1733             Building* building = tile->getCoveringBuilding();
1734             if((building != nullptr) &&
1735                (building->getSeat()->isAlliedSeat(seat)) &&
1736                (std::find(returnList.begin(), returnList.end(), building) == returnList.end()))
1737             {
1738                 returnList.push_back(building);
1739             }
1740         }
1741     }
1742 
1743     return returnList;
1744 }
1745 
getVisibleCreatures(const std::vector<Tile * > & visibleTiles,Seat * seat,bool enemyCreatures)1746 std::vector<GameEntity*> GameMap::getVisibleCreatures(const std::vector<Tile*>& visibleTiles, Seat* seat, bool enemyCreatures)
1747 {
1748     std::vector<GameEntity*> returnList;
1749 
1750     // Loop over the visible tiles
1751     for (Tile* tile : visibleTiles)
1752     {
1753         if(tile == nullptr)
1754         {
1755             OD_LOG_ERR("unexpected null tile");
1756             continue;
1757         }
1758 
1759         if(enemyCreatures)
1760         {
1761             tile->fillWithEntities(returnList, SelectionEntityWanted::creatureAliveEnemyAttackable, seat->getPlayer());
1762         }
1763         else
1764         {
1765             tile->fillWithEntities(returnList, SelectionEntityWanted::creatureAliveAllied, seat->getPlayer());
1766         }
1767     }
1768 
1769     return returnList;
1770 }
1771 
getCarryableEntities(Creature * carrier,const std::vector<Tile * > & tiles)1772 std::vector<GameEntity*> GameMap::getCarryableEntities(Creature* carrier, const std::vector<Tile*>& tiles)
1773 {
1774     std::vector<GameEntity*> returnList;
1775 
1776     // Loop over the visible tiles
1777     for (Tile* tile : tiles)
1778     {
1779         if(tile == nullptr)
1780         {
1781             OD_LOG_ERR("unexpected null tile");
1782             continue;
1783         }
1784 
1785         tile->fillWithCarryableEntities(carrier, returnList);
1786     }
1787 
1788     return returnList;
1789 }
1790 
clearRooms()1791 void GameMap::clearRooms()
1792 {
1793     // We need to work on a copy of mRooms because removeFromGameMap will remove them from this vector
1794     std::vector<Room*> rooms = mRooms;
1795     for (Room *tempRoom : rooms)
1796     {
1797         tempRoom->removeFromGameMap();
1798         tempRoom->deleteYourself();
1799     }
1800 
1801     mRooms.clear();
1802 }
1803 
addRoom(Room * r)1804 void GameMap::addRoom(Room *r)
1805 {
1806     int nbTiles = r->numCoveredTiles();
1807     OD_LOG_INF(serverStr() + "Adding room " + r->getName() + ", nbTiles="
1808         + Helper::toString(nbTiles) + ", seatId=" + Helper::toString(r->getSeat()->getId()));
1809     for(Tile* tile : r->getCoveredTiles())
1810     {
1811         OD_LOG_INF(serverStr() + "Adding room " + r->getName() + ", tile=" + Tile::displayAsString(tile));
1812     }
1813 
1814     mRooms.push_back(r);
1815 }
1816 
removeRoom(Room * r)1817 void GameMap::removeRoom(Room *r)
1818 {
1819     OD_LOG_INF(serverStr() + "Removing room " + r->getName());
1820     // Rooms are removed when absorbed by another room or when they have no more tile
1821     // In both cases, the client have enough information to do that alone so no need to notify him
1822     std::vector<Room*>::iterator it = std::find(mRooms.begin(), mRooms.end(), r);
1823     if(it == mRooms.end())
1824     {
1825         OD_LOG_ERR("Room name=" + r->getName());
1826         return;
1827     }
1828 
1829     mRooms.erase(it);
1830 }
1831 
getRoomsByType(RoomType type) const1832 std::vector<Room*> GameMap::getRoomsByType(RoomType type) const
1833 {
1834     std::vector<Room*> returnList;
1835     for (Room* room : mRooms)
1836     {
1837         if (room->getType() == type  && room->getHP(nullptr) > 0.0)
1838             returnList.push_back(room);
1839     }
1840 
1841     return returnList;
1842 }
1843 
getRoomsByTypeAndSeat(RoomType type,const Seat * seat)1844 std::vector<Room*> GameMap::getRoomsByTypeAndSeat(RoomType type, const Seat* seat)
1845 {
1846     std::vector<Room*> returnList;
1847     for (Room* room : mRooms)
1848     {
1849         if (room->getType() == type && room->getSeat() == seat && room->getHP(nullptr) > 0.0)
1850             returnList.push_back(room);
1851     }
1852 
1853     return returnList;
1854 }
1855 
getRoomsByTypeAndSeat(RoomType type,const Seat * seat) const1856 std::vector<const Room*> GameMap::getRoomsByTypeAndSeat(RoomType type, const Seat* seat) const
1857 {
1858     std::vector<const Room*> returnList;
1859     for (const Room* room : mRooms)
1860     {
1861         if (room->getType() == type && room->getSeat() == seat && room->getHP(nullptr) > 0.0)
1862             returnList.push_back(room);
1863     }
1864 
1865     return returnList;
1866 }
1867 
numRoomsByTypeAndSeat(RoomType type,const Seat * seat) const1868 unsigned int GameMap::numRoomsByTypeAndSeat(RoomType type, const Seat* seat) const
1869 {
1870     int cptRooms = 0;
1871     for (Room* room : mRooms)
1872     {
1873         if (room->getType() == type && room->getSeat() == seat && room->getHP(nullptr) > 0.0)
1874             ++cptRooms;
1875     }
1876     return cptRooms;
1877 }
1878 
getReachableRooms(const std::vector<Room * > & vec,Tile * startTile,const Creature * creature)1879 std::vector<Room*> GameMap::getReachableRooms(const std::vector<Room*>& vec,
1880                                               Tile* startTile,
1881                                               const Creature* creature)
1882 {
1883     std::vector<Room*> returnVector;
1884 
1885     for (unsigned int i = 0; i < vec.size(); ++i)
1886     {
1887         Room* room = vec[i];
1888         Tile* coveredTile = room->getCoveredTile(0);
1889         if (pathExists(creature, startTile, coveredTile))
1890         {
1891             returnVector.push_back(room);
1892         }
1893     }
1894 
1895     return returnVector;
1896 }
1897 
getReachableBuildingsPerSeat(Seat * seat,Tile * startTile,const Creature * creature)1898 std::vector<Building*> GameMap::getReachableBuildingsPerSeat(Seat* seat,
1899        Tile *startTile, const Creature* creature)
1900 {
1901     std::vector<Building*> returnList;
1902     for (Room* room : mRooms)
1903     {
1904         if (room->getSeat() != seat)
1905             continue;
1906 
1907         if (room->getHP(nullptr) <= 0.0)
1908             continue;
1909 
1910         if(!pathExists(creature, startTile, room->getCoveredTile(0)))
1911             continue;
1912 
1913         returnList.push_back(room);
1914     }
1915 
1916     for (Trap* trap : mTraps)
1917     {
1918         if (trap->getSeat() != seat)
1919             continue;
1920 
1921         if (trap->getHP(nullptr) <= 0.0)
1922             continue;
1923 
1924         if(!pathExists(creature, startTile, trap->getCoveredTile(0)))
1925             continue;
1926 
1927         returnList.push_back(trap);
1928     }
1929 
1930     return returnList;
1931 }
1932 
getRoomByName(const std::string & name)1933 Room* GameMap::getRoomByName(const std::string& name)
1934 {
1935     for (Room* room : mRooms)
1936     {
1937         if(room->getName().compare(name) == 0)
1938             return room;
1939     }
1940 
1941     return nullptr;
1942 }
1943 
getTrapByName(const std::string & name)1944 Trap* GameMap::getTrapByName(const std::string& name)
1945 {
1946     for (Trap* trap : mTraps)
1947     {
1948         if(trap->getName().compare(name) == 0)
1949             return trap;
1950     }
1951 
1952     return nullptr;
1953 }
1954 
clearTraps()1955 void GameMap::clearTraps()
1956 {
1957     // We need to work on a copy of mTraps because removeFromGameMap will remove them from this vector
1958     std::vector<Trap*> traps = mTraps;
1959     for (Trap* trap : traps)
1960     {
1961         trap->removeFromGameMap();
1962         trap->deleteYourself();
1963     }
1964 
1965     mTraps.clear();
1966 }
1967 
addTrap(Trap * trap)1968 void GameMap::addTrap(Trap *trap)
1969 {
1970     int nbTiles = trap->numCoveredTiles();
1971     OD_LOG_INF(serverStr() + "Adding trap " + trap->getName() + ", nbTiles="
1972         + Helper::toString(nbTiles) + ", seatId=" + Helper::toString(trap->getSeat()->getId()));
1973 
1974     mTraps.push_back(trap);
1975 }
1976 
removeTrap(Trap * t)1977 void GameMap::removeTrap(Trap *t)
1978 {
1979     OD_LOG_INF(serverStr() + "Removing trap " + t->getName());
1980     std::vector<Trap*>::iterator it = std::find(mTraps.begin(), mTraps.end(), t);
1981     if(it == mTraps.end())
1982     {
1983         OD_LOG_ERR("Trap name=" + t->getName());
1984         return;
1985     }
1986 
1987     mTraps.erase(it);
1988 }
1989 
withdrawFromTreasuries(int gold,Seat * seat)1990 bool GameMap::withdrawFromTreasuries(int gold, Seat* seat)
1991 {
1992     // Check to see if there is enough gold available in all of the treasuries owned by the given seat.
1993     if (seat->getGold() < gold)
1994         return false;
1995 
1996     // Loop over the treasuries withdrawing gold until the full amount has been withdrawn.
1997     int goldStillNeeded = gold;
1998     for (Room* room : getRooms())
1999     {
2000         if(room->getSeat() != seat)
2001             continue;
2002 
2003         int goldTaken = room->withdrawGold(goldStillNeeded);
2004         goldStillNeeded -= goldTaken;
2005         if(goldStillNeeded <= 0)
2006             break;
2007     }
2008 
2009     return true;
2010 }
2011 
clearMapLights()2012 void GameMap::clearMapLights()
2013 {
2014     // We need to work on a copy of mMapLights because removeFromGameMap will remove them from this vector
2015     std::vector<MapLight*> mapLights = mMapLights;
2016     for (MapLight* mapLight : mapLights)
2017     {
2018         mapLight->removeFromGameMap();
2019         mapLight->deleteYourself();
2020     }
2021 
2022     mMapLights.clear();
2023 }
2024 
addMapLight(MapLight * m)2025 void GameMap::addMapLight(MapLight *m)
2026 {
2027     OD_LOG_INF(serverStr() + "Adding MapLight " + m->getName());
2028     mMapLights.push_back(m);
2029 }
2030 
removeMapLight(MapLight * m)2031 void GameMap::removeMapLight(MapLight *m)
2032 {
2033     OD_LOG_INF(serverStr() + "Removing MapLight " + m->getName());
2034 
2035     std::vector<MapLight*>::iterator it = std::find(mMapLights.begin(), mMapLights.end(), m);
2036     if(it == mMapLights.end())
2037     {
2038         OD_LOG_ERR("MapLight name=" + m->getName());
2039         return;
2040     }
2041 
2042     mMapLights.erase(it);
2043 }
2044 
getMapLight(const std::string & name) const2045 MapLight* GameMap::getMapLight(const std::string& name) const
2046 {
2047     for (MapLight* mapLight : mMapLights)
2048     {
2049         if (mapLight->getName() == name)
2050             return mapLight;
2051     }
2052 
2053     return nullptr;
2054 }
2055 
clearSeats()2056 void GameMap::clearSeats()
2057 {
2058     for (Seat* seat : mSeats)
2059     {
2060         delete seat;
2061     }
2062     mSeats.clear();
2063 }
2064 
addSeat(Seat * s)2065 bool GameMap::addSeat(Seat *s)
2066 {
2067     if(s == nullptr)
2068     {
2069         OD_LOG_ERR("unexpected null seat");
2070         return false;
2071     }
2072 
2073     for(Seat* seat : mSeats)
2074     {
2075         if(seat->getId() == s->getId())
2076         {
2077             OD_LOG_ERR("Duplicated seat id=" + Helper::toString(seat->getId()));
2078             return false;
2079         }
2080     }
2081     mSeats.push_back(s);
2082     // We set the Seat color value
2083     const Ogre::ColourValue& colorValue = ConfigManager::getSingleton().getColorFromId(s->getColorId());
2084     s->setColorValue(colorValue);
2085 
2086     // Add the goals for all seats to this seat.
2087     for (auto& goal : mGoalsForAllSeats)
2088     {
2089         s->addGoal(goal.get());
2090     }
2091     return true;
2092 }
2093 
getSeatById(int id) const2094 Seat* GameMap::getSeatById(int id) const
2095 {
2096     for (Seat* seat : mSeats)
2097     {
2098         if (seat->getId() == id)
2099             return seat;
2100     }
2101 
2102     return nullptr;
2103 }
2104 
addWinningSeat(Seat * s)2105 void GameMap::addWinningSeat(Seat *s)
2106 {
2107     // Make sure the seat has not already been added.
2108     if(std::find(mWinningSeats.begin(), mWinningSeats.end(), s) != mWinningSeats.end())
2109         return;
2110 
2111     Player* player = getPlayerBySeat(s);
2112     if (player && player->getIsHuman())
2113     {
2114         ServerNotification* serverNotification = new ServerNotification(
2115             ServerNotificationType::chatServer, player);
2116         serverNotification->mPacket << "You Won" << EventShortNoticeType::majorGameEvent;
2117         ODServer::getSingleton().queueServerNotification(serverNotification);
2118     }
2119 
2120     std::vector<Seat*> seats;
2121     seats.push_back(s);
2122     fireRelativeSound(seats, SoundRelativeKeeperStatements::Victory);
2123 
2124     mWinningSeats.push_back(s);
2125 }
2126 
seatIsAWinner(Seat * s) const2127 bool GameMap::seatIsAWinner(Seat *s) const
2128 {
2129     if(std::find(mWinningSeats.begin(), mWinningSeats.end(), s) != mWinningSeats.end())
2130         return true;
2131 
2132     return false;
2133 }
2134 
addGoalForAllSeats(std::unique_ptr<Goal> && g)2135 void GameMap::addGoalForAllSeats(std::unique_ptr<Goal>&& g)
2136 {
2137     // Add the goal to each of the empty seats currently in the game.
2138     for (Seat* seat : mSeats)
2139         seat->addGoal(g.get());
2140 
2141     mGoalsForAllSeats.emplace_back(std::move(g));
2142 }
2143 
clearGoalsForAllSeats()2144 void GameMap::clearGoalsForAllSeats()
2145 {
2146     for (Seat* seat : mSeats)
2147     {
2148         seat->clearUncompleteGoals();
2149         seat->clearCompletedGoals();
2150     }
2151 
2152     mGoalsForAllSeats.clear();
2153 }
2154 
doFloodFill(Seat * seat,Tile * tile)2155 bool GameMap::doFloodFill(Seat* seat, Tile* tile)
2156 {
2157     if (!mFloodFillEnabled)
2158         return false;
2159 
2160     if(tile->isFloodFillFilled(seat))
2161         return false;
2162 
2163     bool hasChanged = false;
2164     // If a neigboor is colored with the same colors, we color the tile
2165     for(Tile* neigh : tile->getAllNeighbors())
2166     {
2167         // TODO: check if this can be optimized with Tile::isFloodFillPossible
2168         switch(tile->getType())
2169         {
2170             case TileType::dirt:
2171             case TileType::gold:
2172             case TileType::rock:
2173             {
2174                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::ground, neigh);
2175                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::groundWater, neigh);
2176                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::groundLava, neigh);
2177                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::groundWaterLava, neigh);
2178                 break;
2179             }
2180             case TileType::water:
2181             {
2182                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::groundWater, neigh);
2183                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::groundWaterLava, neigh);
2184                 break;
2185             }
2186             case TileType::lava:
2187             {
2188                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::groundLava, neigh);
2189                 hasChanged |= tile->updateFloodFillFromTile(seat, FloodFillType::groundWaterLava, neigh);
2190                 break;
2191             }
2192             default:
2193                 continue;
2194         }
2195 
2196         // If the tile is fully filled, no need to continue
2197         if(tile->isFloodFillFilled(seat))
2198             return true;
2199     }
2200 
2201     return hasChanged;
2202 }
2203 
replaceFloodFill(Seat * seat,FloodFillType floodFillType,uint32_t colorOld,uint32_t colorNew)2204 void GameMap::replaceFloodFill(Seat* seat, FloodFillType floodFillType, uint32_t colorOld, uint32_t colorNew)
2205 {
2206     for (int jj = 0; jj < getMapSizeY(); ++jj)
2207     {
2208         for (int ii = 0; ii < getMapSizeX(); ++ii)
2209         {
2210             Tile* tile = getTile(ii,jj);
2211             if(tile->getFloodFillValue(seat, floodFillType) != colorOld)
2212                 continue;
2213 
2214             tile->replaceFloodFill(seat, floodFillType, colorNew);
2215         }
2216     }
2217 }
2218 
refreshFloodFill(Seat * seat,Tile * tile)2219 void GameMap::refreshFloodFill(Seat* seat, Tile* tile)
2220 {
2221     std::vector<uint32_t> colors(static_cast<uint32_t>(FloodFillType::nbValues), Tile::NO_FLOODFILL);
2222 
2223     // If the tile has opened a new place, we use the same floodfillcolor for all the areas
2224     for(uint32_t i = 0; i < colors.size(); ++i)
2225     {
2226         FloodFillType type = static_cast<FloodFillType>(i);
2227         if(!tile->isFloodFillPossible(seat, type))
2228             continue;
2229 
2230         for(Tile* neigh : tile->getAllNeighbors())
2231         {
2232             uint32_t neighColor = neigh->getFloodFillValue(seat, type);
2233             if(neighColor == Tile::NO_FLOODFILL)
2234                 continue;
2235 
2236             colors[i] = neighColor;
2237             break;
2238         }
2239     }
2240 
2241     // Now, we fill floodfill if no color was found on any neighboor tile
2242     // That might happen if a tile surrounded by water is dug
2243     for(uint32_t i = 0; i < colors.size(); ++i)
2244     {
2245         FloodFillType type = static_cast<FloodFillType>(i);
2246         if(colors[i] != Tile::NO_FLOODFILL)
2247             continue;
2248 
2249         if(!tile->isFloodFillPossible(seat, type))
2250             continue;
2251 
2252         colors[i] = nextUniqueFloodFillValue();
2253     }
2254 
2255     // Now, we update the tile and its neighboors
2256     for(uint32_t i = 0; i < colors.size(); ++i)
2257     {
2258         const uint32_t& color = colors[i];
2259         FloodFillType type = static_cast<FloodFillType>(i);
2260         if(color == Tile::NO_FLOODFILL)
2261             continue;
2262 
2263         tile->replaceFloodFill(seat, type, color);
2264         for(Tile* neigh : tile->getAllNeighbors())
2265         {
2266             uint32_t neighColor = neigh->getFloodFillValue(seat, type);
2267             if(neighColor == Tile::NO_FLOODFILL)
2268                 continue;
2269             if(neighColor == color)
2270                 continue;
2271 
2272             replaceFloodFill(seat, type, neighColor, color);
2273         }
2274     }
2275 }
2276 
enableFloodFill()2277 void GameMap::enableFloodFill()
2278 {
2279     // Carry out a flood fill of the whole level to make sure everything is good.
2280     // Start by setting the flood fill color for every tile on the map to -1.
2281     for (int jj = 0; jj < getMapSizeY(); ++jj)
2282     {
2283         for (int ii = 0; ii < getMapSizeX(); ++ii)
2284         {
2285             getTile(ii,jj)->resetFloodFill();
2286         }
2287     }
2288 
2289     // The algorithm used to find a path is efficient when the path exists but not if it doesn't.
2290     // To improve path finding, we tag the contiguous tiles to know if a path exists between 2 tiles or not.
2291     // Because creatures can go through ground, water or lava, we process all of theses.
2292     // Note : when a tile is digged, floodfill will have to be refreshed.
2293     mFloodFillEnabled = true;
2294 
2295     // To optimize floodfilling, we start by tagging the dirt tiles with fullness = 0
2296     // because they are walkable for most creatures. When we will have tagged all
2297     // thoses, we will deal with water/lava remaining (there can be some left if
2298     // surrounded by not passable tiles).
2299     FloodFillType currentType = FloodFillType::ground;
2300     // We do the floodfill for the rogue seat. Then, once it is done, we copy for the other seats.
2301     // If there are locked doors, floodfill will be refreshed when they are added
2302     Seat* rogueSeat = getSeatRogue();
2303     while(true)
2304     {
2305         int yy = 0;
2306 
2307         bool isTileFound;
2308         isTileFound = false;
2309         while(!isTileFound && (yy < getMapSizeY()))
2310         {
2311             for(int xx = 0; xx < getMapSizeX(); ++xx)
2312             {
2313                 Tile* tile = getTile(xx, yy);
2314                 if(tile->getFullness() > 0.0)
2315                     continue;
2316 
2317                 if(currentType == FloodFillType::ground)
2318                 {
2319                     if(((tile->getType() == TileType::dirt) ||
2320                         (tile->getType() == TileType::gold) ||
2321                         (tile->getType() == TileType::rock)) &&
2322                        (tile->getFloodFillValue(rogueSeat, FloodFillType::ground) == Tile::NO_FLOODFILL))
2323                     {
2324                         isTileFound = true;
2325                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::ground) == Tile::NO_FLOODFILL)
2326                             tile->replaceFloodFill(rogueSeat, FloodFillType::ground, nextUniqueFloodFillValue());
2327                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::groundWater) == Tile::NO_FLOODFILL)
2328                             tile->replaceFloodFill(rogueSeat, FloodFillType::groundWater, nextUniqueFloodFillValue());
2329                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::groundLava) == Tile::NO_FLOODFILL)
2330                             tile->replaceFloodFill(rogueSeat, FloodFillType::groundLava, nextUniqueFloodFillValue());
2331                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::groundWaterLava) == Tile::NO_FLOODFILL)
2332                             tile->replaceFloodFill(rogueSeat, FloodFillType::groundWaterLava, nextUniqueFloodFillValue());
2333                         break;
2334                     }
2335                 }
2336                 else if(currentType == FloodFillType::groundWater)
2337                 {
2338                     if((tile->getType() == TileType::water) &&
2339                        (tile->getFloodFillValue(rogueSeat, FloodFillType::groundWater) == Tile::NO_FLOODFILL))
2340                     {
2341                         isTileFound = true;
2342                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::groundWater) == Tile::NO_FLOODFILL)
2343                             tile->replaceFloodFill(rogueSeat, FloodFillType::groundWater, nextUniqueFloodFillValue());
2344                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::groundWaterLava) == Tile::NO_FLOODFILL)
2345                             tile->replaceFloodFill(rogueSeat, FloodFillType::groundWaterLava, nextUniqueFloodFillValue());
2346                         break;
2347                     }
2348                 }
2349                 else if(currentType == FloodFillType::groundLava)
2350                 {
2351                     if((tile->getType() == TileType::lava) &&
2352                        (tile->getFloodFillValue(rogueSeat, FloodFillType::groundLava) == Tile::NO_FLOODFILL))
2353                     {
2354                         isTileFound = true;
2355                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::groundLava) == Tile::NO_FLOODFILL)
2356                             tile->replaceFloodFill(rogueSeat, FloodFillType::groundLava, nextUniqueFloodFillValue());
2357                         if(tile->getFloodFillValue(rogueSeat, FloodFillType::groundWaterLava) == Tile::NO_FLOODFILL)
2358                             tile->replaceFloodFill(rogueSeat, FloodFillType::groundWaterLava, nextUniqueFloodFillValue());
2359                         break;
2360                     }
2361                 }
2362             }
2363 
2364             if(!isTileFound)
2365                 ++yy;
2366         }
2367 
2368         // If there are no more walkable tiles, we go for water. Then, for lava. After that, floodfill should be complete
2369         if(!isTileFound)
2370         {
2371             switch(currentType)
2372             {
2373                 case FloodFillType::ground:
2374                 {
2375                     // There are no more ground tiles. We go for water tiles
2376                     currentType = FloodFillType::groundWater;
2377                     isTileFound = true;
2378                     break;
2379                 }
2380                 case FloodFillType::groundWater:
2381                 {
2382                     // There are no more ground tiles. We go for water tiles
2383                     currentType = FloodFillType::groundLava;
2384                     isTileFound = true;
2385                     break;
2386                 }
2387                 case FloodFillType::groundLava:
2388                 {
2389                     // There are no more tiles. We can stop
2390                     break;
2391                 }
2392                 default:
2393                     OD_LOG_ERR("Unexpected enum value=" + Tile::toString(currentType));
2394                     break;
2395             }
2396         }
2397 
2398         if(!isTileFound)
2399             break;
2400 
2401         while(yy < getMapSizeY())
2402         {
2403             int nbTiles = 0;
2404             for(int xx = 0; xx < getMapSizeX(); ++xx)
2405             {
2406                 Tile* tile = getTile(xx, yy);
2407                 if(doFloodFill(rogueSeat, tile))
2408                     ++nbTiles;
2409             }
2410 
2411             // For optimization purposes, if a tile has changed, we go on the other side
2412             if(nbTiles > 0)
2413             {
2414                 for(int xx = getMapSizeX() - 1; xx >= 0; --xx)
2415                 {
2416                     Tile* tile = getTile(xx, yy);
2417                     if(doFloodFill(rogueSeat, tile))
2418                         ++nbTiles;
2419                 }
2420             }
2421 
2422             // If at least one tile as been changed, we go back to the previous line
2423             if((nbTiles > 0) && (yy > 0))
2424                 --yy;
2425             else
2426                 ++yy;
2427         }
2428     }
2429 
2430     // We copy floodfill for all seats
2431     for(int xx = 0; xx < getMapSizeX(); ++xx)
2432     {
2433         for(int yy = 0; yy < getMapSizeY(); ++yy)
2434         {
2435             Tile* tile = getTile(xx, yy);
2436             if(tile == nullptr)
2437                 continue;
2438 
2439             tile->copyFloodFillToOtherSeats(rogueSeat);
2440         }
2441     }
2442 }
2443 
path(Creature * c1,Creature * c2,const Creature * creature,Seat * seat,bool throughDiggableTiles)2444 std::list<Tile*> GameMap::path(Creature *c1, Creature *c2, const Creature* creature, Seat* seat, bool throughDiggableTiles)
2445 {
2446     return path(c1->getPositionTile()->getX(), c1->getPositionTile()->getY(),
2447                 c2->getPositionTile()->getX(), c2->getPositionTile()->getY(), creature, seat, throughDiggableTiles);
2448 }
2449 
path(Tile * t1,Tile * t2,const Creature * creature,Seat * seat,bool throughDiggableTiles)2450 std::list<Tile*> GameMap::path(Tile *t1, Tile *t2, const Creature* creature, Seat* seat, bool throughDiggableTiles)
2451 {
2452     return path(t1->getX(), t1->getY(), t2->getX(), t2->getY(), creature, seat, throughDiggableTiles);
2453 }
2454 
path(const Creature * creature,Tile * destination,bool throughDiggableTiles)2455 std::list<Tile*> GameMap::path(const Creature* creature, Tile* destination, bool throughDiggableTiles)
2456 {
2457     if (destination == nullptr)
2458         return std::list<Tile*>();
2459 
2460     Tile* positionTile = creature->getPositionTile();
2461     if (positionTile == nullptr)
2462         return std::list<Tile*>();
2463 
2464     return path(positionTile->getX(), positionTile->getY(),
2465                 destination->getX(), destination->getY(),
2466                 creature, creature->getSeat(), throughDiggableTiles);
2467 }
2468 
processDeletionQueues()2469 void GameMap::processDeletionQueues()
2470 {
2471     for(GameEntity* entity : mEntitiesToDelete)
2472         delete entity;
2473 
2474     mEntitiesToDelete.clear();
2475 }
2476 
refreshBorderingTilesOf(const std::vector<Tile * > & affectedTiles)2477 void GameMap::refreshBorderingTilesOf(const std::vector<Tile*>& affectedTiles)
2478 {
2479     // Add the tiles which border the affected region to the affectedTiles vector since they may need to have their meshes changed.
2480     std::vector<Tile*> borderTiles = tilesBorderedByRegion(affectedTiles);
2481 
2482     borderTiles.insert(borderTiles.end(), affectedTiles.begin(), affectedTiles.end());
2483 
2484     // Loop over all the affected tiles and force them to examine their neighbors.  This allows
2485     // them to switch to a mesh with fewer polygons if some are hidden by the neighbors, etc.
2486     for (Tile* tile : borderTiles)
2487         tile->refreshMesh();
2488 }
2489 
getBuildableTilesForPlayerInArea(int x1,int y1,int x2,int y2,Player * player)2490 std::vector<Tile*> GameMap::getBuildableTilesForPlayerInArea(int x1, int y1, int x2, int y2,
2491     Player* player)
2492 {
2493     std::vector<Tile*> tiles = rectangularRegion(x1, y1, x2, y2);
2494     for (std::vector<Tile*>::iterator it = tiles.begin(); it != tiles.end();)
2495     {
2496         Tile* tile = *it;
2497         if (!tile->isBuildableUpon(player->getSeat()))
2498         {
2499             it = tiles.erase(it);
2500             continue;
2501         }
2502 
2503         ++it;
2504     }
2505     return tiles;
2506 }
2507 
getGoalsStringForPlayer(Player * player)2508 std::string GameMap::getGoalsStringForPlayer(Player* player)
2509 {
2510     bool playerIsAWinner = seatIsAWinner(player->getSeat());
2511     std::stringstream tempSS("");
2512     Seat* seat = player->getSeat();
2513     seat->resetGoalsChanged();
2514 
2515     const std::string formatTitleOn = "[font='MedievalSharp-12'][colour='CCBBBBFF']";
2516     const std::string formatTitleOff = "[font='MedievalSharp-10'][colour='FFFFFFFF']";
2517 
2518     if (playerIsAWinner)
2519     {
2520         tempSS << "Congratulations, you have completed this level.";
2521     }
2522     else if (seat->numFailedGoals() > 0)
2523     {
2524         // Loop over the list of completed goals for the seat we are sitting in an print them.
2525         tempSS << formatTitleOn << "Failed Goals:\n" << formatTitleOff
2526                << "(You cannot complete this level!)\n\n";
2527         for (unsigned int i = 0; i < seat->numFailedGoals(); ++i)
2528         {
2529             Goal *tempGoal = seat->getFailedGoal(i);
2530             tempSS << tempGoal->getFailedMessage(*seat) << "\n";
2531         }
2532     }
2533 
2534     if (seat->numUncompleteGoals() > 0)
2535     {
2536         // Loop over the list of unmet goals for the seat we are sitting in an print them.
2537         tempSS << formatTitleOn << "Unfinished Goals:" << formatTitleOff << "\n\n";
2538         for (unsigned int i = 0; i < seat->numUncompleteGoals(); ++i)
2539         {
2540             Goal *tempGoal = seat->getUncompleteGoal(i);
2541             tempSS << tempGoal->getDescription(*seat) << "\n";
2542         }
2543     }
2544 
2545     if (seat->numCompletedGoals() > 0)
2546     {
2547         // Loop over the list of completed goals for the seat we are sitting in an print them.
2548         tempSS << "\n" << formatTitleOn << "Completed Goals:" << formatTitleOff << "\n\n";
2549         for (unsigned int i = 0; i < seat->numCompletedGoals(); ++i)
2550         {
2551             Goal *tempGoal = seat->getCompletedGoal(i);
2552             tempSS << tempGoal->getSuccessMessage(*seat) << "\n";
2553         }
2554     }
2555 
2556     return tempSS.str();
2557 }
2558 
addGoldToSeat(int gold,int seatId)2559 int GameMap::addGoldToSeat(int gold, int seatId)
2560 {
2561     Seat* seat = getSeatById(seatId);
2562     if(seat == nullptr)
2563         return gold;
2564 
2565     for (Room* room : getRooms())
2566     {
2567         if(room->getSeat() != seat)
2568             continue;
2569 
2570         if(room->numCoveredTiles() == 0)
2571             continue;
2572 
2573         Tile* tile = room->getCoveredTile(0);
2574         gold -= room->depositGold(gold, tile);
2575         if(gold <= 0)
2576             break;
2577     }
2578 
2579     return gold;
2580 }
2581 
addManaToSeat(int mana,int seatId)2582 int GameMap::addManaToSeat(int mana, int seatId)
2583 {
2584     Seat* seat = getSeatById(seatId);
2585     if(seat == nullptr)
2586         return mana;
2587 
2588     seat->mMana += mana;
2589     double maxMana = ConfigManager::getSingleton().getMaxManaPerSeat();
2590     if (seat->mMana > maxMana)
2591         seat->mMana = maxMana;
2592 
2593     return mana;
2594 }
2595 
nextSeatId(int SeatId)2596 int GameMap::nextSeatId(int SeatId)
2597 {
2598     int firstSeatId = -1;
2599     bool useNext = false;
2600     for(Seat* seat : mSeats)
2601     {
2602         if(useNext)
2603             return seat->getId();
2604 
2605         if(firstSeatId == -1)
2606             firstSeatId = seat->getId();
2607 
2608         if(seat->getId() == SeatId)
2609             useNext = true;
2610     }
2611 
2612     // If we reach here, that means that we have to last seat id. We return the first one we could find
2613     return firstSeatId;
2614 }
2615 
nextUniqueNameCreature(const std::string & className)2616 std::string GameMap::nextUniqueNameCreature(const std::string& className)
2617 {
2618     std::string ret;
2619     do
2620     {
2621         ++mUniqueNumberCreature;
2622         ret = className + Helper::toString(mUniqueNumberCreature);
2623     } while(getCreature(ret) != nullptr);
2624     return ret;
2625 }
2626 
nextUniqueNameRoom(RoomType type)2627 std::string GameMap::nextUniqueNameRoom(RoomType type)
2628 {
2629     std::string ret;
2630     do
2631     {
2632         ++mUniqueNumberRoom;
2633         ret = RoomManager::getRoomNameFromRoomType(type) + "_" + Helper::toString(mUniqueNumberRoom);
2634     } while(getRoomByName(ret) != nullptr);
2635     return ret;
2636 }
2637 
nextUniqueNameRenderedMovableEntity(const std::string & baseName)2638 std::string GameMap::nextUniqueNameRenderedMovableEntity(const std::string& baseName)
2639 {
2640     std::string ret;
2641     do
2642     {
2643         ++mUniqueNumberRenderedMovableEntity;
2644         ret = RenderedMovableEntity::RENDEREDMOVABLEENTITY_PREFIX + baseName + "_" + Helper::toString(mUniqueNumberRenderedMovableEntity);
2645     } while(getRenderedMovableEntity(ret) != nullptr);
2646     return ret;
2647 }
2648 
nextUniqueNameTrap(TrapType type)2649 std::string GameMap::nextUniqueNameTrap(TrapType type)
2650 {
2651     std::string ret;
2652     do
2653     {
2654         ++mUniqueNumberTrap;
2655         ret = TrapManager::getTrapNameFromTrapType(type) + "_" + Helper::toString(mUniqueNumberTrap);
2656     } while(getTrapByName(ret) != nullptr);
2657     return ret;
2658 }
2659 
nextUniqueNameMapLight()2660 std::string GameMap::nextUniqueNameMapLight()
2661 {
2662     std::string ret;
2663     do
2664     {
2665         ++mUniqueNumberMapLight;
2666         ret = MapLight::MAPLIGHT_NAME_PREFIX + Helper::toString(mUniqueNumberMapLight);
2667     } while(getMapLight(ret) != nullptr);
2668     return ret;
2669 }
2670 
getEntityFromTypeAndName(GameEntityType entityType,const std::string & entityName)2671 GameEntity* GameMap::getEntityFromTypeAndName(GameEntityType entityType,
2672     const std::string& entityName)
2673 {
2674     switch(entityType)
2675     {
2676         case GameEntityType::creature:
2677             return getCreature(entityName);
2678 
2679         case GameEntityType::buildingObject:
2680         case GameEntityType::chickenEntity:
2681         case GameEntityType::craftedTrap:
2682         case GameEntityType::missileObject:
2683         case GameEntityType::persistentObject:
2684         case GameEntityType::smallSpiderEntity:
2685         case GameEntityType::trapEntity:
2686         case GameEntityType::treasuryObject:
2687         case GameEntityType::skillEntity:
2688         case GameEntityType::giftBoxEntity:
2689             return getRenderedMovableEntity(entityName);
2690 
2691         case GameEntityType::spell:
2692             return getSpell(entityName);
2693 
2694         case GameEntityType::mapLight:
2695             return getMapLight(entityName);
2696 
2697         case GameEntityType::room:
2698             return getRoomByName(entityName);
2699 
2700         case GameEntityType::trap:
2701             return getTrapByName(entityName);
2702 
2703         default:
2704             break;
2705     }
2706 
2707     return nullptr;
2708 }
2709 
logFloodFileTiles()2710 void GameMap::logFloodFileTiles()
2711 {
2712     for(int yy = 0; yy < getMapSizeY(); ++yy)
2713     {
2714         for(int xx = 0; xx < getMapSizeX(); ++xx)
2715         {
2716             Tile* tile = getTile(xx, yy);
2717             tile->logFloodFill();
2718         }
2719     }
2720 }
2721 
consoleSetCreatureDestination(const std::string & creatureName,int x,int y)2722 void GameMap::consoleSetCreatureDestination(const std::string& creatureName, int x, int y)
2723 {
2724     Creature* creature = getCreature(creatureName);
2725     if(creature == nullptr)
2726         return;
2727     Tile* tile = getTile(x, y);
2728     if(tile == nullptr)
2729         return;
2730     if(creature->getPositionTile() == nullptr)
2731         return;
2732     creature->clearActionQueue();
2733     creature->setDestination(tile);
2734 }
2735 
consoleToggleCreatureVisualDebug(const std::string & creatureName)2736 void GameMap::consoleToggleCreatureVisualDebug(const std::string& creatureName)
2737 {
2738     Creature* creature = getCreature(creatureName);
2739     if(creature == nullptr)
2740         return;
2741 
2742     bool enable = !creature->getHasVisualDebuggingEntities();
2743     if(enable)
2744         creature->computeVisualDebugEntities();
2745     else
2746         creature->stopComputeVisualDebugEntities();
2747 }
2748 
consoleToggleSeatVisualDebug(int seatId)2749 void GameMap::consoleToggleSeatVisualDebug(int seatId)
2750 {
2751     Seat* seat = getSeatById(seatId);
2752     if(seat == nullptr)
2753         return;
2754 
2755     seat->toggleSeatVisualDebug();
2756     seat->refreshSeatVisualDebug();
2757 }
2758 
consoleSetLevelCreature(const std::string & creatureName,uint32_t level)2759 void GameMap::consoleSetLevelCreature(const std::string& creatureName, uint32_t level)
2760 {
2761     Creature* creature = getCreature(creatureName);
2762     if(creature == nullptr)
2763         return;
2764 
2765     creature->setLevel(level);
2766 }
2767 
consoleAskToggleFOW()2768 void GameMap::consoleAskToggleFOW()
2769 {
2770     mIsFOWActivated = !mIsFOWActivated;
2771 }
2772 
consoleAskUnlockSkills()2773 void GameMap::consoleAskUnlockSkills()
2774 {
2775     for(Seat* seat : getSeats())
2776     {
2777         for(uint32_t i = 0; i < static_cast<uint32_t>(SkillType::countSkill); ++i)
2778         {
2779             SkillType skill = static_cast<SkillType>(i);
2780             if(skill == SkillType::nullSkillType)
2781                 continue;
2782 
2783             if(seat->isSkillDone(skill))
2784                 continue;
2785 
2786             seat->addSkill(skill);
2787         }
2788     }
2789 }
2790 
getWorkerForPathFinding(Seat * seat)2791 Creature* GameMap::getWorkerForPathFinding(Seat* seat)
2792 {
2793     for (Creature* creature : mCreatures)
2794     {
2795         if(creature->getSeat() != seat)
2796             continue;
2797 
2798         if (creature->getDefinition()->isWorker())
2799             return creature;
2800     }
2801     return nullptr;
2802 }
2803 
updateVisibleEntities()2804 void GameMap::updateVisibleEntities()
2805 {
2806     // Notify what happened to entities on visible tiles
2807     for (int jj = 0; jj < getMapSizeY(); ++jj)
2808     {
2809         for (int ii = 0; ii < getMapSizeX(); ++ii)
2810         {
2811             Tile* tile = getTile(ii,jj);
2812             tile->notifyEntitiesSeatsWithVision();
2813         }
2814     }
2815 }
2816 
fireRefreshEntities()2817 void GameMap::fireRefreshEntities()
2818 {
2819     // Notify changes on visible tiles
2820     for(Seat* seat : mSeats)
2821         seat->notifyChangedVisibleTiles();
2822 
2823     for(Creature* creature : mCreatures)
2824     {
2825         creature->fireCreatureRefreshIfNeeded();
2826     }
2827 }
2828 
addSpell(Spell * spell)2829 void GameMap::addSpell(Spell *spell)
2830 {
2831     OD_LOG_INF(serverStr() + "Adding spell " + spell->getName()
2832         + ",MeshName=" + spell->getMeshName());
2833     mSpells.push_back(spell);
2834 }
2835 
removeSpell(Spell * spell)2836 void GameMap::removeSpell(Spell *spell)
2837 {
2838     OD_LOG_INF(serverStr() + "Removing spell " + spell->getName()
2839         + ",MeshName=" + spell->getMeshName());
2840     std::vector<Spell*>::iterator it = std::find(mSpells.begin(), mSpells.end(), spell);
2841     if(it == mSpells.end())
2842     {
2843         OD_LOG_ERR("spell name=" + spell->getName());
2844         return;
2845     }
2846 
2847     mSpells.erase(it);
2848 }
2849 
getSpell(const std::string & name) const2850 Spell* GameMap::getSpell(const std::string& name) const
2851 {
2852     for(Spell* spell : mSpells)
2853     {
2854         if(name.compare(spell->getName()) == 0)
2855             return spell;
2856     }
2857     return nullptr;
2858 }
2859 
clearSpells()2860 void GameMap::clearSpells()
2861 {
2862     // We need to work on a copy of mSpells because removeFromGameMap will remove them from this vector
2863     std::vector<Spell*> spells = mSpells;
2864     for (Spell* spell : spells)
2865     {
2866         spell->removeFromGameMap();
2867         spell->deleteYourself();
2868     }
2869 
2870     mSpells.clear();
2871 }
2872 
getSpellsBySeatAndType(Seat * seat,SpellType type) const2873 std::vector<Spell*> GameMap::getSpellsBySeatAndType(Seat* seat, SpellType type) const
2874 {
2875     std::vector<Spell*> ret;
2876     for (Spell* spell : mSpells)
2877     {
2878         if(spell->getSeat() != seat)
2879             continue;
2880 
2881         if(spell->getSpellType() != type)
2882             continue;
2883 
2884         ret.push_back(spell);
2885     }
2886 
2887     return ret;
2888 }
2889 
getMeshForDefaultTile() const2890 const std::string& GameMap::getMeshForDefaultTile() const
2891 {
2892     // 0 means tile not linked to any neighboor
2893     return mTileSet->getTileValues(TileVisual::dirtFull).at(0).getMeshName();
2894 }
2895 
getMeshForTile(const Tile * tile) const2896 const TileSetValue& GameMap::getMeshForTile(const Tile* tile) const
2897 {
2898     int index = 0;
2899     for(int i = 0; i < 4; ++i)
2900     {
2901         int diffX;
2902         int diffY;
2903         switch(i)
2904         {
2905             case 0:
2906                 diffX = 0;
2907                 diffY = -1;
2908                 break;
2909             case 1:
2910                 diffX = 1;
2911                 diffY = 0;
2912                 break;
2913             case 2:
2914                 diffX = 0;
2915                 diffY = 1;
2916                 break;
2917             case 3:
2918             default:
2919                 diffX = -1;
2920                 diffY = 0;
2921                 break;
2922         }
2923         const Tile* t = getTile(tile->getX() + diffX, tile->getY() + diffY);
2924         if(t == nullptr)
2925             continue;
2926 
2927         if(mTileSet->areLinked(tile, t))
2928             index |= (1 << i);
2929     }
2930 
2931     return mTileSet->getTileValues(tile->getTileVisual()).at(index);
2932 }
2933 
getMaxNumberCreatures(Seat * seat) const2934 uint32_t GameMap::getMaxNumberCreatures(Seat* seat) const
2935 {
2936     uint32_t nbCreatures = ConfigManager::getSingleton().getMaxCreaturesPerSeatDefault();
2937 
2938     std::vector<const Room*> portals = getRoomsByTypeAndSeat(RoomType::portal, seat);
2939     for(const Room* room : portals)
2940     {
2941         const RoomPortal* roomPortal = static_cast<const RoomPortal*>(room);
2942         nbCreatures += roomPortal->getNbCreatureMaxIncrease();
2943     }
2944 
2945     return std::min(nbCreatures, ConfigManager::getSingleton().getMaxCreaturesPerSeatAbsolute());
2946 }
2947 
playerSelects(std::vector<GameEntity * > & entities,int tileX1,int tileY1,int tileX2,int tileY2,SelectionTileAllowed tileAllowed,SelectionEntityWanted entityWanted,Player * player)2948 void GameMap::playerSelects(std::vector<GameEntity*>& entities, int tileX1, int tileY1, int tileX2,
2949     int tileY2, SelectionTileAllowed tileAllowed, SelectionEntityWanted entityWanted, Player* player)
2950 {
2951     std::vector<Tile*> tiles = rectangularRegion(tileX1, tileY1, tileX2, tileY2);
2952     for(Tile* tile : tiles)
2953     {
2954         switch(tileAllowed)
2955         {
2956             case SelectionTileAllowed::groundClaimedOwned:
2957             {
2958                 if(tile->isFullTile())
2959                     continue;
2960 
2961                 if(tile->getSeat() == nullptr)
2962                     continue;
2963 
2964                 if(!tile->isClaimed())
2965                     continue;
2966 
2967                 if(player->getSeat() != tile->getSeat())
2968                     continue;
2969 
2970                 break;
2971             }
2972             case SelectionTileAllowed::groundClaimedAllied:
2973             {
2974                 if(tile->isFullTile())
2975                     continue;
2976 
2977                 if(tile->getSeat() == nullptr)
2978                     continue;
2979 
2980                 if(!tile->isClaimed())
2981                     continue;
2982 
2983                 if(!player->getSeat()->isAlliedSeat(tile->getSeat()))
2984                     continue;
2985 
2986                 break;
2987             }
2988             case SelectionTileAllowed::groundClaimedNotEnemy:
2989             {
2990                 if(tile->isFullTile())
2991                     continue;
2992 
2993                 if(tile->getSeat() == nullptr)
2994                     continue;
2995 
2996                 if(tile->isClaimed() && !player->getSeat()->isAlliedSeat(tile->getSeat()))
2997                     continue;
2998 
2999                 break;
3000             }
3001             case SelectionTileAllowed::groundTiles:
3002             {
3003                 if(tile->isFullTile())
3004                     continue;
3005 
3006                 break;
3007             }
3008             default:
3009             {
3010                 static bool logMsg = false;
3011                 if(!logMsg)
3012                 {
3013                     logMsg = true;
3014                     OD_LOG_ERR("Wrong SelectionTileAllowed int=" + Helper::toString(static_cast<uint32_t>(tileAllowed)));
3015                 }
3016                 continue;
3017             }
3018         }
3019 
3020         if(entityWanted == SelectionEntityWanted::tiles)
3021         {
3022             entities.push_back(tile);
3023             continue;
3024         }
3025 
3026         tile->fillWithEntities(entities, entityWanted, player);
3027     }
3028 }
3029 
addClientUpkeepEntity(GameEntity * entity)3030 void GameMap::addClientUpkeepEntity(GameEntity* entity)
3031 {
3032     // GameEntityClientUpkeep objects are only used on client side
3033     if(isServerGameMap())
3034         return;
3035 
3036     mGameEntityClientUpkeep.push_back(entity);
3037 }
3038 
removeClientUpkeepEntity(GameEntity * entity)3039 void GameMap::removeClientUpkeepEntity(GameEntity* entity)
3040 {
3041     auto it = std::find(mGameEntityClientUpkeep.begin(), mGameEntityClientUpkeep.end(), entity);
3042     if(it == mGameEntityClientUpkeep.end())
3043         return;
3044 
3045     mGameEntityClientUpkeep.erase(it);
3046 }
3047 
clientUpKeep(int64_t turnNumber)3048 void GameMap::clientUpKeep(int64_t turnNumber)
3049 {
3050     mTurnNumber = turnNumber;
3051     mLocalPlayer->decreaseSpellCooldowns();
3052 
3053     for(GameEntity* entity : mGameEntityClientUpkeep)
3054     {
3055         entity->clientUpkeep();
3056     }
3057 }
3058 
doorLock(Tile * tileDoor,Seat * seat,bool locked)3059 void GameMap::doorLock(Tile* tileDoor, Seat* seat, bool locked)
3060 {
3061     if(!locked)
3062     {
3063         // When a door is unlocked, we check all its neighboors to find a floodfill value for each possible
3064         // tile type and set the same for all neighboor tiles
3065         std::vector<uint32_t> colors(static_cast<uint32_t>(FloodFillType::nbValues), Tile::NO_FLOODFILL);
3066 
3067         for(uint32_t i = 0; i < colors.size(); ++i)
3068         {
3069             FloodFillType type = static_cast<FloodFillType>(i);
3070             colors[i] = tileDoor->getFloodFillValue(seat, type);
3071         }
3072 
3073         // Now, we check all neighboors and replace floodfill values if different
3074         for(uint32_t i = 0; i < colors.size(); ++i)
3075         {
3076             if(colors[i] == Tile::NO_FLOODFILL)
3077                 continue;
3078 
3079             FloodFillType type = static_cast<FloodFillType>(i);
3080             for(Tile* neigh : tileDoor->getAllNeighbors())
3081             {
3082                 uint32_t neighColor = neigh->getFloodFillValue(seat, type);
3083                 if(neighColor == Tile::NO_FLOODFILL)
3084                     continue;
3085 
3086                 if(neighColor == colors[i])
3087                     continue;
3088 
3089                 replaceFloodFill(seat, type, neighColor, colors[i]);
3090             }
3091         }
3092 
3093         return;
3094     }
3095 
3096     // We save the list of the creatures that are on the same floodfill as the door tile. Then, we will check if the path
3097     // is still valid
3098     std::vector<Creature*> creatures;
3099     for(Seat* alliedSeat : getSeats())
3100     {
3101         if((alliedSeat != seat) &&
3102            (!alliedSeat->isAlliedSeat(seat)))
3103         {
3104             continue;
3105         }
3106 
3107         std::vector<Creature*> alliedCreatures = getCreaturesBySeat(seat);
3108         for(Creature* creature : alliedCreatures)
3109         {
3110             if(!pathExists(creature, tileDoor, creature->getPositionTile()))
3111                 continue;
3112 
3113             creatures.push_back(creature);
3114         }
3115     }
3116 
3117     std::vector<uint32_t> colors(static_cast<uint32_t>(FloodFillType::nbValues), Tile::NO_FLOODFILL);
3118     Tile* tileChange = nullptr;
3119     uint32_t nbTilesNotFull = 0;
3120     for(Tile* neigh : tileDoor->getAllNeighbors())
3121     {
3122         // We look for the second not full tile and replace its floodfillvalues (the second is better than
3123         // the first in case a door is placed next to the gamemap border)
3124         if(neigh->isFullTile())
3125             continue;
3126 
3127         ++nbTilesNotFull;
3128         if(nbTilesNotFull >= 2)
3129         {
3130             tileChange = neigh;
3131             break;
3132         }
3133     }
3134 
3135     // If there is no tile to change, leaving
3136     if(tileChange == nullptr)
3137         return;
3138 
3139     // We only change tiles floodfilled like tileChange. That will avoid changing floodfill on tiles closed by
3140     // another closed door or something
3141     std::vector<uint32_t> colorsToChange(static_cast<uint32_t>(FloodFillType::nbValues), Tile::NO_FLOODFILL);
3142     for(uint32_t i = 0; i < colors.size(); ++i)
3143     {
3144         FloodFillType type = static_cast<FloodFillType>(i);
3145         uint32_t tileChangeColor = tileChange->getFloodFillValue(seat, type);
3146         if(tileChangeColor == Tile::NO_FLOODFILL)
3147             continue;
3148 
3149         colorsToChange[i] = tileChangeColor;
3150         colors[i] = nextUniqueFloodFillValue();
3151     }
3152 
3153     changeFloodFillConnectedTiles(tileChange, seat, colorsToChange, colors, tileDoor);
3154 
3155     // We check if a creature from the given seat has a path through the door and stop it if there is
3156     for(Creature* creature : creatures)
3157         creature->checkWalkPathValid();
3158 }
3159 
changeFloodFillConnectedTiles(Tile * startTile,Seat * seat,const std::vector<uint32_t> & oldColors,const std::vector<uint32_t> & newColors,Tile * tileIgnored)3160 void GameMap::changeFloodFillConnectedTiles(Tile* startTile, Seat* seat, const std::vector<uint32_t>& oldColors,
3161     const std::vector<uint32_t>& newColors, Tile* tileIgnored)
3162 {
3163     std::vector<Tile*> tiles;
3164     tiles.push_back(startTile);
3165     while(!tiles.empty())
3166     {
3167         Tile* tile = tiles.back();
3168         tiles.pop_back();
3169 
3170         // We add the neighboor tiles if they are floodfilled as startTile
3171         for(Tile* neigh : tile->getAllNeighbors())
3172         {
3173             // We check if the tile should not be processed
3174             if(neigh == tileIgnored)
3175                 continue;
3176 
3177             for(uint32_t i = 0; i < newColors.size(); ++i)
3178             {
3179                 if(newColors[i] == Tile::NO_FLOODFILL)
3180                     continue;
3181 
3182                 FloodFillType type = static_cast<FloodFillType>(i);
3183                 uint32_t neighColor = neigh->getFloodFillValue(seat, type);
3184                 if(neighColor == Tile::NO_FLOODFILL)
3185                     continue;
3186 
3187                 if((neighColor == oldColors[i]) &&
3188                    (std::find(tiles.begin(), tiles.end(), tile) == tiles.end()))
3189                 {
3190                     tiles.push_back(neigh);
3191                     break;
3192                 }
3193             }
3194         }
3195 
3196         // We replace floodillcolor for the current tile
3197         for(uint32_t i = 0; i < newColors.size(); ++i)
3198         {
3199             FloodFillType type = static_cast<FloodFillType>(i);
3200             if(newColors[i] == Tile::NO_FLOODFILL)
3201                 continue;
3202 
3203             if(tile->getFloodFillValue(seat, type) == oldColors[i])
3204                 tile->replaceFloodFill(seat, type, newColors[i]);
3205         }
3206     }
3207 }
3208 
notifySeatsConfigured()3209 void GameMap::notifySeatsConfigured()
3210 {
3211     mTeamIds.clear();
3212     // We always add the rogue team id
3213     mTeamIds.push_back(0);
3214 
3215     for(Seat* seat : mSeats)
3216     {
3217         if(seat->isRogueSeat())
3218             continue;
3219 
3220         uint32_t teamIndex = 0;
3221         for(int teamId : mTeamIds)
3222         {
3223             if(teamId == seat->getTeamId())
3224                 break;
3225 
3226             ++teamIndex;
3227         }
3228 
3229         // If the team id was not in the list, save it
3230         if(teamIndex == mTeamIds.size())
3231             mTeamIds.push_back(seat->getTeamId());
3232 
3233         seat->setTeamIndex(teamIndex);
3234     }
3235 
3236     uint32_t nbTeams = mTeamIds.size();
3237     for(int xxx = 0; xxx < getMapSizeX(); ++xxx)
3238     {
3239         for(int yyy = 0; yyy < getMapSizeY(); ++yyy)
3240         {
3241             Tile* tile = getTile(xxx, yyy);
3242             if(tile == nullptr)
3243                 continue;
3244 
3245             tile->setTeamsNumber(nbTeams);
3246         }
3247     }
3248     // Now that team ids are set and tiles are configured, we can compute floodfill
3249     enableFloodFill();
3250 }
3251 
fireGameSound(Tile & tile,const std::string & soundFamily)3252 void GameMap::fireGameSound(Tile& tile, const std::string& soundFamily)
3253 {
3254     std::string sound = "Game/" + soundFamily;
3255     for(Seat* seat : tile.getSeatsWithVision())
3256     {
3257         if(seat->getPlayer() == nullptr)
3258             continue;
3259         if(!seat->getPlayer()->getIsHuman())
3260             continue;
3261 
3262         ServerNotification *serverNotification = new ServerNotification(
3263             ServerNotificationType::playSpatialSound, seat->getPlayer());
3264         serverNotification->mPacket << sound << tile.getX() << tile.getY();
3265         ODServer::getSingleton().queueServerNotification(serverNotification);
3266     }
3267 }
3268 
fireRelativeSound(const std::vector<Seat * > & seats,const std::string & soundFamily)3269 void GameMap::fireRelativeSound(const std::vector<Seat*>& seats, const std::string& soundFamily)
3270 {
3271     for(Seat* seat : seats)
3272     {
3273         if(seat->getPlayer() == nullptr)
3274             continue;
3275         if(!seat->getPlayer()->getIsHuman())
3276             continue;
3277 
3278         ServerNotification *serverNotification = new ServerNotification(
3279             ServerNotificationType::playRelativeSound, seat->getPlayer());
3280         serverNotification->mPacket << soundFamily;
3281         ODServer::getSingleton().queueServerNotification(serverNotification);
3282     }
3283 }
3284