Advertisement
Guest User

Untitled

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