Skip to content

Commit

Permalink
Add ability to override item images using meta (#12614)
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenwardy committed Apr 17, 2023
1 parent 8c2c7fa commit 4158b72
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 151 deletions.
5 changes: 5 additions & 0 deletions doc/lua_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2271,6 +2271,11 @@ Some of the values in the key-value store are handled specially:
See also: `get_description` in [`ItemStack`]
* `short_description`: Set the item stack's short description.
See also: `get_short_description` in [`ItemStack`]
* `inventory_image`: Override inventory_image
* `inventory_overlay`: Override inventory_overlay
* `wield_image`: Override wield_image
* `wield_overlay`: Override wield_overlay
* `wield_scale`: Override wield_scale, use vector.to_string
* `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.
Expand Down
39 changes: 39 additions & 0 deletions games/devtest/mods/testitems/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ minetest.register_craftitem("testitems:overlay_meta", {
on_place = overlay_on_place,
on_secondary_use = overlay_on_place,
})

minetest.register_craftitem("testitems:overlay_global", {
description = S("Texture Overlay Test Item, Global Color") .. "\n" ..
S("Image must be an orange square with rainbow cross (inventory and wield)"),
Expand All @@ -51,3 +52,41 @@ minetest.register_craftitem("testitems:overlay_global", {
wield_overlay = "testitems_overlay_overlay.png",
color = GLOBAL_COLOR_ARG,
})

minetest.register_craftitem("testitems:image_meta", {
description = S("Image Override Meta Test Item"),
inventory_image = "default_apple.png",
wield_image = "basetools_icesword.png",

on_use = function(itemstack, player)
local meta = itemstack:get_meta()
local state = meta:get_int("state")
state = (state + 1) % 5
meta:set_int("state", state)
minetest.chat_send_player(player:get_player_name(), "State " .. state)

if state == 0 then
meta:set_string("inventory_image", "")
meta:set_string("wield_image", "")
meta:set_string("inventory_overlay", "")
meta:set_string("wield_overlay", "")
meta:set_string("wield_scale", "")
elseif state == 1 then
meta:set_string("inventory_image", "default_tree.png")
meta:set_string("wield_image", "basetools_firesword.png")
elseif state == 2 then
meta:set_string("inventory_image", "default_apple.png^testitems_overridden.png")
meta:set_string("wield_image", "basetools_icesword.png^testitems_overridden.png")
elseif state == 3 then
meta:set_string("inventory_image", "default_tree.png")
meta:set_string("wield_image", "basetools_firesword.png")
meta:set_string("inventory_overlay", "default_apple.png")
meta:set_string("wield_overlay", "default_apple.png")
elseif state == 4 then
local scale = vector.new(0.5, 0.5, 0.5)
meta:set_string("wield_scale", scale:to_string())
end

return itemstack
end,
})
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/client/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1840,7 +1840,6 @@ inline bool Game::handleCallbacks()
void Game::processQueues()
{
texture_src->processQueue();
itemdef_manager->processQueue(client);
shader_src->processQueue();
}

Expand Down
18 changes: 11 additions & 7 deletions src/client/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1008,10 +1008,14 @@ void drawItemStack(
const static thread_local bool enable_animations =
g_settings->getBool("inventory_items_animations");

const ItemDefinition &def = item.getDefinition(client->idef());
auto *idef = client->idef();
const ItemDefinition &def = item.getDefinition(idef);

bool draw_overlay = false;

const std::string inventory_image = item.getInventoryImage(idef);
const std::string inventory_overlay = item.getInventoryOverlay(idef);

bool has_mesh = false;
ItemMesh *imesh;

Expand All @@ -1020,8 +1024,8 @@ void drawItemStack(
viewrect.clipAgainst(*clip);

// Render as mesh if animated or no inventory image
if ((enable_animations && rotation_kind < IT_ROT_NONE) || def.inventory_image.empty()) {
imesh = client->idef()->getWieldMesh(def.name, client);
if ((enable_animations && rotation_kind < IT_ROT_NONE) || inventory_image.empty()) {
imesh = idef->getWieldMesh(item, client);
has_mesh = imesh && imesh->mesh;
}
if (has_mesh) {
Expand Down Expand Up @@ -1110,9 +1114,9 @@ void drawItemStack(
driver->setTransform(video::ETS_PROJECTION, oldProjMat);
driver->setViewPort(oldViewPort);

draw_overlay = def.type == ITEM_NODE && def.inventory_image.empty();
draw_overlay = def.type == ITEM_NODE && inventory_image.empty();
} else { // Otherwise just draw as 2D
video::ITexture *texture = client->idef()->getInventoryTexture(def.name, client);
video::ITexture *texture = client->idef()->getInventoryTexture(item, client);
video::SColor color;
if (texture) {
color = client->idef()->getItemstackColor(item, client);
Expand All @@ -1134,9 +1138,9 @@ void drawItemStack(
}

// draw the inventory_overlay
if (!def.inventory_overlay.empty() && draw_overlay) {
if (!inventory_overlay.empty() && draw_overlay) {
ITextureSource *tsrc = client->getTextureSource();
video::ITexture *overlay_texture = tsrc->getTexture(def.inventory_overlay);
video::ITexture *overlay_texture = tsrc->getTexture(inventory_overlay);
core::dimension2d<u32> dimens = overlay_texture->getOriginalSize();
core::rect<s32> srcrect(0, 0, dimens.Width, dimens.Height);
draw2DImageFilterScaled(driver, overlay_texture, rect, srcrect, clip, 0, true);
Expand Down
33 changes: 19 additions & 14 deletions src/client/wieldmesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -384,9 +384,13 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
m_colors.clear();
m_base_color = idef->getItemstackColor(item, client);

const std::string wield_image = item.getWieldImage(idef);
const std::string wield_overlay = item.getWieldOverlay(idef);
const v3f wield_scale = item.getWieldScale(idef);

// If wield_image needs to be checked and is defined, it overrides everything else
if (!def.wield_image.empty() && check_wield_image) {
setExtruded(def.wield_image, def.wield_overlay, def.wield_scale, tsrc,
if (!wield_image.empty() && check_wield_image) {
setExtruded(wield_image, wield_overlay, wield_scale, tsrc,
1);
m_colors.emplace_back();
// overlay is white, if present
Expand All @@ -413,7 +417,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
case NDT_RAILLIKE:
case NDT_PLANTLIKE:
case NDT_FLOWINGLIQUID: {
v3f wscale = def.wield_scale;
v3f wscale = wield_scale;
if (f.drawtype == NDT_FLOWINGLIQUID)
wscale.Z *= 0.1f;
setExtruded(tsrc->getTextureName(f.tiles[0].layers[0].texture_id),
Expand All @@ -429,7 +433,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
}
case NDT_PLANTLIKE_ROOTED: {
setExtruded(tsrc->getTextureName(f.special_tiles[0].layers[0].texture_id),
"", def.wield_scale, tsrc,
"", wield_scale, tsrc,
f.special_tiles[0].layers[0].animation_frame_count);
// Add color
const TileLayer &l0 = f.special_tiles[0].layers[0];
Expand All @@ -439,7 +443,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
case NDT_NORMAL:
case NDT_ALLFACES:
case NDT_LIQUID:
setCube(f, def.wield_scale);
setCube(f, wield_scale);
break;
default: {
// Render non-trivial drawtypes like the actual node
Expand All @@ -450,7 +454,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
changeToMesh(mesh);
mesh->drop();
m_meshnode->setScale(
def.wield_scale * WIELD_SCALE_FACTOR
wield_scale * WIELD_SCALE_FACTOR
/ (BS * f.visual_scale));
break;
}
Expand All @@ -471,9 +475,10 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
setColor(video::SColor(0xFFFFFFFF));
return;
} else {
if (!def.inventory_image.empty()) {
setExtruded(def.inventory_image, def.inventory_overlay, def.wield_scale,
tsrc, 1);
const std::string inventory_image = item.getInventoryImage(idef);
if (!inventory_image.empty()) {
const std::string inventory_overlay = item.getInventoryOverlay(idef);
setExtruded(inventory_image, inventory_overlay, def.wield_scale, tsrc, 1);
} else {
setExtruded("no_texture.png", "", def.wield_scale, tsrc, 1);
}
Expand Down Expand Up @@ -578,18 +583,18 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
bool cull_backface = f.needsBackfaceCulling();

// If inventory_image is defined, it overrides everything else
if (!def.inventory_image.empty()) {
mesh = getExtrudedMesh(tsrc, def.inventory_image,
def.inventory_overlay);
const std::string inventory_image = item.getInventoryImage(idef);
const std::string inventory_overlay = item.getInventoryOverlay(idef);
if (!inventory_image.empty()) {
mesh = getExtrudedMesh(tsrc, inventory_image, inventory_overlay);
result->buffer_colors.emplace_back();
// overlay is white, if present
result->buffer_colors.emplace_back(true, video::SColor(0xFFFFFFFF));
// Items with inventory images do not need shading
result->needs_shading = false;
} else if (def.type == ITEM_NODE && f.drawtype == NDT_AIRLIKE) {
// Fallback image for airlike node
mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png",
def.inventory_overlay);
mesh = getExtrudedMesh(tsrc, "no_texture_airlike.png", inventory_overlay);
result->needs_shading = false;
} else if (def.type == ITEM_NODE) {
switch (f.drawtype) {
Expand Down
44 changes: 44 additions & 0 deletions src/inventory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,50 @@ std::string ItemStack::getShortDescription(const IItemDefManager *itemdef) const
return desc;
}

std::string ItemStack::getInventoryImage(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("inventory_image");
if (texture.empty())
texture = getDefinition(itemdef).inventory_image;

return texture;
}

std::string ItemStack::getInventoryOverlay(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("inventory_overlay");
if (texture.empty())
texture = getDefinition(itemdef).inventory_overlay;

return texture;
}

std::string ItemStack::getWieldImage(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("wield_image");
if (texture.empty())
texture = getDefinition(itemdef).wield_image;

return texture;
}

std::string ItemStack::getWieldOverlay(const IItemDefManager *itemdef) const
{
std::string texture = metadata.getString("wield_overlay");
if (texture.empty())
texture = getDefinition(itemdef).wield_overlay;

return texture;
}

v3f ItemStack::getWieldScale(const IItemDefManager *itemdef) const
{
std::string scale = metadata.getString("wield_scale");
if (scale.empty())
return getDefinition(itemdef).wield_scale;

return str_to_v3f(scale);
}

ItemStack ItemStack::addItem(ItemStack newitem, IItemDefManager *itemdef)
{
Expand Down
6 changes: 6 additions & 0 deletions src/inventory.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ struct ItemStack
std::string getDescription(const IItemDefManager *itemdef) const;
std::string getShortDescription(const IItemDefManager *itemdef) const;

std::string getInventoryImage(const IItemDefManager *itemdef) const;
std::string getInventoryOverlay(const IItemDefManager *itemdef) const;
std::string getWieldImage(const IItemDefManager *itemdef) const;
std::string getWieldOverlay(const IItemDefManager *itemdef) const;
v3f getWieldScale(const IItemDefManager *itemdef) const;

/*
Quantity methods
*/
Expand Down
Loading

0 comments on commit 4158b72

Please sign in to comment.