diff --git a/builtin/game/item_entity.lua b/builtin/game/item_entity.lua index be158c119..7b8247116 100644 --- a/builtin/game/item_entity.lua +++ b/builtin/game/item_entity.lua @@ -53,6 +53,8 @@ core.register_entity(":__builtin:item", { if itemtable then itemname = stack:to_table().name end + -- Backwards compatibility: old clients use the texture + -- to get the type of the item local item_texture = nil local item_type = "" if core.registered_items[itemname] then @@ -66,6 +68,7 @@ core.register_entity(":__builtin:item", { visual_size = {x = s, y = s}, collisionbox = {-c, -c, -c, c, c, c}, automatic_rotate = math.pi * 0.5, + wield_item = itemstring, } self.object:set_properties(prop) end, @@ -101,31 +104,39 @@ core.register_entity(":__builtin:item", { self:set_item(self.itemstring) end, + -- moves items from this stack to an other stack try_merge_with = function(self, own_stack, object, obj) + -- other item's stack local stack = ItemStack(obj.itemstring) - if own_stack:get_name() == stack:get_name() and stack:get_free_space() > 0 then + -- only merge if items are the same + if own_stack:get_name() == stack:get_name() and + own_stack:get_meta() == stack:get_meta() and + own_stack:get_wear() == stack:get_wear() and + stack:get_free_space() > 0 then local overflow = false local count = stack:get_count() + own_stack:get_count() local max_count = stack:get_stack_max() if count > max_count then overflow = true + stack:set_count(max_count) count = count - max_count + own_stack:set_count(count) else self.itemstring = '' + stack:set_count(count) end local pos = object:getpos() pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15 object:moveto(pos, false) local s, c - local max_count = stack:get_stack_max() - local name = stack:get_name() if not overflow then - obj.itemstring = name .. " " .. count + obj.itemstring = stack:to_string() s = 0.2 + 0.1 * (count / max_count) c = s object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = obj.itemstring }) self.object:remove() -- merging succeeded @@ -133,18 +144,20 @@ core.register_entity(":__builtin:item", { else s = 0.4 c = 0.3 + obj.itemstring = stack:to_string() object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = obj.itemstring }) - obj.itemstring = name .. " " .. max_count s = 0.2 + 0.1 * (count / max_count) c = s + self.itemstring = own_stack:to_string() self.object:set_properties({ visual_size = {x = s, y = s}, - collisionbox = {-c, -c, -c, c, c, c} + collisionbox = {-c, -c, -c, c, c, c}, + wield_item = self.itemstring }) - self.itemstring = name .. " " .. count end end -- merging didn't succeed diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ca1b5d14c..721f5448a 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -1480,6 +1480,9 @@ Item metadata only contains a key-value store. Some of the values in the key-value store are handled specially: * `description`: Set the itemstack's description. Defaults to idef.description +* `color`: A `ColorString`, which sets the stack's color. +* `palette_index`: If the item has a palette, this is used to get the + current color from the palette. Example stuff: @@ -2855,6 +2858,8 @@ See `StorageRef`, `NodeMetaRef` and `ItemStackMetaRef`. * Any non-table value will clear the metadata * See "Node Metadata" for an example * returns `true` on success +* `equals(other)` + * returns `true` if this metadata has the same key-value pairs as `other` ### `NodeMetaRef` Node metadata: reference extra data and functionality stored in a node. @@ -3735,6 +3740,19 @@ Definition tables {hard = 1, metal = 1, spikes = 1} inventory_image = "default_tool_steelaxe.png", wield_image = "", + palette = "", + --[[ + ^ An image file containing the palette of a node. + ^ You can set the currently used color as the + ^ "palette_index" field of the item stack metadata. + ^ The palette is always stretched to fit indices + ^ between 0 and 255, to ensure compatibility with + ^ "colorfacedir" and "colorwallmounted" nodes. + ]] + color = "0xFFFFFFFF", + --[[ + ^ The color of the item. The palette overrides this. + ]] wield_scale = {x = 1, y = 1, z = 1}, stack_max = 99, range = 4.0, diff --git a/src/camera.cpp b/src/camera.cpp index d5c337fe8..7e83dadeb 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -501,7 +501,8 @@ void Camera::setDigging(s32 button) void Camera::wield(const ItemStack &item) { - if (item.name != m_wield_item_next.name) { + if (item.name != m_wield_item_next.name || + item.metadata != m_wield_item_next.metadata) { m_wield_item_next = item; if (m_wield_change_timer > 0) m_wield_change_timer = -m_wield_change_timer; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 86ca7d422..0aa06980c 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -341,6 +341,8 @@ public: */ video::ITexture* getTextureForMesh(const std::string &name, u32 *id); + virtual Palette* getPalette(const std::string &name); + // Returns a pointer to the irrlicht device virtual IrrlichtDevice* getDevice() { @@ -377,8 +379,6 @@ public: video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms); - video::IImage* generateImage(const std::string &name); - video::ITexture* getNormalTexture(const std::string &name); video::SColor getTextureAverageColor(const std::string &name); video::ITexture *getShaderFlagsTexture(bool normamap_present); @@ -401,6 +401,13 @@ private: // if baseimg is NULL, it is created. Otherwise stuff is made on it. bool generateImagePart(std::string part_of_name, video::IImage *& baseimg); + /*! Generates an image from a full string like + * "stone.png^mineral_coal.png^[crack:1:0". + * Shall be called from the main thread. + * The returned Image should be dropped. + */ + video::IImage* generateImage(const std::string &name); + // Thread-safe cache of what source images are known (true = known) MutexedMap m_source_image_existence; @@ -419,6 +426,9 @@ private: // but can't be deleted because the ITexture* might still be used std::vector m_texture_trash; + // Maps image file names to loaded palettes. + UNORDERED_MAP m_palettes; + // Cached settings needed for making textures from meshes bool m_setting_trilinear_filter; bool m_setting_bilinear_filter; @@ -682,6 +692,61 @@ video::ITexture* TextureSource::getTextureForMesh(const std::string &name, u32 * return getTexture(name + "^[applyfiltersformesh", id); } +Palette* TextureSource::getPalette(const std::string &name) +{ + // Only the main thread may load images + sanity_check(thr_is_current_thread(m_main_thread)); + + if (name == "") + return NULL; + + UNORDERED_MAP::iterator it = m_palettes.find(name); + if (it == m_palettes.end()) { + // Create palette + video::IImage *img = generateImage(name); + if (!img) { + warningstream << "TextureSource::getPalette(): palette \"" << name + << "\" could not be loaded." << std::endl; + return NULL; + } + Palette new_palette; + u32 w = img->getDimension().Width; + u32 h = img->getDimension().Height; + // Real area of the image + u32 area = h * w; + if (area == 0) + return NULL; + if (area > 256) { + warningstream << "TextureSource::getPalette(): the specified" + << " palette image \"" << name << "\" is larger than 256" + << " pixels, using the first 256." << std::endl; + area = 256; + } else if (256 % area != 0) + warningstream << "TextureSource::getPalette(): the " + << "specified palette image \"" << name << "\" does not " + << "contain power of two pixels." << std::endl; + // We stretch the palette so it will fit 256 values + // This many param2 values will have the same color + u32 step = 256 / area; + // For each pixel in the image + for (u32 i = 0; i < area; i++) { + video::SColor c = img->getPixel(i % w, i / w); + // Fill in palette with 'step' colors + for (u32 j = 0; j < step; j++) + new_palette.push_back(c); + } + img->drop(); + // Fill in remaining elements + while (new_palette.size() < 256) + new_palette.push_back(video::SColor(0xFFFFFFFF)); + m_palettes[name] = new_palette; + it = m_palettes.find(name); + } + if (it != m_palettes.end()) + return &((*it).second); + return NULL; +} + void TextureSource::processQueue() { /* diff --git a/src/client/tile.h b/src/client/tile.h index d04ab918a..5eec0f2ea 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -33,6 +33,8 @@ class IGameDef; struct TileSpec; struct TileDef; +typedef std::vector Palette; + /* tile.{h,cpp}: Texture handling stuff. */ @@ -106,14 +108,15 @@ public: const std::string &name, u32 *id = NULL)=0; virtual video::ITexture* getTextureForMesh( const std::string &name, u32 *id = NULL) = 0; + /*! + * Returns a palette from the given texture name. + * The pointer is valid until the texture source is + * destructed. + * Should be called from the main thread. + */ + virtual Palette* getPalette(const std::string &name) = 0; virtual IrrlichtDevice* getDevice()=0; virtual bool isKnownSourceImage(const std::string &name)=0; - /*! Generates an image from a full string like - * "stone.png^mineral_coal.png^[crack:1:0". - * Shall be called from the main thread. - * The returned Image should be dropped. - */ - virtual video::IImage* generateImage(const std::string &name)=0; virtual video::ITexture* generateTextureFromMesh( const TextureFromMeshParams ¶ms)=0; virtual video::ITexture* getNormalTexture(const std::string &name)=0; diff --git a/src/content_cao.cpp b/src/content_cao.cpp index e0b1c4cd2..84f198b75 100644 --- a/src/content_cao.cpp +++ b/src/content_cao.cpp @@ -933,23 +933,30 @@ void GenericCAO::addToScene(scene::ISceneManager *smgr, errorstream<<"GenericCAO::addToScene(): Could not load mesh "<= 1){ - infostream<<"textures[0]: "<idef(); - ItemStack item(m_prop.textures[0], 1, 0, idef); - - m_wield_meshnode = new WieldMeshSceneNode( - smgr->getRootSceneNode(), smgr, -1); - m_wield_meshnode->setItem(item, m_client); - - m_wield_meshnode->setScale(v3f(m_prop.visual_size.X/2, - m_prop.visual_size.Y/2, - m_prop.visual_size.X/2)); - u8 li = m_last_light; - m_wield_meshnode->setColor(video::SColor(255,li,li,li)); + ItemStack item; + infostream << "GenericCAO::addToScene(): wielditem" << std::endl; + if (m_prop.wield_item == "") { + // Old format, only textures are specified. + infostream << "textures: " << m_prop.textures.size() << std::endl; + if (m_prop.textures.size() >= 1) { + infostream << "textures[0]: " << m_prop.textures[0] + << std::endl; + IItemDefManager *idef = m_client->idef(); + item = ItemStack(m_prop.textures[0], 1, 0, idef); + } + } else { + infostream << "serialized form: " << m_prop.wield_item << std::endl; + item.deSerialize(m_prop.wield_item, m_client->idef()); } + m_wield_meshnode = new WieldMeshSceneNode(smgr->getRootSceneNode(), + smgr, -1); + m_wield_meshnode->setItem(item, m_client); + + m_wield_meshnode->setScale( + v3f(m_prop.visual_size.X / 2, m_prop.visual_size.Y / 2, + m_prop.visual_size.X / 2)); + u8 li = m_last_light; + m_wield_meshnode->setColor(video::SColor(255, li, li, li)); } else { infostream<<"GenericCAO::addToScene(): \""< #ifdef HAVE_TOUCHSCREENGUI @@ -642,9 +643,10 @@ void drawItemStack(video::IVideoDriver *driver, } const ItemDefinition &def = item.getDefinition(client->idef()); - scene::IMesh* mesh = client->idef()->getWieldMesh(def.name, client); + ItemMesh *imesh = client->idef()->getWieldMesh(def.name, client); - if (mesh) { + if (imesh && imesh->mesh) { + scene::IMesh *mesh = imesh->mesh; driver->clearZBuffer(); s32 delta = 0; if (rotation_kind < IT_ROT_NONE) { @@ -667,16 +669,28 @@ void drawItemStack(video::IVideoDriver *driver, matrix.makeIdentity(); if (enable_animations) { - float timer_f = (float)delta / 5000.0; + float timer_f = (float) delta / 5000.0; matrix.setRotationDegrees(core::vector3df(0, 360 * timer_f, 0)); } driver->setTransform(video::ETS_WORLD, matrix); driver->setViewPort(rect); + video::SColor basecolor = + client->idef()->getItemstackColor(item, client); + u32 mc = mesh->getMeshBufferCount(); for (u32 j = 0; j < mc; ++j) { scene::IMeshBuffer *buf = mesh->getMeshBuffer(j); + // we can modify vertices relatively fast, + // because these meshes are not buffered. + assert(buf->getHardwareMappingHint_Vertex() == scene::EHM_NEVER); + video::SColor c = basecolor; + if (imesh->buffer_colors.size() > j) { + std::pair p = imesh->buffer_colors[j]; + c = p.first ? p.second : basecolor; + } + colorizeMeshBuffer(buf, &c); video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; material.Lighting = false; diff --git a/src/itemdef.cpp b/src/itemdef.cpp index 627ad1b6c..51d8f1d5d 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -82,6 +82,8 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def) sound_place = def.sound_place; sound_place_failed = def.sound_place_failed; range = def.range; + palette_image = def.palette_image; + color = def.color; return *this; } @@ -104,6 +106,8 @@ void ItemDefinition::reset() description = ""; inventory_image = ""; wield_image = ""; + palette_image = ""; + color = video::SColor(0xFFFFFFFF); wield_scale = v3f(1.0, 1.0, 1.0); stack_max = 99; usable = false; @@ -153,6 +157,8 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const writeF1000(os, range); os << serializeString(sound_place_failed.name); writeF1000(os, sound_place_failed.gain); + os << serializeString(palette_image); + writeU32(os, color.color); } void ItemDefinition::deSerialize(std::istream &is) @@ -209,6 +215,8 @@ void ItemDefinition::deSerialize(std::istream &is) try { sound_place_failed.name = deSerializeString(is); sound_place_failed.gain = readF1000(is); + palette_image = deSerializeString(is); + color.set(readU32(is)); } catch(SerializationError &e) {}; } @@ -224,11 +232,13 @@ class CItemDefManager: public IWritableItemDefManager struct ClientCached { video::ITexture *inventory_texture; - scene::IMesh *wield_mesh; + ItemMesh wield_mesh; + Palette *palette; ClientCached(): inventory_texture(NULL), - wield_mesh(NULL) + wield_mesh(), + palette(NULL) {} }; #endif @@ -250,8 +260,8 @@ public: i = values.begin(); i != values.end(); ++i) { ClientCached *cc = *i; - if (cc->wield_mesh) - cc->wield_mesh->drop(); + if (cc->wield_mesh.mesh) + cc->wield_mesh.mesh->drop(); delete cc; } @@ -335,8 +345,9 @@ public: ItemStack item = ItemStack(); item.name = def.name; - scene::IMesh *mesh = getItemMesh(client, item); - cc->wield_mesh = mesh; + getItemMesh(client, item, &(cc->wield_mesh)); + + cc->palette = tsrc->getPalette(def.palette_image); // Put in cache m_clientcached.set(name, cc); @@ -390,13 +401,41 @@ public: return cc->inventory_texture; } // Get item wield mesh - virtual scene::IMesh* getWieldMesh(const std::string &name, + virtual ItemMesh* getWieldMesh(const std::string &name, Client *client) const { ClientCached *cc = getClientCached(name, client); if(!cc) return NULL; - return cc->wield_mesh; + return &(cc->wield_mesh); + } + + // Get item palette + virtual Palette* getPalette(const std::string &name, + Client *client) const + { + ClientCached *cc = getClientCached(name, client); + if(!cc) + return NULL; + return cc->palette; + } + + virtual video::SColor getItemstackColor(const ItemStack &stack, + Client *client) const + { + // Look for direct color definition + const std::string &colorstring = stack.metadata.getString("color", 0); + video::SColor directcolor; + if ((colorstring != "") + && parseColorString(colorstring, directcolor, true)) + return directcolor; + // See if there is a palette + Palette *palette = getPalette(stack.name, client); + const std::string &index = stack.metadata.getString("palette_index", 0); + if ((palette != NULL) && (index != "")) + return (*palette)[mystoi(index, 0, 255)]; + // Fallback color + return get(stack.name).color; } #endif void clear() diff --git a/src/itemdef.h b/src/itemdef.h index 01ec4fa2f..2d7ff570d 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -30,6 +30,11 @@ with this program; if not, write to the Free Software Foundation, Inc., class IGameDef; class Client; struct ToolCapabilities; +#ifndef SERVER +#include "client/tile.h" +struct ItemMesh; +struct ItemStack; +#endif /* Base item definition @@ -57,6 +62,8 @@ struct ItemDefinition */ 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 + std::string palette_image; // If specified, the item will be colorized based on this + video::SColor color; // The fallback color of the node. v3f wield_scale; /* @@ -110,8 +117,15 @@ public: virtual video::ITexture* getInventoryTexture(const std::string &name, Client *client) const=0; // Get item wield mesh - virtual scene::IMesh* getWieldMesh(const std::string &name, + virtual ItemMesh* getWieldMesh(const std::string &name, Client *client) const=0; + // Get item palette + virtual Palette* getPalette(const std::string &name, + Client *client) const = 0; + // Returns the base color of an item stack: the color of all + // tiles that do not define their own color. + virtual video::SColor getItemstackColor(const ItemStack &stack, + Client *client) const = 0; #endif virtual void serialize(std::ostream &os, u16 protocol_version)=0; @@ -136,7 +150,7 @@ public: virtual video::ITexture* getInventoryTexture(const std::string &name, Client *client) const=0; // Get item wield mesh - virtual scene::IMesh* getWieldMesh(const std::string &name, + virtual ItemMesh* getWieldMesh(const std::string &name, Client *client) const=0; #endif diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 2745a45e8..558acafd6 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -786,6 +786,8 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc tiledef_special[j].backface_culling, material_type); } + palette = tsrc->getPalette(palette_name); + if ((drawtype == NDT_MESH) && (mesh != "")) { // Meshnode drawtype // Read the mesh and apply scale @@ -859,9 +861,6 @@ public: virtual void removeNode(const std::string &name); virtual void updateAliases(IItemDefManager *idef); virtual void applyTextureOverrides(const std::string &override_filepath); - //! Returns a palette or NULL if not found. Only on client. - std::vector *getPalette(const ContentFeatures &f, - const IGameDef *gamedef); virtual void updateTextures(IGameDef *gamedef, void (*progress_cbk)(void *progress_args, u32 progress, u32 max_progress), void *progress_cbk_args); @@ -910,9 +909,6 @@ private: // Next possibly free id content_t m_next_id; - // Maps image file names to loaded palettes. - UNORDERED_MAP > m_palettes; - // NodeResolvers to callback once node registration has ended std::vector m_pending_resolve_callbacks; @@ -1401,78 +1397,6 @@ void CNodeDefManager::applyTextureOverrides(const std::string &override_filepath } } -std::vector *CNodeDefManager::getPalette( - const ContentFeatures &f, const IGameDef *gamedef) -{ -#ifndef SERVER - // This works because colors always use the most significant bits - // of param2. If you add a new colored type which uses param2 - // in a more advanced way, you should change this code, too. - u32 palette_pixels = 0; - switch (f.param_type_2) { - case CPT2_COLOR: - palette_pixels = 256; - break; - case CPT2_COLORED_FACEDIR: - palette_pixels = 8; - break; - case CPT2_COLORED_WALLMOUNTED: - palette_pixels = 32; - break; - default: - return NULL; - } - // This many param2 values will have the same color - u32 step = 256 / palette_pixels; - const std::string &name = f.palette_name; - if (name == "") - return NULL; - Client *client = (Client *) gamedef; - ITextureSource *tsrc = client->tsrc(); - - UNORDERED_MAP >::iterator it = - m_palettes.find(name); - if (it == m_palettes.end()) { - // Create palette - if (!tsrc->isKnownSourceImage(name)) { - warningstream << "CNodeDefManager::getPalette(): palette \"" << name - << "\" could not be loaded." << std::endl; - return NULL; - } - video::IImage *img = tsrc->generateImage(name); - std::vector new_palette; - u32 w = img->getDimension().Width; - u32 h = img->getDimension().Height; - // Real area of the image - u32 area = h * w; - if (area != palette_pixels) - warningstream << "CNodeDefManager::getPalette(): the " - << "specified palette image \"" << name << "\" does not " - << "contain exactly " << palette_pixels - << " pixels." << std::endl; - if (area > palette_pixels) - area = palette_pixels; - // For each pixel in the image - for (u32 i = 0; i < area; i++) { - video::SColor c = img->getPixel(i % w, i / w); - // Fill in palette with 'step' colors - for (u32 j = 0; j < step; j++) - new_palette.push_back(c); - } - img->drop(); - // Fill in remaining elements - while (new_palette.size() < 256) - new_palette.push_back(video::SColor(0xFFFFFFFF)); - m_palettes[name] = new_palette; - it = m_palettes.find(name); - } - if (it != m_palettes.end()) - return &((*it).second); - -#endif - return NULL; -} - void CNodeDefManager::updateTextures(IGameDef *gamedef, void (*progress_callback)(void *progress_args, u32 progress, u32 max_progress), void *progress_callback_args) @@ -1489,12 +1413,10 @@ void CNodeDefManager::updateTextures(IGameDef *gamedef, TextureSettings tsettings; tsettings.readSettings(); - m_palettes.clear(); u32 size = m_content_features.size(); for (u32 i = 0; i < size; i++) { ContentFeatures *f = &(m_content_features[i]); - f->palette = getPalette(*f, gamedef); f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings); progress_callback(progress_callback_args, i, size); } diff --git a/src/object_properties.cpp b/src/object_properties.cpp index f4e4953ba..a77368151 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -117,6 +117,7 @@ void ObjectProperties::serialize(std::ostream &os) const writeARGB8(os, nametag_color); writeF1000(os, automatic_face_movement_max_rotation_per_sec); os << serializeString(infotext); + os << serializeString(wield_item); // Add stuff only at the bottom. // Never remove anything, because we don't want new versions of this @@ -159,6 +160,7 @@ void ObjectProperties::deSerialize(std::istream &is) nametag_color = readARGB8(is); automatic_face_movement_max_rotation_per_sec = readF1000(is); infotext = deSerializeString(is); + wield_item = deSerializeString(is); }catch(SerializationError &e){} } else diff --git a/src/object_properties.h b/src/object_properties.h index 082d9a529..908757a64 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -52,6 +52,8 @@ struct ObjectProperties video::SColor nametag_color; f32 automatic_face_movement_max_rotation_per_sec; std::string infotext; + //! For dropped items, this contains item information. + std::string wield_item; ObjectProperties(); std::string dump(); diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 99e12cd82..bcae874b9 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -58,6 +58,12 @@ ItemDefinition read_item_definition(lua_State* L,int index, getstringfield(L, index, "description", def.description); getstringfield(L, index, "inventory_image", def.inventory_image); getstringfield(L, index, "wield_image", def.wield_image); + getstringfield(L, index, "palette", def.palette_image); + + // Read item color. + lua_getfield(L, index, "color"); + read_color(L, -1, &def.color); + lua_pop(L, 1); lua_getfield(L, index, "wield_scale"); if(lua_istable(L, -1)){ @@ -118,7 +124,7 @@ ItemDefinition read_item_definition(lua_State* L,int index, /******************************************************************************/ void read_object_properties(lua_State *L, int index, - ObjectProperties *prop) + ObjectProperties *prop, IItemDefManager *idef) { if(index < 0) index = lua_gettop(L) + 1 + index; @@ -216,6 +222,10 @@ void read_object_properties(lua_State *L, int index, } lua_pop(L, 1); getstringfield(L, -1, "infotext", prop->infotext); + lua_getfield(L, -1, "wield_item"); + if (!lua_isnil(L, -1)) + prop->wield_item = read_item(L, -1, idef).getItemString(); + lua_pop(L, 1); } /******************************************************************************/ @@ -284,6 +294,8 @@ void push_object_properties(lua_State *L, ObjectProperties *prop) lua_setfield(L, -2, "automatic_face_movement_max_rotation_per_sec"); lua_pushlstring(L, prop->infotext.c_str(), prop->infotext.size()); lua_setfield(L, -2, "infotext"); + lua_pushlstring(L, prop->wield_item.c_str(), prop->wield_item.size()); + lua_setfield(L, -2, "wield_item"); } /******************************************************************************/ diff --git a/src/script/common/c_content.h b/src/script/common/c_content.h index 10cccbb01..949b136eb 100644 --- a/src/script/common/c_content.h +++ b/src/script/common/c_content.h @@ -89,7 +89,8 @@ void push_tool_capabilities (lua_State *L, ItemDefinition read_item_definition (lua_State *L, int index, ItemDefinition default_def); void read_object_properties (lua_State *L, int index, - ObjectProperties *prop); + ObjectProperties *prop, + IItemDefManager *idef); void push_object_properties (lua_State *L, ObjectProperties *prop); diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 9e2193970..2e1d277e4 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "object_properties.h" #include "common/c_converter.h" #include "common/c_content.h" +#include "server.h" bool ScriptApiEntity::luaentity_Add(u16 id, const char *name) { @@ -187,11 +188,11 @@ void ScriptApiEntity::luaentity_GetProperties(u16 id, getstringfield(L, -1, "mesh", prop->mesh); // Deprecated: read object properties directly - read_object_properties(L, -1, prop); + read_object_properties(L, -1, prop, getServer()->idef()); // Read initial_properties lua_getfield(L, -1, "initial_properties"); - read_object_properties(L, -1, prop); + read_object_properties(L, -1, prop, getServer()->idef()); lua_pop(L, 1); } diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp index efdd77b51..c37a82116 100644 --- a/src/script/lua_api/l_itemstackmeta.cpp +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -92,6 +92,10 @@ void ItemStackMetaRef::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + lua_pop(L, 1); // drop metatable luaL_openlib(L, 0, methods, 0); // fill methodtable @@ -111,5 +115,6 @@ const luaL_Reg ItemStackMetaRef::methods[] = { luamethod(MetaDataRef, set_float), luamethod(MetaDataRef, to_table), luamethod(MetaDataRef, from_table), + luamethod(MetaDataRef, equals), {0,0} }; diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index b54005bac..5f4e984cb 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -250,3 +250,17 @@ bool MetaDataRef::handleFromTable(lua_State *L, int table, Metadata *meta) return true; } + +// equals(self, other) +int MetaDataRef::l_equals(lua_State *L) +{ + MetaDataRef *ref1 = checkobject(L, 1); + Metadata *data1 = ref1->getmeta(false); + MetaDataRef *ref2 = checkobject(L, 2); + Metadata *data2 = ref2->getmeta(false); + if (data1 == NULL || data2 == NULL) + lua_pushboolean(L, data1 == data2); + else + lua_pushboolean(L, *data1 == *data2); + return 1; +} diff --git a/src/script/lua_api/l_metadata.h b/src/script/lua_api/l_metadata.h index be31d95ad..a4d8214d3 100644 --- a/src/script/lua_api/l_metadata.h +++ b/src/script/lua_api/l_metadata.h @@ -67,6 +67,9 @@ protected: // from_table(self, table) static int l_from_table(lua_State *L); + + // equals(self, other) + static int l_equals(lua_State *L); }; #endif /* L_NODEMETA_H_ */ diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 55d11fc13..c65d56f14 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -204,6 +204,10 @@ void NodeMetaRef::RegisterCommon(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + lua_pop(L, 1); // drop metatable } @@ -225,6 +229,7 @@ const luaL_Reg NodeMetaRef::methodsServer[] = { luamethod(MetaDataRef, to_table), luamethod(MetaDataRef, from_table), luamethod(NodeMetaRef, get_inventory), + luamethod(MetaDataRef, equals), {0,0} }; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index f9d2754e7..0699705cb 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -737,7 +737,7 @@ int ObjectRef::l_set_properties(lua_State *L) ObjectProperties *prop = co->accessObjectProperties(); if (!prop) return 0; - read_object_properties(L, 2, prop); + read_object_properties(L, 2, prop, getServer(L)->idef()); co->notifyObjectPropertiesModified(); return 0; } diff --git a/src/script/lua_api/l_storage.cpp b/src/script/lua_api/l_storage.cpp index 59906dda5..4c6b2a182 100644 --- a/src/script/lua_api/l_storage.cpp +++ b/src/script/lua_api/l_storage.cpp @@ -98,6 +98,10 @@ void StorageRef::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + lua_pop(L, 1); // drop metatable luaL_openlib(L, 0, methods, 0); // fill methodtable @@ -138,5 +142,6 @@ const luaL_Reg StorageRef::methods[] = { luamethod(MetaDataRef, set_float), luamethod(MetaDataRef, to_table), luamethod(MetaDataRef, from_table), + luamethod(MetaDataRef, equals), {0,0} }; diff --git a/src/wieldmesh.cpp b/src/wieldmesh.cpp index 089a67f33..40af0be5f 100644 --- a/src/wieldmesh.cpp +++ b/src/wieldmesh.cpp @@ -318,11 +318,15 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) u32 shader_id = shdrsrc->getShader("wielded_shader", TILE_MATERIAL_BASIC, NDT_NORMAL); m_material_type = shdrsrc->getShaderInfo(shader_id).material; } + + // Color-related m_colors.clear(); + video::SColor basecolor = idef->getItemstackColor(item, client); // If wield_image is defined, it overrides everything else if (def.wield_image != "") { setExtruded(def.wield_image, def.wield_scale, tsrc, 1); + m_colors.push_back(basecolor); return; } // Handle nodes @@ -371,7 +375,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) } else { material.setTexture(0, tile->texture); } - m_colors.push_back(tile->color); + m_colors.push_back(tile->has_color ? tile->color : basecolor); material.MaterialType = m_material_type; if (m_enable_shaders) { if (tile->normal_texture) { @@ -389,6 +393,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client) } else if (def.inventory_image != "") { setExtruded(def.inventory_image, def.wield_scale, tsrc, 1); + m_colors.push_back(basecolor); return; } @@ -455,7 +460,7 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) m_meshnode->setVisible(true); } -scene::IMesh *getItemMesh(Client *client, const ItemStack &item) +void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) { ITextureSource *tsrc = client->getTextureSource(); IItemDefManager *idef = client->getItemDefManager(); @@ -475,12 +480,13 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) // If inventory_image is defined, it overrides everything else if (def.inventory_image != "") { mesh = getExtrudedMesh(tsrc, def.inventory_image); - return mesh; + result->mesh = mesh; + result->buffer_colors.push_back( + std::pair(false, video::SColor(0xFFFFFFFF))); } else if (def.type == ITEM_NODE) { if (f.mesh_ptr[0]) { mesh = cloneMesh(f.mesh_ptr[0]); scaleMesh(mesh, v3f(0.12, 0.12, 0.12)); - setMeshColor(mesh, video::SColor (255, 255, 255, 255)); } else if (f.drawtype == NDT_PLANTLIKE) { mesh = getExtrudedMesh(tsrc, tsrc->getTextureName(f.tiles[0].texture_id)); @@ -515,6 +521,8 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) for (u32 i = 0; i < mc; ++i) { const TileSpec *tile = &(f.tiles[i]); scene::IMeshBuffer *buf = mesh->getMeshBuffer(i); + result->buffer_colors.push_back( + std::pair(tile->has_color, tile->color)); colorizeMeshBuffer(buf, &tile->color); video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -532,9 +540,8 @@ scene::IMesh *getItemMesh(Client *client, const ItemStack &item) rotateMeshXZby(mesh, -45); rotateMeshYZby(mesh, -30); - return mesh; + result->mesh = mesh; } - return NULL; } scene::IMesh * getExtrudedMesh(ITextureSource *tsrc, diff --git a/src/wieldmesh.h b/src/wieldmesh.h index 200587058..94edb1de6 100644 --- a/src/wieldmesh.h +++ b/src/wieldmesh.h @@ -28,6 +28,22 @@ class Client; class ITextureSource; struct TileSpec; +struct ItemMesh +{ + scene::IMesh* mesh; + /*! + * Stores the color of each mesh buffer. + * If the boolean is true, the color is fixed, else + * palettes can modify it. + */ + std::vector > buffer_colors; + + ItemMesh(): + mesh(NULL), + buffer_colors() + {} +}; + /* Wield item scene node, renders the wield mesh of some item */ @@ -79,7 +95,7 @@ private: aabb3f m_bounding_box; }; -scene::IMesh *getItemMesh(Client *client, const ItemStack &item); +void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result); scene::IMesh *getExtrudedMesh(ITextureSource *tsrc, const std::string &imagename); #endif