Advertisement
Guest User

Untitled

a guest
May 20th, 2019
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 125.14 KB | None | 0 0
  1. /**
  2. * The Forgotten Server - a free and open-source MMORPG server emulator
  3. * Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. */
  19.  
  20. #include "otpch.h"
  21.  
  22. #include "pugicast.h"
  23.  
  24. #include "items.h"
  25. #include "commands.h"
  26. #include "creature.h"
  27. #include "monster.h"
  28. #include "game.h"
  29. #include "actions.h"
  30. #include "iologindata.h"
  31. #include "talkaction.h"
  32. #include "spells.h"
  33. #include "configmanager.h"
  34. #include "server.h"
  35. #include "globalevent.h"
  36. #include "bed.h"
  37. #include "scheduler.h"
  38. #include "events.h"
  39. #include "databasetasks.h"
  40.  
  41. extern ConfigManager g_config;
  42. extern Actions* g_actions;
  43. extern Chat* g_chat;
  44. extern TalkActions* g_talkActions;
  45. extern Spells* g_spells;
  46. extern Vocations g_vocations;
  47. extern GlobalEvents* g_globalEvents;
  48. extern Events* g_events;
  49.  
  50. Game::Game() :
  51. wildcardTree(false)
  52. {
  53. gameState = GAME_STATE_NORMAL;
  54. worldType = WORLD_TYPE_PVP;
  55.  
  56. serviceManager = nullptr;
  57. lastStageLevel = 0;
  58. playersRecord = 0;
  59. motdNum = 0;
  60. useLastStageLevel = false;
  61. stagesEnabled = false;
  62.  
  63. lastBucket = 0;
  64.  
  65. //(1440 minutes/day)/(3600 seconds/day)*10 seconds event interval
  66. int32_t dayCycle = 3600;
  67. lightHourDelta = 1440 * 10 / dayCycle;
  68. lightHour = SUNRISE + (SUNSET - SUNRISE) / 2;
  69. lightLevel = LIGHT_LEVEL_DAY;
  70. lightState = LIGHT_STATE_DAY;
  71. }
  72.  
  73. Game::~Game()
  74. {
  75. for (const auto& it : guilds) {
  76. delete it.second;
  77. }
  78. }
  79.  
  80. void Game::start(ServiceManager* manager)
  81. {
  82. serviceManager = manager;
  83.  
  84. g_scheduler.addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, std::bind(&Game::checkLight, this)));
  85. g_scheduler.addEvent(createSchedulerTask(EVENT_CREATURE_THINK_INTERVAL, std::bind(&Game::checkCreatures, this, 0)));
  86. g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this)));
  87. }
  88.  
  89. GameState_t Game::getGameState() const
  90. {
  91. return gameState;
  92. }
  93.  
  94. void Game::setWorldType(WorldType_t type)
  95. {
  96. worldType = type;
  97. }
  98.  
  99. void Game::setGameState(GameState_t newState)
  100. {
  101. if (gameState == GAME_STATE_SHUTDOWN) {
  102. return; //this cannot be stopped
  103. }
  104.  
  105. if (gameState == newState) {
  106. return;
  107. }
  108.  
  109. gameState = newState;
  110. switch (newState) {
  111. case GAME_STATE_INIT: {
  112. commands.loadFromXml();
  113.  
  114. loadExperienceStages();
  115.  
  116. groups.load();
  117. g_chat->load();
  118.  
  119. map.spawns.startup();
  120.  
  121. raids.loadFromXml();
  122. raids.startup();
  123.  
  124. quests.loadFromXml();
  125.  
  126. loadMotdNum();
  127. loadPlayersRecord();
  128.  
  129. g_globalEvents->startup();
  130. break;
  131. }
  132.  
  133. case GAME_STATE_SHUTDOWN: {
  134. g_globalEvents->execute(GLOBALEVENT_SHUTDOWN);
  135.  
  136. //kick all players that are still online
  137. auto it = players.begin();
  138. while (it != players.end()) {
  139. it->second->kickPlayer(true);
  140. it = players.begin();
  141. }
  142.  
  143. saveMotdNum();
  144. saveGameState();
  145.  
  146. g_dispatcher.addTask(
  147. createTask(std::bind(&Game::shutdown, this)));
  148.  
  149. g_scheduler.stop();
  150. g_databaseTasks.stop();
  151. g_dispatcher.stop();
  152. break;
  153. }
  154.  
  155. case GAME_STATE_CLOSED: {
  156. /* kick all players without the CanAlwaysLogin flag */
  157. auto it = players.begin();
  158. while (it != players.end()) {
  159. if (!it->second->hasFlag(PlayerFlag_CanAlwaysLogin)) {
  160. it->second->kickPlayer(true);
  161. it = players.begin();
  162. } else {
  163. ++it;
  164. }
  165. }
  166.  
  167. saveGameState();
  168. break;
  169. }
  170.  
  171. default:
  172. break;
  173. }
  174. }
  175.  
  176. void Game::saveGameState()
  177. {
  178. if (gameState == GAME_STATE_NORMAL) {
  179. setGameState(GAME_STATE_MAINTAIN);
  180. }
  181.  
  182. std::cout << "Saving server..." << std::endl;
  183.  
  184. for (const auto& it : players) {
  185. it.second->loginPosition = it.second->getPosition();
  186. IOLoginData::savePlayer(it.second);
  187. }
  188.  
  189. Map::save();
  190.  
  191. if (gameState == GAME_STATE_MAINTAIN) {
  192. setGameState(GAME_STATE_NORMAL);
  193. }
  194. }
  195.  
  196. bool Game::loadMainMap(const std::string& filename)
  197. {
  198. Monster::despawnRange = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRANGE);
  199. Monster::despawnRadius = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRADIUS);
  200. return map.loadMap("data/world/" + filename + ".otbm", true);
  201. }
  202.  
  203. void Game::loadMap(const std::string& path)
  204. {
  205. map.loadMap(path, false);
  206. }
  207.  
  208. Cylinder* Game::internalGetCylinder(Player* player, const Position& pos) const
  209. {
  210. if (pos.x != 0xFFFF) {
  211. return map.getTile(pos);
  212. }
  213.  
  214. //container
  215. if (pos.y & 0x40) {
  216. uint8_t from_cid = pos.y & 0x0F;
  217. return player->getContainerByID(from_cid);
  218. }
  219.  
  220. //inventory
  221. return player;
  222. }
  223.  
  224. Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index, uint32_t spriteId, stackPosType_t type) const
  225. {
  226. if (pos.x != 0xFFFF) {
  227. Tile* tile = map.getTile(pos);
  228. if (!tile) {
  229. return nullptr;
  230. }
  231.  
  232. Thing* thing;
  233. switch (type) {
  234. case STACKPOS_LOOK: {
  235. return tile->getTopVisibleThing(player);
  236. }
  237.  
  238. case STACKPOS_MOVE: {
  239. Item* item = tile->getTopDownItem();
  240. if (item && item->isMoveable()) {
  241. thing = item;
  242. } else {
  243. thing = tile->getTopVisibleCreature(player);
  244. }
  245. break;
  246. }
  247.  
  248. case STACKPOS_USEITEM: {
  249. thing = tile->getUseItem();
  250. break;
  251. }
  252.  
  253. case STACKPOS_TOPDOWN_ITEM: {
  254. thing = tile->getTopDownItem();
  255. break;
  256. }
  257.  
  258. case STACKPOS_USETARGET: {
  259. thing = tile->getTopVisibleCreature(player);
  260. if (!thing) {
  261. thing = tile->getUseItem();
  262. }
  263. break;
  264. }
  265.  
  266. default: {
  267. thing = nullptr;
  268. break;
  269. }
  270. }
  271.  
  272. if (player && tile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
  273. //do extra checks here if the thing is accessable
  274. if (thing && thing->getItem()) {
  275. if (tile->hasProperty(CONST_PROP_ISVERTICAL)) {
  276. if (player->getPosition().x + 1 == tile->getPosition().x) {
  277. thing = nullptr;
  278. }
  279. } else { // horizontal
  280. if (player->getPosition().y + 1 == tile->getPosition().y) {
  281. thing = nullptr;
  282. }
  283. }
  284. }
  285. }
  286. return thing;
  287. }
  288.  
  289. //container
  290. if (pos.y & 0x40) {
  291. uint8_t fromCid = pos.y & 0x0F;
  292.  
  293. Container* parentContainer = player->getContainerByID(fromCid);
  294. if (!parentContainer) {
  295. return nullptr;
  296. }
  297.  
  298. uint8_t slot = pos.z;
  299. return parentContainer->getItemByIndex(player->getContainerIndex(fromCid) + slot);
  300. } else if (pos.y == 0 && pos.z == 0) {
  301. const ItemType& it = Item::items.getItemIdByClientId(spriteId);
  302. if (it.id == 0) {
  303. return nullptr;
  304. }
  305.  
  306. int32_t subType;
  307. if (it.isFluidContainer() && index < static_cast<int32_t>(sizeof(reverseFluidMap) / sizeof(uint8_t))) {
  308. subType = reverseFluidMap[index];
  309. } else {
  310. subType = -1;
  311. }
  312.  
  313. return findItemOfType(player, it.id, true, subType);
  314. }
  315.  
  316. //inventory
  317. slots_t slot = static_cast<slots_t>(pos.y);
  318. return player->getInventoryItem(slot);
  319. }
  320.  
  321. void Game::internalGetPosition(Item* item, Position& pos, uint8_t& stackpos)
  322. {
  323. pos.x = 0;
  324. pos.y = 0;
  325. pos.z = 0;
  326. stackpos = 0;
  327.  
  328. Cylinder* topParent = item->getTopParent();
  329. if (topParent) {
  330. if (Player* player = dynamic_cast<Player*>(topParent)) {
  331. pos.x = 0xFFFF;
  332.  
  333. Container* container = dynamic_cast<Container*>(item->getParent());
  334. if (container) {
  335. pos.y = static_cast<uint16_t>(0x40) | static_cast<uint16_t>(player->getContainerID(container));
  336. pos.z = container->getThingIndex(item);
  337. stackpos = pos.z;
  338. } else {
  339. pos.y = player->getThingIndex(item);
  340. stackpos = pos.y;
  341. }
  342. } else if (Tile* tile = topParent->getTile()) {
  343. pos = tile->getPosition();
  344. stackpos = tile->getThingIndex(item);
  345. }
  346. }
  347. }
  348.  
  349. Creature* Game::getCreatureByID(uint32_t id)
  350. {
  351. if (id <= Player::playerAutoID) {
  352. return getPlayerByID(id);
  353. } else if (id <= Monster::monsterAutoID) {
  354. return getMonsterByID(id);
  355. } else if (id <= Npc::npcAutoID) {
  356. return getNpcByID(id);
  357. }
  358. return nullptr;
  359. }
  360.  
  361. Monster* Game::getMonsterByID(uint32_t id)
  362. {
  363. if (id == 0) {
  364. return nullptr;
  365. }
  366.  
  367. auto it = monsters.find(id);
  368. if (it == monsters.end()) {
  369. return nullptr;
  370. }
  371. return it->second;
  372. }
  373.  
  374. Npc* Game::getNpcByID(uint32_t id)
  375. {
  376. if (id == 0) {
  377. return nullptr;
  378. }
  379.  
  380. auto it = npcs.find(id);
  381. if (it == npcs.end()) {
  382. return nullptr;
  383. }
  384. return it->second;
  385. }
  386.  
  387. Player* Game::getPlayerByID(uint32_t id)
  388. {
  389. if (id == 0) {
  390. return nullptr;
  391. }
  392.  
  393. auto it = players.find(id);
  394. if (it == players.end()) {
  395. return nullptr;
  396. }
  397. return it->second;
  398. }
  399.  
  400. Creature* Game::getCreatureByName(const std::string& s)
  401. {
  402. if (s.empty()) {
  403. return nullptr;
  404. }
  405.  
  406. const std::string& lowerCaseName = asLowerCaseString(s);
  407.  
  408. auto m_it = mappedPlayerNames.find(lowerCaseName);
  409. if (m_it != mappedPlayerNames.end()) {
  410. return m_it->second;
  411. }
  412.  
  413. for (const auto& it : npcs) {
  414. if (lowerCaseName == asLowerCaseString(it.second->getName())) {
  415. return it.second;
  416. }
  417. }
  418.  
  419. for (const auto& it : monsters) {
  420. if (lowerCaseName == asLowerCaseString(it.second->getName())) {
  421. return it.second;
  422. }
  423. }
  424. return nullptr;
  425. }
  426.  
  427. Npc* Game::getNpcByName(const std::string& s)
  428. {
  429. if (s.empty()) {
  430. return nullptr;
  431. }
  432.  
  433. const char* npcName = s.c_str();
  434. for (const auto& it : npcs) {
  435. if (strcasecmp(npcName, it.second->getName().c_str()) == 0) {
  436. return it.second;
  437. }
  438. }
  439. return nullptr;
  440. }
  441.  
  442. Player* Game::getPlayerByName(const std::string& s)
  443. {
  444. if (s.empty()) {
  445. return nullptr;
  446. }
  447.  
  448. auto it = mappedPlayerNames.find(asLowerCaseString(s));
  449. if (it == mappedPlayerNames.end()) {
  450. return nullptr;
  451. }
  452. return it->second;
  453. }
  454.  
  455. Player* Game::getPlayerByGUID(const uint32_t& guid)
  456. {
  457. if (guid == 0) {
  458. return nullptr;
  459. }
  460.  
  461. for (const auto& it : players) {
  462. if (guid == it.second->getGUID()) {
  463. return it.second;
  464. }
  465. }
  466. return nullptr;
  467. }
  468.  
  469. ReturnValue Game::getPlayerByNameWildcard(const std::string& s, Player*& player)
  470. {
  471. size_t strlen = s.length();
  472. if (strlen == 0 || strlen > 20) {
  473. return RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE;
  474. }
  475.  
  476. if (s.back() == '~') {
  477. const std::string& query = asLowerCaseString(s.substr(0, strlen - 1));
  478. std::string result;
  479. ReturnValue ret = wildcardTree.findOne(query, result);
  480. if (ret != RETURNVALUE_NOERROR) {
  481. return ret;
  482. }
  483.  
  484. player = getPlayerByName(result);
  485. } else {
  486. player = getPlayerByName(s);
  487. }
  488.  
  489. if (!player) {
  490. return RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE;
  491. }
  492.  
  493. return RETURNVALUE_NOERROR;
  494. }
  495.  
  496. Player* Game::getPlayerByAccount(uint32_t acc)
  497. {
  498. for (const auto& it : players) {
  499. if (it.second->getAccount() == acc) {
  500. return it.second;
  501. }
  502. }
  503. return nullptr;
  504. }
  505.  
  506. bool Game::internalPlaceCreature(Creature* creature, const Position& pos, bool extendedPos /*=false*/, bool forced /*= false*/)
  507. {
  508. if (creature->getParent() != nullptr) {
  509. return false;
  510. }
  511.  
  512. if (!map.placeCreature(pos, creature, extendedPos, forced)) {
  513. return false;
  514. }
  515.  
  516. creature->incrementReferenceCounter();
  517. creature->setID();
  518. creature->addList();
  519. return true;
  520. }
  521.  
  522. bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedPos /*=false*/, bool forced /*= false*/)
  523. {
  524. if (!internalPlaceCreature(creature, pos, extendedPos, forced)) {
  525. return false;
  526. }
  527.  
  528. SpectatorVec list;
  529. map.getSpectators(list, creature->getPosition(), true);
  530. for (Creature* spectator : list) {
  531. if (Player* tmpPlayer = spectator->getPlayer()) {
  532. tmpPlayer->sendCreatureAppear(creature, creature->getPosition(), true);
  533. }
  534. }
  535.  
  536. for (Creature* spectator : list) {
  537. spectator->onCreatureAppear(creature, true);
  538. }
  539.  
  540. creature->getParent()->postAddNotification(creature, nullptr, 0);
  541.  
  542. addCreatureCheck(creature);
  543. creature->onPlacedCreature();
  544. return true;
  545. }
  546.  
  547. bool Game::removeCreature(Creature* creature, bool isLogout/* = true*/)
  548. {
  549. if (creature->isRemoved()) {
  550. return false;
  551. }
  552.  
  553. Tile* tile = creature->getTile();
  554.  
  555. std::vector<int32_t> oldStackPosVector;
  556.  
  557. SpectatorVec list;
  558. map.getSpectators(list, tile->getPosition(), true);
  559. for (Creature* spectator : list) {
  560. if (Player* player = spectator->getPlayer()) {
  561. oldStackPosVector.push_back(player->canSeeCreature(creature) ? tile->getStackposOfCreature(player, creature) : -1);
  562. }
  563. }
  564.  
  565. tile->removeCreature(creature);
  566.  
  567. const Position& tilePosition = tile->getPosition();
  568.  
  569. //send to client
  570. size_t i = 0;
  571. for (Creature* spectator : list) {
  572. if (Player* player = spectator->getPlayer()) {
  573. player->sendRemoveTileThing(tilePosition, oldStackPosVector[i++]);
  574. }
  575. }
  576.  
  577. //event method
  578. for (Creature* spectator : list) {
  579. spectator->onRemoveCreature(creature, isLogout);
  580. }
  581.  
  582. creature->getParent()->postRemoveNotification(creature, nullptr, 0);
  583.  
  584. creature->removeList();
  585. creature->setRemoved();
  586. ReleaseCreature(creature);
  587.  
  588. removeCreatureCheck(creature);
  589.  
  590. for (Creature* summon : creature->summons) {
  591. summon->setLossSkill(false);
  592. removeCreature(summon);
  593. }
  594. return true;
  595. }
  596.  
  597. void Game::playerMoveThing(uint32_t playerId, const Position& fromPos,
  598. uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count)
  599. {
  600. Player* player = getPlayerByID(playerId);
  601. if (!player) {
  602. return;
  603. }
  604.  
  605. uint8_t fromIndex = 0;
  606. if (fromPos.x == 0xFFFF) {
  607. if (fromPos.y & 0x40) {
  608. fromIndex = fromPos.z;
  609. } else {
  610. fromIndex = static_cast<uint8_t>(fromPos.y);
  611. }
  612. } else {
  613. fromIndex = fromStackPos;
  614. }
  615.  
  616. Thing* thing = internalGetThing(player, fromPos, fromIndex, 0, STACKPOS_MOVE);
  617. if (!thing) {
  618. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  619. return;
  620. }
  621.  
  622. if (Creature* movingCreature = thing->getCreature()) {
  623. Tile* tile = map.getTile(toPos);
  624. if (!tile) {
  625. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  626. return;
  627. }
  628.  
  629. if (Position::areInRange<1, 1, 0>(movingCreature->getPosition(), player->getPosition())) {
  630. SchedulerTask* task = createSchedulerTask(1000,
  631. std::bind(&Game::playerMoveCreatureByID, this, player->getID(),
  632. movingCreature->getID(), movingCreature->getPosition(), tile->getPosition()));
  633. player->setNextActionTask(task);
  634. } else {
  635. playerMoveCreature(player, movingCreature, movingCreature->getPosition(), tile);
  636. }
  637. } else if (thing->getItem()) {
  638. Cylinder* toCylinder = internalGetCylinder(player, toPos);
  639. if (!toCylinder) {
  640. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  641. return;
  642. }
  643.  
  644. playerMoveItem(player, fromPos, spriteId, fromStackPos, toPos, count, thing->getItem(), toCylinder);
  645. }
  646. }
  647.  
  648. void Game::playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, const Position& movingCreatureOrigPos, const Position& toPos)
  649. {
  650. Player* player = getPlayerByID(playerId);
  651. if (!player) {
  652. return;
  653. }
  654.  
  655. Creature* movingCreature = getCreatureByID(movingCreatureId);
  656. if (!movingCreature) {
  657. return;
  658. }
  659.  
  660. Tile* toTile = map.getTile(toPos);
  661. if (!toTile) {
  662. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  663. return;
  664. }
  665.  
  666. playerMoveCreature(player, movingCreature, movingCreatureOrigPos, toTile);
  667. }
  668.  
  669. void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile)
  670. {
  671. if (!player->canDoAction()) {
  672. uint32_t delay = player->getNextActionTime();
  673. SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()));
  674. player->setNextActionTask(task);
  675. return;
  676. }
  677.  
  678. player->setNextActionTask(nullptr);
  679.  
  680. if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) {
  681. //need to walk to the creature first before moving it
  682. std::forward_list<Direction> listDir;
  683. if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) {
  684. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
  685. SchedulerTask* task = createSchedulerTask(1500, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition())); //1500 push
  686. player->setNextWalkActionTask(task);
  687. } else {
  688. player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  689. }
  690. return;
  691. }
  692.  
  693. if ((!movingCreature->isPushable() && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) ||
  694. (movingCreature->isInGhostMode() && !player->isAccessPlayer())) {
  695. player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
  696. return;
  697. }
  698.  
  699. //check throw distance
  700. const Position& movingCreaturePos = movingCreature->getPosition();
  701. const Position& toPos = toTile->getPosition();
  702. if ((Position::getDistanceX(movingCreaturePos, toPos) > movingCreature->getThrowRange()) || (Position::getDistanceY(movingCreaturePos, toPos) > movingCreature->getThrowRange()) || (Position::getDistanceZ(movingCreaturePos, toPos) * 4 > movingCreature->getThrowRange())) {
  703. player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
  704. return;
  705. }
  706.  
  707. if (player != movingCreature) {
  708. if (toTile->hasFlag(TILESTATE_BLOCKPATH)) {
  709. player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
  710. return;
  711. } else if ((movingCreature->getZone() == ZONE_PROTECTION && !toTile->hasFlag(TILESTATE_PROTECTIONZONE)) || (movingCreature->getZone() == ZONE_NOPVP && !toTile->hasFlag(TILESTATE_NOPVPZONE))) {
  712. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  713. return;
  714. } else {
  715. if (CreatureVector* tileCreatures = toTile->getCreatures()) {
  716. for (Creature* tileCreature : *tileCreatures) {
  717. if (!tileCreature->isInGhostMode()) {
  718. player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
  719. return;
  720. }
  721. }
  722. }
  723.  
  724. Npc* movingNpc = movingCreature->getNpc();
  725. if (movingNpc && !Spawns::isInZone(movingNpc->getMasterPos(), movingNpc->getMasterRadius(), toPos)) {
  726. player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
  727. return;
  728. }
  729. }
  730. }
  731.  
  732. if (!g_events->eventPlayerOnMoveCreature(player, movingCreature, movingCreaturePos, toPos)) {
  733. return;
  734. }
  735.  
  736. ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
  737. if (ret != RETURNVALUE_NOERROR) {
  738. player->sendCancelMessage(ret);
  739. }
  740. }
  741.  
  742. ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, uint32_t flags /*= 0*/)
  743. {
  744. const Position& currentPos = creature->getPosition();
  745. Position destPos = getNextPosition(direction, currentPos);
  746.  
  747. bool diagonalMovement = (direction & DIRECTION_DIAGONAL_MASK) != 0;
  748. if (creature->getPlayer() && !diagonalMovement) {
  749. //try go up
  750. if (currentPos.z != 8 && creature->getTile()->hasHeight(3)) {
  751. Tile* tmpTile = map.getTile(currentPos.x, currentPos.y, currentPos.getZ() - 1);
  752. if (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID))) {
  753. tmpTile = map.getTile(destPos.x, destPos.y, destPos.getZ() - 1);
  754. if (tmpTile && tmpTile->getGround() && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)) {
  755. flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
  756.  
  757. if (!tmpTile->hasFlag(TILESTATE_FLOORCHANGE)) {
  758. destPos.z--;
  759. }
  760. }
  761. }
  762. } else {
  763. //try go down
  764. Tile* tmpTile = map.getTile(destPos.x, destPos.y, destPos.z);
  765. if (currentPos.z != 7 && (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)))) {
  766. tmpTile = map.getTile(destPos.x, destPos.y, destPos.z + 1);
  767. if (tmpTile && tmpTile->hasHeight(3)) {
  768. flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
  769. destPos.z++;
  770. }
  771. }
  772. }
  773. }
  774.  
  775. Tile* toTile = map.getTile(destPos);
  776. if (!toTile) {
  777. return RETURNVALUE_NOTPOSSIBLE;
  778. }
  779. return internalMoveCreature(*creature, *toTile, flags);
  780. }
  781.  
  782. ReturnValue Game::internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags /*= 0*/)
  783. {
  784. //check if we can move the creature to the destination
  785. ReturnValue ret = toTile.queryAdd(0, creature, 1, flags);
  786. if (ret != RETURNVALUE_NOERROR) {
  787. return ret;
  788. }
  789.  
  790. map.moveCreature(creature, toTile);
  791. if (creature.getParent() != &toTile) {
  792. return RETURNVALUE_NOERROR;
  793. }
  794.  
  795. int32_t index = 0;
  796. Item* toItem = nullptr;
  797. Tile* subCylinder = nullptr;
  798. Tile* toCylinder = &toTile;
  799. Tile* fromCylinder = nullptr;
  800. uint32_t n = 0;
  801.  
  802. while ((subCylinder = toCylinder->queryDestination(index, creature, &toItem, flags)) != toCylinder) {
  803. map.moveCreature(creature, *subCylinder);
  804.  
  805. if (creature.getParent() != subCylinder) {
  806. //could happen if a script move the creature
  807. fromCylinder = nullptr;
  808. break;
  809. }
  810.  
  811. fromCylinder = toCylinder;
  812. toCylinder = subCylinder;
  813. flags = 0;
  814.  
  815. //to prevent infinite loop
  816. if (++n >= MAP_MAX_LAYERS) {
  817. break;
  818. }
  819. }
  820.  
  821. if (fromCylinder) {
  822. const Position& fromPosition = fromCylinder->getPosition();
  823. const Position& toPosition = toCylinder->getPosition();
  824. if (fromPosition.z != toPosition.z && (fromPosition.x != toPosition.x || fromPosition.y != toPosition.y)) {
  825. Direction dir = getDirectionTo(fromPosition, toPosition);
  826. if ((dir & DIRECTION_DIAGONAL_MASK) == 0) {
  827. internalCreatureTurn(&creature, dir);
  828. }
  829. }
  830. }
  831.  
  832. return RETURNVALUE_NOERROR;
  833. }
  834.  
  835. void Game::playerMoveItemByPlayerID(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count)
  836. {
  837. Player* player = getPlayerByID(playerId);
  838. if (!player) {
  839. return;
  840. }
  841. playerMoveItem(player, fromPos, spriteId, fromStackPos, toPos, count, nullptr, nullptr);
  842. }
  843.  
  844. void Game::playerMoveItem(Player* player, const Position& fromPos,
  845. uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count, Item* item, Cylinder* toCylinder)
  846. {
  847. if (!player->canDoAction()) {
  848. uint32_t delay = player->getNextActionTime();
  849. SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveItemByPlayerID, this,
  850. player->getID(), fromPos, spriteId, fromStackPos, toPos, count));
  851. player->setNextActionTask(task);
  852. return;
  853. }
  854.  
  855. player->setNextActionTask(nullptr);
  856.  
  857. if (item == nullptr) {
  858. uint8_t fromIndex = 0;
  859. if (fromPos.x == 0xFFFF) {
  860. if (fromPos.y & 0x40) {
  861. fromIndex = fromPos.z;
  862. } else {
  863. fromIndex = static_cast<uint8_t>(fromPos.y);
  864. }
  865. } else {
  866. fromIndex = fromStackPos;
  867. }
  868.  
  869. Thing* thing = internalGetThing(player, fromPos, fromIndex, 0, STACKPOS_MOVE);
  870. if (!thing || !thing->getItem()) {
  871. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  872. return;
  873. }
  874.  
  875. item = thing->getItem();
  876. }
  877.  
  878. if (item->getClientID() != spriteId) {
  879. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  880. return;
  881. }
  882.  
  883. Cylinder* fromCylinder = internalGetCylinder(player, fromPos);
  884. if (fromCylinder == nullptr) {
  885. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  886. return;
  887. }
  888.  
  889. if (toCylinder == nullptr) {
  890. toCylinder = internalGetCylinder(player, toPos);
  891. if (toCylinder == nullptr) {
  892. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  893. return;
  894. }
  895. }
  896.  
  897. if (!item->isPushable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  898. player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
  899. return;
  900. }
  901.  
  902. const Position& playerPos = player->getPosition();
  903. const Position& mapFromPos = fromCylinder->getTile()->getPosition();
  904. if (playerPos.z != mapFromPos.z) {
  905. player->sendCancelMessage(playerPos.z > mapFromPos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS);
  906. return;
  907. }
  908.  
  909. if (!Position::areInRange<1, 1>(playerPos, mapFromPos)) {
  910. //need to walk to the item first before using it
  911. std::forward_list<Direction> listDir;
  912. if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) {
  913. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  914. this, player->getID(), listDir)));
  915.  
  916. SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this,
  917. player->getID(), fromPos, spriteId, fromStackPos, toPos, count));
  918. player->setNextWalkActionTask(task);
  919. } else {
  920. player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  921. }
  922. return;
  923. }
  924.  
  925. const Tile* toCylinderTile = toCylinder->getTile();
  926. const Position& mapToPos = toCylinderTile->getPosition();
  927.  
  928. //hangable item specific code
  929. if (item->isHangable() && toCylinderTile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
  930. //destination supports hangable objects so need to move there first
  931. bool vertical = toCylinderTile->hasProperty(CONST_PROP_ISVERTICAL);
  932. if (vertical) {
  933. if (playerPos.x + 1 == mapToPos.x) {
  934. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  935. return;
  936. }
  937. } else { // horizontal
  938. if (playerPos.y + 1 == mapToPos.y) {
  939. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  940. return;
  941. }
  942. }
  943.  
  944. if (!Position::areInRange<1, 1, 0>(playerPos, mapToPos)) {
  945. Position walkPos = mapToPos;
  946. if (vertical) {
  947. walkPos.x++;
  948. } else {
  949. walkPos.y++;
  950. }
  951.  
  952. Position itemPos = fromPos;
  953. uint8_t itemStackPos = fromStackPos;
  954.  
  955. if (fromPos.x != 0xFFFF && Position::areInRange<1, 1>(mapFromPos, playerPos)
  956. && !Position::areInRange<1, 1, 0>(mapFromPos, walkPos)) {
  957. //need to pickup the item first
  958. Item* moveItem = nullptr;
  959.  
  960. ReturnValue ret = internalMoveItem(fromCylinder, player, INDEX_WHEREEVER, item, count, &moveItem);
  961. if (ret != RETURNVALUE_NOERROR) {
  962. player->sendCancelMessage(ret);
  963. return;
  964. }
  965.  
  966. //changing the position since its now in the inventory of the player
  967. internalGetPosition(moveItem, itemPos, itemStackPos);
  968. }
  969.  
  970. std::forward_list<Direction> listDir;
  971. if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) {
  972. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  973. this, player->getID(), listDir)));
  974.  
  975. SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this,
  976. player->getID(), itemPos, spriteId, itemStackPos, toPos, count));
  977. player->setNextWalkActionTask(task);
  978. } else {
  979. player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  980. }
  981. return;
  982. }
  983. }
  984.  
  985. if ((Position::getDistanceX(playerPos, mapToPos) > item->getThrowRange()) ||
  986. (Position::getDistanceY(playerPos, mapToPos) > item->getThrowRange()) ||
  987. (Position::getDistanceZ(mapFromPos, mapToPos) * 4 > item->getThrowRange())) {
  988. player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
  989. return;
  990. }
  991.  
  992. if (!canThrowObjectTo(mapFromPos, mapToPos, true, MAPPATH_ITEM)) {
  993. player->sendCancelMessage(RETURNVALUE_CANNOTTHROW);
  994. return;
  995. }
  996.  
  997. if (!g_events->eventPlayerOnMoveItem(player, item, count, fromPos, toPos)) {
  998. return;
  999. }
  1000.  
  1001. uint8_t toIndex = 0;
  1002. if (toPos.x == 0xFFFF) {
  1003. if (toPos.y & 0x40) {
  1004. toIndex = toPos.z;
  1005. } else {
  1006. toIndex = static_cast<uint8_t>(toPos.y);
  1007. }
  1008. }
  1009.  
  1010. ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, item->isRune() ? item->getItemCount() : count, nullptr, 0, player);
  1011. if (ret != RETURNVALUE_NOERROR) {
  1012. player->sendCancelMessage(ret);
  1013. }
  1014. }
  1015.  
  1016. ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index,
  1017. Item* item, uint32_t count, Item** _moveItem, uint32_t flags /*= 0*/, Creature* actor/* = nullptr*/, Item* tradeItem/* = nullptr*/)
  1018. {
  1019. Item* toItem = nullptr;
  1020.  
  1021. Cylinder* subCylinder;
  1022. int floorN = 0;
  1023.  
  1024. while ((subCylinder = toCylinder->queryDestination(index, *item, &toItem, flags)) != toCylinder) {
  1025. toCylinder = subCylinder;
  1026. flags = 0;
  1027.  
  1028. //to prevent infinite loop
  1029. if (++floorN >= MAP_MAX_LAYERS) {
  1030. break;
  1031. }
  1032. }
  1033.  
  1034. //destination is the same as the source?
  1035. if (item == toItem) {
  1036. return RETURNVALUE_NOERROR; //silently ignore move
  1037. }
  1038.  
  1039. //check if we can add this item
  1040. ReturnValue ret = toCylinder->queryAdd(index, *item, count, flags, actor);
  1041. if (ret == RETURNVALUE_NEEDEXCHANGE) {
  1042. //check if we can add it to source cylinder
  1043. ret = fromCylinder->queryAdd(fromCylinder->getThingIndex(item), *toItem, toItem->getItemCount(), 0);
  1044. if (ret == RETURNVALUE_NOERROR) {
  1045. //check how much we can move
  1046. uint32_t maxExchangeQueryCount = 0;
  1047. ReturnValue retExchangeMaxCount = fromCylinder->queryMaxCount(INDEX_WHEREEVER, *toItem, toItem->getItemCount(), maxExchangeQueryCount, 0);
  1048.  
  1049. if (retExchangeMaxCount != RETURNVALUE_NOERROR && maxExchangeQueryCount == 0) {
  1050. return retExchangeMaxCount;
  1051. }
  1052.  
  1053. if (toCylinder->queryRemove(*toItem, toItem->getItemCount(), flags) == RETURNVALUE_NOERROR) {
  1054. int32_t oldToItemIndex = toCylinder->getThingIndex(toItem);
  1055. toCylinder->removeThing(toItem, toItem->getItemCount());
  1056. fromCylinder->addThing(toItem);
  1057.  
  1058. if (oldToItemIndex != -1) {
  1059. toCylinder->postRemoveNotification(toItem, fromCylinder, oldToItemIndex);
  1060. }
  1061.  
  1062. int32_t newToItemIndex = fromCylinder->getThingIndex(toItem);
  1063. if (newToItemIndex != -1) {
  1064. fromCylinder->postAddNotification(toItem, toCylinder, newToItemIndex);
  1065. }
  1066.  
  1067. ret = toCylinder->queryAdd(index, *item, count, flags);
  1068. toItem = nullptr;
  1069. }
  1070. }
  1071. }
  1072.  
  1073. if (ret != RETURNVALUE_NOERROR) {
  1074. return ret;
  1075. }
  1076.  
  1077. //check how much we can move
  1078. uint32_t maxQueryCount = 0;
  1079. ReturnValue retMaxCount = toCylinder->queryMaxCount(index, *item, count, maxQueryCount, flags);
  1080. if (retMaxCount != RETURNVALUE_NOERROR && maxQueryCount == 0) {
  1081. return retMaxCount;
  1082. }
  1083.  
  1084. uint32_t m;
  1085. if (item->isStackable()) {
  1086. if (item->isRune()) {
  1087. m = std::min<uint32_t>(item->getItemCount(), maxQueryCount);
  1088. } else {
  1089. m = std::min<uint32_t>(count, maxQueryCount);
  1090. }
  1091. } else {
  1092. m = maxQueryCount;
  1093. }
  1094.  
  1095. Item* moveItem = item;
  1096.  
  1097. //check if we can remove this item
  1098. ret = fromCylinder->queryRemove(*item, m, flags);
  1099. if (ret != RETURNVALUE_NOERROR) {
  1100. return ret;
  1101. }
  1102.  
  1103. if (tradeItem) {
  1104. if (toCylinder->getItem() == tradeItem) {
  1105. return RETURNVALUE_NOTENOUGHROOM;
  1106. }
  1107.  
  1108. Cylinder* tmpCylinder = toCylinder->getParent();
  1109. while (tmpCylinder) {
  1110. if (tmpCylinder->getItem() == tradeItem) {
  1111. return RETURNVALUE_NOTENOUGHROOM;
  1112. }
  1113.  
  1114. tmpCylinder = tmpCylinder->getParent();
  1115. }
  1116. }
  1117.  
  1118. //remove the item
  1119. int32_t itemIndex = fromCylinder->getThingIndex(item);
  1120. Item* updateItem = nullptr;
  1121. fromCylinder->removeThing(item, m);
  1122.  
  1123. //update item(s)
  1124. if (item->isStackable()) {
  1125. uint32_t n;
  1126.  
  1127. if (!item->isRune() && item->equals(toItem)) {
  1128. n = std::min<uint32_t>(100 - toItem->getItemCount(), m);
  1129. toCylinder->updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
  1130. updateItem = toItem;
  1131. } else {
  1132. n = 0;
  1133. }
  1134.  
  1135. int32_t newCount = m - n;
  1136. if (newCount > 0) {
  1137. moveItem = item->clone();
  1138. moveItem->setItemCount(newCount);
  1139. } else {
  1140. moveItem = nullptr;
  1141. }
  1142.  
  1143. if (item->isRemoved()) {
  1144. ReleaseItem(item);
  1145. }
  1146. }
  1147.  
  1148. //add item
  1149. if (moveItem /*m - n > 0*/) {
  1150. toCylinder->addThing(index, moveItem);
  1151. }
  1152.  
  1153. if (itemIndex != -1) {
  1154. fromCylinder->postRemoveNotification(item, toCylinder, itemIndex);
  1155. }
  1156.  
  1157. if (moveItem) {
  1158. int32_t moveItemIndex = toCylinder->getThingIndex(moveItem);
  1159. if (moveItemIndex != -1) {
  1160. toCylinder->postAddNotification(moveItem, fromCylinder, moveItemIndex);
  1161. }
  1162. }
  1163.  
  1164. if (updateItem) {
  1165. int32_t updateItemIndex = toCylinder->getThingIndex(updateItem);
  1166. if (updateItemIndex != -1) {
  1167. toCylinder->postAddNotification(updateItem, fromCylinder, updateItemIndex);
  1168. }
  1169. }
  1170.  
  1171. if (_moveItem) {
  1172. if (moveItem) {
  1173. *_moveItem = moveItem;
  1174. } else {
  1175. *_moveItem = item;
  1176. }
  1177. }
  1178.  
  1179. //we could not move all, inform the player
  1180. if (item->isStackable() && maxQueryCount < count) {
  1181. return retMaxCount;
  1182. }
  1183.  
  1184. return ret;
  1185. }
  1186.  
  1187. ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index /*= INDEX_WHEREEVER*/, uint32_t flags/* = 0*/, bool test/* = false*/)
  1188. {
  1189. uint32_t remainderCount = 0;
  1190. return internalAddItem(toCylinder, item, index, flags, test, remainderCount);
  1191. }
  1192.  
  1193. ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index, uint32_t flags, bool test, uint32_t& remainderCount)
  1194. {
  1195. if (toCylinder == nullptr || item == nullptr) {
  1196. return RETURNVALUE_NOTPOSSIBLE;
  1197. }
  1198.  
  1199. Cylinder* destCylinder = toCylinder;
  1200. Item* toItem = nullptr;
  1201. toCylinder = toCylinder->queryDestination(index, *item, &toItem, flags);
  1202.  
  1203. //check if we can add this item
  1204. ReturnValue ret = toCylinder->queryAdd(index, *item, item->getItemCount(), flags);
  1205. if (ret != RETURNVALUE_NOERROR) {
  1206. return ret;
  1207. }
  1208.  
  1209. /*
  1210. Check if we can move add the whole amount, we do this by checking against the original cylinder,
  1211. since the queryDestination can return a cylinder that might only hold a part of the full amount.
  1212. */
  1213. uint32_t maxQueryCount = 0;
  1214. ret = destCylinder->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), maxQueryCount, flags);
  1215.  
  1216. if (ret != RETURNVALUE_NOERROR) {
  1217. return ret;
  1218. }
  1219.  
  1220. if (test) {
  1221. return RETURNVALUE_NOERROR;
  1222. }
  1223.  
  1224. if (item->isStackable() && !item->isRune() && item->equals(toItem)) {
  1225. uint32_t m = std::min<uint32_t>(item->getItemCount(), maxQueryCount);
  1226. uint32_t n = std::min<uint32_t>(100 - toItem->getItemCount(), m);
  1227.  
  1228. toCylinder->updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
  1229.  
  1230. int32_t count = m - n;
  1231. if (count > 0) {
  1232. if (item->getItemCount() != count) {
  1233. Item* remainderItem = item->clone();
  1234. remainderItem->setItemCount(count);
  1235. if (internalAddItem(destCylinder, remainderItem, INDEX_WHEREEVER, flags, false) != RETURNVALUE_NOERROR) {
  1236. ReleaseItem(remainderItem);
  1237. remainderCount = count;
  1238. }
  1239. } else {
  1240. toCylinder->addThing(index, item);
  1241.  
  1242. int32_t itemIndex = toCylinder->getThingIndex(item);
  1243. if (itemIndex != -1) {
  1244. toCylinder->postAddNotification(item, nullptr, itemIndex);
  1245. }
  1246. }
  1247. } else {
  1248. //fully merged with toItem, item will be destroyed
  1249. item->onRemoved();
  1250. ReleaseItem(item);
  1251.  
  1252. int32_t itemIndex = toCylinder->getThingIndex(toItem);
  1253. if (itemIndex != -1) {
  1254. toCylinder->postAddNotification(toItem, nullptr, itemIndex);
  1255. }
  1256. }
  1257. } else {
  1258. toCylinder->addThing(index, item);
  1259.  
  1260. int32_t itemIndex = toCylinder->getThingIndex(item);
  1261. if (itemIndex != -1) {
  1262. toCylinder->postAddNotification(item, nullptr, itemIndex);
  1263. }
  1264. }
  1265.  
  1266. return RETURNVALUE_NOERROR;
  1267. }
  1268.  
  1269. ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool test /*= false*/, uint32_t flags /*= 0*/)
  1270. {
  1271. Cylinder* cylinder = item->getParent();
  1272. if (cylinder == nullptr) {
  1273. return RETURNVALUE_NOTPOSSIBLE;
  1274. }
  1275.  
  1276. if (count == -1) {
  1277. count = item->getItemCount();
  1278. }
  1279.  
  1280. //check if we can remove this item
  1281. ReturnValue ret = cylinder->queryRemove(*item, count, flags | FLAG_IGNORENOTMOVEABLE);
  1282. if (ret != RETURNVALUE_NOERROR) {
  1283. return ret;
  1284. }
  1285.  
  1286. if (!item->canRemove()) {
  1287. return RETURNVALUE_NOTPOSSIBLE;
  1288. }
  1289.  
  1290. if (!test) {
  1291. int32_t index = cylinder->getThingIndex(item);
  1292.  
  1293. //remove the item
  1294. cylinder->removeThing(item, count);
  1295.  
  1296. if (item->isRemoved()) {
  1297. ReleaseItem(item);
  1298. }
  1299.  
  1300. cylinder->postRemoveNotification(item, nullptr, index);
  1301. }
  1302.  
  1303. item->onRemoved();
  1304. return RETURNVALUE_NOERROR;
  1305. }
  1306.  
  1307. ReturnValue Game::internalPlayerAddItem(Player* player, Item* item, bool dropOnMap /*= true*/, slots_t slot /*= CONST_SLOT_WHEREEVER*/)
  1308. {
  1309. uint32_t remainderCount = 0;
  1310. ReturnValue ret = internalAddItem(player, item, static_cast<int32_t>(slot), 0, false, remainderCount);
  1311. if (remainderCount != 0) {
  1312. Item* remainderItem = Item::CreateItem(item->getID(), remainderCount);
  1313. ReturnValue remaindRet = internalAddItem(player->getTile(), remainderItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1314. if (remaindRet != RETURNVALUE_NOERROR) {
  1315. ReleaseItem(remainderItem);
  1316. }
  1317. }
  1318.  
  1319. if (ret != RETURNVALUE_NOERROR && dropOnMap) {
  1320. ret = internalAddItem(player->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1321. }
  1322.  
  1323. return ret;
  1324. }
  1325.  
  1326. Item* Game::findItemOfType(Cylinder* cylinder, uint16_t itemId,
  1327. bool depthSearch /*= true*/, int32_t subType /*= -1*/) const
  1328. {
  1329. if (cylinder == nullptr) {
  1330. return nullptr;
  1331. }
  1332.  
  1333. std::vector<Container*> containers;
  1334. for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
  1335. Thing* thing = cylinder->getThing(i);
  1336. if (!thing) {
  1337. continue;
  1338. }
  1339.  
  1340. Item* item = thing->getItem();
  1341. if (!item) {
  1342. continue;
  1343. }
  1344.  
  1345. if (item->getID() == itemId && (subType == -1 || subType == item->getSubType())) {
  1346. return item;
  1347. }
  1348.  
  1349. if (depthSearch) {
  1350. Container* container = item->getContainer();
  1351. if (container) {
  1352. containers.push_back(container);
  1353. }
  1354. }
  1355. }
  1356.  
  1357. size_t i = 0;
  1358. while (i < containers.size()) {
  1359. Container* container = containers[i++];
  1360. for (Item* item : container->getItemList()) {
  1361. if (item->getID() == itemId && (subType == -1 || subType == item->getSubType())) {
  1362. return item;
  1363. }
  1364.  
  1365. Container* subContainer = item->getContainer();
  1366. if (subContainer) {
  1367. containers.push_back(subContainer);
  1368. }
  1369. }
  1370. }
  1371. return nullptr;
  1372. }
  1373.  
  1374. bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
  1375. {
  1376. if (cylinder == nullptr) {
  1377. return false;
  1378. }
  1379.  
  1380. if (money == 0) {
  1381. return true;
  1382. }
  1383.  
  1384. std::vector<Container*> containers;
  1385.  
  1386. std::multimap<uint64_t, Item*> moneyMap;
  1387. uint64_t moneyCount = 0;
  1388.  
  1389. for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
  1390. Thing* thing = cylinder->getThing(i);
  1391. if (!thing) {
  1392. continue;
  1393. }
  1394.  
  1395. Item* item = thing->getItem();
  1396. if (!item) {
  1397. continue;
  1398. }
  1399.  
  1400. Container* container = item->getContainer();
  1401. if (container) {
  1402. containers.push_back(container);
  1403. } else {
  1404. const uint32_t worth = item->getWorth();
  1405. if (worth != 0) {
  1406. moneyCount += worth;
  1407. moneyMap.emplace(worth, item);
  1408. }
  1409. }
  1410. }
  1411.  
  1412. size_t i = 0;
  1413. while (i < containers.size()) {
  1414. Container* container = containers[i++];
  1415. for (Item* item : container->getItemList()) {
  1416. Container* tmpContainer = item->getContainer();
  1417. if (tmpContainer) {
  1418. containers.push_back(tmpContainer);
  1419. } else {
  1420. const uint32_t worth = item->getWorth();
  1421. if (worth != 0) {
  1422. moneyCount += worth;
  1423. moneyMap.emplace(worth, item);
  1424. }
  1425. }
  1426. }
  1427. }
  1428.  
  1429. if (moneyCount < money) {
  1430. return false;
  1431. }
  1432.  
  1433. for (const auto& moneyEntry : moneyMap) {
  1434. Item* item = moneyEntry.second;
  1435. if (moneyEntry.first < money) {
  1436. internalRemoveItem(item);
  1437. money -= moneyEntry.first;
  1438. } else if (moneyEntry.first > money) {
  1439. const uint32_t worth = moneyEntry.first / item->getItemCount();
  1440. const uint32_t removeCount = (money / worth) + 1;
  1441.  
  1442. addMoney(cylinder, (worth * removeCount) - money, flags);
  1443. internalRemoveItem(item, removeCount);
  1444. break;
  1445. } else {
  1446. internalRemoveItem(item);
  1447. break;
  1448. }
  1449. }
  1450. return true;
  1451. }
  1452.  
  1453. void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
  1454. {
  1455. if (money == 0) {
  1456. return;
  1457. }
  1458.  
  1459. uint32_t crystalCoins = money / 10000;
  1460. money -= crystalCoins * 10000;
  1461. while (crystalCoins > 0) {
  1462. const uint16_t count = std::min<uint32_t>(100, crystalCoins);
  1463.  
  1464. Item* remaindItem = Item::CreateItem(ITEM_CRYSTAL_COIN, count);
  1465.  
  1466. ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
  1467. if (ret != RETURNVALUE_NOERROR) {
  1468. internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1469. }
  1470.  
  1471. crystalCoins -= count;
  1472. }
  1473.  
  1474. uint16_t platinumCoins = money / 100;
  1475. if (platinumCoins != 0) {
  1476. Item* remaindItem = Item::CreateItem(ITEM_PLATINUM_COIN, platinumCoins);
  1477.  
  1478. ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
  1479. if (ret != RETURNVALUE_NOERROR) {
  1480. internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1481. }
  1482.  
  1483. money -= platinumCoins * 100;
  1484. }
  1485.  
  1486. if (money != 0) {
  1487. Item* remaindItem = Item::CreateItem(ITEM_GOLD_COIN, money);
  1488.  
  1489. ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
  1490. if (ret != RETURNVALUE_NOERROR) {
  1491. internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
  1492. }
  1493. }
  1494. }
  1495.  
  1496. Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/)
  1497. {
  1498. if (item->getID() == newId && (newCount == -1 || (newCount == item->getSubType() && newCount != 0))) { //chargeless item placed on map = infinite
  1499. return item;
  1500. }
  1501.  
  1502. Cylinder* cylinder = item->getParent();
  1503. if (cylinder == nullptr) {
  1504. return nullptr;
  1505. }
  1506.  
  1507. int32_t itemIndex = cylinder->getThingIndex(item);
  1508. if (itemIndex == -1) {
  1509. return item;
  1510. }
  1511.  
  1512. if (!item->canTransform()) {
  1513. return item;
  1514. }
  1515.  
  1516. const ItemType& newType = Item::items[newId];
  1517. if (newType.id == 0) {
  1518. return item;
  1519. }
  1520.  
  1521. const ItemType& curType = Item::items[item->getID()];
  1522. if (curType.alwaysOnTop != newType.alwaysOnTop) {
  1523. //This only occurs when you transform items on tiles from a downItem to a topItem (or vice versa)
  1524. //Remove the old, and add the new
  1525. cylinder->removeThing(item, item->getItemCount());
  1526. cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1527.  
  1528. item->setID(newId);
  1529. if (newCount != -1) {
  1530. item->setSubType(newCount);
  1531. }
  1532. cylinder->addThing(item);
  1533.  
  1534. Cylinder* newParent = item->getParent();
  1535. if (newParent == nullptr) {
  1536. ReleaseItem(item);
  1537. return nullptr;
  1538. }
  1539.  
  1540. newParent->postAddNotification(item, cylinder, newParent->getThingIndex(item));
  1541. return item;
  1542. }
  1543.  
  1544. if (curType.type == newType.type) {
  1545. //Both items has the same type so we can safely change id/subtype
  1546. if (newCount == 0 && (item->isStackable() || item->hasAttribute(ITEM_ATTRIBUTE_CHARGES))) {
  1547. if (item->isStackable()) {
  1548. internalRemoveItem(item);
  1549. return nullptr;
  1550. } else {
  1551. int32_t newItemId = newId;
  1552. if (curType.id == newType.id) {
  1553. newItemId = curType.decayTo;
  1554. }
  1555.  
  1556. if (newItemId < 0) {
  1557. internalRemoveItem(item);
  1558. return nullptr;
  1559. } else if (newItemId != newId) {
  1560. //Replacing the the old item with the new while maintaining the old position
  1561. Item* newItem = Item::CreateItem(newItemId, 1);
  1562. if (newItem == nullptr) {
  1563. return nullptr;
  1564. }
  1565.  
  1566. cylinder->replaceThing(itemIndex, newItem);
  1567. cylinder->postAddNotification(newItem, cylinder, itemIndex);
  1568.  
  1569. item->setParent(nullptr);
  1570. cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1571. ReleaseItem(item);
  1572. return newItem;
  1573. } else {
  1574. return transformItem(item, newItemId);
  1575. }
  1576. }
  1577. } else {
  1578. cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1579. uint16_t itemId = item->getID();
  1580. int32_t count = item->getSubType();
  1581.  
  1582. if (curType.id != newType.id) {
  1583. if (newType.group != curType.group) {
  1584. item->setDefaultSubtype();
  1585. }
  1586.  
  1587. itemId = newId;
  1588. }
  1589.  
  1590. if (newCount != -1 && newType.hasSubType()) {
  1591. count = newCount;
  1592. }
  1593.  
  1594. cylinder->updateThing(item, itemId, count);
  1595. cylinder->postAddNotification(item, cylinder, itemIndex);
  1596. return item;
  1597. }
  1598. }
  1599.  
  1600. //Replacing the the old item with the new while maintaining the old position
  1601. Item* newItem;
  1602. if (newCount == -1) {
  1603. newItem = Item::CreateItem(newId);
  1604. } else {
  1605. newItem = Item::CreateItem(newId, newCount);
  1606. }
  1607.  
  1608. if (newItem == nullptr) {
  1609. return nullptr;
  1610. }
  1611.  
  1612. cylinder->replaceThing(itemIndex, newItem);
  1613. cylinder->postAddNotification(newItem, cylinder, itemIndex);
  1614.  
  1615. item->setParent(nullptr);
  1616. cylinder->postRemoveNotification(item, cylinder, itemIndex);
  1617. ReleaseItem(item);
  1618.  
  1619. return newItem;
  1620. }
  1621.  
  1622. ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pushMove/* = true*/, uint32_t flags /*= 0*/)
  1623. {
  1624. if (newPos == thing->getPosition()) {
  1625. return RETURNVALUE_NOERROR;
  1626. } else if (thing->isRemoved()) {
  1627. return RETURNVALUE_NOTPOSSIBLE;
  1628. }
  1629.  
  1630. Tile* toTile = map.getTile(newPos);
  1631. if (!toTile) {
  1632. return RETURNVALUE_NOTPOSSIBLE;
  1633. }
  1634.  
  1635. if (Creature* creature = thing->getCreature()) {
  1636. ReturnValue ret = toTile->queryAdd(0, *creature, 1, FLAG_NOLIMIT);
  1637. if (ret != RETURNVALUE_NOERROR) {
  1638. return ret;
  1639. }
  1640.  
  1641. map.moveCreature(*creature, *toTile, !pushMove);
  1642. return RETURNVALUE_NOERROR;
  1643. } else if (Item* item = thing->getItem()) {
  1644. return internalMoveItem(item->getParent(), toTile, INDEX_WHEREEVER, item, item->getItemCount(), nullptr, flags);
  1645. }
  1646. return RETURNVALUE_NOTPOSSIBLE;
  1647. }
  1648.  
  1649. //Implementation of player invoked events
  1650. void Game::playerMove(uint32_t playerId, Direction direction)
  1651. {
  1652. Player* player = getPlayerByID(playerId);
  1653. if (!player) {
  1654. return;
  1655. }
  1656.  
  1657. player->resetIdleTime();
  1658. player->setNextWalkActionTask(nullptr);
  1659.  
  1660. player->startAutoWalk(std::forward_list<Direction> { direction });
  1661. }
  1662.  
  1663. bool Game::playerBroadcastMessage(Player* player, const std::string& text) const
  1664. {
  1665. if (!player->hasFlag(PlayerFlag_CanBroadcast)) {
  1666. return false;
  1667. }
  1668.  
  1669. std::cout << "> " << player->getName() << " broadcasted: \"" << text << "\"." << std::endl;
  1670.  
  1671. for (const auto& it : players) {
  1672. it.second->sendPrivateMessage(player, TALKTYPE_BROADCAST, text);
  1673. }
  1674.  
  1675. return true;
  1676. }
  1677.  
  1678. void Game::playerCreatePrivateChannel(uint32_t playerId)
  1679. {
  1680. Player* player = getPlayerByID(playerId);
  1681. if (!player || !player->isPremium()) {
  1682. return;
  1683. }
  1684.  
  1685. ChatChannel* channel = g_chat->createChannel(*player, CHANNEL_PRIVATE);
  1686. if (!channel || !channel->addUser(*player)) {
  1687. return;
  1688. }
  1689.  
  1690. player->sendCreatePrivateChannel(channel->getId(), channel->getName());
  1691. }
  1692.  
  1693. void Game::playerChannelInvite(uint32_t playerId, const std::string& name)
  1694. {
  1695. Player* player = getPlayerByID(playerId);
  1696. if (!player) {
  1697. return;
  1698. }
  1699.  
  1700. PrivateChatChannel* channel = g_chat->getPrivateChannel(*player);
  1701. if (!channel) {
  1702. return;
  1703. }
  1704.  
  1705. Player* invitePlayer = getPlayerByName(name);
  1706. if (!invitePlayer) {
  1707. return;
  1708. }
  1709.  
  1710. if (player == invitePlayer) {
  1711. return;
  1712. }
  1713.  
  1714. channel->invitePlayer(*player, *invitePlayer);
  1715. }
  1716.  
  1717. void Game::playerChannelExclude(uint32_t playerId, const std::string& name)
  1718. {
  1719. Player* player = getPlayerByID(playerId);
  1720. if (!player) {
  1721. return;
  1722. }
  1723.  
  1724. PrivateChatChannel* channel = g_chat->getPrivateChannel(*player);
  1725. if (!channel) {
  1726. return;
  1727. }
  1728.  
  1729. Player* excludePlayer = getPlayerByName(name);
  1730. if (!excludePlayer) {
  1731. return;
  1732. }
  1733.  
  1734. if (player == excludePlayer) {
  1735. return;
  1736. }
  1737.  
  1738. channel->excludePlayer(*player, *excludePlayer);
  1739. }
  1740.  
  1741. void Game::playerRequestChannels(uint32_t playerId)
  1742. {
  1743. Player* player = getPlayerByID(playerId);
  1744. if (!player) {
  1745. return;
  1746. }
  1747.  
  1748. player->sendChannelsDialog();
  1749. }
  1750.  
  1751. void Game::playerOpenChannel(uint32_t playerId, uint16_t channelId)
  1752. {
  1753. Player* player = getPlayerByID(playerId);
  1754. if (!player) {
  1755. return;
  1756. }
  1757.  
  1758. ChatChannel* channel = g_chat->addUserToChannel(*player, channelId);
  1759. if (!channel) {
  1760. return;
  1761. }
  1762.  
  1763. const InvitedMap* invitedUsers = channel->getInvitedUsers();
  1764. const UsersMap* users;
  1765. if (!channel->isPublicChannel()) {
  1766. users = &channel->getUsers();
  1767. } else {
  1768. users = nullptr;
  1769. }
  1770.  
  1771. player->sendChannel(channel->getId(), channel->getName(), users, invitedUsers);
  1772. }
  1773.  
  1774. void Game::playerCloseChannel(uint32_t playerId, uint16_t channelId)
  1775. {
  1776. Player* player = getPlayerByID(playerId);
  1777. if (!player) {
  1778. return;
  1779. }
  1780.  
  1781. g_chat->removeUserFromChannel(*player, channelId);
  1782. }
  1783.  
  1784. void Game::playerOpenPrivateChannel(uint32_t playerId, std::string& receiver)
  1785. {
  1786. Player* player = getPlayerByID(playerId);
  1787. if (!player) {
  1788. return;
  1789. }
  1790.  
  1791. if (!IOLoginData::formatPlayerName(receiver)) {
  1792. player->sendCancelMessage("A player with this name does not exist.");
  1793. return;
  1794. }
  1795.  
  1796. player->sendOpenPrivateChannel(receiver);
  1797. }
  1798.  
  1799. void Game::playerReceivePing(uint32_t playerId)
  1800. {
  1801. Player* player = getPlayerByID(playerId);
  1802. if (!player) {
  1803. return;
  1804. }
  1805.  
  1806. player->receivePing();
  1807. }
  1808.  
  1809. void Game::playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir)
  1810. {
  1811. Player* player = getPlayerByID(playerId);
  1812. if (!player) {
  1813. return;
  1814. }
  1815.  
  1816. player->resetIdleTime();
  1817. player->setNextWalkTask(nullptr);
  1818. player->startAutoWalk(listDir);
  1819. }
  1820.  
  1821. void Game::playerStopAutoWalk(uint32_t playerId)
  1822. {
  1823. Player* player = getPlayerByID(playerId);
  1824. if (!player) {
  1825. return;
  1826. }
  1827.  
  1828. player->stopWalk();
  1829. }
  1830.  
  1831. void Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint16_t fromSpriteId,
  1832. const Position& toPos, uint8_t toStackPos, uint16_t toSpriteId)
  1833. {
  1834. Player* player = getPlayerByID(playerId);
  1835. if (!player) {
  1836. return;
  1837. }
  1838.  
  1839. bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0);
  1840. if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
  1841. return;
  1842. }
  1843.  
  1844. Thing* thing = internalGetThing(player, fromPos, fromStackPos, fromSpriteId, STACKPOS_USEITEM);
  1845. if (!thing) {
  1846. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  1847. return;
  1848. }
  1849.  
  1850. Item* item = thing->getItem();
  1851. if (!item || !item->isUseable() || item->getClientID() != fromSpriteId) {
  1852. player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
  1853. return;
  1854. }
  1855.  
  1856. Position walkToPos = fromPos;
  1857. ReturnValue ret = g_actions->canUse(player, fromPos);
  1858. if (ret == RETURNVALUE_NOERROR) {
  1859. ret = g_actions->canUse(player, toPos, item);
  1860. if (ret == RETURNVALUE_TOOFARAWAY) {
  1861. walkToPos = toPos;
  1862. }
  1863. }
  1864.  
  1865. if (ret != RETURNVALUE_NOERROR) {
  1866. if (ret == RETURNVALUE_TOOFARAWAY) {
  1867. Position itemPos = fromPos;
  1868. uint8_t itemStackPos = fromStackPos;
  1869.  
  1870. if (fromPos.x != 0xFFFF && toPos.x != 0xFFFF && Position::areInRange<1, 1, 0>(fromPos, player->getPosition()) &&
  1871. !Position::areInRange<1, 1, 0>(fromPos, toPos)) {
  1872. Item* moveItem = nullptr;
  1873.  
  1874. ret = internalMoveItem(item->getParent(), player, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
  1875. if (ret != RETURNVALUE_NOERROR) {
  1876. player->sendCancelMessage(ret);
  1877. return;
  1878. }
  1879.  
  1880. //changing the position since its now in the inventory of the player
  1881. internalGetPosition(moveItem, itemPos, itemStackPos);
  1882. }
  1883.  
  1884. std::forward_list<Direction> listDir;
  1885. if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) {
  1886. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
  1887.  
  1888. SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItemEx, this,
  1889. playerId, itemPos, itemStackPos, fromSpriteId, toPos, toStackPos, toSpriteId));
  1890. player->setNextWalkActionTask(task);
  1891. } else {
  1892. player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  1893. }
  1894. return;
  1895. }
  1896.  
  1897. player->sendCancelMessage(ret);
  1898. return;
  1899. }
  1900.  
  1901. if (!player->canDoAction()) {
  1902. uint32_t delay = player->getNextActionTime();
  1903. SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItemEx, this,
  1904. playerId, fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId));
  1905. player->setNextActionTask(task);
  1906. return;
  1907. }
  1908.  
  1909. player->resetIdleTime();
  1910. player->setNextActionTask(nullptr);
  1911.  
  1912. g_actions->useItemEx(player, fromPos, toPos, toStackPos, item, isHotkey);
  1913. }
  1914.  
  1915. void Game::playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPos,
  1916. uint8_t index, uint16_t spriteId)
  1917. {
  1918.  
  1919. Player* player = getPlayerByID(playerId);
  1920. if (!player) {
  1921. return;
  1922. }
  1923.  
  1924. bool isHotkey = (pos.x == 0xFFFF && pos.y == 0 && pos.z == 0);
  1925. if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
  1926. return;
  1927. }
  1928.  
  1929. Thing* thing = internalGetThing(player, pos, stackPos, spriteId, STACKPOS_USEITEM);
  1930. if (!thing) {
  1931. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  1932. return;
  1933. }
  1934.  
  1935. Item* item = thing->getItem();
  1936. if (!item || item->isUseable() || item->getClientID() != spriteId) {
  1937. player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
  1938. return;
  1939. }
  1940.  
  1941. ReturnValue ret = g_actions->canUse(player, pos);
  1942. if (ret != RETURNVALUE_NOERROR) {
  1943. if (ret == RETURNVALUE_TOOFARAWAY) {
  1944. std::forward_list<Direction> listDir;
  1945. if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
  1946. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
  1947. SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, spriteId));
  1948. player->setNextWalkActionTask(task);
  1949. return;
  1950. }
  1951.  
  1952. ret = RETURNVALUE_THEREISNOWAY;
  1953. }
  1954.  
  1955. player->sendCancelMessage(ret);
  1956. return;
  1957. }
  1958.  
  1959. if (!player->canDoAction()) {
  1960. uint32_t delay = player->getNextActionTime();
  1961. SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, spriteId));
  1962. player->setNextActionTask(task);
  1963. return;
  1964. }
  1965.  
  1966. player->resetIdleTime();
  1967. player->setNextActionTask(nullptr);
  1968.  
  1969. g_actions->useItem(player, pos, index, item, isHotkey);
  1970. }
  1971.  
  1972. void Game::playerUseWithCreature(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint32_t creatureId, uint16_t spriteId)
  1973. {
  1974. Player* player = getPlayerByID(playerId);
  1975. if (!player) {
  1976. return;
  1977. }
  1978.  
  1979. Creature* creature = getCreatureByID(creatureId);
  1980. if (!creature) {
  1981. return;
  1982. }
  1983.  
  1984. if (!Position::areInRange<7, 5, 0>(creature->getPosition(), player->getPosition())) {
  1985. return;
  1986. }
  1987.  
  1988. bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0);
  1989. if (!g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
  1990. if (creature->getPlayer() || isHotkey) {
  1991. player->sendCancelMessage(RETURNVALUE_DIRECTPLAYERSHOOT);
  1992. return;
  1993. }
  1994. }
  1995.  
  1996. Thing* thing = internalGetThing(player, fromPos, fromStackPos, spriteId, STACKPOS_USEITEM);
  1997. if (!thing) {
  1998. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  1999. return;
  2000. }
  2001.  
  2002. Item* item = thing->getItem();
  2003. if (!item || !item->isUseable() || item->getClientID() != spriteId) {
  2004. player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
  2005. return;
  2006. }
  2007.  
  2008. Position toPos = creature->getPosition();
  2009. Position walkToPos = fromPos;
  2010. ReturnValue ret = g_actions->canUse(player, fromPos);
  2011. if (ret == RETURNVALUE_NOERROR) {
  2012. ret = g_actions->canUse(player, toPos, item);
  2013. if (ret == RETURNVALUE_TOOFARAWAY) {
  2014. walkToPos = toPos;
  2015. }
  2016. }
  2017.  
  2018. if (ret != RETURNVALUE_NOERROR) {
  2019. if (ret == RETURNVALUE_TOOFARAWAY) {
  2020. Position itemPos = fromPos;
  2021. uint8_t itemStackPos = fromStackPos;
  2022.  
  2023. if (fromPos.x != 0xFFFF && Position::areInRange<1, 1, 0>(fromPos, player->getPosition()) && !Position::areInRange<1, 1, 0>(fromPos, toPos)) {
  2024. Item* moveItem = nullptr;
  2025. ret = internalMoveItem(item->getParent(), player, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
  2026. if (ret != RETURNVALUE_NOERROR) {
  2027. player->sendCancelMessage(ret);
  2028. return;
  2029. }
  2030.  
  2031. //changing the position since its now in the inventory of the player
  2032. internalGetPosition(moveItem, itemPos, itemStackPos);
  2033. }
  2034.  
  2035. std::forward_list<Direction> listDir;
  2036. if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) {
  2037. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  2038. this, player->getID(), listDir)));
  2039.  
  2040. SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseWithCreature, this,
  2041. playerId, itemPos, itemStackPos, creatureId, spriteId));
  2042. player->setNextWalkActionTask(task);
  2043. } else {
  2044. player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  2045. }
  2046. return;
  2047. }
  2048.  
  2049. player->sendCancelMessage(ret);
  2050. return;
  2051. }
  2052.  
  2053. if (!player->canDoAction()) {
  2054. uint32_t delay = player->getNextActionTime();
  2055. SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseWithCreature, this,
  2056. playerId, fromPos, fromStackPos, creatureId, spriteId));
  2057. player->setNextActionTask(task);
  2058. return;
  2059. }
  2060.  
  2061. player->resetIdleTime();
  2062. player->setNextActionTask(nullptr);
  2063.  
  2064. g_actions->useItemEx(player, fromPos, creature->getPosition(), creature->getParent()->getThingIndex(creature), item, isHotkey, creature);
  2065. }
  2066.  
  2067. void Game::playerCloseContainer(uint32_t playerId, uint8_t cid)
  2068. {
  2069. Player* player = getPlayerByID(playerId);
  2070. if (!player) {
  2071. return;
  2072. }
  2073.  
  2074. player->closeContainer(cid);
  2075. player->sendCloseContainer(cid);
  2076. }
  2077.  
  2078. void Game::playerMoveUpContainer(uint32_t playerId, uint8_t cid)
  2079. {
  2080. Player* player = getPlayerByID(playerId);
  2081. if (!player) {
  2082. return;
  2083. }
  2084.  
  2085. Container* container = player->getContainerByID(cid);
  2086. if (!container) {
  2087. return;
  2088. }
  2089.  
  2090. Container* parentContainer = dynamic_cast<Container*>(container->getRealParent());
  2091. if (!parentContainer) {
  2092. return;
  2093. }
  2094.  
  2095. player->addContainer(cid, parentContainer);
  2096. player->sendContainer(cid, parentContainer, parentContainer->hasParent(), player->getContainerIndex(cid));
  2097. }
  2098.  
  2099. void Game::playerUpdateContainer(uint32_t playerId, uint8_t cid)
  2100. {
  2101. Player* player = getPlayerByID(playerId);
  2102. if (!player) {
  2103. return;
  2104. }
  2105.  
  2106. Container* container = player->getContainerByID(cid);
  2107. if (!container) {
  2108. return;
  2109. }
  2110.  
  2111. player->sendContainer(cid, container, container->hasParent(), player->getContainerIndex(cid));
  2112. }
  2113.  
  2114. void Game::playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stackPos, const uint16_t spriteId)
  2115. {
  2116. Player* player = getPlayerByID(playerId);
  2117. if (!player) {
  2118. return;
  2119. }
  2120.  
  2121. Thing* thing = internalGetThing(player, pos, stackPos, 0, STACKPOS_TOPDOWN_ITEM);
  2122. if (!thing) {
  2123. return;
  2124. }
  2125.  
  2126. Item* item = thing->getItem();
  2127. if (!item || item->getClientID() != spriteId || !item->isRotatable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  2128. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2129. return;
  2130. }
  2131.  
  2132. if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) {
  2133. std::forward_list<Direction> listDir;
  2134. if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
  2135. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  2136. this, player->getID(), listDir)));
  2137.  
  2138. SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRotateItem, this,
  2139. playerId, pos, stackPos, spriteId));
  2140. player->setNextWalkActionTask(task);
  2141. } else {
  2142. player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  2143. }
  2144. return;
  2145. }
  2146.  
  2147. uint16_t newId = Item::items[item->getID()].rotateTo;
  2148. if (newId != 0) {
  2149. transformItem(item, newId);
  2150. }
  2151. }
  2152.  
  2153. void Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text)
  2154. {
  2155. Player* player = getPlayerByID(playerId);
  2156. if (!player) {
  2157. return;
  2158. }
  2159.  
  2160. uint16_t maxTextLength = 0;
  2161. uint32_t internalWindowTextId = 0;
  2162.  
  2163. Item* writeItem = player->getWriteItem(internalWindowTextId, maxTextLength);
  2164. if (text.length() > maxTextLength || windowTextId != internalWindowTextId) {
  2165. return;
  2166. }
  2167.  
  2168. if (!writeItem || writeItem->isRemoved()) {
  2169. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2170. return;
  2171. }
  2172.  
  2173. Cylinder* topParent = writeItem->getTopParent();
  2174.  
  2175. Player* owner = dynamic_cast<Player*>(topParent);
  2176. if (owner && owner != player) {
  2177. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2178. return;
  2179. }
  2180.  
  2181. if (!Position::areInRange<1, 1, 0>(writeItem->getPosition(), player->getPosition())) {
  2182. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2183. return;
  2184. }
  2185.  
  2186. for (auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT)) {
  2187. if (!creatureEvent->executeTextEdit(player, writeItem, text)) {
  2188. player->setWriteItem(nullptr);
  2189. return;
  2190. }
  2191. }
  2192.  
  2193. if (!text.empty()) {
  2194. if (writeItem->getText() != text) {
  2195. writeItem->setText(text);
  2196. writeItem->setWriter(player->getName());
  2197. writeItem->setDate(time(nullptr));
  2198. }
  2199. } else {
  2200. writeItem->resetText();
  2201. writeItem->resetWriter();
  2202. writeItem->resetDate();
  2203. }
  2204.  
  2205. uint16_t newId = Item::items[writeItem->getID()].writeOnceItemId;
  2206. if (newId != 0) {
  2207. transformItem(writeItem, newId);
  2208. }
  2209.  
  2210. player->setWriteItem(nullptr);
  2211. }
  2212.  
  2213. void Game::playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text)
  2214. {
  2215. Player* player = getPlayerByID(playerId);
  2216. if (!player) {
  2217. return;
  2218. }
  2219.  
  2220. uint32_t internalWindowTextId;
  2221. uint32_t internalListId;
  2222.  
  2223. House* house = player->getEditHouse(internalWindowTextId, internalListId);
  2224. if (house && internalWindowTextId == windowTextId && listId == 0) {
  2225. house->setAccessList(internalListId, text);
  2226. player->setEditHouse(nullptr);
  2227. }
  2228. }
  2229.  
  2230. void Game::playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t stackPos,
  2231. uint32_t tradePlayerId, uint16_t spriteId)
  2232. {
  2233. Player* player = getPlayerByID(playerId);
  2234. if (!player) {
  2235. return;
  2236. }
  2237.  
  2238. Player* tradePartner = getPlayerByID(tradePlayerId);
  2239. if (!tradePartner || tradePartner == player) {
  2240. player->sendTextMessage(MESSAGE_INFO_DESCR, "Sorry, not possible.");
  2241. return;
  2242. }
  2243.  
  2244. if (!Position::areInRange<2, 2, 0>(tradePartner->getPosition(), player->getPosition())) {
  2245. std::ostringstream ss;
  2246. ss << tradePartner->getName() << " tells you to move closer.";
  2247. player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
  2248. return;
  2249. }
  2250.  
  2251. if (!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())) {
  2252. player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
  2253. return;
  2254. }
  2255.  
  2256. Thing* tradeThing = internalGetThing(player, pos, stackPos, 0, STACKPOS_TOPDOWN_ITEM);
  2257. if (!tradeThing) {
  2258. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2259. return;
  2260. }
  2261.  
  2262. Item* tradeItem = tradeThing->getItem();
  2263. if (tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || tradeItem->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
  2264. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2265. return;
  2266. }
  2267.  
  2268. const Position& playerPosition = player->getPosition();
  2269. const Position& tradeItemPosition = tradeItem->getPosition();
  2270. if (playerPosition.z != tradeItemPosition.z) {
  2271. player->sendCancelMessage(playerPosition.z > tradeItemPosition.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS);
  2272. return;
  2273. }
  2274.  
  2275. if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) {
  2276. std::forward_list<Direction> listDir;
  2277. if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
  2278. g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
  2279. this, player->getID(), listDir)));
  2280.  
  2281. SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRequestTrade, this,
  2282. playerId, pos, stackPos, tradePlayerId, spriteId));
  2283. player->setNextWalkActionTask(task);
  2284. } else {
  2285. player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  2286. }
  2287. return;
  2288. }
  2289.  
  2290. Container* tradeItemContainer = tradeItem->getContainer();
  2291. if (tradeItemContainer) {
  2292. for (const auto& it : tradeItems) {
  2293. Item* item = it.first;
  2294. if (tradeItem == item) {
  2295. player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2296. return;
  2297. }
  2298.  
  2299. if (tradeItemContainer->isHoldingItem(item)) {
  2300. player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2301. return;
  2302. }
  2303.  
  2304. Container* container = item->getContainer();
  2305. if (container && container->isHoldingItem(tradeItem)) {
  2306. player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2307. return;
  2308. }
  2309. }
  2310. } else {
  2311. for (const auto& it : tradeItems) {
  2312. Item* item = it.first;
  2313. if (tradeItem == item) {
  2314. player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2315. return;
  2316. }
  2317.  
  2318. Container* container = item->getContainer();
  2319. if (container && container->isHoldingItem(tradeItem)) {
  2320. player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
  2321. return;
  2322. }
  2323. }
  2324. }
  2325.  
  2326. Container* tradeContainer = tradeItem->getContainer();
  2327. if (tradeContainer && tradeContainer->getItemHoldingCount() + 1 > 100) {
  2328. player->sendTextMessage(MESSAGE_INFO_DESCR, "You can not trade more than 100 items.");
  2329. return;
  2330. }
  2331.  
  2332. if (!g_events->eventPlayerOnTradeRequest(player, tradePartner, tradeItem)) {
  2333. return;
  2334. }
  2335.  
  2336. internalStartTrade(player, tradePartner, tradeItem);
  2337. }
  2338.  
  2339. bool Game::internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem)
  2340. {
  2341. if (player->tradeState != TRADE_NONE && !(player->tradeState == TRADE_ACKNOWLEDGE && player->tradePartner == tradePartner)) {
  2342. player->sendCancelMessage(RETURNVALUE_YOUAREALREADYTRADING);
  2343. return false;
  2344. } else if (tradePartner->tradeState != TRADE_NONE && tradePartner->tradePartner != player) {
  2345. player->sendCancelMessage(RETURNVALUE_THISPLAYERISALREADYTRADING);
  2346. return false;
  2347. }
  2348.  
  2349. player->tradePartner = tradePartner;
  2350. player->tradeItem = tradeItem;
  2351. player->tradeState = TRADE_INITIATED;
  2352. tradeItem->incrementReferenceCounter();
  2353. tradeItems[tradeItem] = player->getID();
  2354.  
  2355. player->sendTradeItemRequest(player->getName(), tradeItem, true);
  2356.  
  2357. if (tradePartner->tradeState == TRADE_NONE) {
  2358. std::ostringstream ss;
  2359. ss << player->getName() << " wants to trade with you.";
  2360. tradePartner->sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  2361. tradePartner->tradeState = TRADE_ACKNOWLEDGE;
  2362. tradePartner->tradePartner = player;
  2363. } else {
  2364. Item* counterOfferItem = tradePartner->tradeItem;
  2365. player->sendTradeItemRequest(tradePartner->getName(), counterOfferItem, false);
  2366. tradePartner->sendTradeItemRequest(player->getName(), tradeItem, false);
  2367. }
  2368.  
  2369. return true;
  2370. }
  2371.  
  2372. void Game::playerAcceptTrade(uint32_t playerId)
  2373. {
  2374. Player* player = getPlayerByID(playerId);
  2375. if (!player) {
  2376. return;
  2377. }
  2378.  
  2379. if (!(player->getTradeState() == TRADE_ACKNOWLEDGE || player->getTradeState() == TRADE_INITIATED)) {
  2380. return;
  2381. }
  2382.  
  2383. Player* tradePartner = player->tradePartner;
  2384. if (!tradePartner) {
  2385. return;
  2386. }
  2387.  
  2388. if (!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())) {
  2389. player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
  2390. return;
  2391. }
  2392.  
  2393. player->setTradeState(TRADE_ACCEPT);
  2394.  
  2395. if (tradePartner->getTradeState() == TRADE_ACCEPT) {
  2396. Item* tradeItem1 = player->tradeItem;
  2397. Item* tradeItem2 = tradePartner->tradeItem;
  2398.  
  2399. if (!g_events->eventPlayerOnTradeAccept(player, tradePartner, tradeItem1, tradeItem2)) {
  2400. internalCloseTrade(player);
  2401. return;
  2402. }
  2403.  
  2404. player->setTradeState(TRADE_TRANSFER);
  2405. tradePartner->setTradeState(TRADE_TRANSFER);
  2406.  
  2407. std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradeItem1);
  2408. if (it != tradeItems.end()) {
  2409. ReleaseItem(it->first);
  2410. tradeItems.erase(it);
  2411. }
  2412.  
  2413. it = tradeItems.find(tradeItem2);
  2414. if (it != tradeItems.end()) {
  2415. ReleaseItem(it->first);
  2416. tradeItems.erase(it);
  2417. }
  2418.  
  2419. bool isSuccess = false;
  2420.  
  2421. ReturnValue ret1 = internalAddItem(tradePartner, tradeItem1, INDEX_WHEREEVER, 0, true);
  2422. ReturnValue ret2 = internalAddItem(player, tradeItem2, INDEX_WHEREEVER, 0, true);
  2423. if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) {
  2424. ret1 = internalRemoveItem(tradeItem1, tradeItem1->getItemCount(), true);
  2425. ret2 = internalRemoveItem(tradeItem2, tradeItem2->getItemCount(), true);
  2426. if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) {
  2427. Cylinder* cylinder1 = tradeItem1->getParent();
  2428. Cylinder* cylinder2 = tradeItem2->getParent();
  2429.  
  2430. uint32_t count1 = tradeItem1->getItemCount();
  2431. uint32_t count2 = tradeItem2->getItemCount();
  2432.  
  2433. ret1 = internalMoveItem(cylinder1, tradePartner, INDEX_WHEREEVER, tradeItem1, count1, nullptr, FLAG_IGNOREAUTOSTACK, nullptr, tradeItem2);
  2434. if (ret1 == RETURNVALUE_NOERROR) {
  2435. internalMoveItem(cylinder2, player, INDEX_WHEREEVER, tradeItem2, count2, nullptr, FLAG_IGNOREAUTOSTACK);
  2436.  
  2437. tradeItem1->onTradeEvent(ON_TRADE_TRANSFER, tradePartner);
  2438. tradeItem2->onTradeEvent(ON_TRADE_TRANSFER, player);
  2439.  
  2440. isSuccess = true;
  2441. }
  2442. }
  2443. }
  2444.  
  2445. if (!isSuccess) {
  2446. std::string errorDescription;
  2447.  
  2448. if (tradePartner->tradeItem) {
  2449. errorDescription = getTradeErrorDescription(ret1, tradeItem1);
  2450. tradePartner->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription);
  2451. tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner);
  2452. }
  2453.  
  2454. if (player->tradeItem) {
  2455. errorDescription = getTradeErrorDescription(ret2, tradeItem2);
  2456. player->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription);
  2457. player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player);
  2458. }
  2459. }
  2460.  
  2461. player->setTradeState(TRADE_NONE);
  2462. player->tradeItem = nullptr;
  2463. player->tradePartner = nullptr;
  2464. player->sendTradeClose();
  2465.  
  2466. tradePartner->setTradeState(TRADE_NONE);
  2467. tradePartner->tradeItem = nullptr;
  2468. tradePartner->tradePartner = nullptr;
  2469. tradePartner->sendTradeClose();
  2470. }
  2471. }
  2472.  
  2473. std::string Game::getTradeErrorDescription(ReturnValue ret, Item* item)
  2474. {
  2475. if (item) {
  2476. if (ret == RETURNVALUE_NOTENOUGHCAPACITY) {
  2477. std::ostringstream ss;
  2478. ss << "You do not have enough capacity to carry";
  2479.  
  2480. if (item->isStackable() && item->getItemCount() > 1) {
  2481. ss << " these objects.";
  2482. } else {
  2483. ss << " this object.";
  2484. }
  2485.  
  2486. ss << std::endl << ' ' << item->getWeightDescription();
  2487. return ss.str();
  2488. } else if (ret == RETURNVALUE_NOTENOUGHROOM || ret == RETURNVALUE_CONTAINERNOTENOUGHROOM) {
  2489. std::ostringstream ss;
  2490. ss << "You do not have enough room to carry";
  2491.  
  2492. if (item->isStackable() && item->getItemCount() > 1) {
  2493. ss << " these objects.";
  2494. } else {
  2495. ss << " this object.";
  2496. }
  2497.  
  2498. return ss.str();
  2499. }
  2500. }
  2501. return "Trade could not be completed.";
  2502. }
  2503.  
  2504. void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index)
  2505. {
  2506. Player* player = getPlayerByID(playerId);
  2507. if (!player) {
  2508. return;
  2509. }
  2510.  
  2511. Player* tradePartner = player->tradePartner;
  2512. if (!tradePartner) {
  2513. return;
  2514. }
  2515.  
  2516. Item* tradeItem;
  2517. if (lookAtCounterOffer) {
  2518. tradeItem = tradePartner->getTradeItem();
  2519. } else {
  2520. tradeItem = player->getTradeItem();
  2521. }
  2522.  
  2523. if (!tradeItem) {
  2524. return;
  2525. }
  2526.  
  2527. const Position& playerPosition = player->getPosition();
  2528. const Position& tradeItemPosition = tradeItem->getPosition();
  2529.  
  2530. int32_t lookDistance = std::max<int32_t>(Position::getDistanceX(playerPosition, tradeItemPosition),
  2531. Position::getDistanceY(playerPosition, tradeItemPosition));
  2532. if (index == 0) {
  2533. g_events->eventPlayerOnLookInTrade(player, tradePartner, tradeItem, lookDistance);
  2534. return;
  2535. }
  2536.  
  2537. Container* tradeContainer = tradeItem->getContainer();
  2538. if (!tradeContainer) {
  2539. return;
  2540. }
  2541.  
  2542. std::vector<const Container*> containers {tradeContainer};
  2543. size_t i = 0;
  2544. while (i < containers.size()) {
  2545. const Container* container = containers[i++];
  2546. for (Item* item : container->getItemList()) {
  2547. Container* tmpContainer = item->getContainer();
  2548. if (tmpContainer) {
  2549. containers.push_back(tmpContainer);
  2550. }
  2551.  
  2552. if (--index == 0) {
  2553. g_events->eventPlayerOnLookInTrade(player, tradePartner, item, lookDistance);
  2554. return;
  2555. }
  2556. }
  2557. }
  2558. }
  2559.  
  2560. void Game::playerCloseTrade(uint32_t playerId)
  2561. {
  2562. Player* player = getPlayerByID(playerId);
  2563. if (!player) {
  2564. return;
  2565. }
  2566.  
  2567. internalCloseTrade(player);
  2568. }
  2569.  
  2570. void Game::internalCloseTrade(Player* player)
  2571. {
  2572. Player* tradePartner = player->tradePartner;
  2573. if ((tradePartner && tradePartner->getTradeState() == TRADE_TRANSFER) || player->getTradeState() == TRADE_TRANSFER) {
  2574. return;
  2575. }
  2576.  
  2577. if (player->getTradeItem()) {
  2578. std::map<Item*, uint32_t>::iterator it = tradeItems.find(player->getTradeItem());
  2579. if (it != tradeItems.end()) {
  2580. ReleaseItem(it->first);
  2581. tradeItems.erase(it);
  2582. }
  2583.  
  2584. player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player);
  2585. player->tradeItem = nullptr;
  2586. }
  2587.  
  2588. player->setTradeState(TRADE_NONE);
  2589. player->tradePartner = nullptr;
  2590.  
  2591. player->sendTextMessage(MESSAGE_STATUS_SMALL, "Trade cancelled.");
  2592. player->sendTradeClose();
  2593.  
  2594. if (tradePartner) {
  2595. if (tradePartner->getTradeItem()) {
  2596. std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradePartner->getTradeItem());
  2597. if (it != tradeItems.end()) {
  2598. ReleaseItem(it->first);
  2599. tradeItems.erase(it);
  2600. }
  2601.  
  2602. tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner);
  2603. tradePartner->tradeItem = nullptr;
  2604. }
  2605.  
  2606. tradePartner->setTradeState(TRADE_NONE);
  2607. tradePartner->tradePartner = nullptr;
  2608.  
  2609. tradePartner->sendTextMessage(MESSAGE_STATUS_SMALL, "Trade cancelled.");
  2610. tradePartner->sendTradeClose();
  2611. }
  2612. }
  2613.  
  2614. void Game::playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos)
  2615. {
  2616. Player* player = getPlayerByID(playerId);
  2617. if (!player) {
  2618. return;
  2619. }
  2620.  
  2621. Thing* thing = internalGetThing(player, pos, stackPos, 0, STACKPOS_LOOK);
  2622. if (!thing) {
  2623. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2624. return;
  2625. }
  2626.  
  2627. Position thingPos = thing->getPosition();
  2628. if (!player->canSee(thingPos)) {
  2629. player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  2630. return;
  2631. }
  2632.  
  2633. Position playerPos = player->getPosition();
  2634.  
  2635. int32_t lookDistance;
  2636. if (thing != player) {
  2637. lookDistance = std::max<int32_t>(Position::getDistanceX(playerPos, thingPos), Position::getDistanceY(playerPos, thingPos));
  2638. if (playerPos.z != thingPos.z) {
  2639. lookDistance += 15;
  2640. }
  2641. } else {
  2642. lookDistance = -1;
  2643. }
  2644.  
  2645. g_events->eventPlayerOnLook(player, pos, thing, stackPos, lookDistance);
  2646. }
  2647.  
  2648. void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId)
  2649. {
  2650. Player* player = getPlayerByID(playerId);
  2651. if (!player) {
  2652. return;
  2653. }
  2654.  
  2655. Creature* creature = getCreatureByID(creatureId);
  2656. if (!creature) {
  2657. return;
  2658. }
  2659.  
  2660. if (!player->canSeeCreature(creature)) {
  2661. return;
  2662. }
  2663.  
  2664. const Position& creaturePos = creature->getPosition();
  2665. if (!player->canSee(creaturePos)) {
  2666. return;
  2667. }
  2668.  
  2669. int32_t lookDistance;
  2670. if (creature != player) {
  2671. const Position& playerPos = player->getPosition();
  2672. lookDistance = std::max<int32_t>(Position::getDistanceX(playerPos, creaturePos), Position::getDistanceY(playerPos, creaturePos));
  2673. if (playerPos.z != creaturePos.z) {
  2674. lookDistance += 15;
  2675. }
  2676. } else {
  2677. lookDistance = -1;
  2678. }
  2679.  
  2680. g_events->eventPlayerOnLookInBattleList(player, creature, lookDistance);
  2681. }
  2682.  
  2683. void Game::playerCancelAttackAndFollow(uint32_t playerId)
  2684. {
  2685. Player* player = getPlayerByID(playerId);
  2686. if (!player) {
  2687. return;
  2688. }
  2689.  
  2690. playerSetAttackedCreature(playerId, 0);
  2691. playerFollowCreature(playerId, 0);
  2692. player->stopWalk();
  2693. }
  2694.  
  2695. void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId)
  2696. {
  2697. Player* player = getPlayerByID(playerId);
  2698. if (!player) {
  2699. return;
  2700. }
  2701.  
  2702. if (player->getAttackedCreature() && creatureId == 0) {
  2703. player->setAttackedCreature(nullptr);
  2704. player->sendCancelTarget();
  2705. return;
  2706. }
  2707.  
  2708. Creature* attackCreature = getCreatureByID(creatureId);
  2709. if (!attackCreature) {
  2710. player->setAttackedCreature(nullptr);
  2711. player->sendCancelTarget();
  2712. return;
  2713. }
  2714.  
  2715. ReturnValue ret = Combat::canTargetCreature(player, attackCreature);
  2716. if (ret != RETURNVALUE_NOERROR) {
  2717. player->sendCancelMessage(ret);
  2718. player->sendCancelTarget();
  2719. player->setAttackedCreature(nullptr);
  2720. return;
  2721. }
  2722.  
  2723. player->setAttackedCreature(attackCreature);
  2724. g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID())));
  2725. }
  2726.  
  2727. void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId)
  2728. {
  2729. Player* player = getPlayerByID(playerId);
  2730. if (!player) {
  2731. return;
  2732. }
  2733.  
  2734. player->setAttackedCreature(nullptr);
  2735. g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID())));
  2736. player->setFollowCreature(getCreatureByID(creatureId));
  2737. }
  2738.  
  2739. void Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, bool secureMode)
  2740. {
  2741. Player* player = getPlayerByID(playerId);
  2742. if (!player) {
  2743. return;
  2744. }
  2745.  
  2746. player->setFightMode(fightMode);
  2747. player->setChaseMode(chaseMode);
  2748. player->setSecureMode(secureMode);
  2749. }
  2750.  
  2751. void Game::playerRequestAddVip(uint32_t playerId, const std::string& name)
  2752. {
  2753. if (name.length() > 20) {
  2754. return;
  2755. }
  2756.  
  2757. Player* player = getPlayerByID(playerId);
  2758. if (!player) {
  2759. return;
  2760. }
  2761.  
  2762. Player* vipPlayer = getPlayerByName(name);
  2763. if (!vipPlayer) {
  2764. uint32_t guid;
  2765. bool specialVip;
  2766. std::string formattedName = name;
  2767. if (!IOLoginData::getGuidByNameEx(guid, specialVip, formattedName)) {
  2768. player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name does not exist.");
  2769. return;
  2770. }
  2771.  
  2772. if (specialVip && !player->hasFlag(PlayerFlag_SpecialVIP)) {
  2773. player->sendTextMessage(MESSAGE_STATUS_SMALL, "You can not add this player.");
  2774. return;
  2775. }
  2776.  
  2777. player->addVIP(guid, formattedName, VIPSTATUS_OFFLINE);
  2778. } else {
  2779. if (vipPlayer->hasFlag(PlayerFlag_SpecialVIP) && !player->hasFlag(PlayerFlag_SpecialVIP)) {
  2780. player->sendTextMessage(MESSAGE_STATUS_SMALL, "You can not add this player.");
  2781. return;
  2782. }
  2783.  
  2784. if (!vipPlayer->isInGhostMode() || player->isAccessPlayer()) {
  2785. player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), VIPSTATUS_ONLINE);
  2786. } else {
  2787. player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), VIPSTATUS_OFFLINE);
  2788. }
  2789. }
  2790. }
  2791.  
  2792. void Game::playerRequestRemoveVip(uint32_t playerId, uint32_t guid)
  2793. {
  2794. Player* player = getPlayerByID(playerId);
  2795. if (!player) {
  2796. return;
  2797. }
  2798.  
  2799. player->removeVIP(guid);
  2800. }
  2801.  
  2802. void Game::playerTurn(uint32_t playerId, Direction dir)
  2803. {
  2804. Player* player = getPlayerByID(playerId);
  2805. if (!player) {
  2806. return;
  2807. }
  2808.  
  2809. if (!g_events->eventPlayerOnTurn(player, dir)) {
  2810. return;
  2811. }
  2812.  
  2813. player->resetIdleTime();
  2814. internalCreatureTurn(player, dir);
  2815. }
  2816.  
  2817. void Game::playerRequestOutfit(uint32_t playerId)
  2818. {
  2819. if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
  2820. return;
  2821. }
  2822.  
  2823. Player* player = getPlayerByID(playerId);
  2824. if (!player) {
  2825. return;
  2826. }
  2827.  
  2828. player->sendOutfitWindow();
  2829. }
  2830.  
  2831. void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
  2832. {
  2833. if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
  2834. return;
  2835. }
  2836.  
  2837. Player* player = getPlayerByID(playerId);
  2838. if (!player) {
  2839. return;
  2840. }
  2841.  
  2842. if (player->canWear(outfit.lookType, outfit.lookAddons)) {
  2843. player->defaultOutfit = outfit;
  2844.  
  2845. if (player->hasCondition(CONDITION_OUTFIT)) {
  2846. return;
  2847. }
  2848.  
  2849. internalCreatureChangeOutfit(player, outfit);
  2850. }
  2851. }
  2852.  
  2853. void Game::playerShowQuestLog(uint32_t playerId)
  2854. {
  2855. Player* player = getPlayerByID(playerId);
  2856. if (!player) {
  2857. return;
  2858. }
  2859.  
  2860. player->sendQuestLog();
  2861. }
  2862.  
  2863. void Game::playerShowQuestLine(uint32_t playerId, uint16_t questId)
  2864. {
  2865. Player* player = getPlayerByID(playerId);
  2866. if (!player) {
  2867. return;
  2868. }
  2869.  
  2870. Quest* quest = quests.getQuestByID(questId);
  2871. if (!quest) {
  2872. return;
  2873. }
  2874.  
  2875. player->sendQuestLine(quest);
  2876. }
  2877.  
  2878. void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, const std::string& receiver, const std::string& text)
  2879. {
  2880. Player* player = getPlayerByID(playerId);
  2881. if (!player) {
  2882. return;
  2883. }
  2884.  
  2885. player->resetIdleTime();
  2886.  
  2887. if (playerSaySpell(player, type, text)) {
  2888. return;
  2889. }
  2890.  
  2891. uint32_t muteTime = player->isMuted();
  2892. if (muteTime > 0) {
  2893. std::ostringstream ss;
  2894. ss << "You are still muted for " << muteTime << " seconds.";
  2895. player->sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
  2896. return;
  2897. }
  2898.  
  2899. if (playerSayCommand(player, text)) {
  2900. return;
  2901. }
  2902.  
  2903. if (!text.empty() && text.front() == '/' && player->isAccessPlayer()) {
  2904. return;
  2905. }
  2906.  
  2907. if (type != TALKTYPE_PRIVATE) {
  2908. player->removeMessageBuffer();
  2909. }
  2910.  
  2911. switch (type) {
  2912. case TALKTYPE_SAY:
  2913. internalCreatureSay(player, TALKTYPE_SAY, text, false);
  2914. break;
  2915.  
  2916. case TALKTYPE_WHISPER:
  2917. playerWhisper(player, text);
  2918. break;
  2919.  
  2920. case TALKTYPE_YELL:
  2921. playerYell(player, text);
  2922. break;
  2923.  
  2924. case TALKTYPE_PRIVATE:
  2925. case TALKTYPE_PRIVATE_RED:
  2926. playerSpeakTo(player, type, receiver, text);
  2927. break;
  2928.  
  2929. case TALKTYPE_CHANNEL_O:
  2930. case TALKTYPE_CHANNEL_Y:
  2931. case TALKTYPE_CHANNEL_R1:
  2932. case TALKTYPE_CHANNEL_R2:
  2933. g_chat->talkToChannel(*player, type, text, channelId);
  2934. break;
  2935.  
  2936. case TALKTYPE_BROADCAST:
  2937. playerBroadcastMessage(player, text);
  2938. break;
  2939.  
  2940. default:
  2941. break;
  2942. }
  2943. }
  2944.  
  2945. bool Game::playerSayCommand(Player* player, const std::string& text)
  2946. {
  2947. if (text.empty()) {
  2948. return false;
  2949. }
  2950.  
  2951. char firstCharacter = text.front();
  2952. for (char commandTag : commandTags) {
  2953. if (commandTag == firstCharacter) {
  2954. if (commands.exeCommand(*player, text)) {
  2955. return true;
  2956. }
  2957. }
  2958. }
  2959. return false;
  2960. }
  2961.  
  2962. bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& text)
  2963. {
  2964. std::string words = text;
  2965.  
  2966. TalkActionResult_t result = g_talkActions->playerSaySpell(player, type, words);
  2967. if (result == TALKACTION_BREAK) {
  2968. return true;
  2969. }
  2970.  
  2971. result = g_spells->playerSaySpell(player, words);
  2972. if (result == TALKACTION_BREAK) {
  2973. if (!g_config.getBoolean(ConfigManager::EMOTE_SPELLS)) {
  2974. return internalCreatureSay(player, TALKTYPE_SAY, words, false);
  2975. } else {
  2976. return internalCreatureSay(player, TALKTYPE_MONSTER_SAY, words, false);
  2977. }
  2978.  
  2979. } else if (result == TALKACTION_FAILED) {
  2980. return true;
  2981. }
  2982.  
  2983. return false;
  2984. }
  2985.  
  2986. void Game::playerWhisper(Player* player, const std::string& text)
  2987. {
  2988. SpectatorVec list;
  2989. map.getSpectators(list, player->getPosition(), false, false,
  2990. Map::maxClientViewportX, Map::maxClientViewportX,
  2991. Map::maxClientViewportY, Map::maxClientViewportY);
  2992.  
  2993. //send to client
  2994. for (Creature* spectator : list) {
  2995. if (Player* spectatorPlayer = spectator->getPlayer()) {
  2996. if (!Position::areInRange<1, 1>(player->getPosition(), spectatorPlayer->getPosition())) {
  2997. spectatorPlayer->sendCreatureSay(player, TALKTYPE_WHISPER, "pspsps");
  2998. } else {
  2999. spectatorPlayer->sendCreatureSay(player, TALKTYPE_WHISPER, text);
  3000. }
  3001. }
  3002. }
  3003.  
  3004. //event method
  3005. for (Creature* spectator : list) {
  3006. spectator->onCreatureSay(player, TALKTYPE_WHISPER, text);
  3007. }
  3008. }
  3009.  
  3010. bool Game::playerYell(Player* player, const std::string& text)
  3011. {
  3012. if (player->getLevel() < 50) {
  3013. player->sendTextMessage(MESSAGE_STATUS_SMALL, "You may not yell under 50 level.");
  3014. return false;
  3015. }
  3016.  
  3017. if (player->hasCondition(CONDITION_YELLTICKS)) {
  3018. player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
  3019. return false;
  3020. }
  3021.  
  3022. if (player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER) {
  3023. Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_YELLTICKS, 30000, 0);
  3024. player->addCondition(condition);
  3025. }
  3026.  
  3027. internalCreatureSay(player, TALKTYPE_YELL, asUpperCaseString(text), false);
  3028. return true;
  3029. }
  3030.  
  3031. bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, const std::string& text)
  3032. {
  3033. Player* toPlayer = getPlayerByName(receiver);
  3034. if (!toPlayer) {
  3035. player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name is not online.");
  3036. return false;
  3037. }
  3038.  
  3039. if (type == TALKTYPE_PRIVATE_RED && (player->hasFlag(PlayerFlag_CanTalkRedPrivate) || player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER)) {
  3040. type = TALKTYPE_PRIVATE_RED;
  3041. } else {
  3042. type = TALKTYPE_PRIVATE;
  3043. }
  3044.  
  3045. toPlayer->sendPrivateMessage(player, type, text);
  3046. toPlayer->onCreatureSay(player, type, text);
  3047.  
  3048. if (toPlayer->isInGhostMode() && !player->isAccessPlayer()) {
  3049. player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name is not online.");
  3050. } else {
  3051. std::ostringstream ss;
  3052. ss << "Message sent to " << toPlayer->getName() << '.';
  3053. player->sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
  3054. }
  3055. return true;
  3056. }
  3057.  
  3058. //--
  3059. bool Game::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= Map::maxClientViewportX*/, int32_t rangey /*= Map::maxClientViewportY*/, MapPathFinding_t path) const
  3060. {
  3061. return map.canThrowObjectTo(fromPos, toPos, checkLineOfSight, rangex, rangey, path);
  3062. }
  3063.  
  3064. bool Game::isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck) const
  3065. {
  3066. return map.isSightClear(fromPos, toPos, floorCheck);
  3067. }
  3068.  
  3069. bool Game::internalCreatureTurn(Creature* creature, Direction dir)
  3070. {
  3071. if (creature->getDirection() == dir) {
  3072. return false;
  3073. }
  3074.  
  3075. creature->setDirection(dir);
  3076.  
  3077. //send to client
  3078. SpectatorVec list;
  3079. map.getSpectators(list, creature->getPosition(), true, true);
  3080. for (Creature* spectator : list) {
  3081. spectator->getPlayer()->sendCreatureTurn(creature);
  3082. }
  3083. return true;
  3084. }
  3085.  
  3086. bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, bool ghostMode, SpectatorVec* listPtr/* = nullptr*/, const Position* pos/* = nullptr*/)
  3087. {
  3088. if (text.empty()) {
  3089. return false;
  3090. }
  3091.  
  3092. if (!pos) {
  3093. pos = &creature->getPosition();
  3094. }
  3095.  
  3096. SpectatorVec list;
  3097.  
  3098. if (!listPtr || listPtr->empty()) {
  3099. // This somewhat complex construct ensures that the cached SpectatorVec
  3100. // is used if available and if it can be used, else a local vector is
  3101. // used (hopefully the compiler will optimize away the construction of
  3102. // the temporary when it's not used).
  3103. if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) {
  3104. map.getSpectators(list, *pos, false, false,
  3105. Map::maxClientViewportX, Map::maxClientViewportX,
  3106. Map::maxClientViewportY, Map::maxClientViewportY);
  3107. } else {
  3108. map.getSpectators(list, *pos, true, false, 18, 18, 14, 14);
  3109. }
  3110. } else {
  3111. list = (*listPtr);
  3112. }
  3113.  
  3114. //send to client
  3115. for (Creature* spectator : list) {
  3116. if (Player* tmpPlayer = spectator->getPlayer()) {
  3117. if (!ghostMode || tmpPlayer->canSeeCreature(creature)) {
  3118. tmpPlayer->sendCreatureSay(creature, type, text, pos);
  3119. }
  3120. }
  3121. }
  3122.  
  3123. //event method
  3124. for (Creature* spectator : list) {
  3125. spectator->onCreatureSay(creature, type, text);
  3126. }
  3127. return true;
  3128. }
  3129.  
  3130. void Game::checkCreatureWalk(uint32_t creatureId)
  3131. {
  3132. Creature* creature = getCreatureByID(creatureId);
  3133. if (creature && creature->getHealth() > 0) {
  3134. creature->onWalk();
  3135. cleanup();
  3136. }
  3137. }
  3138.  
  3139. void Game::updateCreatureWalk(uint32_t creatureId)
  3140. {
  3141. Creature* creature = getCreatureByID(creatureId);
  3142. if (creature && creature->getHealth() > 0) {
  3143. creature->goToFollowCreature();
  3144. }
  3145. }
  3146.  
  3147. void Game::checkCreatureAttack(uint32_t creatureId)
  3148. {
  3149. Creature* creature = getCreatureByID(creatureId);
  3150. if (creature && creature->getHealth() > 0) {
  3151. creature->onAttacking(0);
  3152. }
  3153. }
  3154.  
  3155. void Game::addCreatureCheck(Creature* creature)
  3156. {
  3157. creature->creatureCheck = true;
  3158.  
  3159. if (creature->inCheckCreaturesVector) {
  3160. // already in a vector
  3161. return;
  3162. }
  3163.  
  3164. creature->inCheckCreaturesVector = true;
  3165. checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].push_back(creature);
  3166. creature->incrementReferenceCounter();
  3167. }
  3168.  
  3169. void Game::removeCreatureCheck(Creature* creature)
  3170. {
  3171. if (creature->inCheckCreaturesVector) {
  3172. creature->creatureCheck = false;
  3173. }
  3174. }
  3175.  
  3176. void Game::checkCreatures(size_t index)
  3177. {
  3178. g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT)));
  3179.  
  3180. auto& checkCreatureList = checkCreatureLists[index];
  3181. auto it = checkCreatureList.begin(), end = checkCreatureList.end();
  3182. while (it != end) {
  3183. Creature* creature = *it;
  3184. if (creature->creatureCheck) {
  3185. if (creature->getHealth() > 0) {
  3186. creature->onThink(EVENT_CREATURE_THINK_INTERVAL);
  3187. creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL);
  3188. creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL);
  3189. } else {
  3190. creature->onDeath();
  3191. }
  3192. ++it;
  3193. } else {
  3194. creature->inCheckCreaturesVector = false;
  3195. it = checkCreatureList.erase(it);
  3196. ReleaseCreature(creature);
  3197. }
  3198. }
  3199.  
  3200. cleanup();
  3201. }
  3202.  
  3203. void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta)
  3204. {
  3205. int32_t varSpeed = creature->getSpeed() - creature->getBaseSpeed();
  3206. varSpeed += varSpeedDelta;
  3207.  
  3208. creature->setSpeed(varSpeed);
  3209.  
  3210. //send to clients
  3211. SpectatorVec list;
  3212. map.getSpectators(list, creature->getPosition(), false, true);
  3213. for (Creature* spectator : list) {
  3214. spectator->getPlayer()->sendChangeSpeed(creature, creature->getStepSpeed());
  3215. }
  3216. }
  3217.  
  3218. void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit)
  3219. {
  3220. if (!g_events->eventCreatureOnChangeOutfit(creature, outfit)) {
  3221. return;
  3222. }
  3223.  
  3224. creature->setCurrentOutfit(outfit);
  3225.  
  3226. if (creature->isInvisible()) {
  3227. return;
  3228. }
  3229.  
  3230. //send to clients
  3231. SpectatorVec list;
  3232. map.getSpectators(list, creature->getPosition(), true, true);
  3233. for (Creature* spectator : list) {
  3234. spectator->getPlayer()->sendCreatureChangeOutfit(creature, outfit);
  3235. }
  3236. }
  3237.  
  3238. void Game::internalCreatureChangeVisible(Creature* creature, bool visible)
  3239. {
  3240. //send to clients
  3241. SpectatorVec list;
  3242. map.getSpectators(list, creature->getPosition(), true, true);
  3243. for (Creature* spectator : list) {
  3244. spectator->getPlayer()->sendCreatureChangeVisible(creature, visible);
  3245. }
  3246. }
  3247.  
  3248. void Game::changeLight(const Creature* creature)
  3249. {
  3250. //send to clients
  3251. SpectatorVec list;
  3252. map.getSpectators(list, creature->getPosition(), true, true);
  3253. for (Creature* spectator : list) {
  3254. spectator->getPlayer()->sendCreatureLight(creature);
  3255. }
  3256. }
  3257.  
  3258. bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* target, bool checkDefense, bool checkArmor, bool field)
  3259. {
  3260. if (damage.primary.type == COMBAT_NONE && damage.secondary.type == COMBAT_NONE) {
  3261. return true;
  3262. }
  3263.  
  3264. if (target->getPlayer() && target->isInGhostMode()) {
  3265. return true;
  3266. }
  3267.  
  3268. if (damage.primary.value > 0) {
  3269. return false;
  3270. }
  3271.  
  3272. static const auto sendBlockEffect = [this](BlockType_t blockType, CombatType_t combatType, const Position& targetPos) {
  3273. if (blockType == BLOCK_DEFENSE) {
  3274. addMagicEffect(targetPos, CONST_ME_POFF);
  3275. } else if (blockType == BLOCK_ARMOR) {
  3276. addMagicEffect(targetPos, CONST_ME_BLOCKHIT);
  3277. } else if (blockType == BLOCK_IMMUNITY) {
  3278. uint8_t hitEffect = 0;
  3279. switch (combatType) {
  3280. case COMBAT_UNDEFINEDDAMAGE: {
  3281. return;
  3282. }
  3283. case COMBAT_ENERGYDAMAGE:
  3284. case COMBAT_FIREDAMAGE:
  3285. case COMBAT_PHYSICALDAMAGE: {
  3286. hitEffect = CONST_ME_BLOCKHIT;
  3287. break;
  3288. }
  3289. case COMBAT_EARTHDAMAGE: {
  3290. hitEffect = CONST_ME_GREEN_RINGS;
  3291. break;
  3292. }
  3293. default: {
  3294. hitEffect = CONST_ME_POFF;
  3295. break;
  3296. }
  3297. }
  3298. addMagicEffect(targetPos, hitEffect);
  3299. }
  3300. };
  3301.  
  3302. BlockType_t primaryBlockType, secondaryBlockType;
  3303. if (damage.primary.type != COMBAT_NONE) {
  3304. damage.primary.value = -damage.primary.value;
  3305. primaryBlockType = target->blockHit(attacker, damage.primary.type, damage.primary.value, checkDefense, checkArmor, field);
  3306.  
  3307. damage.primary.value = -damage.primary.value;
  3308. sendBlockEffect(primaryBlockType, damage.primary.type, target->getPosition());
  3309. } else {
  3310. primaryBlockType = BLOCK_NONE;
  3311. }
  3312.  
  3313. if (damage.secondary.type != COMBAT_NONE) {
  3314. damage.secondary.value = -damage.secondary.value;
  3315. secondaryBlockType = target->blockHit(attacker, damage.secondary.type, damage.secondary.value, false, false, field);
  3316.  
  3317. damage.secondary.value = -damage.secondary.value;
  3318. sendBlockEffect(secondaryBlockType, damage.secondary.type, target->getPosition());
  3319. } else {
  3320. secondaryBlockType = BLOCK_NONE;
  3321. }
  3322. return (primaryBlockType != BLOCK_NONE) && (secondaryBlockType != BLOCK_NONE);
  3323. }
  3324.  
  3325. void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect)
  3326. {
  3327. switch (combatType) {
  3328. case COMBAT_PHYSICALDAMAGE: {
  3329. Item* splash = nullptr;
  3330. switch (target->getRace()) {
  3331. case RACE_VENOM:
  3332. color = TEXTCOLOR_LIGHTGREEN;
  3333. effect = CONST_ME_HITBYPOISON;
  3334. splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_GREEN);
  3335. break;
  3336. case RACE_BLOOD:
  3337. color = TEXTCOLOR_RED;
  3338. effect = CONST_ME_DRAWBLOOD;
  3339. splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD);
  3340. break;
  3341. case RACE_UNDEAD:
  3342. color = TEXTCOLOR_LIGHTGREY;
  3343. effect = CONST_ME_HITAREA;
  3344. break;
  3345. case RACE_FIRE:
  3346. color = TEXTCOLOR_ORANGE;
  3347. effect = CONST_ME_DRAWBLOOD;
  3348. break;
  3349. case RACE_ENERGY:
  3350. color = TEXTCOLOR_PURPLE;
  3351. effect = CONST_ME_ENERGYHIT;
  3352. break;
  3353. default:
  3354. color = TEXTCOLOR_NONE;
  3355. effect = CONST_ME_NONE;
  3356. break;
  3357. }
  3358.  
  3359. if (splash) {
  3360. internalAddItem(target->getTile(), splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
  3361. startDecay(splash);
  3362. }
  3363.  
  3364. break;
  3365. }
  3366.  
  3367. case COMBAT_ENERGYDAMAGE: {
  3368. color = TEXTCOLOR_PURPLE;
  3369. effect = CONST_ME_ENERGYHIT;
  3370. break;
  3371. }
  3372.  
  3373. case COMBAT_EARTHDAMAGE: {
  3374. color = TEXTCOLOR_LIGHTGREEN;
  3375. effect = CONST_ME_GREEN_RINGS;
  3376. break;
  3377. }
  3378.  
  3379. case COMBAT_FIREDAMAGE: {
  3380. color = TEXTCOLOR_ORANGE;
  3381. effect = CONST_ME_HITBYFIRE;
  3382. break;
  3383. }
  3384.  
  3385. case COMBAT_LIFEDRAIN: {
  3386. color = TEXTCOLOR_RED;
  3387. effect = CONST_ME_MAGIC_RED;
  3388. break;
  3389. }
  3390.  
  3391. default: {
  3392. color = TEXTCOLOR_NONE;
  3393. effect = CONST_ME_NONE;
  3394. break;
  3395. }
  3396. }
  3397. }
  3398.  
  3399. bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage)
  3400. {
  3401. const Position& targetPos = target->getPosition();
  3402. if (damage.primary.value > 0) {
  3403. if (target->getHealth() <= 0) {
  3404. return false;
  3405. }
  3406.  
  3407. Player* attackerPlayer;
  3408. if (attacker) {
  3409. attackerPlayer = attacker->getPlayer();
  3410. } else {
  3411. attackerPlayer = nullptr;
  3412. }
  3413.  
  3414. Player* targetPlayer = target->getPlayer();
  3415. if (damage.origin != ORIGIN_NONE) {
  3416. const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
  3417. if (!events.empty()) {
  3418. for (CreatureEvent* creatureEvent : events) {
  3419. creatureEvent->executeHealthChange(target, attacker, damage);
  3420. }
  3421. damage.origin = ORIGIN_NONE;
  3422. return combatChangeHealth(attacker, target, damage);
  3423. }
  3424. }
  3425.  
  3426. int32_t realHealthChange = target->getHealth();
  3427. target->gainHealth(attacker, damage.primary.value);
  3428. realHealthChange = target->getHealth() - realHealthChange;
  3429.  
  3430. if (realHealthChange > 0 && !target->isInGhostMode()) {
  3431. std::string damageString = std::to_string(realHealthChange) + (realHealthChange != 1 ? " hitpoints." : " hitpoint.");
  3432.  
  3433. std::string spectatorMessage;
  3434. if (!attacker) {
  3435. spectatorMessage += ucfirst(target->getNameDescription());
  3436. spectatorMessage += " was healed for " + damageString;
  3437. } else {
  3438. spectatorMessage += ucfirst(attacker->getNameDescription());
  3439. spectatorMessage += " healed ";
  3440. if (attacker == target) {
  3441. spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "herself" : "himself") : "itself");
  3442. } else {
  3443. spectatorMessage += target->getNameDescription();
  3444. }
  3445. spectatorMessage += " for " + damageString;
  3446. }
  3447.  
  3448. TextMessage message;
  3449. std::ostringstream strHealthChange;
  3450. strHealthChange << realHealthChange;
  3451. addAnimatedText(strHealthChange.str(), targetPos, TEXTCOLOR_MAYABLUE);
  3452.  
  3453. SpectatorVec list;
  3454. map.getSpectators(list, targetPos, false, true);
  3455. for (Creature* spectator : list) {
  3456. Player* tmpPlayer = spectator->getPlayer();
  3457. if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3458. message.type = MESSAGE_STATUS_SMALL;
  3459. message.text = "You heal " + target->getNameDescription() + " for " + damageString;
  3460. } else if (tmpPlayer == targetPlayer) {
  3461. message.type = MESSAGE_STATUS_SMALL;
  3462. if (!attacker) {
  3463. message.text = "You were healed for " + damageString;
  3464. } else if (targetPlayer == attackerPlayer) {
  3465. message.text = "You heal yourself for " + damageString;
  3466. } else {
  3467. message.text = "You were healed by " + attacker->getNameDescription() + " for " + damageString;
  3468. }
  3469. } else {
  3470. message.type = MESSAGE_STATUS_SMALL;
  3471. message.text = spectatorMessage;
  3472. }
  3473. tmpPlayer->sendTextMessage(message);
  3474. }
  3475. }
  3476. } else {
  3477. if (!target->isAttackable()) {
  3478. if (!target->isInGhostMode()) {
  3479. addMagicEffect(targetPos, CONST_ME_POFF);
  3480. }
  3481. return true;
  3482. }
  3483.  
  3484. Player* attackerPlayer;
  3485. if (attacker) {
  3486. attackerPlayer = attacker->getPlayer();
  3487. } else {
  3488. attackerPlayer = nullptr;
  3489. }
  3490.  
  3491. Player* targetPlayer = target->getPlayer();
  3492. damage.primary.value = std::abs(damage.primary.value);
  3493. damage.secondary.value = std::abs(damage.secondary.value);
  3494.  
  3495. int32_t healthChange = damage.primary.value + damage.secondary.value;
  3496. if (healthChange == 0) {
  3497. return true;
  3498. }
  3499.  
  3500. TextMessage message;
  3501. SpectatorVec list;
  3502. if (target->hasCondition(CONDITION_MANASHIELD) && damage.primary.type != COMBAT_UNDEFINEDDAMAGE) {
  3503. int32_t manaDamage = std::min<int32_t>(target->getMana(), healthChange);
  3504. if (manaDamage != 0) {
  3505. if (damage.origin != ORIGIN_NONE) {
  3506. const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
  3507. if (!events.empty()) {
  3508. for (CreatureEvent* creatureEvent : events) {
  3509. creatureEvent->executeManaChange(target, attacker, healthChange, damage.origin);
  3510. }
  3511. if (healthChange == 0) {
  3512. return true;
  3513. }
  3514. manaDamage = std::min<int32_t>(target->getMana(), healthChange);
  3515. }
  3516. }
  3517.  
  3518. target->drainMana(attacker, manaDamage);
  3519. map.getSpectators(list, targetPos, true, true);
  3520. addMagicEffect(list, targetPos, CONST_ME_LOSEENERGY);
  3521.  
  3522. std::string damageString = std::to_string(manaDamage);
  3523. std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana";
  3524. if (attacker) {
  3525. spectatorMessage += " due to ";
  3526. if (attacker == target) {
  3527. spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
  3528. } else {
  3529. spectatorMessage += "an attack by " + attacker->getNameDescription();
  3530. }
  3531. }
  3532. spectatorMessage += '.';
  3533.  
  3534. std::ostringstream strManaDamage;
  3535. strManaDamage << manaDamage;
  3536. addAnimatedText(strManaDamage.str(), targetPos, TEXTCOLOR_BLUE);
  3537.  
  3538. for (Creature* spectator : list) {
  3539. Player* tmpPlayer = spectator->getPlayer();
  3540. if (tmpPlayer->getPosition().z != targetPos.z) {
  3541. continue;
  3542. }
  3543.  
  3544. if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3545. message.type = MESSAGE_STATUS_SMALL;
  3546. message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana due to your attack.";
  3547. } else if (tmpPlayer == targetPlayer) {
  3548. message.type = MESSAGE_STATUS_SMALL;
  3549. if (!attacker) {
  3550. message.text = "You lose " + damageString + " mana.";
  3551. } else if (targetPlayer == attackerPlayer) {
  3552. message.text = "You lose " + damageString + " mana due to your own attack.";
  3553. } else {
  3554. message.text = "You lose " + damageString + " mana due to an attack by " + attacker->getNameDescription() + '.';
  3555. }
  3556. } else {
  3557. message.type = MESSAGE_STATUS_SMALL;
  3558. message.text = spectatorMessage;
  3559. }
  3560. tmpPlayer->sendTextMessage(message);
  3561. }
  3562.  
  3563. damage.primary.value -= manaDamage;
  3564. if (damage.primary.value < 0) {
  3565. damage.secondary.value = std::max<int32_t>(0, damage.secondary.value + damage.primary.value);
  3566. damage.primary.value = 0;
  3567. }
  3568. }
  3569. }
  3570.  
  3571. int32_t realDamage = damage.primary.value + damage.secondary.value;
  3572. if (realDamage == 0) {
  3573. return true;
  3574. }
  3575.  
  3576. if (damage.origin != ORIGIN_NONE) {
  3577. const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
  3578. if (!events.empty()) {
  3579. for (CreatureEvent* creatureEvent : events) {
  3580. creatureEvent->executeHealthChange(target, attacker, damage);
  3581. }
  3582. damage.origin = ORIGIN_NONE;
  3583. return combatChangeHealth(attacker, target, damage);
  3584. }
  3585. }
  3586.  
  3587. int32_t targetHealth = target->getHealth();
  3588. if (damage.primary.value >= targetHealth) {
  3589. damage.primary.value = targetHealth;
  3590. damage.secondary.value = 0;
  3591. } else if (damage.secondary.value) {
  3592. damage.secondary.value = std::min<int32_t>(damage.secondary.value, targetHealth - damage.primary.value);
  3593. }
  3594.  
  3595. realDamage = damage.primary.value + damage.secondary.value;
  3596. if (realDamage == 0) {
  3597. return true;
  3598. } else if (realDamage >= targetHealth) {
  3599. for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) {
  3600. if (!creatureEvent->executeOnPrepareDeath(target, attacker)) {
  3601. return false;
  3602. }
  3603. }
  3604. }
  3605.  
  3606. target->drainHealth(attacker, realDamage);
  3607. if (list.empty()) {
  3608. map.getSpectators(list, targetPos, true, true);
  3609. }
  3610. addCreatureHealth(list, target);
  3611.  
  3612. message.primary.value = damage.primary.value;
  3613. message.secondary.value = damage.secondary.value;
  3614.  
  3615. uint8_t hitEffect;
  3616. if (message.primary.value) {
  3617. combatGetTypeInfo(damage.primary.type, target, message.primary.color, hitEffect);
  3618. if (hitEffect != CONST_ME_NONE) {
  3619. addMagicEffect(list, targetPos, hitEffect);
  3620. }
  3621.  
  3622. if (message.primary.color != TEXTCOLOR_NONE) {
  3623. std::ostringstream strPrimaryDamage;
  3624. strPrimaryDamage << message.primary.value;
  3625. addAnimatedText(strPrimaryDamage.str(), targetPos, message.primary.color);
  3626. }
  3627. }
  3628.  
  3629. if (message.secondary.value) {
  3630. combatGetTypeInfo(damage.secondary.type, target, message.secondary.color, hitEffect);
  3631. if (hitEffect != CONST_ME_NONE) {
  3632. addMagicEffect(list, targetPos, hitEffect);
  3633. }
  3634.  
  3635. if (message.secondary.color != TEXTCOLOR_NONE) {
  3636. std::ostringstream strSecondaryDamage;
  3637. strSecondaryDamage << message.secondary.value;
  3638. addAnimatedText(strSecondaryDamage.str(), targetPos, message.secondary.color);
  3639. }
  3640. }
  3641.  
  3642. if (message.primary.color != TEXTCOLOR_NONE || message.secondary.color != TEXTCOLOR_NONE) {
  3643. std::string damageString = std::to_string(realDamage) + (realDamage != 1 ? " hitpoints" : " hitpoint");
  3644. std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString;
  3645. if (attacker) {
  3646. spectatorMessage += " due to ";
  3647. if (attacker == target) {
  3648. spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
  3649. } else {
  3650. spectatorMessage += "an attack by " + attacker->getNameDescription();
  3651. }
  3652. }
  3653. spectatorMessage += '.';
  3654.  
  3655. for (Creature* spectator : list) {
  3656. Player* tmpPlayer = spectator->getPlayer();
  3657. if (tmpPlayer->getPosition().z != targetPos.z) {
  3658. continue;
  3659. }
  3660.  
  3661. if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3662. message.type = MESSAGE_STATUS_SMALL;
  3663. message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " due to your attack.";
  3664. } else if (tmpPlayer == targetPlayer) {
  3665. message.type = MESSAGE_STATUS_SMALL;
  3666. if (!attacker) {
  3667. message.text = "You lose " + damageString + '.';
  3668. } else if (targetPlayer == attackerPlayer) {
  3669. message.text = "You lose " + damageString + " due to your own attack.";
  3670. } else {
  3671. message.text = "You lose " + damageString + " due to an attack by " + attacker->getNameDescription() + '.';
  3672. }
  3673. } else {
  3674. message.type = MESSAGE_STATUS_SMALL;
  3675. // TODO: Avoid copying spectatorMessage everytime we send to a spectator
  3676. message.text = spectatorMessage;
  3677. }
  3678. tmpPlayer->sendTextMessage(message);
  3679. }
  3680. }
  3681. }
  3682.  
  3683. return true;
  3684. }
  3685.  
  3686. bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange, CombatOrigin origin)
  3687. {
  3688. if (manaChange > 0) {
  3689. if (origin != ORIGIN_NONE) {
  3690. const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
  3691. if (!events.empty()) {
  3692. for (CreatureEvent* creatureEvent : events) {
  3693. creatureEvent->executeManaChange(target, attacker, manaChange, origin);
  3694. }
  3695. return combatChangeMana(attacker, target, manaChange, ORIGIN_NONE);
  3696. }
  3697. }
  3698.  
  3699. target->changeMana(manaChange);
  3700. } else {
  3701. const Position& targetPos = target->getPosition();
  3702. if (!target->isAttackable()) {
  3703. if (!target->isInGhostMode()) {
  3704. addMagicEffect(targetPos, CONST_ME_POFF);
  3705. }
  3706. return false;
  3707. }
  3708.  
  3709. Player* attackerPlayer;
  3710. if (attacker) {
  3711. attackerPlayer = attacker->getPlayer();
  3712. } else {
  3713. attackerPlayer = nullptr;
  3714. }
  3715.  
  3716. Player* targetPlayer = target->getPlayer();
  3717. int32_t manaLoss = std::min<int32_t>(target->getMana(), -manaChange);
  3718. BlockType_t blockType = target->blockHit(attacker, COMBAT_MANADRAIN, manaLoss);
  3719. if (blockType != BLOCK_NONE) {
  3720. addMagicEffect(targetPos, CONST_ME_POFF);
  3721. return false;
  3722. }
  3723.  
  3724. if (manaLoss <= 0) {
  3725. return true;
  3726. }
  3727.  
  3728. if (origin != ORIGIN_NONE) {
  3729. const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
  3730. if (!events.empty()) {
  3731. for (CreatureEvent* creatureEvent : events) {
  3732. creatureEvent->executeManaChange(target, attacker, manaChange, origin);
  3733. }
  3734. return combatChangeMana(attacker, target, manaChange, ORIGIN_NONE);
  3735. }
  3736. }
  3737.  
  3738. target->drainMana(attacker, manaLoss);
  3739.  
  3740. std::string damageString = std::to_string(manaLoss);
  3741. std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana";
  3742. if (attacker) {
  3743. spectatorMessage += " due to ";
  3744. if (attacker == target) {
  3745. spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
  3746. } else {
  3747. spectatorMessage += "an attack by " + attacker->getNameDescription();
  3748. }
  3749. }
  3750. spectatorMessage += '.';
  3751.  
  3752. TextMessage message;
  3753. std::ostringstream strManaLoss;
  3754. strManaLoss << manaLoss;
  3755. addAnimatedText(strManaLoss.str(), targetPos, TEXTCOLOR_BLUE);
  3756.  
  3757. SpectatorVec list;
  3758. map.getSpectators(list, targetPos, false, true);
  3759. for (Creature* spectator : list) {
  3760. Player* tmpPlayer = spectator->getPlayer();
  3761. if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
  3762. message.type = MESSAGE_STATUS_SMALL;
  3763. message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana due to your attack.";
  3764. } else if (tmpPlayer == targetPlayer) {
  3765. message.type = MESSAGE_STATUS_SMALL;
  3766. if (!attacker) {
  3767. message.text = "You lose " + damageString + " mana.";
  3768. } else if (targetPlayer == attackerPlayer) {
  3769. message.text = "You lose " + damageString + " mana due to your own attack.";
  3770. } else {
  3771. message.text = "You lose " + damageString + " mana due to an attack by " + attacker->getNameDescription() + '.';
  3772. }
  3773. } else {
  3774. message.type = MESSAGE_STATUS_SMALL;
  3775. message.text = spectatorMessage;
  3776. }
  3777. tmpPlayer->sendTextMessage(message);
  3778. }
  3779. }
  3780.  
  3781. return true;
  3782. }
  3783.  
  3784. void Game::addCreatureHealth(const Creature* target)
  3785. {
  3786. SpectatorVec list;
  3787. map.getSpectators(list, target->getPosition(), true, true);
  3788. addCreatureHealth(list, target);
  3789. }
  3790.  
  3791. void Game::addCreatureHealth(const SpectatorVec& list, const Creature* target)
  3792. {
  3793. for (Creature* spectator : list) {
  3794. if (Player* tmpPlayer = spectator->getPlayer()) {
  3795. tmpPlayer->sendCreatureHealth(target);
  3796. }
  3797. }
  3798. }
  3799.  
  3800. void Game::addAnimatedText(const std::string& message, const Position& pos, TextColor_t color)
  3801. {
  3802. SpectatorVec list;
  3803. map.getSpectators(list, pos, true, true);
  3804. addAnimatedText(list, message, pos, color);
  3805. }
  3806.  
  3807. void Game::addAnimatedText(const SpectatorVec& list, const std::string& message, const Position& pos, TextColor_t color)
  3808. {
  3809. for (Creature* spectator : list) {
  3810. if (Player* tmpPlayer = spectator->getPlayer()) {
  3811. tmpPlayer->sendAnimatedText(message, pos, color);
  3812. }
  3813. }
  3814. }
  3815.  
  3816. void Game::addMagicEffect(const Position& pos, uint8_t effect)
  3817. {
  3818. SpectatorVec list;
  3819. map.getSpectators(list, pos, true, true);
  3820. addMagicEffect(list, pos, effect);
  3821. }
  3822.  
  3823. void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect)
  3824. {
  3825. for (Creature* spectator : list) {
  3826. if (Player* tmpPlayer = spectator->getPlayer()) {
  3827. tmpPlayer->sendMagicEffect(pos, effect);
  3828. }
  3829. }
  3830. }
  3831.  
  3832. void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect)
  3833. {
  3834. SpectatorVec list;
  3835. map.getSpectators(list, fromPos, false, true);
  3836. map.getSpectators(list, toPos, false, true);
  3837. addDistanceEffect(list, fromPos, toPos, effect);
  3838. }
  3839.  
  3840. void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect)
  3841. {
  3842. for (Creature* spectator : list) {
  3843. if (Player* tmpPlayer = spectator->getPlayer()) {
  3844. tmpPlayer->sendDistanceShoot(fromPos, toPos, effect);
  3845. }
  3846. }
  3847. }
  3848.  
  3849. void Game::startDecay(Item* item)
  3850. {
  3851. if (!item || !item->canDecay()) {
  3852. return;
  3853. }
  3854.  
  3855. ItemDecayState_t decayState = item->getDecaying();
  3856. if (decayState == DECAYING_TRUE) {
  3857. return;
  3858. }
  3859.  
  3860. if (item->getDuration() > 0) {
  3861. item->incrementReferenceCounter();
  3862. item->setDecaying(DECAYING_TRUE);
  3863. toDecayItems.push_front(item);
  3864. } else {
  3865. internalDecayItem(item);
  3866. }
  3867. }
  3868.  
  3869. void Game::internalDecayItem(Item* item)
  3870. {
  3871. const ItemType& it = Item::items[item->getID()];
  3872. if (it.decayTo != 0) {
  3873. Item* newItem = transformItem(item, it.decayTo);
  3874. startDecay(newItem);
  3875. } else {
  3876. ReturnValue ret = internalRemoveItem(item);
  3877. if (ret != RETURNVALUE_NOERROR) {
  3878. std::cout << "[Debug - Game::internalDecayItem] internalDecayItem failed, error code: " << static_cast<uint32_t>(ret) << ", item id: " << item->getID() << std::endl;
  3879. }
  3880. }
  3881. }
  3882.  
  3883. void Game::checkDecay()
  3884. {
  3885. g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this)));
  3886.  
  3887. size_t bucket = (lastBucket + 1) % EVENT_DECAY_BUCKETS;
  3888.  
  3889. auto it = decayItems[bucket].begin(), end = decayItems[bucket].end();
  3890. while (it != end) {
  3891. Item* item = *it;
  3892. if (!item->canDecay()) {
  3893. item->setDecaying(DECAYING_FALSE);
  3894. ReleaseItem(item);
  3895. it = decayItems[bucket].erase(it);
  3896. continue;
  3897. }
  3898.  
  3899. int32_t duration = item->getDuration();
  3900. int32_t decreaseTime = std::min<int32_t>(EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS, duration);
  3901.  
  3902. duration -= decreaseTime;
  3903. item->decreaseDuration(decreaseTime);
  3904.  
  3905. if (duration <= 0) {
  3906. it = decayItems[bucket].erase(it);
  3907. internalDecayItem(item);
  3908. ReleaseItem(item);
  3909. } else if (duration < EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
  3910. it = decayItems[bucket].erase(it);
  3911. size_t newBucket = (bucket + ((duration + EVENT_DECAYINTERVAL / 2) / 1000)) % EVENT_DECAY_BUCKETS;
  3912. if (newBucket == bucket) {
  3913. internalDecayItem(item);
  3914. ReleaseItem(item);
  3915. } else {
  3916. decayItems[newBucket].push_back(item);
  3917. }
  3918. } else {
  3919. ++it;
  3920. }
  3921. }
  3922.  
  3923. lastBucket = bucket;
  3924. cleanup();
  3925. }
  3926.  
  3927. void Game::checkLight()
  3928. {
  3929. g_scheduler.addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, std::bind(&Game::checkLight, this)));
  3930.  
  3931. lightHour += lightHourDelta;
  3932.  
  3933. if (lightHour > 1440) {
  3934. lightHour -= 1440;
  3935. }
  3936.  
  3937. if (std::abs(lightHour - SUNRISE) < 2 * lightHourDelta) {
  3938. lightState = LIGHT_STATE_SUNRISE;
  3939. } else if (std::abs(lightHour - SUNSET) < 2 * lightHourDelta) {
  3940. lightState = LIGHT_STATE_SUNSET;
  3941. }
  3942.  
  3943. int32_t newLightLevel = lightLevel;
  3944. bool lightChange = false;
  3945.  
  3946. switch (lightState) {
  3947. case LIGHT_STATE_SUNRISE: {
  3948. newLightLevel += (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
  3949. lightChange = true;
  3950. break;
  3951. }
  3952. case LIGHT_STATE_SUNSET: {
  3953. newLightLevel -= (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
  3954. lightChange = true;
  3955. break;
  3956. }
  3957. default:
  3958. break;
  3959. }
  3960.  
  3961. if (newLightLevel <= LIGHT_LEVEL_NIGHT) {
  3962. lightLevel = LIGHT_LEVEL_NIGHT;
  3963. lightState = LIGHT_STATE_NIGHT;
  3964. } else if (newLightLevel >= LIGHT_LEVEL_DAY) {
  3965. lightLevel = LIGHT_LEVEL_DAY;
  3966. lightState = LIGHT_STATE_DAY;
  3967. } else {
  3968. lightLevel = newLightLevel;
  3969. }
  3970.  
  3971. if (lightChange) {
  3972. LightInfo lightInfo;
  3973. getWorldLightInfo(lightInfo);
  3974.  
  3975. for (const auto& it : players) {
  3976. it.second->sendWorldLight(lightInfo);
  3977. }
  3978. }
  3979. }
  3980.  
  3981. void Game::getWorldLightInfo(LightInfo& lightInfo) const
  3982. {
  3983. lightInfo.level = lightLevel;
  3984. lightInfo.color = 0xD7;
  3985. }
  3986.  
  3987. void Game::addCommandTag(char tag)
  3988. {
  3989. for (char commandTag : commandTags) {
  3990. if (commandTag == tag) {
  3991. return;
  3992. }
  3993. }
  3994. commandTags.push_back(tag);
  3995. }
  3996.  
  3997. void Game::resetCommandTag()
  3998. {
  3999. commandTags.clear();
  4000. }
  4001.  
  4002. void Game::shutdown()
  4003. {
  4004. std::cout << "Shutting down..." << std::flush;
  4005.  
  4006. g_scheduler.shutdown();
  4007. g_databaseTasks.shutdown();
  4008. g_dispatcher.shutdown();
  4009. map.spawns.clear();
  4010. raids.clear();
  4011.  
  4012. cleanup();
  4013.  
  4014. if (serviceManager) {
  4015. serviceManager->stop();
  4016. }
  4017.  
  4018. ConnectionManager::getInstance().closeAll();
  4019.  
  4020. std::cout << " done!" << std::endl;
  4021. }
  4022.  
  4023. void Game::cleanup()
  4024. {
  4025. //free memory
  4026. for (auto creature : ToReleaseCreatures) {
  4027. creature->decrementReferenceCounter();
  4028. }
  4029. ToReleaseCreatures.clear();
  4030.  
  4031. for (auto item : ToReleaseItems) {
  4032. item->decrementReferenceCounter();
  4033. }
  4034. ToReleaseItems.clear();
  4035.  
  4036. for (Item* item : toDecayItems) {
  4037. const uint32_t dur = item->getDuration();
  4038. if (dur >= EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
  4039. decayItems[lastBucket].push_back(item);
  4040. } else {
  4041. decayItems[(lastBucket + 1 + dur / 1000) % EVENT_DECAY_BUCKETS].push_back(item);
  4042. }
  4043. }
  4044. toDecayItems.clear();
  4045. }
  4046.  
  4047. void Game::ReleaseCreature(Creature* creature)
  4048. {
  4049. ToReleaseCreatures.push_back(creature);
  4050. }
  4051.  
  4052. void Game::ReleaseItem(Item* item)
  4053. {
  4054. ToReleaseItems.push_back(item);
  4055. }
  4056.  
  4057. void Game::broadcastMessage(const std::string& text, MessageClasses type) const
  4058. {
  4059. std::cout << "> Broadcasted message: \"" << text << "\"." << std::endl;
  4060. for (const auto& it : players) {
  4061. it.second->sendTextMessage(type, text);
  4062. }
  4063. }
  4064.  
  4065. void Game::updateCreatureSkull(const Creature* creature)
  4066. {
  4067. if (getWorldType() != WORLD_TYPE_PVP) {
  4068. return;
  4069. }
  4070.  
  4071. SpectatorVec list;
  4072. map.getSpectators(list, creature->getPosition(), true, true);
  4073. for (Creature* spectator : list) {
  4074. spectator->getPlayer()->sendCreatureSkull(creature);
  4075. }
  4076. }
  4077.  
  4078. void Game::updatePlayerShield(Player* player)
  4079. {
  4080. SpectatorVec list;
  4081. map.getSpectators(list, player->getPosition(), true, true);
  4082. for (Creature* spectator : list) {
  4083. spectator->getPlayer()->sendCreatureShield(player);
  4084. }
  4085. }
  4086.  
  4087. void Game::updatePremium(Account& account)
  4088. {
  4089. bool save = false;
  4090. time_t timeNow = time(nullptr);
  4091.  
  4092. if (account.premiumDays != 0 && account.premiumDays != std::numeric_limits<uint16_t>::max()) {
  4093. if (account.lastDay == 0) {
  4094. account.lastDay = timeNow;
  4095. save = true;
  4096. } else {
  4097. uint32_t days = (timeNow - account.lastDay) / 86400;
  4098. if (days > 0) {
  4099. if (days >= account.premiumDays) {
  4100. account.premiumDays = 0;
  4101. account.lastDay = 0;
  4102. } else {
  4103. account.premiumDays -= days;
  4104. time_t remainder = (timeNow - account.lastDay) % 86400;
  4105. account.lastDay = timeNow - remainder;
  4106. }
  4107.  
  4108. save = true;
  4109. }
  4110. }
  4111. } else if (account.lastDay != 0) {
  4112. account.lastDay = 0;
  4113. save = true;
  4114. }
  4115.  
  4116. if (save && !IOLoginData::saveAccount(account)) {
  4117. std::cout << "> ERROR: Failed to save account: " << account.name << "!" << std::endl;
  4118. }
  4119. }
  4120.  
  4121. void Game::loadMotdNum()
  4122. {
  4123. Database& db = Database::getInstance();
  4124.  
  4125. DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_num'");
  4126. if (result) {
  4127. motdNum = result->getNumber<uint32_t>("value");
  4128. } else {
  4129. db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_num', '0')");
  4130. }
  4131.  
  4132. result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_hash'");
  4133. if (result) {
  4134. motdHash = result->getString("value");
  4135. if (motdHash != transformToSHA1(g_config.getString(ConfigManager::MOTD))) {
  4136. ++motdNum;
  4137. }
  4138. } else {
  4139. db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_hash', '')");
  4140. }
  4141. }
  4142.  
  4143. void Game::saveMotdNum() const
  4144. {
  4145. Database& db = Database::getInstance();
  4146.  
  4147. std::ostringstream query;
  4148. query << "UPDATE `server_config` SET `value` = '" << motdNum << "' WHERE `config` = 'motd_num'";
  4149. db.executeQuery(query.str());
  4150.  
  4151. query.str(std::string());
  4152. query << "UPDATE `server_config` SET `value` = '" << transformToSHA1(g_config.getString(ConfigManager::MOTD)) << "' WHERE `config` = 'motd_hash'";
  4153. db.executeQuery(query.str());
  4154. }
  4155.  
  4156. void Game::checkPlayersRecord()
  4157. {
  4158. const size_t playersOnline = getPlayersOnline();
  4159. if (playersOnline > playersRecord) {
  4160. uint32_t previousRecord = playersRecord;
  4161. playersRecord = playersOnline;
  4162.  
  4163. for (const auto& it : g_globalEvents->getEventMap(GLOBALEVENT_RECORD)) {
  4164. it.second->executeRecord(playersRecord, previousRecord);
  4165. }
  4166. updatePlayersRecord();
  4167. }
  4168. }
  4169.  
  4170. void Game::updatePlayersRecord() const
  4171. {
  4172. Database& db = Database::getInstance();
  4173.  
  4174. std::ostringstream query;
  4175. query << "UPDATE `server_config` SET `value` = '" << playersRecord << "' WHERE `config` = 'players_record'";
  4176. db.executeQuery(query.str());
  4177. }
  4178.  
  4179. void Game::loadPlayersRecord()
  4180. {
  4181. Database& db = Database::getInstance();
  4182.  
  4183. DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'players_record'");
  4184. if (result) {
  4185. playersRecord = result->getNumber<uint32_t>("value");
  4186. } else {
  4187. db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('players_record', '0')");
  4188. }
  4189. }
  4190.  
  4191. uint64_t Game::getExperienceStage(uint32_t level)
  4192. {
  4193. if (!stagesEnabled) {
  4194. return g_config.getNumber(ConfigManager::RATE_EXPERIENCE);
  4195. }
  4196.  
  4197. if (useLastStageLevel && level >= lastStageLevel) {
  4198. return stages[lastStageLevel];
  4199. }
  4200.  
  4201. return stages[level];
  4202. }
  4203.  
  4204. bool Game::loadExperienceStages()
  4205. {
  4206. pugi::xml_document doc;
  4207. pugi::xml_parse_result result = doc.load_file("data/XML/stages.xml");
  4208. if (!result) {
  4209. printXMLError("Error - Game::loadExperienceStages", "data/XML/stages.xml", result);
  4210. return false;
  4211. }
  4212.  
  4213. for (auto stageNode : doc.child("stages").children()) {
  4214. if (strcasecmp(stageNode.name(), "config") == 0) {
  4215. stagesEnabled = stageNode.attribute("enabled").as_bool();
  4216. } else {
  4217. uint32_t minLevel, maxLevel, multiplier;
  4218.  
  4219. pugi::xml_attribute minLevelAttribute = stageNode.attribute("minlevel");
  4220. if (minLevelAttribute) {
  4221. minLevel = pugi::cast<uint32_t>(minLevelAttribute.value());
  4222. } else {
  4223. minLevel = 1;
  4224. }
  4225.  
  4226. pugi::xml_attribute maxLevelAttribute = stageNode.attribute("maxlevel");
  4227. if (maxLevelAttribute) {
  4228. maxLevel = pugi::cast<uint32_t>(maxLevelAttribute.value());
  4229. } else {
  4230. maxLevel = 0;
  4231. lastStageLevel = minLevel;
  4232. useLastStageLevel = true;
  4233. }
  4234.  
  4235. pugi::xml_attribute multiplierAttribute = stageNode.attribute("multiplier");
  4236. if (multiplierAttribute) {
  4237. multiplier = pugi::cast<uint32_t>(multiplierAttribute.value());
  4238. } else {
  4239. multiplier = 1;
  4240. }
  4241.  
  4242. if (useLastStageLevel) {
  4243. stages[lastStageLevel] = multiplier;
  4244. } else {
  4245. for (uint32_t i = minLevel; i <= maxLevel; ++i) {
  4246. stages[i] = multiplier;
  4247. }
  4248. }
  4249. }
  4250. }
  4251. return true;
  4252. }
  4253.  
  4254. void Game::playerInviteToParty(uint32_t playerId, uint32_t invitedId)
  4255. {
  4256. Player* player = getPlayerByID(playerId);
  4257. if (!player) {
  4258. return;
  4259. }
  4260.  
  4261. Player* invitedPlayer = getPlayerByID(invitedId);
  4262. if (!invitedPlayer || invitedPlayer->isInviting(player)) {
  4263. return;
  4264. }
  4265.  
  4266. if (invitedPlayer->getParty()) {
  4267. std::ostringstream ss;
  4268. ss << invitedPlayer->getName() << " is already in a party.";
  4269. player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
  4270. return;
  4271. }
  4272.  
  4273. Party* party = player->getParty();
  4274. if (!party) {
  4275. party = new Party(player);
  4276. } else if (party->getLeader() != player) {
  4277. return;
  4278. }
  4279.  
  4280. party->invitePlayer(*invitedPlayer);
  4281. }
  4282.  
  4283. void Game::playerJoinParty(uint32_t playerId, uint32_t leaderId)
  4284. {
  4285. Player* player = getPlayerByID(playerId);
  4286. if (!player) {
  4287. return;
  4288. }
  4289.  
  4290. Player* leader = getPlayerByID(leaderId);
  4291. if (!leader || !leader->isInviting(player)) {
  4292. return;
  4293. }
  4294.  
  4295. Party* party = leader->getParty();
  4296. if (!party || party->getLeader() != leader) {
  4297. return;
  4298. }
  4299.  
  4300. if (player->getParty()) {
  4301. player->sendTextMessage(MESSAGE_INFO_DESCR, "You are already in a party.");
  4302. return;
  4303. }
  4304.  
  4305. party->joinParty(*player);
  4306. }
  4307.  
  4308. void Game::playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId)
  4309. {
  4310. Player* player = getPlayerByID(playerId);
  4311. if (!player) {
  4312. return;
  4313. }
  4314.  
  4315. Party* party = player->getParty();
  4316. if (!party || party->getLeader() != player) {
  4317. return;
  4318. }
  4319.  
  4320. Player* invitedPlayer = getPlayerByID(invitedId);
  4321. if (!invitedPlayer || !player->isInviting(invitedPlayer)) {
  4322. return;
  4323. }
  4324.  
  4325. party->revokeInvitation(*invitedPlayer);
  4326. }
  4327.  
  4328. void Game::playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId)
  4329. {
  4330. Player* player = getPlayerByID(playerId);
  4331. if (!player) {
  4332. return;
  4333. }
  4334.  
  4335. Party* party = player->getParty();
  4336. if (!party || party->getLeader() != player) {
  4337. return;
  4338. }
  4339.  
  4340. Player* newLeader = getPlayerByID(newLeaderId);
  4341. if (!newLeader || !player->isPartner(newLeader)) {
  4342. return;
  4343. }
  4344.  
  4345. party->passPartyLeadership(newLeader);
  4346. }
  4347.  
  4348. void Game::playerLeaveParty(uint32_t playerId)
  4349. {
  4350. Player* player = getPlayerByID(playerId);
  4351. if (!player) {
  4352. return;
  4353. }
  4354.  
  4355. Party* party = player->getParty();
  4356. if (!party || player->hasCondition(CONDITION_INFIGHT)) {
  4357. return;
  4358. }
  4359.  
  4360. party->leaveParty(player);
  4361. }
  4362.  
  4363. void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
  4364. {
  4365. Player* player = getPlayerByID(playerId);
  4366. if (!player) {
  4367. return;
  4368. }
  4369.  
  4370. Party* party = player->getParty();
  4371. if (!party || player->hasCondition(CONDITION_INFIGHT)) {
  4372. return;
  4373. }
  4374.  
  4375. party->setSharedExperience(player, sharedExpActive);
  4376. }
  4377.  
  4378. void Game::sendGuildMotd(uint32_t playerId)
  4379. {
  4380. Player* player = getPlayerByID(playerId);
  4381. if (!player) {
  4382. return;
  4383. }
  4384.  
  4385. Guild* guild = player->getGuild();
  4386. if (guild) {
  4387. player->sendChannelMessage("Message of the Day", guild->getMotd(), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD);
  4388. }
  4389. }
  4390.  
  4391. void Game::kickPlayer(uint32_t playerId, bool displayEffect)
  4392. {
  4393. Player* player = getPlayerByID(playerId);
  4394. if (!player) {
  4395. return;
  4396. }
  4397.  
  4398. player->kickPlayer(displayEffect);
  4399. }
  4400.  
  4401. void Game::playerReportBug(uint32_t playerId, const std::string& bug)
  4402. {
  4403. Player* player = getPlayerByID(playerId);
  4404. if (!player) {
  4405. return;
  4406. }
  4407.  
  4408. if (player->getAccountType() == ACCOUNT_TYPE_NORMAL) {
  4409. return;
  4410. }
  4411.  
  4412. std::string fileName = "data/reports/" + player->getName() + " report.txt";
  4413. FILE* file = fopen(fileName.c_str(), "a");
  4414. if (!file) {
  4415. player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster.");
  4416. return;
  4417. }
  4418.  
  4419. const Position& position = player->getPosition();
  4420. fprintf(file, "------------------------------\nName: %s [Position X: %u Y: %u Z: %u]\nBug Report: %s\n", player->getName().c_str(), position.x, position.y, position.z, bug.c_str());
  4421. fclose(file);
  4422.  
  4423. player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " + g_config.getString(ConfigManager::SERVER_NAME) + ".");
  4424. }
  4425.  
  4426. void Game::playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment)
  4427. {
  4428. Player* player = getPlayerByID(playerId);
  4429. if (!player) {
  4430. return;
  4431. }
  4432.  
  4433. // TODO: move debug assertions to database
  4434. FILE* file = fopen("client_assertions.txt", "a");
  4435. if (file) {
  4436. fprintf(file, "----- %s - %s (%s) -----\n", formatDate(time(nullptr)).c_str(), player->getName().c_str(), convertIPToString(player->getIP()).c_str());
  4437. fprintf(file, "%s\n%s\n%s\n%s\n", assertLine.c_str(), date.c_str(), description.c_str(), comment.c_str());
  4438. fclose(file);
  4439. }
  4440. }
  4441.  
  4442. void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer)
  4443. {
  4444. Player* player = getPlayerByID(playerId);
  4445. if (!player) {
  4446. return;
  4447. }
  4448.  
  4449. for (CreatureEvent* creatureEvent : player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE)) {
  4450. creatureEvent->executeExtendedOpcode(player, opcode, buffer);
  4451. }
  4452. }
  4453.  
  4454. void Game::forceAddCondition(uint32_t creatureId, Condition* condition)
  4455. {
  4456. Creature* creature = getCreatureByID(creatureId);
  4457. if (!creature) {
  4458. delete condition;
  4459. return;
  4460. }
  4461.  
  4462. creature->addCondition(condition, true);
  4463. }
  4464.  
  4465. void Game::forceRemoveCondition(uint32_t creatureId, ConditionType_t type)
  4466. {
  4467. Creature* creature = getCreatureByID(creatureId);
  4468. if (!creature) {
  4469. return;
  4470. }
  4471.  
  4472. creature->removeCondition(type, true);
  4473. }
  4474.  
  4475. void Game::addPlayer(Player* player)
  4476. {
  4477. const std::string& lowercase_name = asLowerCaseString(player->getName());
  4478. mappedPlayerNames[lowercase_name] = player;
  4479. wildcardTree.insert(lowercase_name);
  4480. players[player->getID()] = player;
  4481. }
  4482.  
  4483. void Game::removePlayer(Player* player)
  4484. {
  4485. const std::string& lowercase_name = asLowerCaseString(player->getName());
  4486. mappedPlayerNames.erase(lowercase_name);
  4487. wildcardTree.remove(lowercase_name);
  4488. players.erase(player->getID());
  4489. }
  4490.  
  4491. void Game::addNpc(Npc* npc)
  4492. {
  4493. npcs[npc->getID()] = npc;
  4494. }
  4495.  
  4496. void Game::removeNpc(Npc* npc)
  4497. {
  4498. npcs.erase(npc->getID());
  4499. }
  4500.  
  4501. void Game::addMonster(Monster* monster)
  4502. {
  4503. monsters[monster->getID()] = monster;
  4504. }
  4505.  
  4506. void Game::removeMonster(Monster* monster)
  4507. {
  4508. monsters.erase(monster->getID());
  4509. }
  4510.  
  4511. Guild* Game::getGuild(uint32_t id) const
  4512. {
  4513. auto it = guilds.find(id);
  4514. if (it == guilds.end()) {
  4515. return nullptr;
  4516. }
  4517. return it->second;
  4518. }
  4519.  
  4520. void Game::addGuild(Guild* guild)
  4521. {
  4522. guilds[guild->getId()] = guild;
  4523. }
  4524.  
  4525. void Game::removeGuild(uint32_t guildId)
  4526. {
  4527. guilds.erase(guildId);
  4528. }
  4529.  
  4530. void Game::internalRemoveItems(std::vector<Item*> itemList, uint32_t amount, bool stackable)
  4531. {
  4532. if (stackable) {
  4533. for (Item* item : itemList) {
  4534. if (item->getItemCount() > amount) {
  4535. internalRemoveItem(item, amount);
  4536. break;
  4537. } else {
  4538. amount -= item->getItemCount();
  4539. internalRemoveItem(item);
  4540. }
  4541. }
  4542. } else {
  4543. for (Item* item : itemList) {
  4544. internalRemoveItem(item);
  4545. }
  4546. }
  4547. }
  4548.  
  4549. BedItem* Game::getBedBySleeper(uint32_t guid) const
  4550. {
  4551. auto it = bedSleepersMap.find(guid);
  4552. if (it == bedSleepersMap.end()) {
  4553. return nullptr;
  4554. }
  4555. return it->second;
  4556. }
  4557.  
  4558. void Game::setBedSleeper(BedItem* bed, uint32_t guid)
  4559. {
  4560. bedSleepersMap[guid] = bed;
  4561. }
  4562.  
  4563. void Game::removeBedSleeper(uint32_t guid)
  4564. {
  4565. auto it = bedSleepersMap.find(guid);
  4566. if (it != bedSleepersMap.end()) {
  4567. bedSleepersMap.erase(it);
  4568. }
  4569. }
  4570.  
  4571. Item* Game::getUniqueItem(uint16_t uniqueId)
  4572. {
  4573. auto it = uniqueItems.find(uniqueId);
  4574. if (it == uniqueItems.end()) {
  4575. return nullptr;
  4576. }
  4577. return it->second;
  4578. }
  4579.  
  4580. bool Game::addUniqueItem(uint16_t uniqueId, Item* item)
  4581. {
  4582. auto result = uniqueItems.emplace(uniqueId, item);
  4583. if (!result.second) {
  4584. std::cout << "Duplicate unique id: " << uniqueId << std::endl;
  4585. }
  4586. return result.second;
  4587. }
  4588.  
  4589. void Game::removeUniqueItem(uint16_t uniqueId)
  4590. {
  4591. auto it = uniqueItems.find(uniqueId);
  4592. if (it != uniqueItems.end()) {
  4593. uniqueItems.erase(it);
  4594. }
  4595. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement