Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * The Forgotten Server - a free and open-source MMORPG server emulator
- * Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "otpch.h"
- #include "pugicast.h"
- #include "items.h"
- #include "commands.h"
- #include "creature.h"
- #include "monster.h"
- #include "game.h"
- #include "actions.h"
- #include "iologindata.h"
- #include "talkaction.h"
- #include "spells.h"
- #include "configmanager.h"
- #include "server.h"
- #include "globalevent.h"
- #include "bed.h"
- #include "scheduler.h"
- #include "events.h"
- #include "databasetasks.h"
- extern ConfigManager g_config;
- extern Actions* g_actions;
- extern Chat* g_chat;
- extern TalkActions* g_talkActions;
- extern Spells* g_spells;
- extern Vocations g_vocations;
- extern GlobalEvents* g_globalEvents;
- extern Events* g_events;
- Game::Game() :
- wildcardTree(false)
- {
- gameState = GAME_STATE_NORMAL;
- worldType = WORLD_TYPE_PVP;
- serviceManager = nullptr;
- lastStageLevel = 0;
- playersRecord = 0;
- motdNum = 0;
- useLastStageLevel = false;
- stagesEnabled = false;
- lastBucket = 0;
- //(1440 minutes/day)/(3600 seconds/day)*10 seconds event interval
- int32_t dayCycle = 3600;
- lightHourDelta = 1440 * 10 / dayCycle;
- lightHour = SUNRISE + (SUNSET - SUNRISE) / 2;
- lightLevel = LIGHT_LEVEL_DAY;
- lightState = LIGHT_STATE_DAY;
- }
- Game::~Game()
- {
- for (const auto& it : guilds) {
- delete it.second;
- }
- }
- void Game::start(ServiceManager* manager)
- {
- serviceManager = manager;
- g_scheduler.addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, std::bind(&Game::checkLight, this)));
- g_scheduler.addEvent(createSchedulerTask(EVENT_CREATURE_THINK_INTERVAL, std::bind(&Game::checkCreatures, this, 0)));
- g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this)));
- }
- GameState_t Game::getGameState() const
- {
- return gameState;
- }
- void Game::setWorldType(WorldType_t type)
- {
- worldType = type;
- }
- void Game::setGameState(GameState_t newState)
- {
- if (gameState == GAME_STATE_SHUTDOWN) {
- return; //this cannot be stopped
- }
- if (gameState == newState) {
- return;
- }
- gameState = newState;
- switch (newState) {
- case GAME_STATE_INIT: {
- commands.loadFromXml();
- loadExperienceStages();
- groups.load();
- g_chat->load();
- map.spawns.startup();
- raids.loadFromXml();
- raids.startup();
- quests.loadFromXml();
- loadMotdNum();
- loadPlayersRecord();
- g_globalEvents->startup();
- break;
- }
- case GAME_STATE_SHUTDOWN: {
- g_globalEvents->execute(GLOBALEVENT_SHUTDOWN);
- //kick all players that are still online
- auto it = players.begin();
- while (it != players.end()) {
- it->second->kickPlayer(true);
- it = players.begin();
- }
- saveMotdNum();
- saveGameState();
- g_dispatcher.addTask(
- createTask(std::bind(&Game::shutdown, this)));
- g_scheduler.stop();
- g_databaseTasks.stop();
- g_dispatcher.stop();
- break;
- }
- case GAME_STATE_CLOSED: {
- /* kick all players without the CanAlwaysLogin flag */
- auto it = players.begin();
- while (it != players.end()) {
- if (!it->second->hasFlag(PlayerFlag_CanAlwaysLogin)) {
- it->second->kickPlayer(true);
- it = players.begin();
- } else {
- ++it;
- }
- }
- saveGameState();
- break;
- }
- default:
- break;
- }
- }
- void Game::saveGameState()
- {
- if (gameState == GAME_STATE_NORMAL) {
- setGameState(GAME_STATE_MAINTAIN);
- }
- std::cout << "Saving server..." << std::endl;
- for (const auto& it : players) {
- it.second->loginPosition = it.second->getPosition();
- IOLoginData::savePlayer(it.second);
- }
- Map::save();
- if (gameState == GAME_STATE_MAINTAIN) {
- setGameState(GAME_STATE_NORMAL);
- }
- }
- bool Game::loadMainMap(const std::string& filename)
- {
- Monster::despawnRange = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRANGE);
- Monster::despawnRadius = g_config.getNumber(ConfigManager::DEFAULT_DESPAWNRADIUS);
- return map.loadMap("data/world/" + filename + ".otbm", true);
- }
- void Game::loadMap(const std::string& path)
- {
- map.loadMap(path, false);
- }
- Cylinder* Game::internalGetCylinder(Player* player, const Position& pos) const
- {
- if (pos.x != 0xFFFF) {
- return map.getTile(pos);
- }
- //container
- if (pos.y & 0x40) {
- uint8_t from_cid = pos.y & 0x0F;
- return player->getContainerByID(from_cid);
- }
- //inventory
- return player;
- }
- Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index, uint32_t spriteId, stackPosType_t type) const
- {
- if (pos.x != 0xFFFF) {
- Tile* tile = map.getTile(pos);
- if (!tile) {
- return nullptr;
- }
- Thing* thing;
- switch (type) {
- case STACKPOS_LOOK: {
- return tile->getTopVisibleThing(player);
- }
- case STACKPOS_MOVE: {
- Item* item = tile->getTopDownItem();
- if (item && item->isMoveable()) {
- thing = item;
- } else {
- thing = tile->getTopVisibleCreature(player);
- }
- break;
- }
- case STACKPOS_USEITEM: {
- thing = tile->getUseItem();
- break;
- }
- case STACKPOS_TOPDOWN_ITEM: {
- thing = tile->getTopDownItem();
- break;
- }
- case STACKPOS_USETARGET: {
- thing = tile->getTopVisibleCreature(player);
- if (!thing) {
- thing = tile->getUseItem();
- }
- break;
- }
- default: {
- thing = nullptr;
- break;
- }
- }
- if (player && tile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
- //do extra checks here if the thing is accessable
- if (thing && thing->getItem()) {
- if (tile->hasProperty(CONST_PROP_ISVERTICAL)) {
- if (player->getPosition().x + 1 == tile->getPosition().x) {
- thing = nullptr;
- }
- } else { // horizontal
- if (player->getPosition().y + 1 == tile->getPosition().y) {
- thing = nullptr;
- }
- }
- }
- }
- return thing;
- }
- //container
- if (pos.y & 0x40) {
- uint8_t fromCid = pos.y & 0x0F;
- Container* parentContainer = player->getContainerByID(fromCid);
- if (!parentContainer) {
- return nullptr;
- }
- uint8_t slot = pos.z;
- return parentContainer->getItemByIndex(player->getContainerIndex(fromCid) + slot);
- } else if (pos.y == 0 && pos.z == 0) {
- const ItemType& it = Item::items.getItemIdByClientId(spriteId);
- if (it.id == 0) {
- return nullptr;
- }
- int32_t subType;
- if (it.isFluidContainer() && index < static_cast<int32_t>(sizeof(reverseFluidMap) / sizeof(uint8_t))) {
- subType = reverseFluidMap[index];
- } else {
- subType = -1;
- }
- return findItemOfType(player, it.id, true, subType);
- }
- //inventory
- slots_t slot = static_cast<slots_t>(pos.y);
- return player->getInventoryItem(slot);
- }
- void Game::internalGetPosition(Item* item, Position& pos, uint8_t& stackpos)
- {
- pos.x = 0;
- pos.y = 0;
- pos.z = 0;
- stackpos = 0;
- Cylinder* topParent = item->getTopParent();
- if (topParent) {
- if (Player* player = dynamic_cast<Player*>(topParent)) {
- pos.x = 0xFFFF;
- Container* container = dynamic_cast<Container*>(item->getParent());
- if (container) {
- pos.y = static_cast<uint16_t>(0x40) | static_cast<uint16_t>(player->getContainerID(container));
- pos.z = container->getThingIndex(item);
- stackpos = pos.z;
- } else {
- pos.y = player->getThingIndex(item);
- stackpos = pos.y;
- }
- } else if (Tile* tile = topParent->getTile()) {
- pos = tile->getPosition();
- stackpos = tile->getThingIndex(item);
- }
- }
- }
- Creature* Game::getCreatureByID(uint32_t id)
- {
- if (id <= Player::playerAutoID) {
- return getPlayerByID(id);
- } else if (id <= Monster::monsterAutoID) {
- return getMonsterByID(id);
- } else if (id <= Npc::npcAutoID) {
- return getNpcByID(id);
- }
- return nullptr;
- }
- Monster* Game::getMonsterByID(uint32_t id)
- {
- if (id == 0) {
- return nullptr;
- }
- auto it = monsters.find(id);
- if (it == monsters.end()) {
- return nullptr;
- }
- return it->second;
- }
- Npc* Game::getNpcByID(uint32_t id)
- {
- if (id == 0) {
- return nullptr;
- }
- auto it = npcs.find(id);
- if (it == npcs.end()) {
- return nullptr;
- }
- return it->second;
- }
- Player* Game::getPlayerByID(uint32_t id)
- {
- if (id == 0) {
- return nullptr;
- }
- auto it = players.find(id);
- if (it == players.end()) {
- return nullptr;
- }
- return it->second;
- }
- Creature* Game::getCreatureByName(const std::string& s)
- {
- if (s.empty()) {
- return nullptr;
- }
- const std::string& lowerCaseName = asLowerCaseString(s);
- auto m_it = mappedPlayerNames.find(lowerCaseName);
- if (m_it != mappedPlayerNames.end()) {
- return m_it->second;
- }
- for (const auto& it : npcs) {
- if (lowerCaseName == asLowerCaseString(it.second->getName())) {
- return it.second;
- }
- }
- for (const auto& it : monsters) {
- if (lowerCaseName == asLowerCaseString(it.second->getName())) {
- return it.second;
- }
- }
- return nullptr;
- }
- Npc* Game::getNpcByName(const std::string& s)
- {
- if (s.empty()) {
- return nullptr;
- }
- const char* npcName = s.c_str();
- for (const auto& it : npcs) {
- if (strcasecmp(npcName, it.second->getName().c_str()) == 0) {
- return it.second;
- }
- }
- return nullptr;
- }
- Player* Game::getPlayerByName(const std::string& s)
- {
- if (s.empty()) {
- return nullptr;
- }
- auto it = mappedPlayerNames.find(asLowerCaseString(s));
- if (it == mappedPlayerNames.end()) {
- return nullptr;
- }
- return it->second;
- }
- Player* Game::getPlayerByGUID(const uint32_t& guid)
- {
- if (guid == 0) {
- return nullptr;
- }
- for (const auto& it : players) {
- if (guid == it.second->getGUID()) {
- return it.second;
- }
- }
- return nullptr;
- }
- ReturnValue Game::getPlayerByNameWildcard(const std::string& s, Player*& player)
- {
- size_t strlen = s.length();
- if (strlen == 0 || strlen > 20) {
- return RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE;
- }
- if (s.back() == '~') {
- const std::string& query = asLowerCaseString(s.substr(0, strlen - 1));
- std::string result;
- ReturnValue ret = wildcardTree.findOne(query, result);
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- player = getPlayerByName(result);
- } else {
- player = getPlayerByName(s);
- }
- if (!player) {
- return RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE;
- }
- return RETURNVALUE_NOERROR;
- }
- Player* Game::getPlayerByAccount(uint32_t acc)
- {
- for (const auto& it : players) {
- if (it.second->getAccount() == acc) {
- return it.second;
- }
- }
- return nullptr;
- }
- bool Game::internalPlaceCreature(Creature* creature, const Position& pos, bool extendedPos /*=false*/, bool forced /*= false*/)
- {
- if (creature->getParent() != nullptr) {
- return false;
- }
- if (!map.placeCreature(pos, creature, extendedPos, forced)) {
- return false;
- }
- creature->incrementReferenceCounter();
- creature->setID();
- creature->addList();
- return true;
- }
- bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedPos /*=false*/, bool forced /*= false*/)
- {
- if (!internalPlaceCreature(creature, pos, extendedPos, forced)) {
- return false;
- }
- SpectatorVec list;
- map.getSpectators(list, creature->getPosition(), true);
- for (Creature* spectator : list) {
- if (Player* tmpPlayer = spectator->getPlayer()) {
- tmpPlayer->sendCreatureAppear(creature, creature->getPosition(), true);
- }
- }
- for (Creature* spectator : list) {
- spectator->onCreatureAppear(creature, true);
- }
- creature->getParent()->postAddNotification(creature, nullptr, 0);
- addCreatureCheck(creature);
- creature->onPlacedCreature();
- return true;
- }
- bool Game::removeCreature(Creature* creature, bool isLogout/* = true*/)
- {
- if (creature->isRemoved()) {
- return false;
- }
- Tile* tile = creature->getTile();
- std::vector<int32_t> oldStackPosVector;
- SpectatorVec list;
- map.getSpectators(list, tile->getPosition(), true);
- for (Creature* spectator : list) {
- if (Player* player = spectator->getPlayer()) {
- oldStackPosVector.push_back(player->canSeeCreature(creature) ? tile->getStackposOfCreature(player, creature) : -1);
- }
- }
- tile->removeCreature(creature);
- const Position& tilePosition = tile->getPosition();
- //send to client
- size_t i = 0;
- for (Creature* spectator : list) {
- if (Player* player = spectator->getPlayer()) {
- player->sendRemoveTileThing(tilePosition, oldStackPosVector[i++]);
- }
- }
- //event method
- for (Creature* spectator : list) {
- spectator->onRemoveCreature(creature, isLogout);
- }
- creature->getParent()->postRemoveNotification(creature, nullptr, 0);
- creature->removeList();
- creature->setRemoved();
- ReleaseCreature(creature);
- removeCreatureCheck(creature);
- for (Creature* summon : creature->summons) {
- summon->setLossSkill(false);
- removeCreature(summon);
- }
- return true;
- }
- void Game::playerMoveThing(uint32_t playerId, const Position& fromPos,
- uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- uint8_t fromIndex = 0;
- if (fromPos.x == 0xFFFF) {
- if (fromPos.y & 0x40) {
- fromIndex = fromPos.z;
- } else {
- fromIndex = static_cast<uint8_t>(fromPos.y);
- }
- } else {
- fromIndex = fromStackPos;
- }
- Thing* thing = internalGetThing(player, fromPos, fromIndex, 0, STACKPOS_MOVE);
- if (!thing) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- if (Creature* movingCreature = thing->getCreature()) {
- Tile* tile = map.getTile(toPos);
- if (!tile) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- if (Position::areInRange<1, 1, 0>(movingCreature->getPosition(), player->getPosition())) {
- SchedulerTask* task = createSchedulerTask(1000,
- std::bind(&Game::playerMoveCreatureByID, this, player->getID(),
- movingCreature->getID(), movingCreature->getPosition(), tile->getPosition()));
- player->setNextActionTask(task);
- } else {
- playerMoveCreature(player, movingCreature, movingCreature->getPosition(), tile);
- }
- } else if (thing->getItem()) {
- Cylinder* toCylinder = internalGetCylinder(player, toPos);
- if (!toCylinder) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- playerMoveItem(player, fromPos, spriteId, fromStackPos, toPos, count, thing->getItem(), toCylinder);
- }
- }
- void Game::playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, const Position& movingCreatureOrigPos, const Position& toPos)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Creature* movingCreature = getCreatureByID(movingCreatureId);
- if (!movingCreature) {
- return;
- }
- Tile* toTile = map.getTile(toPos);
- if (!toTile) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- playerMoveCreature(player, movingCreature, movingCreatureOrigPos, toTile);
- }
- void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile)
- {
- if (!player->canDoAction()) {
- uint32_t delay = player->getNextActionTime();
- SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()));
- player->setNextActionTask(task);
- return;
- }
- player->setNextActionTask(nullptr);
- if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) {
- //need to walk to the creature first before moving it
- std::forward_list<Direction> listDir;
- if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(1500, std::bind(&Game::playerMoveCreatureByID, this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition())); //1500 push
- player->setNextWalkActionTask(task);
- } else {
- player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
- }
- return;
- }
- if ((!movingCreature->isPushable() && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) ||
- (movingCreature->isInGhostMode() && !player->isAccessPlayer())) {
- player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
- return;
- }
- //check throw distance
- const Position& movingCreaturePos = movingCreature->getPosition();
- const Position& toPos = toTile->getPosition();
- if ((Position::getDistanceX(movingCreaturePos, toPos) > movingCreature->getThrowRange()) || (Position::getDistanceY(movingCreaturePos, toPos) > movingCreature->getThrowRange()) || (Position::getDistanceZ(movingCreaturePos, toPos) * 4 > movingCreature->getThrowRange())) {
- player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
- return;
- }
- if (player != movingCreature) {
- if (toTile->hasFlag(TILESTATE_BLOCKPATH)) {
- player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
- return;
- } else if ((movingCreature->getZone() == ZONE_PROTECTION && !toTile->hasFlag(TILESTATE_PROTECTIONZONE)) || (movingCreature->getZone() == ZONE_NOPVP && !toTile->hasFlag(TILESTATE_NOPVPZONE))) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- } else {
- if (CreatureVector* tileCreatures = toTile->getCreatures()) {
- for (Creature* tileCreature : *tileCreatures) {
- if (!tileCreature->isInGhostMode()) {
- player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
- return;
- }
- }
- }
- Npc* movingNpc = movingCreature->getNpc();
- if (movingNpc && !Spawns::isInZone(movingNpc->getMasterPos(), movingNpc->getMasterRadius(), toPos)) {
- player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
- return;
- }
- }
- }
- if (!g_events->eventPlayerOnMoveCreature(player, movingCreature, movingCreaturePos, toPos)) {
- return;
- }
- ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
- if (ret != RETURNVALUE_NOERROR) {
- player->sendCancelMessage(ret);
- }
- }
- ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, uint32_t flags /*= 0*/)
- {
- const Position& currentPos = creature->getPosition();
- Position destPos = getNextPosition(direction, currentPos);
- bool diagonalMovement = (direction & DIRECTION_DIAGONAL_MASK) != 0;
- if (creature->getPlayer() && !diagonalMovement) {
- //try go up
- if (currentPos.z != 8 && creature->getTile()->hasHeight(3)) {
- Tile* tmpTile = map.getTile(currentPos.x, currentPos.y, currentPos.getZ() - 1);
- if (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID))) {
- tmpTile = map.getTile(destPos.x, destPos.y, destPos.getZ() - 1);
- if (tmpTile && tmpTile->getGround() && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)) {
- flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
- if (!tmpTile->hasFlag(TILESTATE_FLOORCHANGE)) {
- destPos.z--;
- }
- }
- }
- } else {
- //try go down
- Tile* tmpTile = map.getTile(destPos.x, destPos.y, destPos.z);
- if (currentPos.z != 7 && (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)))) {
- tmpTile = map.getTile(destPos.x, destPos.y, destPos.z + 1);
- if (tmpTile && tmpTile->hasHeight(3)) {
- flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE;
- destPos.z++;
- }
- }
- }
- }
- Tile* toTile = map.getTile(destPos);
- if (!toTile) {
- return RETURNVALUE_NOTPOSSIBLE;
- }
- return internalMoveCreature(*creature, *toTile, flags);
- }
- ReturnValue Game::internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags /*= 0*/)
- {
- //check if we can move the creature to the destination
- ReturnValue ret = toTile.queryAdd(0, creature, 1, flags);
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- map.moveCreature(creature, toTile);
- if (creature.getParent() != &toTile) {
- return RETURNVALUE_NOERROR;
- }
- int32_t index = 0;
- Item* toItem = nullptr;
- Tile* subCylinder = nullptr;
- Tile* toCylinder = &toTile;
- Tile* fromCylinder = nullptr;
- uint32_t n = 0;
- while ((subCylinder = toCylinder->queryDestination(index, creature, &toItem, flags)) != toCylinder) {
- map.moveCreature(creature, *subCylinder);
- if (creature.getParent() != subCylinder) {
- //could happen if a script move the creature
- fromCylinder = nullptr;
- break;
- }
- fromCylinder = toCylinder;
- toCylinder = subCylinder;
- flags = 0;
- //to prevent infinite loop
- if (++n >= MAP_MAX_LAYERS) {
- break;
- }
- }
- if (fromCylinder) {
- const Position& fromPosition = fromCylinder->getPosition();
- const Position& toPosition = toCylinder->getPosition();
- if (fromPosition.z != toPosition.z && (fromPosition.x != toPosition.x || fromPosition.y != toPosition.y)) {
- Direction dir = getDirectionTo(fromPosition, toPosition);
- if ((dir & DIRECTION_DIAGONAL_MASK) == 0) {
- internalCreatureTurn(&creature, dir);
- }
- }
- }
- return RETURNVALUE_NOERROR;
- }
- void Game::playerMoveItemByPlayerID(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- playerMoveItem(player, fromPos, spriteId, fromStackPos, toPos, count, nullptr, nullptr);
- }
- void Game::playerMoveItem(Player* player, const Position& fromPos,
- uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count, Item* item, Cylinder* toCylinder)
- {
- if (!player->canDoAction()) {
- uint32_t delay = player->getNextActionTime();
- SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveItemByPlayerID, this,
- player->getID(), fromPos, spriteId, fromStackPos, toPos, count));
- player->setNextActionTask(task);
- return;
- }
- player->setNextActionTask(nullptr);
- if (item == nullptr) {
- uint8_t fromIndex = 0;
- if (fromPos.x == 0xFFFF) {
- if (fromPos.y & 0x40) {
- fromIndex = fromPos.z;
- } else {
- fromIndex = static_cast<uint8_t>(fromPos.y);
- }
- } else {
- fromIndex = fromStackPos;
- }
- Thing* thing = internalGetThing(player, fromPos, fromIndex, 0, STACKPOS_MOVE);
- if (!thing || !thing->getItem()) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- item = thing->getItem();
- }
- if (item->getClientID() != spriteId) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Cylinder* fromCylinder = internalGetCylinder(player, fromPos);
- if (fromCylinder == nullptr) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- if (toCylinder == nullptr) {
- toCylinder = internalGetCylinder(player, toPos);
- if (toCylinder == nullptr) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- }
- if (!item->isPushable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
- player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
- return;
- }
- const Position& playerPos = player->getPosition();
- const Position& mapFromPos = fromCylinder->getTile()->getPosition();
- if (playerPos.z != mapFromPos.z) {
- player->sendCancelMessage(playerPos.z > mapFromPos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS);
- return;
- }
- if (!Position::areInRange<1, 1>(playerPos, mapFromPos)) {
- //need to walk to the item first before using it
- std::forward_list<Direction> listDir;
- if (player->getPathTo(item->getPosition(), listDir, 0, 1, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
- this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this,
- player->getID(), fromPos, spriteId, fromStackPos, toPos, count));
- player->setNextWalkActionTask(task);
- } else {
- player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
- }
- return;
- }
- const Tile* toCylinderTile = toCylinder->getTile();
- const Position& mapToPos = toCylinderTile->getPosition();
- //hangable item specific code
- if (item->isHangable() && toCylinderTile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
- //destination supports hangable objects so need to move there first
- bool vertical = toCylinderTile->hasProperty(CONST_PROP_ISVERTICAL);
- if (vertical) {
- if (playerPos.x + 1 == mapToPos.x) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- } else { // horizontal
- if (playerPos.y + 1 == mapToPos.y) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- }
- if (!Position::areInRange<1, 1, 0>(playerPos, mapToPos)) {
- Position walkPos = mapToPos;
- if (vertical) {
- walkPos.x++;
- } else {
- walkPos.y++;
- }
- Position itemPos = fromPos;
- uint8_t itemStackPos = fromStackPos;
- if (fromPos.x != 0xFFFF && Position::areInRange<1, 1>(mapFromPos, playerPos)
- && !Position::areInRange<1, 1, 0>(mapFromPos, walkPos)) {
- //need to pickup the item first
- Item* moveItem = nullptr;
- ReturnValue ret = internalMoveItem(fromCylinder, player, INDEX_WHEREEVER, item, count, &moveItem);
- if (ret != RETURNVALUE_NOERROR) {
- player->sendCancelMessage(ret);
- return;
- }
- //changing the position since its now in the inventory of the player
- internalGetPosition(moveItem, itemPos, itemStackPos);
- }
- std::forward_list<Direction> listDir;
- if (player->getPathTo(walkPos, listDir, 0, 0, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
- this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerMoveItemByPlayerID, this,
- player->getID(), itemPos, spriteId, itemStackPos, toPos, count));
- player->setNextWalkActionTask(task);
- } else {
- player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
- }
- return;
- }
- }
- if ((Position::getDistanceX(playerPos, mapToPos) > item->getThrowRange()) ||
- (Position::getDistanceY(playerPos, mapToPos) > item->getThrowRange()) ||
- (Position::getDistanceZ(mapFromPos, mapToPos) * 4 > item->getThrowRange())) {
- player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
- return;
- }
- if (!canThrowObjectTo(mapFromPos, mapToPos, true, MAPPATH_ITEM)) {
- player->sendCancelMessage(RETURNVALUE_CANNOTTHROW);
- return;
- }
- if (!g_events->eventPlayerOnMoveItem(player, item, count, fromPos, toPos)) {
- return;
- }
- uint8_t toIndex = 0;
- if (toPos.x == 0xFFFF) {
- if (toPos.y & 0x40) {
- toIndex = toPos.z;
- } else {
- toIndex = static_cast<uint8_t>(toPos.y);
- }
- }
- ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, item->isRune() ? item->getItemCount() : count, nullptr, 0, player);
- if (ret != RETURNVALUE_NOERROR) {
- player->sendCancelMessage(ret);
- }
- }
- ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index,
- Item* item, uint32_t count, Item** _moveItem, uint32_t flags /*= 0*/, Creature* actor/* = nullptr*/, Item* tradeItem/* = nullptr*/)
- {
- Item* toItem = nullptr;
- Cylinder* subCylinder;
- int floorN = 0;
- while ((subCylinder = toCylinder->queryDestination(index, *item, &toItem, flags)) != toCylinder) {
- toCylinder = subCylinder;
- flags = 0;
- //to prevent infinite loop
- if (++floorN >= MAP_MAX_LAYERS) {
- break;
- }
- }
- //destination is the same as the source?
- if (item == toItem) {
- return RETURNVALUE_NOERROR; //silently ignore move
- }
- //check if we can add this item
- ReturnValue ret = toCylinder->queryAdd(index, *item, count, flags, actor);
- if (ret == RETURNVALUE_NEEDEXCHANGE) {
- //check if we can add it to source cylinder
- ret = fromCylinder->queryAdd(fromCylinder->getThingIndex(item), *toItem, toItem->getItemCount(), 0);
- if (ret == RETURNVALUE_NOERROR) {
- //check how much we can move
- uint32_t maxExchangeQueryCount = 0;
- ReturnValue retExchangeMaxCount = fromCylinder->queryMaxCount(INDEX_WHEREEVER, *toItem, toItem->getItemCount(), maxExchangeQueryCount, 0);
- if (retExchangeMaxCount != RETURNVALUE_NOERROR && maxExchangeQueryCount == 0) {
- return retExchangeMaxCount;
- }
- if (toCylinder->queryRemove(*toItem, toItem->getItemCount(), flags) == RETURNVALUE_NOERROR) {
- int32_t oldToItemIndex = toCylinder->getThingIndex(toItem);
- toCylinder->removeThing(toItem, toItem->getItemCount());
- fromCylinder->addThing(toItem);
- if (oldToItemIndex != -1) {
- toCylinder->postRemoveNotification(toItem, fromCylinder, oldToItemIndex);
- }
- int32_t newToItemIndex = fromCylinder->getThingIndex(toItem);
- if (newToItemIndex != -1) {
- fromCylinder->postAddNotification(toItem, toCylinder, newToItemIndex);
- }
- ret = toCylinder->queryAdd(index, *item, count, flags);
- toItem = nullptr;
- }
- }
- }
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- //check how much we can move
- uint32_t maxQueryCount = 0;
- ReturnValue retMaxCount = toCylinder->queryMaxCount(index, *item, count, maxQueryCount, flags);
- if (retMaxCount != RETURNVALUE_NOERROR && maxQueryCount == 0) {
- return retMaxCount;
- }
- uint32_t m;
- if (item->isStackable()) {
- if (item->isRune()) {
- m = std::min<uint32_t>(item->getItemCount(), maxQueryCount);
- } else {
- m = std::min<uint32_t>(count, maxQueryCount);
- }
- } else {
- m = maxQueryCount;
- }
- Item* moveItem = item;
- //check if we can remove this item
- ret = fromCylinder->queryRemove(*item, m, flags);
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- if (tradeItem) {
- if (toCylinder->getItem() == tradeItem) {
- return RETURNVALUE_NOTENOUGHROOM;
- }
- Cylinder* tmpCylinder = toCylinder->getParent();
- while (tmpCylinder) {
- if (tmpCylinder->getItem() == tradeItem) {
- return RETURNVALUE_NOTENOUGHROOM;
- }
- tmpCylinder = tmpCylinder->getParent();
- }
- }
- //remove the item
- int32_t itemIndex = fromCylinder->getThingIndex(item);
- Item* updateItem = nullptr;
- fromCylinder->removeThing(item, m);
- //update item(s)
- if (item->isStackable()) {
- uint32_t n;
- if (!item->isRune() && item->equals(toItem)) {
- n = std::min<uint32_t>(100 - toItem->getItemCount(), m);
- toCylinder->updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
- updateItem = toItem;
- } else {
- n = 0;
- }
- int32_t newCount = m - n;
- if (newCount > 0) {
- moveItem = item->clone();
- moveItem->setItemCount(newCount);
- } else {
- moveItem = nullptr;
- }
- if (item->isRemoved()) {
- ReleaseItem(item);
- }
- }
- //add item
- if (moveItem /*m - n > 0*/) {
- toCylinder->addThing(index, moveItem);
- }
- if (itemIndex != -1) {
- fromCylinder->postRemoveNotification(item, toCylinder, itemIndex);
- }
- if (moveItem) {
- int32_t moveItemIndex = toCylinder->getThingIndex(moveItem);
- if (moveItemIndex != -1) {
- toCylinder->postAddNotification(moveItem, fromCylinder, moveItemIndex);
- }
- }
- if (updateItem) {
- int32_t updateItemIndex = toCylinder->getThingIndex(updateItem);
- if (updateItemIndex != -1) {
- toCylinder->postAddNotification(updateItem, fromCylinder, updateItemIndex);
- }
- }
- if (_moveItem) {
- if (moveItem) {
- *_moveItem = moveItem;
- } else {
- *_moveItem = item;
- }
- }
- //we could not move all, inform the player
- if (item->isStackable() && maxQueryCount < count) {
- return retMaxCount;
- }
- return ret;
- }
- ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index /*= INDEX_WHEREEVER*/, uint32_t flags/* = 0*/, bool test/* = false*/)
- {
- uint32_t remainderCount = 0;
- return internalAddItem(toCylinder, item, index, flags, test, remainderCount);
- }
- ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t index, uint32_t flags, bool test, uint32_t& remainderCount)
- {
- if (toCylinder == nullptr || item == nullptr) {
- return RETURNVALUE_NOTPOSSIBLE;
- }
- Cylinder* destCylinder = toCylinder;
- Item* toItem = nullptr;
- toCylinder = toCylinder->queryDestination(index, *item, &toItem, flags);
- //check if we can add this item
- ReturnValue ret = toCylinder->queryAdd(index, *item, item->getItemCount(), flags);
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- /*
- Check if we can move add the whole amount, we do this by checking against the original cylinder,
- since the queryDestination can return a cylinder that might only hold a part of the full amount.
- */
- uint32_t maxQueryCount = 0;
- ret = destCylinder->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), maxQueryCount, flags);
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- if (test) {
- return RETURNVALUE_NOERROR;
- }
- if (item->isStackable() && !item->isRune() && item->equals(toItem)) {
- uint32_t m = std::min<uint32_t>(item->getItemCount(), maxQueryCount);
- uint32_t n = std::min<uint32_t>(100 - toItem->getItemCount(), m);
- toCylinder->updateThing(toItem, toItem->getID(), toItem->getItemCount() + n);
- int32_t count = m - n;
- if (count > 0) {
- if (item->getItemCount() != count) {
- Item* remainderItem = item->clone();
- remainderItem->setItemCount(count);
- if (internalAddItem(destCylinder, remainderItem, INDEX_WHEREEVER, flags, false) != RETURNVALUE_NOERROR) {
- ReleaseItem(remainderItem);
- remainderCount = count;
- }
- } else {
- toCylinder->addThing(index, item);
- int32_t itemIndex = toCylinder->getThingIndex(item);
- if (itemIndex != -1) {
- toCylinder->postAddNotification(item, nullptr, itemIndex);
- }
- }
- } else {
- //fully merged with toItem, item will be destroyed
- item->onRemoved();
- ReleaseItem(item);
- int32_t itemIndex = toCylinder->getThingIndex(toItem);
- if (itemIndex != -1) {
- toCylinder->postAddNotification(toItem, nullptr, itemIndex);
- }
- }
- } else {
- toCylinder->addThing(index, item);
- int32_t itemIndex = toCylinder->getThingIndex(item);
- if (itemIndex != -1) {
- toCylinder->postAddNotification(item, nullptr, itemIndex);
- }
- }
- return RETURNVALUE_NOERROR;
- }
- ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool test /*= false*/, uint32_t flags /*= 0*/)
- {
- Cylinder* cylinder = item->getParent();
- if (cylinder == nullptr) {
- return RETURNVALUE_NOTPOSSIBLE;
- }
- if (count == -1) {
- count = item->getItemCount();
- }
- //check if we can remove this item
- ReturnValue ret = cylinder->queryRemove(*item, count, flags | FLAG_IGNORENOTMOVEABLE);
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- if (!item->canRemove()) {
- return RETURNVALUE_NOTPOSSIBLE;
- }
- if (!test) {
- int32_t index = cylinder->getThingIndex(item);
- //remove the item
- cylinder->removeThing(item, count);
- if (item->isRemoved()) {
- ReleaseItem(item);
- }
- cylinder->postRemoveNotification(item, nullptr, index);
- }
- item->onRemoved();
- return RETURNVALUE_NOERROR;
- }
- ReturnValue Game::internalPlayerAddItem(Player* player, Item* item, bool dropOnMap /*= true*/, slots_t slot /*= CONST_SLOT_WHEREEVER*/)
- {
- uint32_t remainderCount = 0;
- ReturnValue ret = internalAddItem(player, item, static_cast<int32_t>(slot), 0, false, remainderCount);
- if (remainderCount != 0) {
- Item* remainderItem = Item::CreateItem(item->getID(), remainderCount);
- ReturnValue remaindRet = internalAddItem(player->getTile(), remainderItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
- if (remaindRet != RETURNVALUE_NOERROR) {
- ReleaseItem(remainderItem);
- }
- }
- if (ret != RETURNVALUE_NOERROR && dropOnMap) {
- ret = internalAddItem(player->getTile(), item, INDEX_WHEREEVER, FLAG_NOLIMIT);
- }
- return ret;
- }
- Item* Game::findItemOfType(Cylinder* cylinder, uint16_t itemId,
- bool depthSearch /*= true*/, int32_t subType /*= -1*/) const
- {
- if (cylinder == nullptr) {
- return nullptr;
- }
- std::vector<Container*> containers;
- for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
- Thing* thing = cylinder->getThing(i);
- if (!thing) {
- continue;
- }
- Item* item = thing->getItem();
- if (!item) {
- continue;
- }
- if (item->getID() == itemId && (subType == -1 || subType == item->getSubType())) {
- return item;
- }
- if (depthSearch) {
- Container* container = item->getContainer();
- if (container) {
- containers.push_back(container);
- }
- }
- }
- size_t i = 0;
- while (i < containers.size()) {
- Container* container = containers[i++];
- for (Item* item : container->getItemList()) {
- if (item->getID() == itemId && (subType == -1 || subType == item->getSubType())) {
- return item;
- }
- Container* subContainer = item->getContainer();
- if (subContainer) {
- containers.push_back(subContainer);
- }
- }
- }
- return nullptr;
- }
- bool Game::removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
- {
- if (cylinder == nullptr) {
- return false;
- }
- if (money == 0) {
- return true;
- }
- std::vector<Container*> containers;
- std::multimap<uint64_t, Item*> moneyMap;
- uint64_t moneyCount = 0;
- for (size_t i = cylinder->getFirstIndex(), j = cylinder->getLastIndex(); i < j; ++i) {
- Thing* thing = cylinder->getThing(i);
- if (!thing) {
- continue;
- }
- Item* item = thing->getItem();
- if (!item) {
- continue;
- }
- Container* container = item->getContainer();
- if (container) {
- containers.push_back(container);
- } else {
- const uint32_t worth = item->getWorth();
- if (worth != 0) {
- moneyCount += worth;
- moneyMap.emplace(worth, item);
- }
- }
- }
- size_t i = 0;
- while (i < containers.size()) {
- Container* container = containers[i++];
- for (Item* item : container->getItemList()) {
- Container* tmpContainer = item->getContainer();
- if (tmpContainer) {
- containers.push_back(tmpContainer);
- } else {
- const uint32_t worth = item->getWorth();
- if (worth != 0) {
- moneyCount += worth;
- moneyMap.emplace(worth, item);
- }
- }
- }
- }
- if (moneyCount < money) {
- return false;
- }
- for (const auto& moneyEntry : moneyMap) {
- Item* item = moneyEntry.second;
- if (moneyEntry.first < money) {
- internalRemoveItem(item);
- money -= moneyEntry.first;
- } else if (moneyEntry.first > money) {
- const uint32_t worth = moneyEntry.first / item->getItemCount();
- const uint32_t removeCount = (money / worth) + 1;
- addMoney(cylinder, (worth * removeCount) - money, flags);
- internalRemoveItem(item, removeCount);
- break;
- } else {
- internalRemoveItem(item);
- break;
- }
- }
- return true;
- }
- void Game::addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags /*= 0*/)
- {
- if (money == 0) {
- return;
- }
- uint32_t crystalCoins = money / 10000;
- money -= crystalCoins * 10000;
- while (crystalCoins > 0) {
- const uint16_t count = std::min<uint32_t>(100, crystalCoins);
- Item* remaindItem = Item::CreateItem(ITEM_CRYSTAL_COIN, count);
- ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
- if (ret != RETURNVALUE_NOERROR) {
- internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
- }
- crystalCoins -= count;
- }
- uint16_t platinumCoins = money / 100;
- if (platinumCoins != 0) {
- Item* remaindItem = Item::CreateItem(ITEM_PLATINUM_COIN, platinumCoins);
- ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
- if (ret != RETURNVALUE_NOERROR) {
- internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
- }
- money -= platinumCoins * 100;
- }
- if (money != 0) {
- Item* remaindItem = Item::CreateItem(ITEM_GOLD_COIN, money);
- ReturnValue ret = internalAddItem(cylinder, remaindItem, INDEX_WHEREEVER, flags);
- if (ret != RETURNVALUE_NOERROR) {
- internalAddItem(cylinder->getTile(), remaindItem, INDEX_WHEREEVER, FLAG_NOLIMIT);
- }
- }
- }
- Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/)
- {
- if (item->getID() == newId && (newCount == -1 || (newCount == item->getSubType() && newCount != 0))) { //chargeless item placed on map = infinite
- return item;
- }
- Cylinder* cylinder = item->getParent();
- if (cylinder == nullptr) {
- return nullptr;
- }
- int32_t itemIndex = cylinder->getThingIndex(item);
- if (itemIndex == -1) {
- return item;
- }
- if (!item->canTransform()) {
- return item;
- }
- const ItemType& newType = Item::items[newId];
- if (newType.id == 0) {
- return item;
- }
- const ItemType& curType = Item::items[item->getID()];
- if (curType.alwaysOnTop != newType.alwaysOnTop) {
- //This only occurs when you transform items on tiles from a downItem to a topItem (or vice versa)
- //Remove the old, and add the new
- cylinder->removeThing(item, item->getItemCount());
- cylinder->postRemoveNotification(item, cylinder, itemIndex);
- item->setID(newId);
- if (newCount != -1) {
- item->setSubType(newCount);
- }
- cylinder->addThing(item);
- Cylinder* newParent = item->getParent();
- if (newParent == nullptr) {
- ReleaseItem(item);
- return nullptr;
- }
- newParent->postAddNotification(item, cylinder, newParent->getThingIndex(item));
- return item;
- }
- if (curType.type == newType.type) {
- //Both items has the same type so we can safely change id/subtype
- if (newCount == 0 && (item->isStackable() || item->hasAttribute(ITEM_ATTRIBUTE_CHARGES))) {
- if (item->isStackable()) {
- internalRemoveItem(item);
- return nullptr;
- } else {
- int32_t newItemId = newId;
- if (curType.id == newType.id) {
- newItemId = curType.decayTo;
- }
- if (newItemId < 0) {
- internalRemoveItem(item);
- return nullptr;
- } else if (newItemId != newId) {
- //Replacing the the old item with the new while maintaining the old position
- Item* newItem = Item::CreateItem(newItemId, 1);
- if (newItem == nullptr) {
- return nullptr;
- }
- cylinder->replaceThing(itemIndex, newItem);
- cylinder->postAddNotification(newItem, cylinder, itemIndex);
- item->setParent(nullptr);
- cylinder->postRemoveNotification(item, cylinder, itemIndex);
- ReleaseItem(item);
- return newItem;
- } else {
- return transformItem(item, newItemId);
- }
- }
- } else {
- cylinder->postRemoveNotification(item, cylinder, itemIndex);
- uint16_t itemId = item->getID();
- int32_t count = item->getSubType();
- if (curType.id != newType.id) {
- if (newType.group != curType.group) {
- item->setDefaultSubtype();
- }
- itemId = newId;
- }
- if (newCount != -1 && newType.hasSubType()) {
- count = newCount;
- }
- cylinder->updateThing(item, itemId, count);
- cylinder->postAddNotification(item, cylinder, itemIndex);
- return item;
- }
- }
- //Replacing the the old item with the new while maintaining the old position
- Item* newItem;
- if (newCount == -1) {
- newItem = Item::CreateItem(newId);
- } else {
- newItem = Item::CreateItem(newId, newCount);
- }
- if (newItem == nullptr) {
- return nullptr;
- }
- cylinder->replaceThing(itemIndex, newItem);
- cylinder->postAddNotification(newItem, cylinder, itemIndex);
- item->setParent(nullptr);
- cylinder->postRemoveNotification(item, cylinder, itemIndex);
- ReleaseItem(item);
- return newItem;
- }
- ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pushMove/* = true*/, uint32_t flags /*= 0*/)
- {
- if (newPos == thing->getPosition()) {
- return RETURNVALUE_NOERROR;
- } else if (thing->isRemoved()) {
- return RETURNVALUE_NOTPOSSIBLE;
- }
- Tile* toTile = map.getTile(newPos);
- if (!toTile) {
- return RETURNVALUE_NOTPOSSIBLE;
- }
- if (Creature* creature = thing->getCreature()) {
- ReturnValue ret = toTile->queryAdd(0, *creature, 1, FLAG_NOLIMIT);
- if (ret != RETURNVALUE_NOERROR) {
- return ret;
- }
- map.moveCreature(*creature, *toTile, !pushMove);
- return RETURNVALUE_NOERROR;
- } else if (Item* item = thing->getItem()) {
- return internalMoveItem(item->getParent(), toTile, INDEX_WHEREEVER, item, item->getItemCount(), nullptr, flags);
- }
- return RETURNVALUE_NOTPOSSIBLE;
- }
- //Implementation of player invoked events
- void Game::playerMove(uint32_t playerId, Direction direction)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->resetIdleTime();
- player->setNextWalkActionTask(nullptr);
- player->startAutoWalk(std::forward_list<Direction> { direction });
- }
- bool Game::playerBroadcastMessage(Player* player, const std::string& text) const
- {
- if (!player->hasFlag(PlayerFlag_CanBroadcast)) {
- return false;
- }
- std::cout << "> " << player->getName() << " broadcasted: \"" << text << "\"." << std::endl;
- for (const auto& it : players) {
- it.second->sendPrivateMessage(player, TALKTYPE_BROADCAST, text);
- }
- return true;
- }
- void Game::playerCreatePrivateChannel(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player || !player->isPremium()) {
- return;
- }
- ChatChannel* channel = g_chat->createChannel(*player, CHANNEL_PRIVATE);
- if (!channel || !channel->addUser(*player)) {
- return;
- }
- player->sendCreatePrivateChannel(channel->getId(), channel->getName());
- }
- void Game::playerChannelInvite(uint32_t playerId, const std::string& name)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- PrivateChatChannel* channel = g_chat->getPrivateChannel(*player);
- if (!channel) {
- return;
- }
- Player* invitePlayer = getPlayerByName(name);
- if (!invitePlayer) {
- return;
- }
- if (player == invitePlayer) {
- return;
- }
- channel->invitePlayer(*player, *invitePlayer);
- }
- void Game::playerChannelExclude(uint32_t playerId, const std::string& name)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- PrivateChatChannel* channel = g_chat->getPrivateChannel(*player);
- if (!channel) {
- return;
- }
- Player* excludePlayer = getPlayerByName(name);
- if (!excludePlayer) {
- return;
- }
- if (player == excludePlayer) {
- return;
- }
- channel->excludePlayer(*player, *excludePlayer);
- }
- void Game::playerRequestChannels(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->sendChannelsDialog();
- }
- void Game::playerOpenChannel(uint32_t playerId, uint16_t channelId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- ChatChannel* channel = g_chat->addUserToChannel(*player, channelId);
- if (!channel) {
- return;
- }
- const InvitedMap* invitedUsers = channel->getInvitedUsers();
- const UsersMap* users;
- if (!channel->isPublicChannel()) {
- users = &channel->getUsers();
- } else {
- users = nullptr;
- }
- player->sendChannel(channel->getId(), channel->getName(), users, invitedUsers);
- }
- void Game::playerCloseChannel(uint32_t playerId, uint16_t channelId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- g_chat->removeUserFromChannel(*player, channelId);
- }
- void Game::playerOpenPrivateChannel(uint32_t playerId, std::string& receiver)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- if (!IOLoginData::formatPlayerName(receiver)) {
- player->sendCancelMessage("A player with this name does not exist.");
- return;
- }
- player->sendOpenPrivateChannel(receiver);
- }
- void Game::playerReceivePing(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->receivePing();
- }
- void Game::playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->resetIdleTime();
- player->setNextWalkTask(nullptr);
- player->startAutoWalk(listDir);
- }
- void Game::playerStopAutoWalk(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->stopWalk();
- }
- void Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint16_t fromSpriteId,
- const Position& toPos, uint8_t toStackPos, uint16_t toSpriteId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0);
- if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
- return;
- }
- Thing* thing = internalGetThing(player, fromPos, fromStackPos, fromSpriteId, STACKPOS_USEITEM);
- if (!thing) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Item* item = thing->getItem();
- if (!item || !item->isUseable() || item->getClientID() != fromSpriteId) {
- player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
- return;
- }
- Position walkToPos = fromPos;
- ReturnValue ret = g_actions->canUse(player, fromPos);
- if (ret == RETURNVALUE_NOERROR) {
- ret = g_actions->canUse(player, toPos, item);
- if (ret == RETURNVALUE_TOOFARAWAY) {
- walkToPos = toPos;
- }
- }
- if (ret != RETURNVALUE_NOERROR) {
- if (ret == RETURNVALUE_TOOFARAWAY) {
- Position itemPos = fromPos;
- uint8_t itemStackPos = fromStackPos;
- if (fromPos.x != 0xFFFF && toPos.x != 0xFFFF && Position::areInRange<1, 1, 0>(fromPos, player->getPosition()) &&
- !Position::areInRange<1, 1, 0>(fromPos, toPos)) {
- Item* moveItem = nullptr;
- ret = internalMoveItem(item->getParent(), player, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
- if (ret != RETURNVALUE_NOERROR) {
- player->sendCancelMessage(ret);
- return;
- }
- //changing the position since its now in the inventory of the player
- internalGetPosition(moveItem, itemPos, itemStackPos);
- }
- std::forward_list<Direction> listDir;
- if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItemEx, this,
- playerId, itemPos, itemStackPos, fromSpriteId, toPos, toStackPos, toSpriteId));
- player->setNextWalkActionTask(task);
- } else {
- player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
- }
- return;
- }
- player->sendCancelMessage(ret);
- return;
- }
- if (!player->canDoAction()) {
- uint32_t delay = player->getNextActionTime();
- SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItemEx, this,
- playerId, fromPos, fromStackPos, fromSpriteId, toPos, toStackPos, toSpriteId));
- player->setNextActionTask(task);
- return;
- }
- player->resetIdleTime();
- player->setNextActionTask(nullptr);
- g_actions->useItemEx(player, fromPos, toPos, toStackPos, item, isHotkey);
- }
- void Game::playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPos,
- uint8_t index, uint16_t spriteId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- bool isHotkey = (pos.x == 0xFFFF && pos.y == 0 && pos.z == 0);
- if (isHotkey && !g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
- return;
- }
- Thing* thing = internalGetThing(player, pos, stackPos, spriteId, STACKPOS_USEITEM);
- if (!thing) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Item* item = thing->getItem();
- if (!item || item->isUseable() || item->getClientID() != spriteId) {
- player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
- return;
- }
- ReturnValue ret = g_actions->canUse(player, pos);
- if (ret != RETURNVALUE_NOERROR) {
- if (ret == RETURNVALUE_TOOFARAWAY) {
- std::forward_list<Direction> listDir;
- if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, spriteId));
- player->setNextWalkActionTask(task);
- return;
- }
- ret = RETURNVALUE_THEREISNOWAY;
- }
- player->sendCancelMessage(ret);
- return;
- }
- if (!player->canDoAction()) {
- uint32_t delay = player->getNextActionTime();
- SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseItem, this, playerId, pos, stackPos, index, spriteId));
- player->setNextActionTask(task);
- return;
- }
- player->resetIdleTime();
- player->setNextActionTask(nullptr);
- g_actions->useItem(player, pos, index, item, isHotkey);
- }
- void Game::playerUseWithCreature(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint32_t creatureId, uint16_t spriteId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Creature* creature = getCreatureByID(creatureId);
- if (!creature) {
- return;
- }
- if (!Position::areInRange<7, 5, 0>(creature->getPosition(), player->getPosition())) {
- return;
- }
- bool isHotkey = (fromPos.x == 0xFFFF && fromPos.y == 0 && fromPos.z == 0);
- if (!g_config.getBoolean(ConfigManager::AIMBOT_HOTKEY_ENABLED)) {
- if (creature->getPlayer() || isHotkey) {
- player->sendCancelMessage(RETURNVALUE_DIRECTPLAYERSHOOT);
- return;
- }
- }
- Thing* thing = internalGetThing(player, fromPos, fromStackPos, spriteId, STACKPOS_USEITEM);
- if (!thing) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Item* item = thing->getItem();
- if (!item || !item->isUseable() || item->getClientID() != spriteId) {
- player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
- return;
- }
- Position toPos = creature->getPosition();
- Position walkToPos = fromPos;
- ReturnValue ret = g_actions->canUse(player, fromPos);
- if (ret == RETURNVALUE_NOERROR) {
- ret = g_actions->canUse(player, toPos, item);
- if (ret == RETURNVALUE_TOOFARAWAY) {
- walkToPos = toPos;
- }
- }
- if (ret != RETURNVALUE_NOERROR) {
- if (ret == RETURNVALUE_TOOFARAWAY) {
- Position itemPos = fromPos;
- uint8_t itemStackPos = fromStackPos;
- if (fromPos.x != 0xFFFF && Position::areInRange<1, 1, 0>(fromPos, player->getPosition()) && !Position::areInRange<1, 1, 0>(fromPos, toPos)) {
- Item* moveItem = nullptr;
- ret = internalMoveItem(item->getParent(), player, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem);
- if (ret != RETURNVALUE_NOERROR) {
- player->sendCancelMessage(ret);
- return;
- }
- //changing the position since its now in the inventory of the player
- internalGetPosition(moveItem, itemPos, itemStackPos);
- }
- std::forward_list<Direction> listDir;
- if (player->getPathTo(walkToPos, listDir, 0, 1, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
- this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerUseWithCreature, this,
- playerId, itemPos, itemStackPos, creatureId, spriteId));
- player->setNextWalkActionTask(task);
- } else {
- player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
- }
- return;
- }
- player->sendCancelMessage(ret);
- return;
- }
- if (!player->canDoAction()) {
- uint32_t delay = player->getNextActionTime();
- SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerUseWithCreature, this,
- playerId, fromPos, fromStackPos, creatureId, spriteId));
- player->setNextActionTask(task);
- return;
- }
- player->resetIdleTime();
- player->setNextActionTask(nullptr);
- g_actions->useItemEx(player, fromPos, creature->getPosition(), creature->getParent()->getThingIndex(creature), item, isHotkey, creature);
- }
- void Game::playerCloseContainer(uint32_t playerId, uint8_t cid)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->closeContainer(cid);
- player->sendCloseContainer(cid);
- }
- void Game::playerMoveUpContainer(uint32_t playerId, uint8_t cid)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Container* container = player->getContainerByID(cid);
- if (!container) {
- return;
- }
- Container* parentContainer = dynamic_cast<Container*>(container->getRealParent());
- if (!parentContainer) {
- return;
- }
- player->addContainer(cid, parentContainer);
- player->sendContainer(cid, parentContainer, parentContainer->hasParent(), player->getContainerIndex(cid));
- }
- void Game::playerUpdateContainer(uint32_t playerId, uint8_t cid)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Container* container = player->getContainerByID(cid);
- if (!container) {
- return;
- }
- player->sendContainer(cid, container, container->hasParent(), player->getContainerIndex(cid));
- }
- void Game::playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stackPos, const uint16_t spriteId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Thing* thing = internalGetThing(player, pos, stackPos, 0, STACKPOS_TOPDOWN_ITEM);
- if (!thing) {
- return;
- }
- Item* item = thing->getItem();
- if (!item || item->getClientID() != spriteId || !item->isRotatable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- if (pos.x != 0xFFFF && !Position::areInRange<1, 1, 0>(pos, player->getPosition())) {
- std::forward_list<Direction> listDir;
- if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
- this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRotateItem, this,
- playerId, pos, stackPos, spriteId));
- player->setNextWalkActionTask(task);
- } else {
- player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
- }
- return;
- }
- uint16_t newId = Item::items[item->getID()].rotateTo;
- if (newId != 0) {
- transformItem(item, newId);
- }
- }
- void Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- uint16_t maxTextLength = 0;
- uint32_t internalWindowTextId = 0;
- Item* writeItem = player->getWriteItem(internalWindowTextId, maxTextLength);
- if (text.length() > maxTextLength || windowTextId != internalWindowTextId) {
- return;
- }
- if (!writeItem || writeItem->isRemoved()) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Cylinder* topParent = writeItem->getTopParent();
- Player* owner = dynamic_cast<Player*>(topParent);
- if (owner && owner != player) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- if (!Position::areInRange<1, 1, 0>(writeItem->getPosition(), player->getPosition())) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- for (auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT)) {
- if (!creatureEvent->executeTextEdit(player, writeItem, text)) {
- player->setWriteItem(nullptr);
- return;
- }
- }
- if (!text.empty()) {
- if (writeItem->getText() != text) {
- writeItem->setText(text);
- writeItem->setWriter(player->getName());
- writeItem->setDate(time(nullptr));
- }
- } else {
- writeItem->resetText();
- writeItem->resetWriter();
- writeItem->resetDate();
- }
- uint16_t newId = Item::items[writeItem->getID()].writeOnceItemId;
- if (newId != 0) {
- transformItem(writeItem, newId);
- }
- player->setWriteItem(nullptr);
- }
- void Game::playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- uint32_t internalWindowTextId;
- uint32_t internalListId;
- House* house = player->getEditHouse(internalWindowTextId, internalListId);
- if (house && internalWindowTextId == windowTextId && listId == 0) {
- house->setAccessList(internalListId, text);
- player->setEditHouse(nullptr);
- }
- }
- void Game::playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t stackPos,
- uint32_t tradePlayerId, uint16_t spriteId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Player* tradePartner = getPlayerByID(tradePlayerId);
- if (!tradePartner || tradePartner == player) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "Sorry, not possible.");
- return;
- }
- if (!Position::areInRange<2, 2, 0>(tradePartner->getPosition(), player->getPosition())) {
- std::ostringstream ss;
- ss << tradePartner->getName() << " tells you to move closer.";
- player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
- return;
- }
- if (!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())) {
- player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
- return;
- }
- Thing* tradeThing = internalGetThing(player, pos, stackPos, 0, STACKPOS_TOPDOWN_ITEM);
- if (!tradeThing) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Item* tradeItem = tradeThing->getItem();
- if (tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || tradeItem->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- const Position& playerPosition = player->getPosition();
- const Position& tradeItemPosition = tradeItem->getPosition();
- if (playerPosition.z != tradeItemPosition.z) {
- player->sendCancelMessage(playerPosition.z > tradeItemPosition.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS);
- return;
- }
- if (!Position::areInRange<1, 1>(tradeItemPosition, playerPosition)) {
- std::forward_list<Direction> listDir;
- if (player->getPathTo(pos, listDir, 0, 1, true, true)) {
- g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
- this, player->getID(), listDir)));
- SchedulerTask* task = createSchedulerTask(400, std::bind(&Game::playerRequestTrade, this,
- playerId, pos, stackPos, tradePlayerId, spriteId));
- player->setNextWalkActionTask(task);
- } else {
- player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
- }
- return;
- }
- Container* tradeItemContainer = tradeItem->getContainer();
- if (tradeItemContainer) {
- for (const auto& it : tradeItems) {
- Item* item = it.first;
- if (tradeItem == item) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
- return;
- }
- if (tradeItemContainer->isHoldingItem(item)) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
- return;
- }
- Container* container = item->getContainer();
- if (container && container->isHoldingItem(tradeItem)) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
- return;
- }
- }
- } else {
- for (const auto& it : tradeItems) {
- Item* item = it.first;
- if (tradeItem == item) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
- return;
- }
- Container* container = item->getContainer();
- if (container && container->isHoldingItem(tradeItem)) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "This item is already being traded.");
- return;
- }
- }
- }
- Container* tradeContainer = tradeItem->getContainer();
- if (tradeContainer && tradeContainer->getItemHoldingCount() + 1 > 100) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "You can not trade more than 100 items.");
- return;
- }
- if (!g_events->eventPlayerOnTradeRequest(player, tradePartner, tradeItem)) {
- return;
- }
- internalStartTrade(player, tradePartner, tradeItem);
- }
- bool Game::internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem)
- {
- if (player->tradeState != TRADE_NONE && !(player->tradeState == TRADE_ACKNOWLEDGE && player->tradePartner == tradePartner)) {
- player->sendCancelMessage(RETURNVALUE_YOUAREALREADYTRADING);
- return false;
- } else if (tradePartner->tradeState != TRADE_NONE && tradePartner->tradePartner != player) {
- player->sendCancelMessage(RETURNVALUE_THISPLAYERISALREADYTRADING);
- return false;
- }
- player->tradePartner = tradePartner;
- player->tradeItem = tradeItem;
- player->tradeState = TRADE_INITIATED;
- tradeItem->incrementReferenceCounter();
- tradeItems[tradeItem] = player->getID();
- player->sendTradeItemRequest(player->getName(), tradeItem, true);
- if (tradePartner->tradeState == TRADE_NONE) {
- std::ostringstream ss;
- ss << player->getName() << " wants to trade with you.";
- tradePartner->sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
- tradePartner->tradeState = TRADE_ACKNOWLEDGE;
- tradePartner->tradePartner = player;
- } else {
- Item* counterOfferItem = tradePartner->tradeItem;
- player->sendTradeItemRequest(tradePartner->getName(), counterOfferItem, false);
- tradePartner->sendTradeItemRequest(player->getName(), tradeItem, false);
- }
- return true;
- }
- void Game::playerAcceptTrade(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- if (!(player->getTradeState() == TRADE_ACKNOWLEDGE || player->getTradeState() == TRADE_INITIATED)) {
- return;
- }
- Player* tradePartner = player->tradePartner;
- if (!tradePartner) {
- return;
- }
- if (!canThrowObjectTo(tradePartner->getPosition(), player->getPosition())) {
- player->sendCancelMessage(RETURNVALUE_CREATUREISNOTREACHABLE);
- return;
- }
- player->setTradeState(TRADE_ACCEPT);
- if (tradePartner->getTradeState() == TRADE_ACCEPT) {
- Item* tradeItem1 = player->tradeItem;
- Item* tradeItem2 = tradePartner->tradeItem;
- if (!g_events->eventPlayerOnTradeAccept(player, tradePartner, tradeItem1, tradeItem2)) {
- internalCloseTrade(player);
- return;
- }
- player->setTradeState(TRADE_TRANSFER);
- tradePartner->setTradeState(TRADE_TRANSFER);
- std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradeItem1);
- if (it != tradeItems.end()) {
- ReleaseItem(it->first);
- tradeItems.erase(it);
- }
- it = tradeItems.find(tradeItem2);
- if (it != tradeItems.end()) {
- ReleaseItem(it->first);
- tradeItems.erase(it);
- }
- bool isSuccess = false;
- ReturnValue ret1 = internalAddItem(tradePartner, tradeItem1, INDEX_WHEREEVER, 0, true);
- ReturnValue ret2 = internalAddItem(player, tradeItem2, INDEX_WHEREEVER, 0, true);
- if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) {
- ret1 = internalRemoveItem(tradeItem1, tradeItem1->getItemCount(), true);
- ret2 = internalRemoveItem(tradeItem2, tradeItem2->getItemCount(), true);
- if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) {
- Cylinder* cylinder1 = tradeItem1->getParent();
- Cylinder* cylinder2 = tradeItem2->getParent();
- uint32_t count1 = tradeItem1->getItemCount();
- uint32_t count2 = tradeItem2->getItemCount();
- ret1 = internalMoveItem(cylinder1, tradePartner, INDEX_WHEREEVER, tradeItem1, count1, nullptr, FLAG_IGNOREAUTOSTACK, nullptr, tradeItem2);
- if (ret1 == RETURNVALUE_NOERROR) {
- internalMoveItem(cylinder2, player, INDEX_WHEREEVER, tradeItem2, count2, nullptr, FLAG_IGNOREAUTOSTACK);
- tradeItem1->onTradeEvent(ON_TRADE_TRANSFER, tradePartner);
- tradeItem2->onTradeEvent(ON_TRADE_TRANSFER, player);
- isSuccess = true;
- }
- }
- }
- if (!isSuccess) {
- std::string errorDescription;
- if (tradePartner->tradeItem) {
- errorDescription = getTradeErrorDescription(ret1, tradeItem1);
- tradePartner->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription);
- tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner);
- }
- if (player->tradeItem) {
- errorDescription = getTradeErrorDescription(ret2, tradeItem2);
- player->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription);
- player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player);
- }
- }
- player->setTradeState(TRADE_NONE);
- player->tradeItem = nullptr;
- player->tradePartner = nullptr;
- player->sendTradeClose();
- tradePartner->setTradeState(TRADE_NONE);
- tradePartner->tradeItem = nullptr;
- tradePartner->tradePartner = nullptr;
- tradePartner->sendTradeClose();
- }
- }
- std::string Game::getTradeErrorDescription(ReturnValue ret, Item* item)
- {
- if (item) {
- if (ret == RETURNVALUE_NOTENOUGHCAPACITY) {
- std::ostringstream ss;
- ss << "You do not have enough capacity to carry";
- if (item->isStackable() && item->getItemCount() > 1) {
- ss << " these objects.";
- } else {
- ss << " this object.";
- }
- ss << std::endl << ' ' << item->getWeightDescription();
- return ss.str();
- } else if (ret == RETURNVALUE_NOTENOUGHROOM || ret == RETURNVALUE_CONTAINERNOTENOUGHROOM) {
- std::ostringstream ss;
- ss << "You do not have enough room to carry";
- if (item->isStackable() && item->getItemCount() > 1) {
- ss << " these objects.";
- } else {
- ss << " this object.";
- }
- return ss.str();
- }
- }
- return "Trade could not be completed.";
- }
- void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Player* tradePartner = player->tradePartner;
- if (!tradePartner) {
- return;
- }
- Item* tradeItem;
- if (lookAtCounterOffer) {
- tradeItem = tradePartner->getTradeItem();
- } else {
- tradeItem = player->getTradeItem();
- }
- if (!tradeItem) {
- return;
- }
- const Position& playerPosition = player->getPosition();
- const Position& tradeItemPosition = tradeItem->getPosition();
- int32_t lookDistance = std::max<int32_t>(Position::getDistanceX(playerPosition, tradeItemPosition),
- Position::getDistanceY(playerPosition, tradeItemPosition));
- if (index == 0) {
- g_events->eventPlayerOnLookInTrade(player, tradePartner, tradeItem, lookDistance);
- return;
- }
- Container* tradeContainer = tradeItem->getContainer();
- if (!tradeContainer) {
- return;
- }
- std::vector<const Container*> containers {tradeContainer};
- size_t i = 0;
- while (i < containers.size()) {
- const Container* container = containers[i++];
- for (Item* item : container->getItemList()) {
- Container* tmpContainer = item->getContainer();
- if (tmpContainer) {
- containers.push_back(tmpContainer);
- }
- if (--index == 0) {
- g_events->eventPlayerOnLookInTrade(player, tradePartner, item, lookDistance);
- return;
- }
- }
- }
- }
- void Game::playerCloseTrade(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- internalCloseTrade(player);
- }
- void Game::internalCloseTrade(Player* player)
- {
- Player* tradePartner = player->tradePartner;
- if ((tradePartner && tradePartner->getTradeState() == TRADE_TRANSFER) || player->getTradeState() == TRADE_TRANSFER) {
- return;
- }
- if (player->getTradeItem()) {
- std::map<Item*, uint32_t>::iterator it = tradeItems.find(player->getTradeItem());
- if (it != tradeItems.end()) {
- ReleaseItem(it->first);
- tradeItems.erase(it);
- }
- player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player);
- player->tradeItem = nullptr;
- }
- player->setTradeState(TRADE_NONE);
- player->tradePartner = nullptr;
- player->sendTextMessage(MESSAGE_STATUS_SMALL, "Trade cancelled.");
- player->sendTradeClose();
- if (tradePartner) {
- if (tradePartner->getTradeItem()) {
- std::map<Item*, uint32_t>::iterator it = tradeItems.find(tradePartner->getTradeItem());
- if (it != tradeItems.end()) {
- ReleaseItem(it->first);
- tradeItems.erase(it);
- }
- tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner);
- tradePartner->tradeItem = nullptr;
- }
- tradePartner->setTradeState(TRADE_NONE);
- tradePartner->tradePartner = nullptr;
- tradePartner->sendTextMessage(MESSAGE_STATUS_SMALL, "Trade cancelled.");
- tradePartner->sendTradeClose();
- }
- }
- void Game::playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Thing* thing = internalGetThing(player, pos, stackPos, 0, STACKPOS_LOOK);
- if (!thing) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Position thingPos = thing->getPosition();
- if (!player->canSee(thingPos)) {
- player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
- return;
- }
- Position playerPos = player->getPosition();
- int32_t lookDistance;
- if (thing != player) {
- lookDistance = std::max<int32_t>(Position::getDistanceX(playerPos, thingPos), Position::getDistanceY(playerPos, thingPos));
- if (playerPos.z != thingPos.z) {
- lookDistance += 15;
- }
- } else {
- lookDistance = -1;
- }
- g_events->eventPlayerOnLook(player, pos, thing, stackPos, lookDistance);
- }
- void Game::playerLookInBattleList(uint32_t playerId, uint32_t creatureId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Creature* creature = getCreatureByID(creatureId);
- if (!creature) {
- return;
- }
- if (!player->canSeeCreature(creature)) {
- return;
- }
- const Position& creaturePos = creature->getPosition();
- if (!player->canSee(creaturePos)) {
- return;
- }
- int32_t lookDistance;
- if (creature != player) {
- const Position& playerPos = player->getPosition();
- lookDistance = std::max<int32_t>(Position::getDistanceX(playerPos, creaturePos), Position::getDistanceY(playerPos, creaturePos));
- if (playerPos.z != creaturePos.z) {
- lookDistance += 15;
- }
- } else {
- lookDistance = -1;
- }
- g_events->eventPlayerOnLookInBattleList(player, creature, lookDistance);
- }
- void Game::playerCancelAttackAndFollow(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- playerSetAttackedCreature(playerId, 0);
- playerFollowCreature(playerId, 0);
- player->stopWalk();
- }
- void Game::playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- if (player->getAttackedCreature() && creatureId == 0) {
- player->setAttackedCreature(nullptr);
- player->sendCancelTarget();
- return;
- }
- Creature* attackCreature = getCreatureByID(creatureId);
- if (!attackCreature) {
- player->setAttackedCreature(nullptr);
- player->sendCancelTarget();
- return;
- }
- ReturnValue ret = Combat::canTargetCreature(player, attackCreature);
- if (ret != RETURNVALUE_NOERROR) {
- player->sendCancelMessage(ret);
- player->sendCancelTarget();
- player->setAttackedCreature(nullptr);
- return;
- }
- player->setAttackedCreature(attackCreature);
- g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID())));
- }
- void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->setAttackedCreature(nullptr);
- g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, this, player->getID())));
- player->setFollowCreature(getCreatureByID(creatureId));
- }
- void Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, bool secureMode)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->setFightMode(fightMode);
- player->setChaseMode(chaseMode);
- player->setSecureMode(secureMode);
- }
- void Game::playerRequestAddVip(uint32_t playerId, const std::string& name)
- {
- if (name.length() > 20) {
- return;
- }
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Player* vipPlayer = getPlayerByName(name);
- if (!vipPlayer) {
- uint32_t guid;
- bool specialVip;
- std::string formattedName = name;
- if (!IOLoginData::getGuidByNameEx(guid, specialVip, formattedName)) {
- player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name does not exist.");
- return;
- }
- if (specialVip && !player->hasFlag(PlayerFlag_SpecialVIP)) {
- player->sendTextMessage(MESSAGE_STATUS_SMALL, "You can not add this player.");
- return;
- }
- player->addVIP(guid, formattedName, VIPSTATUS_OFFLINE);
- } else {
- if (vipPlayer->hasFlag(PlayerFlag_SpecialVIP) && !player->hasFlag(PlayerFlag_SpecialVIP)) {
- player->sendTextMessage(MESSAGE_STATUS_SMALL, "You can not add this player.");
- return;
- }
- if (!vipPlayer->isInGhostMode() || player->isAccessPlayer()) {
- player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), VIPSTATUS_ONLINE);
- } else {
- player->addVIP(vipPlayer->getGUID(), vipPlayer->getName(), VIPSTATUS_OFFLINE);
- }
- }
- }
- void Game::playerRequestRemoveVip(uint32_t playerId, uint32_t guid)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->removeVIP(guid);
- }
- void Game::playerTurn(uint32_t playerId, Direction dir)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- if (!g_events->eventPlayerOnTurn(player, dir)) {
- return;
- }
- player->resetIdleTime();
- internalCreatureTurn(player, dir);
- }
- void Game::playerRequestOutfit(uint32_t playerId)
- {
- if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
- return;
- }
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->sendOutfitWindow();
- }
- void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit)
- {
- if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) {
- return;
- }
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- if (player->canWear(outfit.lookType, outfit.lookAddons)) {
- player->defaultOutfit = outfit;
- if (player->hasCondition(CONDITION_OUTFIT)) {
- return;
- }
- internalCreatureChangeOutfit(player, outfit);
- }
- }
- void Game::playerShowQuestLog(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->sendQuestLog();
- }
- void Game::playerShowQuestLine(uint32_t playerId, uint16_t questId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Quest* quest = quests.getQuestByID(questId);
- if (!quest) {
- return;
- }
- player->sendQuestLine(quest);
- }
- void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, const std::string& receiver, const std::string& text)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->resetIdleTime();
- if (playerSaySpell(player, type, text)) {
- return;
- }
- uint32_t muteTime = player->isMuted();
- if (muteTime > 0) {
- std::ostringstream ss;
- ss << "You are still muted for " << muteTime << " seconds.";
- player->sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
- return;
- }
- if (playerSayCommand(player, text)) {
- return;
- }
- if (!text.empty() && text.front() == '/' && player->isAccessPlayer()) {
- return;
- }
- if (type != TALKTYPE_PRIVATE) {
- player->removeMessageBuffer();
- }
- switch (type) {
- case TALKTYPE_SAY:
- internalCreatureSay(player, TALKTYPE_SAY, text, false);
- break;
- case TALKTYPE_WHISPER:
- playerWhisper(player, text);
- break;
- case TALKTYPE_YELL:
- playerYell(player, text);
- break;
- case TALKTYPE_PRIVATE:
- case TALKTYPE_PRIVATE_RED:
- playerSpeakTo(player, type, receiver, text);
- break;
- case TALKTYPE_CHANNEL_O:
- case TALKTYPE_CHANNEL_Y:
- case TALKTYPE_CHANNEL_R1:
- case TALKTYPE_CHANNEL_R2:
- g_chat->talkToChannel(*player, type, text, channelId);
- break;
- case TALKTYPE_BROADCAST:
- playerBroadcastMessage(player, text);
- break;
- default:
- break;
- }
- }
- bool Game::playerSayCommand(Player* player, const std::string& text)
- {
- if (text.empty()) {
- return false;
- }
- char firstCharacter = text.front();
- for (char commandTag : commandTags) {
- if (commandTag == firstCharacter) {
- if (commands.exeCommand(*player, text)) {
- return true;
- }
- }
- }
- return false;
- }
- bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& text)
- {
- std::string words = text;
- TalkActionResult_t result = g_talkActions->playerSaySpell(player, type, words);
- if (result == TALKACTION_BREAK) {
- return true;
- }
- result = g_spells->playerSaySpell(player, words);
- if (result == TALKACTION_BREAK) {
- if (!g_config.getBoolean(ConfigManager::EMOTE_SPELLS)) {
- return internalCreatureSay(player, TALKTYPE_SAY, words, false);
- } else {
- return internalCreatureSay(player, TALKTYPE_MONSTER_SAY, words, false);
- }
- } else if (result == TALKACTION_FAILED) {
- return true;
- }
- return false;
- }
- void Game::playerWhisper(Player* player, const std::string& text)
- {
- SpectatorVec list;
- map.getSpectators(list, player->getPosition(), false, false,
- Map::maxClientViewportX, Map::maxClientViewportX,
- Map::maxClientViewportY, Map::maxClientViewportY);
- //send to client
- for (Creature* spectator : list) {
- if (Player* spectatorPlayer = spectator->getPlayer()) {
- if (!Position::areInRange<1, 1>(player->getPosition(), spectatorPlayer->getPosition())) {
- spectatorPlayer->sendCreatureSay(player, TALKTYPE_WHISPER, "pspsps");
- } else {
- spectatorPlayer->sendCreatureSay(player, TALKTYPE_WHISPER, text);
- }
- }
- }
- //event method
- for (Creature* spectator : list) {
- spectator->onCreatureSay(player, TALKTYPE_WHISPER, text);
- }
- }
- bool Game::playerYell(Player* player, const std::string& text)
- {
- if (player->getLevel() < 50) {
- player->sendTextMessage(MESSAGE_STATUS_SMALL, "You may not yell under 50 level.");
- return false;
- }
- if (player->hasCondition(CONDITION_YELLTICKS)) {
- player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
- return false;
- }
- if (player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER) {
- Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_YELLTICKS, 30000, 0);
- player->addCondition(condition);
- }
- internalCreatureSay(player, TALKTYPE_YELL, asUpperCaseString(text), false);
- return true;
- }
- bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, const std::string& text)
- {
- Player* toPlayer = getPlayerByName(receiver);
- if (!toPlayer) {
- player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name is not online.");
- return false;
- }
- if (type == TALKTYPE_PRIVATE_RED && (player->hasFlag(PlayerFlag_CanTalkRedPrivate) || player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER)) {
- type = TALKTYPE_PRIVATE_RED;
- } else {
- type = TALKTYPE_PRIVATE;
- }
- toPlayer->sendPrivateMessage(player, type, text);
- toPlayer->onCreatureSay(player, type, text);
- if (toPlayer->isInGhostMode() && !player->isAccessPlayer()) {
- player->sendTextMessage(MESSAGE_STATUS_SMALL, "A player with this name is not online.");
- } else {
- std::ostringstream ss;
- ss << "Message sent to " << toPlayer->getName() << '.';
- player->sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
- }
- return true;
- }
- //--
- bool Game::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= Map::maxClientViewportX*/, int32_t rangey /*= Map::maxClientViewportY*/, MapPathFinding_t path) const
- {
- return map.canThrowObjectTo(fromPos, toPos, checkLineOfSight, rangex, rangey, path);
- }
- bool Game::isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck) const
- {
- return map.isSightClear(fromPos, toPos, floorCheck);
- }
- bool Game::internalCreatureTurn(Creature* creature, Direction dir)
- {
- if (creature->getDirection() == dir) {
- return false;
- }
- creature->setDirection(dir);
- //send to client
- SpectatorVec list;
- map.getSpectators(list, creature->getPosition(), true, true);
- for (Creature* spectator : list) {
- spectator->getPlayer()->sendCreatureTurn(creature);
- }
- return true;
- }
- bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, bool ghostMode, SpectatorVec* listPtr/* = nullptr*/, const Position* pos/* = nullptr*/)
- {
- if (text.empty()) {
- return false;
- }
- if (!pos) {
- pos = &creature->getPosition();
- }
- SpectatorVec list;
- if (!listPtr || listPtr->empty()) {
- // This somewhat complex construct ensures that the cached SpectatorVec
- // is used if available and if it can be used, else a local vector is
- // used (hopefully the compiler will optimize away the construction of
- // the temporary when it's not used).
- if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) {
- map.getSpectators(list, *pos, false, false,
- Map::maxClientViewportX, Map::maxClientViewportX,
- Map::maxClientViewportY, Map::maxClientViewportY);
- } else {
- map.getSpectators(list, *pos, true, false, 18, 18, 14, 14);
- }
- } else {
- list = (*listPtr);
- }
- //send to client
- for (Creature* spectator : list) {
- if (Player* tmpPlayer = spectator->getPlayer()) {
- if (!ghostMode || tmpPlayer->canSeeCreature(creature)) {
- tmpPlayer->sendCreatureSay(creature, type, text, pos);
- }
- }
- }
- //event method
- for (Creature* spectator : list) {
- spectator->onCreatureSay(creature, type, text);
- }
- return true;
- }
- void Game::checkCreatureWalk(uint32_t creatureId)
- {
- Creature* creature = getCreatureByID(creatureId);
- if (creature && creature->getHealth() > 0) {
- creature->onWalk();
- cleanup();
- }
- }
- void Game::updateCreatureWalk(uint32_t creatureId)
- {
- Creature* creature = getCreatureByID(creatureId);
- if (creature && creature->getHealth() > 0) {
- creature->goToFollowCreature();
- }
- }
- void Game::checkCreatureAttack(uint32_t creatureId)
- {
- Creature* creature = getCreatureByID(creatureId);
- if (creature && creature->getHealth() > 0) {
- creature->onAttacking(0);
- }
- }
- void Game::addCreatureCheck(Creature* creature)
- {
- creature->creatureCheck = true;
- if (creature->inCheckCreaturesVector) {
- // already in a vector
- return;
- }
- creature->inCheckCreaturesVector = true;
- checkCreatureLists[uniform_random(0, EVENT_CREATURECOUNT - 1)].push_back(creature);
- creature->incrementReferenceCounter();
- }
- void Game::removeCreatureCheck(Creature* creature)
- {
- if (creature->inCheckCreaturesVector) {
- creature->creatureCheck = false;
- }
- }
- void Game::checkCreatures(size_t index)
- {
- g_scheduler.addEvent(createSchedulerTask(EVENT_CHECK_CREATURE_INTERVAL, std::bind(&Game::checkCreatures, this, (index + 1) % EVENT_CREATURECOUNT)));
- auto& checkCreatureList = checkCreatureLists[index];
- auto it = checkCreatureList.begin(), end = checkCreatureList.end();
- while (it != end) {
- Creature* creature = *it;
- if (creature->creatureCheck) {
- if (creature->getHealth() > 0) {
- creature->onThink(EVENT_CREATURE_THINK_INTERVAL);
- creature->onAttacking(EVENT_CREATURE_THINK_INTERVAL);
- creature->executeConditions(EVENT_CREATURE_THINK_INTERVAL);
- } else {
- creature->onDeath();
- }
- ++it;
- } else {
- creature->inCheckCreaturesVector = false;
- it = checkCreatureList.erase(it);
- ReleaseCreature(creature);
- }
- }
- cleanup();
- }
- void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta)
- {
- int32_t varSpeed = creature->getSpeed() - creature->getBaseSpeed();
- varSpeed += varSpeedDelta;
- creature->setSpeed(varSpeed);
- //send to clients
- SpectatorVec list;
- map.getSpectators(list, creature->getPosition(), false, true);
- for (Creature* spectator : list) {
- spectator->getPlayer()->sendChangeSpeed(creature, creature->getStepSpeed());
- }
- }
- void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit)
- {
- if (!g_events->eventCreatureOnChangeOutfit(creature, outfit)) {
- return;
- }
- creature->setCurrentOutfit(outfit);
- if (creature->isInvisible()) {
- return;
- }
- //send to clients
- SpectatorVec list;
- map.getSpectators(list, creature->getPosition(), true, true);
- for (Creature* spectator : list) {
- spectator->getPlayer()->sendCreatureChangeOutfit(creature, outfit);
- }
- }
- void Game::internalCreatureChangeVisible(Creature* creature, bool visible)
- {
- //send to clients
- SpectatorVec list;
- map.getSpectators(list, creature->getPosition(), true, true);
- for (Creature* spectator : list) {
- spectator->getPlayer()->sendCreatureChangeVisible(creature, visible);
- }
- }
- void Game::changeLight(const Creature* creature)
- {
- //send to clients
- SpectatorVec list;
- map.getSpectators(list, creature->getPosition(), true, true);
- for (Creature* spectator : list) {
- spectator->getPlayer()->sendCreatureLight(creature);
- }
- }
- bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* target, bool checkDefense, bool checkArmor, bool field)
- {
- if (damage.primary.type == COMBAT_NONE && damage.secondary.type == COMBAT_NONE) {
- return true;
- }
- if (target->getPlayer() && target->isInGhostMode()) {
- return true;
- }
- if (damage.primary.value > 0) {
- return false;
- }
- static const auto sendBlockEffect = [this](BlockType_t blockType, CombatType_t combatType, const Position& targetPos) {
- if (blockType == BLOCK_DEFENSE) {
- addMagicEffect(targetPos, CONST_ME_POFF);
- } else if (blockType == BLOCK_ARMOR) {
- addMagicEffect(targetPos, CONST_ME_BLOCKHIT);
- } else if (blockType == BLOCK_IMMUNITY) {
- uint8_t hitEffect = 0;
- switch (combatType) {
- case COMBAT_UNDEFINEDDAMAGE: {
- return;
- }
- case COMBAT_ENERGYDAMAGE:
- case COMBAT_FIREDAMAGE:
- case COMBAT_PHYSICALDAMAGE: {
- hitEffect = CONST_ME_BLOCKHIT;
- break;
- }
- case COMBAT_EARTHDAMAGE: {
- hitEffect = CONST_ME_GREEN_RINGS;
- break;
- }
- default: {
- hitEffect = CONST_ME_POFF;
- break;
- }
- }
- addMagicEffect(targetPos, hitEffect);
- }
- };
- BlockType_t primaryBlockType, secondaryBlockType;
- if (damage.primary.type != COMBAT_NONE) {
- damage.primary.value = -damage.primary.value;
- primaryBlockType = target->blockHit(attacker, damage.primary.type, damage.primary.value, checkDefense, checkArmor, field);
- damage.primary.value = -damage.primary.value;
- sendBlockEffect(primaryBlockType, damage.primary.type, target->getPosition());
- } else {
- primaryBlockType = BLOCK_NONE;
- }
- if (damage.secondary.type != COMBAT_NONE) {
- damage.secondary.value = -damage.secondary.value;
- secondaryBlockType = target->blockHit(attacker, damage.secondary.type, damage.secondary.value, false, false, field);
- damage.secondary.value = -damage.secondary.value;
- sendBlockEffect(secondaryBlockType, damage.secondary.type, target->getPosition());
- } else {
- secondaryBlockType = BLOCK_NONE;
- }
- return (primaryBlockType != BLOCK_NONE) && (secondaryBlockType != BLOCK_NONE);
- }
- void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect)
- {
- switch (combatType) {
- case COMBAT_PHYSICALDAMAGE: {
- Item* splash = nullptr;
- switch (target->getRace()) {
- case RACE_VENOM:
- color = TEXTCOLOR_LIGHTGREEN;
- effect = CONST_ME_HITBYPOISON;
- splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_GREEN);
- break;
- case RACE_BLOOD:
- color = TEXTCOLOR_RED;
- effect = CONST_ME_DRAWBLOOD;
- splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD);
- break;
- case RACE_UNDEAD:
- color = TEXTCOLOR_LIGHTGREY;
- effect = CONST_ME_HITAREA;
- break;
- case RACE_FIRE:
- color = TEXTCOLOR_ORANGE;
- effect = CONST_ME_DRAWBLOOD;
- break;
- case RACE_ENERGY:
- color = TEXTCOLOR_PURPLE;
- effect = CONST_ME_ENERGYHIT;
- break;
- default:
- color = TEXTCOLOR_NONE;
- effect = CONST_ME_NONE;
- break;
- }
- if (splash) {
- internalAddItem(target->getTile(), splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
- startDecay(splash);
- }
- break;
- }
- case COMBAT_ENERGYDAMAGE: {
- color = TEXTCOLOR_PURPLE;
- effect = CONST_ME_ENERGYHIT;
- break;
- }
- case COMBAT_EARTHDAMAGE: {
- color = TEXTCOLOR_LIGHTGREEN;
- effect = CONST_ME_GREEN_RINGS;
- break;
- }
- case COMBAT_FIREDAMAGE: {
- color = TEXTCOLOR_ORANGE;
- effect = CONST_ME_HITBYFIRE;
- break;
- }
- case COMBAT_LIFEDRAIN: {
- color = TEXTCOLOR_RED;
- effect = CONST_ME_MAGIC_RED;
- break;
- }
- default: {
- color = TEXTCOLOR_NONE;
- effect = CONST_ME_NONE;
- break;
- }
- }
- }
- bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage)
- {
- const Position& targetPos = target->getPosition();
- if (damage.primary.value > 0) {
- if (target->getHealth() <= 0) {
- return false;
- }
- Player* attackerPlayer;
- if (attacker) {
- attackerPlayer = attacker->getPlayer();
- } else {
- attackerPlayer = nullptr;
- }
- Player* targetPlayer = target->getPlayer();
- if (damage.origin != ORIGIN_NONE) {
- const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
- if (!events.empty()) {
- for (CreatureEvent* creatureEvent : events) {
- creatureEvent->executeHealthChange(target, attacker, damage);
- }
- damage.origin = ORIGIN_NONE;
- return combatChangeHealth(attacker, target, damage);
- }
- }
- int32_t realHealthChange = target->getHealth();
- target->gainHealth(attacker, damage.primary.value);
- realHealthChange = target->getHealth() - realHealthChange;
- if (realHealthChange > 0 && !target->isInGhostMode()) {
- std::string damageString = std::to_string(realHealthChange) + (realHealthChange != 1 ? " hitpoints." : " hitpoint.");
- std::string spectatorMessage;
- if (!attacker) {
- spectatorMessage += ucfirst(target->getNameDescription());
- spectatorMessage += " was healed for " + damageString;
- } else {
- spectatorMessage += ucfirst(attacker->getNameDescription());
- spectatorMessage += " healed ";
- if (attacker == target) {
- spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "herself" : "himself") : "itself");
- } else {
- spectatorMessage += target->getNameDescription();
- }
- spectatorMessage += " for " + damageString;
- }
- TextMessage message;
- std::ostringstream strHealthChange;
- strHealthChange << realHealthChange;
- addAnimatedText(strHealthChange.str(), targetPos, TEXTCOLOR_MAYABLUE);
- SpectatorVec list;
- map.getSpectators(list, targetPos, false, true);
- for (Creature* spectator : list) {
- Player* tmpPlayer = spectator->getPlayer();
- if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- message.text = "You heal " + target->getNameDescription() + " for " + damageString;
- } else if (tmpPlayer == targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- if (!attacker) {
- message.text = "You were healed for " + damageString;
- } else if (targetPlayer == attackerPlayer) {
- message.text = "You heal yourself for " + damageString;
- } else {
- message.text = "You were healed by " + attacker->getNameDescription() + " for " + damageString;
- }
- } else {
- message.type = MESSAGE_STATUS_SMALL;
- message.text = spectatorMessage;
- }
- tmpPlayer->sendTextMessage(message);
- }
- }
- } else {
- if (!target->isAttackable()) {
- if (!target->isInGhostMode()) {
- addMagicEffect(targetPos, CONST_ME_POFF);
- }
- return true;
- }
- Player* attackerPlayer;
- if (attacker) {
- attackerPlayer = attacker->getPlayer();
- } else {
- attackerPlayer = nullptr;
- }
- Player* targetPlayer = target->getPlayer();
- damage.primary.value = std::abs(damage.primary.value);
- damage.secondary.value = std::abs(damage.secondary.value);
- int32_t healthChange = damage.primary.value + damage.secondary.value;
- if (healthChange == 0) {
- return true;
- }
- TextMessage message;
- SpectatorVec list;
- if (target->hasCondition(CONDITION_MANASHIELD) && damage.primary.type != COMBAT_UNDEFINEDDAMAGE) {
- int32_t manaDamage = std::min<int32_t>(target->getMana(), healthChange);
- if (manaDamage != 0) {
- if (damage.origin != ORIGIN_NONE) {
- const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
- if (!events.empty()) {
- for (CreatureEvent* creatureEvent : events) {
- creatureEvent->executeManaChange(target, attacker, healthChange, damage.origin);
- }
- if (healthChange == 0) {
- return true;
- }
- manaDamage = std::min<int32_t>(target->getMana(), healthChange);
- }
- }
- target->drainMana(attacker, manaDamage);
- map.getSpectators(list, targetPos, true, true);
- addMagicEffect(list, targetPos, CONST_ME_LOSEENERGY);
- std::string damageString = std::to_string(manaDamage);
- std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana";
- if (attacker) {
- spectatorMessage += " due to ";
- if (attacker == target) {
- spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
- } else {
- spectatorMessage += "an attack by " + attacker->getNameDescription();
- }
- }
- spectatorMessage += '.';
- std::ostringstream strManaDamage;
- strManaDamage << manaDamage;
- addAnimatedText(strManaDamage.str(), targetPos, TEXTCOLOR_BLUE);
- for (Creature* spectator : list) {
- Player* tmpPlayer = spectator->getPlayer();
- if (tmpPlayer->getPosition().z != targetPos.z) {
- continue;
- }
- if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana due to your attack.";
- } else if (tmpPlayer == targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- if (!attacker) {
- message.text = "You lose " + damageString + " mana.";
- } else if (targetPlayer == attackerPlayer) {
- message.text = "You lose " + damageString + " mana due to your own attack.";
- } else {
- message.text = "You lose " + damageString + " mana due to an attack by " + attacker->getNameDescription() + '.';
- }
- } else {
- message.type = MESSAGE_STATUS_SMALL;
- message.text = spectatorMessage;
- }
- tmpPlayer->sendTextMessage(message);
- }
- damage.primary.value -= manaDamage;
- if (damage.primary.value < 0) {
- damage.secondary.value = std::max<int32_t>(0, damage.secondary.value + damage.primary.value);
- damage.primary.value = 0;
- }
- }
- }
- int32_t realDamage = damage.primary.value + damage.secondary.value;
- if (realDamage == 0) {
- return true;
- }
- if (damage.origin != ORIGIN_NONE) {
- const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE);
- if (!events.empty()) {
- for (CreatureEvent* creatureEvent : events) {
- creatureEvent->executeHealthChange(target, attacker, damage);
- }
- damage.origin = ORIGIN_NONE;
- return combatChangeHealth(attacker, target, damage);
- }
- }
- int32_t targetHealth = target->getHealth();
- if (damage.primary.value >= targetHealth) {
- damage.primary.value = targetHealth;
- damage.secondary.value = 0;
- } else if (damage.secondary.value) {
- damage.secondary.value = std::min<int32_t>(damage.secondary.value, targetHealth - damage.primary.value);
- }
- realDamage = damage.primary.value + damage.secondary.value;
- if (realDamage == 0) {
- return true;
- } else if (realDamage >= targetHealth) {
- for (CreatureEvent* creatureEvent : target->getCreatureEvents(CREATURE_EVENT_PREPAREDEATH)) {
- if (!creatureEvent->executeOnPrepareDeath(target, attacker)) {
- return false;
- }
- }
- }
- target->drainHealth(attacker, realDamage);
- if (list.empty()) {
- map.getSpectators(list, targetPos, true, true);
- }
- addCreatureHealth(list, target);
- message.primary.value = damage.primary.value;
- message.secondary.value = damage.secondary.value;
- uint8_t hitEffect;
- if (message.primary.value) {
- combatGetTypeInfo(damage.primary.type, target, message.primary.color, hitEffect);
- if (hitEffect != CONST_ME_NONE) {
- addMagicEffect(list, targetPos, hitEffect);
- }
- if (message.primary.color != TEXTCOLOR_NONE) {
- std::ostringstream strPrimaryDamage;
- strPrimaryDamage << message.primary.value;
- addAnimatedText(strPrimaryDamage.str(), targetPos, message.primary.color);
- }
- }
- if (message.secondary.value) {
- combatGetTypeInfo(damage.secondary.type, target, message.secondary.color, hitEffect);
- if (hitEffect != CONST_ME_NONE) {
- addMagicEffect(list, targetPos, hitEffect);
- }
- if (message.secondary.color != TEXTCOLOR_NONE) {
- std::ostringstream strSecondaryDamage;
- strSecondaryDamage << message.secondary.value;
- addAnimatedText(strSecondaryDamage.str(), targetPos, message.secondary.color);
- }
- }
- if (message.primary.color != TEXTCOLOR_NONE || message.secondary.color != TEXTCOLOR_NONE) {
- std::string damageString = std::to_string(realDamage) + (realDamage != 1 ? " hitpoints" : " hitpoint");
- std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString;
- if (attacker) {
- spectatorMessage += " due to ";
- if (attacker == target) {
- spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
- } else {
- spectatorMessage += "an attack by " + attacker->getNameDescription();
- }
- }
- spectatorMessage += '.';
- for (Creature* spectator : list) {
- Player* tmpPlayer = spectator->getPlayer();
- if (tmpPlayer->getPosition().z != targetPos.z) {
- continue;
- }
- if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " due to your attack.";
- } else if (tmpPlayer == targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- if (!attacker) {
- message.text = "You lose " + damageString + '.';
- } else if (targetPlayer == attackerPlayer) {
- message.text = "You lose " + damageString + " due to your own attack.";
- } else {
- message.text = "You lose " + damageString + " due to an attack by " + attacker->getNameDescription() + '.';
- }
- } else {
- message.type = MESSAGE_STATUS_SMALL;
- // TODO: Avoid copying spectatorMessage everytime we send to a spectator
- message.text = spectatorMessage;
- }
- tmpPlayer->sendTextMessage(message);
- }
- }
- }
- return true;
- }
- bool Game::combatChangeMana(Creature* attacker, Creature* target, int32_t manaChange, CombatOrigin origin)
- {
- if (manaChange > 0) {
- if (origin != ORIGIN_NONE) {
- const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
- if (!events.empty()) {
- for (CreatureEvent* creatureEvent : events) {
- creatureEvent->executeManaChange(target, attacker, manaChange, origin);
- }
- return combatChangeMana(attacker, target, manaChange, ORIGIN_NONE);
- }
- }
- target->changeMana(manaChange);
- } else {
- const Position& targetPos = target->getPosition();
- if (!target->isAttackable()) {
- if (!target->isInGhostMode()) {
- addMagicEffect(targetPos, CONST_ME_POFF);
- }
- return false;
- }
- Player* attackerPlayer;
- if (attacker) {
- attackerPlayer = attacker->getPlayer();
- } else {
- attackerPlayer = nullptr;
- }
- Player* targetPlayer = target->getPlayer();
- int32_t manaLoss = std::min<int32_t>(target->getMana(), -manaChange);
- BlockType_t blockType = target->blockHit(attacker, COMBAT_MANADRAIN, manaLoss);
- if (blockType != BLOCK_NONE) {
- addMagicEffect(targetPos, CONST_ME_POFF);
- return false;
- }
- if (manaLoss <= 0) {
- return true;
- }
- if (origin != ORIGIN_NONE) {
- const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE);
- if (!events.empty()) {
- for (CreatureEvent* creatureEvent : events) {
- creatureEvent->executeManaChange(target, attacker, manaChange, origin);
- }
- return combatChangeMana(attacker, target, manaChange, ORIGIN_NONE);
- }
- }
- target->drainMana(attacker, manaLoss);
- std::string damageString = std::to_string(manaLoss);
- std::string spectatorMessage = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana";
- if (attacker) {
- spectatorMessage += " due to ";
- if (attacker == target) {
- spectatorMessage += (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack") : "its own attack");
- } else {
- spectatorMessage += "an attack by " + attacker->getNameDescription();
- }
- }
- spectatorMessage += '.';
- TextMessage message;
- std::ostringstream strManaLoss;
- strManaLoss << manaLoss;
- addAnimatedText(strManaLoss.str(), targetPos, TEXTCOLOR_BLUE);
- SpectatorVec list;
- map.getSpectators(list, targetPos, false, true);
- for (Creature* spectator : list) {
- Player* tmpPlayer = spectator->getPlayer();
- if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- message.text = ucfirst(target->getNameDescription()) + " loses " + damageString + " mana due to your attack.";
- } else if (tmpPlayer == targetPlayer) {
- message.type = MESSAGE_STATUS_SMALL;
- if (!attacker) {
- message.text = "You lose " + damageString + " mana.";
- } else if (targetPlayer == attackerPlayer) {
- message.text = "You lose " + damageString + " mana due to your own attack.";
- } else {
- message.text = "You lose " + damageString + " mana due to an attack by " + attacker->getNameDescription() + '.';
- }
- } else {
- message.type = MESSAGE_STATUS_SMALL;
- message.text = spectatorMessage;
- }
- tmpPlayer->sendTextMessage(message);
- }
- }
- return true;
- }
- void Game::addCreatureHealth(const Creature* target)
- {
- SpectatorVec list;
- map.getSpectators(list, target->getPosition(), true, true);
- addCreatureHealth(list, target);
- }
- void Game::addCreatureHealth(const SpectatorVec& list, const Creature* target)
- {
- for (Creature* spectator : list) {
- if (Player* tmpPlayer = spectator->getPlayer()) {
- tmpPlayer->sendCreatureHealth(target);
- }
- }
- }
- void Game::addAnimatedText(const std::string& message, const Position& pos, TextColor_t color)
- {
- SpectatorVec list;
- map.getSpectators(list, pos, true, true);
- addAnimatedText(list, message, pos, color);
- }
- void Game::addAnimatedText(const SpectatorVec& list, const std::string& message, const Position& pos, TextColor_t color)
- {
- for (Creature* spectator : list) {
- if (Player* tmpPlayer = spectator->getPlayer()) {
- tmpPlayer->sendAnimatedText(message, pos, color);
- }
- }
- }
- void Game::addMagicEffect(const Position& pos, uint8_t effect)
- {
- SpectatorVec list;
- map.getSpectators(list, pos, true, true);
- addMagicEffect(list, pos, effect);
- }
- void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect)
- {
- for (Creature* spectator : list) {
- if (Player* tmpPlayer = spectator->getPlayer()) {
- tmpPlayer->sendMagicEffect(pos, effect);
- }
- }
- }
- void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect)
- {
- SpectatorVec list;
- map.getSpectators(list, fromPos, false, true);
- map.getSpectators(list, toPos, false, true);
- addDistanceEffect(list, fromPos, toPos, effect);
- }
- void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect)
- {
- for (Creature* spectator : list) {
- if (Player* tmpPlayer = spectator->getPlayer()) {
- tmpPlayer->sendDistanceShoot(fromPos, toPos, effect);
- }
- }
- }
- void Game::startDecay(Item* item)
- {
- if (!item || !item->canDecay()) {
- return;
- }
- ItemDecayState_t decayState = item->getDecaying();
- if (decayState == DECAYING_TRUE) {
- return;
- }
- if (item->getDuration() > 0) {
- item->incrementReferenceCounter();
- item->setDecaying(DECAYING_TRUE);
- toDecayItems.push_front(item);
- } else {
- internalDecayItem(item);
- }
- }
- void Game::internalDecayItem(Item* item)
- {
- const ItemType& it = Item::items[item->getID()];
- if (it.decayTo != 0) {
- Item* newItem = transformItem(item, it.decayTo);
- startDecay(newItem);
- } else {
- ReturnValue ret = internalRemoveItem(item);
- if (ret != RETURNVALUE_NOERROR) {
- std::cout << "[Debug - Game::internalDecayItem] internalDecayItem failed, error code: " << static_cast<uint32_t>(ret) << ", item id: " << item->getID() << std::endl;
- }
- }
- }
- void Game::checkDecay()
- {
- g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this)));
- size_t bucket = (lastBucket + 1) % EVENT_DECAY_BUCKETS;
- auto it = decayItems[bucket].begin(), end = decayItems[bucket].end();
- while (it != end) {
- Item* item = *it;
- if (!item->canDecay()) {
- item->setDecaying(DECAYING_FALSE);
- ReleaseItem(item);
- it = decayItems[bucket].erase(it);
- continue;
- }
- int32_t duration = item->getDuration();
- int32_t decreaseTime = std::min<int32_t>(EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS, duration);
- duration -= decreaseTime;
- item->decreaseDuration(decreaseTime);
- if (duration <= 0) {
- it = decayItems[bucket].erase(it);
- internalDecayItem(item);
- ReleaseItem(item);
- } else if (duration < EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
- it = decayItems[bucket].erase(it);
- size_t newBucket = (bucket + ((duration + EVENT_DECAYINTERVAL / 2) / 1000)) % EVENT_DECAY_BUCKETS;
- if (newBucket == bucket) {
- internalDecayItem(item);
- ReleaseItem(item);
- } else {
- decayItems[newBucket].push_back(item);
- }
- } else {
- ++it;
- }
- }
- lastBucket = bucket;
- cleanup();
- }
- void Game::checkLight()
- {
- g_scheduler.addEvent(createSchedulerTask(EVENT_LIGHTINTERVAL, std::bind(&Game::checkLight, this)));
- lightHour += lightHourDelta;
- if (lightHour > 1440) {
- lightHour -= 1440;
- }
- if (std::abs(lightHour - SUNRISE) < 2 * lightHourDelta) {
- lightState = LIGHT_STATE_SUNRISE;
- } else if (std::abs(lightHour - SUNSET) < 2 * lightHourDelta) {
- lightState = LIGHT_STATE_SUNSET;
- }
- int32_t newLightLevel = lightLevel;
- bool lightChange = false;
- switch (lightState) {
- case LIGHT_STATE_SUNRISE: {
- newLightLevel += (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
- lightChange = true;
- break;
- }
- case LIGHT_STATE_SUNSET: {
- newLightLevel -= (LIGHT_LEVEL_DAY - LIGHT_LEVEL_NIGHT) / 30;
- lightChange = true;
- break;
- }
- default:
- break;
- }
- if (newLightLevel <= LIGHT_LEVEL_NIGHT) {
- lightLevel = LIGHT_LEVEL_NIGHT;
- lightState = LIGHT_STATE_NIGHT;
- } else if (newLightLevel >= LIGHT_LEVEL_DAY) {
- lightLevel = LIGHT_LEVEL_DAY;
- lightState = LIGHT_STATE_DAY;
- } else {
- lightLevel = newLightLevel;
- }
- if (lightChange) {
- LightInfo lightInfo;
- getWorldLightInfo(lightInfo);
- for (const auto& it : players) {
- it.second->sendWorldLight(lightInfo);
- }
- }
- }
- void Game::getWorldLightInfo(LightInfo& lightInfo) const
- {
- lightInfo.level = lightLevel;
- lightInfo.color = 0xD7;
- }
- void Game::addCommandTag(char tag)
- {
- for (char commandTag : commandTags) {
- if (commandTag == tag) {
- return;
- }
- }
- commandTags.push_back(tag);
- }
- void Game::resetCommandTag()
- {
- commandTags.clear();
- }
- void Game::shutdown()
- {
- std::cout << "Shutting down..." << std::flush;
- g_scheduler.shutdown();
- g_databaseTasks.shutdown();
- g_dispatcher.shutdown();
- map.spawns.clear();
- raids.clear();
- cleanup();
- if (serviceManager) {
- serviceManager->stop();
- }
- ConnectionManager::getInstance().closeAll();
- std::cout << " done!" << std::endl;
- }
- void Game::cleanup()
- {
- //free memory
- for (auto creature : ToReleaseCreatures) {
- creature->decrementReferenceCounter();
- }
- ToReleaseCreatures.clear();
- for (auto item : ToReleaseItems) {
- item->decrementReferenceCounter();
- }
- ToReleaseItems.clear();
- for (Item* item : toDecayItems) {
- const uint32_t dur = item->getDuration();
- if (dur >= EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) {
- decayItems[lastBucket].push_back(item);
- } else {
- decayItems[(lastBucket + 1 + dur / 1000) % EVENT_DECAY_BUCKETS].push_back(item);
- }
- }
- toDecayItems.clear();
- }
- void Game::ReleaseCreature(Creature* creature)
- {
- ToReleaseCreatures.push_back(creature);
- }
- void Game::ReleaseItem(Item* item)
- {
- ToReleaseItems.push_back(item);
- }
- void Game::broadcastMessage(const std::string& text, MessageClasses type) const
- {
- std::cout << "> Broadcasted message: \"" << text << "\"." << std::endl;
- for (const auto& it : players) {
- it.second->sendTextMessage(type, text);
- }
- }
- void Game::updateCreatureSkull(const Creature* creature)
- {
- if (getWorldType() != WORLD_TYPE_PVP) {
- return;
- }
- SpectatorVec list;
- map.getSpectators(list, creature->getPosition(), true, true);
- for (Creature* spectator : list) {
- spectator->getPlayer()->sendCreatureSkull(creature);
- }
- }
- void Game::updatePlayerShield(Player* player)
- {
- SpectatorVec list;
- map.getSpectators(list, player->getPosition(), true, true);
- for (Creature* spectator : list) {
- spectator->getPlayer()->sendCreatureShield(player);
- }
- }
- void Game::updatePremium(Account& account)
- {
- bool save = false;
- time_t timeNow = time(nullptr);
- if (account.premiumDays != 0 && account.premiumDays != std::numeric_limits<uint16_t>::max()) {
- if (account.lastDay == 0) {
- account.lastDay = timeNow;
- save = true;
- } else {
- uint32_t days = (timeNow - account.lastDay) / 86400;
- if (days > 0) {
- if (days >= account.premiumDays) {
- account.premiumDays = 0;
- account.lastDay = 0;
- } else {
- account.premiumDays -= days;
- time_t remainder = (timeNow - account.lastDay) % 86400;
- account.lastDay = timeNow - remainder;
- }
- save = true;
- }
- }
- } else if (account.lastDay != 0) {
- account.lastDay = 0;
- save = true;
- }
- if (save && !IOLoginData::saveAccount(account)) {
- std::cout << "> ERROR: Failed to save account: " << account.name << "!" << std::endl;
- }
- }
- void Game::loadMotdNum()
- {
- Database& db = Database::getInstance();
- DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_num'");
- if (result) {
- motdNum = result->getNumber<uint32_t>("value");
- } else {
- db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_num', '0')");
- }
- result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_hash'");
- if (result) {
- motdHash = result->getString("value");
- if (motdHash != transformToSHA1(g_config.getString(ConfigManager::MOTD))) {
- ++motdNum;
- }
- } else {
- db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_hash', '')");
- }
- }
- void Game::saveMotdNum() const
- {
- Database& db = Database::getInstance();
- std::ostringstream query;
- query << "UPDATE `server_config` SET `value` = '" << motdNum << "' WHERE `config` = 'motd_num'";
- db.executeQuery(query.str());
- query.str(std::string());
- query << "UPDATE `server_config` SET `value` = '" << transformToSHA1(g_config.getString(ConfigManager::MOTD)) << "' WHERE `config` = 'motd_hash'";
- db.executeQuery(query.str());
- }
- void Game::checkPlayersRecord()
- {
- const size_t playersOnline = getPlayersOnline();
- if (playersOnline > playersRecord) {
- uint32_t previousRecord = playersRecord;
- playersRecord = playersOnline;
- for (const auto& it : g_globalEvents->getEventMap(GLOBALEVENT_RECORD)) {
- it.second->executeRecord(playersRecord, previousRecord);
- }
- updatePlayersRecord();
- }
- }
- void Game::updatePlayersRecord() const
- {
- Database& db = Database::getInstance();
- std::ostringstream query;
- query << "UPDATE `server_config` SET `value` = '" << playersRecord << "' WHERE `config` = 'players_record'";
- db.executeQuery(query.str());
- }
- void Game::loadPlayersRecord()
- {
- Database& db = Database::getInstance();
- DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'players_record'");
- if (result) {
- playersRecord = result->getNumber<uint32_t>("value");
- } else {
- db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('players_record', '0')");
- }
- }
- uint64_t Game::getExperienceStage(uint32_t level)
- {
- if (!stagesEnabled) {
- return g_config.getNumber(ConfigManager::RATE_EXPERIENCE);
- }
- if (useLastStageLevel && level >= lastStageLevel) {
- return stages[lastStageLevel];
- }
- return stages[level];
- }
- bool Game::loadExperienceStages()
- {
- pugi::xml_document doc;
- pugi::xml_parse_result result = doc.load_file("data/XML/stages.xml");
- if (!result) {
- printXMLError("Error - Game::loadExperienceStages", "data/XML/stages.xml", result);
- return false;
- }
- for (auto stageNode : doc.child("stages").children()) {
- if (strcasecmp(stageNode.name(), "config") == 0) {
- stagesEnabled = stageNode.attribute("enabled").as_bool();
- } else {
- uint32_t minLevel, maxLevel, multiplier;
- pugi::xml_attribute minLevelAttribute = stageNode.attribute("minlevel");
- if (minLevelAttribute) {
- minLevel = pugi::cast<uint32_t>(minLevelAttribute.value());
- } else {
- minLevel = 1;
- }
- pugi::xml_attribute maxLevelAttribute = stageNode.attribute("maxlevel");
- if (maxLevelAttribute) {
- maxLevel = pugi::cast<uint32_t>(maxLevelAttribute.value());
- } else {
- maxLevel = 0;
- lastStageLevel = minLevel;
- useLastStageLevel = true;
- }
- pugi::xml_attribute multiplierAttribute = stageNode.attribute("multiplier");
- if (multiplierAttribute) {
- multiplier = pugi::cast<uint32_t>(multiplierAttribute.value());
- } else {
- multiplier = 1;
- }
- if (useLastStageLevel) {
- stages[lastStageLevel] = multiplier;
- } else {
- for (uint32_t i = minLevel; i <= maxLevel; ++i) {
- stages[i] = multiplier;
- }
- }
- }
- }
- return true;
- }
- void Game::playerInviteToParty(uint32_t playerId, uint32_t invitedId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Player* invitedPlayer = getPlayerByID(invitedId);
- if (!invitedPlayer || invitedPlayer->isInviting(player)) {
- return;
- }
- if (invitedPlayer->getParty()) {
- std::ostringstream ss;
- ss << invitedPlayer->getName() << " is already in a party.";
- player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
- return;
- }
- Party* party = player->getParty();
- if (!party) {
- party = new Party(player);
- } else if (party->getLeader() != player) {
- return;
- }
- party->invitePlayer(*invitedPlayer);
- }
- void Game::playerJoinParty(uint32_t playerId, uint32_t leaderId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Player* leader = getPlayerByID(leaderId);
- if (!leader || !leader->isInviting(player)) {
- return;
- }
- Party* party = leader->getParty();
- if (!party || party->getLeader() != leader) {
- return;
- }
- if (player->getParty()) {
- player->sendTextMessage(MESSAGE_INFO_DESCR, "You are already in a party.");
- return;
- }
- party->joinParty(*player);
- }
- void Game::playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Party* party = player->getParty();
- if (!party || party->getLeader() != player) {
- return;
- }
- Player* invitedPlayer = getPlayerByID(invitedId);
- if (!invitedPlayer || !player->isInviting(invitedPlayer)) {
- return;
- }
- party->revokeInvitation(*invitedPlayer);
- }
- void Game::playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Party* party = player->getParty();
- if (!party || party->getLeader() != player) {
- return;
- }
- Player* newLeader = getPlayerByID(newLeaderId);
- if (!newLeader || !player->isPartner(newLeader)) {
- return;
- }
- party->passPartyLeadership(newLeader);
- }
- void Game::playerLeaveParty(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Party* party = player->getParty();
- if (!party || player->hasCondition(CONDITION_INFIGHT)) {
- return;
- }
- party->leaveParty(player);
- }
- void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Party* party = player->getParty();
- if (!party || player->hasCondition(CONDITION_INFIGHT)) {
- return;
- }
- party->setSharedExperience(player, sharedExpActive);
- }
- void Game::sendGuildMotd(uint32_t playerId)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- Guild* guild = player->getGuild();
- if (guild) {
- player->sendChannelMessage("Message of the Day", guild->getMotd(), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD);
- }
- }
- void Game::kickPlayer(uint32_t playerId, bool displayEffect)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- player->kickPlayer(displayEffect);
- }
- void Game::playerReportBug(uint32_t playerId, const std::string& bug)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- if (player->getAccountType() == ACCOUNT_TYPE_NORMAL) {
- return;
- }
- std::string fileName = "data/reports/" + player->getName() + " report.txt";
- FILE* file = fopen(fileName.c_str(), "a");
- if (!file) {
- player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "There was an error when processing your report, please contact a gamemaster.");
- return;
- }
- const Position& position = player->getPosition();
- fprintf(file, "------------------------------\nName: %s [Position X: %u Y: %u Z: %u]\nBug Report: %s\n", player->getName().c_str(), position.x, position.y, position.z, bug.c_str());
- fclose(file);
- player->sendTextMessage(MESSAGE_EVENT_DEFAULT, "Your report has been sent to " + g_config.getString(ConfigManager::SERVER_NAME) + ".");
- }
- void Game::playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- // TODO: move debug assertions to database
- FILE* file = fopen("client_assertions.txt", "a");
- if (file) {
- fprintf(file, "----- %s - %s (%s) -----\n", formatDate(time(nullptr)).c_str(), player->getName().c_str(), convertIPToString(player->getIP()).c_str());
- fprintf(file, "%s\n%s\n%s\n%s\n", assertLine.c_str(), date.c_str(), description.c_str(), comment.c_str());
- fclose(file);
- }
- }
- void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer)
- {
- Player* player = getPlayerByID(playerId);
- if (!player) {
- return;
- }
- for (CreatureEvent* creatureEvent : player->getCreatureEvents(CREATURE_EVENT_EXTENDED_OPCODE)) {
- creatureEvent->executeExtendedOpcode(player, opcode, buffer);
- }
- }
- void Game::forceAddCondition(uint32_t creatureId, Condition* condition)
- {
- Creature* creature = getCreatureByID(creatureId);
- if (!creature) {
- delete condition;
- return;
- }
- creature->addCondition(condition, true);
- }
- void Game::forceRemoveCondition(uint32_t creatureId, ConditionType_t type)
- {
- Creature* creature = getCreatureByID(creatureId);
- if (!creature) {
- return;
- }
- creature->removeCondition(type, true);
- }
- void Game::addPlayer(Player* player)
- {
- const std::string& lowercase_name = asLowerCaseString(player->getName());
- mappedPlayerNames[lowercase_name] = player;
- wildcardTree.insert(lowercase_name);
- players[player->getID()] = player;
- }
- void Game::removePlayer(Player* player)
- {
- const std::string& lowercase_name = asLowerCaseString(player->getName());
- mappedPlayerNames.erase(lowercase_name);
- wildcardTree.remove(lowercase_name);
- players.erase(player->getID());
- }
- void Game::addNpc(Npc* npc)
- {
- npcs[npc->getID()] = npc;
- }
- void Game::removeNpc(Npc* npc)
- {
- npcs.erase(npc->getID());
- }
- void Game::addMonster(Monster* monster)
- {
- monsters[monster->getID()] = monster;
- }
- void Game::removeMonster(Monster* monster)
- {
- monsters.erase(monster->getID());
- }
- Guild* Game::getGuild(uint32_t id) const
- {
- auto it = guilds.find(id);
- if (it == guilds.end()) {
- return nullptr;
- }
- return it->second;
- }
- void Game::addGuild(Guild* guild)
- {
- guilds[guild->getId()] = guild;
- }
- void Game::removeGuild(uint32_t guildId)
- {
- guilds.erase(guildId);
- }
- void Game::internalRemoveItems(std::vector<Item*> itemList, uint32_t amount, bool stackable)
- {
- if (stackable) {
- for (Item* item : itemList) {
- if (item->getItemCount() > amount) {
- internalRemoveItem(item, amount);
- break;
- } else {
- amount -= item->getItemCount();
- internalRemoveItem(item);
- }
- }
- } else {
- for (Item* item : itemList) {
- internalRemoveItem(item);
- }
- }
- }
- BedItem* Game::getBedBySleeper(uint32_t guid) const
- {
- auto it = bedSleepersMap.find(guid);
- if (it == bedSleepersMap.end()) {
- return nullptr;
- }
- return it->second;
- }
- void Game::setBedSleeper(BedItem* bed, uint32_t guid)
- {
- bedSleepersMap[guid] = bed;
- }
- void Game::removeBedSleeper(uint32_t guid)
- {
- auto it = bedSleepersMap.find(guid);
- if (it != bedSleepersMap.end()) {
- bedSleepersMap.erase(it);
- }
- }
- Item* Game::getUniqueItem(uint16_t uniqueId)
- {
- auto it = uniqueItems.find(uniqueId);
- if (it == uniqueItems.end()) {
- return nullptr;
- }
- return it->second;
- }
- bool Game::addUniqueItem(uint16_t uniqueId, Item* item)
- {
- auto result = uniqueItems.emplace(uniqueId, item);
- if (!result.second) {
- std::cout << "Duplicate unique id: " << uniqueId << std::endl;
- }
- return result.second;
- }
- void Game::removeUniqueItem(uint16_t uniqueId)
- {
- auto it = uniqueItems.find(uniqueId);
- if (it != uniqueItems.end()) {
- uniqueItems.erase(it);
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement