diff options
Diffstat (limited to 'src/inventorymanager.cpp')
-rw-r--r-- | src/inventorymanager.cpp | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp new file mode 100644 index 000000000..195438d26 --- /dev/null +++ b/src/inventorymanager.cpp @@ -0,0 +1,544 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@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 "inventorymanager.h" +#include "serverremoteplayer.h" +#include "log.h" +#include "mapblock.h" // getNodeBlockPos + +/* + InventoryManager +*/ + +// Wrapper for old code +Inventory* InventoryManager::getInventory(InventoryContext *c, std::string id) +{ + if(id == "current_player") + { + assert(c->current_player); + InventoryLocation loc; + loc.setPlayer(c->current_player->getName()); + return getInventory(loc); + } + + Strfnd fn(id); + std::string id0 = fn.next(":"); + + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + + InventoryLocation loc; + loc.setNodeMeta(p); + return getInventory(loc); + } + + errorstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl; + return NULL; +} +// Wrapper for old code +void InventoryManager::inventoryModified(InventoryContext *c, std::string id) +{ + if(id == "current_player") + { + assert(c->current_player); + InventoryLocation loc; + loc.setPlayer(c->current_player->getName()); + setInventoryModified(loc); + return; + } + + Strfnd fn(id); + std::string id0 = fn.next(":"); + + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + v3s16 blockpos = getNodeBlockPos(p); + + InventoryLocation loc; + loc.setNodeMeta(p); + setInventoryModified(loc); + return; + } + + errorstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl; +} + +/* + InventoryAction +*/ + +InventoryAction * InventoryAction::deSerialize(std::istream &is) +{ + std::string type; + std::getline(is, type, ' '); + + InventoryAction *a = NULL; + + if(type == "Move") + { + a = new IMoveAction(is); + } + else if(type == "Drop") + { + a = new IDropAction(is); + } + + return a; +} + +static std::string describeC(const struct InventoryContext *c) +{ + if(c->current_player == NULL) + return "current_player=NULL"; + else + return std::string("current_player=") + c->current_player->getName(); +} + +IMoveAction::IMoveAction(std::istream &is) +{ + std::string ts; + + std::getline(is, ts, ' '); + count = stoi(ts); + + std::getline(is, from_inv, ' '); + + std::getline(is, from_list, ' '); + + std::getline(is, ts, ' '); + from_i = stoi(ts); + + std::getline(is, to_inv, ' '); + + std::getline(is, to_list, ' '); + + std::getline(is, ts, ' '); + to_i = stoi(ts); +} + +void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr, + ServerEnvironment *env) +{ + Inventory *inv_from = mgr->getInventory(c, from_inv); + Inventory *inv_to = mgr->getInventory(c, to_inv); + + if(!inv_from){ + infostream<<"IMoveAction::apply(): FAIL: source inventory not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", to_inv=\""<<to_inv<<"\""<<std::endl; + return; + } + if(!inv_to){ + infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: " + "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", to_inv=\""<<to_inv<<"\""<<std::endl; + return; + } + + InventoryList *list_from = inv_from->getList(from_list); + InventoryList *list_to = inv_to->getList(to_list); + + /* + If a list doesn't exist or the source item doesn't exist + */ + if(!list_from){ + infostream<<"IMoveAction::apply(): FAIL: source list not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\""<<std::endl; + return; + } + if(!list_to){ + infostream<<"IMoveAction::apply(): FAIL: destination list not found: " + <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\"" + <<", to_list=\""<<to_list<<"\""<<std::endl; + return; + } + if(list_from->getItem(from_i) == NULL) + { + infostream<<"IMoveAction::apply(): FAIL: source item not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\"" + <<" from_i="<<from_i<<std::endl; + return; + } + /* + If the source and the destination slots are the same + */ + if(inv_from == inv_to && list_from == list_to && from_i == to_i) + { + infostream<<"IMoveAction::apply(): FAIL: source and destination slots " + <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list + <<"\" i="<<from_i<<std::endl; + return; + } + + // Take item from source list + InventoryItem *item1 = NULL; + if(count == 0) + item1 = list_from->changeItem(from_i, NULL); + else + item1 = list_from->takeItem(from_i, count); + + // Try to add the item to destination list + InventoryItem *olditem = item1; + item1 = list_to->addItem(to_i, item1); + + // If something is returned, the item was not fully added + if(item1 != NULL) + { + // If olditem is returned, nothing was added. + bool nothing_added = (item1 == olditem); + + // If something else is returned, part of the item was left unadded. + // Add the other part back to the source item + list_from->addItem(from_i, item1); + + // If olditem is returned, nothing was added. + // Swap the items + if(nothing_added) + { + // Take item from source list + item1 = list_from->changeItem(from_i, NULL); + // Adding was not possible, swap the items. + InventoryItem *item2 = list_to->changeItem(to_i, item1); + // Put item from destination list to the source list + list_from->changeItem(from_i, item2); + } + } + + mgr->inventoryModified(c, from_inv); + if(from_inv != to_inv) + mgr->inventoryModified(c, to_inv); + + infostream<<"IMoveAction::apply(): moved at " + <<"["<<describeC(c)<<"]" + <<" from inv=\""<<from_inv<<"\"" + <<" list=\""<<from_list<<"\"" + <<" i="<<from_i + <<" to inv=\""<<to_inv<<"\"" + <<" list=\""<<to_list<<"\"" + <<" i="<<to_i + <<std::endl; +} + +IDropAction::IDropAction(std::istream &is) +{ + std::string ts; + + std::getline(is, ts, ' '); + count = stoi(ts); + + std::getline(is, from_inv, ' '); + + std::getline(is, from_list, ' '); + + std::getline(is, ts, ' '); + from_i = stoi(ts); +} + +void IDropAction::apply(InventoryContext *c, InventoryManager *mgr, + ServerEnvironment *env) +{ + if(c->current_player == NULL){ + infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl; + return; + } + + // Do NOT cast directly to ServerActiveObject*, it breaks + // because of multiple inheritance. + ServerActiveObject *dropper = + static_cast<ServerActiveObject*>( + static_cast<ServerRemotePlayer*>( + c->current_player + )); + + Inventory *inv_from = mgr->getInventory(c, from_inv); + + if(!inv_from){ + infostream<<"IDropAction::apply(): FAIL: source inventory not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl; + return; + } + + InventoryList *list_from = inv_from->getList(from_list); + + /* + If a list doesn't exist or the source item doesn't exist + */ + if(!list_from){ + infostream<<"IDropAction::apply(): FAIL: source list not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\""<<std::endl; + return; + } + InventoryItem *item = list_from->getItem(from_i); + if(item == NULL) + { + infostream<<"IDropAction::apply(): FAIL: source item not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\"" + <<" from_i="<<from_i<<std::endl; + return; + } + + v3f pos = dropper->getBasePosition(); + pos.Y += 0.5*BS; + + s16 count2 = count; + if(count2 == 0) + count2 = -1; + + /* + Drop the item + */ + bool remove = item->dropOrPlace(env, dropper, pos, false, count2); + if(remove) + list_from->deleteItem(from_i); + + mgr->inventoryModified(c, from_inv); + + infostream<<"IDropAction::apply(): dropped " + <<"["<<describeC(c)<<"]" + <<" from inv=\""<<from_inv<<"\"" + <<" list=\""<<from_list<<"\"" + <<" i="<<from_i + <<std::endl; +} + +/* + Craft checking system +*/ + +bool ItemSpec::checkItem(const InventoryItem *item) const +{ + if(type == ITEM_NONE) + { + // Has to be no item + if(item != NULL) + return false; + return true; + } + + // There should be an item + if(item == NULL) + return false; + + std::string itemname = item->getName(); + + if(type == ITEM_MATERIAL) + { + if(itemname != "MaterialItem") + return false; + MaterialItem *mitem = (MaterialItem*)item; + if(num != 65535){ + if(mitem->getMaterial() != num) + return false; + } else { + if(mitem->getNodeName() != name) + return false; + } + } + else if(type == ITEM_CRAFT) + { + if(itemname != "CraftItem") + return false; + CraftItem *mitem = (CraftItem*)item; + if(mitem->getSubName() != name) + return false; + } + else if(type == ITEM_TOOL) + { + // Not supported yet + assert(0); + } + else if(type == ITEM_MBO) + { + // Not supported yet + assert(0); + } + else + { + // Not supported yet + assert(0); + } + return true; +} + +bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs) +{ + u16 items_min_x = 100; + u16 items_max_x = 100; + u16 items_min_y = 100; + u16 items_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(items[y*3 + x] == NULL) + continue; + if(items_min_x == 100 || x < items_min_x) + items_min_x = x; + if(items_min_y == 100 || y < items_min_y) + items_min_y = y; + if(items_max_x == 100 || x > items_max_x) + items_max_x = x; + if(items_max_y == 100 || y > items_max_y) + items_max_y = y; + } + // No items at all, just return false + if(items_min_x == 100) + return false; + + u16 items_w = items_max_x - items_min_x + 1; + u16 items_h = items_max_y - items_min_y + 1; + + u16 specs_min_x = 100; + u16 specs_max_x = 100; + u16 specs_min_y = 100; + u16 specs_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(specs[y*3 + x].type == ITEM_NONE) + continue; + if(specs_min_x == 100 || x < specs_min_x) + specs_min_x = x; + if(specs_min_y == 100 || y < specs_min_y) + specs_min_y = y; + if(specs_max_x == 100 || x > specs_max_x) + specs_max_x = x; + if(specs_max_y == 100 || y > specs_max_y) + specs_max_y = y; + } + // No specs at all, just return false + if(specs_min_x == 100) + return false; + + u16 specs_w = specs_max_x - specs_min_x + 1; + u16 specs_h = specs_max_y - specs_min_y + 1; + + // Different sizes + if(items_w != specs_w || items_h != specs_h) + return false; + + for(u16 y=0; y<specs_h; y++) + for(u16 x=0; x<specs_w; x++) + { + u16 items_x = items_min_x + x; + u16 items_y = items_min_y + y; + u16 specs_x = specs_min_x + x; + u16 specs_y = specs_min_y + y; + const InventoryItem *item = items[items_y * 3 + items_x]; + const ItemSpec &spec = specs[specs_y * 3 + specs_x]; + + if(spec.checkItem(item) == false) + return false; + } + + return true; +} + +bool checkItemCombination(const InventoryItem * const * items, + const InventoryItem * const * specs) +{ + u16 items_min_x = 100; + u16 items_max_x = 100; + u16 items_min_y = 100; + u16 items_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(items[y*3 + x] == NULL) + continue; + if(items_min_x == 100 || x < items_min_x) + items_min_x = x; + if(items_min_y == 100 || y < items_min_y) + items_min_y = y; + if(items_max_x == 100 || x > items_max_x) + items_max_x = x; + if(items_max_y == 100 || y > items_max_y) + items_max_y = y; + } + // No items at all, just return false + if(items_min_x == 100) + return false; + + u16 items_w = items_max_x - items_min_x + 1; + u16 items_h = items_max_y - items_min_y + 1; + + u16 specs_min_x = 100; + u16 specs_max_x = 100; + u16 specs_min_y = 100; + u16 specs_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(specs[y*3 + x] == NULL) + continue; + if(specs_min_x == 100 || x < specs_min_x) + specs_min_x = x; + if(specs_min_y == 100 || y < specs_min_y) + specs_min_y = y; + if(specs_max_x == 100 || x > specs_max_x) + specs_max_x = x; + if(specs_max_y == 100 || y > specs_max_y) + specs_max_y = y; + } + // No specs at all, just return false + if(specs_min_x == 100) + return false; + + u16 specs_w = specs_max_x - specs_min_x + 1; + u16 specs_h = specs_max_y - specs_min_y + 1; + + // Different sizes + if(items_w != specs_w || items_h != specs_h) + return false; + + for(u16 y=0; y<specs_h; y++) + for(u16 x=0; x<specs_w; x++) + { + u16 items_x = items_min_x + x; + u16 items_y = items_min_y + y; + u16 specs_x = specs_min_x + x; + u16 specs_y = specs_min_y + y; + const InventoryItem *item = items[items_y * 3 + items_x]; + const InventoryItem *spec = specs[specs_y * 3 + specs_x]; + + if(item == NULL && spec == NULL) + continue; + if(item == NULL && spec != NULL) + return false; + if(item != NULL && spec == NULL) + return false; + if(!spec->isSubsetOf(item)) + return false; + } + + return true; +} + + |