From 6a76c226e10e92c3e3339096f07f8ab065e2098b Mon Sep 17 00:00:00 2001 From: Kahrl Date: Thu, 12 Jan 2012 06:10:39 +0100 Subject: The huge item definition and item namespace unification patch (itemdef), see http://c55.me/minetest/wiki/doku.php?id=changes:itemdef --- src/CMakeLists.txt | 3 +- src/camera.cpp | 211 +--- src/camera.h | 54 +- src/client.cpp | 261 ++-- src/client.h | 29 +- src/clientserver.h | 15 +- src/connection.cpp | 6 +- src/content_cao.cpp | 107 +- src/content_mapblock.cpp | 284 ++--- src/content_mapnode.cpp | 831 ++----------- src/content_mapnode.h | 6 - src/content_nodemeta.cpp | 287 +++-- src/content_sao.cpp | 154 ++- src/content_sao.h | 8 +- src/craftdef.cpp | 844 +++++++++++-- src/craftdef.h | 349 +++++- src/craftitemdef.cpp | 214 ---- src/craftitemdef.h | 79 -- src/environment.h | 1 + src/game.cpp | 134 +-- src/gamedef.h | 10 +- src/guiInventoryMenu.cpp | 149 ++- src/guiInventoryMenu.h | 37 +- src/inventory.cpp | 967 +++++++-------- src/inventory.h | 517 +++----- src/inventorymanager.cpp | 452 ++----- src/inventorymanager.h | 121 +- src/irrlichttypes.h | 2 + src/itemdef.cpp | 502 ++++++++ src/itemdef.h | 147 +++ src/mapblock.cpp | 3 + src/mapblock.h | 3 - src/mapblock_mesh.cpp | 76 +- src/mapblock_mesh.h | 8 + src/materials.cpp | 51 +- src/materials.h | 24 +- src/mesh.cpp | 117 ++ src/mesh.h | 22 + src/mineral.cpp | 11 + src/mineral.h | 13 +- src/nodedef.cpp | 199 +-- src/nodedef.h | 49 +- src/player.cpp | 10 +- src/player.h | 14 +- src/scriptapi.cpp | 2859 ++++++++++++++++++++++---------------------- src/scriptapi.h | 21 +- src/server.cpp | 976 ++++++--------- src/server.h | 28 +- src/serverobject.cpp | 29 +- src/serverobject.h | 27 +- src/serverremoteplayer.cpp | 155 +-- src/serverremoteplayer.h | 20 +- src/test.cpp | 198 ++- src/tile.cpp | 190 +-- src/tile.h | 4 + src/tooldef.cpp | 238 ---- src/tooldef.h | 103 -- src/utility.cpp | 95 ++ src/utility.h | 6 + 59 files changed, 5888 insertions(+), 6442 deletions(-) delete mode 100644 src/craftitemdef.cpp delete mode 100644 src/craftitemdef.h create mode 100644 src/itemdef.cpp create mode 100644 src/itemdef.h delete mode 100644 src/tooldef.cpp delete mode 100644 src/tooldef.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3b3b326c4..e82e26dc7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -100,9 +100,8 @@ set(common_SRCS content_abm.cpp craftdef.cpp nameidmapping.cpp - tooldef.cpp + itemdef.cpp nodedef.cpp - craftitemdef.cpp luaentity_common.cpp scriptapi.cpp script.cpp diff --git a/src/camera.cpp b/src/camera.cpp index c0e171468..066208569 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tile.h" #include #include "settings.h" -#include "nodedef.h" // For wield visualization +#include "itemdef.h" // For wield visualization Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): m_smgr(smgr), @@ -37,6 +37,7 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): m_wieldmgr(NULL), m_wieldnode(NULL), + m_wieldlight(0), m_draw_control(draw_control), m_viewing_range_min(5.0), @@ -77,15 +78,15 @@ Camera::Camera(scene::ISceneManager* smgr, MapDrawControl& draw_control): // all other 3D scene nodes and before the GUI. m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr->addCameraSceneNode(); - m_wieldnode = new ExtrudedSpriteSceneNode(m_wieldmgr->getRootSceneNode(), m_wieldmgr); + m_wieldnode = m_wieldmgr->addMeshSceneNode(createCubeMesh(v3f(1,1,1)), NULL); // need a dummy mesh updateSettings(); } Camera::~Camera() { + m_wieldnode->setMesh(NULL); m_wieldmgr->drop(); - m_wieldnode->drop(); } bool Camera::successfullyCreated(std::wstring& error_message) @@ -292,7 +293,7 @@ void Camera::update(LocalPlayer* player, f32 frametime, v2u32 screensize) } m_wieldnode->setPosition(wield_position); m_wieldnode->setRotation(wield_rotation); - m_wieldnode->updateLight(player->light); + m_wieldlight = player->light; // Render distance feedback loop updateViewingRange(frametime); @@ -449,62 +450,38 @@ void Camera::updateSettings() m_wanted_frametime = 1.0 / wanted_fps; } -void Camera::wield(const InventoryItem* item, IGameDef *gamedef) +void Camera::setDigging(s32 button) { - //ITextureSource *tsrc = gamedef->tsrc(); - INodeDefManager *ndef = gamedef->ndef(); + if (m_digging_button == -1) + m_digging_button = button; +} - if (item != NULL) +void Camera::wield(const ItemStack &item, IGameDef *gamedef) +{ + IItemDefManager *idef = gamedef->idef(); + scene::IMesh *wield_mesh = item.getDefinition(idef).wield_mesh; + if(wield_mesh) { - bool isCube = false; - - // Try to make a MaterialItem cube. - if (std::string(item->getName()) == "MaterialItem") - { - // A block-type material - MaterialItem* mat_item = (MaterialItem*) item; - content_t content = mat_item->getMaterial(); - switch(ndef->get(content).drawtype){ - case NDT_NORMAL: - case NDT_LIQUID: - case NDT_FLOWINGLIQUID: - case NDT_GLASSLIKE: - case NDT_ALLFACES: - case NDT_ALLFACES_OPTIONAL: - m_wieldnode->setCube(ndef->get(content).tiles); - isCube = true; - break; - default: - break; - } - } - - // If that failed, make an extruded sprite. - if (!isCube) - { - m_wieldnode->setSprite(item->getImageRaw()); - } - + m_wieldnode->setMesh(wield_mesh); m_wieldnode->setVisible(true); } else { - // Bare hands - m_wieldnode->setSprite(gamedef->tsrc()->getTextureRaw("wieldhand.png")); - m_wieldnode->setVisible(true); + m_wieldnode->setVisible(false); } } -void Camera::setDigging(s32 button) -{ - if (m_digging_button == -1) - m_digging_button = button; -} - void Camera::drawWieldedTool() { + // Set vertex colors of wield mesh according to light level + u8 li = decode_light(m_wieldlight); + video::SColor color(255,li,li,li); + setMeshColor(m_wieldnode->getMesh(), color); + + // Clear Z buffer m_wieldmgr->getVideoDriver()->clearZBuffer(); + // Draw the wielded node (in a separate scene manager) scene::ICameraSceneNode* cam = m_wieldmgr->getActiveCamera(); cam->setAspectRatio(m_cameranode->getAspectRatio()); cam->setFOV(m_cameranode->getFOV()); @@ -512,145 +489,3 @@ void Camera::drawWieldedTool() cam->setFarValue(100); m_wieldmgr->drawAll(); } - - -ExtrudedSpriteSceneNode::ExtrudedSpriteSceneNode( - scene::ISceneNode* parent, - scene::ISceneManager* mgr, - s32 id, - const v3f& position, - const v3f& rotation, - const v3f& scale -): - ISceneNode(parent, mgr, id, position, rotation, scale) -{ - m_meshnode = mgr->addMeshSceneNode(NULL, this, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); - m_cubemesh = NULL; - m_is_cube = false; - m_light = LIGHT_MAX; -} - -ExtrudedSpriteSceneNode::~ExtrudedSpriteSceneNode() -{ - removeChild(m_meshnode); - if (m_cubemesh) - m_cubemesh->drop(); -} - -void ExtrudedSpriteSceneNode::setSprite(video::ITexture* texture) -{ - const v3f sprite_scale(40.0, 40.0, 4.0); // width, height, thickness - - if (texture == NULL) - { - m_meshnode->setVisible(false); - return; - } - - io::path name = getExtrudedName(texture); - scene::IMeshCache* cache = SceneManager->getMeshCache(); - scene::IAnimatedMesh* mesh = cache->getMeshByName(name); - if (mesh != NULL) - { - // Extruded texture has been found in cache. - m_meshnode->setMesh(mesh); - } - else - { - // Texture was not yet extruded, do it now and save in cache - mesh = createExtrudedMesh(texture, - SceneManager->getVideoDriver(), - sprite_scale); - if (mesh == NULL) - { - dstream << "Warning: failed to extrude sprite" << std::endl; - m_meshnode->setVisible(false); - return; - } - cache->addMesh(name, mesh); - m_meshnode->setMesh(mesh); - mesh->drop(); - } - - m_meshnode->getMaterial(0).setTexture(0, texture); - m_meshnode->getMaterial(0).setFlag(video::EMF_LIGHTING, false); - m_meshnode->getMaterial(0).setFlag(video::EMF_BILINEAR_FILTER, false); - m_meshnode->getMaterial(0).MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - m_meshnode->setVisible(true); - m_is_cube = false; - updateLight(m_light); -} - -void ExtrudedSpriteSceneNode::setCube(const TileSpec tiles[6]) -{ - const v3f cube_scale(30.0, 30.0, 30.0); - - if (m_cubemesh == NULL) - { - m_cubemesh = createCubeMesh(cube_scale); - } - - m_meshnode->setMesh(m_cubemesh); - for (int i = 0; i < 6; ++i) - { - // Get the tile texture and atlas transformation - video::ITexture* atlas = tiles[i].texture.atlas; - v2f pos = tiles[i].texture.pos; - v2f size = tiles[i].texture.size; - - // Set material flags and texture - video::SMaterial& material = m_meshnode->getMaterial(i); - material.setFlag(video::EMF_LIGHTING, false); - material.setFlag(video::EMF_BILINEAR_FILTER, false); - tiles[i].applyMaterialOptions(material); - material.setTexture(0, atlas); - material.getTextureMatrix(0).setTextureTranslate(pos.X, pos.Y); - material.getTextureMatrix(0).setTextureScale(size.X, size.Y); - } - m_meshnode->setVisible(true); - m_is_cube = true; - updateLight(m_light); -} - -void ExtrudedSpriteSceneNode::updateLight(u8 light) -{ - m_light = light; - - u8 li = decode_light(light); - // Set brightness one lower than incoming light - diminish_light(li); - video::SColor color(255,li,li,li); - setMeshColor(m_meshnode->getMesh(), color); -} - -void ExtrudedSpriteSceneNode::removeSpriteFromCache(video::ITexture* texture) -{ - scene::IMeshCache* cache = SceneManager->getMeshCache(); - scene::IAnimatedMesh* mesh = cache->getMeshByName(getExtrudedName(texture)); - if (mesh != NULL) - cache->removeMesh(mesh); -} - -const core::aabbox3d& ExtrudedSpriteSceneNode::getBoundingBox() const -{ - return m_meshnode->getBoundingBox(); -} - -void ExtrudedSpriteSceneNode::OnRegisterSceneNode() -{ - if (IsVisible) - SceneManager->registerNodeForRendering(this); - ISceneNode::OnRegisterSceneNode(); -} - -void ExtrudedSpriteSceneNode::render() -{ - // do nothing -} - -io::path ExtrudedSpriteSceneNode::getExtrudedName(video::ITexture* texture) -{ - io::path path = texture->getName(); - path.append("/[extruded]"); - return path; -} diff --git a/src/camera.h b/src/camera.h index d5789d807..56c99d101 100644 --- a/src/camera.h +++ b/src/camera.h @@ -26,12 +26,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tile.h" #include "utility.h" #include -#include -#include class LocalPlayer; struct MapDrawControl; -class ExtrudedSpriteSceneNode; class IGameDef; /* @@ -116,13 +113,13 @@ public: // Update settings from g_settings void updateSettings(); - // Replace the wielded item mesh - void wield(const InventoryItem* item, IGameDef *gamedef); - // Start digging animation // Pass 0 for left click, 1 for right click void setDigging(s32 button); + // Replace the wielded item mesh + void wield(const ItemStack &item, IGameDef *gamedef); + // Draw the wielded tool. // This has to happen *after* the main scene is drawn. // Warning: This clears the Z buffer. @@ -136,7 +133,8 @@ private: scene::ICameraSceneNode* m_cameranode; scene::ISceneManager* m_wieldmgr; - ExtrudedSpriteSceneNode* m_wieldnode; + scene::IMeshSceneNode* m_wieldnode; + u8 m_wieldlight; // draw control MapDrawControl& m_draw_control; @@ -182,46 +180,4 @@ private: s32 m_digging_button; }; - -/* - A scene node that displays a 2D mesh extruded into the third dimension, - to add an illusion of depth. - - Since this class was created to display the wielded tool of the local - player, and only tools and items are rendered like this (but not solid - content like stone and mud, which are shown as cubes), the option to - draw a textured cube instead is provided. - */ -class ExtrudedSpriteSceneNode: public scene::ISceneNode -{ -public: - ExtrudedSpriteSceneNode( - scene::ISceneNode* parent, - scene::ISceneManager* mgr, - s32 id = -1, - const v3f& position = v3f(0,0,0), - const v3f& rotation = v3f(0,0,0), - const v3f& scale = v3f(1,1,1)); - ~ExtrudedSpriteSceneNode(); - - void setSprite(video::ITexture* texture); - void setCube(const TileSpec tiles[6]); - - void updateLight(u8 light); - - void removeSpriteFromCache(video::ITexture* texture); - - virtual const core::aabbox3d& getBoundingBox() const; - virtual void OnRegisterSceneNode(); - virtual void render(); - -private: - scene::IMeshSceneNode* m_meshnode; - scene::IMesh* m_cubemesh; - bool m_is_cube; - u8 m_light; - - io::path getExtrudedName(video::ITexture* texture); -}; - #endif diff --git a/src/client.cpp b/src/client.cpp index 38ed14978..feb8a3a1e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -33,8 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "nodemetadata.h" #include "nodedef.h" -#include "tooldef.h" -#include "craftitemdef.h" +#include "itemdef.h" #include #include "sha1.h" #include "base64.h" @@ -207,14 +206,12 @@ Client::Client( std::string password, MapDrawControl &control, IWritableTextureSource *tsrc, - IWritableToolDefManager *tooldef, - IWritableNodeDefManager *nodedef, - IWritableCraftItemDefManager *craftitemdef + IWritableItemDefManager *itemdef, + IWritableNodeDefManager *nodedef ): m_tsrc(tsrc), - m_tooldef(tooldef), + m_itemdef(itemdef), m_nodedef(nodedef), - m_craftitemdef(craftitemdef), m_mesh_update_thread(this), m_env( new ClientMap(this, this, control, @@ -234,9 +231,8 @@ Client::Client( m_access_denied(false), m_texture_receive_progress(0), m_textures_received(false), - m_tooldef_received(false), - m_nodedef_received(false), - m_craftitemdef_received(false) + m_itemdef_received(false), + m_nodedef_received(false) { m_packetcounter_timer = 0.0; //m_delete_unused_sectors_timer = 0.0; @@ -251,12 +247,6 @@ Client::Client( else infostream<<"Not building texture atlas."<updateTextures(m_tsrc); - - // Start threads after setting up content definitions - m_mesh_update_thread.Start(); - /* Add local player */ @@ -266,9 +256,6 @@ Client::Client( player->updateName(playername); m_env.addPlayer(player); - - // Initialize player in the inventory context - m_inventory_context.current_player = player; } } @@ -983,7 +970,7 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) //t4.stop(); //TimeTaker t1("inventory.deSerialize()", m_device); - player->inventory.deSerialize(is, this); + player->inventory.deSerialize(is); //t1.stop(); m_inventory_updated = true; @@ -1216,18 +1203,18 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } else { InventoryList *inv = player->inventory.getList("main"); std::string itemstring(deSerializeString(is)); - if (itemstring.empty()) { - inv->deleteItem(0); - infostream - <<"Client: empty player item for peer " - << peer_id << std::endl; - } else { - std::istringstream iss(itemstring); - delete inv->changeItem(0, - InventoryItem::deSerialize(iss, this)); - infostream<<"Client: player item for peer " << peer_id << ": "; - player->getWieldItem()->serialize(infostream); - infostream<changeItem(0, item); + if(itemstring.empty()) + { + infostream<<"Client: empty player item for peer " + <processQueue(); - } + // Mesh update thread must be stopped while + // updating content definitions + assert(!m_mesh_update_thread.IsRunning()); int num_textures = readU16(is); @@ -1362,9 +1344,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } } - // Resume threads - m_mesh_update_thread.setRun(true); - m_mesh_update_thread.Start(); ClientEvent event; event.type = CE_TEXTURES_UPDATED; @@ -1412,14 +1391,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) std::string datastring((char*)&data[2], datasize-2); std::istringstream is(datastring, std::ios_base::binary); - // Stop threads while updating content definitions - m_mesh_update_thread.setRun(false); - // Process the remaining TextureSource queue to let MeshUpdateThread - // get it's remaining textures and thus let it stop - while(m_mesh_update_thread.IsRunning()){ - m_tsrc->processQueue(); - } - + // Mesh update thread must be stopped while + // updating content definitions + assert(!m_mesh_update_thread.IsRunning()); + /* u16 command u16 total number of texture bunches @@ -1484,22 +1459,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) img->drop(); rfile->drop(); } - - if(m_nodedef_received && m_textures_received){ - // Rebuild inherited images and recreate textures - m_tsrc->rebuildImagesAndTextures(); - - // Update texture atlas - if(g_settings->getBool("enable_texture_atlas")) - m_tsrc->buildMainAtlas(this); - - // Update node textures - m_nodedef->updateTextures(m_tsrc); - } - - // Resume threads - m_mesh_update_thread.setRun(true); - m_mesh_update_thread.Start(); ClientEvent event; event.type = CE_TEXTURES_UPDATED; @@ -1507,82 +1466,53 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } else if(command == TOCLIENT_TOOLDEF) { - infostream<<"Client: Received tool definitions: packet size: " - <processQueue(); - } - - std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary); - m_tooldef->deSerialize(tmp_is); - - // Resume threads - m_mesh_update_thread.setRun(true); - m_mesh_update_thread.Start(); + infostream<<"Client: WARNING: Ignoring TOCLIENT_TOOLDEF"<deSerialize(tmp_is, this); - - if(m_textures_received){ - // Update texture atlas - if(g_settings->getBool("enable_texture_atlas")) - m_tsrc->buildMainAtlas(this); - - // Update node textures - m_nodedef->updateTextures(m_tsrc); - } + std::ostringstream tmp_os; + decompressZlib(tmp_is, tmp_os); - // Resume threads - m_mesh_update_thread.setRun(true); - m_mesh_update_thread.Start(); + // Deserialize node definitions + std::istringstream tmp_is2(tmp_os.str()); + m_nodedef->deSerialize(tmp_is2); + m_nodedef_received = true; } else if(command == TOCLIENT_CRAFTITEMDEF) { - infostream<<"Client: Received CraftItem definitions: packet size: " + infostream<<"Client: WARNING: Ignoring TOCLIENT_CRAFTITEMDEF"<processQueue(); - } - std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary); - m_craftitemdef->deSerialize(tmp_is); - - // Resume threads - m_mesh_update_thread.setRun(true); - m_mesh_update_thread.Start(); + std::ostringstream tmp_os; + decompressZlib(tmp_is, tmp_os); + + // Deserialize node definitions + std::istringstream tmp_is2(tmp_os.str()); + m_itemdef->deSerialize(tmp_is2); + m_itemdef_received = true; } else { @@ -1943,11 +1873,6 @@ void Client::selectPlayerItem(u16 item) //JMutexAutoLock envlock(m_env_mutex); //bulk comment-out m_playeritem = item; m_inventory_updated = true; - - LocalPlayer *player = m_env.getLocalPlayer(); - assert(player != NULL); - player->wieldItem(item); - sendPlayerItem(item); } @@ -1971,17 +1896,19 @@ void Client::getLocalInventory(Inventory &dst) dst = player->inventory; } -InventoryContext *Client::getInventoryContext() -{ - return &m_inventory_context; -} - Inventory* Client::getInventory(const InventoryLocation &loc) { switch(loc.type){ case InventoryLocation::UNDEFINED: {} break; + case InventoryLocation::CURRENT_PLAYER: + { + Player *player = m_env.getLocalPlayer(); + assert(player != NULL); + return &player->inventory; + } + break; case InventoryLocation::PLAYER: { Player *player = m_env.getPlayer(loc.name.c_str()); @@ -2003,36 +1930,6 @@ Inventory* Client::getInventory(const InventoryLocation &loc) } return NULL; } -#if 0 -Inventory* Client::getInventory(InventoryContext *c, std::string id) -{ - if(id == "current_player") - { - assert(c->current_player); - return &(c->current_player->inventory); - } - - 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(",")); - NodeMetadata* meta = getNodeMetadata(p); - if(meta) - return meta->getInventory(); - infostream<<"nodemeta at ("<rebuildImagesAndTextures(); + + // Update texture atlas + if(g_settings->getBool("enable_texture_atlas")) + m_tsrc->buildMainAtlas(this); + + // Update node aliases + m_nodedef->updateAliases(m_itemdef); + + // Update node textures + m_nodedef->updateTextures(m_tsrc); + + // Update item textures and meshes + m_itemdef->updateTexturesAndMeshes(this); + + // Start mesh update thread after setting up content definitions + m_mesh_update_thread.Start(); +} + float Client::getRTT(void) { try{ @@ -2245,9 +2168,9 @@ float Client::getRTT(void) // IGameDef interface // Under envlock -IToolDefManager* Client::getToolDefManager() +IItemDefManager* Client::getItemDefManager() { - return m_tooldef; + return m_itemdef; } INodeDefManager* Client::getNodeDefManager() { @@ -2258,10 +2181,6 @@ ICraftDefManager* Client::getCraftDefManager() return NULL; //return m_craftdef; } -ICraftItemDefManager* Client::getCraftItemDefManager() -{ - return m_craftitemdef; -} ITextureSource* Client::getTextureSource() { return m_tsrc; diff --git a/src/client.h b/src/client.h index 49794acf5..6d5e5c525 100644 --- a/src/client.h +++ b/src/client.h @@ -36,10 +36,9 @@ with this program; if not, write to the Free Software Foundation, Inc., struct MeshMakeData; class IGameDef; class IWritableTextureSource; -class IWritableToolDefManager; +class IWritableItemDefManager; class IWritableNodeDefManager; //class IWritableCraftDefManager; -class IWritableCraftItemDefManager; class ClientNotReadyException : public BaseException { @@ -167,9 +166,8 @@ public: std::string password, MapDrawControl &control, IWritableTextureSource *tsrc, - IWritableToolDefManager *tooldef, - IWritableNodeDefManager *nodedef, - IWritableCraftItemDefManager *craftitemdef + IWritableItemDefManager *itemdef, + IWritableNodeDefManager *nodedef ); ~Client(); @@ -245,11 +243,8 @@ public: // Copies the inventory of the local player to parameter void getLocalInventory(Inventory &dst); - InventoryContext *getInventoryContext(); - /* InventoryManager interface */ Inventory* getInventory(const InventoryLocation &loc); - //Inventory* getInventory(InventoryContext *c, std::string id); void inventoryAction(InventoryAction *a); // Gets closest object pointed by the shootline @@ -323,20 +318,19 @@ public: bool texturesReceived() { return m_textures_received; } - bool tooldefReceived() - { return m_tooldef_received; } + bool itemdefReceived() + { return m_itemdef_received; } bool nodedefReceived() { return m_nodedef_received; } - bool craftitemdefReceived() - { return m_craftitemdef_received; } + void afterContentReceived(); + float getRTT(void); // IGameDef interface - virtual IToolDefManager* getToolDefManager(); + virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ICraftItemDefManager* getCraftItemDefManager(); virtual ITextureSource* getTextureSource(); virtual u16 allocateUnknownNodeId(const std::string &name); @@ -363,9 +357,8 @@ private: IntervalLimiter m_map_timer_and_unload_interval; IWritableTextureSource *m_tsrc; - IWritableToolDefManager *m_tooldef; + IWritableItemDefManager *m_itemdef; IWritableNodeDefManager *m_nodedef; - IWritableCraftItemDefManager *m_craftitemdef; MeshUpdateThread m_mesh_update_thread; ClientEnvironment m_env; con::Connection m_con; @@ -387,13 +380,11 @@ private: std::string m_password; bool m_access_denied; std::wstring m_access_denied_reason; - InventoryContext m_inventory_context; Queue m_client_event_queue; float m_texture_receive_progress; bool m_textures_received; - bool m_tooldef_received; + bool m_itemdef_received; bool m_nodedef_received; - bool m_craftitemdef_received; friend class FarMesh; }; diff --git a/src/clientserver.h b/src/clientserver.h index 43de689e4..acb4f8530 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -39,9 +39,14 @@ with this program; if not, write to the Free Software Foundation, Inc., Make players to be handled mostly as ActiveObjects PROTOCOL_VERSION 6: Only non-cached textures are sent + PROTOCOL_VERSION 7: + Add TOCLIENT_ITEMDEF + Obsolete TOCLIENT_TOOLDEF + Obsolete TOCLIENT_CRAFTITEMDEF + Compress the contents of TOCLIENT_ITEMDEF and TOCLIENT_NODEDEF */ -#define PROTOCOL_VERSION 6 +#define PROTOCOL_VERSION 7 #define PROTOCOL_ID 0x4f457403 @@ -252,6 +257,14 @@ enum ToClientCommand string sha1_digest } */ + + TOCLIENT_ITEMDEF = 0x3d, + /* + u16 command + u32 length of next item + serialized ItemDefManager + */ + }; enum ToServerCommand diff --git a/src/connection.cpp b/src/connection.cpp index b9c5d2ac8..8f308c99f 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -666,7 +666,7 @@ void Connection::send(float dtime) // Receive packets from the network and buffers and create ConnectionEvents void Connection::receive() { - u32 datasize = 100000; + u32 datasize = m_max_packet_size * 2; // Double it just to be safe // TODO: We can not know how many layers of header there are. // For now, just assume there are no other than the base headers. u32 packet_maxsize = datasize + BASE_HEADER_SIZE; @@ -854,10 +854,6 @@ void Connection::receive() dout_con<<"ProcessPacket returned data of size " < #include @@ -172,6 +173,8 @@ public: void updateLight(u8 light_at_pos); v3s16 getLightPosition(); void updateNodePos(); + void updateInfoText(); + void updateTexture(); void step(float dtime, ClientEnvironment *env); @@ -191,7 +194,7 @@ private: core::aabbox3d m_selection_box; scene::IMeshSceneNode *m_node; v3f m_position; - std::string m_inventorystring; + std::string m_itemstring; std::string m_infotext; }; @@ -595,39 +598,13 @@ void ItemCAO::addToScene(scene::ISceneManager *smgr, ITextureSource *tsrc, buf->drop(); m_node = smgr->addMeshSceneNode(mesh, NULL); mesh->drop(); - // Set it to use the materials of the meshbuffers directly. - // This is needed for changing the texture in the future - m_node->setReadOnlyMaterials(true); updateNodePos(); /* Update image of node */ - // Create an inventory item to see what is its image - std::istringstream is(m_inventorystring, std::ios_base::binary); - video::ITexture *texture = NULL; - try{ - InventoryItem *item = NULL; - item = InventoryItem::deSerialize(is, m_gamedef); - infostream<<__FUNCTION_NAME<<": m_inventorystring=\"" - < item="<getImage(); - delete item; - } - } - catch(SerializationError &e) - { - infostream<<"WARNING: "<<__FUNCTION_NAME - <<": error deSerializing inventorystring \"" - <getMaterial().setTexture(0, texture); + updateTexture(); } void ItemCAO::removeFromScene() @@ -662,6 +639,51 @@ void ItemCAO::updateNodePos() m_node->setPosition(m_position); } +void ItemCAO::updateInfoText() +{ + try{ + IItemDefManager *idef = m_gamedef->idef(); + ItemStack item; + item.deSerialize(m_itemstring, idef); + if(item.isKnown(idef)) + m_infotext = item.getDefinition(idef).description; + else + m_infotext = "Unknown item: '" + m_itemstring + "'"; + if(item.count >= 2) + m_infotext += " (" + itos(item.count) + ")"; + } + catch(SerializationError &e) + { + m_infotext = "Unknown item: '" + m_itemstring + "'"; + } +} + +void ItemCAO::updateTexture() +{ + if(m_node == NULL) + return; + + // Create an inventory item to see what is its image + std::istringstream is(m_itemstring, std::ios_base::binary); + video::ITexture *texture = NULL; + try{ + IItemDefManager *idef = m_gamedef->idef(); + ItemStack item; + item.deSerialize(is, idef); + texture = item.getDefinition(idef).inventory_texture; + } + catch(SerializationError &e) + { + infostream<<"WARNING: "<<__FUNCTION_NAME + <<": error deSerializing itemstring \"" + <getMaterial(0).setTexture(0, texture); +} + + void ItemCAO::step(float dtime, ClientEnvironment *env) { if(m_node) @@ -689,6 +711,13 @@ void ItemCAO::processMessage(const std::string &data) m_position = readV3F1000(is); updateNodePos(); } + if(cmd == 1) + { + // itemstring + m_itemstring = deSerializeString(is); + updateInfoText(); + updateTexture(); + } } void ItemCAO::initialize(const std::string &data) @@ -704,28 +733,12 @@ void ItemCAO::initialize(const std::string &data) return; // pos m_position = readV3F1000(is); - // inventorystring - m_inventorystring = deSerializeString(is); + // itemstring + m_itemstring = deSerializeString(is); } updateNodePos(); - - /* - Set infotext to item name if item cannot be deserialized - */ - try{ - InventoryItem *item = NULL; - item = InventoryItem::deSerialize(m_inventorystring, m_gamedef); - if(item){ - if(!item->isKnown()) - m_infotext = "Unknown item: '" + m_inventorystring + "'"; - } - delete item; - } - catch(SerializationError &e) - { - m_infotext = "Unknown item: '" + m_inventorystring + "'"; - } + updateInfoText(); } /* diff --git a/src/content_mapblock.cpp b/src/content_mapblock.cpp index e8e4fd231..79f49cbf8 100644 --- a/src/content_mapblock.cpp +++ b/src/content_mapblock.cpp @@ -20,112 +20,105 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_mapblock.h" #include "main.h" // For g_settings -#include "mineral.h" -#include "mapblock_mesh.h" // For MapBlock_LightColor() +#include "mapblock_mesh.h" // For MapBlock_LightColor() and MeshCollector #include "settings.h" #include "nodedef.h" +#include "tile.h" #include "gamedef.h" -#ifndef SERVER // Create a cuboid. -// material - the material to use (for all 6 faces) // collector - the MeshCollector for the resulting polygons -// pa - texture atlas pointer for the material +// box - the position and size of the box +// materials - the materials to use (for all 6 faces) +// pa - texture atlas pointers for the materials +// matcount - number of entries in "materials" and "pa", 1<=matcount<=6 // c - vertex colour - used for all -// pos - the position of the centre of the cuboid -// rz,ry,rz - the radius of the cuboid in each dimension // txc - texture coordinates - this is a list of texture coordinates // for the opposite corners of each face - therefore, there // should be (2+2)*6=24 values in the list. Alternatively, pass // NULL to use the entire texture for each face. The order of -// the faces in the list is top-backi-right-front-left-bottom -// If you specified 0,0,1,1 for each face, that would be the -// same as passing NULL. -void makeCuboid(video::SMaterial &material, MeshCollector *collector, - AtlasPointer* pa, video::SColor &c, - v3f &pos, f32 rx, f32 ry, f32 rz, f32* txc) +// the faces in the list is up-down-right-left-back-front +// (compatible with ContentFeatures). If you specified 0,0,1,1 +// for each face, that would be the same as passing NULL. +void makeCuboid(MeshCollector *collector, const aabb3f &box, + const video::SMaterial *materials, const AtlasPointer *pa, int matcount, + video::SColor &c, const f32* txc) { - f32 tu0=pa->x0(); - f32 tu1=pa->x1(); - f32 tv0=pa->y0(); - f32 tv1=pa->y1(); - f32 txus=tu1-tu0; - f32 txvs=tv1-tv0; - - video::S3DVertex v[4] = + assert(matcount >= 1); + + v3f min = box.MinEdge; + v3f max = box.MaxEdge; + + if(txc == NULL) { - video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv1), - video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv1), - video::S3DVertex(0,0,0, 0,0,0, c, tu1, tv0), - video::S3DVertex(0,0,0, 0,0,0, c, tu0, tv0) - }; + static const f32 txc_default[24] = { + 0,0,1,1, + 0,0,1,1, + 0,0,1,1, + 0,0,1,1, + 0,0,1,1, + 0,0,1,1 + }; + txc = txc_default; + } - for(int i=0;i<6;i++) + video::S3DVertex vertices[24] = { - switch(i) - { - case 0: // top - v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz; - v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz; - v[2].Pos.X= rx; v[2].Pos.Y= ry; v[2].Pos.Z= rz; - v[3].Pos.X= rx; v[3].Pos.Y= ry, v[3].Pos.Z=-rz; - break; - case 1: // back - v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz; - v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz; - v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz; - v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz; - break; - case 2: //right - v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z=-rz; - v[1].Pos.X= rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz; - v[2].Pos.X= rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz; - v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz; - break; - case 3: // front - v[0].Pos.X= rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz; - v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z= rz; - v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z= rz; - v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz; - break; - case 4: // left - v[0].Pos.X=-rx; v[0].Pos.Y= ry; v[0].Pos.Z= rz; - v[1].Pos.X=-rx; v[1].Pos.Y= ry; v[1].Pos.Z=-rz; - v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz; - v[3].Pos.X=-rx; v[3].Pos.Y=-ry, v[3].Pos.Z= rz; - break; - case 5: // bottom - v[0].Pos.X= rx; v[0].Pos.Y=-ry; v[0].Pos.Z= rz; - v[1].Pos.X=-rx; v[1].Pos.Y=-ry; v[1].Pos.Z= rz; - v[2].Pos.X=-rx; v[2].Pos.Y=-ry; v[2].Pos.Z=-rz; - v[3].Pos.X= rx; v[3].Pos.Y=-ry, v[3].Pos.Z=-rz; - break; - } + // up + video::S3DVertex(min.X,max.Y,max.Z, 0,1,0, c, txc[0],txc[1]), + video::S3DVertex(max.X,max.Y,max.Z, 0,1,0, c, txc[2],txc[1]), + video::S3DVertex(max.X,max.Y,min.Z, 0,1,0, c, txc[2],txc[3]), + video::S3DVertex(min.X,max.Y,min.Z, 0,1,0, c, txc[0],txc[3]), + // down + video::S3DVertex(min.X,min.Y,min.Z, 0,-1,0, c, txc[4],txc[5]), + video::S3DVertex(max.X,min.Y,min.Z, 0,-1,0, c, txc[6],txc[5]), + video::S3DVertex(max.X,min.Y,max.Z, 0,-1,0, c, txc[6],txc[7]), + video::S3DVertex(min.X,min.Y,max.Z, 0,-1,0, c, txc[4],txc[7]), + // right + video::S3DVertex(max.X,max.Y,min.Z, 1,0,0, c, txc[ 8],txc[9]), + video::S3DVertex(max.X,max.Y,max.Z, 1,0,0, c, txc[10],txc[9]), + video::S3DVertex(max.X,min.Y,max.Z, 1,0,0, c, txc[10],txc[11]), + video::S3DVertex(max.X,min.Y,min.Z, 1,0,0, c, txc[ 8],txc[11]), + // left + video::S3DVertex(min.X,max.Y,max.Z, -1,0,0, c, txc[12],txc[13]), + video::S3DVertex(min.X,max.Y,min.Z, -1,0,0, c, txc[14],txc[13]), + video::S3DVertex(min.X,min.Y,min.Z, -1,0,0, c, txc[14],txc[15]), + video::S3DVertex(min.X,min.Y,max.Z, -1,0,0, c, txc[12],txc[15]), + // back + video::S3DVertex(min.X,max.Y,min.Z, 0,0,-1, c, txc[16],txc[17]), + video::S3DVertex(max.X,max.Y,min.Z, 0,0,-1, c, txc[18],txc[17]), + video::S3DVertex(max.X,min.Y,min.Z, 0,0,-1, c, txc[18],txc[19]), + video::S3DVertex(min.X,min.Y,min.Z, 0,0,-1, c, txc[16],txc[19]), + // front + video::S3DVertex(max.X,max.Y,max.Z, 0,0,1, c, txc[20],txc[21]), + video::S3DVertex(min.X,max.Y,max.Z, 0,0,1, c, txc[22],txc[21]), + video::S3DVertex(min.X,min.Y,max.Z, 0,0,1, c, txc[22],txc[23]), + video::S3DVertex(max.X,min.Y,max.Z, 0,0,1, c, txc[20],txc[23]), + }; - if(txc!=NULL) - { - v[0].TCoords.X=tu0+txus*txc[0]; v[0].TCoords.Y=tv0+txvs*txc[3]; - v[1].TCoords.X=tu0+txus*txc[2]; v[1].TCoords.Y=tv0+txvs*txc[3]; - v[2].TCoords.X=tu0+txus*txc[2]; v[2].TCoords.Y=tv0+txvs*txc[1]; - v[3].TCoords.X=tu0+txus*txc[0]; v[3].TCoords.Y=tv0+txvs*txc[1]; - txc+=4; - } + for(s32 j=0; j<24; j++) + { + int matindex = MYMIN(j/4, matcount-1); + vertices[j].TCoords *= pa[matindex].size; + vertices[j].TCoords += pa[matindex].pos; + } - for(u16 i=0; i<4; i++) - v[i].Pos += pos; - u16 indices[] = {0,1,2,2,3,0}; - collector->append(material, v, 4, indices, 6); + u16 indices[] = {0,1,2,2,3,0}; + // Add to mesh collector + for(s32 j=0; j<24; j+=4) + { + int matindex = MYMIN(j/4, matcount-1); + collector->append(materials[matindex], + vertices+j, 4, indices, 6); } - } -#endif -#ifndef SERVER void mapblock_mesh_generate_special(MeshMakeData *data, MeshCollector &collector, IGameDef *gamedef) { INodeDefManager *nodedef = gamedef->ndef(); + ITextureSource *tsrc = gamedef->getTextureSource(); // 0ms //TimeTaker timer("mapblock_mesh_generate_special()"); @@ -521,7 +514,9 @@ void mapblock_mesh_generate_special(MeshMakeData *data, material_glass.setFlag(video::EMF_BILINEAR_FILTER, false); material_glass.setFlag(video::EMF_FOG_ENABLE, true); material_glass.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - AtlasPointer pa_glass = f.tiles[0].texture; + TileSpec tile_glass = getNodeTile(n, p, v3s16(0,0,0), + &data->m_temp_mods, tsrc, nodedef); + AtlasPointer pa_glass = tile_glass.texture; material_glass.setTexture(0, pa_glass.atlas); u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); @@ -585,54 +580,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data, material_leaves1.setFlag(video::EMF_BILINEAR_FILTER, false); material_leaves1.setFlag(video::EMF_FOG_ENABLE, true); material_leaves1.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - AtlasPointer pa_leaves1 = f.tiles[0].texture; + TileSpec tile_leaves1 = getNodeTile(n, p, v3s16(0,0,0), + &data->m_temp_mods, tsrc, nodedef); + AtlasPointer pa_leaves1 = tile_leaves1.texture; material_leaves1.setTexture(0, pa_leaves1.atlas); u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); video::SColor c = MapBlock_LightColor(255, l); - for(u32 j=0; j<6; j++) - { - video::S3DVertex vertices[4] = - { - video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, - pa_leaves1.x0(), pa_leaves1.y1()), - video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, - pa_leaves1.x1(), pa_leaves1.y1()), - video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, - pa_leaves1.x1(), pa_leaves1.y0()), - video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, - pa_leaves1.x0(), pa_leaves1.y0()), - }; - - // Rotations in the g_6dirs format - if(j == 0) // Z+ - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(0); - else if(j == 1) // Y+ - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateYZBy(-90); - else if(j == 2) // X+ - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(-90); - else if(j == 3) // Z- - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(180); - else if(j == 4) // Y- - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateYZBy(90); - else if(j == 5) // X- - for(u16 i=0; i<4; i++) - vertices[i].Pos.rotateXZBy(90); - - for(u16 i=0; i<4; i++){ - vertices[i].Pos += intToFloat(p + blockpos_nodes, BS); - } - - u16 indices[] = {0,1,2,2,3,0}; - // Add to mesh collector - collector.append(material_leaves1, vertices, 4, indices, 6); - } + v3f pos = intToFloat(p+blockpos_nodes, BS); + aabb3f box(-BS/2,-BS/2,-BS/2,BS/2,BS/2,BS/2); + box.MinEdge += pos; + box.MaxEdge += pos; + makeCuboid(&collector, box, + &material_leaves1, &pa_leaves1, 1, + c, NULL); break;} case NDT_ALLFACES_OPTIONAL: // This is always pre-converted to something else @@ -824,9 +786,22 @@ void mapblock_mesh_generate_special(MeshMakeData *data, material_wood.setFlag(video::EMF_BILINEAR_FILTER, false); material_wood.setFlag(video::EMF_FOG_ENABLE, true); material_wood.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - AtlasPointer pa_wood = f.tiles[0].texture; + TileSpec tile_wood = getNodeTile(n, p, v3s16(0,0,0), + &data->m_temp_mods, tsrc, nodedef); + AtlasPointer pa_wood = tile_wood.texture; material_wood.setTexture(0, pa_wood.atlas); + video::SMaterial material_wood_nomod; + material_wood_nomod.setFlag(video::EMF_LIGHTING, false); + material_wood_nomod.setFlag(video::EMF_BILINEAR_FILTER, false); + material_wood_nomod.setFlag(video::EMF_FOG_ENABLE, true); + material_wood_nomod.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + + TileSpec tile_wood_nomod = getNodeTile(n, p, v3s16(0,0,0), + NULL, tsrc, nodedef); + AtlasPointer pa_wood_nomod = tile_wood_nomod.texture; + material_wood_nomod.setTexture(0, pa_wood_nomod.atlas); + u8 l = decode_light(undiminish_light(n.getLightBlend(data->m_daynight_ratio, nodedef))); video::SColor c = MapBlock_LightColor(255, l); @@ -834,18 +809,21 @@ void mapblock_mesh_generate_special(MeshMakeData *data, const f32 bar_rad=(f32)BS/20; const f32 bar_len=(f32)(BS/2)-post_rad; - // The post - always present v3f pos = intToFloat(p+blockpos_nodes, BS); + + // The post - always present + aabb3f post(-post_rad,-BS/2,-post_rad,post_rad,BS/2,post_rad); + post.MinEdge += pos; + post.MaxEdge += pos; f32 postuv[24]={ + 0.4,0.4,0.6,0.6, 0.4,0.4,0.6,0.6, 0.35,0,0.65,1, 0.35,0,0.65,1, 0.35,0,0.65,1, - 0.35,0,0.65,1, - 0.4,0.4,0.6,0.6}; - makeCuboid(material_wood, &collector, - &pa_wood, c, pos, - post_rad,BS/2,post_rad, postuv); + 0.35,0,0.65,1}; + makeCuboid(&collector, post, &material_wood, + &pa_wood, 1, c, postuv); // Now a section of fence, +X, if there's a post there v3s16 p2 = p; @@ -854,9 +832,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, const ContentFeatures *f2 = &nodedef->get(n2); if(f2->drawtype == NDT_FENCELIKE) { - pos = intToFloat(p+blockpos_nodes, BS); - pos.X += BS/2; - pos.Y += BS/4; + aabb3f bar(-bar_len+BS/2,-bar_rad+BS/4,-bar_rad, + bar_len+BS/2,bar_rad+BS/4,bar_rad); + bar.MinEdge += pos; + bar.MaxEdge += pos; f32 xrailuv[24]={ 0,0.4,1,0.6, 0,0.4,1,0.6, @@ -864,14 +843,12 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6}; - makeCuboid(material_wood, &collector, - &pa_wood, c, pos, - bar_len,bar_rad,bar_rad, xrailuv); - - pos.Y -= BS/2; - makeCuboid(material_wood, &collector, - &pa_wood, c, pos, - bar_len,bar_rad,bar_rad, xrailuv); + makeCuboid(&collector, bar, &material_wood_nomod, + &pa_wood_nomod, 1, c, xrailuv); + bar.MinEdge.Y -= BS/2; + bar.MaxEdge.Y -= BS/2; + makeCuboid(&collector, bar, &material_wood_nomod, + &pa_wood_nomod, 1, c, xrailuv); } // Now a section of fence, +Z, if there's a post there @@ -881,9 +858,10 @@ void mapblock_mesh_generate_special(MeshMakeData *data, f2 = &nodedef->get(n2); if(f2->drawtype == NDT_FENCELIKE) { - pos = intToFloat(p+blockpos_nodes, BS); - pos.Z += BS/2; - pos.Y += BS/4; + aabb3f bar(-bar_rad,-bar_rad+BS/4,-bar_len+BS/2, + bar_rad,bar_rad+BS/4,bar_len+BS/2); + bar.MinEdge += pos; + bar.MaxEdge += pos; f32 zrailuv[24]={ 0,0.4,1,0.6, 0,0.4,1,0.6, @@ -891,14 +869,13 @@ void mapblock_mesh_generate_special(MeshMakeData *data, 0,0.4,1,0.6, 0,0.4,1,0.6, 0,0.4,1,0.6}; - makeCuboid(material_wood, &collector, - &pa_wood, c, pos, - bar_rad,bar_rad,bar_len, zrailuv); - pos.Y -= BS/2; - makeCuboid(material_wood, &collector, - &pa_wood, c, pos, - bar_rad,bar_rad,bar_len, zrailuv); + makeCuboid(&collector, bar, &material_wood_nomod, + &pa_wood_nomod, 1, c, zrailuv); + bar.MinEdge.Y -= BS/2; + bar.MaxEdge.Y -= BS/2; + makeCuboid(&collector, bar, &material_wood_nomod, + &pa_wood_nomod, 1, c, zrailuv); } break;} case NDT_RAILLIKE: @@ -1011,5 +988,4 @@ void mapblock_mesh_generate_special(MeshMakeData *data, } } } -#endif diff --git a/src/content_mapnode.cpp b/src/content_mapnode.cpp index a74760ba3..4701736cf 100644 --- a/src/content_mapnode.cpp +++ b/src/content_mapnode.cpp @@ -26,75 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nameidmapping.h" #include -/* - Legacy node definitions -*/ - -#define WATER_ALPHA 160 - -#define WATER_VISC 1 -#define LAVA_VISC 7 - -void setConstantMaterialProperties(MaterialProperties &mprop, float time) -{ - mprop.diggability = DIGGABLE_CONSTANT; - mprop.constant_time = time; -} - -void setStoneLikeMaterialProperties(MaterialProperties &mprop, float toughness) -{ - mprop.diggability = DIGGABLE_NORMAL; - mprop.weight = 5.0 * toughness; - mprop.crackiness = 1.0; - mprop.crumbliness = -0.1; - mprop.cuttability = -0.2; -} - -void setDirtLikeMaterialProperties(MaterialProperties &mprop, float toughness) -{ - mprop.diggability = DIGGABLE_NORMAL; - mprop.weight = toughness * 1.2; - mprop.crackiness = 0; - mprop.crumbliness = 1.2; - mprop.cuttability = -0.4; -} - -void setGravelLikeMaterialProperties(MaterialProperties &mprop, float toughness) -{ - mprop.diggability = DIGGABLE_NORMAL; - mprop.weight = toughness * 2.0; - mprop.crackiness = 0.2; - mprop.crumbliness = 1.5; - mprop.cuttability = -1.0; -} - -void setWoodLikeMaterialProperties(MaterialProperties &mprop, float toughness) -{ - mprop.diggability = DIGGABLE_NORMAL; - mprop.weight = toughness * 1.0; - mprop.crackiness = 0.75; - mprop.crumbliness = -1.0; - mprop.cuttability = 1.5; -} - -void setLeavesLikeMaterialProperties(MaterialProperties &mprop, float toughness) -{ - mprop.diggability = DIGGABLE_NORMAL; - mprop.weight = -0.5 * toughness; - mprop.crackiness = 0; - mprop.crumbliness = 0; - mprop.cuttability = 2.0; -} - -void setGlassLikeMaterialProperties(MaterialProperties &mprop, float toughness) -{ - mprop.diggability = DIGGABLE_NORMAL; - mprop.weight = 0.1 * toughness; - mprop.crackiness = 2.0; - mprop.crumbliness = -1.0; - mprop.cuttability = -1.0; -} - /* Legacy node content type IDs Ranges: @@ -209,46 +140,46 @@ MapNode mapnode_translate_to_internal(MapNode n_from, u8 version) void content_mapnode_get_name_id_mapping(NameIdMapping *nimap) { - nimap->set(0, "stone"); - nimap->set(2, "water_flowing"); - nimap->set(3, "torch"); - nimap->set(9, "water_source"); - nimap->set(14, "sign_wall"); - nimap->set(15, "chest"); - nimap->set(16, "furnace"); - nimap->set(17, "locked_chest"); - nimap->set(21, "wooden_fence"); - nimap->set(30, "rail"); - nimap->set(31, "ladder"); - nimap->set(32, "lava_flowing"); - nimap->set(33, "lava_source"); - nimap->set(0x800, "dirt_with_grass"); - nimap->set(0x801, "tree"); - nimap->set(0x802, "leaves"); - nimap->set(0x803, "dirt_with_grass_footsteps"); - nimap->set(0x804, "mese"); - nimap->set(0x805, "dirt"); - nimap->set(0x806, "cloud"); - nimap->set(0x807, "coalstone"); - nimap->set(0x808, "wood"); - nimap->set(0x809, "sand"); - nimap->set(0x80a, "cobble"); - nimap->set(0x80b, "steel"); - nimap->set(0x80c, "glass"); - nimap->set(0x80d, "mossycobble"); - nimap->set(0x80e, "gravel"); - nimap->set(0x80f, "sandstone"); - nimap->set(0x810, "cactus"); - nimap->set(0x811, "brick"); - nimap->set(0x812, "clay"); - nimap->set(0x813, "papyrus"); - nimap->set(0x814, "bookshelf"); - nimap->set(0x815, "jungletree"); - nimap->set(0x816, "junglegrass"); - nimap->set(0x817, "nyancat"); - nimap->set(0x818, "nyancat_rainbow"); - nimap->set(0x819, "apple"); - nimap->set(0x820, "sapling"); + nimap->set(0, "default:stone"); + nimap->set(2, "default:water_flowing"); + nimap->set(3, "default:torch"); + nimap->set(9, "default:water_source"); + nimap->set(14, "default:sign_wall"); + nimap->set(15, "default:chest"); + nimap->set(16, "default:furnace"); + nimap->set(17, "default:chest_locked"); + nimap->set(21, "default:fence_wood"); + nimap->set(30, "default:rail"); + nimap->set(31, "default:ladder"); + nimap->set(32, "default:lava_flowing"); + nimap->set(33, "default:lava_source"); + nimap->set(0x800, "default:dirt_with_grass"); + nimap->set(0x801, "default:tree"); + nimap->set(0x802, "default:leaves"); + nimap->set(0x803, "default:dirt_with_grass_footsteps"); + nimap->set(0x804, "default:mese"); + nimap->set(0x805, "default:dirt"); + nimap->set(0x806, "default:cloud"); + nimap->set(0x807, "default:coalstone"); + nimap->set(0x808, "default:wood"); + nimap->set(0x809, "default:sand"); + nimap->set(0x80a, "default:cobble"); + nimap->set(0x80b, "default:steelblock"); + nimap->set(0x80c, "default:glass"); + nimap->set(0x80d, "default:mossycobble"); + nimap->set(0x80e, "default:gravel"); + nimap->set(0x80f, "default:sandstone"); + nimap->set(0x810, "default:cactus"); + nimap->set(0x811, "default:brick"); + nimap->set(0x812, "default:clay"); + nimap->set(0x813, "default:papyrus"); + nimap->set(0x814, "default:bookshelf"); + nimap->set(0x815, "default:jungletree"); + nimap->set(0x816, "default:junglegrass"); + nimap->set(0x817, "default:nyancat"); + nimap->set(0x818, "default:nyancat_rainbow"); + nimap->set(0x819, "default:apple"); + nimap->set(0x820, "default:sapling"); // Static types nimap->set(CONTENT_IGNORE, "ignore"); nimap->set(CONTENT_AIR, "air"); @@ -259,46 +190,46 @@ class NewNameGetter public: NewNameGetter() { - old_to_new["CONTENT_STONE"] = "stone"; - old_to_new["CONTENT_WATER"] = "water_flowing"; - old_to_new["CONTENT_TORCH"] = "torch"; - old_to_new["CONTENT_WATERSOURCE"] = "water_source"; - old_to_new["CONTENT_SIGN_WALL"] = "sign_wall"; - old_to_new["CONTENT_CHEST"] = "chest"; - old_to_new["CONTENT_FURNACE"] = "furnace"; - old_to_new["CONTENT_LOCKABLE_CHEST"] = "locked_chest"; - old_to_new["CONTENT_FENCE"] = "wooden_fence"; - old_to_new["CONTENT_RAIL"] = "rail"; - old_to_new["CONTENT_LADDER"] = "ladder"; - old_to_new["CONTENT_LAVA"] = "lava_flowing"; - old_to_new["CONTENT_LAVASOURCE"] = "lava_source"; - old_to_new["CONTENT_GRASS"] = "dirt_with_grass"; - old_to_new["CONTENT_TREE"] = "tree"; - old_to_new["CONTENT_LEAVES"] = "leaves"; - old_to_new["CONTENT_GRASS_FOOTSTEPS"] = "dirt_with_grass_footsteps"; - old_to_new["CONTENT_MESE"] = "mese"; - old_to_new["CONTENT_MUD"] = "dirt"; - old_to_new["CONTENT_CLOUD"] = "cloud"; - old_to_new["CONTENT_COALSTONE"] = "coalstone"; - old_to_new["CONTENT_WOOD"] = "wood"; - old_to_new["CONTENT_SAND"] = "sand"; - old_to_new["CONTENT_COBBLE"] = "cobble"; - old_to_new["CONTENT_STEEL"] = "steel"; - old_to_new["CONTENT_GLASS"] = "glass"; - old_to_new["CONTENT_MOSSYCOBBLE"] = "mossycobble"; - old_to_new["CONTENT_GRAVEL"] = "gravel"; - old_to_new["CONTENT_SANDSTONE"] = "sandstone"; - old_to_new["CONTENT_CACTUS"] = "cactus"; - old_to_new["CONTENT_BRICK"] = "brick"; - old_to_new["CONTENT_CLAY"] = "clay"; - old_to_new["CONTENT_PAPYRUS"] = "papyrus"; - old_to_new["CONTENT_BOOKSHELF"] = "bookshelf"; - old_to_new["CONTENT_JUNGLETREE"] = "jungletree"; - old_to_new["CONTENT_JUNGLEGRASS"] = "junglegrass"; - old_to_new["CONTENT_NC"] = "nyancat"; - old_to_new["CONTENT_NC_RB"] = "nyancat_rainbow"; - old_to_new["CONTENT_APPLE"] = "apple"; - old_to_new["CONTENT_SAPLING"] = "sapling"; + old_to_new["CONTENT_STONE"] = "default:stone"; + old_to_new["CONTENT_WATER"] = "default:water_flowing"; + old_to_new["CONTENT_TORCH"] = "default:torch"; + old_to_new["CONTENT_WATERSOURCE"] = "default:water_source"; + old_to_new["CONTENT_SIGN_WALL"] = "default:sign_wall"; + old_to_new["CONTENT_CHEST"] = "default:chest"; + old_to_new["CONTENT_FURNACE"] = "default:furnace"; + old_to_new["CONTENT_LOCKABLE_CHEST"] = "default:locked_chest"; + old_to_new["CONTENT_FENCE"] = "default:wooden_fence"; + old_to_new["CONTENT_RAIL"] = "default:rail"; + old_to_new["CONTENT_LADDER"] = "default:ladder"; + old_to_new["CONTENT_LAVA"] = "default:lava_flowing"; + old_to_new["CONTENT_LAVASOURCE"] = "default:lava_source"; + old_to_new["CONTENT_GRASS"] = "default:dirt_with_grass"; + old_to_new["CONTENT_TREE"] = "default:tree"; + old_to_new["CONTENT_LEAVES"] = "default:leaves"; + old_to_new["CONTENT_GRASS_FOOTSTEPS"] = "default:dirt_with_grass_footsteps"; + old_to_new["CONTENT_MESE"] = "default:mese"; + old_to_new["CONTENT_MUD"] = "default:dirt"; + old_to_new["CONTENT_CLOUD"] = "default:cloud"; + old_to_new["CONTENT_COALSTONE"] = "default:coalstone"; + old_to_new["CONTENT_WOOD"] = "default:wood"; + old_to_new["CONTENT_SAND"] = "default:sand"; + old_to_new["CONTENT_COBBLE"] = "default:cobble"; + old_to_new["CONTENT_STEEL"] = "default:steel"; + old_to_new["CONTENT_GLASS"] = "default:glass"; + old_to_new["CONTENT_MOSSYCOBBLE"] = "default:mossycobble"; + old_to_new["CONTENT_GRAVEL"] = "default:gravel"; + old_to_new["CONTENT_SANDSTONE"] = "default:sandstone"; + old_to_new["CONTENT_CACTUS"] = "default:cactus"; + old_to_new["CONTENT_BRICK"] = "default:brick"; + old_to_new["CONTENT_CLAY"] = "default:clay"; + old_to_new["CONTENT_PAPYRUS"] = "default:papyrus"; + old_to_new["CONTENT_BOOKSHELF"] = "default:bookshelf"; + old_to_new["CONTENT_JUNGLETREE"] = "default:jungletree"; + old_to_new["CONTENT_JUNGLEGRASS"] = "default:junglegrass"; + old_to_new["CONTENT_NC"] = "default:nyancat"; + old_to_new["CONTENT_NC_RB"] = "default:nyancat_rainbow"; + old_to_new["CONTENT_APPLE"] = "default:apple"; + old_to_new["CONTENT_SAPLING"] = "default:sapling"; // Just in case old_to_new["CONTENT_IGNORE"] = "ignore"; old_to_new["CONTENT_AIR"] = "air"; @@ -334,605 +265,3 @@ content_t legacy_get_id(const std::string &oldname, INodeDefManager *ndef) return id; } -// Initialize default (legacy) node definitions -void content_mapnode_init(IWritableNodeDefManager *nodemgr) -{ - content_t i; - ContentFeatures f; - - i = CONTENT_STONE; - f = ContentFeatures(); - f.name = "stone"; - f.setAllTextures("stone.png"); - f.setInventoryTextureCube("stone.png", "stone.png", "stone.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.often_contains_mineral = true; - f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 1"; - setStoneLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_GRASS; - f = ContentFeatures(); - f.name = "dirt_with_grass"; - f.setAllTextures("mud.png^grass_side.png"); - f.setTexture(0, "grass.png"); - f.setTexture(1, "mud.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_MUD)+" 1"; - setDirtLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_GRASS_FOOTSTEPS; - f = ContentFeatures(); - f.name = "dirt_with_grass_footsteps"; - f.setAllTextures("mud.png^grass_side.png"); - f.setTexture(0, "grass_footsteps.png"); - f.setTexture(1, "mud.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_MUD)+" 1"; - setDirtLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_MUD; - f = ContentFeatures(); - f.name = "dirt"; - f.setAllTextures("mud.png"); - f.setInventoryTextureCube("mud.png", "mud.png", "mud.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setDirtLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_SAND; - f = ContentFeatures(); - f.name = "sand"; - f.setAllTextures("sand.png"); - f.setInventoryTextureCube("sand.png", "sand.png", "sand.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.cookresult_item = std::string("MaterialItem2 ")+itos(CONTENT_GLASS)+" 1"; - setDirtLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_GRAVEL; - f = ContentFeatures(); - f.name = "gravel"; - f.setAllTextures("gravel.png"); - f.setInventoryTextureCube("gravel.png", "gravel.png", "gravel.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setGravelLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_SANDSTONE; - f = ContentFeatures(); - f.name = "sandstone"; - f.setAllTextures("sandstone.png"); - f.setInventoryTextureCube("sandstone.png", "sandstone.png", "sandstone.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_SAND)+" 1"; - setDirtLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_CLAY; - f = ContentFeatures(); - f.name = "clay"; - f.setAllTextures("clay.png"); - f.setInventoryTextureCube("clay.png", "clay.png", "clay.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("CraftItem lump_of_clay 4"); - setDirtLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_BRICK; - f = ContentFeatures(); - f.name = "brick"; - f.setAllTextures("brick.png"); - f.setInventoryTextureCube("brick.png", "brick.png", "brick.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("CraftItem clay_brick 4"); - setStoneLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_TREE; - f = ContentFeatures(); - f.name = "tree"; - f.setAllTextures("tree.png"); - f.setTexture(0, "tree_top.png"); - f.setTexture(1, "tree_top.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.cookresult_item = "CraftItem lump_of_coal 1"; - f.furnace_burntime = 30; - setWoodLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_JUNGLETREE; - f = ContentFeatures(); - f.name = "jungletree"; - f.setAllTextures("jungletree.png"); - f.setTexture(0, "jungletree_top.png"); - f.setTexture(1, "jungletree_top.png"); - f.param_type = CPT_MINERAL; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.furnace_burntime = 30; - setWoodLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_JUNGLEGRASS; - f = ContentFeatures(); - f.name = "junglegrass"; - f.drawtype = NDT_PLANTLIKE; - f.visual_scale = 1.3; - f.setAllTextures("junglegrass.png"); - f.setInventoryTexture("junglegrass.png"); - f.light_propagates = true; - f.param_type = CPT_LIGHT; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.walkable = false; - setLeavesLikeMaterialProperties(f.material, 1.0); - f.furnace_burntime = 2; - nodemgr->set(i, f); - - i = CONTENT_LEAVES; - f = ContentFeatures(); - f.name = "leaves"; - f.drawtype = NDT_ALLFACES_OPTIONAL; - f.setAllTextures("leaves.png"); - //f.setAllTextures("[noalpha:leaves.png"); - f.light_propagates = true; - f.param_type = CPT_LIGHT; - f.extra_dug_item = std::string("MaterialItem2 ")+itos(CONTENT_SAPLING)+" 1"; - f.extra_dug_item_rarity = 20; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setLeavesLikeMaterialProperties(f.material, 1.0); - f.furnace_burntime = 1.0; - nodemgr->set(i, f); - - i = CONTENT_CACTUS; - f = ContentFeatures(); - f.name = "cactus"; - f.setAllTextures("cactus_side.png"); - f.setTexture(0, "cactus_top.png"); - f.setTexture(1, "cactus_top.png"); - f.setInventoryTextureCube("cactus_top.png", "cactus_side.png", "cactus_side.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setWoodLikeMaterialProperties(f.material, 0.75); - f.furnace_burntime = 15; - nodemgr->set(i, f); - - i = CONTENT_PAPYRUS; - f = ContentFeatures(); - f.name = "papyrus"; - f.drawtype = NDT_PLANTLIKE; - f.setAllTextures("papyrus.png"); - f.setInventoryTexture("papyrus.png"); - f.light_propagates = true; - f.param_type = CPT_LIGHT; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.walkable = false; - setLeavesLikeMaterialProperties(f.material, 0.5); - f.furnace_burntime = 1; - nodemgr->set(i, f); - - i = CONTENT_BOOKSHELF; - f = ContentFeatures(); - f.name = "bookshelf"; - f.setAllTextures("bookshelf.png"); - f.setTexture(0, "wood.png"); - f.setTexture(1, "wood.png"); - // FIXME: setInventoryTextureCube() only cares for the first texture - f.setInventoryTextureCube("bookshelf.png", "bookshelf.png", "bookshelf.png"); - //f.setInventoryTextureCube("wood.png", "bookshelf.png", "bookshelf.png"); - f.param_type = CPT_MINERAL; - f.is_ground_content = true; - setWoodLikeMaterialProperties(f.material, 0.75); - f.furnace_burntime = 30; - nodemgr->set(i, f); - - i = CONTENT_GLASS; - f = ContentFeatures(); - f.name = "glass"; - f.drawtype = NDT_GLASSLIKE; - f.setAllTextures("glass.png"); - f.light_propagates = true; - f.sunlight_propagates = true; - f.param_type = CPT_LIGHT; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.setInventoryTextureCube("glass.png", "glass.png", "glass.png"); - setGlassLikeMaterialProperties(f.material, 1.0); - nodemgr->set(i, f); - - i = CONTENT_FENCE; - f = ContentFeatures(); - f.name = "wooden_fence"; - f.drawtype = NDT_FENCELIKE; - f.setInventoryTexture("fence.png"); - f.setTexture(0, "wood.png"); - f.light_propagates = true; - f.param_type = CPT_LIGHT; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.selection_box.type = NODEBOX_FIXED; - f.selection_box.fixed = core::aabbox3d( - -BS/7, -BS/2, -BS/7, BS/7, BS/2, BS/7); - f.furnace_burntime = 30/2; - setWoodLikeMaterialProperties(f.material, 0.75); - nodemgr->set(i, f); - - i = CONTENT_RAIL; - f = ContentFeatures(); - f.name = "rail"; - f.drawtype = NDT_RAILLIKE; - f.setInventoryTexture("rail.png"); - f.setTexture(0, "rail.png"); - f.setTexture(1, "rail_curved.png"); - f.setTexture(2, "rail_t_junction.png"); - f.setTexture(3, "rail_crossing.png"); - f.light_propagates = true; - f.param_type = CPT_LIGHT; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.walkable = false; - f.selection_box.type = NODEBOX_FIXED; - f.furnace_burntime = 5; - setDirtLikeMaterialProperties(f.material, 0.75); - nodemgr->set(i, f); - - i = CONTENT_LADDER; - f = ContentFeatures(); - f.name = "ladder"; - f.drawtype = NDT_SIGNLIKE; - f.setAllTextures("ladder.png"); - f.setInventoryTexture("ladder.png"); - f.light_propagates = true; - f.param_type = CPT_LIGHT; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem ")+itos(i)+" 1"; - f.wall_mounted = true; - f.walkable = false; - f.climbable = true; - f.selection_box.type = NODEBOX_WALLMOUNTED; - f.furnace_burntime = 5; - setWoodLikeMaterialProperties(f.material, 0.5); - - nodemgr->set(i, f); - - i = CONTENT_COALSTONE; - f = ContentFeatures(); - f.name = "coalstone"; - f.setAllTextures("stone.png^mineral_coal.png"); - f.is_ground_content = true; - setStoneLikeMaterialProperties(f.material, 1.5); - nodemgr->set(i, f); - - i = CONTENT_WOOD; - f = ContentFeatures(); - f.name = "wood"; - f.setAllTextures("wood.png"); - f.setInventoryTextureCube("wood.png", "wood.png", "wood.png"); - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.furnace_burntime = 30/4; - setWoodLikeMaterialProperties(f.material, 0.75); - nodemgr->set(i, f); - - i = CONTENT_MESE; - f = ContentFeatures(); - f.name = "mese"; - f.setAllTextures("mese.png"); - f.setInventoryTextureCube("mese.png", "mese.png", "mese.png"); - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.furnace_burntime = 30; - setStoneLikeMaterialProperties(f.material, 0.5); - nodemgr->set(i, f); - - i = CONTENT_CLOUD; - f = ContentFeatures(); - f.name = "cloud"; - f.setAllTextures("cloud.png"); - f.setInventoryTextureCube("cloud.png", "cloud.png", "cloud.png"); - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - nodemgr->set(i, f); - - i = CONTENT_AIR; - f = ContentFeatures(); - f.name = "air"; - f.param_type = CPT_LIGHT; - f.light_propagates = true; - f.sunlight_propagates = true; - f.walkable = false; - f.pointable = false; - f.diggable = false; - f.buildable_to = true; - nodemgr->set(i, f); - - i = CONTENT_WATER; - f = ContentFeatures(); - f.name = "water_flowing"; - f.drawtype = NDT_FLOWINGLIQUID; - f.setAllTextures("water.png"); - f.alpha = WATER_ALPHA; - f.setInventoryTextureCube("water.png", "water.png", "water.png"); - f.param_type = CPT_LIGHT; - f.light_propagates = true; - f.walkable = false; - f.pointable = false; - f.diggable = false; - f.buildable_to = true; - f.liquid_type = LIQUID_FLOWING; - f.liquid_alternative_flowing = "water_flowing"; - f.liquid_alternative_source = "water_source"; - f.liquid_viscosity = WATER_VISC; - f.post_effect_color = video::SColor(64, 100, 100, 200); - f.setSpecialMaterial(0, MaterialSpec("water.png", false)); - f.setSpecialMaterial(1, MaterialSpec("water.png", true)); - nodemgr->set(i, f); - - i = CONTENT_WATERSOURCE; - f = ContentFeatures(); - f.name = "water_source"; - f.drawtype = NDT_LIQUID; - f.setAllTextures("water.png"); - f.alpha = WATER_ALPHA; - f.setInventoryTextureCube("water.png", "water.png", "water.png"); - f.param_type = CPT_LIGHT; - f.light_propagates = true; - f.walkable = false; - f.pointable = false; - f.diggable = false; - f.buildable_to = true; - f.liquid_type = LIQUID_SOURCE; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.liquid_alternative_flowing = "water_flowing"; - f.liquid_alternative_source = "water_source"; - f.liquid_viscosity = WATER_VISC; - f.post_effect_color = video::SColor(64, 100, 100, 200); - // New-style water source material (mostly unused) - f.setSpecialMaterial(0, MaterialSpec("water.png", false)); - nodemgr->set(i, f); - - i = CONTENT_LAVA; - f = ContentFeatures(); - f.name = "lava_flowing"; - f.drawtype = NDT_FLOWINGLIQUID; - f.setAllTextures("lava.png"); - f.setInventoryTextureCube("lava.png", "lava.png", "lava.png"); - f.param_type = CPT_LIGHT; - f.light_propagates = false; - f.light_source = LIGHT_MAX-1; - f.walkable = false; - f.pointable = false; - f.diggable = false; - f.buildable_to = true; - f.liquid_type = LIQUID_FLOWING; - f.liquid_alternative_flowing = "lava_flowing"; - f.liquid_alternative_source = "lava_source"; - f.liquid_viscosity = LAVA_VISC; - f.damage_per_second = 4*2; - f.post_effect_color = video::SColor(192, 255, 64, 0); - f.setSpecialMaterial(0, MaterialSpec("lava.png", false)); - f.setSpecialMaterial(1, MaterialSpec("lava.png", true)); - nodemgr->set(i, f); - - i = CONTENT_LAVASOURCE; - f = ContentFeatures(); - f.name = "lava_source"; - f.drawtype = NDT_LIQUID; - f.setAllTextures("lava.png"); - f.setInventoryTextureCube("lava.png", "lava.png", "lava.png"); - f.param_type = CPT_LIGHT; - f.light_propagates = false; - f.light_source = LIGHT_MAX-1; - f.walkable = false; - f.pointable = false; - f.diggable = false; - f.buildable_to = true; - f.liquid_type = LIQUID_SOURCE; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.liquid_alternative_flowing = "lava_flowing"; - f.liquid_alternative_source = "lava_source"; - f.liquid_viscosity = LAVA_VISC; - f.damage_per_second = 4*2; - f.post_effect_color = video::SColor(192, 255, 64, 0); - // New-style lava source material (mostly unused) - f.setSpecialMaterial(0, MaterialSpec("lava.png", false)); - f.furnace_burntime = 60; - nodemgr->set(i, f); - - i = CONTENT_TORCH; - f = ContentFeatures(); - f.name = "torch"; - f.drawtype = NDT_TORCHLIKE; - f.setTexture(0, "torch_on_floor.png"); - f.setTexture(1, "torch_on_ceiling.png"); - f.setTexture(2, "torch.png"); - f.setInventoryTexture("torch_on_floor.png"); - f.param_type = CPT_LIGHT; - f.light_propagates = true; - f.sunlight_propagates = true; - f.walkable = false; - f.wall_mounted = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.light_source = LIGHT_MAX-1; - f.selection_box.type = NODEBOX_WALLMOUNTED; - f.selection_box.wall_top = core::aabbox3d( - -BS/10, BS/2-BS/3.333*2, -BS/10, BS/10, BS/2, BS/10); - f.selection_box.wall_bottom = core::aabbox3d( - -BS/10, -BS/2, -BS/10, BS/10, -BS/2+BS/3.333*2, BS/10); - f.selection_box.wall_side = core::aabbox3d( - -BS/2, -BS/3.333, -BS/10, -BS/2+BS/3.333, BS/3.333, BS/10); - setConstantMaterialProperties(f.material, 0.0); - f.furnace_burntime = 4; - nodemgr->set(i, f); - - i = CONTENT_SIGN_WALL; - f = ContentFeatures(); - f.name = "sign_wall"; - f.drawtype = NDT_SIGNLIKE; - f.setAllTextures("sign_wall.png"); - f.setInventoryTexture("sign_wall.png"); - f.param_type = CPT_LIGHT; - f.light_propagates = true; - f.sunlight_propagates = true; - f.walkable = false; - f.wall_mounted = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.metadata_name = "sign"; - setConstantMaterialProperties(f.material, 0.5); - f.selection_box.type = NODEBOX_WALLMOUNTED; - f.furnace_burntime = 10; - nodemgr->set(i, f); - - i = CONTENT_CHEST; - f = ContentFeatures(); - f.name = "chest"; - f.param_type = CPT_FACEDIR_SIMPLE; - f.setAllTextures("chest_side.png"); - f.setTexture(0, "chest_top.png"); - f.setTexture(1, "chest_top.png"); - f.setTexture(5, "chest_front.png"); // Z- - f.setInventoryTexture("chest_top.png"); - //f.setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png"); - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.metadata_name = "chest"; - setWoodLikeMaterialProperties(f.material, 1.0); - f.furnace_burntime = 30; - nodemgr->set(i, f); - - i = CONTENT_LOCKABLE_CHEST; - f = ContentFeatures(); - f.name = "locked_chest"; - f.param_type = CPT_FACEDIR_SIMPLE; - f.setAllTextures("chest_side.png"); - f.setTexture(0, "chest_top.png"); - f.setTexture(1, "chest_top.png"); - f.setTexture(5, "chest_lock.png"); // Z- - f.setInventoryTexture("chest_lock.png"); - //f.setInventoryTextureCube("chest_top.png", "chest_side.png", "chest_side.png"); - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.metadata_name = "locked_chest"; - setWoodLikeMaterialProperties(f.material, 1.0); - f.furnace_burntime = 30; - nodemgr->set(i, f); - - i = CONTENT_FURNACE; - f = ContentFeatures(); - f.name = "furnace"; - f.param_type = CPT_FACEDIR_SIMPLE; - f.setAllTextures("furnace_side.png"); - f.setTexture(5, "furnace_front.png"); // Z- - f.setInventoryTexture("furnace_front.png"); - //f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.dug_item = std::string("MaterialItem2 ")+itos(CONTENT_COBBLE)+" 6"; - f.metadata_name = "furnace"; - setStoneLikeMaterialProperties(f.material, 3.0); - nodemgr->set(i, f); - - i = CONTENT_COBBLE; - f = ContentFeatures(); - f.name = "cobble"; - f.setAllTextures("cobble.png"); - f.setInventoryTextureCube("cobble.png", "cobble.png", "cobble.png"); - f.param_type = CPT_NONE; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.cookresult_item = std::string("MaterialItem2 ")+itos(CONTENT_STONE)+" 1"; - setStoneLikeMaterialProperties(f.material, 0.9); - nodemgr->set(i, f); - - i = CONTENT_MOSSYCOBBLE; - f = ContentFeatures(); - f.name = "mossycobble"; - f.setAllTextures("mossycobble.png"); - f.setInventoryTextureCube("mossycobble.png", "mossycobble.png", "mossycobble.png"); - f.param_type = CPT_NONE; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setStoneLikeMaterialProperties(f.material, 0.8); - nodemgr->set(i, f); - - i = CONTENT_STEEL; - f = ContentFeatures(); - f.name = "steelblock"; - f.setAllTextures("steel_block.png"); - f.setInventoryTextureCube("steel_block.png", "steel_block.png", - "steel_block.png"); - f.param_type = CPT_NONE; - f.is_ground_content = true; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setStoneLikeMaterialProperties(f.material, 5.0); - nodemgr->set(i, f); - - i = CONTENT_NC; - f = ContentFeatures(); - f.name = "nyancat"; - f.param_type = CPT_FACEDIR_SIMPLE; - f.setAllTextures("nc_side.png"); - f.setTexture(5, "nc_front.png"); // Z- - f.setTexture(4, "nc_back.png"); // Z+ - f.setInventoryTexture("nc_front.png"); - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setStoneLikeMaterialProperties(f.material, 3.0); - f.furnace_burntime = 1; - nodemgr->set(i, f); - - i = CONTENT_NC_RB; - f = ContentFeatures(); - f.name = "nyancat_rainbow"; - f.setAllTextures("nc_rb.png"); - f.setInventoryTexture("nc_rb.png"); - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - setStoneLikeMaterialProperties(f.material, 3.0); - f.furnace_burntime = 1; - nodemgr->set(i, f); - - i = CONTENT_SAPLING; - f = ContentFeatures(); - f.name = "sapling"; - f.drawtype = NDT_PLANTLIKE; - f.visual_scale = 1.0; - f.setAllTextures("sapling.png"); - f.setInventoryTexture("sapling.png"); - f.param_type = CPT_LIGHT; - f.dug_item = std::string("MaterialItem2 ")+itos(i)+" 1"; - f.light_propagates = true; - f.walkable = false; - setConstantMaterialProperties(f.material, 0.0); - f.furnace_burntime = 10; - nodemgr->set(i, f); - - i = CONTENT_APPLE; - f = ContentFeatures(); - f.name = "apple"; - f.drawtype = NDT_PLANTLIKE; - f.visual_scale = 1.0; - f.setAllTextures("apple.png"); - f.setInventoryTexture("apple.png"); - f.param_type = CPT_LIGHT; - f.light_propagates = true; - f.sunlight_propagates = true; - f.walkable = false; - f.dug_item = std::string("CraftItem apple 1"); - setConstantMaterialProperties(f.material, 0.0); - f.furnace_burntime = 3; - nodemgr->set(i, f); -} - - diff --git a/src/content_mapnode.h b/src/content_mapnode.h index b928e4407..003b7edd7 100644 --- a/src/content_mapnode.h +++ b/src/content_mapnode.h @@ -26,12 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., Legacy node definitions */ -class IWritableNodeDefManager; - -// Initialize legacy node definitions -// Not used used anywhere else than in test.cpp (and SHALL NOT BE) -void content_mapnode_init(IWritableNodeDefManager *nodemgr); - // Backwards compatibility for non-extended content types in v19 extern content_t trans_table_19[21][2]; MapNode mapnode_translate_from_internal(MapNode n_from, u8 version); diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp index 8666051a4..9fb5450cf 100644 --- a/src/content_nodemeta.cpp +++ b/src/content_nodemeta.cpp @@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventory.h" #include "log.h" #include "utility.h" +#include "craftdef.h" +#include "gamedef.h" class Inventory; @@ -125,9 +127,14 @@ public: virtual bool step(float dtime); virtual bool nodeRemovalDisabled(); virtual std::string getInventoryDrawSpecString(); + +protected: + bool getCookResult(bool remove, std::string &cookresult, float &cooktime); + bool getBurnResult(bool remove, float &burntime); private: Inventory *m_inventory; + std::string m_infotext; float m_step_accumulator; float m_fuel_totaltime; float m_fuel_time; @@ -185,9 +192,7 @@ ChestNodeMetadata::ChestNodeMetadata(IGameDef *gamedef): NodeMetadata(gamedef) { NodeMetadata::registerType(typeId(), typeName(), create, create); - - m_inventory = new Inventory(); - m_inventory->addList("0", 8*4); + m_inventory = NULL; } ChestNodeMetadata::~ChestNodeMetadata() { @@ -200,18 +205,21 @@ u16 ChestNodeMetadata::typeId() const NodeMetadata* ChestNodeMetadata::create(std::istream &is, IGameDef *gamedef) { ChestNodeMetadata *d = new ChestNodeMetadata(gamedef); - d->m_inventory->deSerialize(is, gamedef); + d->m_inventory = new Inventory(gamedef->idef()); + d->m_inventory->deSerialize(is); return d; } NodeMetadata* ChestNodeMetadata::create(IGameDef *gamedef) { ChestNodeMetadata *d = new ChestNodeMetadata(gamedef); + d->m_inventory = new Inventory(gamedef->idef()); + d->m_inventory->addList("0", 8*4); return d; } NodeMetadata* ChestNodeMetadata::clone(IGameDef *gamedef) { ChestNodeMetadata *d = new ChestNodeMetadata(gamedef); - *d->m_inventory = *m_inventory; + d->m_inventory = new Inventory(*m_inventory); return d; } void ChestNodeMetadata::serializeBody(std::ostream &os) @@ -253,9 +261,7 @@ LockingChestNodeMetadata::LockingChestNodeMetadata(IGameDef *gamedef): NodeMetadata(gamedef) { NodeMetadata::registerType(typeId(), typeName(), create, create); - - m_inventory = new Inventory(); - m_inventory->addList("0", 8*4); + m_inventory = NULL; } LockingChestNodeMetadata::~LockingChestNodeMetadata() { @@ -269,18 +275,21 @@ NodeMetadata* LockingChestNodeMetadata::create(std::istream &is, IGameDef *gamed { LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef); d->setOwner(deSerializeString(is)); - d->m_inventory->deSerialize(is, gamedef); + d->m_inventory = new Inventory(gamedef->idef()); + d->m_inventory->deSerialize(is); return d; } NodeMetadata* LockingChestNodeMetadata::create(IGameDef *gamedef) { LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef); + d->m_inventory = new Inventory(gamedef->idef()); + d->m_inventory->addList("0", 8*4); return d; } NodeMetadata* LockingChestNodeMetadata::clone(IGameDef *gamedef) { LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef); - *d->m_inventory = *m_inventory; + d->m_inventory = new Inventory(*m_inventory); return d; } void LockingChestNodeMetadata::serializeBody(std::ostream &os) @@ -324,10 +333,9 @@ FurnaceNodeMetadata::FurnaceNodeMetadata(IGameDef *gamedef): { NodeMetadata::registerType(typeId(), typeName(), create, create); - m_inventory = new Inventory(); - m_inventory->addList("fuel", 1); - m_inventory->addList("src", 1); - m_inventory->addList("dst", 4); + m_inventory = NULL; + + m_infotext = "Furnace is inactive"; m_step_accumulator = 0; m_fuel_totaltime = 0; @@ -346,26 +354,52 @@ u16 FurnaceNodeMetadata::typeId() const NodeMetadata* FurnaceNodeMetadata::clone(IGameDef *gamedef) { FurnaceNodeMetadata *d = new FurnaceNodeMetadata(m_gamedef); - *d->m_inventory = *m_inventory; + d->m_inventory = new Inventory(*m_inventory); return d; } NodeMetadata* FurnaceNodeMetadata::create(std::istream &is, IGameDef *gamedef) { FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef); - d->m_inventory->deSerialize(is, gamedef); + d->m_inventory = new Inventory(gamedef->idef()); + d->m_inventory->deSerialize(is); - int temp; + int temp = 0; is>>temp; d->m_fuel_totaltime = (float)temp/10; + temp = 0; is>>temp; d->m_fuel_time = (float)temp/10; + temp = 0; + is>>temp; + d->m_src_totaltime = (float)temp/10; + temp = 0; + is>>temp; + d->m_src_time = (float)temp/10; + + if(is.eof()) + { + // Old furnaces didn't serialize src_totaltime and src_time + d->m_src_totaltime = 0; + d->m_src_time = 0; + d->m_infotext = ""; + } + else + { + // New furnaces also serialize the infotext (so that the + // client doesn't need to have the list of cooking recipes). + d->m_infotext = deSerializeJsonString(is); + } return d; } NodeMetadata* FurnaceNodeMetadata::create(IGameDef *gamedef) { FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef); + d->m_inventory = new Inventory(gamedef->idef()); + d->m_inventory->addList("fuel", 1); + d->m_inventory->addList("src", 1); + d->m_inventory->addList("dst", 4); return d; } void FurnaceNodeMetadata::serializeBody(std::ostream &os) @@ -373,36 +407,13 @@ void FurnaceNodeMetadata::serializeBody(std::ostream &os) m_inventory->serialize(os); os<= m_fuel_totaltime) - { - const InventoryList *src_list = m_inventory->getList("src"); - assert(src_list); - const InventoryItem *src_item = src_list->getItem(0); - - if(src_item && src_item->isCookable()) { - InventoryList *dst_list = m_inventory->getList("dst"); - if(!dst_list->roomForCookedItem(src_item)) - return "Furnace is overloaded"; - return "Furnace is out of fuel"; - } - else - return "Furnace is inactive"; - } - else - { - std::string s = "Furnace is active"; - // Do this so it doesn't always show (0%) for weak fuel - if(m_fuel_totaltime > 3) { - s += " ("; - s += itos(m_fuel_time/m_fuel_totaltime*100); - s += "%)"; - } - return s; - } + return m_infotext; } bool FurnaceNodeMetadata::nodeRemovalDisabled() { @@ -440,86 +451,107 @@ bool FurnaceNodeMetadata::step(float dtime) dtime = interval; //infostream<<"Furnace step dtime="<getList("dst"); assert(dst_list); - InventoryList *src_list = m_inventory->getList("src"); - assert(src_list); - InventoryItem *src_item = src_list->getItem(0); - + // Check + // 1. if the source item is cookable + // 2. if there is room for the cooked item + std::string cookresult; + float cooktime; + bool cookable = getCookResult(false, cookresult, cooktime); + ItemStack cookresult_item; bool room_available = false; - - if(src_item && src_item->isCookable()) - room_available = dst_list->roomForCookedItem(src_item); - - // Start only if there are free slots in dst, so that it can - // accomodate any result item - if(room_available) + if(cookable) { - m_src_totaltime = src_item->getCookTime(); + cookresult_item.deSerialize(cookresult, m_gamedef->idef()); + room_available = dst_list->roomForItem(cookresult_item); } - else + + // Step fuel time + bool burning = (m_fuel_time < m_fuel_totaltime); + if(burning) { - m_src_time = 0; - m_src_totaltime = 0; + changed = true; + m_fuel_time += dtime; } - - /* - If fuel is burning, increment the burn counters. - If item finishes cooking, move it to result. - */ - if(m_fuel_time < m_fuel_totaltime) + + std::string infotext; + if(room_available) { - //infostream<<"Furnace is active"<= m_src_totaltime && m_src_totaltime > 0.001 - && src_item) + float burntime; + if(burning) + { + changed = true; + m_src_time += dtime; + m_src_totaltime = cooktime; + infotext = "Furnace is cooking"; + } + else if(getBurnResult(true, burntime)) + { + // Fuel inserted + changed = true; + m_fuel_time = 0; + m_fuel_totaltime = burntime; + //m_src_time += dtime; + //m_src_totaltime = cooktime; + infotext = "Furnace is cooking"; + } + else { - InventoryItem *cookresult = src_item->createCookResult(); - dst_list->addItem(cookresult); - src_list->decrementMaterials(1); m_src_time = 0; m_src_totaltime = 0; + infotext = "Furnace is out of fuel"; + } + if(m_src_totaltime > 0.001 && m_src_time >= m_src_totaltime) + { + // One item fully cooked + changed = true; + dst_list->addItem(cookresult_item); + getCookResult(true, cookresult, cooktime); // decrement source + m_src_totaltime = 0; + m_src_time = 0; } - changed = true; - - // If the fuel was not used up this step, just keep burning it - if(m_fuel_time < m_fuel_totaltime) - continue; } - - /* - Get the source again in case it has all burned - */ - src_item = src_list->getItem(0); - - /* - If there is no source item, or the source item is not cookable, - or the furnace is still cooking, or the furnace became overloaded, stop loop. - */ - if(src_item == NULL || !room_available || m_fuel_time < m_fuel_totaltime || - dst_list->roomForCookedItem(src_item) == false) + else { - m_step_accumulator = 0; - break; + // Not cookable or no room available + m_src_totaltime = 0; + m_src_time = 0; + if(cookable) + infotext = "Furnace is overloaded"; + else if(burning) + infotext = "Furnace is active"; + else + { + infotext = "Furnace is inactive"; + m_fuel_totaltime = 0; + m_fuel_time = 0; + } + } + + // Do this so it doesn't always show (0%) for weak fuel + if(m_fuel_totaltime > 3) { + infotext += " ("; + infotext += itos(m_fuel_time/m_fuel_totaltime*100); + infotext += "%)"; } - - //infostream<<"Furnace is out of fuel"<getList("fuel"); - assert(fuel_list); - const InventoryItem *fuel_item = fuel_list->getItem(0); + if(infotext != m_infotext) + { + m_infotext = infotext; + changed = true; + } - if(fuel_item && fuel_item->getBurnTime() >= 0){ - m_fuel_totaltime = fuel_item->getBurnTime(); + if(burning && m_fuel_time >= m_fuel_totaltime) + { m_fuel_time = 0; - fuel_list->decrementMaterials(1); - changed = true; - } else { - //infostream<<"No fuel found"< items; + InventoryList *src_list = m_inventory->getList("src"); + assert(src_list); + items.push_back(src_list->getItem(0)); + + CraftInput ci(CRAFT_METHOD_COOKING, 1, items); + CraftOutput co; + bool found = m_gamedef->getCraftDefManager()->getCraftResult( + ci, co, remove, m_gamedef); + if(remove) + src_list->changeItem(0, ci.items[0]); + + cookresult = co.item; + cooktime = co.time; + return found; +} +bool FurnaceNodeMetadata::getBurnResult(bool remove, float &burntime) +{ + std::vector items; + InventoryList *fuel_list = m_inventory->getList("fuel"); + assert(fuel_list); + items.push_back(fuel_list->getItem(0)); + + CraftInput ci(CRAFT_METHOD_FUEL, 1, items); + CraftOutput co; + bool found = m_gamedef->getCraftDefManager()->getCraftResult( + ci, co, remove, m_gamedef); + if(remove) + fuel_list->changeItem(0, ci.items[0]); + + burntime = co.time; + return found; +} + /* GenericNodeMetadata @@ -571,7 +640,7 @@ public: GenericNodeMetadata(IGameDef *gamedef): NodeMetadata(gamedef), - m_inventory(new Inventory()), + m_inventory(NULL), m_text(""), m_owner(""), @@ -594,7 +663,7 @@ public: { GenericNodeMetadata *d = new GenericNodeMetadata(m_gamedef); - *d->m_inventory = *m_inventory; + d->m_inventory = new Inventory(*m_inventory); d->m_text = m_text; d->m_owner = m_owner; @@ -610,13 +679,15 @@ public: static NodeMetadata* create(IGameDef *gamedef) { GenericNodeMetadata *d = new GenericNodeMetadata(gamedef); + d->m_inventory = new Inventory(gamedef->idef()); return d; } static NodeMetadata* create(std::istream &is, IGameDef *gamedef) { GenericNodeMetadata *d = new GenericNodeMetadata(gamedef); - d->m_inventory->deSerialize(is, gamedef); + d->m_inventory = new Inventory(gamedef->idef()); + d->m_inventory->deSerialize(is); d->m_text = deSerializeLongString(is); d->m_owner = deSerializeString(is); diff --git a/src/content_sao.cpp b/src/content_sao.cpp index f195b16bd..02be64c64 100644 --- a/src/content_sao.cpp +++ b/src/content_sao.cpp @@ -24,8 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_profiler #include "profiler.h" #include "serialization.h" // For compressZlib -#include "materials.h" // For MaterialProperties -#include "tooldef.h" // ToolDiggingProperties +#include "materials.h" // For MaterialProperties and ToolDiggingProperties +#include "gamedef.h" core::map ServerActiveObject::m_types; @@ -114,9 +114,10 @@ void TestSAO::step(float dtime, bool send_recommended) ItemSAO proto_ItemSAO(NULL, v3f(0,0,0), ""); ItemSAO::ItemSAO(ServerEnvironment *env, v3f pos, - const std::string inventorystring): + const std::string itemstring): ServerActiveObject(env, pos), - m_inventorystring(inventorystring), + m_itemstring(itemstring), + m_itemstring_changed(false), m_speed_f(0,0,0), m_last_sent_position(0,0,0) { @@ -134,10 +135,10 @@ ServerActiveObject* ItemSAO::create(ServerEnvironment *env, v3f pos, // check if version is supported if(version != 0) return NULL; - std::string inventorystring = deSerializeString(is); + std::string itemstring = deSerializeString(is); infostream<<"ItemSAO::create(): Creating item \"" - <getGameDef(); - InventoryItem *item = InventoryItem::deSerialize(is, gamedef); - infostream<<__FUNCTION_NAME<<": m_inventorystring=\"" - < item="<getGameDef()->idef(); + ItemStack item; + item.deSerialize(m_itemstring, idef); + infostream<<__FUNCTION_NAME<<": m_itemstring=\""< item=\""<addToInventory(item); - if(fits) + // Allow removing items in creative mode + if(g_settings->getBool("creative_mode") == true) + { m_removed = true; - else - delete item; + return; + } + + ItemStack item = createItemStack(); + Inventory *inv = puncher->getInventory(); + if(inv != NULL) + { + std::string wieldlist = puncher->getWieldList(); + ItemStack leftover = inv->addItem(wieldlist, item); + puncher->setInventoryModified(); + if(leftover.empty()) + { + m_removed = true; + } + else + { + m_itemstring = leftover.getItemString(); + m_itemstring_changed = true; + } + } } /* @@ -436,14 +452,24 @@ std::string RatSAO::getStaticData() void RatSAO::punch(ServerActiveObject *puncher, float time_from_last_punch) { - std::istringstream is("CraftItem rat 1", std::ios_base::binary); - IGameDef *gamedef = m_env->getGameDef(); - InventoryItem *item = InventoryItem::deSerialize(is, gamedef); - bool fits = puncher->addToInventory(item); - if(fits) + // Allow removing rats in creative mode + if(g_settings->getBool("creative_mode") == true) + { m_removed = true; - else - delete item; + return; + } + + IItemDefManager *idef = m_env->getGameDef()->idef(); + ItemStack item("rat", 1, 0, "", idef); + Inventory *inv = puncher->getInventory(); + if(inv != NULL) + { + std::string wieldlist = puncher->getWieldList(); + ItemStack leftover = inv->addItem(wieldlist, item); + puncher->setInventoryModified(); + if(leftover.empty()) + m_removed = true; + } } /* @@ -703,14 +729,20 @@ void Oerkki1SAO::punch(ServerActiveObject *puncher, float time_from_last_punch) mp.crackiness = -1.0; mp.cuttability = 1.0; - ToolDiggingProperties tp; - puncher->getWieldDiggingProperties(&tp); + IItemDefManager *idef = m_env->getGameDef()->idef(); + ItemStack punchitem = puncher->getWieldedItem(); + ToolDiggingProperties tp = + punchitem.getToolDiggingProperties(idef); HittingProperties hitprop = getHittingProperties(&mp, &tp, time_from_last_punch); doDamage(hitprop.hp); - puncher->damageWieldedItem(hitprop.wear); + if(g_settings->getBool("creative_mode") == false) + { + punchitem.addWear(hitprop.wear, idef); + puncher->setWieldedItem(punchitem); + } } void Oerkki1SAO::doDamage(u16 d) @@ -1393,14 +1425,20 @@ void MobV2SAO::punch(ServerActiveObject *puncher, float time_from_last_punch) mp.crackiness = -1.0; mp.cuttability = 1.0; - ToolDiggingProperties tp; - puncher->getWieldDiggingProperties(&tp); + IItemDefManager *idef = m_env->getGameDef()->idef(); + ItemStack punchitem = puncher->getWieldedItem(); + ToolDiggingProperties tp = + punchitem.getToolDiggingProperties(idef); HittingProperties hitprop = getHittingProperties(&mp, &tp, time_from_last_punch); doDamage(hitprop.hp); - puncher->damageWieldedItem(hitprop.wear); + if(g_settings->getBool("creative_mode") == false) + { + punchitem.addWear(hitprop.wear, idef); + puncher->setWieldedItem(punchitem); + } } bool MobV2SAO::isPeaceful() diff --git a/src/content_sao.h b/src/content_sao.h index c2bb9c3f5..f0c9cea90 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -40,8 +40,7 @@ private: class ItemSAO : public ServerActiveObject { public: - ItemSAO(ServerEnvironment *env, v3f pos, - const std::string inventorystring); + ItemSAO(ServerEnvironment *env, v3f pos, const std::string itemstring); u8 getType() const {return ACTIVEOBJECT_TYPE_ITEM;} static ServerActiveObject* create(ServerEnvironment *env, v3f pos, @@ -49,11 +48,12 @@ public: void step(float dtime, bool send_recommended); std::string getClientInitializationData(); std::string getStaticData(); - InventoryItem* createInventoryItem(); + ItemStack createItemStack(); void punch(ServerActiveObject *puncher, float time_from_last_punch); float getMinimumSavedMovement(){ return 0.1*BS; } private: - std::string m_inventorystring; + std::string m_itemstring; + bool m_itemstring_changed; v3f m_speed_f; v3f m_last_sent_position; IntervalLimiter m_move_interval; diff --git a/src/craftdef.cpp b/src/craftdef.cpp index 0cbb74ea0..5bcbf6f94 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -22,88 +22,738 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include "log.h" #include +#include #include "utility.h" #include "gamedef.h" #include "inventory.h" -#include "inventorymanager.h" // checkItemCombination -CraftPointerInput::~CraftPointerInput() + +// Deserialize an itemstring then return the name of the item +static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef) +{ + ItemStack item; + item.deSerialize(itemstring, gamedef->idef()); + return item.name; +} + +// (mapcar craftGetItemName itemstrings) +static std::vector craftGetItemNames( + const std::vector &itemstrings, IGameDef *gamedef) +{ + std::vector result; + for(std::vector::const_iterator + i = itemstrings.begin(); + i != itemstrings.end(); i++) + { + result.push_back(craftGetItemName(*i, gamedef)); + } + return result; +} + +// Get name of each item, and return them as a new list. +static std::vector craftGetItemNames( + const std::vector &items, IGameDef *gamedef) { - for(u32 i=0; i result; + for(std::vector::const_iterator + i = items.begin(); + i != items.end(); i++) + { + result.push_back(i->name); + } + return result; } -CraftPointerInput createPointerInput(const CraftInput &ci, IGameDef *gamedef) +// Compute bounding rectangle given a matrix of items +// Returns false if every item is "" +static bool craftGetBounds(const std::vector &items, unsigned int width, + unsigned int &min_x, unsigned int &max_x, + unsigned int &min_y, unsigned int &max_y) { - std::vector items; - for(u32 i=0; i::const_iterator + i = items.begin(); + i != items.end(); i++) + { + if(*i != "") // Is this an actual item? + { + if(!success) + { + // This is the first nonempty item + min_x = max_x = x; + min_y = max_y = y; + success = true; + } + else + { + if(x < min_x) min_x = x; + if(x > max_x) max_x = x; + if(y < min_y) min_y = y; + if(y > max_y) max_y = y; + } } - items.push_back(item); + + // Step coordinate + x++; + if(x == width) + { + x = 0; + y++; + } + } + return success; +} + +// Convert a list of item names to a multiset +static std::multiset craftMakeMultiset(const std::vector &names) +{ + std::multiset set; + for(std::vector::const_iterator + i = names.begin(); + i != names.end(); i++) + { + if(*i != "") + set.insert(*i); } - return CraftPointerInput(ci.width, items); + return set; } -CraftInput createInput(const CraftPointerInput &cpi) +// Removes 1 from each item stack +static void craftDecrementInput(CraftInput &input, IGameDef *gamedef) { - std::vector items; - for(u32 i=0; iserialize(oss); - items.push_back(oss.str()); + for(std::vector::iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(i->count != 0) + i->remove(1); + } +} + +// Removes 1 from each item stack with replacement support +// Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"), +// a water bucket will not be removed but replaced by an empty bucket. +static void craftDecrementOrReplaceInput(CraftInput &input, + const CraftReplacements &replacements, + IGameDef *gamedef) +{ + if(replacements.pairs.empty()) + { + craftDecrementInput(input, gamedef); + return; + } + + // Make a copy of the replacements pair list + std::vector > pairs = replacements.pairs; + + for(std::vector::iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(i->count == 1) + { + // Find an appropriate replacement + bool found_replacement = false; + for(std::vector >::iterator + j = pairs.begin(); + j != pairs.end(); j++) + { + ItemStack from_item; + from_item.deSerialize(j->first, gamedef->idef()); + if(i->name == from_item.name) + { + i->deSerialize(j->second, gamedef->idef()); + found_replacement = true; + pairs.erase(j); + break; + } + } + // No replacement was found, simply decrement count to zero + if(!found_replacement) + i->remove(1); + } + else if(i->count >= 2) + { + // Ignore replacements for items with count >= 2 + i->remove(1); + } + } +} + +// Dump an itemstring matrix +static std::string craftDumpMatrix(const std::vector &items, + unsigned int width) +{ + std::ostringstream os(std::ios::binary); + os<<"{ "; + unsigned int x = 0; + for(std::vector::const_iterator + i = items.begin(); + i != items.end(); i++, x++) + { + if(x == width) + { + os<<"; "; + x = 0; + } + else if(x != 0) + { + os<<","; + } + os<<"\""<<(*i)<<"\""; + } + os<<" }"; + return os.str(); +} + +// Dump an item matrix +std::string craftDumpMatrix(const std::vector &items, + unsigned int width) +{ + std::ostringstream os(std::ios::binary); + os<<"{ "; + unsigned int x = 0; + for(std::vector::const_iterator + i = items.begin(); + i != items.end(); i++, x++) + { + if(x == width) + { + os<<"; "; + x = 0; + } + else if(x != 0) + { + os<<","; } + os<<"\""<<(i->getItemString())<<"\""; } - return CraftInput(cpi.width, items); + os<<" }"; + return os.str(); } + +/* + CraftInput +*/ + std::string CraftInput::dump() const { std::ostringstream os(std::ios::binary); - os<<"(width="< >::const_iterator + i = pairs.begin(); + i != pairs.end(); i++) + { + os<first)<<"\"=>\""<<(i->second)<<"\""; + sep = ","; + } + os<<"}"; + return os.str(); +} + + +/* + CraftDefinition +*/ + void CraftDefinition::serialize(std::ostream &os) const { - writeU8(os, 0); // version - os<deSerializeBody(is, version); + return def; +} + +/* + CraftDefinitionShaped +*/ + +std::string CraftDefinitionShaped::getName() const +{ + return "shaped"; +} + +bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const +{ + if(input.method != CRAFT_METHOD_NORMAL) + return false; + + // Get input item matrix + std::vector inp_names = craftGetItemNames(input.items, gamedef); + unsigned int inp_width = input.width; + if(inp_width == 0) + return false; + while(inp_names.size() % inp_width != 0) + inp_names.push_back(""); + + // Get input bounds + unsigned int inp_min_x=0, inp_max_x=0, inp_min_y=0, inp_max_y=0; + if(!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x, inp_min_y, inp_max_y)) + return false; // it was empty + + // Get recipe item matrix + std::vector rec_names = craftGetItemNames(recipe, gamedef); + unsigned int rec_width = width; + if(rec_width == 0) + return false; + while(rec_names.size() % rec_width != 0) + rec_names.push_back(""); + + // Get recipe bounds + unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0; + if(!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x, rec_min_y, rec_max_y)) + return false; // it was empty + + // Different sizes? + if(inp_max_x - inp_min_x != rec_max_x - rec_min_x) + return false; + if(inp_max_y - inp_min_y != rec_max_y - rec_min_y) + return false; + + // Verify that all item names in the bounding box are equal + unsigned int w = inp_max_x - inp_min_x + 1; + unsigned int h = inp_max_y - inp_min_y + 1; + for(unsigned int y=0; y inp_names = craftGetItemNames(input.items, gamedef); + std::multiset inp_names_multiset = craftMakeMultiset(inp_names); + + // Get recipe item multiset + std::vector rec_names = craftGetItemNames(recipe, gamedef); + std::multiset rec_names_multiset = craftMakeMultiset(rec_names); + + // Recipe is matched when the multisets coincide + return inp_names_multiset == rec_names_multiset; +} + +CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + return CraftOutput(output, 0); +} + +void CraftDefinitionShapeless::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementOrReplaceInput(input, replacements, gamedef); +} + +std::string CraftDefinitionShapeless::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(shapeless, output=\""<idef(); + if(item1.count != 1 || item2.count != 1 || item1.name != item2.name + || idef->get(item1.name).type != ITEM_TOOL + || idef->get(item2.name).type != ITEM_TOOL) + { + // Failure + return ItemStack(); + } + + s32 item1_uses = 65536 - (u32) item1.wear; + s32 item2_uses = 65536 - (u32) item2.wear; + s32 new_uses = item1_uses + item2_uses; + s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5); + if(new_wear >= 65536) + return ItemStack(); + if(new_wear < 0) + new_wear = 0; + + ItemStack repaired = item1; + repaired.wear = new_wear; + return repaired; +} + +std::string CraftDefinitionToolRepair::getName() const +{ + return "toolrepair"; +} + +bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const +{ + if(input.method != CRAFT_METHOD_NORMAL) + return false; + + ItemStack item1; + ItemStack item2; + for(std::vector::const_iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(!i->empty()) + { + if(item1.empty()) + item1 = *i; + else if(item2.empty()) + item2 = *i; + else + return false; + } + } + ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef); + return !repaired.empty(); +} + +CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + ItemStack item1; + ItemStack item2; + for(std::vector::const_iterator + i = input.items.begin(); + i != input.items.end(); i++) + { + if(!i->empty()) + { + if(item1.empty()) + item1 = *i; + else if(item2.empty()) + item2 = *i; + } + } + ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef); + return CraftOutput(repaired.getItemString(), 0); +} + +void CraftDefinitionToolRepair::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementInput(input, gamedef); +} + +std::string CraftDefinitionToolRepair::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(toolrepair, additional_wear="< inp_names = craftGetItemNames(input.items, gamedef); + std::multiset inp_names_multiset = craftMakeMultiset(inp_names); + + // Get recipe item multiset + std::multiset rec_names_multiset; + rec_names_multiset.insert(craftGetItemName(recipe, gamedef)); + + // Recipe is matched when the multisets coincide + return inp_names_multiset == rec_names_multiset; +} + +CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + return CraftOutput(output, cooktime); +} + +void CraftDefinitionCooking::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementInput(input, gamedef); +} + +std::string CraftDefinitionCooking::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(cooking, output=\""< inp_names = craftGetItemNames(input.items, gamedef); + std::multiset inp_names_multiset = craftMakeMultiset(inp_names); + + // Get recipe item multiset + std::multiset rec_names_multiset; + rec_names_multiset.insert(craftGetItemName(recipe, gamedef)); + + // Recipe is matched when the multisets coincide + return inp_names_multiset == rec_names_multiset; +} + +CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const +{ + return CraftOutput("", burntime); +} + +void CraftDefinitionFuel::decrementInput(CraftInput &input, IGameDef *gamedef) const +{ + craftDecrementInput(input, gamedef); +} + +std::string CraftDefinitionFuel::dump() const +{ + std::ostringstream os(std::ios::binary); + os<<"(fuel, recipe=\""< 3){ - errorstream<<"getCraftResult(): ERROR: " - <<"input_cpi.width > 3; Failing to craft."<::const_iterator + i = input.items.begin(); + i != input.items.end(); i++) { - u32 i=y*3+x; - if(x >= input_cpi.width || y >= input_cpi.height()) - input_items[i] = NULL; - else - input_items[i] = input_cpi.items[y*input_cpi.width+x]; + if(!i->empty()) + { + all_empty = false; + break; + } } - for(core::list::ConstIterator - i = m_craft_definitions.begin(); - i != m_craft_definitions.end(); i++) + if(all_empty) + return false; + + // Walk crafting definitions from back to front, so that later + // definitions can override earlier ones. + for(std::vector::const_reverse_iterator + i = m_craft_definitions.rbegin(); + i != m_craft_definitions.rend(); i++) { CraftDefinition *def = *i; - /*infostream<<"Checking "<input.dump() - <<" (output=\""<output<<"\")"<dump()<input, gamedef); - if(spec_cpi.width > 3){ - errorstream<<"getCraftResult: ERROR: " - <<"spec_cpi.width > 3 in recipe " - <dump()<check(input, gamedef)) { - u32 i=y*3+x; - if(x >= spec_cpi.width || y >= spec_cpi.height()) - spec_items[i] = NULL; - else - spec_items[i] = spec_cpi.items[y*spec_cpi.width+x]; - } - - bool match = checkItemCombination(input_items, spec_items); - - if(match){ - std::istringstream iss(def->output, std::ios::binary); - return InventoryItem::deSerialize(iss, gamedef); + // Get output, then decrement input (if requested) + output = def->getOutput(input, gamedef); + if(decrementInput) + def->decrementInput(input, gamedef); + return true; } } catch(SerializationError &e) @@ -173,34 +811,41 @@ public: // then go on with the next craft definition } } - return NULL; + return false; } - virtual void registerCraft(const CraftDefinition &def) + virtual std::string dump() const { - infostream<<"registerCraft: registering craft definition: " - < 3 || def.input.height() > 3){ - errorstream<<"registerCraft: input size is larger than 3x3," - <<" ignoring"<::const_iterator + i = m_craft_definitions.begin(); + i != m_craft_definitions.end(); i++) + { + os<<(*i)->dump()<<"\n"; } - m_craft_definitions.push_back(new CraftDefinition(def)); + return os.str(); + } + virtual void registerCraft(CraftDefinition *def) + { + infostream<<"registerCraft: registering craft definition: " + <dump()<::Iterator + for(std::vector::iterator i = m_craft_definitions.begin(); i != m_craft_definitions.end(); i++){ delete *i; } m_craft_definitions.clear(); } - virtual void serialize(std::ostream &os) + virtual void serialize(std::ostream &os) const { writeU8(os, 0); // version u16 count = m_craft_definitions.size(); writeU16(os, count); - for(core::list::Iterator + for(std::vector::const_iterator i = m_craft_definitions.begin(); i != m_craft_definitions.end(); i++){ CraftDefinition *def = *i; @@ -222,14 +867,13 @@ public: for(u16 i=0; i m_craft_definitions; + std::vector m_craft_definitions; }; IWritableCraftDefManager* createCraftDefManager() diff --git a/src/craftdef.h b/src/craftdef.h index cfd58ad10..57f26f049 100644 --- a/src/craftdef.h +++ b/src/craftdef.h @@ -23,71 +23,328 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -class IGameDef; -class InventoryItem; +#include +#include "gamedef.h" +#include "inventory.h" -struct CraftPointerInput +/* + Crafting methods. + + The crafting method depends on the inventory list + that the crafting input comes from. +*/ +enum CraftMethod +{ + // Crafting grid + CRAFT_METHOD_NORMAL, + // Cooking something in a furnace + CRAFT_METHOD_COOKING, + // Using something as fuel for a furnace + CRAFT_METHOD_FUEL, +}; + +/* + Input: The contents of the crafting slots, arranged in matrix form +*/ +struct CraftInput { + CraftMethod method; unsigned int width; - std::vector items; + std::vector items; - CraftPointerInput(unsigned int width_, const std::vector &items_): - width(width_), - items(items_) + CraftInput(): + method(CRAFT_METHOD_NORMAL), width(0), items() {} - CraftPointerInput(): - width(0) + CraftInput(CraftMethod method_, unsigned int width_, + const std::vector &items_): + method(method_), width(width_), items(items_) {} - ~CraftPointerInput(); - unsigned int height() const{ - return (items.size() + width - 1) / width; - } + std::string dump() const; }; -struct CraftInput +/* + Output: Result of crafting operation +*/ +struct CraftOutput { - unsigned int width; - std::vector items; + // Used for normal crafting and cooking, itemstring + std::string item; + // Used for cooking (cook time) and fuel (burn time), seconds + float time; - CraftInput(unsigned int width_, const std::vector &items_): - width(width_), - items(items_) + CraftOutput(): + item(""), time(0) {} - CraftInput(): - width(0) + CraftOutput(std::string item_, float time_): + item(item_), time(time_) {} - unsigned int height() const{ - return (items.size() + width - 1) / width; - } std::string dump() const; }; -struct CraftDefinition +/* + A list of replacements. A replacement indicates that a specific + input item should not be deleted (when crafting) but replaced with + a different item. Each replacements is a pair (itemstring to remove, + itemstring to replace with) + + Example: If ("bucket:bucket_water", "bucket:bucket_empty") is a + replacement pair, the crafting input slot that contained a water + bucket will contain an empty bucket after crafting. + + Note: replacements only work correctly when stack_max of the item + to be replaced is 1. It is up to the mod writer to ensure this. +*/ +struct CraftReplacements { - std::string output; - CraftInput input; + // List of replacements + std::vector > pairs; - CraftDefinition(){} - CraftDefinition(const std::string &output_, unsigned int width_, - const std::vector &input_): - output(output_), - input(width_, input_) + CraftReplacements(): + pairs() + {} + CraftReplacements(std::vector > pairs_): + pairs(pairs_) {} - std::string dump() const; +}; + +/* + Crafting definition base class +*/ +class CraftDefinition +{ +public: + CraftDefinition(){} + virtual ~CraftDefinition(){} + void serialize(std::ostream &os) const; - void deSerialize(std::istream &is); + static CraftDefinition* deSerialize(std::istream &is); + + // Returns type of crafting definition + virtual std::string getName() const=0; + + // Checks whether the recipe is applicable + virtual bool check(const CraftInput &input, IGameDef *gamedef) const=0; + // Returns the output structure, meaning depends on crafting method + // The implementation can assume that check(input) returns true + virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const=0; + // Decreases count of every input item + virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const=0; + + virtual std::string dump() const=0; + +protected: + virtual void serializeBody(std::ostream &os) const=0; + virtual void deSerializeBody(std::istream &is, int version)=0; +}; + +/* + A plain-jane (shaped) crafting definition + + Supported crafting method: CRAFT_METHOD_NORMAL. + Requires the input items to be arranged exactly like in the recipe. +*/ +class CraftDefinitionShaped: public CraftDefinition +{ +public: + CraftDefinitionShaped(): + output(""), width(1), recipe(), replacements() + {} + CraftDefinitionShaped( + const std::string &output_, + unsigned int width_, + const std::vector &recipe_, + const CraftReplacements &replacements_): + output(output_), width(width_), recipe(recipe_), replacements(replacements_) + {} + virtual ~CraftDefinitionShaped(){} + + virtual std::string getName() const; + virtual bool check(const CraftInput &input, IGameDef *gamedef) const; + virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + + virtual std::string dump() const; + +protected: + virtual void serializeBody(std::ostream &os) const; + virtual void deSerializeBody(std::istream &is, int version); + +private: + // Output itemstring + std::string output; + // Width of recipe + unsigned int width; + // Recipe matrix (itemstrings) + std::vector recipe; + // Replacement items for decrementInput() + CraftReplacements replacements; +}; + +/* + A shapeless crafting definition + Supported crafting method: CRAFT_METHOD_NORMAL. + Input items can arranged in any way. +*/ +class CraftDefinitionShapeless: public CraftDefinition +{ +public: + CraftDefinitionShapeless(): + output(""), recipe(), replacements() + {} + CraftDefinitionShapeless( + const std::string &output_, + const std::vector &recipe_, + const CraftReplacements &replacements_): + output(output_), recipe(recipe_), replacements(replacements_) + {} + virtual ~CraftDefinitionShapeless(){} + + virtual std::string getName() const; + virtual bool check(const CraftInput &input, IGameDef *gamedef) const; + virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + + virtual std::string dump() const; + +protected: + virtual void serializeBody(std::ostream &os) const; + virtual void deSerializeBody(std::istream &is, int version); + +private: + // Output itemstring + std::string output; + // Recipe list (itemstrings) + std::vector recipe; + // Replacement items for decrementInput() + CraftReplacements replacements; +}; + +/* + Tool repair crafting definition + Supported crafting method: CRAFT_METHOD_NORMAL. + Put two damaged tools into the crafting grid, get one tool back. + There should only be one crafting definition of this type. +*/ +class CraftDefinitionToolRepair: public CraftDefinition +{ +public: + CraftDefinitionToolRepair(): + additional_wear(0) + {} + CraftDefinitionToolRepair(float additional_wear_): + additional_wear(additional_wear_) + {} + virtual ~CraftDefinitionToolRepair(){} + + virtual std::string getName() const; + virtual bool check(const CraftInput &input, IGameDef *gamedef) const; + virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + + virtual std::string dump() const; + +protected: + virtual void serializeBody(std::ostream &os) const; + virtual void deSerializeBody(std::istream &is, int version); + +private: + // This is a constant that is added to the wear of the result. + // May be positive or negative, allowed range [-1,1]. + // 1 = new tool is completely broken + // 0 = simply add remaining uses of both input tools + // -1 = new tool is completely pristine + float additional_wear; +}; + +/* + A cooking (in furnace) definition + Supported crafting method: CRAFT_METHOD_COOKING. +*/ +class CraftDefinitionCooking: public CraftDefinition +{ +public: + CraftDefinitionCooking(): + output(""), recipe(""), cooktime() + {} + CraftDefinitionCooking( + const std::string &output_, + const std::string &recipe_, + float cooktime_): + output(output_), recipe(recipe_), cooktime(cooktime_) + {} + virtual ~CraftDefinitionCooking(){} + + virtual std::string getName() const; + virtual bool check(const CraftInput &input, IGameDef *gamedef) const; + virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + + virtual std::string dump() const; + +protected: + virtual void serializeBody(std::ostream &os) const; + virtual void deSerializeBody(std::istream &is, int version); + +private: + // Output itemstring + std::string output; + // Recipe itemstring + std::string recipe; + // Time in seconds + float cooktime; }; +/* + A fuel (for furnace) definition + Supported crafting method: CRAFT_METHOD_FUEL. +*/ +class CraftDefinitionFuel: public CraftDefinition +{ +public: + CraftDefinitionFuel(): + recipe(""), burntime() + {} + CraftDefinitionFuel(std::string recipe_, float burntime_): + recipe(recipe_), burntime(burntime_) + {} + virtual ~CraftDefinitionFuel(){} + + virtual std::string getName() const; + virtual bool check(const CraftInput &input, IGameDef *gamedef) const; + virtual CraftOutput getOutput(const CraftInput &input, IGameDef *gamedef) const; + virtual void decrementInput(CraftInput &input, IGameDef *gamedef) const; + + virtual std::string dump() const; + +protected: + virtual void serializeBody(std::ostream &os) const; + virtual void deSerializeBody(std::istream &is, int version); + +private: + // Recipe itemstring + std::string recipe; + // Time in seconds + float burntime; +}; + +/* + Crafting definition manager +*/ class ICraftDefManager { public: ICraftDefManager(){} virtual ~ICraftDefManager(){} - virtual InventoryItem* getCraftResult(const CraftPointerInput &input_cpi, - IGameDef *gamedef) const=0; + + // The main crafting function + virtual bool getCraftResult(CraftInput &input, CraftOutput &output, + bool decrementInput, IGameDef *gamedef) const=0; - virtual void serialize(std::ostream &os)=0; + // Print crafting recipes for debugging + virtual std::string dump() const=0; + + virtual void serialize(std::ostream &os) const=0; }; class IWritableCraftDefManager : public ICraftDefManager @@ -95,13 +352,21 @@ 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; + + // The main crafting function + virtual bool getCraftResult(CraftInput &input, CraftOutput &output, + bool decrementInput, IGameDef *gamedef) const=0; + + // Print crafting recipes for debugging + virtual std::string dump() const=0; + + // Add a crafting definition. + // After calling this, the pointer belongs to the manager. + virtual void registerCraft(CraftDefinition *def)=0; + // Delete all crafting definitions virtual void clear()=0; - virtual void serialize(std::ostream &os)=0; + virtual void serialize(std::ostream &os) const=0; virtual void deSerialize(std::istream &is)=0; }; diff --git a/src/craftitemdef.cpp b/src/craftitemdef.cpp deleted file mode 100644 index 4461e38a7..000000000 --- a/src/craftitemdef.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2011 celeron55, Perttu Ahola -Copyright (C) 2011 Kahrl - -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 "craftitemdef.h" -#include "irrlichttypes.h" -#include "log.h" -#include -#include "utility.h" -#include - -CraftItemDefinition::CraftItemDefinition(): - imagename(""), - cookresult_item(""), - furnace_cooktime(3.0), - furnace_burntime(-1.0), - usable(false), - liquids_pointable(false), - dropcount(-1), - stack_max(99) -{} - -std::string CraftItemDefinition::dump() -{ - std::ostringstream os(std::ios::binary); - os<<"imagename="<::Node *n; - n = m_item_definitions.find(itemname); - if(n == NULL) - return NULL; - return n->getValue(); - } - virtual std::string getImagename(const std::string &itemname) const - { - const CraftItemDefinition *def = getCraftItemDefinition(itemname); - if(def == NULL) - return ""; - return def->imagename; - } - virtual std::string getAlias(const std::string &name) const - { - std::map::const_iterator i; - i = m_aliases.find(name); - if(i != m_aliases.end()) - return i->second; - return name; - } - virtual bool registerCraftItem(std::string itemname, const CraftItemDefinition &def) - { - infostream<<"registerCraftItem: registering CraftItem \""<::Iterator - i = m_item_definitions.getIterator(); - i.atEnd() == false; i++){ - delete i.getNode()->getValue(); - } - m_item_definitions.clear(); - m_aliases.clear(); - } - virtual void setAlias(const std::string &name, - const std::string &convert_to) - { - if(getCraftItemDefinition(name) != NULL){ - infostream<<"nidef: not setting alias "< "< "<::Iterator - i = m_item_definitions.getIterator(); - i.atEnd() == false; i++){ - std::string name = i.getNode()->getKey(); - CraftItemDefinition *def = i.getNode()->getValue(); - // Serialize name - os<serialize(tmp_os); - os<::const_iterator - i = m_aliases.begin(); i != m_aliases.end(); i++) - { - os<first); - os<second); - } - } - virtual void deSerialize(std::istream &is) - { - // Clear everything - clear(); - // Deserialize - int version = readU8(is); - if(version != 0) throw SerializationError( - "unsupported CraftItemDefManager version"); - u16 count = readU16(is); - for(u16 i=0; i m_item_definitions; - // Aliases - std::map m_aliases; -}; - -IWritableCraftItemDefManager* createCraftItemDefManager() -{ - return new CCraftItemDefManager(); -} diff --git a/src/craftitemdef.h b/src/craftitemdef.h deleted file mode 100644 index b5d4b9348..000000000 --- a/src/craftitemdef.h +++ /dev/null @@ -1,79 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2011 celeron55, Perttu Ahola -Copyright (C) 2011 Kahrl - -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 CRAFTITEMDEF_HEADER -#define CRAFTITEMDEF_HEADER - -#include "common_irrlicht.h" -#include -#include - -struct CraftItemDefinition -{ - std::string imagename; - std::string cookresult_item; - float furnace_cooktime; - float furnace_burntime; - bool usable; - bool liquids_pointable; - s16 dropcount; - s16 stack_max; - - CraftItemDefinition(); - std::string dump(); - void serialize(std::ostream &os); - void deSerialize(std::istream &is); -}; - -class ICraftItemDefManager -{ -public: - ICraftItemDefManager(){} - virtual ~ICraftItemDefManager(){} - virtual const CraftItemDefinition* getCraftItemDefinition(const std::string &itemname) const=0; - virtual std::string getImagename(const std::string &itemname) const =0; - virtual std::string getAlias(const std::string &name) const =0; - - virtual void serialize(std::ostream &os)=0; -}; - -class IWritableCraftItemDefManager : public ICraftItemDefManager -{ -public: - IWritableCraftItemDefManager(){} - virtual ~IWritableCraftItemDefManager(){} - virtual const CraftItemDefinition* getCraftItemDefinition(const std::string &itemname) const=0; - virtual std::string getImagename(const std::string &itemname) const =0; - - virtual bool registerCraftItem(std::string itemname, const CraftItemDefinition &def)=0; - virtual void clear()=0; - // Set an alias so that entries named will load as . - // Alias is not set if has already been defined. - // Alias will be removed if is defined at a later point of time. - virtual void setAlias(const std::string &name, - const std::string &convert_to)=0; - - virtual void serialize(std::ostream &os)=0; - virtual void deSerialize(std::istream &is)=0; -}; - -IWritableCraftItemDefManager* createCraftItemDefManager(); - -#endif diff --git a/src/environment.h b/src/environment.h index beb49885c..89c9fd676 100644 --- a/src/environment.h +++ b/src/environment.h @@ -39,6 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "activeobject.h" class Server; +class ServerEnvironment; class ActiveBlockModifier; class ServerActiveObject; typedef struct lua_State lua_State; diff --git a/src/game.cpp b/src/game.cpp index 0d08074ad..7bd5d9587 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -40,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "mainmenumanager.h" -#include "craftitemdef.h" #include "gettext.h" #include "log.h" #include "filesys.h" @@ -48,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "nodemetadata.h" #include "main.h" // For g_settings -#include "tooldef.h" +#include "itemdef.h" #include "tile.h" // For TextureSource #include "logoutputbuffer.h" @@ -79,15 +78,6 @@ struct ChatLine std::wstring text; }; -/* - Inventory stuff -*/ - -// Inventory actions from the menu are buffered here before sending -Queue inventory_action_queue; -// This is a copy of the inventory that the client's environment has -Inventory local_inventory; - /* Text input system */ @@ -156,7 +146,7 @@ private: Hotbar draw routine */ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font, - ITextureSource *tsrc, + IGameDef *gamedef, v2s32 centerlowerpos, s32 imgsize, s32 itemcount, Inventory *inventory, s32 halfheartcount, u16 playeritem) { @@ -184,7 +174,7 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font, for(s32 i=0; igetItem(i); + const ItemStack &item = mainlist->getItem(i); core::rect rect = imgrect + pos + v2s32(padding+i*(imgsize+padding*2), padding); @@ -245,17 +235,14 @@ void draw_hotbar(video::IVideoDriver *driver, gui::IGUIFont *font, video::SColor bgcolor2(128,0,0,0); driver->draw2DRectangle(bgcolor2, rect, NULL); - - if(item != NULL) - { - drawInventoryItem(driver, font, item, rect, NULL, tsrc); - } + drawItemStack(driver, font, item, rect, NULL, gamedef); } /* Draw hearts */ - video::ITexture *heart_texture = tsrc->getTextureRaw("heart.png"); + video::ITexture *heart_texture = + gamedef->getTextureSource()->getTextureRaw("heart.png"); if(heart_texture) { v2s32 p = pos + v2s32(0, -20); @@ -691,12 +678,10 @@ void the_game( IWritableTextureSource *tsrc = createTextureSource(device); // These will be filled by data received from the server - // Create tool definition manager - IWritableToolDefManager *tooldef = createToolDefManager(); + // Create item definition manager + IWritableItemDefManager *itemdef = createItemDefManager(); // Create node definition manager IWritableNodeDefManager *nodedef = createNodeDefManager(); - // Create CraftItem definition manager - IWritableCraftItemDefManager *craftitemdef = createCraftItemDefManager(); // Add chat log output for errors to be shown in chat LogOutputBuffer chat_log_error_buf(LMT_ERROR); @@ -725,7 +710,7 @@ void the_game( MapDrawControl draw_control; Client client(device, playername.c_str(), password, draw_control, - tsrc, tooldef, nodedef, craftitemdef); + tsrc, itemdef, nodedef); // Client acts as our GameDef IGameDef *gamedef = &client; @@ -835,9 +820,8 @@ void the_game( // End condition if(client.texturesReceived() && - client.tooldefReceived() && - client.nodedefReceived() && - client.craftitemdefReceived()){ + client.itemdefReceived() && + client.nodedefReceived()){ got_content = true; break; } @@ -853,12 +837,10 @@ void the_game( ss<<(int)(timeout - time_counter + 1.0); ss<getRootSceneNode(), smgr, -1, client.getMapSeed(), &client); } + /* + A copy of the local inventory + */ + Inventory local_inventory(itemdef); + /* Move into game */ @@ -1289,7 +1282,7 @@ void the_game( // drop selected item IDropAction *a = new IDropAction(); a->count = 0; - a->from_inv = "current_player"; + a->from_inv.setCurrentPlayer(); a->from_list = "main"; a->from_i = client.getPlayerItem(); client.inventoryAction(a); @@ -1302,18 +1295,20 @@ void the_game( GUIInventoryMenu *menu = new GUIInventoryMenu(guienv, guiroot, -1, &g_menumgr, v2s16(8,7), - client.getInventoryContext(), - &client, tsrc); + &client, gamedef); + + InventoryLocation inventoryloc; + inventoryloc.setCurrentPlayer(); core::array draw_spec; draw_spec.push_back(GUIInventoryMenu::DrawSpec( - "list", "current_player", "main", + "list", inventoryloc, "main", v2s32(0, 3), v2s32(8, 4))); draw_spec.push_back(GUIInventoryMenu::DrawSpec( - "list", "current_player", "craft", + "list", inventoryloc, "craft", v2s32(3, 0), v2s32(3, 3))); draw_spec.push_back(GUIInventoryMenu::DrawSpec( - "list", "current_player", "craftresult", + "list", inventoryloc, "craftresult", v2s32(7, 1), v2s32(1, 1))); menu->setDrawSpec(draw_spec); @@ -1691,31 +1686,20 @@ void the_game( /* For interaction purposes, get info about the held item - - Is it a tool, and what is the toolname? + - What item is it? - Is it a usable item? - Can it point to liquids? */ - std::string playeritem_toolname = ""; + ItemStack playeritem; bool playeritem_usable = false; bool playeritem_liquids_pointable = false; { InventoryList *mlist = local_inventory.getList("main"); if(mlist != NULL) { - InventoryItem *item = mlist->getItem(client.getPlayerItem()); - if(item) - { - if((std::string)item->getName() == "ToolItem") - { - ToolItem *titem = (ToolItem*)item; - playeritem_toolname = titem->getToolName(); - } - - playeritem_usable = item->isUsable(); - - playeritem_liquids_pointable = - item->areLiquidsPointable(); - } + playeritem = mlist->getItem(client.getPlayerItem()); + playeritem_usable = playeritem.getDefinition(itemdef).usable; + playeritem_liquids_pointable = playeritem.getDefinition(itemdef).liquids_pointable; } } @@ -1845,7 +1829,7 @@ void the_game( // Get digging properties for material and tool content_t material = n.getContent(); ToolDiggingProperties tp = - tooldef->getDiggingProperties(playeritem_toolname); + playeritem.getToolDiggingProperties(itemdef); DiggingProperties prop = getDiggingProperties(material, &tp, nodedef); @@ -1853,9 +1837,6 @@ void the_game( if(prop.diggable == false) { - /*infostream<<"Material "<<(int)material - <<" not diggable with \"" - <getInventoryDrawSpecString() != "" && !random_input) { infostream<<"Launching custom inventory view"<getInventoryDrawSpecString(), - current_name); + inventoryloc); GUIInventoryMenu *menu = new GUIInventoryMenu(guienv, guiroot, -1, &g_menumgr, invsize, - client.getInventoryContext(), - &client, tsrc); + &client, gamedef); menu->setDrawSpec(draw_spec); menu->drop(); } @@ -2001,7 +1975,7 @@ void the_game( v3f objpos = selected_object->getPosition(); v3f dir = (objpos - player_position).normalize(); - bool disable_send = selected_object->directReportPunch(playeritem_toolname, dir); + bool disable_send = selected_object->directReportPunch(playeritem.name, dir); if(!disable_send) client.interact(0, pointed); } @@ -2285,24 +2259,12 @@ void the_game( update_wielded_item_trigger = false; // Update wielded tool InventoryList *mlist = local_inventory.getList("main"); - InventoryItem *item = NULL; + ItemStack item; if(mlist != NULL) item = mlist->getItem(client.getPlayerItem()); camera.wield(item, gamedef); } - /* - Send actions returned by the inventory menu - */ - while(inventory_action_queue.size() != 0) - { - InventoryAction *a = inventory_action_queue.pop_front(); - - client.sendInventoryAction(a); - // Eat it - delete a; - } - /* Drawing begins */ @@ -2411,7 +2373,7 @@ void the_game( Draw hotbar */ { - draw_hotbar(driver, font, tsrc, + draw_hotbar(driver, font, gamedef, v2s32(displaycenter.X, screensize.Y), hotbar_imagesize, hotbar_itemcount, &local_inventory, client.getHP(), client.getPlayerItem()); @@ -2482,9 +2444,9 @@ void the_game( } // Client scope (must be destructed before destructing *def and tsrc - delete tooldef; delete tsrc; delete nodedef; + delete itemdef; } diff --git a/src/gamedef.h b/src/gamedef.h index c450568b7..8df6988ad 100644 --- a/src/gamedef.h +++ b/src/gamedef.h @@ -21,11 +21,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #define GAMEDEF_HEADER #include +#include "irrlichttypes.h" -class IToolDefManager; +class IItemDefManager; class INodeDefManager; class ICraftDefManager; -class ICraftItemDefManager; // Mineral too? class ITextureSource; @@ -39,10 +39,9 @@ class IGameDef public: // These are thread-safe IF they are not edited while running threads. // Thus, first they are set up and then they are only read. - virtual IToolDefManager* getToolDefManager()=0; + virtual IItemDefManager* getItemDefManager()=0; virtual INodeDefManager* getNodeDefManager()=0; virtual ICraftDefManager* getCraftDefManager()=0; - virtual ICraftItemDefManager* getCraftItemDefManager()=0; // This is always thread-safe, but referencing the irrlicht texture // pointers in other threads than main thread will make things explode. @@ -52,10 +51,9 @@ public: virtual u16 allocateUnknownNodeId(const std::string &name)=0; // Shorthands - IToolDefManager* tdef(){return getToolDefManager();} + IItemDefManager* idef(){return getItemDefManager();} INodeDefManager* ndef(){return getNodeDefManager();} ICraftDefManager* cdef(){return getCraftDefManager();} - ICraftItemDefManager* cidef(){return getCraftItemDefManager();} ITextureSource* tsrc(){return getTextureSource();} }; diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index 552e10db2..9b2aed377 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiInventoryMenu.h" #include "constants.h" +#include "gamedef.h" #include "keycode.h" #include "strfnd.h" #include @@ -28,20 +29,21 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "log.h" -#include "inventorymanager.h" -void drawInventoryItem(video::IVideoDriver *driver, +void drawItemStack(video::IVideoDriver *driver, gui::IGUIFont *font, - InventoryItem *item, core::rect rect, + const ItemStack &item, + const core::rect &rect, const core::rect *clip, - ITextureSource *tsrc) + IGameDef *gamedef) { - if(item == NULL) + if(item.empty()) return; - video::ITexture *texture = NULL; - texture = item->getImage(); + const ItemDefinition &def = item.getDefinition(gamedef->idef()); + video::ITexture *texture = def.inventory_texture; + // Draw the inventory texture if(texture != NULL) { const video::SColor color(255,255,255,255); @@ -51,34 +53,59 @@ void drawInventoryItem(video::IVideoDriver *driver, core::dimension2di(texture->getOriginalSize())), clip, colors, true); } - else + + if(def.type == ITEM_TOOL && item.wear != 0) { - video::SColor bgcolor(255,50,50,128); - driver->draw2DRectangle(bgcolor, rect, clip); + // Draw a progressbar + float barheight = rect.getHeight()/16; + float barpad_x = rect.getWidth()/16; + float barpad_y = rect.getHeight()/16; + core::rect progressrect( + rect.UpperLeftCorner.X + barpad_x, + rect.LowerRightCorner.Y - barpad_y - barheight, + rect.LowerRightCorner.X - barpad_x, + rect.LowerRightCorner.Y - barpad_y); + + // Shrink progressrect by amount of tool damage + float wear = item.wear / 65535.0; + progressrect.LowerRightCorner.X = + wear * progressrect.UpperLeftCorner.X + + (1-wear) * progressrect.LowerRightCorner.X; + + // Compute progressbar color + // wear = 0.0: green + // wear = 0.5: yellow + // wear = 1.0: red + video::SColor color(255,255,255,255); + int wear_i = floor(wear * 511); + wear_i = MYMIN(wear_i + 10, 511); + if(wear_i <= 255) + color.set(255, wear_i, 255, 0); + else + color.set(255, 255, 511-wear_i, 0); + + driver->draw2DRectangle(color, progressrect, clip); } - if(font != NULL) + if(font != NULL && item.count >= 2) { - std::string text = item->getText(); - if(font && text != "") - { - v2u32 dim = font->getDimension(narrow_to_wide(text).c_str()); - v2s32 sdim(dim.X,dim.Y); - - core::rect rect2( - /*rect.UpperLeftCorner, - core::dimension2d(rect.getWidth(), 15)*/ - rect.LowerRightCorner - sdim, - sdim - ); - - video::SColor bgcolor(128,0,0,0); - driver->draw2DRectangle(bgcolor, rect2, clip); - - font->draw(text.c_str(), rect2, - video::SColor(255,255,255,255), false, false, - clip); - } + // Get the item count as a string + std::string text = itos(item.count); + v2u32 dim = font->getDimension(narrow_to_wide(text).c_str()); + v2s32 sdim(dim.X,dim.Y); + + core::rect rect2( + /*rect.UpperLeftCorner, + core::dimension2d(rect.getWidth(), 15)*/ + rect.LowerRightCorner - sdim, + sdim + ); + + video::SColor bgcolor(128,0,0,0); + driver->draw2DRectangle(bgcolor, rect2, clip); + + video::SColor color(255,255,255,255); + font->draw(text.c_str(), rect2, color, false, false, clip); } } @@ -90,15 +117,13 @@ GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, v2s16 menu_size, - InventoryContext *c, InventoryManager *invmgr, - ITextureSource *tsrc + IGameDef *gamedef ): GUIModalMenu(env, parent, id, menumgr), m_menu_size(menu_size), - m_c(c), m_invmgr(invmgr), - m_tsrc(tsrc) + m_gamedef(gamedef) { m_selected_item = NULL; } @@ -214,15 +239,15 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const core::rect rect = imgrect + s.pos + p0; if(rect.isPointInside(p)) { - return ItemSpec(s.inventoryname, s.listname, i); + return ItemSpec(s.inventoryloc, s.listname, i); } } } - return ItemSpec("", "", -1); + return ItemSpec(InventoryLocation(), "", -1); } -void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc) +void GUIInventoryMenu::drawList(const ListDrawSpec &s) { video::IVideoDriver* driver = Environment->getVideoDriver(); @@ -232,7 +257,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc) if (skin) font = skin->getFont(); - Inventory *inv = m_invmgr->getInventory(m_c, s.inventoryname); + Inventory *inv = m_invmgr->getInventory(s.inventoryloc); assert(inv); InventoryList *ilist = inv->getList(s.listname); @@ -244,7 +269,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc) s32 y = (i/s.geom.X) * spacing.Y; v2s32 p(x,y); core::rect rect = imgrect + s.pos + p; - InventoryItem *item = NULL; + ItemStack item; if(ilist) item = ilist->getItem(i); @@ -278,10 +303,10 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, ITextureSource *tsrc) driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); } - if(item) + if(!item.empty()) { - drawInventoryItem(driver, font, item, - rect, &AbsoluteClippingRect, tsrc); + drawItemStack(driver, font, item, + rect, &AbsoluteClippingRect, m_gamedef); } } @@ -303,8 +328,7 @@ void GUIInventoryMenu::drawMenu() for(u32 i=0; igetInventory(m_c, - m_selected_item->inventoryname); - Inventory *inv_to = m_invmgr->getInventory(m_c, - s.inventoryname); + Inventory *inv_from = m_invmgr->getInventory( + m_selected_item->inventoryloc); + Inventory *inv_to = m_invmgr->getInventory( + s.inventoryloc); assert(inv_from); assert(inv_to); InventoryList *list_from = @@ -373,21 +397,21 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) // Indicates whether source slot completely empties bool source_empties = false; if(list_from && list_to - && list_from->getItem(m_selected_item->i) != NULL) + && !list_from->getItem(m_selected_item->i).empty()) { infostream<<"Handing IACTION_MOVE to manager"<count = amount; - a->from_inv = m_selected_item->inventoryname; + a->from_inv = m_selected_item->inventoryloc; a->from_list = m_selected_item->listname; a->from_i = m_selected_item->i; - a->to_inv = s.inventoryname; + a->to_inv = s.inventoryloc; a->to_list = s.listname; a->to_i = s.i; //ispec.actions->push_back(a); m_invmgr->inventoryAction(a); - if(list_from->getItem(m_selected_item->i)->getCount()<=amount) + if(list_from->getItem(m_selected_item->i).count<=amount) source_empties = true; } // Remove selection if target was left-clicked or source @@ -401,13 +425,13 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) else { /* - Select if non-NULL + Select if nonempty */ - Inventory *inv = m_invmgr->getInventory(m_c, - s.inventoryname); + Inventory *inv = m_invmgr->getInventory( + s.inventoryloc); assert(inv); InventoryList *list = inv->getList(s.listname); - if(list->getItem(s.i) != NULL) + if(!list->getItem(s.i).empty()) { m_selected_item = new ItemSpec(s); } @@ -489,7 +513,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString( core::array &draw_spec, const std::string &data, - const std::string ¤t_name) + const InventoryLocation ¤t_location) { v2s16 invsize(8,9); Strfnd f(data); @@ -500,8 +524,11 @@ v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString( if(type == "list") { std::string name = f.next(";"); + InventoryLocation loc; if(name == "current_name") - name = current_name; + loc = current_location; + else + loc.deSerialize(name); std::string subname = f.next(";"); s32 pos_x = stoi(f.next(",")); s32 pos_y = stoi(f.next(";")); @@ -512,7 +539,7 @@ v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString( <<", geom=("< rect, + const ItemStack &item, + const core::rect &rect, const core::rect *clip, - ITextureSource *tsrc); + IGameDef *gamedef); class GUIInventoryMenu : public GUIModalMenu { @@ -44,11 +45,11 @@ class GUIInventoryMenu : public GUIModalMenu { i = -1; } - ItemSpec(const std::string &a_inventoryname, + ItemSpec(const InventoryLocation &a_inventoryloc, const std::string &a_listname, s32 a_i) { - inventoryname = a_inventoryname; + inventoryloc = a_inventoryloc; listname = a_listname; i = a_i; } @@ -57,7 +58,7 @@ class GUIInventoryMenu : public GUIModalMenu return i != -1; } - std::string inventoryname; + InventoryLocation inventoryloc; std::string listname; s32 i; }; @@ -67,17 +68,17 @@ class GUIInventoryMenu : public GUIModalMenu ListDrawSpec() { } - ListDrawSpec(const std::string &a_inventoryname, + ListDrawSpec(const InventoryLocation &a_inventoryloc, const std::string &a_listname, v2s32 a_pos, v2s32 a_geom) { - inventoryname = a_inventoryname; + inventoryloc = a_inventoryloc; listname = a_listname; pos = a_pos; geom = a_geom; } - std::string inventoryname; + InventoryLocation inventoryloc; std::string listname; v2s32 pos; v2s32 geom; @@ -89,7 +90,7 @@ public: { } DrawSpec(const std::string &a_type, - const std::string &a_name, + const InventoryLocation &a_name, const std::string &a_subname, v2s32 a_pos, v2s32 a_geom) @@ -102,7 +103,7 @@ public: } std::string type; - std::string name; + InventoryLocation name; std::string subname; v2s32 pos; v2s32 geom; @@ -112,15 +113,14 @@ public: static v2s16 makeDrawSpecArrayFromString( core::array &draw_spec, const std::string &data, - const std::string ¤t_name); + const InventoryLocation ¤t_location); GUIInventoryMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, v2s16 menu_size, - InventoryContext *c, InventoryManager *invmgr, - ITextureSource *tsrc + IGameDef *gamedef ); ~GUIInventoryMenu(); @@ -136,7 +136,7 @@ public: void regenerateGui(v2u32 screensize); ItemSpec getItemAtPos(v2s32 p) const; - void drawList(const ListDrawSpec &s, ITextureSource *tsrc); + void drawList(const ListDrawSpec &s); void drawMenu(); bool OnEvent(const SEvent& event); @@ -153,9 +153,8 @@ protected: v2s32 spacing; v2s32 imgsize; - InventoryContext *m_c; InventoryManager *m_invmgr; - ITextureSource *m_tsrc; + IGameDef *m_gamedef; core::array m_init_draw_spec; core::array m_draw_spec; diff --git a/src/inventory.cpp b/src/inventory.cpp index 0d38bed78..ebd0b9c23 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -22,545 +22,429 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "utility.h" #include "debug.h" #include -#include "main.h" // For tsrc, g_toolmanager -#include "serverobject.h" -#include "content_mapnode.h" -#include "content_sao.h" -#include "environment.h" -#include "mapblock.h" -#include "player.h" #include "log.h" -#include "nodedef.h" -#include "tooldef.h" -#include "craftitemdef.h" -#include "gamedef.h" -#include "scriptapi.h" +#include "itemdef.h" #include "strfnd.h" +#include "content_mapnode.h" // For loading legacy MaterialItems #include "nameidmapping.h" // For loading legacy MaterialItems /* - InventoryItem + ItemStack */ -InventoryItem::InventoryItem(IGameDef *gamedef, u16 count): - m_gamedef(gamedef), - m_count(count) +static content_t content_translate_from_19_to_internal(content_t c_from) { - assert(m_gamedef); + for(u32 i=0; i= 0x7f || s[i] == ' ' || s[i] == '\"') + return serializeJsonString(s); + } + return s; } -content_t content_translate_from_19_to_internal(content_t c_from) +// Parses a string serialized by serializeJsonStringIfNeeded. +static std::string deSerializeJsonStringIfNeeded(std::istream &is) { - for(u32 i=0; igetAlias(name_); + count = count_; + wear = wear_; + metadata = metadata_; + + if(name.empty() || count == 0) + clear(); + else if(itemdef->get(name).type == ITEM_TOOL) + count = 1; } -InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef) +void ItemStack::serialize(std::ostream &os) const { DSTACK(__FUNCTION_NAME); - //is.imbue(std::locale("C")); + if(empty()) + return; + + // Check how many parts of the itemstring are needed + int parts = 1; + if(count != 1) + parts = 2; + if(wear != 0) + parts = 3; + if(metadata != "") + parts = 4; + + os<= 2) + os<<" "<= 3) + os<<" "<= 4) + os<<" "<>material; - u16 count; - is>>count; + u16 materialcount; + is>>materialcount; // Convert old materials if(material <= 0xff) material = content_translate_from_19_to_internal(material); if(material > MAX_CONTENT) throw SerializationError("Too large material number"); - return new MaterialItem(gamedef, material, count); + // Convert old id to name + NameIdMapping legacy_nimap; + content_mapnode_get_name_id_mapping(&legacy_nimap); + legacy_nimap.getName(material, name); + if(name == "") + name = "unknown_block"; + name = itemdef->getAlias(name); + count = materialcount; } else if(name == "MaterialItem2") { + // Obsoleted on 2011-11-16 + u16 material; is>>material; - u16 count; - is>>count; + u16 materialcount; + is>>materialcount; if(material > MAX_CONTENT) throw SerializationError("Too large material number"); - return new MaterialItem(gamedef, material, count); + // Convert old id to name + NameIdMapping legacy_nimap; + content_mapnode_get_name_id_mapping(&legacy_nimap); + legacy_nimap.getName(material, name); + if(name == "") + name = "unknown_block"; + name = itemdef->getAlias(name); + count = materialcount; } - else if(name == "node" || name == "NodeItem" || name == "MaterialItem3") + else if(name == "node" || name == "NodeItem" || name == "MaterialItem3" + || name == "craft" || name == "CraftItem") { + // Obsoleted on 2012-01-07 + std::string all; std::getline(is, all, '\n'); - std::string nodename; // First attempt to read inside "" Strfnd fnd(all); fnd.next("\""); // If didn't skip to end, we have ""s if(!fnd.atend()){ - nodename = fnd.next("\""); + name = fnd.next("\""); } else { // No luck, just read a word then fnd.start(all); - nodename = fnd.next(" "); + name = fnd.next(" "); } fnd.skip_over(" "); - u16 count = stoi(trim(fnd.next(""))); + name = itemdef->getAlias(name); + count = stoi(trim(fnd.next(""))); if(count == 0) count = 1; - return new MaterialItem(gamedef, nodename, count); } else if(name == "MBOItem") { - std::string inventorystring; - std::getline(is, inventorystring, '|'); + // Obsoleted on 2011-10-14 throw SerializationError("MBOItem not supported anymore"); } - else if(name == "craft" || name == "CraftItem") - { - std::string all; - std::getline(is, all, '\n'); - std::string subname; - // First attempt to read inside "" - Strfnd fnd(all); - fnd.next("\""); - // If didn't skip to end, we have ""s - if(!fnd.atend()){ - subname = fnd.next("\""); - } else { // No luck, just read a word then - fnd.start(all); - subname = fnd.next(" "); - } - // Then read count - fnd.skip_over(" "); - u16 count = stoi(trim(fnd.next(""))); - if(count == 0) - count = 1; - return new CraftItem(gamedef, subname, count); - } else if(name == "tool" || name == "ToolItem") { + // Obsoleted on 2012-01-07 + std::string all; std::getline(is, all, '\n'); - std::string toolname; // First attempt to read inside "" Strfnd fnd(all); fnd.next("\""); // If didn't skip to end, we have ""s if(!fnd.atend()){ - toolname = fnd.next("\""); + name = fnd.next("\""); } else { // No luck, just read a word then fnd.start(all); - toolname = fnd.next(" "); + name = fnd.next(" "); } + count = 1; // Then read wear fnd.skip_over(" "); - u16 wear = stoi(trim(fnd.next(""))); - return new ToolItem(gamedef, toolname, wear); + wear = stoi(trim(fnd.next(""))); } else { - infostream<<"Unknown InventoryItem name=\""<getMap().emergeBlock(blockpos, false); - if(block==NULL) - { - infostream<<"InventoryItem::dropOrPlace(): FAIL: block not found: " - < dropcount) - count = dropcount; - if(count < 0 || count > getCount()) - count = getCount(); - if(count > 0) - { - /* - Create an ItemSAO - */ - pos.Y -= BS*0.25; // let it drop a bit - // Randomize a bit - //pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; - //pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; - // Create object - ServerActiveObject *obj = new ItemSAO(env, pos, getItemString()); - // Add the object to the environment - env->addActiveObject(obj); - infostream<<"Dropped item"<ndef()->getAlias(nodename); -} -// Legacy constructor -MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count): - InventoryItem(gamedef, count) -{ - NameIdMapping legacy_nimap; - content_mapnode_get_name_id_mapping(&legacy_nimap); - std::string nodename; - legacy_nimap.getName(content, nodename); - if(nodename == "") - nodename = "unknown_block"; - m_nodename = nodename; -} - -#ifndef SERVER -video::ITexture * MaterialItem::getImage() const -{ - return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture; -} -#endif - -bool MaterialItem::isCookable() const -{ - INodeDefManager *ndef = m_gamedef->ndef(); - const ContentFeatures &f = ndef->get(m_nodename); - return (f.cookresult_item != ""); -} - -InventoryItem *MaterialItem::createCookResult() const -{ - INodeDefManager *ndef = m_gamedef->ndef(); - const ContentFeatures &f = ndef->get(m_nodename); - std::istringstream is(f.cookresult_item, std::ios::binary); - return InventoryItem::deSerialize(is, m_gamedef); -} - -float MaterialItem::getCookTime() const -{ - INodeDefManager *ndef = m_gamedef->ndef(); - const ContentFeatures &f = ndef->get(m_nodename); - return f.furnace_cooktime; -} - -float MaterialItem::getBurnTime() const -{ - INodeDefManager *ndef = m_gamedef->ndef(); - const ContentFeatures &f = ndef->get(m_nodename); - return f.furnace_burntime; -} - -content_t MaterialItem::getMaterial() const -{ - INodeDefManager *ndef = m_gamedef->ndef(); - content_t id = CONTENT_IGNORE; - ndef->getId(m_nodename, id); - return id; -} - -/* - ToolItem -*/ - -ToolItem::ToolItem(IGameDef *gamedef, std::string toolname, u16 wear): - InventoryItem(gamedef, 1) -{ - // Convert directly to the correct name through aliases - m_toolname = gamedef->tdef()->getAlias(toolname); - - m_wear = wear; -} - -std::string ToolItem::getImageBasename() const -{ - return m_gamedef->getToolDefManager()->getImagename(m_toolname); -} - -#ifndef SERVER -video::ITexture * ToolItem::getImage() const -{ - ITextureSource *tsrc = m_gamedef->tsrc(); - - std::string basename = getImageBasename(); - - /* - Calculate a progress value with sane amount of - maximum states - */ - u32 maxprogress = 30; - u32 toolprogress = (65535-m_wear)/(65535/maxprogress); - - float value_f = (float)toolprogress / (float)maxprogress; - std::ostringstream os; - os<getTextureRaw(os.str()); -} + // Apply item aliases + name = itemdef->getAlias(name); -video::ITexture * ToolItem::getImageRaw() const -{ - ITextureSource *tsrc = m_gamedef->tsrc(); - - return tsrc->getTextureRaw(getImageBasename()); -} -#endif + // Read the count + std::string count_str; + std::getline(is, count_str, ' '); + if(count_str.empty()) + { + count = 1; + break; + } + else + count = stoi(count_str); -bool ToolItem::isKnown() const -{ - IToolDefManager *tdef = m_gamedef->tdef(); - const ToolDefinition *def = tdef->getToolDefinition(m_toolname); - return (def != NULL); -} + // Read the wear + std::string wear_str; + std::getline(is, wear_str, ' '); + if(wear_str.empty()) + break; + else + wear = stoi(wear_str); -/* - CraftItem -*/ - -CraftItem::CraftItem(IGameDef *gamedef, std::string subname, u16 count): - InventoryItem(gamedef, count) -{ - // Convert directly to the correct name through aliases - m_subname = gamedef->cidef()->getAlias(subname); -} + // Read metadata + metadata = deSerializeJsonStringIfNeeded(is); -#ifndef SERVER -video::ITexture * CraftItem::getImage() const -{ - ICraftItemDefManager *cidef = m_gamedef->cidef(); - ITextureSource *tsrc = m_gamedef->tsrc(); - std::string imagename = cidef->getImagename(m_subname); - return tsrc->getTextureRaw(imagename); -} -#endif + // In case fields are added after metadata, skip space here: + //std::getline(is, tmp, ' '); + //if(!tmp.empty()) + // throw SerializationError("Unexpected text after metadata"); -bool CraftItem::isKnown() const -{ - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - return (def != NULL); -} - -u16 CraftItem::getStackMax() const -{ - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - if(def == NULL) - return InventoryItem::getStackMax(); - return def->stack_max; -} - -bool CraftItem::isUsable() const -{ - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - return def != NULL && def->usable; -} + } while(false); + } -bool CraftItem::isCookable() const -{ - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - return def != NULL && def->cookresult_item != ""; + if(name.empty() || count == 0) + clear(); + else if(itemdef->get(name).type == ITEM_TOOL) + count = 1; } -InventoryItem *CraftItem::createCookResult() const +void ItemStack::deSerialize(const std::string &str, IItemDefManager *itemdef) { - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - if(def == NULL) - return InventoryItem::createCookResult(); - std::istringstream is(def->cookresult_item, std::ios::binary); - return InventoryItem::deSerialize(is, m_gamedef); + std::istringstream is(str, std::ios::binary); + deSerialize(is, itemdef); } -float CraftItem::getCookTime() const +std::string ItemStack::getItemString() const { - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - if (def == NULL) - return InventoryItem::getCookTime(); - return def->furnace_cooktime; + // Get item string + std::ostringstream os(std::ios::binary); + serialize(os); + return os.str(); } -float CraftItem::getBurnTime() const +ItemStack ItemStack::addItem(const ItemStack &newitem_, + IItemDefManager *itemdef) { - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - if (def == NULL) - return InventoryItem::getBurnTime(); - return def->furnace_burntime; -} + ItemStack newitem = newitem_; -s16 CraftItem::getDropCount() const -{ - // Special cases - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - if(def != NULL && def->dropcount >= 0) - return def->dropcount; - // Default - return InventoryItem::getDropCount(); -} + // If the item is empty or the position invalid, bail out + if(newitem.empty()) + { + // nothing can be added trivially + } + // If this is an empty item, it's an easy job. + else if(empty()) + { + *this = newitem; + newitem.clear(); + } + // If item name differs, bail out + else if(name != newitem.name) + { + // cannot be added + } + // If the item fits fully, add counter and delete it + else if(newitem.count <= freeSpace(itemdef)) + { + add(newitem.count); + newitem.clear(); + } + // Else the item does not fit fully. Add all that fits and return + // the rest. + else + { + u16 freespace = freeSpace(itemdef); + add(freespace); + newitem.remove(freespace); + } -bool CraftItem::areLiquidsPointable() const -{ - ICraftItemDefManager *cidef = m_gamedef->cidef(); - const CraftItemDefinition *def = cidef->getCraftItemDefinition(m_subname); - return def != NULL && def->liquids_pointable; + return newitem; } -bool CraftItem::dropOrPlace(ServerEnvironment *env, - ServerActiveObject *dropper, - v3f pos, bool place, s16 count) +bool ItemStack::itemFits(const ItemStack &newitem_, + ItemStack *restitem, + IItemDefManager *itemdef) const { - if(count == 0) - return false; - - bool callback_exists = false; - bool result = false; + ItemStack newitem = newitem_; - if(place) + // If the item is empty or the position invalid, bail out + if(newitem.empty()) { - result = scriptapi_craftitem_on_place_on_ground( - env->getLua(), - m_subname.c_str(), dropper, pos, - callback_exists); + // nothing can be added trivially } - - // note: on_drop is fallback for on_place_on_ground - - if(!callback_exists) + // If this is an empty item, it's an easy job. + else if(empty()) { - result = scriptapi_craftitem_on_drop( - env->getLua(), - m_subname.c_str(), dropper, pos, - callback_exists); + newitem.clear(); } - - if(callback_exists) + // If item name differs, bail out + else if(name != newitem.name) { - // If the callback returned true, drop one item - if(result) - setCount(getCount() - 1); - return getCount() < 1; + // cannot be added } + // If the item fits fully, delete it + else if(newitem.count <= freeSpace(itemdef)) + { + newitem.clear(); + } + // Else the item does not fit fully. Return the rest. + // the rest. else { - // If neither on_place_on_ground (if place==true) - // nor on_drop exists, call the base implementation - return InventoryItem::dropOrPlace(env, dropper, pos, place, count); + u16 freespace = freeSpace(itemdef); + newitem.remove(freespace); } + + if(restitem) + *restitem = newitem; + return newitem.empty(); } -bool CraftItem::use(ServerEnvironment *env, - ServerActiveObject *user, - const PointedThing& pointed) +ItemStack ItemStack::takeItem(u32 takecount) { - bool callback_exists = false; - bool result = false; + if(takecount == 0 || count == 0) + return ItemStack(); - result = scriptapi_craftitem_on_use( - env->getLua(), - m_subname.c_str(), user, pointed, - callback_exists); - - if(callback_exists) + ItemStack result = *this; + if(takecount >= count) { - // If the callback returned true, drop one item - if(result) - setCount(getCount() - 1); - return getCount() < 1; + // Take all + clear(); } else { - // If neither on_place_on_ground (if place==true) - // nor on_drop exists, call the base implementation - return InventoryItem::use(env, user, pointed); + // Take part + remove(takecount); + result.count = takecount; } + return result; +} + +ItemStack ItemStack::peekItem(u32 peekcount) const +{ + if(peekcount == 0 || count == 0) + return ItemStack(); + + ItemStack result = *this; + if(peekcount < count) + result.count = peekcount; + return result; } /* Inventory */ -InventoryList::InventoryList(std::string name, u32 size) +InventoryList::InventoryList(std::string name, u32 size, IItemDefManager *itemdef) { m_name = name; m_size = size; + m_itemdef = itemdef; clearItems(); //m_dirty = false; } InventoryList::~InventoryList() { - for(u32 i=0; iserialize(os); + os<<"Empty"; } else { - os<<"Empty"; + os<<"Item "; + item.serialize(os); } os<<"\n"; } @@ -604,7 +479,7 @@ void InventoryList::serialize(std::ostream &os) const os<<"EndInventoryList\n"; } -void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef) +void InventoryList::deSerialize(std::istream &is) { //is.imbue(std::locale("C")); @@ -635,14 +510,15 @@ void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef) { if(item_i > getSize() - 1) throw SerializationError("too many items"); - InventoryItem *item = InventoryItem::deSerialize(iss, gamedef); + ItemStack item; + item.deSerialize(iss, m_itemdef); m_items[item_i++] = item; } else if(name == "Empty") { if(item_i > getSize() - 1) throw SerializationError("too many items"); - m_items[item_i++] = NULL; + m_items[item_i++].clear(); } else { @@ -653,26 +529,15 @@ void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef) InventoryList::InventoryList(const InventoryList &other) { - /* - Do this so that the items get cloned. Otherwise the pointers - in the array will just get copied. - */ *this = other; } InventoryList & InventoryList::operator = (const InventoryList &other) { - m_name = other.m_name; + m_items = other.m_items; m_size = other.m_size; - clearItems(); - for(u32 i=0; iclone(); - } - } + m_name = other.m_name; + m_itemdef = other.m_itemdef; //setDirty(true); return *this; @@ -683,48 +548,45 @@ const std::string &InventoryList::getName() const return m_name; } -u32 InventoryList::getSize() +u32 InventoryList::getSize() const { return m_items.size(); } -u32 InventoryList::getUsedSlots() +u32 InventoryList::getUsedSlots() const { u32 num = 0; for(u32 i=0; i= m_items.size()) - return NULL; + assert(i < m_size); return m_items[i]; } -InventoryItem * InventoryList::getItem(u32 i) +ItemStack& InventoryList::getItem(u32 i) { - if(i >= m_items.size()) - return NULL; + assert(i < m_size); return m_items[i]; } -InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem) +ItemStack InventoryList::changeItem(u32 i, const ItemStack &newitem) { if(i >= m_items.size()) return newitem; - InventoryItem *olditem = m_items[i]; + ItemStack olditem = m_items[i]; m_items[i] = newitem; //setDirty(true); return olditem; @@ -733,15 +595,15 @@ InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem) void InventoryList::deleteItem(u32 i) { assert(i < m_items.size()); - InventoryItem *item = changeItem(i, NULL); - if(item) - delete item; + m_items[i].clear(); } -InventoryItem * InventoryList::addItem(InventoryItem *newitem) +ItemStack InventoryList::addItem(const ItemStack &newitem_) { - if(newitem == NULL) - return NULL; + ItemStack newitem = newitem_; + + if(newitem.empty()) + return newitem; /* First try to find if it could be added to some existing items @@ -749,12 +611,12 @@ InventoryItem * InventoryList::addItem(InventoryItem *newitem) for(u32 i=0; i= m_items.size()) return newitem; - - //setDirty(true); - - // If it is an empty position, it's an easy job. - InventoryItem *to_item = getItem(i); - if(to_item == NULL) - { - m_items[i] = newitem; - return NULL; - } - - // If not addable, return the item - if(newitem->addableTo(to_item) == false) - return newitem; - - // If the item fits fully in the slot, add counter and delete it - if(newitem->getCount() <= to_item->freeSpace()) - { - to_item->add(newitem->getCount()); - delete newitem; - return NULL; - } - // Else the item does not fit fully. Add all that fits and return - // the rest. - else - { - u16 freespace = to_item->freeSpace(); - to_item->add(freespace); - newitem->remove(freespace); - return newitem; - } + + ItemStack leftover = m_items[i].addItem(newitem, m_itemdef); + //if(leftover != newitem) + // setDirty(true); + return leftover; } -bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem) +bool InventoryList::itemFits(const u32 i, const ItemStack &newitem, + ItemStack *restitem) const { - // If it is an empty position, it's an easy job. - const InventoryItem *to_item = getItem(i); - if(to_item == NULL) + if(i >= m_items.size()) { - return true; - } - - // If not addable, fail - if(newitem->addableTo(to_item) == false) + if(restitem) + *restitem = newitem; return false; - - // If the item fits fully in the slot, pass - if(newitem->getCount() <= to_item->freeSpace()) - { - return true; } - return false; + return m_items[i].itemFits(newitem, restitem, m_itemdef); } -bool InventoryList::roomForItem(const InventoryItem *item) +bool InventoryList::roomForItem(const ItemStack &item_) const { + ItemStack item = item_; + ItemStack leftover; for(u32 i=0; icreateCookResult(); - if(!cook) - return false; - bool room = roomForItem(cook); - delete cook; - return room; -} - -InventoryItem * InventoryList::takeItem(u32 i, u32 count) +bool InventoryList::containsItem(const ItemStack &item) const { + u32 count = item.count; if(count == 0) - return NULL; - - //setDirty(true); - - InventoryItem *item = getItem(i); - // If it is an empty position, return NULL - if(item == NULL) - return NULL; - - if(count >= item->getCount()) - { - // Get the item by swapping NULL to its place - return changeItem(i, NULL); - } - else + return true; + for(std::vector::const_reverse_iterator + i = m_items.rbegin(); + i != m_items.rend(); i++) { - InventoryItem *item2 = item->clone(); - item->remove(count); - item2->setCount(count); - return item2; + if(count == 0) + break; + if(i->name == item.name) + { + if(i->count >= count) + return true; + else + count -= i->count; + } } - return false; } -void InventoryList::decrementMaterials(u16 count) +ItemStack InventoryList::removeItem(const ItemStack &item) { - for(u32 i=0; i::reverse_iterator + i = m_items.rbegin(); + i != m_items.rend(); i++) { - InventoryItem *item = takeItem(i, count); - if(item) - delete item; + if(i->name == item.name) + { + u32 still_to_remove = item.count - removed.count; + removed.addItem(i->takeItem(still_to_remove), m_itemdef); + if(removed.count == item.count) + break; + } } + return removed; } -void InventoryList::print(std::ostream &o) +ItemStack InventoryList::takeItem(u32 i, u32 takecount) { - o<<"InventoryList:"<serialize(o); - o<<"\n"; - } - } + if(i >= m_items.size()) + return ItemStack(); + + ItemStack taken = m_items[i].takeItem(takecount); + //if(!taken.empty()) + // setDirty(true); + return taken; +} + +ItemStack InventoryList::peekItem(u32 i, u32 peekcount) const +{ + if(i >= m_items.size()) + return ItemStack(); + + return m_items[i].peekItem(peekcount); } /* @@ -927,8 +751,9 @@ void Inventory::clear() m_lists.clear(); } -Inventory::Inventory() +Inventory::Inventory(IItemDefManager *itemdef) { + m_itemdef = itemdef; } Inventory::Inventory(const Inventory &other) @@ -938,10 +763,15 @@ Inventory::Inventory(const Inventory &other) Inventory & Inventory::operator = (const Inventory &other) { - clear(); - for(u32 i=0; i>listsize; - InventoryList *list = new InventoryList(listname, listsize); - list->deSerialize(is, gamedef); + InventoryList *list = new InventoryList(listname, listsize, m_itemdef); + list->deSerialize(is); m_lists.push_back(list); } @@ -1009,14 +839,15 @@ InventoryList * Inventory::addList(const std::string &name, u32 size) if(m_lists[i]->getSize() != size) { delete m_lists[i]; - m_lists[i] = new InventoryList(name, size); + m_lists[i] = new InventoryList(name, size, m_itemdef); } return m_lists[i]; } else { - m_lists.push_back(new InventoryList(name, size)); - return m_lists.getLast(); + InventoryList *list = new InventoryList(name, size, m_itemdef); + m_lists.push_back(list); + return list; } } @@ -1034,7 +865,7 @@ bool Inventory::deleteList(const std::string &name) if(i == -1) return false; delete m_lists[i]; - m_lists.erase(i); + m_lists.erase(m_lists.begin() + i); return true; } diff --git a/src/inventory.h b/src/inventory.h index 15de3c8e7..3f5d83589 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -23,460 +23,221 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #include "common_irrlicht.h" #include "debug.h" -#include "mapnode.h" // For content_t +#include "itemdef.h" -#define QUANTITY_ITEM_MAX_COUNT 99 +struct ToolDiggingProperties; -class ServerActiveObject; -class ServerEnvironment; -struct PointedThing; -class ITextureSource; -class IGameDef; - -class InventoryItem +struct ItemStack { -public: - InventoryItem(IGameDef *gamedef, u16 count); - virtual ~InventoryItem(); - - static InventoryItem* deSerialize(std::istream &is, IGameDef *gamedef); - static InventoryItem* deSerialize(const std::string &str, - IGameDef *gamedef); - - virtual const char* getName() const = 0; - // Shall write the name and the parameters - virtual void serialize(std::ostream &os) const = 0; - // Shall make an exact clone of the item - virtual InventoryItem* clone() = 0; - // Return the name of the image for this item - virtual std::string getImageBasename() const { return ""; } -#ifndef SERVER - // Shall return an image of the item (or NULL) - virtual video::ITexture * getImage() const - { return NULL; } - // Shall return an image of the item without embellishments (or NULL) - virtual video::ITexture * getImageRaw() const - { return getImage(); } -#endif - // Shall return a text to show in the GUI - virtual std::string getText() { return ""; } + ItemStack(): name(""), count(0), wear(0), metadata("") {} + ItemStack(std::string name_, u16 count_, + u16 wear, std::string metadata_, + IItemDefManager *itemdef); + ~ItemStack() {} + + // Serialization + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is, IItemDefManager *itemdef); + void deSerialize(const std::string &s, IItemDefManager *itemdef); + // Returns the string used for inventory - virtual std::string getItemString(); - - // Shall return false if item is not known and cannot be used - virtual bool isKnown() const { return true; } + std::string getItemString() const; /* Quantity methods */ - // Return true if the item can be add()ed to the other - virtual bool addableTo(const InventoryItem *other) const - { 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; } - void setCount(u16 count) - { m_count = count; } - - u16 freeSpace() const + bool empty() const { - u16 max = getStackMax(); - if(m_count > max) - return 0; - return max - m_count; + return count == 0; } - void add(u16 count) + void clear() { - m_count += count; - } - void remove(u16 count) - { - assert(m_count >= count); - m_count -= count; + name = ""; + count = 0; + wear = 0; + metadata = ""; } - /* - Other properties - */ - - // Maximum size of a stack - virtual u16 getStackMax() const {return 1;} - // Whether it can be used - virtual bool isUsable() const {return false;} - // Whether it can be cooked - virtual bool isCookable() const {return false;} - // Result of cooking (can randomize) - virtual InventoryItem *createCookResult() const {return NULL;} - // Time of cooking - virtual float getCookTime() const {return 3.0;} - // Whether it can be burned (<0 = cannot be burned) - virtual float getBurnTime() const {return -1;} - // Gets amount of items that dropping one ItemSAO will decrement - // -1 means as many as possible - virtual s16 getDropCount() const { return -1; } - // Whether this item can point to liquids - virtual bool areLiquidsPointable() const { return false; } - - // Creates an object from the item and places it in the world. - // If return value is true, item should be removed. - virtual bool dropOrPlace(ServerEnvironment *env, - ServerActiveObject *dropper, - v3f pos, bool place, s16 count); - - // Eat, press, activate, whatever. - // Called when item is left-clicked while in hand. - // If returns true, item shall be deleted. - virtual bool use(ServerEnvironment *env, - ServerActiveObject *user, - const PointedThing& pointed){return false;} - -protected: - IGameDef *m_gamedef; - u16 m_count; -}; - -class MaterialItem : public InventoryItem -{ -public: - MaterialItem(IGameDef *gamedef, std::string nodename, u16 count); - // Legacy constructor - MaterialItem(IGameDef *gamedef, content_t content, u16 count); - /* - Implementation interface - */ - virtual const char* getName() const - { - return "MaterialItem"; - } - virtual void serialize(std::ostream &os) const - { - os<<"node"; - os<<" \""; - os<getName()) != "MaterialItem") - return false; - MaterialItem *m = (MaterialItem*)other; - if(m->m_nodename != m_nodename) - 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; + assert(count >= n); + count -= n; + if(count == 0) + clear(); // reset name, wear and metadata too } - virtual bool removeOther(const InventoryItem *other) - { - if(!other->isSubsetOf(this)) - return false; - MaterialItem *m = (MaterialItem*)other; - m_count += m->m_count; - return true; - } - - u16 getStackMax() const - { - return QUANTITY_ITEM_MAX_COUNT; - } - - /* - Other properties - */ - bool isCookable() const; - InventoryItem *createCookResult() const; - float getCookTime() const; - float getBurnTime() const; - /* - Special properties (not part of virtual interface) - */ - std::string getNodeName() const - { return m_nodename; } - content_t getMaterial() const; -private: - std::string m_nodename; -}; -/* - An item that is used as a mid-product when crafting. - Subnames: - - Stick -*/ -class CraftItem : public InventoryItem -{ -public: - CraftItem(IGameDef *gamedef, std::string subname, u16 count); - /* - Implementation interface - */ - virtual const char* getName() const - { - return "CraftItem"; - } - virtual void serialize(std::ostream &os) const - { - os<<"craft"; - os<<" \""; - os<getName()) != "CraftItem") - return false; - CraftItem *m = (CraftItem*)other; - if(m->m_subname != m_subname) - 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) + // Maximum size of a stack + u16 getStackMax(IItemDefManager *itemdef) const { - if(!other->isSubsetOf(this)) - return false; - CraftItem *m = (CraftItem*)other; - m_count += m->m_count; - return true; + s16 max = itemdef->get(name).stack_max; + return (max >= 0) ? max : 0; } - /* - Other properties - */ - - u16 getStackMax() const; - bool isUsable() const; - bool isCookable() const; - InventoryItem *createCookResult() const; - float getCookTime() const; - float getBurnTime() const; - s16 getDropCount() const; - bool areLiquidsPointable() const; - - bool dropOrPlace(ServerEnvironment *env, - ServerActiveObject *dropper, - v3f pos, bool place, s16 count); - bool use(ServerEnvironment *env, - ServerActiveObject *user, - const PointedThing& pointed); - - /* - Special methods - */ - std::string getSubName() + // Number of items that can be added to this stack + u16 freeSpace(IItemDefManager *itemdef) const { - return m_subname; + u16 max = getStackMax(itemdef); + if(count > max) + return 0; + return max - count; } -private: - std::string m_subname; -}; -class ToolItem : public InventoryItem -{ -public: - ToolItem(IGameDef *gamedef, std::string toolname, u16 wear); - /* - Implementation interface - */ - virtual const char* getName() const - { - return "ToolItem"; - } - virtual void serialize(std::ostream &os) const - { - os<<"tool"; - os<<" \""; - os<isKnown(name); } - std::string getImageBasename() const; -#ifndef SERVER - video::ITexture * getImage() const; - video::ITexture * getImageRaw() const; -#endif - - std::string getText() + // Returns a pointer to the item definition struct, + // or a fallback one (name="unknown") if the item is unknown. + const ItemDefinition& getDefinition( + IItemDefManager *itemdef) const { - return ""; + return itemdef->get(name); } - - virtual bool isKnown() const; - virtual bool isSubsetOf(const InventoryItem *other) const + // Get tool digging properties, or those of the hand if not a tool + const ToolDiggingProperties& getToolDiggingProperties( + IItemDefManager *itemdef) const { - if(std::string(other->getName()) != "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; + ToolDiggingProperties *prop; + prop = itemdef->get(name).tool_digging_properties; + if(prop == NULL) + prop = itemdef->get("").tool_digging_properties; + assert(prop != NULL); + return *prop; } - /* - Special methods - */ - std::string getToolName() - { - return m_toolname; - } - u16 getWear() + // Wear out (only tools) + // Returns true if the item is (was) a tool + bool addWear(s32 amount, IItemDefManager *itemdef) { - return m_wear; - } - // Returns true if weared out - bool addWear(u16 add) - { - if(m_wear >= 65535 - add) + if(getDefinition(itemdef).type == ITEM_TOOL) { - m_wear = 65535; + if(amount > 65535 - wear) + clear(); + else if(amount < -wear) + wear = 0; + else + wear += amount; return true; } else { - m_wear += add; return false; } } -private: - std::string m_toolname; - u16 m_wear; + + // If possible, adds newitem to this item. + // If cannot be added at all, returns the item back. + // If can be added partly, decremented item is returned back. + // If can be added fully, empty item is returned. + ItemStack addItem(const ItemStack &newitem, + IItemDefManager *itemdef); + + // Checks whether newitem could be added. + // If restitem is non-NULL, it receives the part of newitem that + // would be left over after adding. + bool itemFits(const ItemStack &newitem, + ItemStack *restitem, // may be NULL + IItemDefManager *itemdef) const; + + // Takes some items. + // If there are not enough, takes as many as it can. + // Returns empty item if couldn't take any. + ItemStack takeItem(u32 takecount); + + // Similar to takeItem, but keeps this ItemStack intact. + ItemStack peekItem(u32 peekcount) const; + + /* + Properties + */ + std::string name; + u16 count; + u16 wear; + std::string metadata; }; class InventoryList { public: - InventoryList(std::string name, u32 size); + InventoryList(std::string name, u32 size, IItemDefManager *itemdef); ~InventoryList(); void clearItems(); void setSize(u32 newsize); void serialize(std::ostream &os) const; - void deSerialize(std::istream &is, IGameDef *gamedef); + void deSerialize(std::istream &is); InventoryList(const InventoryList &other); InventoryList & operator = (const InventoryList &other); const std::string &getName() const; - u32 getSize(); + u32 getSize() const; // Count used slots - u32 getUsedSlots(); - u32 getFreeSlots(); - - /*bool getDirty(){ return m_dirty; } - void setDirty(bool dirty=true){ m_dirty = dirty; }*/ - - // Get pointer to item - const InventoryItem * getItem(u32 i) const; - InventoryItem * getItem(u32 i); - // Returns old item (or NULL). Parameter can be NULL. - InventoryItem * changeItem(u32 i, InventoryItem *newitem); + u32 getUsedSlots() const; + u32 getFreeSlots() const; + + // Get reference to item + const ItemStack& getItem(u32 i) const; + ItemStack& getItem(u32 i); + // Returns old item. Parameter can be an empty item. + ItemStack changeItem(u32 i, const ItemStack &newitem); // Delete item void deleteItem(u32 i); - // Adds an item to a suitable place. Returns leftover item. - // If all went into the list, returns NULL. - InventoryItem * addItem(InventoryItem *newitem); + // Adds an item to a suitable place. Returns leftover item (possibly empty). + ItemStack addItem(const ItemStack &newitem); // If possible, adds item to given slot. // If cannot be added at all, returns the item back. // If can be added partly, decremented item is returned back. - // If can be added fully, NULL is returned. - InventoryItem * addItem(u32 i, InventoryItem *newitem); + // If can be added fully, empty item is returned. + ItemStack addItem(u32 i, const ItemStack &newitem); // Checks whether the item could be added to the given slot - bool itemFits(const u32 i, const InventoryItem *newitem); + // If restitem is non-NULL, it receives the part of newitem that + // would be left over after adding. + bool itemFits(const u32 i, const ItemStack &newitem, + ItemStack *restitem = NULL) const; // Checks whether there is room for a given item - bool roomForItem(const InventoryItem *item); + bool roomForItem(const ItemStack &item) const; + + // Checks whether the given count of the given item name + // exists in this inventory list. + bool containsItem(const ItemStack &item) const; - // Checks whether there is room for a given item aftr it has been cooked - bool roomForCookedItem(const InventoryItem *item); + // Removes the given count of the given item name from + // this inventory list. Walks the list in reverse order. + // If not as many items exist as requested, removes as + // many as possible. + // Returns the items that were actually removed. + ItemStack removeItem(const ItemStack &item); // Takes some items from a slot. // If there are not enough, takes as many as it can. - // Returns NULL if couldn't take any. - InventoryItem * takeItem(u32 i, u32 count); + // Returns empty item if couldn't take any. + ItemStack takeItem(u32 i, u32 takecount); - // Decrements amount of every material item - void decrementMaterials(u16 count); + // Similar to takeItem, but keeps the slot intact. + ItemStack peekItem(u32 i, u32 peekcount) const; - void print(std::ostream &o); - private: - core::array m_items; + std::vector m_items; u32 m_size; std::string m_name; - //bool m_dirty; + IItemDefManager *m_itemdef; }; class Inventory @@ -486,20 +247,19 @@ public: void clear(); - Inventory(); + Inventory(IItemDefManager *itemdef); Inventory(const Inventory &other); Inventory & operator = (const Inventory &other); void serialize(std::ostream &os) const; - void deSerialize(std::istream &is, IGameDef *gamedef); + void deSerialize(std::istream &is); InventoryList * addList(const std::string &name, u32 size); InventoryList * getList(const std::string &name); const InventoryList * getList(const std::string &name) const; bool deleteList(const std::string &name); - // A shorthand for adding items. - // Returns NULL if the item was fully added, leftover otherwise. - InventoryItem * addItem(const std::string &listname, InventoryItem *newitem) + // A shorthand for adding items. Returns leftover item (possibly empty). + ItemStack addItem(const std::string &listname, const ItemStack &newitem) { InventoryList *list = getList(listname); if(list == NULL) @@ -511,7 +271,8 @@ private: // -1 if not found const s32 getListIndex(const std::string &name) const; - core::array m_lists; + std::vector m_lists; + IItemDefManager *m_itemdef; }; #endif diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index 195438d26..0149fd9ab 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -18,73 +18,91 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "inventorymanager.h" -#include "serverremoteplayer.h" #include "log.h" -#include "mapblock.h" // getNodeBlockPos +#include "environment.h" +#include "scriptapi.h" +#include "serverobject.h" +#include "main.h" // for g_settings +#include "settings.h" +#include "utility.h" /* - InventoryManager + InventoryLocation */ -// Wrapper for old code -Inventory* InventoryManager::getInventory(InventoryContext *c, std::string id) +std::string InventoryLocation::dump() const { - 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(",")); + std::ostringstream os(std::ios::binary); + serialize(os); + return os.str(); +} - InventoryLocation loc; - loc.setNodeMeta(p); - return getInventory(loc); +void InventoryLocation::serialize(std::ostream &os) const +{ + switch(type){ + case InventoryLocation::UNDEFINED: + { + os<<"undefined"; } - - errorstream<<__FUNCTION_NAME<<": unknown id "<current_player); - InventoryLocation loc; - loc.setPlayer(c->current_player->getName()); - setInventoryModified(loc); - return; + type = InventoryLocation::UNDEFINED; } - - Strfnd fn(id); - std::string id0 = fn.next(":"); - - if(id0 == "nodemeta") + else if(tname == "current_player") + { + type = InventoryLocation::CURRENT_PLAYER; + } + else if(tname == "player") + { + type = InventoryLocation::PLAYER; + std::getline(is, name, '\n'); + } + else if(tname == "nodemeta") { - v3s16 p; + type = InventoryLocation::NODEMETA; + std::string pos; + std::getline(is, pos, '\n'); + Strfnd fn(pos); 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; } + else + { + infostream<<"Unknown InventoryLocation type=\""<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; @@ -125,14 +135,16 @@ IMoveAction::IMoveAction(std::istream &is) std::getline(is, ts, ' '); count = stoi(ts); - std::getline(is, from_inv, ' '); + std::getline(is, ts, ' '); + from_inv.deSerialize(ts); std::getline(is, from_list, ' '); std::getline(is, ts, ' '); from_i = stoi(ts); - std::getline(is, to_inv, ' '); + std::getline(is, ts, ' '); + to_inv.deSerialize(ts); std::getline(is, to_list, ' '); @@ -140,22 +152,21 @@ IMoveAction::IMoveAction(std::istream &is) to_i = stoi(ts); } -void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env) +void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player) { - Inventory *inv_from = mgr->getInventory(c, from_inv); - Inventory *inv_to = mgr->getInventory(c, to_inv); + Inventory *inv_from = mgr->getInventory(from_inv); + Inventory *inv_to = mgr->getInventory(to_inv); if(!inv_from){ infostream<<"IMoveAction::apply(): FAIL: source inventory not found: " - <<"context=["<getItem(from_i) == NULL) + if(list_from->getItem(from_i).empty()) { infostream<<"IMoveAction::apply(): FAIL: source item not found: " - <<"context=["<( - static_cast( - c->current_player - )); - - Inventory *inv_from = mgr->getInventory(c, from_inv); + Inventory *inv_from = mgr->getInventory(from_inv); if(!inv_from){ infostream<<"IDropAction::apply(): FAIL: source inventory not found: " - <<"context=["<getItem(from_i); - if(item == NULL) + if(list_from->getItem(from_i).empty()) { infostream<<"IDropAction::apply(): FAIL: source item not found: " - <<"context=["<changeItem(from_i, item); + mgr->setInventoryModified(from_inv); + } infostream<<"IDropAction::apply(): dropped " - <<"["<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 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; -} - - diff --git a/src/inventorymanager.h b/src/inventorymanager.h index 4abe5e73d..f6ae4feba 100644 --- a/src/inventorymanager.h +++ b/src/inventorymanager.h @@ -21,19 +21,34 @@ with this program; if not, write to the Free Software Foundation, Inc., #define INVENTORYMANAGER_HEADER #include "inventory.h" +#include +#include +class ServerActiveObject; -// Should probably somehow replace InventoryContext over time struct InventoryLocation { enum Type{ UNDEFINED, + CURRENT_PLAYER, PLAYER, - NODEMETA + NODEMETA, } type; std::string name; // PLAYER v3s16 p; // NODEMETA + InventoryLocation() + { + setUndefined(); + } + void setUndefined() + { + type = UNDEFINED; + } + void setCurrentPlayer() + { + type = CURRENT_PLAYER; + } void setPlayer(const std::string &name_) { type = PLAYER; @@ -44,17 +59,17 @@ struct InventoryLocation type = NODEMETA; p = p_; } -}; -class Player; + void applyCurrentPlayer(const std::string &name_) + { + if(type == CURRENT_PLAYER) + setPlayer(name_); + } -struct InventoryContext -{ - Player *current_player; - - InventoryContext(): - current_player(NULL) - {} + std::string dump() const; + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); + void deSerialize(std::string s); }; struct InventoryAction; @@ -68,18 +83,11 @@ public: // Get an inventory or set it modified (so it will be updated over // network or so) virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;} + virtual std::string getInventoryOwner(const InventoryLocation &loc){return "";} virtual void setInventoryModified(const InventoryLocation &loc){} // Used on the client to send an action to the server virtual void inventoryAction(InventoryAction *a){} - - // (Deprecated; these wrap to the latter ones) - // Get a pointer to an inventory specified by id. id can be: - // - "current_player" - // - "nodemeta:X,Y,Z" - Inventory* getInventory(InventoryContext *c, std::string id); - // Used on the server by InventoryAction::apply and other stuff - void inventoryModified(InventoryContext *c, std::string id); }; #define IACTION_MOVE 0 @@ -91,18 +99,17 @@ struct InventoryAction virtual u16 getType() const = 0; virtual void serialize(std::ostream &os) const = 0; - virtual void apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env) = 0; + virtual void apply(InventoryManager *mgr, ServerActiveObject *player) = 0; }; struct IMoveAction : public InventoryAction { // count=0 means "everything" u16 count; - std::string from_inv; + InventoryLocation from_inv; std::string from_list; s16 from_i; - std::string to_inv; + InventoryLocation to_inv; std::string to_list; s16 to_i; @@ -124,23 +131,22 @@ struct IMoveAction : public InventoryAction { os<<"Move "; os< v2s32; typedef core::vector2d v2u32; typedef core::vector2d v2f32; +typedef core::aabbox3d aabb3f; + #ifdef _MSC_VER // Windows typedef unsigned long long u64; diff --git a/src/itemdef.cpp b/src/itemdef.cpp new file mode 100644 index 000000000..aa888bbdf --- /dev/null +++ b/src/itemdef.cpp @@ -0,0 +1,502 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola +Copyright (C) 2011 Kahrl + +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 "itemdef.h" + +#include "gamedef.h" +#include "nodedef.h" +#include "materials.h" +#include "inventory.h" +#ifndef SERVER +#include "mapblock_mesh.h" +#include "mesh.h" +#include "tile.h" +#endif +#include "log.h" +#include "utility.h" +#include +#include + +/* + ItemDefinition +*/ +ItemDefinition::ItemDefinition() +{ + resetInitial(); +} + +ItemDefinition::ItemDefinition(const ItemDefinition &def) +{ + resetInitial(); + *this = def; +} + +ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) +{ + if(this == &def) + return *this; + + reset(); + + type = def.type; + name = def.name; + description = def.description; + inventory_image = def.inventory_image; + wield_image = def.wield_image; + wield_scale = def.wield_scale; + stack_max = def.stack_max; + usable = def.usable; + liquids_pointable = def.liquids_pointable; + if(def.tool_digging_properties) + { + tool_digging_properties = new ToolDiggingProperties( + *def.tool_digging_properties); + } +#ifndef SERVER + inventory_texture = def.inventory_texture; + if(def.wield_mesh) + { + wield_mesh = def.wield_mesh; + wield_mesh->grab(); + } +#endif + return *this; +} + +ItemDefinition::~ItemDefinition() +{ + reset(); +} + +void ItemDefinition::resetInitial() +{ + // Initialize pointers to NULL so reset() does not delete undefined pointers + tool_digging_properties = NULL; +#ifndef SERVER + inventory_texture = NULL; + wield_mesh = NULL; +#endif + reset(); +} + +void ItemDefinition::reset() +{ + type = ITEM_NONE; + name = ""; + description = ""; + inventory_image = ""; + wield_image = ""; + wield_scale = v3f(1.0, 1.0, 1.0); + stack_max = 99; + usable = false; + liquids_pointable = false; + if(tool_digging_properties) + { + delete tool_digging_properties; + tool_digging_properties = NULL; + } + +#ifndef SERVER + inventory_texture = NULL; + if(wield_mesh) + { + wield_mesh->drop(); + wield_mesh = NULL; + } +#endif +} + +void ItemDefinition::serialize(std::ostream &os) const +{ + writeU8(os, 0); // version + writeU8(os, type); + os<serialize(tmp_os); + tool_digging_properties_s = tmp_os.str(); + } + os<deSerialize(tmp_is); + } +} + +/* + CItemDefManager +*/ + +// SUGG: Support chains of aliases? + +class CItemDefManager: public IWritableItemDefManager +{ +public: + CItemDefManager() + { + clear(); + } + virtual ~CItemDefManager() + { + } + virtual const ItemDefinition& get(const std::string &name_) const + { + // Convert name according to possible alias + std::string name = getAlias(name_); + // Get the definition + std::map::const_iterator i; + i = m_item_definitions.find(name); + if(i == m_item_definitions.end()) + i = m_item_definitions.find("unknown"); + assert(i != m_item_definitions.end()); + return *(i->second); + } + virtual std::string getAlias(const std::string &name) const + { + std::map::const_iterator i; + i = m_aliases.find(name); + if(i != m_aliases.end()) + return i->second; + return name; + } + virtual std::set getAll() const + { + std::set result; + for(std::map::const_iterator + i = m_item_definitions.begin(); + i != m_item_definitions.end(); i++) + { + result.insert(i->first); + } + for(std::map::const_iterator + i = m_aliases.begin(); + i != m_aliases.end(); i++) + { + result.insert(i->first); + } + return result; + } + virtual bool isKnown(const std::string &name_) const + { + // Convert name according to possible alias + std::string name = getAlias(name_); + // Get the definition + std::map::const_iterator i; + return m_item_definitions.find(name) != m_item_definitions.end(); + } + void clear() + { + for(std::map::const_iterator + i = m_item_definitions.begin(); + i != m_item_definitions.end(); i++) + { + delete i->second; + } + m_item_definitions.clear(); + m_aliases.clear(); + + // Add the four builtin items: + // "" is the hand + // "unknown" is returned whenever an undefined item is accessed + // "air" is the air node + // "ignore" is the ignore node + + ItemDefinition* hand_def = new ItemDefinition; + hand_def->name = ""; + hand_def->wield_image = "wieldhand.png"; + hand_def->tool_digging_properties = new ToolDiggingProperties; + m_item_definitions.insert(std::make_pair("", hand_def)); + + ItemDefinition* unknown_def = new ItemDefinition; + unknown_def->name = "unknown"; + m_item_definitions.insert(std::make_pair("unknown", unknown_def)); + + ItemDefinition* air_def = new ItemDefinition; + air_def->type = ITEM_NODE; + air_def->name = "air"; + m_item_definitions.insert(std::make_pair("air", air_def)); + + ItemDefinition* ignore_def = new ItemDefinition; + ignore_def->type = ITEM_NODE; + ignore_def->name = "ignore"; + m_item_definitions.insert(std::make_pair("ignore", ignore_def)); + } + virtual void registerItem(const ItemDefinition &def) + { + infostream<<"ItemDefManager: registering \""< "<getTextureSource(); + INodeDefManager *nodedef = gamedef->getNodeDefManager(); + IrrlichtDevice *device = tsrc->getDevice(); + video::IVideoDriver *driver = device->getVideoDriver(); + + for(std::map::iterator + i = m_item_definitions.begin(); + i != m_item_definitions.end(); i++) + { + ItemDefinition *def = i->second; + + bool need_node_mesh = false; + + // Create an inventory texture + def->inventory_texture = NULL; + if(def->inventory_image != "") + { + def->inventory_texture = tsrc->getTextureRaw(def->inventory_image); + } + else if(def->type == ITEM_NODE) + { + need_node_mesh = true; + } + + // Create a wield mesh + if(def->wield_mesh != NULL) + { + def->wield_mesh->drop(); + def->wield_mesh = NULL; + } + if(def->type == ITEM_NODE && def->wield_image == "") + { + need_node_mesh = true; + } + else if(def->wield_image != "" || def->inventory_image != "") + { + // Extrude the wield image into a mesh + + std::string imagename; + if(def->wield_image != "") + imagename = def->wield_image; + else + imagename = def->inventory_image; + + def->wield_mesh = createExtrudedMesh( + tsrc->getTextureRaw(imagename), + driver, + def->wield_scale * v3f(40.0, 40.0, 4.0)); + if(def->wield_mesh == NULL) + { + infostream<<"ItemDefManager: WARNING: " + <<"updateTexturesAndMeshes(): " + <<"Unable to create extruded mesh for item " + <name<getId(def->name); + const ContentFeatures &f = nodedef->get(id); + + /* + Make a mesh from the node + */ + MeshMakeData mesh_make_data; + MapNode mesh_make_node( + id, + (f.param_type == CPT_LIGHT) ? 0xee : 0, + 0); + mesh_make_data.fillSingleNode(1000, &mesh_make_node); + scene::IMesh *node_mesh = + makeMapBlockMesh(&mesh_make_data, gamedef); + setMeshColor(node_mesh, video::SColor(255, 255, 255, 255)); + + /* + Scale and translate the mesh so it's a unit cube + centered on the origin + */ + scaleMesh(node_mesh, v3f(1.0/BS, 1.0/BS, 1.0/BS)); + translateMesh(node_mesh, v3f(-1.0, -1.0, -1.0)); + + /* + Draw node mesh into a render target texture + */ + if(def->inventory_texture == NULL && node_mesh != NULL) + { + core::dimension2d dim(64,64); + std::string rtt_texture_name = "INVENTORY_" + + def->name + "_RTT"; + v3f camera_position(0, 1.0, -1.5); + camera_position.rotateXZBy(45); + v3f camera_lookat(0, 0, 0); + core::CMatrix4 camera_projection_matrix; + // Set orthogonal projection + camera_projection_matrix.buildProjectionMatrixOrthoLH( + 1.65, 1.65, 0, 100); + + video::SColorf ambient_light(0.2,0.2,0.2); + v3f light_position(10, 100, -50); + video::SColorf light_color(0.5,0.5,0.5); + f32 light_radius = 1000; + + def->inventory_texture = generateTextureFromMesh( + node_mesh, device, dim, rtt_texture_name, + camera_position, + camera_lookat, + camera_projection_matrix, + ambient_light, + light_position, + light_color, + light_radius); + // Note: might have returned NULL + } + + /* + Use the node mesh as the wield mesh + */ + if(def->wield_mesh == NULL && node_mesh != NULL) + { + // Scale to proper wield mesh proportions + scaleMesh(node_mesh, v3f(30.0, 30.0, 30.0) + * def->wield_scale); + def->wield_mesh = node_mesh; + def->wield_mesh->grab(); + } + + + if(node_mesh != NULL) + node_mesh->drop(); + } + } +#endif + } + void serialize(std::ostream &os) + { + writeU8(os, 0); // version + u16 count = m_item_definitions.size(); + writeU16(os, count); + for(std::map::const_iterator + i = m_item_definitions.begin(); + i != m_item_definitions.end(); i++) + { + ItemDefinition *def = i->second; + // Serialize ItemDefinition and write wrapped in a string + std::ostringstream tmp_os(std::ios::binary); + def->serialize(tmp_os); + os<::const_iterator + i = m_aliases.begin(); i != m_aliases.end(); i++) + { + os<first); + os<second); + } + } + void deSerialize(std::istream &is) + { + // Clear everything + clear(); + // Deserialize + int version = readU8(is); + if(version != 0) + throw SerializationError("unsupported ItemDefManager version"); + u16 count = readU16(is); + for(u16 i=0; i m_item_definitions; + // Aliases + std::map m_aliases; +}; + +IWritableItemDefManager* createItemDefManager() +{ + return new CItemDefManager(); +} + diff --git a/src/itemdef.h b/src/itemdef.h new file mode 100644 index 000000000..5a432591d --- /dev/null +++ b/src/itemdef.h @@ -0,0 +1,147 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola +Copyright (C) 2011 Kahrl + +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 ITEMDEF_HEADER +#define ITEMDEF_HEADER + +#include "common_irrlicht.h" +#include +#include +#include +class IGameDef; +struct ToolDiggingProperties; + +/* + Base item definition +*/ + +enum ItemType +{ + ITEM_NONE, + ITEM_NODE, + ITEM_CRAFT, + ITEM_TOOL, +}; + +struct ItemDefinition +{ + /* + Basic item properties + */ + ItemType type; + std::string name; // "" = hand + std::string description; // Shown in tooltip. + + /* + Visual properties + */ + std::string inventory_image; // Optional for nodes, mandatory for tools/craftitems + std::string wield_image; // If empty, inventory_image or mesh (only nodes) is used + v3f wield_scale; + + /* + Item stack and interaction properties + */ + s16 stack_max; + bool usable; + bool liquids_pointable; + // May be NULL. If non-NULL, deleted by destructor + ToolDiggingProperties *tool_digging_properties; + + /* + Cached stuff + */ +#ifndef SERVER + video::ITexture *inventory_texture; + scene::IMesh *wield_mesh; +#endif + + /* + Some helpful methods + */ + ItemDefinition(); + ItemDefinition(const ItemDefinition &def); + ItemDefinition& operator=(const ItemDefinition &def); + ~ItemDefinition(); + void reset(); + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is); +private: + void resetInitial(); +}; + +class IItemDefManager +{ +public: + IItemDefManager(){} + virtual ~IItemDefManager(){} + + // Get item definition + virtual const ItemDefinition& get(const std::string &name) const=0; + // Get alias definition + virtual std::string getAlias(const std::string &name) const=0; + // Get set of all defined item names and aliases + virtual std::set getAll() const=0; + // Check if item is known + virtual bool isKnown(const std::string &name) const=0; + + virtual void serialize(std::ostream &os)=0; +}; + +class IWritableItemDefManager : public IItemDefManager +{ +public: + IWritableItemDefManager(){} + virtual ~IWritableItemDefManager(){} + + // Get item definition + virtual const ItemDefinition& get(const std::string &name) const=0; + // Get alias definition + virtual std::string getAlias(const std::string &name) const=0; + // Get set of all defined item names and aliases + virtual std::set getAll() const=0; + // Check if item is known + virtual bool isKnown(const std::string &name) const=0; + + // Remove all registered item and node definitions and aliases + // Then re-add the builtin item definitions + virtual void clear()=0; + // Register item definition + virtual void registerItem(const ItemDefinition &def)=0; + // Set an alias so that items named will load as . + // Alias is not set if has already been defined. + // Alias will be removed if is defined at a later point of time. + virtual void registerAlias(const std::string &name, + const std::string &convert_to)=0; + + /* + Update inventory textures and wield meshes to latest + return values of ITextureSource and INodeDefManager. + Call after updating the texture atlas of a texture source. + */ + virtual void updateTexturesAndMeshes(IGameDef *gamedef)=0; + + virtual void serialize(std::ostream &os)=0; + virtual void deSerialize(std::istream &is)=0; +}; + +IWritableItemDefManager* createItemDefManager(); + +#endif diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 016cb222e..73c13caca 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -30,6 +30,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "nameidmapping.h" #include "content_mapnode.h" // For legacy name-id mapping +#ifndef SERVER +#include "mapblock_mesh.h" +#endif /* MapBlock diff --git a/src/mapblock.h b/src/mapblock.h index aa5aa43f7..f2d94753c 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -32,9 +32,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "voxel.h" #include "staticobject.h" #include "mapblock_nodemod.h" -#ifndef SERVER - #include "mapblock_mesh.h" -#endif #include "modifiedstate.h" class Map; diff --git a/src/mapblock_mesh.cpp b/src/mapblock_mesh.cpp index c051dda50..db9057e19 100644 --- a/src/mapblock_mesh.cpp +++ b/src/mapblock_mesh.cpp @@ -25,7 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "profiler.h" #include "nodedef.h" -#include "tile.h" #include "gamedef.h" #include "content_mapblock.h" #include "mineral.h" // For mineral_block_texture @@ -83,6 +82,39 @@ void MeshMakeData::fill(u32 daynight_ratio, MapBlock *block) } } +void MeshMakeData::fillSingleNode(u32 daynight_ratio, MapNode *node) +{ + m_daynight_ratio = daynight_ratio; + m_blockpos = v3s16(0,0,0); + m_temp_mods.clear(); + + v3s16 blockpos_nodes = v3s16(0,0,0); + VoxelArea area(blockpos_nodes-v3s16(1,1,1)*MAP_BLOCKSIZE, + blockpos_nodes+v3s16(1,1,1)*MAP_BLOCKSIZE*2-v3s16(1,1,1)); + s32 volume = area.getVolume(); + s32 our_node_index = area.index(1,1,1); + + // Allocate this block + neighbors + m_vmanip.clear(); + m_vmanip.addArea(area); + + // Fill in data + MapNode *data = new MapNode[volume]; + for(s32 i = 0; i < volume; i++) + { + if(i == our_node_index) + { + data[i] = *node; + } + else + { + data[i] = MapNode(CONTENT_AIR, LIGHT_MAX, 0); + } + } + m_vmanip.copyFrom(data, area, area.MinEdge, area.MinEdge, area.getExtent()); + delete[] data; +} + /* vertex_dirs: v3s16[4] */ @@ -207,8 +239,8 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y; else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z; - v3f zerovector = v3f(0,0,0); - + v3f normal(dir.X, dir.Y, dir.Z); + u8 alpha = tile.alpha; /*u8 alpha = 255; if(tile.id == TILE_WATER) @@ -230,16 +262,16 @@ static void makeFastFace(TileSpec tile, u8 li0, u8 li1, u8 li2, u8 li3, v3f p, face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), c, core::vector2d(x0+w*abs_scale, y0));*/ - face.vertices[0] = video::S3DVertex(vertex_pos[0], v3f(0,1,0), + face.vertices[0] = video::S3DVertex(vertex_pos[0], normal, MapBlock_LightColor(alpha, li0), core::vector2d(x0+w*abs_scale, y0+h)); - face.vertices[1] = video::S3DVertex(vertex_pos[1], v3f(0,1,0), + face.vertices[1] = video::S3DVertex(vertex_pos[1], normal, MapBlock_LightColor(alpha, li1), core::vector2d(x0, y0+h)); - face.vertices[2] = video::S3DVertex(vertex_pos[2], v3f(0,1,0), + face.vertices[2] = video::S3DVertex(vertex_pos[2], normal, MapBlock_LightColor(alpha, li2), core::vector2d(x0, y0)); - face.vertices[3] = video::S3DVertex(vertex_pos[3], v3f(0,1,0), + face.vertices[3] = video::S3DVertex(vertex_pos[3], normal, MapBlock_LightColor(alpha, li3), core::vector2d(x0+w*abs_scale, y0)); @@ -309,8 +341,8 @@ static TileSpec getTile(const MapNode &node, v3s16 dir, Gets node tile from any place relative to block. Returns TILE_NODE if doesn't exist or should not be drawn. */ -static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir, - NodeModMap &temp_mods, ITextureSource *tsrc, INodeDefManager *ndef) +TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir, + NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef) { TileSpec spec; spec = getTile(mn, face_dir, tsrc, ndef); @@ -325,13 +357,15 @@ static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir, { struct NodeMod mod = n->getValue();*/ NodeMod mod; - if(temp_mods.get(p, &mod)) + if(temp_mods && temp_mods->get(p, &mod)) { + #if 0 // NODEMOD_CHANGECONTENT isn't used at the moment if(mod.type == NODEMOD_CHANGECONTENT) { MapNode mn2(mod.param); spec = getTile(mn2, face_dir, tsrc, ndef); } + #endif if(mod.type == NODEMOD_CRACK) { /* @@ -361,19 +395,14 @@ static TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir, return spec; } -static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods) +static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap *temp_mods) { /* Check temporary modifications on this node */ - /*core::map::Node *n; - n = m_temp_mods.find(p); - // If modified - if(n != NULL) - { - struct NodeMod mod = n->getValue();*/ + #if 0 // NODEMOD_CHANGECONTENT isn't used at the moment NodeMod mod; - if(temp_mods.get(p, &mod)) + if(temp_mods && temp_mods->get(p, &mod)) { if(mod.type == NODEMOD_CHANGECONTENT) { @@ -395,6 +424,7 @@ static content_t getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods) */ } } + #endif return mn.getContent(); } @@ -469,7 +499,7 @@ static void getTileInfo( v3s16 face_dir, u32 daynight_ratio, VoxelManipulator &vmanip, - NodeModMap &temp_mods, + NodeModMap *temp_mods, bool smooth_lighting, IGameDef *gamedef, // Output: @@ -553,7 +583,7 @@ static void updateFastFaceRow( v3s16 face_dir, v3f face_dir_f, core::array &dest, - NodeModMap &temp_mods, + NodeModMap *temp_mods, VoxelManipulator &vmanip, v3s16 blockpos_nodes, bool smooth_lighting, @@ -749,7 +779,7 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef) v3s16(0,1,0), //face dir v3f (0,1,0), fastfaces_new, - data->m_temp_mods, + &data->m_temp_mods, data->m_vmanip, blockpos_nodes, smooth_lighting, @@ -768,7 +798,7 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef) v3s16(1,0,0), v3f (1,0,0), fastfaces_new, - data->m_temp_mods, + &data->m_temp_mods, data->m_vmanip, blockpos_nodes, smooth_lighting, @@ -787,7 +817,7 @@ scene::SMesh* makeMapBlockMesh(MeshMakeData *data, IGameDef *gamedef) v3s16(0,0,1), v3f (0,0,1), fastfaces_new, - data->m_temp_mods, + &data->m_temp_mods, data->m_vmanip, blockpos_nodes, smooth_lighting, diff --git a/src/mapblock_mesh.h b/src/mapblock_mesh.h index 36cc9be24..4d3e7d29d 100644 --- a/src/mapblock_mesh.h +++ b/src/mapblock_mesh.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common_irrlicht.h" #include "mapblock_nodemod.h" +#include "tile.h" #include "voxel.h" class IGameDef; @@ -125,6 +126,8 @@ private: // Helper functions video::SColor MapBlock_LightColor(u8 alpha, u8 light); +TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir, + NodeModMap *temp_mods, ITextureSource *tsrc, INodeDefManager *ndef); class MapBlock; @@ -140,6 +143,11 @@ struct MeshMakeData parent of block. */ void fill(u32 daynight_ratio, MapBlock *block); + + /* + Set up with only a single node at (1,1,1) + */ + void fillSingleNode(u32 daynight_ratio, MapNode *node); }; // This is the highest-level function in here diff --git a/src/materials.cpp b/src/materials.cpp index d2d37ebf4..146209d58 100644 --- a/src/materials.cpp +++ b/src/materials.cpp @@ -20,7 +20,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "materials.h" #include "mapnode.h" #include "nodedef.h" -#include "tooldef.h" #include "utility.h" void MaterialProperties::serialize(std::ostream &os) @@ -49,6 +48,56 @@ void MaterialProperties::deSerialize(std::istream &is) flammability = readF1000(is); } +ToolDiggingProperties::ToolDiggingProperties(float full_punch_interval_, + float a, float b, float c, float d, float e, + float f, float g, float h, float i, float j): + full_punch_interval(full_punch_interval_), + basetime(a), + dt_weight(b), + dt_crackiness(c), + dt_crumbliness(d), + dt_cuttability(e), + basedurability(f), + dd_weight(g), + dd_crackiness(h), + dd_crumbliness(i), + dd_cuttability(j) +{} + +void ToolDiggingProperties::serialize(std::ostream &os) +{ + writeU8(os, 0); // version + writeF1000(os, full_punch_interval); + writeF1000(os, basetime); + writeF1000(os, dt_weight); + writeF1000(os, dt_crackiness); + writeF1000(os, dt_crumbliness); + writeF1000(os, dt_cuttability); + writeF1000(os, basedurability); + writeF1000(os, dd_weight); + writeF1000(os, dd_crackiness); + writeF1000(os, dd_crumbliness); + writeF1000(os, dd_cuttability); +} + +void ToolDiggingProperties::deSerialize(std::istream &is) +{ + int version = readU8(is); + if(version != 0) throw SerializationError( + "unsupported ToolDiggingProperties version"); + full_punch_interval = readF1000(is); + basetime = readF1000(is); + dt_weight = readF1000(is); + dt_crackiness = readF1000(is); + dt_crumbliness = readF1000(is); + dt_cuttability = readF1000(is); + basedurability = readF1000(is); + dd_weight = readF1000(is); + dd_crackiness = readF1000(is); + dd_crumbliness = readF1000(is); + dd_cuttability = readF1000(is); +} + DiggingProperties getDiggingProperties(const MaterialProperties *mp, const ToolDiggingProperties *tp, float time_from_last_punch) { diff --git a/src/materials.h b/src/materials.h index b25d047be..face7d5df 100644 --- a/src/materials.h +++ b/src/materials.h @@ -72,6 +72,29 @@ struct MaterialProperties void deSerialize(std::istream &is); }; +struct ToolDiggingProperties +{ + // time = basetime + sum(feature here * feature in MaterialProperties) + float full_punch_interval; + float basetime; + float dt_weight; + float dt_crackiness; + float dt_crumbliness; + float dt_cuttability; + float basedurability; + float dd_weight; + float dd_crackiness; + float dd_crumbliness; + float dd_cuttability; + + ToolDiggingProperties(float full_punch_interval_=2.0, + float a=0.75, float b=0, float c=0, float d=0, float e=0, + float f=50, float g=0, float h=0, float i=0, float j=0); + + void serialize(std::ostream &os); + void deSerialize(std::istream &is); +}; + struct DiggingProperties { bool diggable; @@ -87,7 +110,6 @@ struct DiggingProperties {} }; -struct ToolDiggingProperties; class INodeDefManager; DiggingProperties getDiggingProperties(const MaterialProperties *mp, diff --git a/src/mesh.cpp b/src/mesh.cpp index 1d347a09f..5afb4af59 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -18,8 +18,12 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "mesh.h" +#include "log.h" +#include +#include #include #include +#include // In Irrlicht 1.8 the signature of ITexture::lock was changed from // (bool, u32) to (E_TEXTURE_LOCK_MODE, u32). @@ -73,9 +77,15 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale) { scene::IMeshBuffer *buf = new scene::SMeshBuffer(); buf->append(vertices + 4 * i, 4, indices, 6); + // Set default material + buf->getMaterial().setFlag(video::EMF_LIGHTING, false); + buf->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + // Add mesh buffer to mesh mesh->addMeshBuffer(buf); buf->drop(); } + scene::SAnimatedMesh *anim_mesh = new scene::SAnimatedMesh(mesh); mesh->drop(); scaleMesh(anim_mesh, scale); // also recalculates bounding box @@ -280,6 +290,13 @@ scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture, } img1->drop(); } + + // Set default material + mesh->getMeshBuffer(0)->getMaterial().setTexture(0, texture); + mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_LIGHTING, false); + mesh->getMeshBuffer(0)->getMaterial().setFlag(video::EMF_BILINEAR_FILTER, false); + mesh->getMeshBuffer(0)->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + scaleMesh(mesh, scale); // also recalculates bounding box return mesh; } @@ -313,6 +330,35 @@ void scaleMesh(scene::IMesh *mesh, v3f scale) mesh->setBoundingBox(bbox); } +void translateMesh(scene::IMesh *mesh, v3f vec) +{ + if(mesh == NULL) + return; + + core::aabbox3d bbox; + bbox.reset(0,0,0); + + u16 mc = mesh->getMeshBufferCount(); + for(u16 j=0; jgetMeshBuffer(j); + video::S3DVertex *vertices = (video::S3DVertex*)buf->getVertices(); + u16 vc = buf->getVertexCount(); + for(u16 i=0; irecalculateBoundingBox(); + + // calculate total bounding box + if(j == 0) + bbox = buf->getBoundingBox(); + else + bbox.addInternalBox(buf->getBoundingBox()); + } + mesh->setBoundingBox(bbox); +} + void setMeshColor(scene::IMesh *mesh, const video::SColor &color) { if(mesh == NULL) @@ -360,3 +406,74 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh, } } } + +video::ITexture *generateTextureFromMesh(scene::IMesh *mesh, + IrrlichtDevice *device, + core::dimension2d dim, + std::string texture_name, + v3f camera_position, + v3f camera_lookat, + core::CMatrix4 camera_projection_matrix, + video::SColorf ambient_light, + v3f light_position, + video::SColorf light_color, + f32 light_radius) +{ + video::IVideoDriver *driver = device->getVideoDriver(); + if(driver->queryFeature(video::EVDF_RENDER_TO_TARGET) == false) + { + errorstream<<"generateTextureFromMesh(): EVDF_RENDER_TO_TARGET" + " not supported."<addRenderTargetTexture( + dim, texture_name.c_str(), video::ECF_A8R8G8B8); + if(rtt == NULL) + { + errorstream<<"generateTextureFromMesh(): addRenderTargetTexture" + " returned NULL."<setRenderTarget(rtt, true, true, video::SColor(0,0,0,0)); + + // Get a scene manager + scene::ISceneManager *smgr_main = device->getSceneManager(); + assert(smgr_main); + scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); + assert(smgr); + + scene::IMeshSceneNode* meshnode = smgr->addMeshSceneNode(mesh, NULL, -1, v3f(0,0,0), v3f(0,0,0), v3f(1,1,1), true); + meshnode->setMaterialFlag(video::EMF_LIGHTING, true); + meshnode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); + meshnode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true); + + scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, + camera_position, camera_lookat); + // second parameter of setProjectionMatrix (isOrthogonal) is ignored + camera->setProjectionMatrix(camera_projection_matrix, false); + + smgr->setAmbientLight(ambient_light); + smgr->addLightSceneNode(0, light_position, light_color, light_radius); + + // Render scene + driver->beginScene(true, true, video::SColor(0,0,0,0)); + smgr->drawAll(); + driver->endScene(); + + // NOTE: The scene nodes should not be dropped, otherwise + // smgr->drop() segfaults + /*cube->drop(); + camera->drop(); + light->drop();*/ + // Drop scene manager + smgr->drop(); + + // Unset render target + driver->setRenderTarget(0, true, true, 0); + + return rtt; +} diff --git a/src/mesh.h b/src/mesh.h index 33e072927..57166235e 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MESH_HEADER #include "common_irrlicht.h" +#include /* Create a new cube mesh. @@ -47,6 +48,11 @@ scene::IAnimatedMesh* createExtrudedMesh(video::ITexture *texture, */ void scaleMesh(scene::IMesh *mesh, v3f scale); +/* + Translate each vertex coordinate by the specified vector. +*/ +void translateMesh(scene::IMesh *mesh, v3f vec); + /* Set a constant color for all vertices in the mesh */ @@ -63,4 +69,20 @@ void setMeshColorByNormalXYZ(scene::IMesh *mesh, const video::SColor &colorY, const video::SColor &colorZ); +/* + Render a mesh to a texture. + Returns NULL if render-to-texture failed. +*/ +video::ITexture *generateTextureFromMesh(scene::IMesh *mesh, + IrrlichtDevice *device, + core::dimension2d dim, + std::string texture_name, + v3f camera_position, + v3f camera_lookat, + core::CMatrix4 camera_projection_matrix, + video::SColorf ambient_light, + v3f light_position, + video::SColorf light_color, + f32 light_radius); + #endif diff --git a/src/mineral.cpp b/src/mineral.cpp index 038251fa3..4e495fce6 100644 --- a/src/mineral.cpp +++ b/src/mineral.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "mineral.h" +#include "gamedef.h" const char *mineral_filenames[MINERAL_COUNT] = @@ -47,5 +48,15 @@ std::string mineral_block_texture(u8 mineral) return mineral_textures[mineral]; } +ItemStack getDiggedMineralItem(u8 mineral, IGameDef *gamedef) +{ + if(mineral == MINERAL_COAL) + return ItemStack("default:coal_lump", 1, 0, "", gamedef->idef()); + else if(mineral == MINERAL_IRON) + return ItemStack("default:iron_lump", 1, 0, "", gamedef->idef()); + else + return ItemStack(); +} + diff --git a/src/mineral.h b/src/mineral.h index 4949fe07e..a945d0e02 100644 --- a/src/mineral.h +++ b/src/mineral.h @@ -38,19 +38,10 @@ void init_mineral(); #define MINERAL_COUNT 3 -std::string mineral_block_texture(u8 mineral); - class IGameDef; -inline CraftItem * getDiggedMineralItem(u8 mineral, IGameDef *gamedef) -{ - if(mineral == MINERAL_COAL) - return new CraftItem(gamedef, "lump_of_coal", 1); - else if(mineral == MINERAL_IRON) - return new CraftItem(gamedef, "lump_of_iron", 1); - - return NULL; -} +std::string mineral_block_texture(u8 mineral); +ItemStack getDiggedMineralItem(u8 mineral, IGameDef *gamedef); #endif diff --git a/src/nodedef.cpp b/src/nodedef.cpp index d7769700b..87469bfb5 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "main.h" // For g_settings -#include "nodemetadata.h" +#include "itemdef.h" #ifndef SERVER #include "tile.h" #endif @@ -103,8 +103,6 @@ void ContentFeatures::reset() Cached stuff */ #ifndef SERVER - inventory_texture = NULL; - for(u16 j=0; j::const_iterator + i = m_name_id_mapping_with_aliases.find(name); + if(i == m_name_id_mapping_with_aliases.end()) + return false; + result = i->second; + return true; } virtual content_t getId(const std::string &name) const { @@ -420,14 +360,6 @@ public: getId(name, id); return get(id); } - virtual std::string getAlias(const std::string &name) const - { - std::map::const_iterator i; - i = m_aliases.find(name); - if(i != m_aliases.end()) - return i->second; - return name; - } // IWritableNodeDefManager virtual void set(content_t c, const ContentFeatures &def) { @@ -451,20 +383,14 @@ public: } m_content_features[c] = def; if(def.name != "") - m_name_id_mapping.set(c, def.name); - - // Remove conflicting alias if it exists - bool alias_removed = (m_aliases.erase(def.name) != 0); - if(alias_removed) - infostream<<"ndef: erased alias "< "< all = idef->getAll(); + m_name_id_mapping_with_aliases.clear(); + for(std::set::iterator + i = all.begin(); i != all.end(); i++) + { + std::string name = *i; + std::string convert_to = idef->getAlias(name); + content_t id; + if(m_name_id_mapping.getId(convert_to, id)) + { + m_name_id_mapping_with_aliases.insert( + std::make_pair(name, id)); + } } - infostream<<"ndef: setting alias "< "<tname_tiles[j]; + if(tname_tiles[j] == "") + tname_tiles[j] = "unknown_block.png"; + } + switch(f->drawtype){ default: case NDT_NORMAL: @@ -567,8 +505,7 @@ public: f->drawtype = NDT_NORMAL; f->solidness = 2; for(u32 i=0; i<6; i++){ - f->setTexture(i, f->tname_tiles[i] - + std::string("^[noalpha")); + tname_tiles[i] += std::string("^[noalpha"); } } break; @@ -581,16 +518,9 @@ public: break; } - // Inventory texture - if(f->tname_inventory != "") - f->inventory_texture = tsrc->getTextureRaw(f->tname_inventory); - else - f->inventory_texture = NULL; // Tile textures for(u16 j=0; j<6; j++){ - if(f->tname_tiles[j] == "") - continue; - f->tiles[j].texture = tsrc->getTexture(f->tname_tiles[j]); + f->tiles[j].texture = tsrc->getTexture(tname_tiles[j]); f->tiles[j].alpha = f->alpha; if(f->alpha == 255) f->tiles[j].material_type = MATERIAL_ALPHA_SIMPLE; @@ -649,16 +579,8 @@ public: } writeU16(os, count); os<::const_iterator - i = m_aliases.begin(); i != m_aliases.end(); i++) - { - os<first); - os<second); - } } - void deSerialize(std::istream &is, IGameDef *gamedef) + void deSerialize(std::istream &is) { clear(); u16 count = readU16(is); @@ -674,27 +596,26 @@ public: if(i == CONTENT_IGNORE || i == CONTENT_AIR) continue;*/ ContentFeatures *f = &m_content_features[i]; - f->deSerialize(tmp_is, gamedef); + f->deSerialize(tmp_is); if(f->name != "") - m_name_id_mapping.set(i, f->name); - } - - u16 num_aliases = readU16(is); - if(!is.eof()){ - for(u16 i=0; iname); } } +private: + void addNameIdMapping(content_t i, std::string name) + { + m_name_id_mapping.set(i, name); + m_name_id_mapping_with_aliases.insert(std::make_pair(name, i)); + } private: // Features indexed by id ContentFeatures m_content_features[MAX_CONTENT+1]; // A mapping for fast converting back and forth between names and ids NameIdMapping m_name_id_mapping; - // Aliases - std::map m_aliases; + // Like m_name_id_mapping, but only from names to ids, and includes + // item aliases too. Updated by updateAliases() + // Note: Not serialized. + std::map m_name_id_mapping_with_aliases; }; IWritableNodeDefManager* createNodeDefManager() diff --git a/src/nodedef.h b/src/nodedef.h index fdf2f8c45..598ad7fb3 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -29,6 +29,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tile.h" #endif #include "materials.h" // MaterialProperties +class IItemDefManager; class ITextureSource; class IGameDef; @@ -124,7 +125,6 @@ struct ContentFeatures // 0 1 2 3 4 5 // up down right left back front TileSpec tiles[6]; - video::ITexture *inventory_texture; // Special material/texture // - Currently used for flowing liquids video::SMaterial *special_materials[CF_SPECIAL_COUNT]; @@ -133,11 +133,7 @@ struct ContentFeatures u8 visual_solidness; // When solidness=0, this tells how it looks like bool backface_culling; #endif - - // List of textures that are used and are wanted to be included in - // the texture atlas - std::set used_texturenames; - + /* Actual data */ @@ -148,7 +144,6 @@ struct ContentFeatures enum NodeDrawType drawtype; float visual_scale; // Misc. scale parameter std::string tname_tiles[6]; - std::string tname_inventory; MaterialSpec mspec_special[CF_SPECIAL_COUNT]; // Use setter methods u8 alpha; @@ -174,10 +169,6 @@ struct ContentFeatures // If true, param2 is set to direction when placed. Used for torches. // NOTE: the direction format is quite inefficient and should be changed bool wall_mounted; - // Whether this content type often contains mineral. - // Used for texture atlas creation. - // Currently only enabled for CONTENT_STONE. - bool often_contains_mineral; // Inventory item string as which the node appears in inventory when dug. // Mineral overrides this. std::string dug_item; @@ -202,9 +193,6 @@ struct ContentFeatures u32 damage_per_second; NodeBox selection_box; MaterialProperties material; - std::string cookresult_item; - float furnace_cooktime; - float furnace_burntime; /* Methods @@ -214,22 +202,8 @@ struct ContentFeatures ~ContentFeatures(); void reset(); void serialize(std::ostream &os); - void deSerialize(std::istream &is, IGameDef *gamedef); + void deSerialize(std::istream &is); - /* - Texture setters. - - */ - - // Texture setters. They also add stuff to used_texturenames. - void setTexture(u16 i, std::string name); - void setAllTextures(std::string name); - void setSpecialMaterial(u16 i, const MaterialSpec &mspec); - - void setInventoryTexture(std::string imgname); - void setInventoryTextureCube(std::string top, - std::string left, std::string right); - /* Some handy methods */ @@ -253,7 +227,6 @@ public: virtual bool getId(const std::string &name, content_t &result) const=0; virtual content_t getId(const std::string &name) const=0; virtual const ContentFeatures& get(const std::string &name) const=0; - virtual std::string getAlias(const std::string &name) const =0; virtual void serialize(std::ostream &os)=0; }; @@ -271,8 +244,7 @@ public: virtual content_t getId(const std::string &name) const=0; // If not found, returns the features of CONTENT_IGNORE virtual const ContentFeatures& get(const std::string &name) const=0; - virtual std::string getAlias(const std::string &name) const =0; - + // Register node definition virtual void set(content_t c, const ContentFeatures &def)=0; // Register node definition by name (allocate an id) @@ -281,11 +253,12 @@ public: const ContentFeatures &def)=0; // If returns CONTENT_IGNORE, could not allocate id virtual content_t allocateDummy(const std::string &name)=0; - // Set an alias so that nodes named will load as . - // Alias is not set if has already been defined. - // Alias will be removed if is defined at a later point of time. - virtual void setAlias(const std::string &name, - const std::string &convert_to)=0; + + /* + Update item alias mapping. + Call after updating item definitions. + */ + virtual void updateAliases(IItemDefManager *idef)=0; /* Update tile textures to latest return values of TextueSource. @@ -294,7 +267,7 @@ public: virtual void updateTextures(ITextureSource *tsrc)=0; virtual void serialize(std::ostream &os)=0; - virtual void deSerialize(std::istream &is, IGameDef *gamedef)=0; + virtual void deSerialize(std::istream &is)=0; }; IWritableNodeDefManager* createNodeDefManager(); diff --git a/src/player.cpp b/src/player.cpp index 963f67c28..b072746c6 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_settings #include "settings.h" #include "nodedef.h" +#include "collision.h" #include "environment.h" #include "gamedef.h" @@ -37,13 +38,13 @@ Player::Player(IGameDef *gamedef): in_water_stable(false), is_climbing(false), swimming_up(false), + inventory(gamedef->idef()), inventory_backup(NULL), craftresult_is_preview(true), hp(20), peer_id(PEER_ID_INEXISTENT), // protected m_gamedef(gamedef), - m_selected_item(0), m_pitch(0), m_yaw(0), m_speed(0,0,0), @@ -58,11 +59,6 @@ Player::~Player() delete inventory_backup; } -void Player::wieldItem(u16 item) -{ - m_selected_item = item; -} - void Player::resetInventory() { inventory.clear(); @@ -172,7 +168,7 @@ void Player::deSerialize(std::istream &is) hp = 20; } - inventory.deSerialize(is, m_gamedef); + inventory.deSerialize(is); } #ifndef SERVER diff --git a/src/player.h b/src/player.h index 1c9dde7e0..43f6ee5ec 100644 --- a/src/player.h +++ b/src/player.h @@ -22,7 +22,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common_irrlicht.h" #include "inventory.h" -#include "collision.h" #define PLAYERNAME_SIZE 20 @@ -31,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Map; class IGameDef; +class CollisionInfo; class Player { @@ -117,16 +117,7 @@ public: snprintf(m_name, PLAYERNAME_SIZE, "%s", name); } - virtual void wieldItem(u16 item); - virtual const InventoryItem *getWieldItem() const - { - const InventoryList *list = inventory.getList("main"); - if (list) - return list->getItem(m_selected_item); - return NULL; - } - - const char * getName() + const char * getName() const { return m_name; } @@ -174,7 +165,6 @@ protected: IGameDef *m_gamedef; char m_name[PLAYERNAME_SIZE]; - u16 m_selected_item; f32 m_pitch; f32 m_yaw; v3f m_speed; diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index b617626a0..187d1a894 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -36,10 +36,9 @@ extern "C" { //#include "luna.h" #include "luaentity_common.h" #include "content_sao.h" // For LuaEntitySAO -#include "tooldef.h" +#include "itemdef.h" #include "nodedef.h" #include "craftdef.h" -#include "craftitemdef.h" #include "main.h" // For g_settings #include "settings.h" // For accessing g_settings #include "nodemetadata.h" @@ -127,243 +126,55 @@ public: } }; -std::string get_current_modname(lua_State *L) -{ - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); - std::string modname = ""; - if(lua_type(L, -1) == LUA_TSTRING) - modname = lua_tostring(L, -1); - lua_pop(L, 1); - return modname; -} - -void check_modname_prefix(lua_State *L, std::string &name) -{ - if(name.size() == 0) - throw LuaError(L, std::string("Name is empty")); - - if(name[0] == ':'){ - name = name.substr(1); - return; - } - - std::string modname = get_current_modname(L); - assert(modname != ""); - - // For __builtin, anything goes - if(modname == "__builtin") - return; - - if(name.substr(0, modname.size()+1) != modname + ":") - throw LuaError(L, std::string("Name \"")+name - +"\" does not follow naming conventions: " - +"\"modname:\" or \":\" prefix required)"); - - std::string subname = name.substr(modname.size()+1); - if(!string_allowed(subname, "abcdefghijklmnopqrstuvwxyz" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_")) - throw LuaError(L, std::string("Name \"")+name - +"\" does not follow naming conventions: " - +"\"contains unallowed characters"); -} - -static void push_v3f(lua_State *L, v3f p) -{ - lua_newtable(L); - lua_pushnumber(L, p.X); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, p.Z); - lua_setfield(L, -2, "z"); -} - -static v2s16 read_v2s16(lua_State *L, int index) -{ - v2s16 p; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - p.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - p.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - return p; -} - -static v2f read_v2f(lua_State *L, int index) -{ - v2f p; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - p.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - p.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - return p; -} +/* + Getters for stuff in main tables +*/ static Server* get_server(lua_State *L) { // Get server from registry lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - return (Server*)lua_touserdata(L, -1); + Server *server = (Server*)lua_touserdata(L, -1); + lua_pop(L, 1); + return server; } static ServerEnvironment* get_env(lua_State *L) { // Get environment from registry lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env"); - return (ServerEnvironment*)lua_touserdata(L, -1); -} - -static v3f read_v3f(lua_State *L, int index) -{ - v3f pos; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - pos.X = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - pos.Y = lua_tonumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "z"); - pos.Z = lua_tonumber(L, -1); - lua_pop(L, 1); - return pos; -} - -static v3f check_v3f(lua_State *L, int index) -{ - v3f pos; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "x"); - pos.X = luaL_checknumber(L, -1); - lua_pop(L, 1); - lua_getfield(L, index, "y"); - pos.Y = luaL_checknumber(L, -1); + ServerEnvironment *env = (ServerEnvironment*)lua_touserdata(L, -1); lua_pop(L, 1); - lua_getfield(L, index, "z"); - pos.Z = luaL_checknumber(L, -1); - lua_pop(L, 1); - return pos; -} - -static void pushFloatPos(lua_State *L, v3f p) -{ - p /= BS; - push_v3f(L, p); -} - -static v3f checkFloatPos(lua_State *L, int index) -{ - return check_v3f(L, index) * BS; -} - -static void push_v3s16(lua_State *L, v3s16 p) -{ - lua_newtable(L); - lua_pushnumber(L, p.X); - lua_setfield(L, -2, "x"); - lua_pushnumber(L, p.Y); - lua_setfield(L, -2, "y"); - lua_pushnumber(L, p.Z); - lua_setfield(L, -2, "z"); -} - -static v3s16 read_v3s16(lua_State *L, int index) -{ - // Correct rounding at <0 - v3f pf = read_v3f(L, index); - return floatToInt(pf, 1.0); -} - -static v3s16 check_v3s16(lua_State *L, int index) -{ - // Correct rounding at <0 - v3f pf = check_v3f(L, index); - return floatToInt(pf, 1.0); -} - -static void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef) -{ - lua_newtable(L); - lua_pushstring(L, ndef->get(n).name.c_str()); - lua_setfield(L, -2, "name"); - lua_pushnumber(L, n.getParam1()); - lua_setfield(L, -2, "param1"); - lua_pushnumber(L, n.getParam2()); - lua_setfield(L, -2, "param2"); + return env; } -static MapNode readnode(lua_State *L, int index, INodeDefManager *ndef) +static void objectref_get(lua_State *L, u16 id) { - lua_getfield(L, index, "name"); - const char *name = luaL_checkstring(L, -1); - lua_pop(L, 1); - u8 param1; - lua_getfield(L, index, "param1"); - if(lua_isnil(L, -1)) - param1 = 0; - else - param1 = lua_tonumber(L, -1); - lua_pop(L, 1); - u8 param2; - lua_getfield(L, index, "param2"); - if(lua_isnil(L, -1)) - param2 = 0; - else - param2 = lua_tonumber(L, -1); - lua_pop(L, 1); - return MapNode(ndef, name, param1, param2); + // Get minetest.object_refs[i] + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "object_refs"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushnumber(L, id); + lua_gettable(L, -2); + lua_remove(L, -2); // object_refs + lua_remove(L, -2); // minetest } -static video::SColor readARGB8(lua_State *L, int index) +static void luaentity_get(lua_State *L, u16 id) { - video::SColor color; - luaL_checktype(L, index, LUA_TTABLE); - lua_getfield(L, index, "a"); - if(lua_isnumber(L, -1)) - color.setAlpha(lua_tonumber(L, -1)); - lua_pop(L, 1); - lua_getfield(L, index, "r"); - color.setRed(lua_tonumber(L, -1)); - lua_pop(L, 1); - lua_getfield(L, index, "g"); - color.setGreen(lua_tonumber(L, -1)); - lua_pop(L, 1); - lua_getfield(L, index, "b"); - color.setBlue(lua_tonumber(L, -1)); - lua_pop(L, 1); - return color; + // Get minetest.luaentities[i] + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "luaentities"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_pushnumber(L, id); + lua_gettable(L, -2); + lua_remove(L, -2); // luaentities + lua_remove(L, -2); // minetest } -static core::aabbox3d read_aabbox3df32(lua_State *L, int index, f32 scale) -{ - core::aabbox3d box; - if(lua_istable(L, -1)){ - lua_rawgeti(L, -1, 1); - box.MinEdge.X = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, -1, 2); - box.MinEdge.Y = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, -1, 3); - box.MinEdge.Z = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, -1, 4); - box.MaxEdge.X = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, -1, 5); - box.MaxEdge.Y = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - lua_rawgeti(L, -1, 6); - box.MaxEdge.Z = lua_tonumber(L, -1) * scale; - lua_pop(L, 1); - } - return box; -} +/* + Table field getters +*/ static bool getstringfield(lua_State *L, int table, const char *fieldname, std::string &result) @@ -371,7 +182,9 @@ static bool getstringfield(lua_State *L, int table, lua_getfield(L, table, fieldname); bool got = false; if(lua_isstring(L, -1)){ - result = lua_tostring(L, -1); + size_t len = 0; + const char *ptr = lua_tolstring(L, -1, &len); + result.assign(ptr, len); got = true; } lua_pop(L, 1); @@ -442,13 +255,13 @@ static int getintfield_default(lua_State *L, int table, return result; } -/*static float getfloatfield_default(lua_State *L, int table, +static float getfloatfield_default(lua_State *L, int table, const char *fieldname, float default_) { float result = default_; getfloatfield(L, table, fieldname, result); return result; -}*/ +} static bool getboolfield_default(lua_State *L, int table, const char *fieldname, bool default_) @@ -523,216 +336,19 @@ static void warn_if_field_exists(lua_State *L, int table, } /* - Inventory stuff + EnumString definitions */ -static void inventory_set_list_from_lua(Inventory *inv, const char *name, - lua_State *L, int tableindex, IGameDef *gamedef, int forcesize=-1) +struct EnumString es_ItemType[] = { - if(tableindex < 0) - tableindex = lua_gettop(L) + 1 + tableindex; - // If nil, delete list - if(lua_isnil(L, tableindex)){ - inv->deleteList(name); - return; - } - // Otherwise set list - std::list items; - luaL_checktype(L, tableindex, LUA_TTABLE); - int table = tableindex; - lua_pushnil(L); - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - std::string itemstring = luaL_checkstring(L, -1); - items.push_back(itemstring); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - int listsize = (forcesize != -1) ? forcesize : items.size(); - InventoryList *invlist = inv->addList(name, listsize); - int index = 0; - for(std::list::const_iterator - i = items.begin(); i != items.end(); i++){ - if(forcesize != -1 && index == forcesize) - break; - const std::string &itemstring = *i; - InventoryItem *newitem = NULL; - if(itemstring != "") - newitem = InventoryItem::deSerialize(itemstring, - gamedef); - InventoryItem *olditem = invlist->changeItem(index, newitem); - delete olditem; - index++; - } - while(forcesize != -1 && index < forcesize){ - InventoryItem *olditem = invlist->changeItem(index, NULL); - delete olditem; - index++; - } -} + {ITEM_NONE, "none"}, + {ITEM_NODE, "node"}, + {ITEM_CRAFT, "craft"}, + {ITEM_TOOL, "tool"}, + {0, NULL}, +}; -static void inventory_get_list_to_lua(Inventory *inv, const char *name, - lua_State *L) -{ - InventoryList *invlist = inv->getList(name); - if(invlist == NULL){ - lua_pushnil(L); - return; - } - // Get the table insert function - lua_getglobal(L, "table"); - lua_getfield(L, -1, "insert"); - int table_insert = lua_gettop(L); - // Create and fill table - lua_newtable(L); - int table = lua_gettop(L); - for(u32 i=0; igetSize(); i++){ - InventoryItem *item = invlist->getItem(i); - lua_pushvalue(L, table_insert); - lua_pushvalue(L, table); - if(item == NULL){ - lua_pushstring(L, ""); - } else { - lua_pushstring(L, item->getItemString().c_str()); - } - if(lua_pcall(L, 2, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } -} - -static void push_stack_item(lua_State *L, InventoryItem *item0) -{ - if(item0 == NULL){ - lua_pushnil(L); - } - else if(std::string("MaterialItem") == item0->getName()){ - MaterialItem *item = (MaterialItem*)item0; - lua_newtable(L); - lua_pushstring(L, "node"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, item->getNodeName().c_str()); - lua_setfield(L, -2, "name"); - } - else if(std::string("CraftItem") == item0->getName()){ - CraftItem *item = (CraftItem*)item0; - lua_newtable(L); - lua_pushstring(L, "craft"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, item->getSubName().c_str()); - lua_setfield(L, -2, "name"); - } - else if(std::string("ToolItem") == item0->getName()){ - ToolItem *item = (ToolItem*)item0; - lua_newtable(L); - lua_pushstring(L, "tool"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, item->getToolName().c_str()); - lua_setfield(L, -2, "name"); - lua_pushstring(L, itos(item->getWear()).c_str()); - lua_setfield(L, -2, "wear"); - } - else{ - errorstream<<"push_stack_item: Unknown item name: \"" - <getName()<<"\""< Lua table converter functions */ -static void objectref_get(lua_State *L, u16 id) +static void push_v3f(lua_State *L, v3f p) { - // Get minetest.object_refs[i] - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "object_refs"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); - lua_gettable(L, -2); - lua_remove(L, -2); // object_refs - lua_remove(L, -2); // minetest + lua_newtable(L); + lua_pushnumber(L, p.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, p.Y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, p.Z); + lua_setfield(L, -2, "z"); } -static void luaentity_get(lua_State *L, u16 id) +static v2s16 read_v2s16(lua_State *L, int index) { - // Get minetest.luaentities[i] - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "luaentities"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushnumber(L, id); - lua_gettable(L, -2); - lua_remove(L, -2); // luaentities - lua_remove(L, -2); // minetest + v2s16 p; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + p.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + p.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + return p; } -/* - Object wrappers -*/ +static v2f read_v2f(lua_State *L, int index) +{ + v2f p; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + p.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + p.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + return p; +} -#define method(class, name) {#name, class::l_##name} +static v3f read_v3f(lua_State *L, int index) +{ + v3f pos; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + pos.X = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + pos.Y = lua_tonumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "z"); + pos.Z = lua_tonumber(L, -1); + lua_pop(L, 1); + return pos; +} -/* - ItemStack -*/ +static v3f check_v3f(lua_State *L, int index) +{ + v3f pos; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "x"); + pos.X = luaL_checknumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "y"); + pos.Y = luaL_checknumber(L, -1); + lua_pop(L, 1); + lua_getfield(L, index, "z"); + pos.Z = luaL_checknumber(L, -1); + lua_pop(L, 1); + return pos; +} + +static void pushFloatPos(lua_State *L, v3f p) +{ + p /= BS; + push_v3f(L, p); +} -class ItemStack +static v3f checkFloatPos(lua_State *L, int index) { -private: - InventoryItem *m_stack; + return check_v3f(L, index) * BS; +} - static const char className[]; - static const luaL_reg methods[]; +static void push_v3s16(lua_State *L, v3s16 p) +{ + lua_newtable(L); + lua_pushnumber(L, p.X); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, p.Y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, p.Z); + lua_setfield(L, -2, "z"); +} - // Exported functions - - // garbage collector - static int gc_object(lua_State *L) { - ItemStack *o = *(ItemStack **)(lua_touserdata(L, 1)); - delete o; - return 0; +static v3s16 read_v3s16(lua_State *L, int index) +{ + // Correct rounding at <0 + v3f pf = read_v3f(L, index); + return floatToInt(pf, 1.0); +} + +static v3s16 check_v3s16(lua_State *L, int index) +{ + // Correct rounding at <0 + v3f pf = check_v3f(L, index); + return floatToInt(pf, 1.0); +} + +static void pushnode(lua_State *L, const MapNode &n, INodeDefManager *ndef) +{ + lua_newtable(L); + lua_pushstring(L, ndef->get(n).name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushnumber(L, n.getParam1()); + lua_setfield(L, -2, "param1"); + lua_pushnumber(L, n.getParam2()); + lua_setfield(L, -2, "param2"); +} + +static MapNode readnode(lua_State *L, int index, INodeDefManager *ndef) +{ + lua_getfield(L, index, "name"); + const char *name = luaL_checkstring(L, -1); + lua_pop(L, 1); + u8 param1; + lua_getfield(L, index, "param1"); + if(lua_isnil(L, -1)) + param1 = 0; + else + param1 = lua_tonumber(L, -1); + lua_pop(L, 1); + u8 param2; + lua_getfield(L, index, "param2"); + if(lua_isnil(L, -1)) + param2 = 0; + else + param2 = lua_tonumber(L, -1); + lua_pop(L, 1); + return MapNode(ndef, name, param1, param2); +} + +static video::SColor readARGB8(lua_State *L, int index) +{ + video::SColor color; + luaL_checktype(L, index, LUA_TTABLE); + lua_getfield(L, index, "a"); + if(lua_isnumber(L, -1)) + color.setAlpha(lua_tonumber(L, -1)); + lua_pop(L, 1); + lua_getfield(L, index, "r"); + color.setRed(lua_tonumber(L, -1)); + lua_pop(L, 1); + lua_getfield(L, index, "g"); + color.setGreen(lua_tonumber(L, -1)); + lua_pop(L, 1); + lua_getfield(L, index, "b"); + color.setBlue(lua_tonumber(L, -1)); + lua_pop(L, 1); + return color; +} + +static core::aabbox3d read_aabbox3df32(lua_State *L, int index, f32 scale) +{ + core::aabbox3d box; + if(lua_istable(L, -1)){ + lua_rawgeti(L, -1, 1); + box.MinEdge.X = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, -1, 2); + box.MinEdge.Y = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, -1, 3); + box.MinEdge.Z = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, -1, 4); + box.MaxEdge.X = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, -1, 5); + box.MaxEdge.Y = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + lua_rawgeti(L, -1, 6); + box.MaxEdge.Z = lua_tonumber(L, -1) * scale; + lua_pop(L, 1); + } + return box; +} + +/* + ToolDiggingProperties +*/ + +static ToolDiggingProperties read_tool_digging_properties( + lua_State *L, int table) +{ + ToolDiggingProperties prop; + getfloatfield(L, table, "full_punch_interval", prop.full_punch_interval); + getfloatfield(L, table, "basetime", prop.basetime); + getfloatfield(L, table, "dt_weight", prop.dt_weight); + getfloatfield(L, table, "dt_crackiness", prop.dt_crackiness); + getfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness); + getfloatfield(L, table, "dt_cuttability", prop.dt_cuttability); + getfloatfield(L, table, "basedurability", prop.basedurability); + getfloatfield(L, table, "dd_weight", prop.dd_weight); + getfloatfield(L, table, "dd_crackiness", prop.dd_crackiness); + getfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness); + getfloatfield(L, table, "dd_cuttability", prop.dd_cuttability); + return prop; +} + +static void set_tool_digging_properties(lua_State *L, int table, + const ToolDiggingProperties &prop) +{ + setfloatfield(L, table, "full_punch_interval", prop.full_punch_interval); + setfloatfield(L, table, "basetime", prop.basetime); + setfloatfield(L, table, "dt_weight", prop.dt_weight); + setfloatfield(L, table, "dt_crackiness", prop.dt_crackiness); + setfloatfield(L, table, "dt_crumbliness", prop.dt_crumbliness); + setfloatfield(L, table, "dt_cuttability", prop.dt_cuttability); + setfloatfield(L, table, "basedurability", prop.basedurability); + setfloatfield(L, table, "dd_weight", prop.dd_weight); + setfloatfield(L, table, "dd_crackiness", prop.dd_crackiness); + setfloatfield(L, table, "dd_crumbliness", prop.dd_crumbliness); + setfloatfield(L, table, "dd_cuttability", prop.dd_cuttability); +} + +static void push_tool_digging_properties(lua_State *L, + const ToolDiggingProperties &prop) +{ + lua_newtable(L); + set_tool_digging_properties(L, -1, prop); +} + +/* + PointedThing +*/ + +static void push_pointed_thing(lua_State *L, const PointedThing& pointed) +{ + lua_newtable(L); + if(pointed.type == POINTEDTHING_NODE) + { + lua_pushstring(L, "node"); + lua_setfield(L, -2, "type"); + push_v3s16(L, pointed.node_undersurface); + lua_setfield(L, -2, "under"); + push_v3s16(L, pointed.node_abovesurface); + lua_setfield(L, -2, "above"); + } + else if(pointed.type == POINTEDTHING_OBJECT) + { + lua_pushstring(L, "object"); + lua_setfield(L, -2, "type"); + objectref_get(L, pointed.object_id); + lua_setfield(L, -2, "ref"); + } + else + { + lua_pushstring(L, "nothing"); + lua_setfield(L, -2, "type"); + } +} + +/* + ItemDefinition +*/ + +static ItemDefinition read_item_definition(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + // Read the item definition + ItemDefinition def; + + def.type = (ItemType)getenumfield(L, index, "type", + es_ItemType, ITEM_NONE); + getstringfield(L, index, "name", def.name); + getstringfield(L, index, "description", def.description); + getstringfield(L, index, "inventory_image", def.inventory_image); + getstringfield(L, index, "wield_image", def.wield_image); + + lua_getfield(L, index, "wield_scale"); + if(lua_istable(L, -1)){ + def.wield_scale = check_v3f(L, -1); + } + lua_pop(L, 1); + + def.stack_max = getintfield_default(L, index, "stack_max", def.stack_max); + if(def.stack_max == 0) + def.stack_max = 1; + + lua_getfield(L, index, "on_use"); + def.usable = lua_isfunction(L, -1); + lua_pop(L, 1); + + getboolfield(L, index, "liquids_pointable", def.liquids_pointable); + + lua_getfield(L, index, "tool_digging_properties"); + if(lua_istable(L, -1)){ + def.tool_digging_properties = new ToolDiggingProperties( + read_tool_digging_properties(L, -1)); + } + lua_pop(L, 1); + + // If name is "" (hand), ensure there are ToolDiggingProperties + // because it will be looked up there whenever any other item has + // no ToolDiggingProperties + if(def.name == "" && def.tool_digging_properties == NULL){ + def.tool_digging_properties = new ToolDiggingProperties(); + } + + return def; +} + +/* + ContentFeatures +*/ + +static ContentFeatures read_content_features(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + ContentFeatures f; + getstringfield(L, index, "name", f.name); + + /* Visual definition */ + + f.drawtype = (NodeDrawType)getenumfield(L, index, "drawtype", es_DrawType, + NDT_NORMAL); + getfloatfield(L, index, "visual_scale", f.visual_scale); + + lua_getfield(L, index, "tile_images"); + if(lua_istable(L, -1)){ + int table = lua_gettop(L); + lua_pushnil(L); + int i = 0; + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + if(lua_isstring(L, -1)) + f.tname_tiles[i] = lua_tostring(L, -1); + else + f.tname_tiles[i] = ""; + // removes value, keeps key for next iteration + lua_pop(L, 1); + i++; + if(i==6){ + lua_pop(L, 1); + break; + } + } + // Copy last value to all remaining textures + if(i >= 1){ + std::string lastname = f.tname_tiles[i-1]; + while(i < 6){ + f.tname_tiles[i] = lastname; + i++; + } + } + } + lua_pop(L, 1); + + lua_getfield(L, index, "special_materials"); + if(lua_istable(L, -1)){ + int table = lua_gettop(L); + lua_pushnil(L); + int i = 0; + while(lua_next(L, table) != 0){ + // key at index -2 and value at index -1 + int smtable = lua_gettop(L); + std::string tname = getstringfield_default( + L, smtable, "image", ""); + bool backface_culling = getboolfield_default( + L, smtable, "backface_culling", true); + MaterialSpec mspec(tname, backface_culling); + f.mspec_special[i] = mspec; + // removes value, keeps key for next iteration + lua_pop(L, 1); + i++; + if(i==6){ + lua_pop(L, 1); + break; + } + } + } + lua_pop(L, 1); + + f.alpha = getintfield_default(L, index, "alpha", 255); + + /* Other stuff */ + + lua_getfield(L, index, "post_effect_color"); + if(!lua_isnil(L, -1)) + f.post_effect_color = readARGB8(L, -1); + lua_pop(L, 1); + + f.param_type = (ContentParamType)getenumfield(L, index, "paramtype", + es_ContentParamType, CPT_NONE); + + // True for all ground-like things like stone and mud, false for eg. trees + getboolfield(L, index, "is_ground_content", f.is_ground_content); + f.light_propagates = (f.param_type == CPT_LIGHT); + warn_if_field_exists(L, index, "light_propagates", + "deprecated: determined from paramtype"); + getboolfield(L, index, "sunlight_propagates", f.sunlight_propagates); + // This is used for collision detection. + // Also for general solidness queries. + getboolfield(L, index, "walkable", f.walkable); + // Player can point to these + getboolfield(L, index, "pointable", f.pointable); + // Player can dig these + getboolfield(L, index, "diggable", f.diggable); + // Player can climb these + getboolfield(L, index, "climbable", f.climbable); + // Player can build on these + getboolfield(L, index, "buildable_to", f.buildable_to); + // If true, param2 is set to direction when placed. Used for torches. + // NOTE: the direction format is quite inefficient and should be changed + getboolfield(L, index, "wall_mounted", f.wall_mounted); + // Inventory item string as which the node appears in inventory when dug. + // Mineral overrides this. + getstringfield(L, index, "dug_item", f.dug_item); + // Extra dug item and its rarity + getstringfield(L, index, "extra_dug_item", f.extra_dug_item); + // Usual get interval for extra dug item + getintfield(L, index, "extra_dug_item_rarity", f.extra_dug_item_rarity); + // Metadata name of node (eg. "furnace") + getstringfield(L, index, "metadata_name", f.metadata_name); + // Whether the node is non-liquid, source liquid or flowing liquid + f.liquid_type = (LiquidType)getenumfield(L, index, "liquidtype", + es_LiquidType, LIQUID_NONE); + // If the content is liquid, this is the flowing version of the liquid. + getstringfield(L, index, "liquid_alternative_flowing", + f.liquid_alternative_flowing); + // If the content is liquid, this is the source version of the liquid. + getstringfield(L, index, "liquid_alternative_source", + f.liquid_alternative_source); + // Viscosity for fluid flow, ranging from 1 to 7, with + // 1 giving almost instantaneous propagation and 7 being + // the slowest possible + f.liquid_viscosity = getintfield_default(L, index, + "liquid_viscosity", f.liquid_viscosity); + // Amount of light the node emits + f.light_source = getintfield_default(L, index, + "light_source", f.light_source); + f.damage_per_second = getintfield_default(L, index, + "damage_per_second", f.damage_per_second); + + lua_getfield(L, index, "selection_box"); + if(lua_istable(L, -1)){ + f.selection_box.type = (NodeBoxType)getenumfield(L, -1, "type", + es_NodeBoxType, NODEBOX_REGULAR); + + lua_getfield(L, -1, "fixed"); + if(lua_istable(L, -1)) + f.selection_box.fixed = read_aabbox3df32(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, -1, "wall_top"); + if(lua_istable(L, -1)) + f.selection_box.wall_top = read_aabbox3df32(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, -1, "wall_bottom"); + if(lua_istable(L, -1)) + f.selection_box.wall_bottom = read_aabbox3df32(L, -1, BS); + lua_pop(L, 1); + + lua_getfield(L, -1, "wall_side"); + if(lua_istable(L, -1)) + f.selection_box.wall_side = read_aabbox3df32(L, -1, BS); + lua_pop(L, 1); + } + lua_pop(L, 1); + + lua_getfield(L, index, "material"); + if(lua_istable(L, -1)){ + f.material.diggability = (Diggability)getenumfield(L, -1, "diggability", + es_Diggability, DIGGABLE_NORMAL); + + getfloatfield(L, -1, "constant_time", f.material.constant_time); + getfloatfield(L, -1, "weight", f.material.weight); + getfloatfield(L, -1, "crackiness", f.material.crackiness); + getfloatfield(L, -1, "crumbliness", f.material.crumbliness); + getfloatfield(L, -1, "cuttability", f.material.cuttability); + getfloatfield(L, -1, "flammability", f.material.flammability); + } + lua_pop(L, 1); + + return f; +} + +/* + Inventory stuff +*/ + +static ItemStack read_item(lua_State *L, int index); + +static void inventory_set_list_from_lua(Inventory *inv, const char *name, + lua_State *L, int tableindex, int forcesize=-1) +{ + dstream<<"inventory_set_list_from_lua\n"; + if(tableindex < 0) + tableindex = lua_gettop(L) + 1 + tableindex; + // If nil, delete list + if(lua_isnil(L, tableindex)){ + inv->deleteList(name); + return; + } + // Otherwise set list + std::vector items; + luaL_checktype(L, tableindex, LUA_TTABLE); + lua_pushnil(L); + while(lua_next(L, tableindex) != 0){ + // key at index -2 and value at index -1 + items.push_back(read_item(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + int listsize = (forcesize != -1) ? forcesize : items.size(); + InventoryList *invlist = inv->addList(name, listsize); + int index = 0; + for(std::vector::const_iterator + i = items.begin(); i != items.end(); i++){ + if(forcesize != -1 && index == forcesize) + break; + invlist->changeItem(index, *i); + index++; + } + while(forcesize != -1 && index < forcesize){ + invlist->deleteItem(index); + index++; + } + dstream<<"inventory_set_list_from_lua done\n"; +} + +static void inventory_get_list_to_lua(Inventory *inv, const char *name, + lua_State *L) +{ + InventoryList *invlist = inv->getList(name); + if(invlist == NULL){ + lua_pushnil(L); + return; + } + // Get the table insert function + lua_getglobal(L, "table"); + lua_getfield(L, -1, "insert"); + int table_insert = lua_gettop(L); + // Create and fill table + lua_newtable(L); + int table = lua_gettop(L); + for(u32 i=0; igetSize(); i++){ + ItemStack item = invlist->getItem(i); + lua_pushvalue(L, table_insert); + lua_pushvalue(L, table); + lua_pushstring(L, item.getItemString().c_str()); + if(lua_pcall(L, 2, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } +} + +/* + Helpful macros for userdata classes +*/ + +#define method(class, name) {#name, class::l_##name} + +/* + LuaItemStack +*/ + +class LuaItemStack +{ +private: + ItemStack m_stack; + + static const char className[]; + static const luaL_reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L) + { + LuaItemStack *o = *(LuaItemStack **)(lua_touserdata(L, 1)); + delete o; + return 0; + } + + // is_empty(self) -> true/false + static int l_is_empty(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushboolean(L, item.empty()); + return 1; + } + + // get_name(self) -> string + static int l_get_name(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushstring(L, item.name.c_str()); + return 1; + } + + // get_count(self) -> number + static int l_get_count(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.count); + return 1; + } + + // get_wear(self) -> number + static int l_get_wear(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.wear); + return 1; + } + + // get_metadata(self) -> string + static int l_get_metadata(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); + return 1; + } + + // clear(self) -> true + static int l_clear(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + o->m_stack.clear(); + lua_pushboolean(L, true); + return 1; + } + + // replace(self, itemstack or itemstring or table or nil) -> true + static int l_replace(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + o->m_stack = read_item(L, 2); + lua_pushboolean(L, true); + return 1; + } + + // to_string(self) -> string + static int l_to_string(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + std::string itemstring = o->m_stack.getItemString(); + lua_pushstring(L, itemstring.c_str()); + return 1; + } + + // to_table(self) -> table or nil + static int l_to_table(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + const ItemStack &item = o->m_stack; + if(item.empty()) + { + lua_pushnil(L); + } + else + { + lua_newtable(L); + lua_pushstring(L, item.name.c_str()); + lua_setfield(L, -2, "name"); + lua_pushinteger(L, item.count); + lua_setfield(L, -2, "count"); + lua_pushinteger(L, item.wear); + lua_setfield(L, -2, "wear"); + lua_pushlstring(L, item.metadata.c_str(), item.metadata.size()); + lua_setfield(L, -2, "metadata"); + } + return 1; + } + + // get_stack_max(self) -> number + static int l_get_stack_max(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.getStackMax(get_server(L)->idef())); + return 1; + } + + // get_free_space(self) -> number + static int l_get_free_space(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + lua_pushinteger(L, item.freeSpace(get_server(L)->idef())); + return 1; + } + + // is_known(self) -> true/false + // Checks if the item is defined. + static int l_is_known(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + bool is_known = item.isKnown(get_server(L)->idef()); + lua_pushboolean(L, is_known); + return 1; + } + + // get_definition(self) -> table + // Returns the item definition table from minetest.registered_items, + // or a fallback one (name="unknown") + static int l_get_definition(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + + // Get minetest.registered_items[name] + lua_getglobal(L, "minetest"); + lua_getfield(L, -1, "registered_items"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, item.name.c_str()); + if(lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_getfield(L, -1, "unknown"); + } + return 1; } - // peek_item(self) - static int l_peek_item(lua_State *L) + // get_tool_digging_properties(self) -> table + // Returns the effective tool digging properties. + // Returns those of the hand ("") if this item has none associated. + static int l_get_tool_digging_properties(lua_State *L) { - ItemStack *o = checkobject(L, 1); - push_stack_item(L, o->m_stack); + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + const ToolDiggingProperties &prop = + item.getToolDiggingProperties(get_server(L)->idef()); + push_tool_digging_properties(L, prop); return 1; } - // take_item(self) - static int l_take_item(lua_State *L) + // add_wear(self, amount) -> true/false + // The range for "amount" is [0,65535]. Wear is only added if the item + // is a tool. Adding wear might destroy the item. + // Returns true if the item is (or was) a tool. + static int l_add_wear(lua_State *L) { - ItemStack *o = checkobject(L, 1); - push_stack_item(L, o->m_stack); - if(o->m_stack->getCount() <= 1){ - delete o->m_stack; - o->m_stack = NULL; - } else { - o->m_stack->remove(1); - } + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + int amount = lua_tointeger(L, 2); + bool result = item.addWear(amount, get_server(L)->idef()); + lua_pushboolean(L, result); return 1; } - // put_item(self, item) -> true/false - static int l_put_item(lua_State *L) + // add_item(self, itemstack or itemstring or table or nil) -> itemstack + // Returns leftover item stack + static int l_add_item(lua_State *L) { - ItemStack *o = checkobject(L, 1); - InventoryItem *item = check_stack_item(L, 2); - if(!item){ // nil can always be inserted - lua_pushboolean(L, true); - return 1; - } - if(!item->addableTo(o->m_stack)){ - lua_pushboolean(L, false); - return 1; - } - o->m_stack->add(1); - delete item; - lua_pushboolean(L, true); + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + ItemStack newitem = read_item(L, 2); + ItemStack leftover = item.addItem(newitem, get_server(L)->idef()); + create(L, leftover); return 1; } - // put_stackstring(self, stackstring) -> true/false - static int l_put_stackstring(lua_State *L) + // item_fits(self, itemstack or itemstring or table or nil) -> true/false, itemstack + // First return value is true iff the new item fits fully into the stack + // Second return value is the would-be-left-over item stack + static int l_item_fits(lua_State *L) { - ItemStack *o = checkobject(L, 1); - std::string stackstring = luaL_checkstring(L, 2); - try{ - InventoryItem *item = InventoryItem::deSerialize(stackstring, - get_server(L)); - if(!item->addableTo(o->m_stack)){ - lua_pushboolean(L, false); - return 1; - } - o->m_stack->add(1); - delete item; - lua_pushboolean(L, true); - return 1; - } - catch(SerializationError &e){ - lua_pushboolean(L, false); - return 1; - } + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + ItemStack newitem = read_item(L, 2); + ItemStack restitem; + bool fits = item.itemFits(newitem, &restitem, get_server(L)->idef()); + lua_pushboolean(L, fits); // first return value + create(L, restitem); // second return value + return 2; + } + + // take_item(self, takecount=1) -> itemstack + static int l_take_item(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + u32 takecount = 1; + if(!lua_isnone(L, 2)) + takecount = lua_tointeger(L, 2); + ItemStack taken = item.takeItem(takecount); + create(L, taken); + return 1; + } + + // peek_item(self, peekcount=1) -> itemstack + static int l_peek_item(lua_State *L) + { + LuaItemStack *o = checkobject(L, 1); + ItemStack &item = o->m_stack; + u32 peekcount = 1; + if(!lua_isnone(L, 2)) + peekcount = lua_tointeger(L, 2); + ItemStack peekaboo = item.peekItem(peekcount); + create(L, peekaboo); + return 1; } public: - ItemStack(InventoryItem *item=NULL): + LuaItemStack(const ItemStack &item): m_stack(item) { } - ~ItemStack() + ~LuaItemStack() { - delete m_stack; } - static ItemStack* checkobject(lua_State *L, int narg) + const ItemStack& getItem() const { - luaL_checktype(L, narg, LUA_TUSERDATA); - void *ud = luaL_checkudata(L, narg, className); - if(!ud) luaL_typerror(L, narg, className); - return *(ItemStack**)ud; // unbox pointer + return m_stack; } - - InventoryItem* getItemCopy() + ItemStack& getItem() { - if(!m_stack) - return NULL; - return m_stack->clone(); + return m_stack; } - // Creates an ItemStack and leaves it on top of stack + // LuaItemStack(itemstack or itemstring or table or nil) + // Creates an LuaItemStack and leaves it on top of stack static int create_object(lua_State *L) { - InventoryItem *item = NULL; - if(lua_isstring(L, 1)){ - std::string itemstring = lua_tostring(L, 1); - if(itemstring != ""){ - try{ - IGameDef *gdef = get_server(L); - item = InventoryItem::deSerialize(itemstring, gdef); - }catch(SerializationError &e){ - } - } - } - ItemStack *o = new ItemStack(item); + ItemStack item = read_item(L, 1); + LuaItemStack *o = new LuaItemStack(item); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); lua_setmetatable(L, -2); return 1; } // Not callable from Lua - static int create(lua_State *L, InventoryItem *item) + static int create(lua_State *L, const ItemStack &item) { - ItemStack *o = new ItemStack(item); + LuaItemStack *o = new LuaItemStack(item); *(void **)(lua_newuserdata(L, sizeof(void *))) = o; luaL_getmetatable(L, className); lua_setmetatable(L, -2); return 1; } + static LuaItemStack* checkobject(lua_State *L, int narg) + { + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(LuaItemStack**)ud; // unbox pointer + } + static void Register(lua_State *L) { lua_newtable(L); @@ -981,19 +1286,83 @@ public: luaL_openlib(L, 0, methods, 0); // fill methodtable lua_pop(L, 1); // drop methodtable - // Can be created from Lua (ItemStack::create(itemstring)) + // Can be created from Lua (LuaItemStack(itemstack or itemstring or table or nil)) lua_register(L, className, create_object); } }; -const char ItemStack::className[] = "ItemStack"; -const luaL_reg ItemStack::methods[] = { - method(ItemStack, peek_item), - method(ItemStack, take_item), - method(ItemStack, put_item), - method(ItemStack, put_stackstring), +const char LuaItemStack::className[] = "ItemStack"; +const luaL_reg LuaItemStack::methods[] = { + method(LuaItemStack, is_empty), + method(LuaItemStack, get_name), + method(LuaItemStack, get_count), + method(LuaItemStack, get_wear), + method(LuaItemStack, get_metadata), + method(LuaItemStack, clear), + method(LuaItemStack, replace), + method(LuaItemStack, to_string), + method(LuaItemStack, to_table), + method(LuaItemStack, get_stack_max), + method(LuaItemStack, get_free_space), + method(LuaItemStack, is_known), + method(LuaItemStack, get_definition), + method(LuaItemStack, get_tool_digging_properties), + method(LuaItemStack, add_wear), + method(LuaItemStack, add_item), + method(LuaItemStack, item_fits), + method(LuaItemStack, take_item), + method(LuaItemStack, peek_item), {0,0} }; +static ItemStack read_item(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(lua_isnil(L, index)) + { + return ItemStack(); + } + else if(lua_isuserdata(L, index)) + { + // Convert from LuaItemStack + LuaItemStack *o = LuaItemStack::checkobject(L, index); + return o->getItem(); + } + else if(lua_isstring(L, index)) + { + // Convert from itemstring + std::string itemstring = lua_tostring(L, index); + IItemDefManager *idef = get_server(L)->idef(); + try + { + ItemStack item; + item.deSerialize(itemstring, idef); + return item; + } + catch(SerializationError &e) + { + infostream<<"WARNING: unable to create item from itemstring" + <<": "<idef(); + std::string name = getstringfield_default(L, index, "name", ""); + int count = getintfield_default(L, index, "count", 1); + int wear = getintfield_default(L, index, "wear", 0); + std::string metadata = getstringfield_default(L, index, "metadata", ""); + return ItemStack(name, count, wear, metadata, idef); + } + else + { + throw LuaError(L, "Expecting itemstack, itemstring, table or nil"); + } +} + /* InvRef */ @@ -1028,15 +1397,6 @@ private: return inv->getList(listname); } - static InventoryItem* getitem(lua_State *L, InvRef *ref, - const char *listname, int i) - { - InventoryList *list = getlist(L, ref, listname); - if(!list) - return NULL; - return list->getItem(i); - } - static void reportInventoryChange(lua_State *L, InvRef *ref) { // Inform other things that the inventory has changed @@ -1088,39 +1448,35 @@ private: return 0; } - // get_stack(self, listname, i) + // get_stack(self, listname, i) -> itemstack static int l_get_stack(lua_State *L) { InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); int i = luaL_checknumber(L, 3) - 1; - InventoryItem *item = getitem(L, ref, listname, i); - if(!item){ - ItemStack::create(L, NULL); - return 1; - } - ItemStack::create(L, item->clone()); + InventoryList *list = getlist(L, ref, listname); + ItemStack item; + if(list != NULL && i >= 0 && i < (int) list->getSize()) + item = list->getItem(i); + LuaItemStack::create(L, item); return 1; } - // set_stack(self, listname, i, stack) + // set_stack(self, listname, i, stack) -> true/false static int l_set_stack(lua_State *L) { InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); int i = luaL_checknumber(L, 3) - 1; - ItemStack *stack = ItemStack::checkobject(L, 4); + ItemStack newitem = read_item(L, 4); InventoryList *list = getlist(L, ref, listname); - if(!list){ + if(list != NULL && i >= 0 && i < (int) list->getSize()){ + list->changeItem(i, newitem); + reportInventoryChange(L, ref); + lua_pushboolean(L, true); + } else { lua_pushboolean(L, false); - return 1; } - InventoryItem *newitem = stack->getItemCopy(); - InventoryItem *olditem = list->changeItem(i, newitem); - bool success = (olditem != newitem); - delete olditem; - lua_pushboolean(L, success); - reportInventoryChange(L, ref); return 1; } @@ -1143,57 +1499,79 @@ private: InventoryList *list = inv->getList(listname); if(list) inventory_set_list_from_lua(inv, listname, L, 3, - get_server(L), list->getSize()); + list->getSize()); else - inventory_set_list_from_lua(inv, listname, L, 3, - get_server(L)); + inventory_set_list_from_lua(inv, listname, L, 3); reportInventoryChange(L, ref); return 0; } - // autoinsert_stack(self, listname, stack) - static int l_autoinsert_stack(lua_State *L) + // add_item(self, listname, itemstack or itemstring or table or nil) -> itemstack + // Returns the leftover stack + static int l_add_item(lua_State *L) { InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - ItemStack *stack = ItemStack::checkobject(L, 3); + ItemStack item = read_item(L, 3); InventoryList *list = getlist(L, ref, listname); - if(!list){ - lua_pushboolean(L, false); - return 1; + if(list){ + ItemStack leftover = list->addItem(item); + if(leftover.count != item.count) + reportInventoryChange(L, ref); + LuaItemStack::create(L, leftover); + } else { + LuaItemStack::create(L, item); } - InventoryItem *item = stack->getItemCopy(); - if(list->roomForItem(item)){ - delete list->addItem(item); - lua_pushboolean(L, true); - reportInventoryChange(L, ref); + return 1; + } + + // room_for_item(self, listname, itemstack or itemstring or table or nil) -> true/false + // Returns true if the item completely fits into the list + static int l_room_for_item(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3); + InventoryList *list = getlist(L, ref, listname); + if(list){ + lua_pushboolean(L, list->roomForItem(item)); } else { - delete item; lua_pushboolean(L, false); } return 1; } - // autoinsert_stackstring(self, listname, stackstring) - static int l_autoinsert_stackstring(lua_State *L) + // contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false + // Returns true if the list contains the given count of the given item name + static int l_contains_item(lua_State *L) { InvRef *ref = checkobject(L, 1); const char *listname = luaL_checkstring(L, 2); - const char *stackstring = luaL_checkstring(L, 3); + ItemStack item = read_item(L, 3); InventoryList *list = getlist(L, ref, listname); - if(!list){ + if(list){ + lua_pushboolean(L, list->containsItem(item)); + } else { lua_pushboolean(L, false); - return 1; } - InventoryItem *item = InventoryItem::deSerialize(stackstring, - get_server(L)); - if(list->roomForItem(item)){ - delete list->addItem(item); - lua_pushboolean(L, true); - reportInventoryChange(L, ref); + return 1; + } + + // remove_item(self, listname, itemstack or itemstring or table or nil) -> itemstack + // Returns the items that were actually removed + static int l_remove_item(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + ItemStack item = read_item(L, 3); + InventoryList *list = getlist(L, ref, listname); + if(list){ + ItemStack removed = list->removeItem(item); + if(!removed.empty()) + reportInventoryChange(L, ref); + LuaItemStack::create(L, removed); } else { - delete item; - lua_pushboolean(L, false); + LuaItemStack::create(L, ItemStack()); } return 1; } @@ -1266,8 +1644,10 @@ const luaL_reg InvRef::methods[] = { method(InvRef, set_stack), method(InvRef, get_list), method(InvRef, set_list), - method(InvRef, autoinsert_stack), - method(InvRef, autoinsert_stackstring), + method(InvRef, add_item), + method(InvRef, room_for_item), + method(InvRef, contains_item), + method(InvRef, remove_item), {0,0} }; @@ -1420,36 +1800,6 @@ private: return 1; } - // deprecated: inventory_set_list(self, name, {item1, item2, ...}) - static int l_inventory_set_list(lua_State *L) - { - infostream<<"Deprecated: inventory_set_list"<getInventory(); - const char *name = luaL_checkstring(L, 2); - inventory_set_list_from_lua(inv, name, L, 3, - ref->m_env->getGameDef()); - reportMetadataChange(ref); - return 0; - } - - // deprecated: inventory_get_list(self, name) - static int l_inventory_get_list(lua_State *L) - { - infostream<<"Deprecated: inventory_get_list"<getInventory(); - const char *name = luaL_checkstring(L, 2); - inventory_get_list_to_lua(inv, name, L); - return 1; - } - // set_inventory_draw_spec(self, text) static int l_set_inventory_draw_spec(lua_State *L) { @@ -1636,8 +1986,6 @@ const luaL_reg NodeMetaRef::methods[] = { method(NodeMetaRef, get_owner), method(NodeMetaRef, set_infotext), method(NodeMetaRef, get_inventory), - method(NodeMetaRef, inventory_set_list), // deprecated - method(NodeMetaRef, inventory_get_list), // deprecated method(NodeMetaRef, set_inventory_draw_spec), method(NodeMetaRef, set_allow_text_input), method(NodeMetaRef, set_allow_removal), @@ -1798,122 +2146,98 @@ private: return 0; } - // get_wield_digging_properties(self) - static int l_get_wield_digging_properties(lua_State *L) + // set_hp(self, hp) + // hp = number of hitpoints (2 * number of hearts) + // returns: nil + static int l_set_hp(lua_State *L) { ObjectRef *ref = checkobject(L, 1); + luaL_checknumber(L, 2); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; + int hp = lua_tonumber(L, 2); + infostream<<"ObjectRef::l_set_hp(): id="<getId() + <<" hp="<getWieldDiggingProperties(&prop); - push_tool_digging_properties(L, prop); + co->setHP(hp); + // Return + return 0; + } + + // get_hp(self) + // returns: number of hitpoints (2 * number of hearts) + // 0 if not applicable to this type of object + static int l_get_hp(lua_State *L) + { + ObjectRef *ref = checkobject(L, 1); + ServerActiveObject *co = getobject(ref); + if(co == NULL) return 0; + int hp = co->getHP(); + infostream<<"ObjectRef::l_get_hp(): id="<getId() + <<" hp="<damageWieldedItem(amount); - return 0; + InventoryLocation loc = co->getInventoryLocation(); + if(get_server(L)->getInventory(loc) != NULL) + InvRef::create(L, loc); + else + lua_pushnil(L); + return 1; } - // add_to_inventory(self, itemstring) - // returns: true if item was added, (false, "reason") otherwise - static int l_add_to_inventory(lua_State *L) + // get_wield_list(self) + static int l_get_wield_list(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - luaL_checkstring(L, 2); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; - // itemstring - const char *itemstring = luaL_checkstring(L, 2); - infostream<<"ObjectRef::l_add_to_inventory(): id="<getId() - <<" itemstring=\""<getEnv(); - assert(env); - IGameDef *gamedef = env->getGameDef(); - try{ - InventoryItem *item = InventoryItem::deSerialize(is, gamedef); - if(item->getCount() == 0) - item->setCount(1); - bool added = co->addToInventory(item); - // Return - lua_pushboolean(L, added); - if(!added) - lua_pushstring(L, "failed to add item"); - return 2; - } catch(SerializationError &e){ - // Return - lua_pushboolean(L, false); - lua_pushstring(L, (std::string("Invalid item: ") - + e.what()).c_str()); - return 2; - } + lua_pushstring(L, co->getWieldList().c_str()); + return 1; } - // add_to_inventory_later(self, itemstring) - // returns: nil - static int l_add_to_inventory_later(lua_State *L) + // get_wield_index(self) + static int l_get_wield_index(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - luaL_checkstring(L, 2); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; - // itemstring - const char *itemstring = luaL_checkstring(L, 2); - infostream<<"ObjectRef::l_add_to_inventory_later(): id="<getId() - <<" itemstring=\""<getEnv(); - assert(env); - IGameDef *gamedef = env->getGameDef(); - InventoryItem *item = InventoryItem::deSerialize(is, gamedef); - infostream<<"item="<addToInventoryLater(item); - // Return - return 0; + lua_pushinteger(L, co->getWieldIndex() + 1); + return 1; } - // set_hp(self, hp) - // hp = number of hitpoints (2 * number of hearts) - // returns: nil - static int l_set_hp(lua_State *L) + // get_wielded_item(self) + static int l_get_wielded_item(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - luaL_checknumber(L, 2); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; - int hp = lua_tonumber(L, 2); - infostream<<"ObjectRef::l_set_hp(): id="<getId() - <<" hp="<setHP(hp); - // Return - return 0; + LuaItemStack::create(L, co->getWieldedItem()); + return 1; } - // get_hp(self) - // returns: number of hitpoints (2 * number of hearts) - // 0 if not applicable to this type of object - static int l_get_hp(lua_State *L) + // set_wielded_item(self, itemstack or itemstring or table or nil) + static int l_set_wielded_item(lua_State *L) { ObjectRef *ref = checkobject(L, 1); ServerActiveObject *co = getobject(ref); if(co == NULL) return 0; - int hp = co->getHP(); - infostream<<"ObjectRef::l_get_hp(): id="<getId() - <<" hp="<setWieldedItem(item); + lua_pushboolean(L, success); return 1; } @@ -2031,112 +2355,45 @@ private: } // DEPRECATED - // get_entity_name(self) - static int l_get_entity_name(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - std::string name = co->getName(); - lua_pushstring(L, name.c_str()); - return 1; - } - - // get_luaentity(self) - static int l_get_luaentity(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - LuaEntitySAO *co = getluaobject(ref); - if(co == NULL) return 0; - // Do it - luaentity_get(L, co->getId()); - return 1; - } - - /* Player-only */ - - // get_player_name(self) - static int l_get_player_name(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); - if(player == NULL){ - lua_pushnil(L); - return 1; - } - // Do it - lua_pushstring(L, player->getName()); - return 1; - } - - // get_inventory(self) - static int l_get_inventory(lua_State *L) - { - ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); - if(player == NULL) return 0; - // Do it - InvRef::createPlayer(L, player); - return 1; - } - - // deprecated: inventory_set_list(self, name, {item1, item2, ...}) - static int l_inventory_set_list(lua_State *L) - { - infostream<<"Deprecated: inventory_set_list"<inventory, name, L, 3, - player->getEnv()->getGameDef(), PLAYER_INVENTORY_SIZE); - player->m_inventory_not_sent = true; - return 0; - } - - // deprecated: inventory_get_list(self, name) - static int l_inventory_get_list(lua_State *L) + // get_entity_name(self) + static int l_get_entity_name(lua_State *L) { - infostream<<"Deprecated: inventory_get_list"<inventory, name, L); + std::string name = co->getName(); + lua_pushstring(L, name.c_str()); return 1; } - - // get_wielded_itemstring(self) - static int l_get_wielded_itemstring(lua_State *L) + + // get_luaentity(self) + static int l_get_luaentity(lua_State *L) { ObjectRef *ref = checkobject(L, 1); - ServerRemotePlayer *player = getplayer(ref); - if(player == NULL) return 0; + LuaEntitySAO *co = getluaobject(ref); + if(co == NULL) return 0; // Do it - InventoryItem *item = player->getWieldedItem(); - if(item == NULL){ - lua_pushnil(L); - return 1; - } - lua_pushstring(L, item->getItemString().c_str()); + luaentity_get(L, co->getId()); return 1; } - - // get_wielded_item(self) - static int l_get_wielded_item(lua_State *L) + + /* Player-only */ + + // get_player_name(self) + static int l_get_player_name(lua_State *L) { ObjectRef *ref = checkobject(L, 1); ServerRemotePlayer *player = getplayer(ref); - if(player == NULL) return 0; + if(player == NULL){ + lua_pushnil(L); + return 1; + } // Do it - InventoryItem *item0 = player->getWieldedItem(); - push_stack_item(L, item0); + lua_pushstring(L, player->getName()); return 1; } - + // get_look_dir(self) static int l_get_look_dir(lua_State *L) { @@ -2243,12 +2500,13 @@ const luaL_reg ObjectRef::methods[] = { method(ObjectRef, moveto), method(ObjectRef, punch), method(ObjectRef, right_click), - method(ObjectRef, get_wield_digging_properties), - method(ObjectRef, damage_wielded_item), - method(ObjectRef, add_to_inventory), - method(ObjectRef, add_to_inventory_later), method(ObjectRef, set_hp), method(ObjectRef, get_hp), + method(ObjectRef, get_inventory), + method(ObjectRef, get_wield_list), + method(ObjectRef, get_wield_index), + method(ObjectRef, get_wielded_item), + method(ObjectRef, set_wielded_item), // LuaEntitySAO-only method(ObjectRef, setvelocity), method(ObjectRef, getvelocity), @@ -2262,11 +2520,6 @@ const luaL_reg ObjectRef::methods[] = { method(ObjectRef, get_luaentity), // Player-only method(ObjectRef, get_player_name), - method(ObjectRef, get_inventory), - method(ObjectRef, inventory_set_list), // deprecated - method(ObjectRef, inventory_get_list), // deprecated - method(ObjectRef, get_wielded_itemstring), - method(ObjectRef, get_wielded_item), method(ObjectRef, get_look_dir), method(ObjectRef, get_look_pitch), method(ObjectRef, get_look_yaw), @@ -2408,7 +2661,7 @@ private: } } - // EnvRef:add_entity(pos, entityname) + // EnvRef:add_entity(pos, entityname) -> ObjectRef or nil // pos = {x=num, y=num, z=num} static int l_add_entity(lua_State *L) { @@ -2431,22 +2684,29 @@ private: return 1; } - // EnvRef:add_item(pos, inventorystring) + // EnvRef:add_item(pos, itemstack or itemstring or table) -> ObjectRef or nil // pos = {x=num, y=num, z=num} static int l_add_item(lua_State *L) { - infostream<<"EnvRef::l_add_item()"<m_env; if(env == NULL) return 0; // pos v3f pos = checkFloatPos(L, 2); - // inventorystring - const char *inventorystring = luaL_checkstring(L, 3); + // item + ItemStack item = read_item(L, 3); + if(item.empty() || !item.isKnown(get_server(L)->idef())) + return 0; // Do it - ServerActiveObject *obj = new ItemSAO(env, pos, inventorystring); - env->addActiveObject(obj); - return 0; + ServerActiveObject *obj = new ItemSAO(env, pos, item.getItemString()); + int objectid = env->addActiveObject(obj); + // If failed to add, return nothing (reads as nil) + if(objectid == 0) + return 0; + // Return ObjectRef + objectref_get_or_create(L, obj); + return 1; } // EnvRef:add_rat(pos) @@ -2627,55 +2887,6 @@ const luaL_reg EnvRef::methods[] = { Global functions */ -static int l_register_nodedef_defaults(lua_State *L) -{ - luaL_checktype(L, 1, LUA_TTABLE); - - lua_pushvalue(L, 1); // Explicitly put parameter 1 on top of stack - lua_setfield(L, LUA_REGISTRYINDEX, "minetest_nodedef_default"); - - return 0; -} - -// Register new object prototype -// register_entity(name, prototype) -static int l_register_entity(lua_State *L) -{ - std::string name = luaL_checkstring(L, 1); - check_modname_prefix(L, name); - //infostream<<"register_entity: "< stack top - // registered_entities[name] = object - lua_setfield(L, registered_entities, name.c_str()); - - // Get registered object to top of stack - lua_pushvalue(L, 2); - - // Set name field - lua_pushvalue(L, 1); - lua_setfield(L, -2, "name"); - - // Set __index to point to itself - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - - // Set metatable.__index = metatable - luaL_getmetatable(L, "minetest.entity"); - lua_pushvalue(L, -1); // duplicate metatable - lua_setfield(L, -2, "__index"); - // Set object metatable - lua_setmetatable(L, -2); - - return 0; /* number of results */ -} - class LuaABM : public ActiveBlockModifier { private: @@ -2738,465 +2949,329 @@ public: // Call action luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, "action"); - luaL_checktype(L, -1, LUA_TFUNCTION); - push_v3s16(L, p); - pushnode(L, n, env->getGameDef()->ndef()); - lua_pushnumber(L, active_object_count); - lua_pushnumber(L, active_object_count_wider); - if(lua_pcall(L, 4, 0, 0)) - script_error(L, "error: %s", lua_tostring(L, -1)); - } -}; - -// register_abm({...}) -static int l_register_abm(lua_State *L) -{ - //infostream<<"register_abm"<getWritableToolDefManager(); - - ToolDefinition def = read_tool_definition(L, table); - - tooldef->registerTool(name, def); - return 0; /* number of results */ -} - -// register_craftitem(name, {lots of stuff}) -static int l_register_craftitem(lua_State *L) -{ - std::string name = luaL_checkstring(L, 1); - check_modname_prefix(L, name); - //infostream<<"register_craftitem: "<getWritableCraftItemDefManager(); - - // Check if on_drop is defined - lua_getfield(L, table, "on_drop"); - bool got_on_drop = !lua_isnil(L, -1); - lua_pop(L, 1); - - // Check if on_use is defined - lua_getfield(L, table, "on_use"); - bool got_on_use = !lua_isnil(L, -1); - lua_pop(L, 1); - - CraftItemDefinition def; - - getstringfield(L, table, "image", def.imagename); - getstringfield(L, table, "cookresult_itemstring", def.cookresult_item); - getfloatfield(L, table, "furnace_cooktime", def.furnace_cooktime); - getfloatfield(L, table, "furnace_burntime", def.furnace_burntime); - def.usable = getboolfield_default(L, table, "usable", got_on_use); - getboolfield(L, table, "liquids_pointable", def.liquids_pointable); - def.dropcount = getintfield_default(L, table, "dropcount", def.dropcount); - def.stack_max = getintfield_default(L, table, "stack_max", def.stack_max); - - // If an on_drop callback is defined, force dropcount to 1 - if (got_on_drop) - def.dropcount = 1; - - // Register it - craftitemdef->registerCraftItem(name, def); - - lua_pushvalue(L, table); - scriptapi_add_craftitem(L, name.c_str()); - - return 0; /* number of results */ -} - -// register_node(name, {lots of stuff}) -static int l_register_node(lua_State *L) -{ - std::string name = luaL_checkstring(L, 1); - check_modname_prefix(L, name); - //infostream<<"register_node: "<getWritableNodeDefManager(); - - // Get default node definition from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_nodedef_default"); - int nodedef_default = lua_gettop(L); - - /* - Add to minetest.registered_nodes with default as metatable - */ - - // Get the node definition table given as parameter - lua_pushvalue(L, nodedef_table); - - // Set __index to point to itself - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - - // Set nodedef_default as metatable for the definition - lua_pushvalue(L, nodedef_default); - lua_setmetatable(L, nodedef_table); - - // minetest.registered_nodes[name] = nodedef - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_nodes"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushstring(L, name.c_str()); - lua_pushvalue(L, nodedef_table); - lua_settable(L, -3); - - /* - Create definition - */ - - ContentFeatures f; - - // Default to getting the corresponding NodeItem when dug - f.dug_item = std::string("NodeItem \"")+name+"\" 1"; - - // Default to unknown_block.png as all textures - f.setAllTextures("unknown_block.png"); - - /* - Read definiton from Lua - */ - - f.name = name; - - /* Visual definition */ - - f.drawtype = (NodeDrawType)getenumfield(L, nodedef_table, "drawtype", es_DrawType, - NDT_NORMAL); - getfloatfield(L, nodedef_table, "visual_scale", f.visual_scale); - - lua_getfield(L, nodedef_table, "tile_images"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - int i = 0; - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - if(lua_isstring(L, -1)) - f.tname_tiles[i] = lua_tostring(L, -1); - else - f.tname_tiles[i] = ""; - // removes value, keeps key for next iteration - lua_pop(L, 1); - i++; - if(i==6){ - lua_pop(L, 1); - break; - } - } - // Copy last value to all remaining textures - if(i >= 1){ - std::string lastname = f.tname_tiles[i-1]; - while(i < 6){ - f.tname_tiles[i] = lastname; - i++; - } - } - } - lua_pop(L, 1); - - getstringfield(L, nodedef_table, "inventory_image", f.tname_inventory); - - lua_getfield(L, nodedef_table, "special_materials"); - if(lua_istable(L, -1)){ - int table = lua_gettop(L); - lua_pushnil(L); - int i = 0; - while(lua_next(L, table) != 0){ - // key at index -2 and value at index -1 - int smtable = lua_gettop(L); - std::string tname = getstringfield_default( - L, smtable, "image", ""); - bool backface_culling = getboolfield_default( - L, smtable, "backface_culling", true); - MaterialSpec mspec(tname, backface_culling); - f.setSpecialMaterial(i, mspec); - // removes value, keeps key for next iteration - lua_pop(L, 1); - i++; - if(i==6){ - lua_pop(L, 1); - break; - } - } - } - lua_pop(L, 1); - - f.alpha = getintfield_default(L, nodedef_table, "alpha", 255); - - /* Other stuff */ - - lua_getfield(L, nodedef_table, "post_effect_color"); - if(!lua_isnil(L, -1)) - f.post_effect_color = readARGB8(L, -1); - lua_pop(L, 1); - - f.param_type = (ContentParamType)getenumfield(L, nodedef_table, "paramtype", - es_ContentParamType, CPT_NONE); - - // True for all ground-like things like stone and mud, false for eg. trees - getboolfield(L, nodedef_table, "is_ground_content", f.is_ground_content); - f.light_propagates = (f.param_type == CPT_LIGHT); - warn_if_field_exists(L, nodedef_table, "light_propagates", - "deprecated: determined from paramtype"); - getboolfield(L, nodedef_table, "sunlight_propagates", f.sunlight_propagates); - // This is used for collision detection. - // Also for general solidness queries. - getboolfield(L, nodedef_table, "walkable", f.walkable); - // Player can point to these - getboolfield(L, nodedef_table, "pointable", f.pointable); - // Player can dig these - getboolfield(L, nodedef_table, "diggable", f.diggable); - // Player can climb these - getboolfield(L, nodedef_table, "climbable", f.climbable); - // Player can build on these - getboolfield(L, nodedef_table, "buildable_to", f.buildable_to); - // If true, param2 is set to direction when placed. Used for torches. - // NOTE: the direction format is quite inefficient and should be changed - getboolfield(L, nodedef_table, "wall_mounted", f.wall_mounted); - // Whether this content type often contains mineral. - // Used for texture atlas creation. - // Currently only enabled for CONTENT_STONE. - getboolfield(L, nodedef_table, "often_contains_mineral", f.often_contains_mineral); - // Inventory item string as which the node appears in inventory when dug. - // Mineral overrides this. - getstringfield(L, nodedef_table, "dug_item", f.dug_item); - // Extra dug item and its rarity - getstringfield(L, nodedef_table, "extra_dug_item", f.extra_dug_item); - // Usual get interval for extra dug item - getintfield(L, nodedef_table, "extra_dug_item_rarity", f.extra_dug_item_rarity); - // Metadata name of node (eg. "furnace") - getstringfield(L, nodedef_table, "metadata_name", f.metadata_name); - // Whether the node is non-liquid, source liquid or flowing liquid - f.liquid_type = (LiquidType)getenumfield(L, nodedef_table, "liquidtype", - es_LiquidType, LIQUID_NONE); - // If the content is liquid, this is the flowing version of the liquid. - getstringfield(L, nodedef_table, "liquid_alternative_flowing", - f.liquid_alternative_flowing); - // If the content is liquid, this is the source version of the liquid. - getstringfield(L, nodedef_table, "liquid_alternative_source", - f.liquid_alternative_source); - // Viscosity for fluid flow, ranging from 1 to 7, with - // 1 giving almost instantaneous propagation and 7 being - // the slowest possible - f.liquid_viscosity = getintfield_default(L, nodedef_table, - "liquid_viscosity", f.liquid_viscosity); - // Amount of light the node emits - f.light_source = getintfield_default(L, nodedef_table, - "light_source", f.light_source); - f.damage_per_second = getintfield_default(L, nodedef_table, - "damage_per_second", f.damage_per_second); - - lua_getfield(L, nodedef_table, "selection_box"); - if(lua_istable(L, -1)){ - f.selection_box.type = (NodeBoxType)getenumfield(L, -1, "type", - es_NodeBoxType, NODEBOX_REGULAR); + lua_getfield(L, -1, "action"); + luaL_checktype(L, -1, LUA_TFUNCTION); + push_v3s16(L, p); + pushnode(L, n, env->getGameDef()->ndef()); + lua_pushnumber(L, active_object_count); + lua_pushnumber(L, active_object_count_wider); + if(lua_pcall(L, 4, 0, 0)) + script_error(L, "error: %s", lua_tostring(L, -1)); + } +}; - lua_getfield(L, -1, "fixed"); - if(lua_istable(L, -1)) - f.selection_box.fixed = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); +// debug(text) +// Writes a line to dstream +static int l_debug(lua_State *L) +{ + std::string text = lua_tostring(L, 1); + dstream << text << std::endl; + return 0; +} - lua_getfield(L, -1, "wall_top"); - if(lua_istable(L, -1)) - f.selection_box.wall_top = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); +// log([level,] text) +// Writes a line to the logger. +// The one-argument version logs to infostream. +// The two-argument version accept a log level: error, action, info, or verbose. +static int l_log(lua_State *L) +{ + std::string text; + LogMessageLevel level = LMT_INFO; + if(lua_isnone(L, 2)) + { + text = lua_tostring(L, 1); + } + else + { + std::string levelname = lua_tostring(L, 1); + text = lua_tostring(L, 2); + if(levelname == "error") + level = LMT_ERROR; + else if(levelname == "action") + level = LMT_ACTION; + else if(levelname == "verbose") + level = LMT_VERBOSE; + } + log_printline(level, text); + return 0; +} - lua_getfield(L, -1, "wall_bottom"); - if(lua_istable(L, -1)) - f.selection_box.wall_bottom = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); +// register_item_raw({lots of stuff}) +static int l_register_item_raw(lua_State *L) +{ + luaL_checktype(L, 1, LUA_TTABLE); + int table = 1; - lua_getfield(L, -1, "wall_side"); - if(lua_istable(L, -1)) - f.selection_box.wall_side = read_aabbox3df32(L, -1, BS); - lua_pop(L, 1); + // Get the writable item and node definition managers from the server + IWritableItemDefManager *idef = + get_server(L)->getWritableItemDefManager(); + IWritableNodeDefManager *ndef = + get_server(L)->getWritableNodeDefManager(); + + // Check if name is defined + lua_getfield(L, table, "name"); + if(lua_isstring(L, -1)){ + std::string name = lua_tostring(L, -1); + infostream<<"register_item_raw: "<registerItem(def); + + // Read the node definition (content features) and register it + if(def.type == ITEM_NODE) + { + ContentFeatures f = read_content_features(L, table); + ndef->set(f.name, f); } - lua_pop(L, 1); - getstringfield(L, nodedef_table, "cookresult_itemstring", f.cookresult_item); - getfloatfield(L, nodedef_table, "furnace_cooktime", f.furnace_cooktime); - getfloatfield(L, nodedef_table, "furnace_burntime", f.furnace_burntime); - - /* - Register it - */ - - nodedef->set(name, f); - return 0; /* number of results */ } -// alias_node(name, convert_to_name) -static int l_alias_node(lua_State *L) +// register_alias_raw(name, convert_to_name) +static int l_register_alias_raw(lua_State *L) { std::string name = luaL_checkstring(L, 1); std::string convert_to = luaL_checkstring(L, 2); - // Get the writable node definition manager from the server - IWritableNodeDefManager *nodedef = - get_server(L)->getWritableNodeDefManager(); + // Get the writable item definition manager from the server + IWritableItemDefManager *idef = + get_server(L)->getWritableItemDefManager(); - nodedef->setAlias(name, convert_to); + idef->registerAlias(name, convert_to); return 0; /* number of results */ } -// alias_tool(name, convert_to_name) -static int l_alias_tool(lua_State *L) +// helper for register_craft +static bool read_craft_recipe_shaped(lua_State *L, int index, + int &width, std::vector &recipe) { - std::string name = luaL_checkstring(L, 1); - std::string convert_to = luaL_checkstring(L, 2); + if(index < 0) + index = lua_gettop(L) + 1 + index; - // Get the writable tool definition manager from the server - IWritableToolDefManager *tooldef = - get_server(L)->getWritableToolDefManager(); - - tooldef->setAlias(name, convert_to); + if(!lua_istable(L, index)) + return false; - return 0; /* number of results */ + lua_pushnil(L); + int rowcount = 0; + while(lua_next(L, index) != 0){ + int colcount = 0; + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + int table2 = lua_gettop(L); + lua_pushnil(L); + while(lua_next(L, table2) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.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) + return false; + } + // removes value, keeps key for next iteration + lua_pop(L, 1); + rowcount++; + } + return width != 0; } -// alias_craftitem(name, convert_to_name) -static int l_alias_craftitem(lua_State *L) +// helper for register_craft +static bool read_craft_recipe_shapeless(lua_State *L, int index, + std::vector &recipe) { - std::string name = luaL_checkstring(L, 1); - std::string convert_to = luaL_checkstring(L, 2); + if(index < 0) + index = lua_gettop(L) + 1 + index; - // Get the writable CraftItem definition manager from the server - IWritableCraftItemDefManager *craftitemdef = - get_server(L)->getWritableCraftItemDefManager(); - - craftitemdef->setAlias(name, convert_to); + if(!lua_istable(L, index)) + return false; - return 0; /* number of results */ + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_isstring(L, -1)) + return false; + recipe.push_back(lua_tostring(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; } +// helper for register_craft +static bool read_craft_replacements(lua_State *L, int index, + CraftReplacements &replacements) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + + if(!lua_istable(L, index)) + return false; + + lua_pushnil(L); + while(lua_next(L, index) != 0){ + // key at index -2 and value at index -1 + if(!lua_istable(L, -1)) + return false; + lua_rawgeti(L, -1, 1); + if(!lua_isstring(L, -1)) + return false; + std::string replace_from = lua_tostring(L, -1); + lua_pop(L, 1); + lua_rawgeti(L, -1, 2); + if(!lua_isstring(L, -1)) + return false; + std::string replace_to = lua_tostring(L, -1); + lua_pop(L, 1); + replacements.pairs.push_back( + std::make_pair(replace_from, replace_to)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + return true; +} // 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); + std::string type = getstringfield_default(L, table, "type", "shaped"); - 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){ - std::string error; - error += "Invalid crafting recipe (output=\"" - + output + "\")"; - throw LuaError(L, error); - } - } - // removes value, keeps key for next iteration - lua_pop(L, 1); - rowcount++; + /* + CraftDefinitionShaped + */ + if(type == "shaped"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition is missing an output"); + + int width = 0; + std::vector recipe; + lua_getfield(L, table, "recipe"); + if(lua_isnil(L, -1)) + throw LuaError(L, "Crafting definition is missing a recipe" + " (output=\"" + output + "\")"); + if(!read_craft_recipe_shaped(L, -1, width, recipe)) + throw LuaError(L, "Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (output=\"" + output + "\")"); } + + CraftDefinition *def = new CraftDefinitionShaped( + output, width, recipe, replacements); + craftdef->registerCraft(def); } - lua_pop(L, 1); + /* + CraftDefinitionShapeless + */ + else if(type == "shapeless"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition (shapeless)" + " is missing an output"); + + std::vector recipe; + lua_getfield(L, table, "recipe"); + if(lua_isnil(L, -1)) + throw LuaError(L, "Crafting definition (shapeless)" + " is missing a recipe" + " (output=\"" + output + "\")"); + if(!read_craft_recipe_shapeless(L, -1, recipe)) + throw LuaError(L, "Invalid crafting recipe" + " (output=\"" + output + "\")"); + + CraftReplacements replacements; + lua_getfield(L, table, "replacements"); + if(!lua_isnil(L, -1)) + { + if(!read_craft_replacements(L, -1, replacements)) + throw LuaError(L, "Invalid replacements" + " (output=\"" + output + "\")"); + } + + CraftDefinition *def = new CraftDefinitionShapeless( + output, recipe, replacements); + craftdef->registerCraft(def); + } + /* + CraftDefinitionToolRepair + */ + else if(type == "toolrepair"){ + float additional_wear = getfloatfield_default(L, table, + "additional_wear", 0.0); + + CraftDefinition *def = new CraftDefinitionToolRepair( + additional_wear); + craftdef->registerCraft(def); + } + /* + CraftDefinitionCooking + */ + else if(type == "cooking"){ + std::string output = getstringfield_default(L, table, "output", ""); + if(output == "") + throw LuaError(L, "Crafting definition (cooking)" + " is missing an output"); + + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if(recipe == "") + throw LuaError(L, "Crafting definition (cooking)" + " is missing a recipe" + " (output=\"" + output + "\")"); + + float cooktime = getfloatfield_default(L, table, "cooktime", 3.0); + + CraftDefinition *def = new CraftDefinitionCooking( + output, recipe, cooktime); + craftdef->registerCraft(def); + } + /* + CraftDefinitionFuel + */ + else if(type == "fuel"){ + std::string recipe = getstringfield_default(L, table, "recipe", ""); + if(recipe == "") + throw LuaError(L, "Crafting definition (fuel)" + " is missing a recipe"); + + float burntime = getfloatfield_default(L, table, "burntime", 1.0); - CraftDefinition def(output, width, input); - craftdef->registerCraft(def); + CraftDefinition *def = new CraftDefinitionFuel( + recipe, burntime); + craftdef->registerCraft(def); + } + else + { + throw LuaError(L, "Unknown crafting definition type: \"" + type + "\""); + } + lua_pop(L, 1); return 0; /* number of results */ } @@ -3294,15 +3369,19 @@ static int l_get_inventory(lua_State *L) return 1; } +// get_current_modname() +static int l_get_current_modname(lua_State *L) +{ + lua_getfield(L, LUA_REGISTRYINDEX, "minetest_current_modname"); + return 1; +} + // get_modpath(modname) static int l_get_modpath(lua_State *L) { const char *modname = luaL_checkstring(L, 1); - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); // Do it - const ModSpec *mod = server->getModSpec(modname); + const ModSpec *mod = get_server(L)->getModSpec(modname); if(!mod){ lua_pushnil(L); return 1; @@ -3312,34 +3391,22 @@ static int l_get_modpath(lua_State *L) } static const struct luaL_Reg minetest_f [] = { - {"register_nodedef_defaults", l_register_nodedef_defaults}, - {"register_entity", l_register_entity}, - {"register_tool", l_register_tool}, - {"register_craftitem", l_register_craftitem}, - {"register_node", l_register_node}, + {"debug", l_debug}, + {"log", l_log}, + {"register_item_raw", l_register_item_raw}, + {"register_alias_raw", l_register_alias_raw}, {"register_craft", l_register_craft}, - {"register_abm", l_register_abm}, - {"alias_node", l_alias_node}, - {"alias_tool", l_alias_tool}, - {"alias_craftitem", l_alias_craftitem}, {"setting_get", l_setting_get}, {"setting_getbool", l_setting_getbool}, {"chat_send_all", l_chat_send_all}, {"chat_send_player", l_chat_send_player}, {"get_player_privs", l_get_player_privs}, {"get_inventory", l_get_inventory}, + {"get_current_modname", l_get_current_modname}, {"get_modpath", l_get_modpath}, {NULL, NULL} }; -/* - LuaEntity functions -*/ - -static const struct luaL_Reg minetest_entity_m [] = { - {NULL, NULL} -}; - /* Main export function */ @@ -3355,10 +3422,6 @@ void scriptapi_export(lua_State *L, Server *server) lua_pushlightuserdata(L, server); lua_setfield(L, LUA_REGISTRYINDEX, "minetest_server"); - // Store nil as minetest_nodedef_defaults in registry - lua_pushnil(L); - lua_setfield(L, LUA_REGISTRYINDEX, "minetest_nodedef_default"); - // Register global functions in table minetest lua_newtable(L); luaL_register(L, NULL, minetest_f); @@ -3369,31 +3432,13 @@ void scriptapi_export(lua_State *L, Server *server) // Add tables to minetest - lua_newtable(L); - lua_setfield(L, -2, "registered_nodes"); - lua_newtable(L); - lua_setfield(L, -2, "registered_entities"); - lua_newtable(L); - lua_setfield(L, -2, "registered_craftitems"); - lua_newtable(L); - lua_setfield(L, -2, "registered_abms"); - lua_newtable(L); lua_setfield(L, -2, "object_refs"); lua_newtable(L); lua_setfield(L, -2, "luaentities"); - // Create entity prototype - luaL_newmetatable(L, "minetest.entity"); - // metatable.__index = metatable - lua_pushvalue(L, -1); // Duplicate metatable - lua_setfield(L, -2, "__index"); - // Put functions in metatable - luaL_register(L, NULL, minetest_entity_m); - // Put other stuff in metatable - // Register wrappers - ItemStack::Register(L); + LuaItemStack::Register(L); InvRef::Register(L); NodeMetaRef::Register(L); ObjectRef::Register(L); @@ -3705,62 +3750,22 @@ void scriptapi_get_creative_inventory(lua_State *L, ServerRemotePlayer *player) lua_getfield(L, -1, "creative_inventory"); luaL_checktype(L, -1, LUA_TTABLE); inventory_set_list_from_lua(&player->inventory, "main", L, -1, - player->getEnv()->getGameDef(), PLAYER_INVENTORY_SIZE); + PLAYER_INVENTORY_SIZE); } /* - craftitem + item callbacks */ -static void pushPointedThing(lua_State *L, const PointedThing& pointed) -{ - lua_newtable(L); - if(pointed.type == POINTEDTHING_NODE) - { - lua_pushstring(L, "node"); - lua_setfield(L, -2, "type"); - push_v3s16(L, pointed.node_undersurface); - lua_setfield(L, -2, "under"); - push_v3s16(L, pointed.node_abovesurface); - lua_setfield(L, -2, "above"); - } - else if(pointed.type == POINTEDTHING_OBJECT) - { - lua_pushstring(L, "object"); - lua_setfield(L, -2, "type"); - objectref_get(L, pointed.object_id); - lua_setfield(L, -2, "ref"); - } - else - { - lua_pushstring(L, "nothing"); - lua_setfield(L, -2, "type"); - } -} - -void scriptapi_add_craftitem(lua_State *L, const char *name) -{ - StackUnroller stack_unroller(L); - assert(lua_gettop(L) > 0); - - // Set minetest.registered_craftitems[name] = table on top of stack - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_craftitems"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_pushvalue(L, -3); // push another reference to the table to be registered - lua_setfield(L, -2, name); // set minetest.registered_craftitems[name] -} - -static bool get_craftitem_callback(lua_State *L, const char *name, - const char *callbackname) +// Retrieves minetest.registered_items[name][callbackname] +// If that is nil or on error, return false and stack is unchanged +// If that is a function, returns true and pushes the +// function onto the stack +static bool get_item_callback(lua_State *L, + const char *name, const char *callbackname) { - // Get minetest.registered_craftitems[name][callbackname] - // If that is nil or on error, return false and stack is unchanged - // If that is a function, returns true and pushes the - // function onto the stack - lua_getglobal(L, "minetest"); - lua_getfield(L, -1, "registered_craftitems"); + lua_getfield(L, -1, "registered_items"); lua_remove(L, -2); luaL_checktype(L, -1, LUA_TTABLE); lua_getfield(L, -1, name); @@ -3768,7 +3773,7 @@ static bool get_craftitem_callback(lua_State *L, const char *name, // Should be a table if(lua_type(L, -1) != LUA_TTABLE) { - errorstream<<"CraftItem name \""<updateAliases(m_itemdef); + // Initialize Environment m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, @@ -1042,10 +1043,9 @@ Server::~Server() // Delete Environment delete m_env; - delete m_toolmgr; + delete m_itemdef; delete m_nodedef; delete m_craftdef; - delete m_craftitemdef; // Deinitialize scripting infostream<<"Server: Deinitializing scripting"<getBool("creative_mode") == true) - { - infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode" - <getType() == IACTION_MOVE) { InventoryList *rlist = player->inventory.getList("craftresult"); assert(rlist); @@ -2401,17 +2387,28 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) IMoveAction *ma = (IMoveAction*)a; + ma->from_inv.applyCurrentPlayer(player->getName()); + ma->to_inv.applyCurrentPlayer(player->getName()); + + bool from_inv_is_current_player = + (ma->from_inv.type == InventoryLocation::PLAYER) && + (ma->from_inv.name == player->getName()); + + bool to_inv_is_current_player = + (ma->to_inv.type == InventoryLocation::PLAYER) && + (ma->to_inv.name == player->getName()); + /* Disable moving items into craftresult from elsewhere */ - if(ma->to_inv == "current_player" + if(to_inv_is_current_player && ma->to_list == "craftresult" - && (ma->from_inv != "current_player" + && (!from_inv_is_current_player || ma->from_list != "craftresult")) { infostream<<"Ignoring IMoveAction from " - <from_inv<<":"<from_list - <<" to "<to_inv<<":"<to_list + <<(ma->from_inv.dump())<<":"<from_list + <<" to "<<(ma->to_inv.dump())<<":"<to_list <<" because dst is craftresult" <<" and src isn't craftresult"<from_inv == "current_player" + if(from_inv_is_current_player && ma->from_list == "craftresult" - && player->craftresult_is_preview) + && player->craftresult_is_preview + && g_settings->getBool("creative_mode") == false) { + ItemStack crafting_result; + bool crafting_possible = GetCraftingResult(peer_id, + crafting_result, false); + /* - If the craftresult is placed on itself, crafting takes - place and result is moved into main list + If the craftresult is placed on itself, + crafting takes place and result is moved + into main list. */ - if(ma->to_inv == "current_player" + if(crafting_possible + && to_inv_is_current_player && ma->to_list == "craftresult") { - // Except if main list doesn't have free slots - if(mlist->getFreeSlots() == 0){ - infostream<<"Cannot craft: Main list doesn't have" - <<" free slots"<craftresult_is_preview = false; - clist->decrementMaterials(1); - - InventoryItem *item1 = rlist->changeItem(0, NULL); - mlist->addItem(item1); + if(mlist->roomForItem(crafting_result)) + { + actionstream<getName() + <<" crafts " + <m_inventory_not_sent = true; + // Decrement crafting materials + GetCraftingResult(peer_id, crafting_result, true); + mlist->addItem(crafting_result); + rlist->clearItems(); + player->craftresult_is_preview = true; + srp->m_inventory_not_sent = true; + } - delete a; - return; } /* - Disable action if there are no free slots in - destination - - If the item is placed on an item that is not of the - same kind, the existing item will be first moved to - craftresult and immediately moved to the free slot. + Otherwise, if the destination is part of + the same player's inventory, crafting + takes place normally. */ - do{ - Inventory *inv_to = InventoryManager::getInventory(&c, ma->to_inv); - if(!inv_to) break; - InventoryList *list_to = inv_to->getList(ma->to_list); - if(!list_to) break; - if(list_to->getFreeSlots() == 0){ - infostream<<"Cannot craft: Destination doesn't have" - <<" free slots"<inventory.getList(ma->to_list); + if(list && list->itemFits(ma->to_i, crafting_result)) + { + actionstream<getName() + <<" crafts " + <addItem(ma->to_i, crafting_result); + rlist->clearItems(); + player->craftresult_is_preview = true; + srp->m_inventory_not_sent = true; } - }while(0); // Allow break + } - /* - Ok, craft normally. - */ - player->craftresult_is_preview = false; - clist->decrementMaterials(1); - - /* Print out action */ - InventoryItem *item = rlist->getItem(0); - std::string itemstring = "NULL"; - if(item) - itemstring = item->getItemString(); - actionstream<getName()<<" crafts " - <apply(&c, this, m_env); - + // Do not apply the action normally. delete a; return; } @@ -2498,63 +2487,38 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ // Disallow moving items in elsewhere than player's inventory - // if not allowed to build + // if not allowed to interact if((getPlayerPrivs(player) & PRIV_INTERACT) == 0 - && (ma->from_inv != "current_player" - || ma->to_inv != "current_player")) + && (from_inv_is_current_player + || to_inv_is_current_player)) { infostream<<"Cannot move outside of player's inventory: " - <<"No build privilege"<from_inv != "current_player" - && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + // If player is not an admin, check for ownership of src and dst + if((getPlayerPrivs(player) & PRIV_SERVER) == 0) { - Strfnd fn(ma->from_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") + std::string owner_from = getInventoryOwner(ma->from_inv); + if(owner_from != "" && owner_from != player->getName()) { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta->getOwner() != "" && - meta->getOwner() != player->getName()) - { - infostream<<"Cannot move item: " - "not owner of metadata" - <getName() + <<" tried to access an inventory that" + <<" belongs to "<to_inv != "current_player" - && (getPlayerPrivs(player) & PRIV_SERVER) == 0) - { - Strfnd fn(ma->to_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") + + std::string owner_to = getInventoryOwner(ma->to_inv); + if(owner_to != "" && owner_to != player->getName()) { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta->getOwner() != "" && - meta->getOwner() != player->getName()) - { - infostream<<"Cannot move item: " - "not owner of metadata" - <getName() + <<" tried to access an inventory that" + <<" belongs to "<getType() == IACTION_DROP) { IDropAction *da = (IDropAction*)a; - // Disallow dropping items if not allowed to build + + da->from_inv.applyCurrentPlayer(player->getName()); + + // Disallow dropping items if not allowed to interact if((getPlayerPrivs(player) & PRIV_INTERACT) == 0) { delete a; return; } // If player is not an admin, check for ownership - else if (da->from_inv != "current_player" - && (getPlayerPrivs(player) & PRIV_SERVER) == 0) + else if((getPlayerPrivs(player) & PRIV_SERVER) == 0) { - Strfnd fn(da->from_inv); - std::string id0 = fn.next(":"); - if(id0 == "nodemeta") + std::string owner_from = getInventoryOwner(da->from_inv); + if(owner_from != "" && owner_from != player->getName()) { - v3s16 p; - p.X = stoi(fn.next(",")); - p.Y = stoi(fn.next(",")); - p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta->getOwner() != "" && - meta->getOwner() != player->getName()) - { - infostream<<"Cannot move item: " - "not owner of metadata" - <getName() + <<" tried to access an inventory that" + <<" belongs to "<apply(&c, this, m_env); + a->apply(this, srp); // Eat the action delete a; } @@ -2809,8 +2765,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; u16 item = readU16(&data[2]); - player->wieldItem(item); - SendWieldedItem(player); + srp->setWieldIndex(item); + SendWieldedItem(srp); } else if(command == TOSERVER_RESPAWN) { @@ -2880,7 +2836,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) v3f player_pos = srp->m_last_good_position; // Update wielded item - srp->wieldItem(item_i); + if(srp->getWieldIndex() != item_i) + { + srp->setWieldIndex(item_i); + SendWieldedItem(srp); + } // Get pointed to node (undefined if not POINTEDTYPE_NODE) v3s16 p_under = pointed.node_undersurface; @@ -2900,23 +2860,26 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } + v3f pointed_pos_under = player_pos; + v3f pointed_pos_above = player_pos; + if(pointed.type == POINTEDTHING_NODE) + { + pointed_pos_under = intToFloat(p_under, BS); + pointed_pos_above = intToFloat(p_above, BS); + } + else if(pointed.type == POINTEDTHING_OBJECT) + { + pointed_pos_under = pointed_object->getBasePosition(); + pointed_pos_above = pointed_pos_under; + } + /* Check that target is reasonably close (only when digging or placing things) */ if(action == 0 || action == 2 || action == 3) { - v3f pointed_pos = player_pos; - if(pointed.type == POINTEDTHING_NODE) - { - pointed_pos = intToFloat(p_under, BS); - } - else if(pointed.type == POINTEDTHING_OBJECT) - { - pointed_pos = pointed_object->getBasePosition(); - } - - float d = player_pos.getDistanceFrom(pointed_pos); + float d = player_pos.getDistanceFrom(pointed_pos_under); float max_d = BS * 10; // Just some large enough value if(d > max_d){ actionstream<<"Player "<getName() @@ -2926,7 +2889,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<". ignoring."<SetBlockNotSent(blockpos); // Do nothing else return; @@ -2936,8 +2899,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) /* Make sure the player is allowed to do it */ - bool build_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0; - if(!build_priv) + bool interact_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0; + if(!interact_priv) { infostream<<"Ignoring interaction from player "<getName() <<" because privileges are "<inventory.getList("main"); if(mlist != NULL) { - InventoryItem *item = mlist->getItem(item_i); - if(item && (std::string)item->getName() == "ToolItem") - { - ToolItem *titem = (ToolItem*)item; - std::string toolname = titem->getToolName(); - - // Get digging properties for material and tool - ToolDiggingProperties tp = - m_toolmgr->getDiggingProperties(toolname); - DiggingProperties prop = - getDiggingProperties(material, &tp, m_nodedef); - - if(prop.diggable == false) - { - infostream<<"Server: WARNING: Player digged" - <<" with impossible material + tool" - <<" combination"<addWear(prop.wear); - - if(weared_out) - { - mlist->deleteItem(item_i); - } - - srp->m_inventory_not_sent = true; - } + ItemStack &item = mlist->getItem(item_i); + + // Get digging properties for material and tool + ToolDiggingProperties tp = + item.getToolDiggingProperties(m_itemdef); + DiggingProperties prop = + getDiggingProperties(material, &tp, m_nodedef); + item.addWear(prop.wear, m_itemdef); + srp->m_inventory_not_sent = true; } /* Add dug item to inventory */ - InventoryItem *item = NULL; + ItemStack item; if(mineral != MINERAL_NONE) item = getDiggedMineralItem(mineral, this); // If not mineral - if(item == NULL) + if(item.empty()) { const std::string &dug_s = m_nodedef->get(material).dug_item; if(dug_s != "") { - std::istringstream is(dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is, this); + item.deSerialize(dug_s, m_itemdef); } } - if(item != NULL) + if(!item.empty()) { // Add a item to inventory player->inventory.addItem("main", item); srp->m_inventory_not_sent = true; } - item = NULL; + item.clear(); { const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item; @@ -3179,12 +3122,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(extra_dug_s != "" && extra_rarity != 0 && myrand() % extra_rarity == 0) { - std::istringstream is(extra_dug_s, std::ios::binary); - item = InventoryItem::deSerialize(is, this); + item.deSerialize(extra_dug_s, m_itemdef); } } - if(item != NULL) + if(!item.empty()) { // Add a item to inventory player->inventory.addItem("main", item); @@ -3226,242 +3168,155 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ else if(action == 3) { - if(pointed.type == POINTEDTHING_NODE) + if(!interact_priv) { - InventoryList *ilist = player->inventory.getList("main"); - if(ilist == NULL) - return; - - // Get item - InventoryItem *item = ilist->getItem(item_i); - - // If there is no item, it is not possible to add it anywhere - if(item == NULL) - return; + infostream<<"Not allowing player " + <getName()<<" to place item: " + <<"no interact privileges"<getName()) - { - bool cannot_place_node = !build_priv; - - try{ - // Don't add a node if this is not a free space - MapNode n2 = m_env->getMap().getNode(p_above); - if(m_nodedef->get(n2).buildable_to == false) - { - infostream<<"Client "<getWieldedItem(); - if(cannot_place_node) - { - // Client probably has wrong data. - // Set block not sent, so that client will get - // a valid one. - RemoteClient *client = getClient(peer_id); - v3s16 blockpos = getNodeBlockPos(p_above); - client->SetBlockNotSent(blockpos); - return; - } + if(pointed.type == POINTEDTHING_OBJECT) + { + // Right click object - // Reset build time counter - getClient(peer_id)->m_time_from_building = 0.0; - - // Create node data - MaterialItem *mitem = (MaterialItem*)item; - MapNode n; - n.setContent(mitem->getMaterial()); - - actionstream<getName()<<" places material " - <<(int)mitem->getMaterial() - <<" at "<get(n).wall_mounted) - n.param2 = packDir(p_under - p_above); + // Skip if object has been removed + if(pointed_object->m_removed) + return; - // Calculate the direction for furnaces and chests and stuff - if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE) - { - v3f playerpos = player->getPosition(); - v3f blockpos = intToFloat(p_above, BS) - playerpos; - blockpos = blockpos.normalize(); - n.param1 = 0; - if (fabs(blockpos.X) > fabs(blockpos.Z)) { - if (blockpos.X < 0) - n.param1 = 3; - else - n.param1 = 1; - } else { - if (blockpos.Z < 0) - n.param1 = 2; - else - n.param1 = 0; - } - } + actionstream<getName()<<" right-clicks object " + < far_players; - sendAddNode(p_above, n, 0, &far_players, 30); - - /* - Handle inventory - */ - InventoryList *ilist = player->inventory.getList("main"); - if(g_settings->getBool("creative_mode") == false && ilist) - { - // Remove from inventory and send inventory - if(mitem->getCount() <= 1) - ilist->deleteItem(item_i); - else - mitem->remove(1); - srp->m_inventory_not_sent = true; - } - - /* - Add node. + // Do stuff + pointed_object->rightClick(srp); + } + else if(scriptapi_item_on_place(m_lua, + item, srp, pointed)) + { + // Placement was handled in lua - This takes some time so it is done after the quick stuff - */ - core::map modified_blocks; - { - MapEditEventIgnorer ign(&m_ignore_map_edit_events); + // Apply returned ItemStack + if(g_settings->getBool("creative_mode") == false) + srp->setWieldedItem(item); + } + else if(pointed.type == POINTEDTHING_NODE && + item.getDefinition(m_itemdef).type == ITEM_NODE) + { + bool cannot_place_node = !interact_priv; - std::string p_name = std::string(player->getName()); - m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name); - } - /* - Set blocks not sent to far players - */ - for(core::list::Iterator - i = far_players.begin(); - i != far_players.end(); i++) + try{ + // Don't add a node if this is not a free space + MapNode n2 = m_env->getMap().getNode(p_above); + if(m_nodedef->get(n2).buildable_to == false) { - u16 peer_id = *i; - RemoteClient *client = getClient(peer_id); - if(client==NULL) - continue; - client->SetBlocksNotSent(modified_blocks); + infostream<<"Client "<getBasePosition(); + if(cannot_place_node) + { + // Client probably has wrong data. + // Set block not sent, so that client will get + // a valid one. + RemoteClient *client = getClient(peer_id); + v3s16 blockpos = getNodeBlockPos(p_above); + client->SetBlockNotSent(blockpos); + return; + } - // Randomize a bit - pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; - pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0; - } + // Reset build time counter + getClient(peer_id)->m_time_from_building = 0.0; - //pos.Y -= BS*0.45; - //pos.Y -= BS*0.25; // let it drop a bit + // Create node data + MapNode n(m_nodedef, item.name, 0, 0); - /* - Check that the block is loaded so that the item - can properly be added to the static list too - */ - v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS)); - MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); - if(block==NULL) - { - infostream<<"Error while placing item: " - "block not found"<getName()<<" places material " + <getName()<<" places "<getName() - <<" at "<get(n).wall_mounted) + n.param2 = packDir(p_under - p_above); - /* - Place the item - */ - bool remove = item->dropOrPlace(m_env, srp, pos, true, -1); - if(remove && g_settings->getBool("creative_mode") == false) - { - InventoryList *ilist = player->inventory.getList("main"); - if(ilist){ - // Remove from inventory and send inventory - ilist->deleteItem(item_i); - srp->m_inventory_not_sent = true; - } + // Calculate the direction for furnaces and chests and stuff + if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE) + { + v3f playerpos = player->getPosition(); + v3f blockpos = intToFloat(p_above, BS) - playerpos; + blockpos = blockpos.normalize(); + n.param1 = 0; + if (fabs(blockpos.X) > fabs(blockpos.Z)) { + if (blockpos.X < 0) + n.param1 = 3; + else + n.param1 = 1; + } else { + if (blockpos.Z < 0) + n.param1 = 2; + else + n.param1 = 0; } } - } - else if(pointed.type == POINTEDTHING_OBJECT) - { - // Right click object - if(!build_priv) - return; + /* + Send to all close-by players + */ + core::list far_players; + sendAddNode(p_above, n, 0, &far_players, 30); + + /* + Handle inventory + */ + if(g_settings->getBool("creative_mode") == false) + { + // Remove from inventory and send inventory + item.remove(1); + srp->setWieldedItem(item); + } - // Skip if object has been removed - if(pointed_object->m_removed) - return; + /* + Add node. - actionstream<getName()<<" right-clicks object " - < modified_blocks; + { + MapEditEventIgnorer ign(&m_ignore_map_edit_events); - // Do stuff - pointed_object->rightClick(srp); + std::string p_name = std::string(player->getName()); + m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name); + } + /* + Set blocks not sent to far players + */ + for(core::list::Iterator + i = far_players.begin(); + i != far_players.end(); i++) + { + u16 peer_id = *i; + RemoteClient *client = getClient(peer_id); + if(client==NULL) + continue; + client->SetBlocksNotSent(modified_blocks); + } + + /* + Run script hook + */ + scriptapi_environment_on_placenode(m_lua, p_above, n, srp); } } // action == 3 @@ -3471,38 +3326,25 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ else if(action == 4) { - InventoryList *ilist = player->inventory.getList("main"); - if(ilist == NULL) - return; - - // Get item - InventoryItem *item = ilist->getItem(item_i); - - // If there is no item, it is not possible to add it anywhere - if(item == NULL) - return; - - // Requires build privs - if(!build_priv) + // Requires interact privs + if(!interact_priv) { infostream<<"Not allowing player to use item: " - "no build privileges"<getName()<<" uses "<getName() + ItemStack item = srp->getWieldedItem(); + + actionstream<getName()<<" uses "<use(m_env, srp, pointed); - - if(remove && g_settings->getBool("creative_mode") == false) + if(scriptapi_item_on_use(m_lua, + item, srp, pointed)) { - InventoryList *ilist = player->inventory.getList("main"); - if(ilist){ - // Remove from inventory and send inventory - ilist->deleteItem(item_i); - srp->m_inventory_not_sent = true; - } + // Apply returned ItemStack + if(g_settings->getBool("creative_mode") == false) + srp->setWieldedItem(item); } } // action == 4 @@ -3515,9 +3357,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) infostream<<"WARNING: Server: Invalid action " <completeAddToInventoryLater(item_i); } else { @@ -3549,6 +3388,9 @@ Inventory* Server::getInventory(const InventoryLocation &loc) case InventoryLocation::UNDEFINED: {} break; + case InventoryLocation::CURRENT_PLAYER: + {} + break; case InventoryLocation::PLAYER: { Player *player = m_env->getPlayer(loc.name.c_str()); @@ -3570,6 +3412,33 @@ Inventory* Server::getInventory(const InventoryLocation &loc) } return NULL; } +std::string Server::getInventoryOwner(const InventoryLocation &loc) +{ + switch(loc.type){ + case InventoryLocation::UNDEFINED: + {} + break; + case InventoryLocation::CURRENT_PLAYER: + {} + break; + case InventoryLocation::PLAYER: + { + return loc.name; + } + break; + case InventoryLocation::NODEMETA: + { + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if(!meta) + return ""; + return meta->getOwner(); + } + break; + default: + assert(0); + } + return ""; +} void Server::setInventoryModified(const InventoryLocation &loc) { switch(loc.type){ @@ -3604,64 +3473,6 @@ void Server::setInventoryModified(const InventoryLocation &loc) assert(0); } } -#if 0 -Inventory* Server::getInventory(InventoryContext *c, std::string id) -{ - if(id == "current_player") - { - assert(c->current_player); - return &(c->current_player->inventory); - } - - 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); - } - - infostream<<__FUNCTION_NAME<<": unknown id "<current_player); - ServerRemotePlayer *srp = - static_cast(c->current_player); - srp->m_inventory_not_sent = true; - 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; - } - - infostream<<__FUNCTION_NAME<<": unknown id "< Server::getPlayerInfo() { @@ -3783,8 +3594,8 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id, con.Send(peer_id, 0, data, true); } -void Server::SendToolDef(con::Connection &con, u16 peer_id, - IToolDefManager *tooldef) +void Server::SendItemDef(con::Connection &con, u16 peer_id, + IItemDefManager *itemdef) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3792,16 +3603,18 @@ void Server::SendToolDef(con::Connection &con, u16 peer_id, /* u16 command u32 length of the next item - serialized ToolDefManager + zlib-compressed serialized ItemDefManager */ - writeU16(os, TOCLIENT_TOOLDEF); + writeU16(os, TOCLIENT_ITEMDEF); std::ostringstream tmp_os(std::ios::binary); - tooldef->serialize(tmp_os); - os<serialize(tmp_os); + std::ostringstream tmp_os2(std::ios::binary); + compressZlib(tmp_os.str(), tmp_os2); + os<serialize(os); - return os.str(); -} - -void Server::SendWieldedItem(const Player* player) +void Server::SendWieldedItem(const ServerRemotePlayer* srp) { DSTACK(__FUNCTION_NAME); - assert(player); + assert(srp); std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_PLAYERITEM); writeU16(os, 1); - writeU16(os, player->peer_id); - os<peer_id); + os<getWieldedItem().getItemString()); // Make data buffer std::string s = os.str(); @@ -3934,8 +3714,10 @@ void Server::SendPlayerItems() for(i = players.begin(); i != players.end(); ++i) { Player *p = *i; + ServerRemotePlayer *srp = + static_cast(p); writeU16(os, p->peer_id); - os<getWieldedItem().getItemString()); } // Make data buffer @@ -4576,6 +4358,44 @@ void Server::RespawnPlayer(Player *player) SendPlayerHP(player); } +bool Server::GetCraftingResult(u16 peer_id, ItemStack &result, bool decrementInput) +{ + DSTACK(__FUNCTION_NAME); + + Player* player = m_env->getPlayer(peer_id); + assert(player); + + // Get the crafting InventoryList of the player in which we will operate + InventoryList *clist = player->inventory.getList("craft"); + assert(clist); + + // Mangle crafting grid to an another format + CraftInput ci; + ci.method = CRAFT_METHOD_NORMAL; + ci.width = 3; + for(u16 i=0; i<9; i++) + { + ci.items.push_back(clist->getItem(i)); + } + + // Find out what is crafted and add it to result item slot + CraftOutput co; + bool found = m_craftdef->getCraftResult(ci, co, decrementInput, this); + if(found) + result.deSerialize(co.item, m_itemdef); + + if(decrementInput) + { + // CraftInput has been changed, apply changes in clist + for(u16 i=0; i<9; i++) + { + clist->changeItem(i, ci.items[i]); + } + } + + return found; +} + void Server::UpdateCrafting(u16 peer_id) { DSTACK(__FUNCTION_NAME); @@ -4601,12 +4421,11 @@ void Server::UpdateCrafting(u16 peer_id) if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0) { // Grab item out of craftresult - InventoryItem *item = rlist->changeItem(0, NULL); + ItemStack item = rlist->changeItem(0, ItemStack()); // Try to put in main - InventoryItem *leftover = mlist->addItem(item); - // If there are leftovers, put them back to craftresult and - // delete leftovers - delete rlist->addItem(leftover); + ItemStack leftover = mlist->addItem(item); + // If there are leftovers, put them back to craftresult + rlist->addItem(leftover); // Inventory was modified srp->m_inventory_not_sent = true; } @@ -4616,28 +4435,17 @@ void Server::UpdateCrafting(u16 peer_id) if(rlist->getUsedSlots() == 0) player->craftresult_is_preview = true; - // If it is a preview, clear the possible old preview in it - if(player->craftresult_is_preview) - rlist->clearItems(); - // If it is a preview, find out what is the crafting result // and put it in if(player->craftresult_is_preview) { - // Mangle crafting grid to an another format - 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); + // Clear the possible old preview in it + rlist->clearItems(); - // Find out what is crafted and add it to result item slot - InventoryItem *result = m_craftdef->getCraftResult(cpi, this); - if(result) - rlist->addItem(result); + // Put the new preview in + ItemStack crafting_result; + if(GetCraftingResult(peer_id, crafting_result, false)) + rlist->addItem(crafting_result); } } @@ -4734,9 +4542,9 @@ void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate) // IGameDef interface // Under envlock -IToolDefManager* Server::getToolDefManager() +IItemDefManager* Server::getItemDefManager() { - return m_toolmgr; + return m_itemdef; } INodeDefManager* Server::getNodeDefManager() { @@ -4746,10 +4554,6 @@ ICraftDefManager* Server::getCraftDefManager() { return m_craftdef; } -ICraftItemDefManager* Server::getCraftItemDefManager() -{ - return m_craftitemdef; -} ITextureSource* Server::getTextureSource() { return NULL; @@ -4759,9 +4563,9 @@ u16 Server::allocateUnknownNodeId(const std::string &name) return m_nodedef->allocateDummy(name); } -IWritableToolDefManager* Server::getWritableToolDefManager() +IWritableItemDefManager* Server::getWritableItemDefManager() { - return m_toolmgr; + return m_itemdef; } IWritableNodeDefManager* Server::getWritableNodeDefManager() { @@ -4771,10 +4575,6 @@ IWritableCraftDefManager* Server::getWritableCraftDefManager() { return m_craftdef; } -IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager() -{ - return m_craftitemdef; -} const ModSpec* Server::getModSpec(const std::string &modname) { @@ -4874,7 +4674,7 @@ ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id) { // Warning: double code below // Backup actual inventory - player->inventory_backup = new Inventory(); + player->inventory_backup = new Inventory(m_itemdef); *(player->inventory_backup) = player->inventory; // Set creative inventory player->resetInventory(); @@ -4919,7 +4719,7 @@ ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id) { // Warning: double code above // Backup actual inventory - player->inventory_backup = new Inventory(); + player->inventory_backup = new Inventory(m_itemdef); *(player->inventory_backup) = player->inventory; // Set creative inventory player->resetInventory(); diff --git a/src/server.h b/src/server.h index 5938f915d..d979cb518 100644 --- a/src/server.h +++ b/src/server.h @@ -36,10 +36,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inventorymanager.h" struct LuaState; typedef struct lua_State lua_State; -class IWritableToolDefManager; +class IWritableItemDefManager; class IWritableNodeDefManager; class IWritableCraftDefManager; -class IWritableCraftItemDefManager; /* Some random functions @@ -437,6 +436,7 @@ public: Shall be called with the environment and the connection locked. */ Inventory* getInventory(const InventoryLocation &loc); + std::string getInventoryOwner(const InventoryLocation &loc); void setInventoryModified(const InventoryLocation &loc); // Connection must be locked when called @@ -514,17 +514,15 @@ public: // IGameDef interface // Under envlock - virtual IToolDefManager* getToolDefManager(); + virtual IItemDefManager* getItemDefManager(); virtual INodeDefManager* getNodeDefManager(); virtual ICraftDefManager* getCraftDefManager(); - virtual ICraftItemDefManager* getCraftItemDefManager(); virtual ITextureSource* getTextureSource(); virtual u16 allocateUnknownNodeId(const std::string &name); - IWritableToolDefManager* getWritableToolDefManager(); + IWritableItemDefManager* getWritableItemDefManager(); IWritableNodeDefManager* getWritableNodeDefManager(); IWritableCraftDefManager* getWritableCraftDefManager(); - IWritableCraftItemDefManager* getWritableCraftItemDefManager(); const ModSpec* getModSpec(const std::string &modname); @@ -545,12 +543,10 @@ private: const std::wstring &reason); static void SendDeathscreen(con::Connection &con, u16 peer_id, bool set_camera_point_target, v3f camera_point_target); - static void SendToolDef(con::Connection &con, u16 peer_id, - IToolDefManager *tooldef); + static void SendItemDef(con::Connection &con, u16 peer_id, + IItemDefManager *itemdef); static void SendNodeDef(con::Connection &con, u16 peer_id, INodeDefManager *nodedef); - static void SendCraftItemDef(con::Connection &con, u16 peer_id, - ICraftItemDefManager *nodedef); /* Non-static send methods. @@ -562,7 +558,7 @@ private: // Envlock and conlock should be locked when calling these void SendInventory(u16 peer_id); // send wielded item info about player to all - void SendWieldedItem(const Player *player); + void SendWieldedItem(const ServerRemotePlayer *srp); // send wielded item info about all players to all players void SendPlayerItems(); void SendChatMessage(u16 peer_id, const std::wstring &message); @@ -599,6 +595,7 @@ private: void HandlePlayerHP(Player *player, s16 damage); void RespawnPlayer(Player *player); + bool GetCraftingResult(u16 peer_id, ItemStack &result, bool decrementInput); void UpdateCrafting(u16 peer_id); // When called, connection mutex should be locked @@ -664,8 +661,8 @@ private: // Envlock and conlock should be locked when using Lua lua_State *m_lua; - // Tool definition manager - IWritableToolDefManager *m_toolmgr; + // Item definition manager + IWritableItemDefManager *m_itemdef; // Node definition manager IWritableNodeDefManager *m_nodedef; @@ -673,9 +670,6 @@ private: // Craft definition manager IWritableCraftDefManager *m_craftdef; - // CraftItem definition manager - IWritableCraftItemDefManager *m_craftitemdef; - // Mods core::list m_mods; @@ -740,7 +734,7 @@ private: core::list m_modspaths; bool m_shutdown_requested; - + /* Map edit event queue. Automatically receives all map edits. The constructor of this class registers us to receive them through diff --git a/src/serverobject.cpp b/src/serverobject.cpp index ca3d2c3b9..2609e3015 100644 --- a/src/serverobject.cpp +++ b/src/serverobject.cpp @@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serverobject.h" #include #include "inventory.h" -#include "tooldef.h" +#include "materials.h" ServerActiveObject::ServerActiveObject(ServerEnvironment *env, v3f pos): ActiveObject(0), @@ -67,10 +67,31 @@ void ServerActiveObject::registerType(u16 type, Factory f) m_types.insert(type, f); } -void ServerActiveObject::getWieldDiggingProperties(ToolDiggingProperties *dst) +ItemStack ServerActiveObject::getWieldedItem() const { - *dst = ToolDiggingProperties(); + const Inventory *inv = getInventory(); + if(inv) + { + const InventoryList *list = inv->getList(getWieldList()); + if(list) + return list->getItem(getWieldIndex()); + } + return ItemStack(); } - +bool ServerActiveObject::setWieldedItem(const ItemStack &item) +{ + Inventory *inv = getInventory(); + if(inv) + { + InventoryList *list = inv->getList(getWieldList()); + if (list) + { + list->changeItem(getWieldIndex(), item); + setInventoryModified(); + return true; + } + } + return false; +} diff --git a/src/serverobject.h b/src/serverobject.h index fd8a51a9e..94ceb4895 100644 --- a/src/serverobject.h +++ b/src/serverobject.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include "activeobject.h" #include "utility.h" +#include "inventorymanager.h" /* @@ -41,7 +42,7 @@ Some planning */ class ServerEnvironment; -class InventoryItem; +class ItemStack; class Player; struct ToolDiggingProperties; @@ -138,19 +139,27 @@ public: {} virtual void rightClick(ServerActiveObject *clicker) {} - virtual void getWieldDiggingProperties(ToolDiggingProperties *dst); - virtual void damageWieldedItem(u16 amount) - {} - // If all fits, eats item and returns true. Otherwise returns false. - virtual bool addToInventory(InventoryItem *item) - { return false; } - virtual void addToInventoryLater(InventoryItem *item) - {} virtual void setHP(s16 hp) {} virtual s16 getHP() { return 0; } + // Inventory and wielded item + virtual Inventory* getInventory() + { return NULL; } + virtual const Inventory* getInventory() const + { return NULL; } + virtual InventoryLocation getInventoryLocation() const + { return InventoryLocation(); } + virtual void setInventoryModified() + {} + virtual std::string getWieldList() const + { return ""; } + virtual int getWieldIndex() const + { return 0; } + virtual ItemStack getWieldedItem() const; + virtual bool setWieldedItem(const ItemStack &item); + /* Number of players which know about this object. Object won't be deleted until this is 0 to keep the id preserved for the right diff --git a/src/serverremoteplayer.cpp b/src/serverremoteplayer.cpp index 1681900e0..db4b44707 100644 --- a/src/serverremoteplayer.cpp +++ b/src/serverremoteplayer.cpp @@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "log.h" #include "gamedef.h" -#include "tooldef.h" +#include "inventory.h" #include "environment.h" #include "materials.h" @@ -31,6 +31,7 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env): ServerActiveObject(env, v3f(0,0,0)), m_last_good_position(0,0,0), m_last_good_position_age(0), + m_wield_index(0), m_inventory_not_sent(false), m_hp_not_sent(false), m_respawn_active(false), @@ -57,7 +58,6 @@ ServerRemotePlayer::ServerRemotePlayer(ServerEnvironment *env, v3f pos_, u16 pee } ServerRemotePlayer::~ServerRemotePlayer() { - clearAddToInventoryLater(); } void ServerRemotePlayer::setPosition(const v3f &position) @@ -67,12 +67,41 @@ void ServerRemotePlayer::setPosition(const v3f &position) m_position_not_sent = true; } -InventoryItem* ServerRemotePlayer::getWieldedItem() +Inventory* ServerRemotePlayer::getInventory() { - InventoryList *list = inventory.getList("main"); - if (list) - return list->getItem(m_selected_item); - return NULL; + return &inventory; +} + +const Inventory* ServerRemotePlayer::getInventory() const +{ + return &inventory; +} + +InventoryLocation ServerRemotePlayer::getInventoryLocation() const +{ + InventoryLocation loc; + loc.setPlayer(getName()); + return loc; +} + +void ServerRemotePlayer::setInventoryModified() +{ + m_inventory_not_sent = true; +} + +std::string ServerRemotePlayer::getWieldList() const +{ + return "main"; +} + +int ServerRemotePlayer::getWieldIndex() const +{ + return m_wield_index; +} + +void ServerRemotePlayer::setWieldIndex(int i) +{ + m_wield_index = i; } /* ServerActiveObject interface */ @@ -156,8 +185,10 @@ void ServerRemotePlayer::punch(ServerActiveObject *puncher, mp.crackiness = -0.5; mp.cuttability = 0.5; - ToolDiggingProperties tp; - puncher->getWieldDiggingProperties(&tp); + IItemDefManager *idef = m_env->getGameDef()->idef(); + ItemStack punchitem = puncher->getWieldedItem(); + ToolDiggingProperties tp = + punchitem.getToolDiggingProperties(idef); HittingProperties hitprop = getHittingProperties(&mp, &tp, time_from_last_punch); @@ -167,7 +198,8 @@ void ServerRemotePlayer::punch(ServerActiveObject *puncher, <<" HP"<damageWieldedItem(hitprop.wear); + punchitem.addWear(hitprop.wear, idef); + puncher->setWieldedItem(punchitem); if(hitprop.hp != 0) { @@ -201,109 +233,6 @@ void ServerRemotePlayer::moveTo(v3f pos, bool continuous) m_last_good_position_age = 0; } -void ServerRemotePlayer::getWieldDiggingProperties(ToolDiggingProperties *dst) -{ - IGameDef *gamedef = m_env->getGameDef(); - IToolDefManager *tdef = gamedef->tdef(); - - InventoryItem *item = getWieldedItem(); - if(item == NULL || std::string(item->getName()) != "ToolItem"){ - *dst = ToolDiggingProperties(); - return; - } - ToolItem *titem = (ToolItem*)item; - *dst = tdef->getDiggingProperties(titem->getToolName()); -} - -void ServerRemotePlayer::damageWieldedItem(u16 amount) -{ - infostream<<"Damaging "<getItem(m_selected_item); - if(item && (std::string)item->getName() == "ToolItem"){ - ToolItem *titem = (ToolItem*)item; - bool weared_out = titem->addWear(amount); - if(weared_out) - list->deleteItem(m_selected_item); - } -} -bool ServerRemotePlayer::addToInventory(InventoryItem *item) -{ - infostream<<"Adding "<getName()<<" into "<getBool("creative_mode")){ - return false; - } - - // Skip if inventory has no free space - if(ilist->roomForItem(item) == false) - { - infostream<<"Player inventory has no free space"<addItem(item); - assert(!leftover); - - m_inventory_not_sent = true; - - return true; -} -void ServerRemotePlayer::addToInventoryLater(InventoryItem *item) -{ - infostream<<"Adding (later) "<getName()<<" into "<::iterator - i = m_additional_items.begin(); - i != m_additional_items.end(); i++) - { - delete *i; - } - m_additional_items.clear(); -} -void ServerRemotePlayer::completeAddToInventoryLater(u16 preferred_index) -{ - InventoryList *ilist = inventory.getList("main"); - if(ilist == NULL) - { - clearAddToInventoryLater(); - return; - } - - // In creative mode, just delete the items - if(g_settings->getBool("creative_mode")) - { - clearAddToInventoryLater(); - return; - } - - for (std::vector::iterator - i = m_additional_items.begin(); - i != m_additional_items.end(); i++) - { - InventoryItem *item = *i; - InventoryItem *leftover = item; - leftover = ilist->addItem(preferred_index, leftover); - leftover = ilist->addItem(leftover); - delete leftover; - } - m_additional_items.clear(); - m_inventory_not_sent = true; -} void ServerRemotePlayer::setHP(s16 hp_) { s16 oldhp = hp; diff --git a/src/serverremoteplayer.h b/src/serverremoteplayer.h index bdc3bba20..9d9437646 100644 --- a/src/serverremoteplayer.h +++ b/src/serverremoteplayer.h @@ -46,9 +46,6 @@ public: virtual void setPosition(const v3f &position); - // Returns a reference - virtual InventoryItem* getWieldedItem(); - /* ServerActiveObject interface */ u8 getType() const @@ -77,19 +74,20 @@ public: virtual std::string getDescription() {return std::string("player ")+getName();} - virtual void getWieldDiggingProperties(ToolDiggingProperties *dst); - virtual void damageWieldedItem(u16 amount); - // If all fits, eats item and returns true. Otherwise returns false. - virtual bool addToInventory(InventoryItem *item); - virtual void addToInventoryLater(InventoryItem *item); - void clearAddToInventoryLater(); - void completeAddToInventoryLater(u16 preferred_index); + virtual Inventory* getInventory(); + virtual const Inventory* getInventory() const; + virtual InventoryLocation getInventoryLocation() const; + virtual void setInventoryModified(); + virtual std::string getWieldList() const; + virtual int getWieldIndex() const; + virtual void setWieldIndex(int i); + virtual void setHP(s16 hp_); virtual s16 getHP(); v3f m_last_good_position; float m_last_good_position_age; - std::vector m_additional_items; + int m_wield_index; bool m_inventory_not_sent; bool m_hp_not_sent; bool m_respawn_active; diff --git a/src/test.cpp b/src/test.cpp index f778f2d91..caf31d1af 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -47,6 +47,75 @@ with this program; if not, write to the Free Software Foundation, Inc., assert(exception_thrown);\ } +/* + A few item and node definitions for those tests that need them +*/ + +#define CONTENT_STONE 0 +#define CONTENT_GRASS 0x800 + +void define_some_nodes(IWritableItemDefManager *idef, IWritableNodeDefManager *ndef) +{ + content_t i; + ItemDefinition itemdef; + ContentFeatures f; + + /* + Stone + */ + i = CONTENT_STONE; + itemdef = ItemDefinition(); + itemdef.type = ITEM_NODE; + itemdef.name = "default:stone"; + itemdef.description = "Stone"; + itemdef.inventory_image = "[inventorycube" + "{default_stone.png" + "{default_stone.png" + "{default_stone.png"; + f = ContentFeatures(); + f.name = itemdef.name; + for(int i = 0; i < 6; i++) + f.tname_tiles[i] = "default_stone.png"; + f.param_type = CPT_MINERAL; + f.is_ground_content = true; + f.dug_item = itemdef.name; + f.material.diggability = DIGGABLE_NORMAL; + f.material.weight = 5.0; + f.material.crackiness = 1.0; + f.material.crumbliness = -0.1; + f.material.cuttability = -0.2; + idef->registerItem(itemdef); + ndef->set(i, f); + + /* + Grass + */ + i = CONTENT_GRASS; + itemdef = ItemDefinition(); + itemdef.type = ITEM_NODE; + itemdef.name = "default:dirt_with_grass"; + itemdef.description = "Dirt with grass"; + itemdef.inventory_image = "[inventorycube" + "{default_grass.png" + "{default_dirt.png&default_grass_side.png" + "{default_dirt.png&default_grass_side.png"; + f = ContentFeatures(); + f.name = itemdef.name; + f.tname_tiles[0] = "default_grass.png"; + f.tname_tiles[1] = "default_dirt.png"; + for(int i = 2; i < 6; i++) + f.tname_tiles[i] = "default_dirt.png^default_grass_side.png"; + f.is_ground_content = true; + f.dug_item = itemdef.name; + f.material.diggability = DIGGABLE_NORMAL; + f.material.weight = 1.2; + f.material.crackiness = 0.0; + f.material.crumbliness = 1.2; + f.material.cuttability = -0.4; + idef->registerItem(itemdef); + ndef->set(i, f); +} + struct TestUtilities { void Run() @@ -96,7 +165,118 @@ struct TestSettings assert(fabs(s.getV3F("coord2").Z - 3.3) < 0.001); } }; + +struct TestSerialization +{ + // To be used like this: + // mkstr("Some\0string\0with\0embedded\0nuls") + // since std::string("...") doesn't work as expected in that case. + template std::string mkstr(const char (&s)[N]) + { + return std::string(s, N - 1); + } + + void Run() + { + // Tests some serialization primitives + + assert(serializeString("") == mkstr("\0\0")); + assert(serializeWideString(L"") == mkstr("\0\0")); + assert(serializeLongString("") == mkstr("\0\0\0\0")); + assert(serializeJsonString("") == "\"\""); + std::string teststring = "Hello world!"; + assert(serializeString(teststring) == + mkstr("\0\14Hello world!")); + assert(serializeWideString(narrow_to_wide(teststring)) == + mkstr("\0\14\0H\0e\0l\0l\0o\0 \0w\0o\0r\0l\0d\0!")); + assert(serializeLongString(teststring) == + mkstr("\0\0\0\14Hello world!")); + assert(serializeJsonString(teststring) == + "\"Hello world!\""); + + std::string teststring2; + std::wstring teststring2_w; + std::string teststring2_w_encoded; + { + std::ostringstream tmp_os; + std::wostringstream tmp_os_w; + std::ostringstream tmp_os_w_encoded; + for(int i = 0; i < 256; i++) + { + tmp_os<<(char)i; + tmp_os_w<<(wchar_t)i; + tmp_os_w_encoded<<(char)0<<(char)i; + } + teststring2 = tmp_os.str(); + teststring2_w = tmp_os_w.str(); + teststring2_w_encoded = tmp_os_w_encoded.str(); + } + assert(serializeString(teststring2) == + mkstr("\1\0") + teststring2); + assert(serializeWideString(teststring2_w) == + mkstr("\1\0") + teststring2_w_encoded); + assert(serializeLongString(teststring2) == + mkstr("\0\0\1\0") + teststring2); + assert(serializeJsonString(teststring2) == + mkstr("\"") + + "\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" + + "\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f" + + "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" + + "\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f" + + " !\\\"" + teststring2.substr(0x23, 0x2f-0x23) + + "\\/" + teststring2.substr(0x30, 0x5c-0x30) + + "\\\\" + teststring2.substr(0x5d, 0x7f-0x5d) + "\\u007f" + + "\\u0080\\u0081\\u0082\\u0083\\u0084\\u0085\\u0086\\u0087" + + "\\u0088\\u0089\\u008a\\u008b\\u008c\\u008d\\u008e\\u008f" + + "\\u0090\\u0091\\u0092\\u0093\\u0094\\u0095\\u0096\\u0097" + + "\\u0098\\u0099\\u009a\\u009b\\u009c\\u009d\\u009e\\u009f" + + "\\u00a0\\u00a1\\u00a2\\u00a3\\u00a4\\u00a5\\u00a6\\u00a7" + + "\\u00a8\\u00a9\\u00aa\\u00ab\\u00ac\\u00ad\\u00ae\\u00af" + + "\\u00b0\\u00b1\\u00b2\\u00b3\\u00b4\\u00b5\\u00b6\\u00b7" + + "\\u00b8\\u00b9\\u00ba\\u00bb\\u00bc\\u00bd\\u00be\\u00bf" + + "\\u00c0\\u00c1\\u00c2\\u00c3\\u00c4\\u00c5\\u00c6\\u00c7" + + "\\u00c8\\u00c9\\u00ca\\u00cb\\u00cc\\u00cd\\u00ce\\u00cf" + + "\\u00d0\\u00d1\\u00d2\\u00d3\\u00d4\\u00d5\\u00d6\\u00d7" + + "\\u00d8\\u00d9\\u00da\\u00db\\u00dc\\u00dd\\u00de\\u00df" + + "\\u00e0\\u00e1\\u00e2\\u00e3\\u00e4\\u00e5\\u00e6\\u00e7" + + "\\u00e8\\u00e9\\u00ea\\u00eb\\u00ec\\u00ed\\u00ee\\u00ef" + + "\\u00f0\\u00f1\\u00f2\\u00f3\\u00f4\\u00f5\\u00f6\\u00f7" + + "\\u00f8\\u00f9\\u00fa\\u00fb\\u00fc\\u00fd\\u00fe\\u00ff" + + "\""); + + { + std::istringstream is(serializeString(teststring2), std::ios::binary); + assert(deSerializeString(is) == teststring2); + assert(!is.eof()); + is.get(); + assert(is.eof()); + } + { + std::istringstream is(serializeWideString(teststring2_w), std::ios::binary); + assert(deSerializeWideString(is) == teststring2_w); + assert(!is.eof()); + is.get(); + assert(is.eof()); + } + { + std::istringstream is(serializeLongString(teststring2), std::ios::binary); + assert(deSerializeLongString(is) == teststring2); + assert(!is.eof()); + is.get(); + assert(is.eof()); + } + { + std::istringstream is(serializeJsonString(teststring2), std::ios::binary); + //dstream<get(j); - for(std::set::const_iterator - i = f.used_texturenames.begin(); - i != f.used_texturenames.end(); i++) + for(u32 i=0; i<6; i++) { - std::string name = *i; + std::string name = f.tname_tiles[i]; sourcelist[name] = true; - if(f.often_contains_mineral){ + if(f.param_type == CPT_MINERAL){ for(int k=1; kqueryFeature(video::EVDF_RENDER_TO_TARGET) == false) - { - errorstream<<"generate_image(): EVDF_RENDER_TO_TARGET" - " not supported. Creating fallback image"<addRenderTargetTexture(dim, rtt_name.c_str(), - video::ECF_A8R8G8B8); - assert(rtt); - - // Set render target - driver->setRenderTarget(rtt, true, true, - video::SColor(0,0,0,0)); - - // Get a scene manager - scene::ISceneManager *smgr_main = device->getSceneManager(); - assert(smgr_main); - scene::ISceneManager *smgr = smgr_main->createNewSceneManager(); - assert(smgr); - /* - Create scene: - - An unit cube is centered at 0,0,0 - - Camera looks at cube from Y+, Z- towards Y-, Z+ + Draw a cube mesh into a render target texture */ - scene::IMesh* cube = createCubeMesh(v3f(1, 1, 1)); setMeshColor(cube, video::SColor(255, 255, 255, 255)); - - scene::IMeshSceneNode* cubenode = smgr->addMeshSceneNode(cube, NULL, -1, v3f(0,0,0), v3f(0,45,0), v3f(1,1,1), true); - cube->drop(); - - // Set texture of cube - cubenode->getMaterial(0).setTexture(0, texture_top); - cubenode->getMaterial(1).setTexture(0, texture_top); - cubenode->getMaterial(2).setTexture(0, texture_right); - cubenode->getMaterial(3).setTexture(0, texture_right); - cubenode->getMaterial(4).setTexture(0, texture_left); - cubenode->getMaterial(5).setTexture(0, texture_left); - cubenode->setMaterialFlag(video::EMF_LIGHTING, true); - cubenode->setMaterialFlag(video::EMF_ANTI_ALIASING, true); - cubenode->setMaterialFlag(video::EMF_BILINEAR_FILTER, true); - - scene::ICameraSceneNode* camera = smgr->addCameraSceneNode(0, - v3f(0, 1.0, -1.5), v3f(0, 0, 0)); + cube->getMeshBuffer(0)->getMaterial().setTexture(0, texture_top); + cube->getMeshBuffer(1)->getMaterial().setTexture(0, texture_top); + cube->getMeshBuffer(2)->getMaterial().setTexture(0, texture_right); + cube->getMeshBuffer(3)->getMaterial().setTexture(0, texture_right); + cube->getMeshBuffer(4)->getMaterial().setTexture(0, texture_left); + cube->getMeshBuffer(5)->getMaterial().setTexture(0, texture_left); + + core::dimension2d dim(64,64); + std::string rtt_texture_name = part_of_name + "_RTT"; + + v3f camera_position(0, 1.0, -1.5); + camera_position.rotateXZBy(45); + v3f camera_lookat(0, 0, 0); + core::CMatrix4 camera_projection_matrix; // Set orthogonal projection - core::CMatrix4 pm; - pm.buildProjectionMatrixOrthoLH(1.65, 1.65, 0, 100); - camera->setProjectionMatrix(pm, true); - - /*scene::ILightSceneNode *light =*/ smgr->addLightSceneNode(0, - v3f(-50, 100, -75), video::SColorf(0.5,0.5,0.5), 1000); - - smgr->setAmbientLight(video::SColorf(0.2,0.2,0.2)); - - // Render scene - driver->beginScene(true, true, video::SColor(0,0,0,0)); - smgr->drawAll(); - driver->endScene(); + camera_projection_matrix.buildProjectionMatrixOrthoLH( + 1.65, 1.65, 0, 100); + + video::SColorf ambient_light(0.2,0.2,0.2); + v3f light_position(10, 100, -50); + video::SColorf light_color(0.5,0.5,0.5); + f32 light_radius = 1000; + + video::ITexture *rtt = generateTextureFromMesh( + cube, device, dim, rtt_texture_name, + camera_position, + camera_lookat, + camera_projection_matrix, + ambient_light, + light_position, + light_color, + light_radius); - // NOTE: The scene nodes should not be dropped, otherwise - // smgr->drop() segfaults - /*cube->drop(); - camera->drop(); - light->drop();*/ - // Drop scene manager - smgr->drop(); - - // Unset render target - driver->setRenderTarget(0, true, true, 0); + // Drop mesh + cube->drop(); // Free textures of images driver->removeTexture(texture_top); driver->removeTexture(texture_left); driver->removeTexture(texture_right); + if(rtt == NULL) + { + errorstream<<"generate_image(): render to texture failed." + " Creating fallback image"<createImage(rtt, v2s32(0,0), dim); - assert(image); - + baseimg = driver->createImage(video::ECF_A8R8G8B8, dim); if(image) @@ -1567,7 +1516,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, image->copyTo(baseimg); image->drop(); } -#endif } else { @@ -1579,38 +1527,6 @@ bool generate_image(std::string part_of_name, video::IImage *& baseimg, return true; } -void make_progressbar(float value, video::IImage *image) -{ - if(image == NULL) - return; - - core::dimension2d size = image->getDimension(); - - u32 barheight = size.Height/16; - u32 barpad_x = size.Width/16; - u32 barpad_y = size.Height/16; - u32 barwidth = size.Width - barpad_x*2; - v2u32 barpos(barpad_x, size.Height - barheight - barpad_y); - - u32 barvalue_i = (u32)(((float)barwidth * value) + 0.5); - - video::SColor active(255,255,0,0); - video::SColor inactive(255,0,0,0); - for(u32 x0=0; x0setPixel(x,y, *c); - } - } -} - void brighten(video::IImage *image) { if(image == NULL) diff --git a/src/tile.h b/src/tile.h index 23849ca1f..c0d8914b0 100644 --- a/src/tile.h +++ b/src/tile.h @@ -110,6 +110,8 @@ public: {return AtlasPointer(0);} virtual video::ITexture* getTextureRaw(const std::string &name) {return NULL;} + virtual IrrlichtDevice* getDevice() + {return NULL;} virtual void updateAP(AtlasPointer &ap){}; }; @@ -126,6 +128,8 @@ public: {return AtlasPointer(0);} virtual video::ITexture* getTextureRaw(const std::string &name) {return NULL;} + virtual IrrlichtDevice* getDevice() + {return NULL;} virtual void updateAP(AtlasPointer &ap){}; virtual void processQueue()=0; diff --git a/src/tooldef.cpp b/src/tooldef.cpp deleted file mode 100644 index 7d7eceab0..000000000 --- a/src/tooldef.cpp +++ /dev/null @@ -1,238 +0,0 @@ -/* -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 "tooldef.h" -#include "irrlichttypes.h" -#include "log.h" -#include -#include "utility.h" -#include - -ToolDiggingProperties::ToolDiggingProperties(float full_punch_interval_, - float a, float b, float c, float d, float e, - float f, float g, float h, float i, float j): - full_punch_interval(full_punch_interval_), - basetime(a), - dt_weight(b), - dt_crackiness(c), - dt_crumbliness(d), - dt_cuttability(e), - basedurability(f), - dd_weight(g), - dd_crackiness(h), - dd_crumbliness(i), - dd_cuttability(j) -{} - -std::string ToolDefinition::dump() -{ - std::ostringstream os(std::ios::binary); - os<<"[ToolDefinition::dump() not implemented due to lazyness]" - <::Node *n; - n = m_tool_definitions.find(toolname); - if(n == NULL) - return NULL; - return n->getValue(); - } - virtual std::string getImagename(const std::string &toolname) const - { - const ToolDefinition *def = getToolDefinition(toolname); - if(def == NULL) - return ""; - return def->imagename; - } - virtual ToolDiggingProperties getDiggingProperties( - const std::string &toolname) const - { - const ToolDefinition *def = getToolDefinition(toolname); - // If tool does not exist, just return an impossible - if(def == NULL){ - // If tool does not exist, try empty name - const ToolDefinition *def = getToolDefinition(""); - if(def == NULL) // If that doesn't exist either, return default - return ToolDiggingProperties(); - return def->properties; - } - return def->properties; - } - virtual std::string getAlias(const std::string &name) const - { - std::map::const_iterator i; - i = m_aliases.find(name); - if(i != m_aliases.end()) - return i->second; - return name; - } - // IWritableToolDefManager - virtual bool registerTool(std::string toolname, const ToolDefinition &def) - { - infostream<<"registerTool: registering tool \""<::Iterator - i = m_tool_definitions.getIterator(); - i.atEnd() == false; i++){ - delete i.getNode()->getValue(); - } - m_tool_definitions.clear(); - m_aliases.clear(); - } - virtual void setAlias(const std::string &name, - const std::string &convert_to) - { - if(getToolDefinition(name) != NULL){ - infostream<<"tdef: not setting alias "< "< "<::Iterator - i = m_tool_definitions.getIterator(); - i.atEnd() == false; i++){ - std::string name = i.getNode()->getKey(); - ToolDefinition *def = i.getNode()->getValue(); - // Serialize name - os<serialize(tmp_os); - os<::const_iterator - i = m_aliases.begin(); i != m_aliases.end(); i++) - { - os<first); - os<second); - } - } - virtual void deSerialize(std::istream &is) - { - // Clear everything - clear(); - // Deserialize - int version = readU8(is); - if(version != 0) throw SerializationError( - "unsupported ToolDefManager version"); - u16 count = readU16(is); - for(u16 i=0; i m_tool_definitions; - // Aliases - std::map m_aliases; -}; - -IWritableToolDefManager* createToolDefManager() -{ - return new CToolDefManager(); -} - diff --git a/src/tooldef.h b/src/tooldef.h deleted file mode 100644 index 68a894898..000000000 --- a/src/tooldef.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -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 TOOLDEF_HEADER -#define TOOLDEF_HEADER - -#include -#include - -struct ToolDiggingProperties -{ - // time = basetime + sum(feature here * feature in MaterialProperties) - float full_punch_interval; - float basetime; - float dt_weight; - float dt_crackiness; - float dt_crumbliness; - float dt_cuttability; - float basedurability; - float dd_weight; - float dd_crackiness; - float dd_crumbliness; - float dd_cuttability; - - ToolDiggingProperties(float full_punch_interval_=2.0, - float a=0.75, float b=0, float c=0, float d=0, float e=0, - float f=50, float g=0, float h=0, float i=0, float j=0); -}; - -struct ToolDefinition -{ - std::string imagename; - ToolDiggingProperties properties; - - ToolDefinition(){} - ToolDefinition(const std::string &imagename_, - ToolDiggingProperties properties_): - imagename(imagename_), - properties(properties_) - {} - - std::string dump(); - void serialize(std::ostream &os); - void deSerialize(std::istream &is); -}; - -class IToolDefManager -{ -public: - IToolDefManager(){} - virtual ~IToolDefManager(){} - virtual const ToolDefinition* getToolDefinition(const std::string &toolname) const=0; - virtual std::string getImagename(const std::string &toolname) const =0; - virtual ToolDiggingProperties getDiggingProperties( - const std::string &toolname) const =0; - virtual std::string getAlias(const std::string &name) const =0; - - virtual void serialize(std::ostream &os)=0; -}; - -class IWritableToolDefManager : public IToolDefManager -{ -public: - IWritableToolDefManager(){} - virtual ~IWritableToolDefManager(){} - virtual const ToolDefinition* getToolDefinition(const std::string &toolname) const=0; - virtual std::string getImagename(const std::string &toolname) const =0; - virtual ToolDiggingProperties getDiggingProperties( - const std::string &toolname) const =0; - virtual std::string getAlias(const std::string &name) const =0; - - virtual bool registerTool(std::string toolname, const ToolDefinition &def)=0; - virtual void clear()=0; - // Set an alias so that entries named will load as . - // Alias is not set if has already been defined. - // Alias will be removed if is defined at a later point of time. - virtual void setAlias(const std::string &name, - const std::string &convert_to)=0; - - virtual void serialize(std::ostream &os)=0; - virtual void deSerialize(std::istream &is)=0; -}; - -IWritableToolDefManager* createToolDefManager(); - -#endif - diff --git a/src/utility.cpp b/src/utility.cpp index 4e9f307d8..06b60884f 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "sha1.h" #include "base64.h" #include "log.h" +#include TimeTaker::TimeTaker(const char *name, u32 *result) { @@ -234,6 +235,100 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, return true; } +// Creates a string encoded in JSON format (almost equivalent to a C string literal) +std::string serializeJsonString(const std::string &plain) +{ + std::ostringstream os(std::ios::binary); + os<<"\""; + for(size_t i = 0; i < plain.size(); i++) + { + char c = plain[i]; + switch(c) + { + case '"': os<<"\\\""; break; + case '\\': os<<"\\\\"; break; + case '/': os<<"\\/"; break; + case '\b': os<<"\\b"; break; + case '\f': os<<"\\f"; break; + case '\n': os<<"\\n"; break; + case '\r': os<<"\\r"; break; + case '\t': os<<"\\t"; break; + default: + { + if(c >= 32 && c <= 126) + { + os<> c; + if(c != '"') + throw SerializationError("JSON string must start with doublequote"); + + // Parse characters + for(;;) + { + c = is.get(); + if(is.eof()) + throw SerializationError("JSON string ended prematurely"); + if(c == '"') + { + return os.str(); + } + else if(c == '\\') + { + c2 = is.get(); + if(is.eof()) + throw SerializationError("JSON string ended prematurely"); + switch(c2) + { + default: os<> std::hex >> hexnumber; + os<<((char)hexnumber); + break; + } + } + } + else + { + os<