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 "rooms/RoomManager.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 "rooms/Room.h"
32 #include "rooms/RoomType.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 
40 namespace
41 {
getFactories()42     static std::vector<const RoomFactory*>& getFactories()
43     {
44         static std::vector<const RoomFactory*> factory(static_cast<uint32_t>(RoomType::nbRooms), nullptr);
45         return factory;
46     }
47 }
48 
formatBuildRoom(RoomType type,uint32_t price) const49 std::string RoomFactory::formatBuildRoom(RoomType type, uint32_t price) const
50 {
51     return "Build " + RoomManager::getRoomReadableName(type) + " [" + Helper::toString(price)+ " gold]";
52 }
53 
checkBuildRoomDefault(GameMap * gameMap,RoomType type,const InputManager & inputManager,InputCommand & inputCommand) const54 void RoomFactory::checkBuildRoomDefault(GameMap* gameMap, RoomType type, const InputManager& inputManager, InputCommand& inputCommand) const
55 {
56     Player* player = gameMap->getLocalPlayer();
57     int32_t pricePerTarget = RoomManager::costPerTile(type);
58     int32_t playerGold = static_cast<int32_t>(player->getSeat()->getGold());
59     if(inputManager.mCommandState == InputCommandState::infoOnly)
60     {
61         if(playerGold < pricePerTarget)
62         {
63             std::string txt = formatBuildRoom(type, pricePerTarget);
64             inputCommand.displayText(Ogre::ColourValue::Red, txt);
65         }
66         else
67         {
68             std::string txt = formatBuildRoom(type, pricePerTarget);
69             inputCommand.displayText(Ogre::ColourValue::White, txt);
70         }
71         inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos,
72             inputManager.mYPos);
73         return;
74     }
75 
76     std::vector<Tile*> buildableTiles = gameMap->getBuildableTilesForPlayerInArea(inputManager.mXPos,
77         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY, player);
78 
79     if(inputManager.mCommandState == InputCommandState::building)
80         inputCommand.selectTiles(buildableTiles);
81 
82     if(buildableTiles.empty())
83     {
84         std::string txt = formatBuildRoom(type, 0);
85         inputCommand.displayText(Ogre::ColourValue::White, txt);
86         return;
87     }
88 
89     int32_t priceTotal = static_cast<int32_t>(buildableTiles.size()) * pricePerTarget;
90     if(playerGold < priceTotal)
91     {
92         std::string txt = formatBuildRoom(type, priceTotal);
93         inputCommand.displayText(Ogre::ColourValue::Red, txt);
94         return;
95     }
96 
97     std::string txt = formatBuildRoom(type, priceTotal);
98     inputCommand.displayText(Ogre::ColourValue::White, txt);
99 
100     if(inputManager.mCommandState != InputCommandState::validated)
101         return;
102 
103     ClientNotification *clientNotification = RoomManager::createRoomClientNotification(type);
104     uint32_t nbTiles = buildableTiles.size();
105     clientNotification->mPacket << nbTiles;
106     for(Tile* tile : buildableTiles)
107         gameMap->tileToPacket(clientNotification->mPacket, tile);
108 
109     ODClient::getSingleton().queueClientNotification(clientNotification);
110 }
111 
checkBuildRoomDefaultEditor(GameMap * gameMap,RoomType type,const InputManager & inputManager,InputCommand & inputCommand) const112 void RoomFactory::checkBuildRoomDefaultEditor(GameMap* gameMap, RoomType type, const InputManager& inputManager, InputCommand& inputCommand) const
113 {
114     std::string txt = RoomManager::getRoomReadableName(type);
115     inputCommand.displayText(Ogre::ColourValue::White, txt);
116     if(inputManager.mCommandState == InputCommandState::infoOnly)
117     {
118         inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos,
119             inputManager.mYPos);
120         return;
121     }
122 
123     std::vector<Tile*> tiles = gameMap->rectangularRegion(inputManager.mXPos,
124         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY);
125 
126     std::vector<Tile*> buildableTiles;
127     for(Tile* tile : tiles)
128     {
129         // We accept any tile if there is no building
130         if(tile->getIsBuilding())
131             continue;
132 
133         buildableTiles.push_back(tile);
134     }
135     if(inputManager.mCommandState == InputCommandState::building)
136     {
137         inputCommand.selectTiles(buildableTiles);
138         return;
139     }
140 
141     ClientNotification *clientNotification = RoomManager::createRoomClientNotificationEditor(type);
142     uint32_t nbTiles = buildableTiles.size();
143     int32_t seatId = inputManager.mSeatIdSelected;
144     clientNotification->mPacket << seatId;
145     clientNotification->mPacket << nbTiles;
146     for(Tile* tile : buildableTiles)
147         gameMap->tileToPacket(clientNotification->mPacket, tile);
148 
149     ODClient::getSingleton().queueClientNotification(clientNotification);
150 }
151 
getRoomTilesDefault(std::vector<Tile * > & tiles,GameMap * gameMap,Player * player,ODPacket & packet) const152 bool RoomFactory::getRoomTilesDefault(std::vector<Tile*>& tiles, GameMap* gameMap, Player* player, ODPacket& packet) const
153 {
154     uint32_t nbTiles;
155     OD_ASSERT_TRUE(packet >> nbTiles);
156 
157     while(nbTiles > 0)
158     {
159         --nbTiles;
160         Tile* tile = gameMap->tileFromPacket(packet);
161         if(tile == nullptr)
162         {
163             OD_LOG_ERR("unexpected null tile");
164             return false;
165         }
166 
167         if(!tile->isBuildableUpon(player->getSeat()))
168         {
169             OD_LOG_ERR("tile=" + Tile::displayAsString(tile) + ", seatId=" + Helper::toString(player->getSeat()->getId()));
170             continue;
171         }
172 
173         tiles.push_back(tile);
174     }
175 
176     return true;
177 }
178 
buildRoomDefault(GameMap * gameMap,Room * room,Seat * seat,const std::vector<Tile * > & tiles) const179 bool RoomFactory::buildRoomDefault(GameMap* gameMap, Room* room, Seat* seat, const std::vector<Tile*>& tiles) const
180 {
181     if(tiles.empty())
182         return false;
183 
184     room->setupRoom(gameMap->nextUniqueNameRoom(room->getType()), seat, tiles);
185     room->addToGameMap();
186     room->createMesh();
187 
188     if((seat->getPlayer() != nullptr) &&
189        (seat->getPlayer()->getIsHuman()))
190     {
191         // We notify the clients with vision of the changed tiles. Note that we need
192         // to calculate per seat since they could have vision on different parts of the building
193         std::map<Seat*,std::vector<Tile*>> tilesPerSeat;
194         const std::vector<Seat*>& seats = gameMap->getSeats();
195         for(Seat* tmpSeat : seats)
196         {
197             if(tmpSeat->getPlayer() == nullptr)
198                 continue;
199             if(!tmpSeat->getPlayer()->getIsHuman())
200                 continue;
201 
202             for(Tile* tile : tiles)
203             {
204                 if(!tmpSeat->hasVisionOnTile(tile))
205                     continue;
206 
207                 tile->changeNotifiedForSeat(tmpSeat);
208                 tilesPerSeat[tmpSeat].push_back(tile);
209             }
210         }
211 
212         for(const std::pair<Seat* const,std::vector<Tile*>>& p : tilesPerSeat)
213         {
214             uint32_t nbTiles = p.second.size();
215             ServerNotification serverNotification(
216                 ServerNotificationType::refreshTiles, p.first->getPlayer());
217             serverNotification.mPacket << nbTiles;
218             for(Tile* tile : p.second)
219             {
220                 gameMap->tileToPacket(serverNotification.mPacket, tile);
221                 p.first->updateTileStateForSeat(tile, false);
222                 tile->exportToPacketForUpdate(serverNotification.mPacket, p.first);
223             }
224             ODServer::getSingleton().sendAsyncMsg(serverNotification);
225         }
226     }
227 
228     room->checkForRoomAbsorbtion();
229     room->updateActiveSpots();
230 
231     return true;
232 }
233 
buildRoomDefaultEditor(GameMap * gameMap,Room * room,ODPacket & packet) const234 bool RoomFactory::buildRoomDefaultEditor(GameMap* gameMap, Room* room, ODPacket& packet) const
235 {
236     int32_t seatId;
237     OD_ASSERT_TRUE(packet >> seatId);
238     Seat* seatRoom = gameMap->getSeatById(seatId);
239     if(seatRoom == nullptr)
240     {
241         OD_LOG_ERR("seatId=" + Helper::toString(seatId));
242         return false;
243     }
244 
245     std::vector<Tile*> tiles;
246     uint32_t nbTiles;
247     OD_ASSERT_TRUE(packet >> nbTiles);
248 
249     while(nbTiles > 0)
250     {
251         --nbTiles;
252         Tile* tile = gameMap->tileFromPacket(packet);
253         if(tile == nullptr)
254         {
255             OD_LOG_ERR("unexpected null tile room=" + room->getName());
256             return false;
257         }
258 
259         // If the tile is not buildable, we change it
260         if(tile->getCoveringBuilding() != nullptr)
261         {
262             OD_LOG_ERR("tile=" + Tile::displayAsString(tile) + ", seatId=" + Helper::toString(seatId));
263             continue;
264         }
265 
266         tiles.push_back(tile);
267         if((tile->getType() != TileType::gold) &&
268         (tile->getType() != TileType::dirt))
269         {
270             tile->setType(TileType::dirt);
271         }
272         tile->setFullness(0.0);
273         tile->claimTile(seatRoom);
274         tile->computeTileVisual();
275     }
276 
277 
278     return buildRoomDefault(gameMap, room, seatRoom, tiles);
279 }
280 
registerFactory(const RoomFactory * factory)281 void RoomManager::registerFactory(const RoomFactory* factory)
282 {
283     std::vector<const RoomFactory*>& factories = getFactories();
284     uint32_t index = static_cast<uint32_t>(factory->getRoomType());
285     if(index >= factories.size())
286     {
287         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
288         return;
289     }
290 
291     factories[index] = factory;
292 }
293 
unregisterFactory(const RoomFactory * factory)294 void RoomManager::unregisterFactory(const RoomFactory* factory)
295 {
296     std::vector<const RoomFactory*>& factories = getFactories();
297     auto it = std::find(factories.begin(), factories.end(), factory);
298     if(it == factories.end())
299     {
300         OD_LOG_ERR("Trying to unregister unknown factory=" + factory->getName());
301         return;
302     }
303     factories.erase(it);
304 }
305 
load(GameMap * gameMap,std::istream & is)306 Room* RoomManager::load(GameMap* gameMap, std::istream& is)
307 {
308     if(!is.good())
309         return nullptr;
310 
311     std::vector<const RoomFactory*>& factories = getFactories();
312     std::string nextParam;
313     OD_ASSERT_TRUE(is >> nextParam);
314     const RoomFactory* factoryToUse = nullptr;
315     for(const RoomFactory* factory : factories)
316     {
317         if(factory == nullptr)
318             continue;
319 
320         if(factory->getName().compare(nextParam) != 0)
321             continue;
322 
323         factoryToUse = factory;
324         break;
325     }
326 
327     if(factoryToUse == nullptr)
328     {
329         OD_LOG_ERR("Unknown room type=" + nextParam);
330         return nullptr;
331     }
332 
333     Room* room = factoryToUse->getRoomFromStream(gameMap, is);
334     if(!room->importFromStream(is))
335     {
336         OD_LOG_ERR("Couldn't load creature room type=" + nextParam);
337         delete room;
338         return nullptr;
339     }
340 
341     return room;
342 }
343 
dispose(const Room * room)344 void RoomManager::dispose(const Room* room)
345 {
346     delete room;
347 }
348 
write(const Room & room,std::ostream & os)349 void RoomManager::write(const Room& room, std::ostream& os)
350 {
351     os << room.getName();
352     room.exportToStream(os);
353 }
354 
checkBuildRoom(GameMap * gameMap,RoomType type,const InputManager & inputManager,InputCommand & inputCommand)355 void RoomManager::checkBuildRoom(GameMap* gameMap, RoomType type, const InputManager& inputManager, InputCommand& inputCommand)
356 {
357     std::vector<const RoomFactory*>& factories = getFactories();
358     uint32_t index = static_cast<uint32_t>(type);
359     if(index >= factories.size())
360     {
361         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
362         return;
363     }
364 
365     const RoomFactory& factory = *factories[index];
366     factory.checkBuildRoom(gameMap, inputManager, inputCommand);
367 }
368 
buildRoom(GameMap * gameMap,RoomType type,Player * player,ODPacket & packet)369 bool RoomManager::buildRoom(GameMap* gameMap, RoomType type, Player* player, ODPacket& packet)
370 {
371     std::vector<const RoomFactory*>& factories = getFactories();
372     uint32_t index = static_cast<uint32_t>(type);
373     if(index >= factories.size())
374     {
375         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
376         return false;
377     }
378 
379     const RoomFactory& factory = *factories[index];
380     return factory.buildRoom(gameMap, player, packet);
381 }
382 
checkBuildRoomEditor(GameMap * gameMap,RoomType type,const InputManager & inputManager,InputCommand & inputCommand)383 void RoomManager::checkBuildRoomEditor(GameMap* gameMap, RoomType type, const InputManager& inputManager, InputCommand& inputCommand)
384 {
385     std::vector<const RoomFactory*>& factories = getFactories();
386     uint32_t index = static_cast<uint32_t>(type);
387     if(index >= factories.size())
388     {
389         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
390         return;
391     }
392 
393     const RoomFactory& factory = *factories[index];
394     factory.checkBuildRoomEditor(gameMap, inputManager, inputCommand);
395 }
396 
buildRoomEditor(GameMap * gameMap,RoomType type,ODPacket & packet)397 bool RoomManager::buildRoomEditor(GameMap* gameMap, RoomType type, ODPacket& packet)
398 {
399     std::vector<const RoomFactory*>& factories = getFactories();
400     uint32_t index = static_cast<uint32_t>(type);
401     if(index >= factories.size())
402     {
403         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
404         return false;
405     }
406 
407     const RoomFactory& factory = *factories[index];
408     return factory.buildRoomEditor(gameMap, packet);
409 }
410 
getRoomFromStream(GameMap * gameMap,std::istream & is)411 Room* RoomManager::getRoomFromStream(GameMap* gameMap, std::istream& is)
412 {
413     RoomType type;
414     if(!(is >> type))
415         return nullptr;
416 
417     std::vector<const RoomFactory*>& factories = getFactories();
418     uint32_t index = static_cast<uint32_t>(type);
419     if(index >= factories.size())
420     {
421         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
422         return nullptr;
423     }
424 
425     const RoomFactory& factory = *factories[index];
426     return factory.getRoomFromStream(gameMap, is);
427 }
428 
buildRoomOnTiles(GameMap * gameMap,RoomType type,Player * player,const std::vector<Tile * > & tiles)429 bool RoomManager::buildRoomOnTiles(GameMap* gameMap, RoomType type, Player* player, const std::vector<Tile*>& tiles)
430 {
431     std::vector<const RoomFactory*>& factories = getFactories();
432     uint32_t index = static_cast<uint32_t>(type);
433     if(index >= factories.size())
434     {
435         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
436         return false;
437     }
438 
439     const RoomFactory& factory = *factories[index];
440     return factory.buildRoomOnTiles(gameMap, player, tiles);
441 }
442 
getRoomNameFromRoomType(RoomType type)443 const std::string& RoomManager::getRoomNameFromRoomType(RoomType type)
444 {
445     std::vector<const RoomFactory*>& factories = getFactories();
446     uint32_t index = static_cast<uint32_t>(type);
447     if(index >= factories.size())
448     {
449         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
450         return EMPTY_STRING;
451     }
452 
453     const RoomFactory& factory = *factories[index];
454     return factory.getName();
455 }
456 
getRoomReadableName(RoomType type)457 const std::string& RoomManager::getRoomReadableName(RoomType type)
458 {
459     std::vector<const RoomFactory*>& factories = getFactories();
460     uint32_t index = static_cast<uint32_t>(type);
461     if(index >= factories.size())
462     {
463         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
464         return EMPTY_STRING;
465     }
466 
467     const RoomFactory& factory = *factories[index];
468     return factory.getNameReadable();
469 }
470 
getRoomTypeFromRoomName(const std::string & name)471 RoomType RoomManager::getRoomTypeFromRoomName(const std::string& name)
472 {
473     std::vector<const RoomFactory*>& factories = getFactories();
474     for(const RoomFactory* factory : factories)
475     {
476         if(factory == nullptr)
477             continue;
478 
479         if(factory->getName().compare(name) != 0)
480             continue;
481 
482         return factory->getRoomType();
483     }
484 
485     OD_LOG_ERR("Cannot find Room name=" + name);
486     return RoomType::nullRoomType;
487 }
488 
checkSellRoomTiles(GameMap * gameMap,const InputManager & inputManager,InputCommand & inputCommand)489 void RoomManager::checkSellRoomTiles(GameMap* gameMap, const InputManager& inputManager, InputCommand& inputCommand)
490 {
491     Player* player = gameMap->getLocalPlayer();
492     if(inputManager.mCommandState == InputCommandState::infoOnly)
493     {
494         // We do not differentiate between room and trap (because there is no way to know on client side).
495         // Note that price = 0 doesn't mean that the building is not a room
496         Tile* tile = gameMap->getTile(inputManager.mXPos, inputManager.mYPos);
497         if((tile == nullptr) || (!tile->getIsRoom()) || (tile->getSeat() != player->getSeat()))
498         {
499             std::string txt = formatSellRoom(0);
500             inputCommand.displayText(Ogre::ColourValue::White, txt);
501             inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos, inputManager.mYPos);
502             return;
503         }
504 
505         uint32_t price = tile->getRefundPriceRoom();
506         std::string txt = formatSellRoom(price);
507         inputCommand.displayText(Ogre::ColourValue::White, txt);
508         std::vector<Tile*> tiles;
509         tiles.push_back(tile);
510         inputCommand.selectTiles(tiles);
511         return;
512     }
513 
514     std::vector<Tile*> sellTiles;
515     std::vector<Tile*> tiles = gameMap->rectangularRegion(inputManager.mXPos,
516         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY);
517     uint32_t priceTotal = 0;
518     for(Tile* tile : tiles)
519     {
520         if(!tile->getIsRoom())
521             continue;
522 
523         if(tile->getSeat() != player->getSeat())
524             continue;
525 
526         sellTiles.push_back(tile);
527         priceTotal += tile->getRefundPriceRoom();
528     }
529 
530     if(inputManager.mCommandState == InputCommandState::building)
531     {
532         inputCommand.selectTiles(sellTiles);
533         std::string txt = formatSellRoom(priceTotal);
534         inputCommand.displayText(Ogre::ColourValue::White, txt);
535         return;
536     }
537 
538     inputCommand.unselectAllTiles();
539 
540     ClientNotification *clientNotification = new ClientNotification(
541         ClientNotificationType::askSellRoomTiles);
542     uint32_t nbTiles = sellTiles.size();
543     clientNotification->mPacket << nbTiles;
544     for(Tile* tile : sellTiles)
545         gameMap->tileToPacket(clientNotification->mPacket, tile);
546 
547     ODClient::getSingleton().queueClientNotification(clientNotification);
548 }
549 
sellRoomTiles(GameMap * gameMap,Player * player,ODPacket & packet)550 void RoomManager::sellRoomTiles(GameMap* gameMap, Player* player, ODPacket& packet)
551 {
552     uint32_t nbTiles;
553     OD_ASSERT_TRUE(packet >> nbTiles);
554     int32_t price = 0;
555     std::set<Room*> rooms;
556     std::vector<Tile*> tiles;
557     while(nbTiles > 0)
558     {
559         --nbTiles;
560         Tile* tile = gameMap->tileFromPacket(packet);
561         if(tile == nullptr)
562         {
563             OD_LOG_ERR("tile=" + Tile::displayAsString(tile));
564             continue;
565         }
566         Room* room = tile->getCoveringRoom();
567         if(room == nullptr)
568             continue;
569 
570         if(!room->canSeatSellBuilding(player->getSeat()))
571             continue;
572 
573         if(!room->removeCoveredTile(tile))
574         {
575             OD_LOG_ERR("room=" + room->getName() + ", tile=" + Tile::displayAsString(tile) + ", seatId=" + Helper::toString(player->getSeat()->getId()));
576             continue;
577         }
578 
579         price += costPerTile(room->getType()) / 2;
580         tiles.push_back(tile);
581         rooms.insert(room);
582     }
583 
584     gameMap->addGoldToSeat(price, player->getSeat()->getId());
585 
586     // We notify the clients with vision of the changed tiles. Note that we need
587     // to calculate per seat since the could have vision on different parts of the building
588     std::map<Seat*,std::vector<Tile*>> tilesPerSeat;
589     const std::vector<Seat*>& seats = gameMap->getSeats();
590     for(Seat* seat : seats)
591     {
592         if(seat->getPlayer() == nullptr)
593             continue;
594         if(!seat->getPlayer()->getIsHuman())
595             continue;
596 
597         for(Tile* tile : tiles)
598         {
599             if(!seat->hasVisionOnTile(tile))
600                 continue;
601 
602             tile->changeNotifiedForSeat(seat);
603             tilesPerSeat[seat].push_back(tile);
604         }
605     }
606 
607     for(const std::pair<Seat* const,std::vector<Tile*>>& p : tilesPerSeat)
608     {
609         uint32_t nbTiles = p.second.size();
610         ServerNotification serverNotification(
611             ServerNotificationType::refreshTiles, p.first->getPlayer());
612         serverNotification.mPacket << nbTiles;
613         for(Tile* tile : p.second)
614         {
615             gameMap->tileToPacket(serverNotification.mPacket, tile);
616             p.first->updateTileStateForSeat(tile, false);
617             tile->exportToPacketForUpdate(serverNotification.mPacket, p.first);
618         }
619         ODServer::getSingleton().sendAsyncMsg(serverNotification);
620     }
621 
622     // We update active spots of each impacted rooms
623     for(Room* room : rooms)
624         room->updateActiveSpots();
625 }
626 
formatSellRoom(int price)627 std::string RoomManager::formatSellRoom(int price)
628 {
629     return "retrieve " + Helper::toString(price) + " gold";
630 }
631 
checkSellRoomTilesEditor(GameMap * gameMap,const InputManager & inputManager,InputCommand & inputCommand)632 void RoomManager::checkSellRoomTilesEditor(GameMap* gameMap, const InputManager& inputManager, InputCommand& inputCommand)
633 {
634     if(inputManager.mCommandState == InputCommandState::infoOnly)
635     {
636         // We do not differentiate between room and trap (because there is no way to know on client side).
637         // Note that price = 0 doesn't mean that the building is not a room
638         Tile* tile = gameMap->getTile(inputManager.mXPos, inputManager.mYPos);
639         if((tile == nullptr) || (!tile->getIsRoom()))
640         {
641             inputCommand.displayText(Ogre::ColourValue::White, "Remove tiles");
642             inputCommand.selectSquaredTiles(inputManager.mXPos, inputManager.mYPos, inputManager.mXPos, inputManager.mYPos);
643             return;
644         }
645 
646         inputCommand.displayText(Ogre::ColourValue::White, "Remove tiles");
647         std::vector<Tile*> tiles;
648         tiles.push_back(tile);
649         inputCommand.selectTiles(tiles);
650         return;
651     }
652 
653     std::vector<Tile*> sellTiles;
654     std::vector<Tile*> tiles = gameMap->rectangularRegion(inputManager.mXPos,
655         inputManager.mYPos, inputManager.mLStartDragX, inputManager.mLStartDragY);
656     for(Tile* tile : tiles)
657     {
658         if(!tile->getIsRoom())
659             continue;
660 
661         sellTiles.push_back(tile);
662     }
663 
664     if(inputManager.mCommandState == InputCommandState::building)
665     {
666         inputCommand.selectTiles(sellTiles);
667         inputCommand.displayText(Ogre::ColourValue::White, "Remove tiles");
668         return;
669     }
670 
671     inputCommand.unselectAllTiles();
672 
673     ClientNotification *clientNotification = new ClientNotification(
674         ClientNotificationType::editorAskDestroyRoomTiles);
675     uint32_t nbTiles = sellTiles.size();
676     clientNotification->mPacket << nbTiles;
677     for(Tile* tile : sellTiles)
678         gameMap->tileToPacket(clientNotification->mPacket, tile);
679 
680     ODClient::getSingleton().queueClientNotification(clientNotification);
681 }
682 
sellRoomTilesEditor(GameMap * gameMap,ODPacket & packet)683 void RoomManager::sellRoomTilesEditor(GameMap* gameMap, ODPacket& packet)
684 {
685     uint32_t nbTiles;
686     OD_ASSERT_TRUE(packet >> nbTiles);
687     std::set<Room*> rooms;
688     std::vector<Tile*> tiles;
689     while(nbTiles > 0)
690     {
691         --nbTiles;
692         Tile* tile = gameMap->tileFromPacket(packet);
693         if(tile == nullptr)
694         {
695             OD_LOG_ERR("tile=" + Tile::displayAsString(tile));
696             continue;
697         }
698         Room* room = tile->getCoveringRoom();
699         if(room == nullptr)
700             continue;
701 
702         if(!room->removeCoveredTile(tile))
703         {
704             OD_LOG_ERR("room=" + room->getName() + ", tile=" + Tile::displayAsString(tile));
705             continue;
706         }
707 
708         tiles.push_back(tile);
709         rooms.insert(room);
710     }
711 
712     // We notify the clients with vision of the changed tiles. Note that we need
713     // to calculate per seat since the could have vision on different parts of the building
714     std::map<Seat*,std::vector<Tile*>> tilesPerSeat;
715     const std::vector<Seat*>& seats = gameMap->getSeats();
716     for(Seat* seat : seats)
717     {
718         if(seat->getPlayer() == nullptr)
719             continue;
720         if(!seat->getPlayer()->getIsHuman())
721             continue;
722 
723         for(Tile* tile : tiles)
724         {
725             if(!seat->hasVisionOnTile(tile))
726                 continue;
727 
728             tile->changeNotifiedForSeat(seat);
729             tilesPerSeat[seat].push_back(tile);
730         }
731     }
732 
733     for(const std::pair<Seat* const,std::vector<Tile*>>& p : tilesPerSeat)
734     {
735         uint32_t nbTiles = p.second.size();
736         ServerNotification serverNotification(
737             ServerNotificationType::refreshTiles, p.first->getPlayer());
738         serverNotification.mPacket << nbTiles;
739         for(Tile* tile : p.second)
740         {
741             gameMap->tileToPacket(serverNotification.mPacket, tile);
742             p.first->updateTileStateForSeat(tile, false);
743             tile->exportToPacketForUpdate(serverNotification.mPacket, p.first);
744         }
745         ODServer::getSingleton().sendAsyncMsg(serverNotification);
746     }
747 
748     // We update active spots of each impacted rooms
749     for(Room* room : rooms)
750         room->updateActiveSpots();
751 }
752 
costPerTile(RoomType type)753 int RoomManager::costPerTile(RoomType type)
754 {
755     std::vector<const RoomFactory*>& factories = getFactories();
756     uint32_t index = static_cast<uint32_t>(type);
757     if(index >= factories.size())
758     {
759         OD_LOG_ERR("type=" + Helper::toString(index) + ", factories.size=" + Helper::toString(factories.size()));
760         return false;
761     }
762 
763     const RoomFactory& factory = *factories[index];
764     return factory.getCostPerTile();
765 }
766 
createRoomClientNotification(RoomType type)767 ClientNotification* RoomManager::createRoomClientNotification(RoomType type)
768 {
769     ClientNotification *clientNotification = new ClientNotification(ClientNotificationType::askBuildRoom);
770     clientNotification->mPacket << type;
771     return clientNotification;
772 }
773 
createRoomClientNotificationEditor(RoomType type)774 ClientNotification* RoomManager::createRoomClientNotificationEditor(RoomType type)
775 {
776     ClientNotification *clientNotification = new ClientNotification(ClientNotificationType::editorAskBuildRoom);
777     clientNotification->mPacket << type;
778     return clientNotification;
779 }
780