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