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