From 9d5b458479a4eac14acb5af28f51ad23432264ab Mon Sep 17 00:00:00 2001 From: Perttu Ahola Date: Thu, 17 Nov 2011 02:28:46 +0200 Subject: [PATCH] Crafting definition in scripts --- data/mods/default/init.lua | 23 ++++ src/CMakeLists.txt | 1 + src/client.cpp | 5 + src/client.h | 2 + src/craftdef.cpp | 229 +++++++++++++++++++++++++++++++++++++ src/craftdef.h | 111 ++++++++++++++++++ src/gamedef.h | 3 + src/inventory.cpp | 80 +++++++++++++ src/inventory.h | 105 ++++++++++++----- src/scriptapi.cpp | 97 ++++++++++++---- src/server.cpp | 29 +++-- src/server.h | 6 + 12 files changed, 634 insertions(+), 57 deletions(-) create mode 100644 src/craftdef.cpp create mode 100644 src/craftdef.h diff --git a/data/mods/default/init.lua b/data/mods/default/init.lua index c2e1d3300..a0b381fb1 100644 --- a/data/mods/default/init.lua +++ b/data/mods/default/init.lua @@ -321,6 +321,29 @@ minetest.register_node("somenode", { inventory_image = "treeprop.png" }) +minetest.register_node("TNT", { + tile_images = {"tnt_top.png", "tnt_bottom.png", "tnt_side.png", "tnt_side.png", "tnt_side.png", "tnt_side.png"}, + inventory_image = "tnt_side.png" +}) + +minetest.register_craft({ + output = 'ToolItem "STPick" 4', + recipe = { + {'NodeItem "cobble" 1', 'NodeItem "cobble" 1', 'NodeItem "cobble" 1'}, + {'', 'CraftItem "Stick"', ''}, + {'', 'CraftItem "Stick"', ''}, + } +}) + +minetest.register_craft({ + output = 'NodeItem "TNT" 4', + recipe = { + {'NodeItem "wood" 1'}, + {'CraftItem "lump_of_coal" 1'}, + {'NodeItem "wood" 1'} + } +}) + local TNT = { -- Maybe handle gravity and collision this way? dunno physical = true, diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9c6419fcb..9d699e976 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,6 +94,7 @@ configure_file( ) set(common_SRCS + craftdef.cpp nameidmapping.cpp tooldef.cpp nodedef.cpp diff --git a/src/client.cpp b/src/client.cpp index 716dac931..2326ff35d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -2317,6 +2317,11 @@ INodeDefManager* Client::getNodeDefManager() { return m_nodedef; } +ICraftDefManager* Client::getCraftDefManager() +{ + return NULL; + //return m_craftdef; +} ITextureSource* Client::getTextureSource() { return m_tsrc; diff --git a/src/client.h b/src/client.h index cd36c4858..e5368b17a 100644 --- a/src/client.h +++ b/src/client.h @@ -36,6 +36,7 @@ class IGameDef; class IWritableTextureSource; class IWritableToolDefManager; class IWritableNodeDefManager; +//class IWritableCraftDefManager; class ClientNotReadyException : public BaseException { @@ -326,6 +327,7 @@ public: // IGameDef interface virtual IToolDefManager* getToolDefManager(); virtual INodeDefManager* getNodeDefManager(); + virtual ICraftDefManager* getCraftDefManager(); virtual ITextureSource* getTextureSource(); virtual u16 allocateUnknownNodeId(const std::string &name); diff --git a/src/craftdef.cpp b/src/craftdef.cpp new file mode 100644 index 000000000..79761b857 --- /dev/null +++ b/src/craftdef.cpp @@ -0,0 +1,229 @@ +/* +Minetest-c55 +Copyright (C) 2011 celeron55, Perttu Ahola + +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 "craftdef.h" + +#include "irrlichttypes.h" +#include "log.h" +#include +#include "utility.h" +#include "gamedef.h" +#include "inventory.h" + +CraftPointerInput::~CraftPointerInput() +{ + for(u32 i=0; i items; + for(u32 i=0; i items; + for(u32 i=0; iserialize(oss); + items.push_back(oss.str()); + } + } + return CraftInput(cpi.width, items); +} + +std::string CraftInput::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(width="< 3){ + errorstream<<"getCraftResult: IGNORING ERROR: " + <<"input_cpi.width > 3"<= input_cpi.width || y >= input_cpi.height()) + input_items[i] = NULL; + else + input_items[i] = input_cpi.items[y*input_cpi.width+x]; + } + for(core::list::ConstIterator + i = m_craft_definitions.begin(); + i != m_craft_definitions.end(); i++) + { + CraftDefinition *def = *i; + + infostream<<"Checking "<input.dump() + <<" (output=\""<output<<"\")"<input, gamedef); + if(spec_cpi.width > 3){ + errorstream<<"getCraftResult: IGNORING ERROR: " + <<"spec_cpi.width > 3"<= spec_cpi.width || y >= spec_cpi.height()) + spec_items[i] = NULL; + else + spec_items[i] = spec_cpi.items[y*spec_cpi.width+x]; + infostream<<"spec_items["<output, std::ios::binary); + return InventoryItem::deSerialize(iss, gamedef); + } + } + return NULL; + } + virtual void registerCraft(const CraftDefinition &def) + { + infostream<<"registerCraft: registering craft definition: " + < 3 || def.input.height() > 3){ + errorstream<<"registerCraft: input size is larger than 3x3," + <<" ignoring"<::Iterator + i = m_craft_definitions.begin(); + i != m_craft_definitions.end(); i++){ + delete *i; + } + m_craft_definitions.clear(); + } + virtual void serialize(std::ostream &os) + { + writeU8(os, 0); // version + u16 count = m_craft_definitions.size(); + writeU16(os, count); + for(core::list::Iterator + i = m_craft_definitions.begin(); + i != m_craft_definitions.end(); i++){ + CraftDefinition *def = *i; + // Serialize wrapped in a string + std::ostringstream tmp_os(std::ios::binary); + def->serialize(tmp_os); + os< m_craft_definitions; +}; + +IWritableCraftDefManager* createCraftDefManager() +{ + return new CCraftDefManager(); +} + diff --git a/src/craftdef.h b/src/craftdef.h new file mode 100644 index 000000000..cfd58ad10 --- /dev/null +++ b/src/craftdef.h @@ -0,0 +1,111 @@ +/* +Minetest-c55 +Copyright (C) 2011 celeron55, Perttu Ahola + +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. +*/ + +#ifndef CRAFTDEF_HEADER +#define CRAFTDEF_HEADER + +#include +#include +#include +class IGameDef; +class InventoryItem; + +struct CraftPointerInput +{ + unsigned int width; + std::vector items; + + CraftPointerInput(unsigned int width_, const std::vector &items_): + width(width_), + items(items_) + {} + CraftPointerInput(): + width(0) + {} + ~CraftPointerInput(); + unsigned int height() const{ + return (items.size() + width - 1) / width; + } +}; + +struct CraftInput +{ + unsigned int width; + std::vector items; + + CraftInput(unsigned int width_, const std::vector &items_): + width(width_), + items(items_) + {} + CraftInput(): + width(0) + {} + unsigned int height() const{ + return (items.size() + width - 1) / width; + } + std::string dump() const; +}; + +struct CraftDefinition +{ + std::string output; + CraftInput input; + + CraftDefinition(){} + CraftDefinition(const std::string &output_, unsigned int width_, + const std::vector &input_): + output(output_), + input(width_, input_) + {} + + std::string dump() const; + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); +}; + +class ICraftDefManager +{ +public: + ICraftDefManager(){} + virtual ~ICraftDefManager(){} + virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi, + IGameDef *gamedef) const=0; + + virtual void serialize(std::ostream &os)=0; +}; + +class IWritableCraftDefManager : public ICraftDefManager +{ +public: + IWritableCraftDefManager(){} + virtual ~IWritableCraftDefManager(){} + virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi, + IGameDef *gamedef) const=0; + + virtual void registerCraft(const CraftDefinition &def)=0; + virtual void clear()=0; + + virtual void serialize(std::ostream &os)=0; + virtual void deSerialize(std::istream &is)=0; +}; + +IWritableCraftDefManager* createCraftDefManager(); + +#endif + diff --git a/src/gamedef.h b/src/gamedef.h index 6708f887d..bca27a21a 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class IToolDefManager; class INodeDefManager; +class ICraftDefManager; //class IItemDefManager; //TODO // Mineral too? class ITextureSource; @@ -40,6 +41,7 @@ public: // Thus, first they are set up and then they are only read. virtual IToolDefManager* getToolDefManager()=0; virtual INodeDefManager* getNodeDefManager()=0; + virtual ICraftDefManager* getCraftDefManager()=0; //virtual IItemDefManager* getItemDefManager()=0; // This is always thread-safe, but referencing the irrlicht texture @@ -52,6 +54,7 @@ public: // Shorthands IToolDefManager* tdef(){return getToolDefManager();} INodeDefManager* ndef(){return getNodeDefManager();} + ICraftDefManager* cdef(){return getCraftDefManager();} ITextureSource* tsrc(){return getTextureSource();} }; diff --git a/src/inventory.cpp b/src/inventory.cpp index 386868663..c7dd2a87b 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -1153,5 +1153,85 @@ bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *sp 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; yisSubsetOf(item)) + return false; + } + + return true; +} + //END diff --git a/src/inventory.h b/src/inventory.h index 9c6a967a2..d6049f52f 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -70,25 +70,26 @@ public: Quantity methods */ - // Shall return true if the item can be add()ed to the other + // Return true if the item can be add()ed to the other virtual bool addableTo(const InventoryItem *other) const - { - return false; - } + { return false; } + // Return true if the other item contains this item + virtual bool isSubsetOf(const InventoryItem *other) const + { return false; } + // Remove the other item from this one if possible and return true + // Return false if not possible + virtual bool removeOther(const InventoryItem *other) + { return false; } u16 getCount() const - { - return m_count; - } + { return m_count; } void setCount(u16 count) - { - m_count = count; - } + { m_count = count; } + // This should return something else for stackable items virtual u16 freeSpace() const - { - return 0; - } + { return 0; } + void add(u16 count) { assert(m_count + count <= QUANTITY_ITEM_MAX_COUNT); @@ -168,6 +169,24 @@ public: return false; return true; } + virtual bool isSubsetOf(const InventoryItem *other) const + { + if(std::string(other->getName()) != "MaterialItem") + return false; + MaterialItem *m = (MaterialItem*)other; + if(m->m_nodename != m_nodename) + return false; + return m_count <= m->m_count; + } + virtual bool removeOther(const InventoryItem *other) + { + if(!other->isSubsetOf(this)) + return false; + MaterialItem *m = (MaterialItem*)other; + m_count += m->m_count; + return true; + } + u16 freeSpace() const { if(m_count > QUANTITY_ITEM_MAX_COUNT) @@ -245,6 +264,24 @@ public: return false; return true; } + virtual bool isSubsetOf(const InventoryItem *other) const + { + if(std::string(other->getName()) != "CraftItem") + return false; + CraftItem *m = (CraftItem*)other; + if(m->m_subname != m_subname) + return false; + return m_count <= m->m_count; + } + virtual bool removeOther(const InventoryItem *other) + { + if(!other->isSubsetOf(this)) + return false; + CraftItem *m = (CraftItem*)other; + m_count += m->m_count; + return true; + } + u16 freeSpace() const { if(m_count > QUANTITY_ITEM_MAX_COUNT) @@ -312,23 +349,26 @@ public: std::string getText() { return ""; - - /*std::ostringstream os; - u16 f = 4; - u16 d = 65535/f; - u16 i; - for(i=0; i<(65535-m_wear)/d; i++) - os<<'X'; - for(; igetName()) != "ToolItem") + return false; + ToolItem *m = (ToolItem*)other; + if(m->m_toolname != m_toolname) + return false; + return m_wear <= m->m_wear; + } + virtual bool removeOther(const InventoryItem *other) + { + if(!other->isSubsetOf(this)) + return false; + ToolItem *m = (ToolItem*)other; + m_wear -= m->m_wear; + return true; + } + /* Special methods */ @@ -591,5 +631,12 @@ struct ItemSpec */ bool checkItemCombination(const InventoryItem * const*items, const ItemSpec *specs); +/* + items: a pointer to an array of 9 pointers to items + specs: a pointer to an array of 9 pointers to items +*/ +bool checkItemCombination(const InventoryItem * const * items, + const InventoryItem * const * specs); + #endif diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index b37c50bc0..cd501060f 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -37,6 +37,7 @@ extern "C" { #include "content_sao.h" // For LuaEntitySAO #include "tooldef.h" #include "nodedef.h" +#include "craftdef.h" /* TODO: @@ -205,26 +206,6 @@ static int l_register_globalstep(lua_State *L) return 0; /* number of results */ } -#if 0 -// Clear all registered tools -// deregister_tools() -static int l_deregister_tools(lua_State *L) -{ - infostream<<"deregister_tools"<getWritableToolDefManager(); - - tooldef->clear(); - - return 0; /* number of results */ -} -#endif - // register_tool(name, {lots of stuff}) static int l_register_tool(lua_State *L) { @@ -336,16 +317,90 @@ static int l_register_node(lua_State *L) f.tname_inventory = lua_tostring(L, -1); lua_pop(L, 1); + // TODO: Replace with actual parameter reading + // Temporarily set some sane parameters to allow digging + f.material.diggability = DIGGABLE_NORMAL; + f.material.weight = 0; + f.material.crackiness = 0; + f.material.crumbliness = 0; + f.material.cuttability = 0; + nodedef->set(name, f); return 0; /* number of results */ } +// register_craft({output=item, recipe={{item00,item10},{item01,item11}}) +static int l_register_craft(lua_State *L) +{ + infostream<<"register_craft"<getWritableCraftDefManager(); + + std::string output; + int width = 0; + std::vector input; + + lua_getfield(L, table0, "output"); + luaL_checktype(L, -1, LUA_TSTRING); + if(lua_isstring(L, -1)) + output = lua_tostring(L, -1); + lua_pop(L, 1); + + lua_getfield(L, table0, "recipe"); + luaL_checktype(L, -1, LUA_TTABLE); + if(lua_istable(L, -1)){ + int table1 = lua_gettop(L); + lua_pushnil(L); + int rowcount = 0; + while(lua_next(L, table1) != 0){ + int colcount = 0; + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TTABLE); + if(lua_istable(L, -1)){ + int table2 = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table2) != 0){ + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + input.push_back(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + colcount++; + } + } + if(rowcount == 0){ + width = colcount; + } else { + if(colcount != width){ + script_error(L, "error: %s\n", "Invalid crafting recipe"); + } + } + // removes value, keeps key for next iteration + lua_pop(L, 1); + rowcount++; + } + } + lua_pop(L, 1); + + CraftDefinition def(output, width, input); + craftdef->registerCraft(def); + + return 0; /* number of results */ +} + static const struct luaL_Reg minetest_f [] = { {"register_entity", l_register_entity}, {"register_globalstep", l_register_globalstep}, - //{"deregister_tools", l_deregister_tools}, {"register_tool", l_register_tool}, {"register_node", l_register_node}, + {"register_craft", l_register_craft}, {NULL, NULL} }; diff --git a/src/server.cpp b/src/server.cpp index c01b5050d..894c3d362 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -43,6 +43,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scriptapi.h" #include "nodedef.h" #include "tooldef.h" +#include "craftdef.h" #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")" @@ -988,6 +989,7 @@ Server::Server( m_lua(NULL), m_toolmgr(createToolDefManager()), m_nodedef(createNodeDefManager()), + m_craftdef(createCraftDefManager()), m_thread(this), m_emergethread(this), m_time_counter(0), @@ -4332,14 +4334,19 @@ void Server::UpdateCrafting(u16 peer_id) } if(clist && rlist && player->craftresult_is_preview) { - InventoryItem *items[9]; - for(u16 i=0; i<9; i++) - { - items[i] = clist->getItem(i); - } - // Get result of crafting grid - InventoryItem *result = craft_get_result(items, this); + + std::vector items; + for(u16 i=0; i<9; i++){ + if(clist->getItem(i) == NULL) + items.push_back(NULL); + else + items.push_back(clist->getItem(i)->clone()); + } + CraftPointerInput cpi(3, items); + + InventoryItem *result = m_craftdef->getCraftResult(cpi, this); + //InventoryItem *result = craft_get_result(items, this); if(result) rlist->addItem(result); } @@ -4424,6 +4431,10 @@ INodeDefManager* Server::getNodeDefManager() { return m_nodedef; } +ICraftDefManager* Server::getCraftDefManager() +{ + return m_craftdef; +} ITextureSource* Server::getTextureSource() { return NULL; @@ -4441,6 +4452,10 @@ IWritableNodeDefManager* Server::getWritableNodeDefManager() { return m_nodedef; } +IWritableCraftDefManager* Server::getWritableCraftDefManager() +{ + return m_craftdef; +} v3f findSpawnPos(ServerMap &map) { diff --git a/src/server.h b/src/server.h index ff0abccc4..7620f5341 100644 --- a/src/server.h +++ b/src/server.h @@ -35,6 +35,7 @@ struct LuaState; typedef struct lua_State lua_State; class IWritableToolDefManager; class IWritableNodeDefManager; +class IWritableCraftDefManager; /* Some random functions @@ -490,11 +491,13 @@ public: // Under envlock virtual IToolDefManager* getToolDefManager(); virtual INodeDefManager* getNodeDefManager(); + virtual ICraftDefManager* getCraftDefManager(); virtual ITextureSource* getTextureSource(); virtual u16 allocateUnknownNodeId(const std::string &name); IWritableToolDefManager* getWritableToolDefManager(); IWritableNodeDefManager* getWritableNodeDefManager(); + IWritableCraftDefManager* getWritableCraftDefManager(); private: @@ -635,6 +638,9 @@ private: // Node definition manager IWritableNodeDefManager *m_nodedef; + // Craft definition manager + IWritableCraftDefManager *m_craftdef; + /* Threads */