/** * Tibia GIMUD Server - a free and open-source MMORPG server emulator * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 "bed.h" #include "game.h" #include "iologindata.h" #include "scheduler.h" extern Game g_game; BedItem::BedItem(uint16_t id) : Item(id) { internalRemoveSleeper(); } Attr_ReadValue BedItem::readAttr(AttrTypes_t attr, PropStream& propStream) { switch (attr) { case ATTR_SLEEPERGUID: { uint32_t guid; if (!propStream.read(guid)) { return ATTR_READ_ERROR; } if (guid != 0) { std::string name = IOLoginData::getNameByGuid(guid); if (!name.empty()) { setSpecialDescription(name + " is sleeping there."); g_game.setBedSleeper(this, guid); sleeperGUID = guid; } } return ATTR_READ_CONTINUE; } case ATTR_SLEEPSTART: { uint32_t sleep_start; if (!propStream.read(sleep_start)) { return ATTR_READ_ERROR; } sleepStart = static_cast(sleep_start); return ATTR_READ_CONTINUE; } default: break; } return Item::readAttr(attr, propStream); } void BedItem::serializeAttr(PropWriteStream& propWriteStream) const { if (sleeperGUID != 0) { propWriteStream.write(ATTR_SLEEPERGUID); propWriteStream.write(sleeperGUID); } if (sleepStart != 0) { propWriteStream.write(ATTR_SLEEPSTART); // FIXME: should be stored as 64-bit, but we need to retain backwards compatibility propWriteStream.write(static_cast(sleepStart)); } } BedItem* BedItem::getNextBedItem() const { Direction dir = Item::items[id].bedPartnerDir; Position targetPos = getNextPosition(dir, getPosition()); Tile* tile = g_game.map.getTile(targetPos); if (!tile) { return nullptr; } return tile->getBedItem(); } bool BedItem::canUse(Player* player) { if (!player || !house || !player->isPremium()) { return false; } if (sleeperGUID == 0) { return true; } if (house->getHouseAccessLevel(player) == HOUSE_OWNER) { return true; } Player sleeper(nullptr); if (!IOLoginData::loadPlayerById(&sleeper, sleeperGUID)) { return false; } if (house->getHouseAccessLevel(&sleeper) > house->getHouseAccessLevel(player)) { return false; } return true; } bool BedItem::trySleep(Player* player) { if (!house || player->isRemoved()) { return false; } if (sleeperGUID != 0) { if (Item::items[id].transformToFree != 0 && house->getOwner() == player->getGUID()) { wakeUp(nullptr); } g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } return true; } bool BedItem::sleep(Player* player) { if (!house) { return false; } if (sleeperGUID != 0) { return false; } BedItem* nextBedItem = getNextBedItem(); internalSetSleeper(player); if (nextBedItem) { nextBedItem->internalSetSleeper(player); } // update the bedSleepersMap g_game.setBedSleeper(this, player->getGUID()); // make the player walk onto the bed g_game.map.moveCreature(*player, *getTile()); // display poff effect g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); // kick player after he sees himself walk onto the bed and it change id uint32_t playerId = player->getID(); g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&Game::kickPlayer, &g_game, playerId, false))); // change self and partner's appearance updateAppearance(player); if (nextBedItem) { nextBedItem->updateAppearance(player); } return true; } void BedItem::wakeUp(Player* player) { if (!house) { return; } if (sleeperGUID != 0) { if (!player) { Player regenPlayer(nullptr); if (IOLoginData::loadPlayerById(®enPlayer, sleeperGUID)) { regeneratePlayer(®enPlayer); IOLoginData::savePlayer(®enPlayer); } } else { regeneratePlayer(player); g_game.addCreatureHealth(player); } } // update the bedSleepersMap g_game.removeBedSleeper(sleeperGUID); BedItem* nextBedItem = getNextBedItem(); // unset sleep info internalRemoveSleeper(); if (nextBedItem) { nextBedItem->internalRemoveSleeper(); } // change self and partner's appearance updateAppearance(nullptr); if (nextBedItem) { nextBedItem->updateAppearance(nullptr); } } void BedItem::regeneratePlayer(Player* player) const { const uint32_t sleptTime = time(nullptr) - sleepStart; /*Condition* condition = player->getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT); if (condition) { uint32_t regen; if (condition->getTicks() != -1) { regen = std::min((condition->getTicks() / 1000), sleptTime) / 30; const int32_t newRegenTicks = condition->getTicks() - (regen * 30000); if (newRegenTicks <= 0) { player->removeCondition(condition); } else { condition->setTicks(newRegenTicks); } } else { regen = sleptTime / 30; } player->changeHealth(regen, false); player->changeMana(regen); }*/ const int32_t soulRegen = sleptTime / (60 * 15); player->changeSoul(soulRegen); } void BedItem::updateAppearance(const Player* player) { const ItemType& it = Item::items[id]; if (it.type == ITEM_TYPE_BED) { if (player && it.transformToOnUse != 0) { const ItemType& newType = Item::items[it.transformToOnUse]; if (newType.type == ITEM_TYPE_BED) { g_game.transformItem(this, it.transformToOnUse); } } else if (it.transformToFree != 0) { const ItemType& newType = Item::items[it.transformToFree]; if (newType.type == ITEM_TYPE_BED) { g_game.transformItem(this, it.transformToFree); } } } } void BedItem::internalSetSleeper(const Player* player) { std::string desc_str = player->getName() + " is sleeping there."; sleeperGUID = player->getGUID(); sleepStart = time(nullptr); setSpecialDescription(desc_str); } void BedItem::internalRemoveSleeper() { sleeperGUID = 0; sleepStart = 0; setSpecialDescription("Nobody is sleeping there."); }