1 /*
2  *  Copyright (C) 2011-2016  OpenDungeons Team
3  *
4  *  This program is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 3 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "traps/TrapManager.h"
19 
20 #include "entities/Tile.h"
21 #include "game/Player.h"
22 #include "game/Skill.h"
23 #include "game/Seat.h"
24 #include "gamemap/GameMap.h"
25 #include "modes/InputCommand.h"
26 #include "modes/InputManager.h"
27 #include "network/ClientNotification.h"
28 #include "network/ODClient.h"
29 #include "network/ODServer.h"
30 #include "network/ServerNotification.h"
31 #include "traps/Trap.h"
32 #include "traps/TrapType.h"
33 #include "utils/ConfigManager.h"
34 #include "utils/Helper.h"
35 #include "utils/LogManager.h"
36 
37 static const std::string EMPTY_STRING;
38 
39 namespace
40 {
getFactories()41     static std::vector<const TrapFactory*>& getFactories()
42     {
43         static std::vector<const TrapFactory*> factory(static_cast<uint32_t>(TrapType::nbTraps), nullptr);
44         return factory;
45     }
46 }
47 
formatBuildTrap(TrapType type,uint32_t price) const48 std::string TrapFactory::formatBuildTrap(TrapType type, uint32_t price) const
49 {
50     return "Build " + TrapManager::getTrapReadableName(type) + " [" + Helper::toString(price)+ " gold]";
51 }
52 
checkBuildTrapDefault(GameMap * gameMap,TrapType type,const InputManager & inputManager,InputCommand & inputCommand) const53 void TrapFactory::checkBuildTrapDefault(GameMap* gameMap, TrapType type, const InputManager& inputManager, InputCommand& inputCommand) const
54 {
55     Player* player = gameMap->getLocalPlayer();
56     int32_t pricePerTarget = TrapManager::costPerTile(type);
57     int32_t playerGold = static_cast<int32_t>(player->getSeat()->getGold());
58     if(inputManager.mCommandState == InputCommandState::infoOnly)
59     {
60         if(playerGold < pricePerTarget)
61         {
62             std::string txt = formatBuildTrap(type, pricePerTarget);
63             inputCommand.displayText(Ogre::ColourValue::Red, txt);
64         }
65         else
66         {
67             std::string txt = formatBuildTrap(type, pricePerTarget);
68             inputCommand.displayText(Ogre::ColourValue::White, txt);
69         }
70         inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos,
71             inputManager.mYPos);
72         return;
73     }
74 
75     std::vector<Tile*> buildableTiles = gameMap->getBuildableTilesForPlayerInArea(inputManager.mXPos,
76         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY, player);
77 
78     if(inputManager.mCommandState == InputCommandState::building)
79         inputCommand.selectTiles(buildableTiles);
80 
81     if(buildableTiles.empty())
82     {
83         std::string txt = formatBuildTrap(type, 0);
84         inputCommand.displayText(Ogre::ColourValue::White, txt);
85         return;
86     }
87 
88     int32_t priceTotal = static_cast<int32_t>(buildableTiles.size()) * pricePerTarget;
89     if(playerGold < priceTotal)
90     {
91         std::string txt = formatBuildTrap(type, priceTotal);
92         inputCommand.displayText(Ogre::ColourValue::Red, txt);
93         return;
94     }
95 
96     std::string txt = formatBuildTrap(type, priceTotal);
97     inputCommand.displayText(Ogre::ColourValue::White, txt);
98 
99     if(inputManager.mCommandState != InputCommandState::validated)
100         return;
101 
102     ClientNotification *clientNotification = TrapManager::createTrapClientNotification(type);
103     uint32_t nbTiles = buildableTiles.size();
104     clientNotification->mPacket << nbTiles;
105     for(Tile* tile : buildableTiles)
106         gameMap->tileToPacket(clientNotification->mPacket, tile);
107 
108     ODClient::getSingleton().queueClientNotification(clientNotification);
109 }
110 
getTrapTilesDefault(std::vector<Tile * > & tiles,GameMap * gameMap,Player * player,ODPacket & packet) const111 bool TrapFactory::getTrapTilesDefault(std::vector<Tile*>& tiles, GameMap* gameMap, Player* player, ODPacket& packet) const
112 {
113     uint32_t nbTiles;
114     OD_ASSERT_TRUE(packet >> nbTiles);
115 
116     while(nbTiles > 0)
117     {
118         --nbTiles;
119         Tile* tile = gameMap->tileFromPacket(packet);
120         if(tile == nullptr)
121         {
122             OD_LOG_ERR("unexpected null tile");
123             return false;
124         }
125 
126         if(!tile->isBuildableUpon(player->getSeat()))
127         {
128             OD_LOG_ERR("tile=" + Tile::displayAsString(tile) + ", seatId=" + Helper::toString(player->getSeat()->getId()));
129             continue;
130         }
131 
132         tiles.push_back(tile);
133     }
134 
135     return true;
136 }
137 
buildTrapDefault(GameMap * gameMap,Trap * trap,Seat * seat,const std::vector<Tile * > & tiles) const138 bool TrapFactory::buildTrapDefault(GameMap* gameMap, Trap* trap, Seat* seat, const std::vector<Tile*>& tiles) const
139 {
140     if(tiles.empty())
141         return false;
142 
143     trap->setupTrap(gameMap->nextUniqueNameTrap(trap->getType()), seat, tiles);
144     trap->addToGameMap();
145     trap->createMesh();
146 
147     if((seat->getPlayer() != nullptr) &&
148        (seat->getPlayer()->getIsHuman()))
149     {
150         // We notify the clients with vision of the changed tiles. Note that we need
151         // to calculate per seat since they could have vision on different parts of the building
152         std::map<Seat*,std::vector<Tile*>> tilesPerSeat;
153         const std::vector<Seat*>& seats = gameMap->getSeats();
154         for(Seat* tmpSeat : seats)
155         {
156             if(tmpSeat->getPlayer() == nullptr)
157                 continue;
158             if(!tmpSeat->getPlayer()->getIsHuman())
159                 continue;
160 
161             for(Tile* tile : tiles)
162             {
163                 if(!tmpSeat->hasVisionOnTile(tile))
164                     continue;
165 
166                 tile->changeNotifiedForSeat(tmpSeat);
167                 tilesPerSeat[tmpSeat].push_back(tile);
168             }
169         }
170 
171         for(const std::pair<Seat* const,std::vector<Tile*>>& p : tilesPerSeat)
172         {
173             uint32_t nbTiles = p.second.size();
174             ServerNotification serverNotification(
175                 ServerNotificationType::refreshTiles, p.first->getPlayer());
176             serverNotification.mPacket << nbTiles;
177             for(Tile* tile : p.second)
178             {
179                 gameMap->tileToPacket(serverNotification.mPacket, tile);
180                 p.first->updateTileStateForSeat(tile, false);
181                 tile->exportToPacketForUpdate(serverNotification.mPacket, p.first);
182             }
183             ODServer::getSingleton().sendAsyncMsg(serverNotification);
184         }
185     }
186 
187     trap->updateActiveSpots();
188 
189     return true;
190 }
191 
checkBuildTrapDefaultEditor(GameMap * gameMap,TrapType type,const InputManager & inputManager,InputCommand & inputCommand) const192 void TrapFactory::checkBuildTrapDefaultEditor(GameMap* gameMap, TrapType type, const InputManager& inputManager, InputCommand& inputCommand) const
193 {
194     std::string txt = TrapManager::getTrapReadableName(type);
195     inputCommand.displayText(Ogre::ColourValue::White, txt);
196     if(inputManager.mCommandState == InputCommandState::infoOnly)
197     {
198         inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos,
199             inputManager.mYPos);
200         return;
201     }
202 
203     std::vector<Tile*> tiles = gameMap->rectangularRegion(inputManager.mXPos,
204         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY);
205 
206     std::vector<Tile*> buildableTiles;
207     for(Tile* tile : tiles)
208     {
209         // We accept any tile if there is no building
210         if(tile->getIsBuilding())
211             continue;
212 
213         buildableTiles.push_back(tile);
214     }
215     if(inputManager.mCommandState == InputCommandState::building)
216     {
217         inputCommand.selectTiles(buildableTiles);
218         return;
219     }
220 
221     ClientNotification *clientNotification = TrapManager::createTrapClientNotificationEditor(type);
222     uint32_t nbTiles = buildableTiles.size();
223     int32_t seatId = inputManager.mSeatIdSelected;
224     clientNotification->mPacket << seatId;
225     clientNotification->mPacket << nbTiles;
226     for(Tile* tile : buildableTiles)
227         gameMap->tileToPacket(clientNotification->mPacket, tile);
228 
229     ODClient::getSingleton().queueClientNotification(clientNotification);
230 }
231 
buildTrapDefaultEditor(GameMap * gameMap,Trap * trap,ODPacket & packet) const232 bool TrapFactory::buildTrapDefaultEditor(GameMap* gameMap, Trap* trap, ODPacket& packet) const
233 {
234     int32_t seatId;
235     OD_ASSERT_TRUE(packet >> seatId);
236     Seat* seatTrap = gameMap->getSeatById(seatId);
237     if(seatTrap == nullptr)
238     {
239         OD_LOG_ERR("seatId=" + Helper::toString(seatId));
240         return false;
241     }
242 
243     std::vector<Tile*> tiles;
244     uint32_t nbTiles;
245     OD_ASSERT_TRUE(packet >> nbTiles);
246 
247     while(nbTiles > 0)
248     {
249         --nbTiles;
250         Tile* tile = gameMap->tileFromPacket(packet);
251         if(tile == nullptr)
252         {
253             OD_LOG_ERR("unexpected null tile");
254             return false;
255         }
256 
257         // If the tile is not buildable, we change it
258         if(tile->getCoveringBuilding() != nullptr)
259         {
260             OD_LOG_ERR("tile=" + Tile::displayAsString(tile) + ", seatId=" + Helper::toString(seatId));
261             continue;
262         }
263 
264         tiles.push_back(tile);
265         if((tile->getType() != TileType::gold) &&
266            (tile->getType() != TileType::dirt))
267         {
268             tile->setType(TileType::dirt);
269         }
270         tile->setFullness(0.0);
271         tile->claimTile(seatTrap);
272         tile->computeTileVisual();
273     }
274 
275 
276     return buildTrapDefault(gameMap, trap, seatTrap, tiles);
277 }
278 
registerFactory(const TrapFactory * factory)279 void TrapManager::registerFactory(const TrapFactory* factory)
280 {
281     std::vector<const TrapFactory*>& factories = getFactories();
282     uint32_t index = static_cast<uint32_t>(factory->getTrapType());
283     if(index >= factories.size())
284     {
285         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
286         return;
287     }
288 
289     factories[index] = factory;
290 }
291 
unregisterFactory(const TrapFactory * factory)292 void TrapManager::unregisterFactory(const TrapFactory* factory)
293 {
294     std::vector<const TrapFactory*>& factories = getFactories();
295     auto it = std::find(factories.begin(), factories.end(), factory);
296     if(it == factories.end())
297     {
298         OD_LOG_ERR("Trying to unregister unknown factory=" + factory->getName());
299         return;
300     }
301     factories.erase(it);
302 }
303 
load(GameMap * gameMap,std::istream & is)304 Trap* TrapManager::load(GameMap* gameMap, std::istream& is)
305 {
306     if(!is.good())
307         return nullptr;
308 
309     std::vector<const TrapFactory*>& factories = getFactories();
310     std::string nextParam;
311     OD_ASSERT_TRUE(is >> nextParam);
312     const TrapFactory* factoryToUse = nullptr;
313     for(const TrapFactory* factory : factories)
314     {
315         if(factory == nullptr)
316             continue;
317 
318         if(factory->getName().compare(nextParam) != 0)
319             continue;
320 
321         factoryToUse = factory;
322         break;
323     }
324 
325     if(factoryToUse == nullptr)
326     {
327         OD_LOG_ERR("Unknown Trap type=" + nextParam);
328         return nullptr;
329     }
330 
331     Trap* trap = factoryToUse->getTrapFromStream(gameMap, is);
332     if(!trap->importFromStream(is))
333     {
334         OD_LOG_ERR("Couldn't load creature Trap type=" + nextParam);
335         delete trap;
336         return nullptr;
337     }
338 
339     return trap;
340 }
341 
dispose(const Trap * trap)342 void TrapManager::dispose(const Trap* trap)
343 {
344     delete trap;
345 }
346 
write(const Trap & trap,std::ostream & os)347 void TrapManager::write(const Trap& trap, std::ostream& os)
348 {
349     os << trap.getName();
350     trap.exportToStream(os);
351 }
352 
checkBuildTrap(GameMap * gameMap,TrapType type,const InputManager & inputManager,InputCommand & inputCommand)353 void TrapManager::checkBuildTrap(GameMap* gameMap, TrapType type, const InputManager& inputManager, InputCommand& inputCommand)
354 {
355     std::vector<const TrapFactory*>& factories = getFactories();
356     uint32_t index = static_cast<uint32_t>(type);
357     if(index >= factories.size())
358     {
359         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
360         return;
361     }
362 
363     const TrapFactory& factory = *factories[index];
364     factory.checkBuildTrap(gameMap, inputManager, inputCommand);
365 }
366 
buildTrap(GameMap * gameMap,TrapType type,Player * player,ODPacket & packet)367 bool TrapManager::buildTrap(GameMap* gameMap, TrapType type, Player* player, ODPacket& packet)
368 {
369     std::vector<const TrapFactory*>& factories = getFactories();
370     uint32_t index = static_cast<uint32_t>(type);
371     if(index >= factories.size())
372     {
373         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
374         return false;
375     }
376 
377     const TrapFactory& factory = *factories[index];
378     return factory.buildTrap(gameMap, player, packet);
379 }
380 
checkBuildTrapEditor(GameMap * gameMap,TrapType type,const InputManager & inputManager,InputCommand & inputCommand)381 void TrapManager::checkBuildTrapEditor(GameMap* gameMap, TrapType type, const InputManager& inputManager, InputCommand& inputCommand)
382 {
383     std::vector<const TrapFactory*>& factories = getFactories();
384     uint32_t index = static_cast<uint32_t>(type);
385     if(index >= factories.size())
386     {
387         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
388         return;
389     }
390 
391     const TrapFactory& factory = *factories[index];
392     factory.checkBuildTrapEditor(gameMap, inputManager, inputCommand);
393 }
394 
buildTrapEditor(GameMap * gameMap,TrapType type,ODPacket & packet)395 bool TrapManager::buildTrapEditor(GameMap* gameMap, TrapType type, ODPacket& packet)
396 {
397     std::vector<const TrapFactory*>& factories = getFactories();
398     uint32_t index = static_cast<uint32_t>(type);
399     if(index >= factories.size())
400     {
401         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
402         return false;
403     }
404 
405     const TrapFactory& factory = *factories[index];
406     return factory.buildTrapEditor(gameMap, packet);
407 }
408 
buildTrapOnTiles(GameMap * gameMap,TrapType type,Player * player,const std::vector<Tile * > & tiles)409 bool TrapManager::buildTrapOnTiles(GameMap* gameMap, TrapType type, Player* player, const std::vector<Tile*>& tiles)
410 {
411     std::vector<const TrapFactory*>& factories = getFactories();
412     uint32_t index = static_cast<uint32_t>(type);
413     if(index >= factories.size())
414     {
415         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
416         return false;
417     }
418 
419     const TrapFactory& factory = *factories[index];
420     return factory.buildTrapOnTiles(gameMap, player, tiles);
421 }
422 
getTrapFromStream(GameMap * gameMap,std::istream & is)423 Trap* TrapManager::getTrapFromStream(GameMap* gameMap, std::istream& is)
424 {
425     TrapType type;
426     if(!(is >> type))
427         return nullptr;
428 
429     std::vector<const TrapFactory*>& factories = getFactories();
430     uint32_t index = static_cast<uint32_t>(type);
431     if(index >= factories.size())
432     {
433         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
434         return nullptr;
435     }
436 
437     const TrapFactory& factory = *factories[index];
438     return factory.getTrapFromStream(gameMap, is);
439 }
440 
getTrapNameFromTrapType(TrapType type)441 const std::string& TrapManager::getTrapNameFromTrapType(TrapType type)
442 {
443     std::vector<const TrapFactory*>& factories = getFactories();
444     uint32_t index = static_cast<uint32_t>(type);
445     if(index >= factories.size())
446     {
447         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
448         return EMPTY_STRING;
449     }
450 
451     const TrapFactory& factory = *factories[index];
452     return factory.getName();
453 }
454 
getTrapReadableName(TrapType type)455 const std::string& TrapManager::getTrapReadableName(TrapType type)
456 {
457     std::vector<const TrapFactory*>& factories = getFactories();
458     uint32_t index = static_cast<uint32_t>(type);
459     if(index >= factories.size())
460     {
461         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
462         return EMPTY_STRING;
463     }
464 
465     const TrapFactory& factory = *factories[index];
466     return factory.getNameReadable();
467 }
468 
getTrapTypeFromTrapName(const std::string & name)469 TrapType TrapManager::getTrapTypeFromTrapName(const std::string& name)
470 {
471     std::vector<const TrapFactory*>& factories = getFactories();
472     for(const TrapFactory* factory : factories)
473     {
474         if(factory == nullptr)
475             continue;
476         if(factory->getName().compare(name) != 0)
477             continue;
478 
479         return factory->getTrapType();
480     }
481 
482     OD_LOG_ERR("Cannot find Trap name=" + name);
483     return TrapType::nullTrapType;
484 }
485 
checkSellTrapTiles(GameMap * gameMap,const InputManager & inputManager,InputCommand & inputCommand)486 void TrapManager::checkSellTrapTiles(GameMap* gameMap, const InputManager& inputManager, InputCommand& inputCommand)
487 {
488     Player* player = gameMap->getLocalPlayer();
489     if(inputManager.mCommandState == InputCommandState::infoOnly)
490     {
491         // We do not differentiate between Trap and trap (because there is no way to know on client side).
492         // Note that price = 0 doesn't mean that the building is not a Trap
493         Tile* tile = gameMap->getTile(inputManager.mXPos, inputManager.mYPos);
494         if((tile == nullptr) || (!tile->getIsTrap()) || (tile->getSeat() != player->getSeat()))
495         {
496             std::string txt = formatSellTrap(0);
497             inputCommand.displayText(Ogre::ColourValue::White, txt);
498             inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos, inputManager.mYPos);
499             return;
500         }
501 
502         uint32_t price = tile->getRefundPriceTrap();
503         std::string txt = formatSellTrap(price);
504         inputCommand.displayText(Ogre::ColourValue::White, txt);
505         std::vector<Tile*> tiles;
506         tiles.push_back(tile);
507         inputCommand.selectTiles(tiles);
508         return;
509     }
510 
511     std::vector<Tile*> sellTiles;
512     std::vector<Tile*> tiles = gameMap->rectangularRegion(inputManager.mXPos,
513         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY);
514     uint32_t priceTotal = 0;
515     for(Tile* tile : tiles)
516     {
517         if(!tile->getIsTrap())
518             continue;
519 
520         if(tile->getSeat() != player->getSeat())
521             continue;
522 
523         sellTiles.push_back(tile);
524         priceTotal += tile->getRefundPriceTrap();
525     }
526 
527     if(inputManager.mCommandState == InputCommandState::building)
528     {
529         inputCommand.selectTiles(sellTiles);
530         std::string txt = formatSellTrap(priceTotal);
531         inputCommand.displayText(Ogre::ColourValue::White, txt);
532         return;
533     }
534 
535     inputCommand.unselectAllTiles();
536 
537     ClientNotification *clientNotification = new ClientNotification(
538         ClientNotificationType::askSellTrapTiles);
539     uint32_t nbTiles = sellTiles.size();
540     clientNotification->mPacket << nbTiles;
541     for(Tile* tile : sellTiles)
542         gameMap->tileToPacket(clientNotification->mPacket, tile);
543 
544     ODClient::getSingleton().queueClientNotification(clientNotification);
545 }
546 
sellTrapTiles(GameMap * gameMap,Seat * seatSell,ODPacket & packet)547 void TrapManager::sellTrapTiles(GameMap* gameMap, Seat* seatSell, ODPacket& packet)
548 {
549     uint32_t nbTiles;
550     OD_ASSERT_TRUE(packet >> nbTiles);
551     int32_t price = 0;
552     std::set<Trap*> traps;
553     std::vector<Tile*> tiles;
554     while(nbTiles > 0)
555     {
556         --nbTiles;
557         Tile* tile = gameMap->tileFromPacket(packet);
558         if(tile == nullptr)
559         {
560             OD_LOG_ERR("tile=" + Tile::displayAsString(tile));
561             continue;
562         }
563         Trap* trap = tile->getCoveringTrap();
564         if(trap == nullptr)
565             continue;
566 
567         if(!trap->canSeatSellBuilding(seatSell))
568             continue;
569 
570         if(!trap->removeCoveredTile(tile))
571         {
572             OD_LOG_ERR("trap=" + trap->getName() + ", tile=" + Tile::displayAsString(tile) + ", seatId=" + Helper::toString(seatSell->getId()));
573             continue;
574         }
575 
576         price += costPerTile(trap->getType()) / 2;
577         tiles.push_back(tile);
578         traps.insert(trap);
579     }
580 
581     gameMap->addGoldToSeat(price, seatSell->getId());
582 
583     // We notify the clients with vision of the changed tiles. Note that we need
584     // to calculate per seat since the could have vision on different parts of the building
585     std::map<Seat*,std::vector<Tile*>> tilesPerSeat;
586     const std::vector<Seat*>& seats = gameMap->getSeats();
587     for(Seat* seat : seats)
588     {
589         if(seat->getPlayer() == nullptr)
590             continue;
591         if(!seat->getPlayer()->getIsHuman())
592             continue;
593 
594         for(Tile* tile : tiles)
595         {
596             if(!seat->hasVisionOnTile(tile))
597                 continue;
598 
599             tile->changeNotifiedForSeat(seat);
600             tilesPerSeat[seat].push_back(tile);
601         }
602     }
603 
604     for(const std::pair<Seat* const,std::vector<Tile*>>& p : tilesPerSeat)
605     {
606         uint32_t nbTiles = p.second.size();
607         ServerNotification serverNotification(
608             ServerNotificationType::refreshTiles, p.first->getPlayer());
609         serverNotification.mPacket << nbTiles;
610         for(Tile* tile : p.second)
611         {
612             gameMap->tileToPacket(serverNotification.mPacket, tile);
613             p.first->updateTileStateForSeat(tile, false);
614             tile->exportToPacketForUpdate(serverNotification.mPacket, p.first);
615         }
616         ODServer::getSingleton().sendAsyncMsg(serverNotification);
617     }
618 
619     // We update active spots of each impacted traps
620     for(Trap* trap : traps)
621         trap->updateActiveSpots();
622 }
623 
formatSellTrap(int price)624 std::string TrapManager::formatSellTrap(int price)
625 {
626     return "retrieve " + Helper::toString(price) + " gold";
627 }
628 
checkSellTrapTilesEditor(GameMap * gameMap,const InputManager & inputManager,InputCommand & inputCommand)629 void TrapManager::checkSellTrapTilesEditor(GameMap* gameMap, const InputManager& inputManager, InputCommand& inputCommand)
630 {
631     if(inputManager.mCommandState == InputCommandState::infoOnly)
632     {
633         inputCommand.displayText(Ogre::ColourValue::White, "Remove tiles");
634         inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos, inputManager.mYPos);
635         return;
636     }
637 
638     std::vector<Tile*> sellTiles;
639     std::vector<Tile*> tiles = gameMap->rectangularRegion(inputManager.mXPos,
640         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY);
641     for(Tile* tile : tiles)
642     {
643         if(!tile->getIsTrap())
644             continue;
645 
646         sellTiles.push_back(tile);
647     }
648 
649     if(inputManager.mCommandState == InputCommandState::building)
650     {
651         inputCommand.selectTiles(sellTiles);
652         inputCommand.displayText(Ogre::ColourValue::White, "Remove tiles");
653         return;
654     }
655 
656     inputCommand.unselectAllTiles();
657 
658     ClientNotification *clientNotification = new ClientNotification(
659         ClientNotificationType::editorAskDestroyTrapTiles);
660     uint32_t nbTiles = sellTiles.size();
661     clientNotification->mPacket << nbTiles;
662     for(Tile* tile : sellTiles)
663         gameMap->tileToPacket(clientNotification->mPacket, tile);
664 
665     ODClient::getSingleton().queueClientNotification(clientNotification);
666 }
667 
sellTrapTilesEditor(GameMap * gameMap,ODPacket & packet)668 void TrapManager::sellTrapTilesEditor(GameMap* gameMap, ODPacket& packet)
669 {
670     uint32_t nbTiles;
671     OD_ASSERT_TRUE(packet >> nbTiles);
672     std::set<Trap*> traps;
673     std::vector<Tile*> tiles;
674     while(nbTiles > 0)
675     {
676         --nbTiles;
677         Tile* tile = gameMap->tileFromPacket(packet);
678         if(tile == nullptr)
679         {
680             OD_LOG_ERR("tile=" + Tile::displayAsString(tile));
681             continue;
682         }
683         Trap* trap = tile->getCoveringTrap();
684         if(trap == nullptr)
685             continue;
686 
687         if(!trap->removeCoveredTile(tile))
688         {
689             OD_LOG_ERR("trap=" + trap->getName() + ", tile=" + Tile::displayAsString(tile));
690             continue;
691         }
692 
693         tiles.push_back(tile);
694         traps.insert(trap);
695     }
696 
697     // We notify the clients with vision of the changed tiles. Note that we need
698     // to calculate per seat since the could have vision on different parts of the building
699     std::map<Seat*,std::vector<Tile*>> tilesPerSeat;
700     const std::vector<Seat*>& seats = gameMap->getSeats();
701     for(Seat* seat : seats)
702     {
703         if(seat->getPlayer() == nullptr)
704             continue;
705         if(!seat->getPlayer()->getIsHuman())
706             continue;
707 
708         for(Tile* tile : tiles)
709         {
710             if(!seat->hasVisionOnTile(tile))
711                 continue;
712 
713             tile->changeNotifiedForSeat(seat);
714             tilesPerSeat[seat].push_back(tile);
715         }
716     }
717 
718     for(const std::pair<Seat* const,std::vector<Tile*>>& p : tilesPerSeat)
719     {
720         uint32_t nbTiles = p.second.size();
721         ServerNotification serverNotification(
722             ServerNotificationType::refreshTiles, p.first->getPlayer());
723         serverNotification.mPacket << nbTiles;
724         for(Tile* tile : p.second)
725         {
726             gameMap->tileToPacket(serverNotification.mPacket, tile);
727             p.first->updateTileStateForSeat(tile, false);
728             tile->exportToPacketForUpdate(serverNotification.mPacket, p.first);
729         }
730         ODServer::getSingleton().sendAsyncMsg(serverNotification);
731     }
732 
733     // We update active spots of each impacted Traps
734     for(Trap* trap : traps)
735         trap->updateActiveSpots();
736 }
737 
costPerTile(TrapType type)738 int TrapManager::costPerTile(TrapType type)
739 {
740     std::vector<const TrapFactory*>& factories = getFactories();
741     uint32_t index = static_cast<uint32_t>(type);
742     if(index >= factories.size())
743     {
744         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
745         return 0;
746     }
747 
748     const TrapFactory& factory = *factories[index];
749     return factory.getCostPerTile();
750 }
751 
getMeshFromTrapType(TrapType type)752 const std::string& TrapManager::getMeshFromTrapType(TrapType type)
753 {
754     std::vector<const TrapFactory*>& factories = getFactories();
755     uint32_t index = static_cast<uint32_t>(type);
756     if(index >= factories.size())
757     {
758         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
759         return EMPTY_STRING;
760     }
761 
762     const TrapFactory& factory = *factories[index];
763     return factory.getMeshName();
764 }
765 
createTrapClientNotification(TrapType type)766 ClientNotification* TrapManager::createTrapClientNotification(TrapType type)
767 {
768     ClientNotification *clientNotification = new ClientNotification(ClientNotificationType::askBuildTrap);
769     clientNotification->mPacket << type;
770     return clientNotification;
771 }
772 
createTrapClientNotificationEditor(TrapType type)773 ClientNotification* TrapManager::createTrapClientNotificationEditor(TrapType type)
774 {
775     ClientNotification *clientNotification = new ClientNotification(ClientNotificationType::editorAskBuildTrap);
776     clientNotification->mPacket << type;
777     return clientNotification;
778 }
779 
getNeededWorkshopPointsPerTrap(TrapType trapType)780 int32_t TrapManager::getNeededWorkshopPointsPerTrap(TrapType trapType)
781 {
782     switch(trapType)
783     {
784         case TrapType::nullTrapType:
785             return 0;
786         case TrapType::cannon:
787             return ConfigManager::getSingleton().getTrapConfigInt32("CannonWorkshopPointsPerTile");
788         case TrapType::spike:
789             return ConfigManager::getSingleton().getTrapConfigInt32("SpikeWorkshopPointsPerTile");
790         case TrapType::boulder:
791             return ConfigManager::getSingleton().getTrapConfigInt32("BoulderWorkshopPointsPerTile");
792         case TrapType::doorWooden:
793             return ConfigManager::getSingleton().getTrapConfigInt32("WoodenDoorPointsPerTile");
794         default:
795             OD_LOG_ERR("Asked for wrong trap type=" + getTrapNameFromTrapType(trapType));
796             break;
797     }
798     // We shouldn't go here
799     return 0;
800 }
801