Files
minecraft-pe-0.6.1/src/world/inventory/FillingContainer.cpp
2026-03-26 23:32:09 +03:00

630 lines
16 KiB
C++
Executable File

#include "FillingContainer.h"
#include "../level/tile/TreeTile.h"
#include "../item/crafting/Recipe.h"
#include "../../util/Mth.h"
#include "../../nbt/CompoundTag.h"
#include "../level/tile/StoneSlabTile.h"
#define MAGIX_VAL 255
#define MAX_SLOTS 96
FillingContainer::FillingContainer(
int numTotalSlots,
int numLinkedSlots,
int containerType,
bool isCreative)
: super(containerType),
numTotalSlots(numTotalSlots),
numLinkedSlots(numLinkedSlots),
linkedSlots(NULL),
_isCreative(isCreative)
{
if (numLinkedSlots > 0)
linkedSlots = new LinkedSlot[numLinkedSlots];
items.resize(numTotalSlots);
}
FillingContainer::~FillingContainer()
{
clearInventory();
if (numLinkedSlots > 0)
delete[] linkedSlots;
}
void FillingContainer::clearInventory()
{
for (int i = 0; i < numLinkedSlots; ++i) {
linkedSlots[i] = LinkedSlot(i);
}
//@todo: i = MAX_ -> get() transforms count=255-ptrs to real
for (unsigned int i = numLinkedSlots; i < items.size(); i++) {
release(i);
}
items.resize(numTotalSlots);
}
bool FillingContainer::removeResource( int type )
{
if (_isCreative) return true;
int slot = getSlot(type);
if (slot < 0) return false;
if (--items[slot]->count <= 0) release(slot);
return true;
}
bool FillingContainer::removeResource( const ItemInstance& item ) {
return removeResource(item, false) == 0;
}
int FillingContainer::removeResource( const ItemInstance& item, bool requireExactAux )
{
if (_isCreative) return 0;
int count = item.count;
while (count > 0) {
// If any AUX value, remove any with that id
int slot = -1;
if (!requireExactAux && (Recipe::isAnyAuxValue(&item) || item.getAuxValue() == Recipe::ANY_AUX_VALUE))
slot = getNonEmptySlot(item.id);
else
slot = getNonEmptySlot(item.id, item.getAuxValue());
if (slot < 0)
return count;
// Try to remove all items, but if it's not enough,
// we continue on another slot next time
ItemInstance* slotItem = items[slot];
int toRemove = Mth::Min(count, slotItem->count);
slotItem->count -= toRemove;
count -= toRemove;
if (slotItem->count <= 0)
clearSlot(slot);
//removeItem(slot, item.count);
}
return 0;
}
bool FillingContainer::hasResource( int type ) const
{
if (_isCreative) return true;
int slot = getSlot(type);
if (slot < 0) return false;
return true;
}
void FillingContainer::swapSlots( int from, int to )
{
ItemInstance* tmp = items[to];
items[to] = items[from];
items[from] = tmp;
}
bool FillingContainer::add( ItemInstance* item )
{
if (_isCreative) return true;
if (!item || item->isNull()) return true;
if (!item->isDamaged()) {
int lastSize;
do {
lastSize = item->count;
item->count = addResource(*item);
} while (item->count > 0 && item->count < lastSize);
//if (item->count == lastSize && player->abilities.instabuild) {
// // silently destroy the item when having a full inventory
// item->count = 0;
// return true;
//}
return item->count < lastSize;
}
int slot = getFreeSlot();
if (slot >= 0) {
items[slot] = ItemInstance::clone(item);
// items[slot] = item;
//items[slot]->popTime = FillingContainer::POP_TIME_DURATION;
linkEmptySlot(slot);
item->count = 0;
return true;
//} else if (player->abilities.instabuild) {
// // silently destroy the item when having a full inventory
// item->count = 0;
// return true;
LOGI("Inventory:\n");
for (int i = 0; i < numTotalSlots; i++) {
LOGI("\t %i: %s (%i)\n", i, items.at(i)->getName().c_str(), items.at(i)->count);
}
}
return false;
}
ItemInstance FillingContainer::removeItem( int slot, int count )
{
ItemInstance* item = getItem(slot);
if (item) {
if (count > item->count)
count = item->count;
item->count -= count;
if (item->count <= 0)
clearSlot(slot);
}
/*
ItemList& pile = *getSlotList(slot);
if (pile[slot] != NULL) {
if (pile[slot]->count <= count) {
ItemInstance item = *pile[slot];
release(slot);
return item;
} else {
ItemInstance i = pile[slot]->remove(count);
if (pile[slot]->count == 0) release(slot);
return i;
}
}
*/
return ItemInstance();
}
void FillingContainer::setItem( int slot, ItemInstance* item )
{
if (slot < 0 || slot >= numTotalSlots)
return;
if (ItemList* p = getSlotList(slot)) {
ItemList& pile = *p;
if (pile[slot]) *pile[slot] = item? *item : ItemInstance();
else pile[slot] = new ItemInstance(item? *item : ItemInstance());
}
}
ListTag* FillingContainer::save( ListTag* listTag )
{
//LOGI("Save.inv-creative? %d. Size %d\n", _isCreative, items.size());
if (!_isCreative) {
ItemInstance selTmp;
for (int i = 0; i < (int)items.size(); i++) {
ItemInstance* item = items[i];
if (i < numLinkedSlots) {
int slot = linkedSlots[i].inventorySlot;
selTmp = ItemInstance(MAGIX_VAL, MAGIX_VAL, slot);
item = &selTmp;
}
if (item != NULL) {
CompoundTag* tag = new CompoundTag();
tag->putByte("Slot", (char) i);
if (item->count < 0) item->count = 0;
if (item->count > 255) item->count = 255;
ItemInstance iitem(*item);
iitem.save(tag);
//LOGI("Saving %d as : %d@%d:%d\n", i, item->id, item->getAuxValue(), item->count);
listTag->add(tag);
}
}
}
return listTag;
}
void FillingContainer::load( ListTag* inventoryList )
{
//LOGI("Load.inv-creative? %d. Size %d\n", _isCreative, items.size());
if (_isCreative)
return;
clearInventory();
//items = /*new*/ ItemInstance[INVENTORY_SIZE];
for (int i = inventoryList->size()-1; i >= 0; --i) {
Tag* t = inventoryList->get(i);
if (t->getId() != Tag::TAG_Compound) continue;
CompoundTag* tag = (CompoundTag*) t;
int slot = tag->getByte("Slot") & 0xff;
if (slot < 0) continue;
ItemInstance* item = ItemInstance::fromTag(tag);
if (item != NULL) {
//LOGI("Loading %d as : %d@%d:%d\n", slot, item->id, item->getAuxValue(), item->count);
// Special case! (don't we love them!). item->count == 255 means
// we POINT to our real inventory item (from a selection slot).
// This is ONLY AND ALWAYS used in Selection Slots at this moment.
if (slot < numLinkedSlots) {
if (slot < (int)items.size() && item->id == MAGIX_VAL && item->count == MAGIX_VAL) {
int invSlot = item->getAuxValue();
if (invSlot >= numLinkedSlots && invSlot < (int)items.size())
linkSlot(slot, invSlot, false);
} else { // Something's wrong, shouldnt go here
LOGE("Error: Slot %d is selection slot (inventory size: %d) but id/count is %d/%d\n", slot, (int)items.size(), item->id, item->count);
}
delete item;
continue;
}
//item->count = 3;
if (slot < MAX_SLOTS && slot >= numLinkedSlots)
{
// Zero count (valid before, but not anymore), destroy
if (item->count == 0) {
delete item;
continue;
}
// Suggesting the container is larger than it is, drop the item!
if (slot >= (int)items.size()) {
doDrop(item, true);
continue;
}
fixBackwardCompabilityItem(*item);
items[slot] = item;
}
//else if (slot >= 100 && slot < armor.length + 100) armor[slot - 100] = item;
else {
if (slot >= MAX_SLOTS) {
LOGE("Error: Too large slot ID in Inventory: %d!\n", slot);
}
delete item;
}
}
}
compressLinkedSlotList(0);
}
int FillingContainer::getContainerSize() const
{
int sz = items.size();
if (sz != numTotalSlots) {
LOGE("Error@getContainerSize: num items != InventorySize: %d != %d\n", sz, numTotalSlots);
}
return numTotalSlots;
}
ItemInstance* FillingContainer::getItem( int slot )
{
if (slot < 0 || slot >= numTotalSlots)
return NULL;
// Real inventory slot
if (slot >= numLinkedSlots)
return (*getSlotList(slot))[slot];
return getLinked(slot); //@note
}
std::string FillingContainer::getName() const
{
return "Inventory";
}
int FillingContainer::getMaxStackSize() const
{
return MAX_INVENTORY_STACK_SIZE;
}
void FillingContainer::dropSlot( int slot, bool onlyClearContainer, bool randomly/*=false*/ )
{
if (slot >= 0 && slot < numLinkedSlots)
slot = linkedSlots[slot].inventorySlot;
if (slot < 0 || slot >= (int)items.size()) return;
if (items[slot] && items[slot]->count) {
if (!onlyClearContainer)
doDrop(items[slot]->copy(), randomly);
items[slot]->count = 0;
release(slot); //@itodo:
compressLinkedSlotList(slot);
}
}
void FillingContainer::dropAll(bool onlyClearContainer)
{
for (unsigned int i = numLinkedSlots; i < items.size(); i++) {
dropSlot(i, onlyClearContainer, true);
}
}
void FillingContainer::setContainerChanged()
{
}
bool FillingContainer::stillValid( Player* player )
{
return !player->removed;
}
bool FillingContainer::contains( ItemInstance* itemInstance ) const
{
for (unsigned int i = 0; i < items.size(); i++) {
if (items[i] != NULL && items[i]->matches(itemInstance)) return true;
}
return false;
}
int FillingContainer::getSlot( int tileId) const
{
for (unsigned int i = numLinkedSlots; i < items.size(); i++) {
if (items[i] != NULL && items[i]->id == tileId) return i;
}
return -1;
}
int FillingContainer::getSlot( int tileId, int data) const
{
for (unsigned int i = numLinkedSlots; i < items.size(); i++) {
if (items[i] != NULL
&& items[i]->id == tileId
&& items[i]->getAuxValue() == data)
return i;
}
return -1;
}
int FillingContainer::getNonEmptySlot( int tileId) const
{
for (unsigned int i = numLinkedSlots; i < items.size(); i++) {
if (items[i] != NULL && items[i]->id == tileId && items[i]->count > 0) return i;
}
return -1;
}
int FillingContainer::getNonEmptySlot( int tileId, int data) const
{
for (unsigned int i = numLinkedSlots; i < items.size(); i++) {
if (items[i] != NULL
&& items[i]->id == tileId
&& items[i]->getAuxValue() == data
&& items[i]->count > 0)
return i;
}
return -1;
}
int FillingContainer::getNumEmptySlots() {
int numEmpty = 0;
for (unsigned int i = numLinkedSlots; i < items.size(); ++i) {
if (!items[i] || items[i]->isNull())
++numEmpty;
}
return numEmpty;
}
int FillingContainer::getNumLinkedSlots() {
return numLinkedSlots;
}
int FillingContainer::getSlotWithRemainingSpace( const ItemInstance& item )
{
for (unsigned int i = 0; i < items.size(); i++) {
if (items[i] != NULL && items[i]->id == item.id
&& items[i]->isStackable()
&& items[i]->count < items[i]->getMaxStackSize()
&& items[i]->count < getMaxStackSize()
&& (!items[i]->isStackedByData() || items[i]->getAuxValue() == item.getAuxValue())) {
return i;
}
}
return -1;
}
int FillingContainer::addResource( const ItemInstance& itemInstance )
{
int type = itemInstance.id;
int count = itemInstance.count;
if (itemInstance.getMaxStackSize() == 1) {
int slot = getFreeSlot();
if (slot < 0) return count;
if (items[slot] == NULL) {
items[slot] = ItemInstance::clone(&itemInstance);
linkEmptySlot(slot);
} else if (items[slot]->isNull()) {
*items[slot] = itemInstance;
linkEmptySlot(slot);
}
return 0;
}
int slot = getSlotWithRemainingSpace(itemInstance);
if (slot < 0) slot = getFreeSlot();
if (slot < 0) return count;
if (items[slot] == NULL) {
items[slot] = new ItemInstance(type, 0, itemInstance.getAuxValue());
} else if (items[slot]->isNull()) { //@attn: added when doing chests
*items[slot] = ItemInstance(type, 0, itemInstance.getAuxValue());
}
// Add the newly added item to a selections lot, if one's free
linkEmptySlot(slot);
int toAdd = count;
if (toAdd > items[slot]->getMaxStackSize() - items[slot]->count) {
toAdd = items[slot]->getMaxStackSize() - items[slot]->count;
}
if (toAdd > getMaxStackSize() - items[slot]->count) {
toAdd = getMaxStackSize() - items[slot]->count;
}
if (toAdd == 0) return count;
count -= toAdd;
items[slot]->count += toAdd;
//items[slot]->popTime = POP_TIME_DURATION;
return count;
}
int FillingContainer::getFreeSlot() const
{
for (unsigned int i = numLinkedSlots; i < items.size(); i++) {
if (items[i] == NULL || items[i]->isNull()) return i;
}
return -1;
}
void FillingContainer::release( int slot )
{
if (items[slot]) {
delete items[slot];// @itodo
items[slot] = NULL;
}
}
void FillingContainer::clearSlot( int slot )
{
if (slot < 0)
return;
if (slot < numLinkedSlots) {
release(linkedSlots[slot].inventorySlot);
linkedSlots[slot].inventorySlot = -1;
}
else {
release(slot);
}
compressLinkedSlotList(slot);
}
int FillingContainer::addItem(ItemInstance* item) {
for (unsigned int i = numLinkedSlots; i < items.size(); ++i)
if (!items[i]) {
items[i] = item;
return i;
}
int newSize = items.size() + 1;
if (_isCreative && newSize > numTotalSlots)
numTotalSlots = newSize;
if (newSize <= numTotalSlots) {
items.push_back(item);
return newSize-1;
}
LOGE("Error@addItem: adding an item to an already full inventory: %s!\n", item->getDescriptionId().c_str());
delete item;
return 0;
}
void FillingContainer::fixBackwardCompabilityItem( ItemInstance& item )
{
if (item.id == Tile::stoneSlabHalf->id)
item.setAuxValue(item.getAuxValue() & StoneSlabTile::TYPE_MASK);
}
void FillingContainer::replace( std::vector<ItemInstance> newItems, int maxCount /* = -1 */)
{
clearInventory();
maxCount = maxCount < 0 ? newItems.size() : Mth::Min(newItems.size(), maxCount);
int base = numLinkedSlots;
const int iMax = Mth::Min(getContainerSize() - base, maxCount);
for (int i = 0; i < iMax; ++i) {
replaceSlot(base + i, newItems[i].isNull()? NULL : &newItems[i]);
//LOGI("Adding to slot: %d - %s :: %s\n", newItems[i], )
}
}
void FillingContainer::replaceSlot( int slotId, ItemInstance* ins )
{
if (ins) {
if (!items[slotId])
items[slotId] = new ItemInstance();
*items[slotId] = *ins;
} else {
release(slotId);
}
}
FillingContainer::ItemList* FillingContainer::getSlotList( int& slot )
{
ItemList* pile = &items;
//if (slot >= pile->size()) {
// slot -= pile->size();
// pile = armor;
//}
return pile;
}
// Special for this "selection based" inventory
bool FillingContainer::linkSlot(int selectionSlot, int inventorySlot, bool propagate) {
if (selectionSlot < 0 || selectionSlot >= numLinkedSlots)
return false;
if (inventorySlot < numLinkedSlots || inventorySlot >= numTotalSlots)
return false;
if (inventorySlot == linkedSlots[selectionSlot].inventorySlot)
return false;
if (propagate) {
int i = 0;
for (; i < numLinkedSlots - 1; ++i)
if (linkedSlots[i].inventorySlot == inventorySlot) break;
for (; i > selectionSlot; --i) {
linkedSlots[i].inventorySlot = linkedSlots[i-1].inventorySlot;
}
}
linkedSlots[selectionSlot].inventorySlot = inventorySlot;
return true;
}
bool FillingContainer::linkEmptySlot( int inventorySlot )
{
// Check if we already got the inventory slot
for (int i = 0; i < numLinkedSlots; ++i)
if (linkedSlots[i].inventorySlot == inventorySlot) return true;
// Check if there's an empty slot to place the new resource in
for (int i = 0; i < numLinkedSlots; ++i) {
ItemInstance* item = getLinked(i);
if (!item) {
linkedSlots[i].inventorySlot = inventorySlot;
return true;
}
}
return false;
}
void FillingContainer::compressLinkedSlotList(int slot)
{
int i = slot-1, j = 0;
while (++i < numLinkedSlots) {
linkedSlots[i-j] = linkedSlots[i];
ItemInstance* item = getLinked(i);
if (!item) ++j;
}
for (int k = i-j; k < i; ++k)
linkedSlots[k].inventorySlot = -1;
}
void FillingContainer::doDrop( ItemInstance* item, bool randomly )
{
delete item;
}
ItemInstance* FillingContainer::getLinked( int slot )
{
// sanity checking to prevent exploits
if (slot < numLinkedSlots && slot >= 0) {
int i = linkedSlots[slot].inventorySlot;
return (i >= numLinkedSlots && i < numTotalSlots)? items[i] : NULL;
}
return NULL;
}