#include "../../Platform/stdafx.h" #include "../../Headers/com.mojang.nbt.h" #include "../../Headers/net.minecraft.world.h" #include "../../Headers/net.minecraft.world.level.h" #include "TileEntity.h" #include "../../Headers/net.minecraft.world.entity.item.h" #include "../../Headers/net.minecraft.world.entity.player.h" #include "../../Headers/net.minecraft.world.item.h" #include "../../Headers/net.minecraft.world.inventory.h" #include "../../Headers/net.minecraft.world.level.tile.h" #include "../../Headers/net.minecraft.world.phys.h" #include "ChestTileEntity.h" #include "../../Network/Packets/ContainerOpenPacket.h" #include "../../Util/SoundTypes.h" int ChestTileEntity::getContainerType() { if (isBonusChest) return ContainerOpenPacket::BONUS_CHEST; else return ContainerOpenPacket::CONTAINER; } void ChestTileEntity::_init(bool isBonusChest) { items = new ItemInstanceArray(9 * 4); hasCheckedNeighbors = false; this->isBonusChest = isBonusChest; openness = 0.0f; oOpenness = 0.0f; openCount = 0; tickInterval = 0; type = -1; name = L""; } ChestTileEntity::ChestTileEntity(bool isBonusChest /* = false*/) : TileEntity() { _init(isBonusChest); } ChestTileEntity::ChestTileEntity(int type, bool isBonusChest /* = false*/) : TileEntity() { _init(isBonusChest); this->type = type; } ChestTileEntity::~ChestTileEntity() { delete[] items->data; delete items; } unsigned int ChestTileEntity::getContainerSize() { return 9 * 3; } std::shared_ptr ChestTileEntity::getItem(unsigned int slot) { return items->data[slot]; } std::shared_ptr ChestTileEntity::removeItem(unsigned int slot, int count) { if (items->data[slot] != NULL) { if (items->data[slot]->count <= count) { std::shared_ptr item = items->data[slot]; items->data[slot] = nullptr; setChanged(); // 4J Stu - Fix for duplication glitch if (item->count <= 0) return nullptr; return item; } else { std::shared_ptr i = items->data[slot]->remove(count); if (items->data[slot]->count == 0) items->data[slot] = nullptr; setChanged(); // 4J Stu - Fix for duplication glitch if (i->count <= 0) return nullptr; return i; } } return nullptr; } std::shared_ptr ChestTileEntity::removeItemNoUpdate(int slot) { if (items->data[slot] != NULL) { std::shared_ptr item = items->data[slot]; items->data[slot] = nullptr; return item; } return nullptr; } void ChestTileEntity::setItem(unsigned int slot, std::shared_ptr item) { items->data[slot] = item; if (item != NULL && item->count > getMaxStackSize()) item->count = getMaxStackSize(); this->setChanged(); } std::wstring ChestTileEntity::getName() { return hasCustomName() ? name : app.GetString(IDS_TILE_CHEST); } std::wstring ChestTileEntity::getCustomName() { return hasCustomName() ? name : L""; } bool ChestTileEntity::hasCustomName() { return !name.empty(); } void ChestTileEntity::setCustomName(const std::wstring& name) { this->name = name; } void ChestTileEntity::load(CompoundTag* base) { TileEntity::load(base); ListTag* inventoryList = (ListTag*)base->getList(L"Items"); if (items) { delete[] items->data; delete items; } items = new ItemInstanceArray(getContainerSize()); if (base->contains(L"CustomName")) name = base->getString(L"CustomName"); for (int i = 0; i < inventoryList->size(); i++) { CompoundTag* tag = inventoryList->get(i); unsigned int slot = tag->getByte(L"Slot") & 0xff; if (slot >= 0 && slot < items->length) (*items)[slot] = ItemInstance::fromTag(tag); } isBonusChest = base->getBoolean(L"bonus"); } void ChestTileEntity::save(CompoundTag* base) { TileEntity::save(base); ListTag* listTag = new ListTag; for (unsigned int i = 0; i < items->length; i++) { if (items->data[i] != NULL) { CompoundTag* tag = new CompoundTag(); tag->putByte(L"Slot", (uint8_t)i); items->data[i]->save(tag); listTag->add(tag); } } base->put(L"Items", listTag); if (hasCustomName()) base->putString(L"CustomName", name); base->putBoolean(L"bonus", isBonusChest); } int ChestTileEntity::getMaxStackSize() { return Container::LARGE_MAX_STACK_SIZE; } bool ChestTileEntity::stillValid(std::shared_ptr player) { if (level->getTileEntity(x, y, z) != shared_from_this()) return false; if (player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) > 8 * 8) return false; return true; } void ChestTileEntity::setChanged() { TileEntity::setChanged(); } void ChestTileEntity::clearCache() { TileEntity::clearCache(); hasCheckedNeighbors = false; } void ChestTileEntity::heyImYourNeighbor( std::shared_ptr neighbor, int from) { if (neighbor->isRemoved()) { hasCheckedNeighbors = false; } else if (hasCheckedNeighbors) { switch (from) { case Direction::NORTH: if (n.lock() != neighbor) hasCheckedNeighbors = false; break; case Direction::SOUTH: if (s.lock() != neighbor) hasCheckedNeighbors = false; break; case Direction::EAST: if (e.lock() != neighbor) hasCheckedNeighbors = false; break; case Direction::WEST: if (w.lock() != neighbor) hasCheckedNeighbors = false; break; } } } void ChestTileEntity::checkNeighbors() { if (hasCheckedNeighbors) return; hasCheckedNeighbors = true; n = std::weak_ptr(); e = std::weak_ptr(); w = std::weak_ptr(); s = std::weak_ptr(); if (isSameChest(x - 1, y, z)) { w = std::dynamic_pointer_cast( level->getTileEntity(x - 1, y, z)); } if (isSameChest(x + 1, y, z)) { e = std::dynamic_pointer_cast( level->getTileEntity(x + 1, y, z)); } if (isSameChest(x, y, z - 1)) { n = std::dynamic_pointer_cast( level->getTileEntity(x, y, z - 1)); } if (isSameChest(x, y, z + 1)) { s = std::dynamic_pointer_cast( level->getTileEntity(x, y, z + 1)); } std::shared_ptr cteThis = std::dynamic_pointer_cast(shared_from_this()); if (n.lock() != NULL) n.lock()->heyImYourNeighbor(cteThis, Direction::SOUTH); if (s.lock() != NULL) s.lock()->heyImYourNeighbor(cteThis, Direction::NORTH); if (e.lock() != NULL) e.lock()->heyImYourNeighbor(cteThis, Direction::WEST); if (w.lock() != NULL) w.lock()->heyImYourNeighbor(cteThis, Direction::EAST); } bool ChestTileEntity::isSameChest(int x, int y, int z) { Tile* tile = Tile::tiles[level->getTile(x, y, z)]; if (tile == NULL || !(dynamic_cast(tile) != NULL)) return false; return ((ChestTile*)tile)->type == getType(); } void ChestTileEntity::tick() { TileEntity::tick(); checkNeighbors(); ++tickInterval; if (!level->isClientSide && openCount != 0 && (tickInterval + x + y + z) % (SharedConstants::TICKS_PER_SECOND * 10) == 0) { // level.tileEvent(x, y, z, Tile.chest.id, // ChestTile.EVENT_SET_OPEN_COUNT, openCount); openCount = 0; float range = 5; std::vector >* players = level->getEntitiesOfClass( typeid(Player), AABB::newTemp(x - range, y - range, z - range, x + 1 + range, y + 1 + range, z + 1 + range)); for (AUTO_VAR(it, players->begin()); it != players->end(); ++it) { std::shared_ptr player = std::dynamic_pointer_cast(*it); ContainerMenu* containerMenu = dynamic_cast(player->containerMenu); if (containerMenu != NULL) { std::shared_ptr container = containerMenu->getContainer(); std::shared_ptr thisContainer = std::dynamic_pointer_cast(shared_from_this()); std::shared_ptr compoundContainer = std::dynamic_pointer_cast(container); if ((container == thisContainer) || (compoundContainer != NULL && compoundContainer->contains(thisContainer))) { openCount++; } } } delete players; } oOpenness = openness; float speed = 0.10f; if (openCount > 0 && openness == 0) { if (n.lock() == NULL && w.lock() == NULL) { double xc = x + 0.5; double zc = z + 0.5; if (s.lock() != NULL) zc += 0.5; if (e.lock() != NULL) xc += 0.5; // 4J-PB - Seems the chest open volume is much louder than other // sounds from user reports. We'll tone it down a bit level->playSound(xc, y + 0.5, zc, eSoundType_RANDOM_CHEST_OPEN, 0.2f, level->random->nextFloat() * 0.1f + 0.9f); } } if ((openCount == 0 && openness > 0) || (openCount > 0 && openness < 1)) { float oldOpen = openness; if (openCount > 0) openness += speed; else openness -= speed; if (openness > 1) { openness = 1; } float lim = 0.5f; if (openness < lim && oldOpen >= lim) { // Fix for #64546 - Customer Encountered: TU7: Chests placed by the // Player are closing too fast. // openness = 0; if (n.lock() == NULL && w.lock() == NULL) { double xc = x + 0.5; double zc = z + 0.5; if (s.lock() != NULL) zc += 0.5; if (e.lock() != NULL) xc += 0.5; // 4J-PB - Seems the chest open volume is much louder than other // sounds from user reports. We'll tone it down a bit level->playSound(xc, y + 0.5, zc, eSoundType_RANDOM_CHEST_CLOSE, 0.2f, level->random->nextFloat() * 0.1f + 0.9f); } } if (openness < 0) { openness = 0; } } } bool ChestTileEntity::triggerEvent(int b0, int b1) { if (b0 == ChestTile::EVENT_SET_OPEN_COUNT) { openCount = b1; return true; } return TileEntity::triggerEvent(b0, b1); } void ChestTileEntity::startOpen() { if (openCount < 0) { openCount = 0; } openCount++; level->tileEvent(x, y, z, getTile()->id, ChestTile::EVENT_SET_OPEN_COUNT, openCount); level->updateNeighborsAt(x, y, z, getTile()->id); level->updateNeighborsAt(x, y - 1, z, getTile()->id); } void ChestTileEntity::stopOpen() { if (getTile() == NULL || !(dynamic_cast(getTile()) != NULL)) return; openCount--; level->tileEvent(x, y, z, getTile()->id, ChestTile::EVENT_SET_OPEN_COUNT, openCount); level->updateNeighborsAt(x, y, z, getTile()->id); level->updateNeighborsAt(x, y - 1, z, getTile()->id); } bool ChestTileEntity::canPlaceItem(int slot, std::shared_ptr item) { return true; } void ChestTileEntity::setRemoved() { TileEntity::setRemoved(); clearCache(); checkNeighbors(); } int ChestTileEntity::getType() { if (type == -1) { if (level != NULL && dynamic_cast(getTile()) != NULL) { type = ((ChestTile*)getTile())->type; } else { return ChestTile::TYPE_BASIC; } } return type; } // 4J Added std::shared_ptr ChestTileEntity::clone() { std::shared_ptr result = std::shared_ptr(new ChestTileEntity()); TileEntity::clone(result); for (unsigned int i = 0; i < items->length; i++) { if (items->data[i] != NULL) { result->items->data[i] = ItemInstance::clone(items->data[i]); } } return result; }