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