Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add wear bar color API #13328

Merged
merged 20 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
61 changes: 61 additions & 0 deletions doc/lua_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2387,6 +2387,48 @@ Table of resulting tool uses:



Wear Bar Color
==============

'Wear Bar' is a property of items that defines the coloring
of the bar that appears under damaged tools.
If it is absent, the old behavior of green-yellow-red is
used.
techno-sam marked this conversation as resolved.
Show resolved Hide resolved

Wear bar colors definition
--------------------------

### Syntax

```lua
{
-- 'constant' or 'linear'
-- (nil defaults to 'constant')
blend = "linear",
color_stops = {
[0.0] = "#ff0000",
[0.5] = "slateblue",
[1.0] = {r=0, g=255, b=0, a=150},
}
}
```

### Blend mode `blend`

* `linear`: blends smoothly between each defined color point.
* `constant`: each color starts at its defined point, and continues up to the next point

### Color stops `color_stops`

Specified as `float` keys assigned to `ColorSpec` values.
techno-sam marked this conversation as resolved.
Show resolved Hide resolved

### Shortcut usage

Wear bar color can also be specified as a single `ColorSpec` instead of a table.




Entity damage mechanism
=======================

Expand Down Expand Up @@ -7273,6 +7315,8 @@ an itemstring, a table or `nil`.
the item breaks after `max_uses` times
* Valid `max_uses` range is [0,65536]
* Does nothing if item is not a tool or if `max_uses` is 0
* `get_wear_bar_params()`: returns the wear bar parameters of the item,
or nil if none are defined for this item type or in the stack's meta
* `add_item(item)`: returns leftover `ItemStack`
* Put some item or stack onto this stack
* `item_fits(item)`: returns `true` if item or stack can be fully added to
Expand Down Expand Up @@ -7314,6 +7358,10 @@ Can be obtained via `item:get_meta()`.
* Overrides the item's tool capabilities
* A nil value will clear the override data and restore the original
behavior.
* `set_wear_bar_params([wear_bar_params])`
* Overrides the item's wear bar parameters (see "Wear Bar Color" section)
* A nil value will clear the override data and restore the original
behavior.

`MetaDataRef`
-------------
Expand Down Expand Up @@ -8814,6 +8862,19 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and
-- fallback behavior.
},

-- Set wear bar color of the tool by setting color stops and blend mode
-- See "Wear Bar Color" section for further explanation including an example
wear_color = {
techno-sam marked this conversation as resolved.
Show resolved Hide resolved
-- interpolation mode: 'constant' or 'linear'
-- (nil defaults to 'constant')
blend = "linear",
color_stops = {
[0.0] = "#ff0000",
[0.5] = "#ffff00",
[1.0] = "#00ff00",
}
},

node_placement_prediction = nil,
-- If nil and item is node, prediction is made automatically.
-- If nil and item is not a node, no prediction is made.
Expand Down
137 changes: 121 additions & 16 deletions games/devtest/mods/basetools/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -420,36 +420,141 @@ minetest.register_tool("basetools:dagger_steel", {
}
})

-- Test tool uses and punch_attack_uses
local uses = { 1, 2, 3, 5, 10, 50, 100, 1000, 10000, 65535 }
for i=1, #uses do
local u = uses[i]
local ustring
if i == 1 then
ustring = u.."-Use"
else
ustring = u.."-Uses"
end
local color = string.format("#FF00%02X", math.floor(((i-1)/#uses) * 255))
minetest.register_tool("basetools:pick_uses_"..string.format("%05d", u), {
-- Test tool uses, punch_attack_uses, and wear bar coloring
local tool_params = {
{uses = 1},
{uses = 2},
{uses = 3},
{
uses = 5,
wear_color = "#5865f2",
wear_description = "Solid color: #5865f2",
},
{
uses = 10,
wear_color = "slateblue",
wear_description = "Solid color: slateblue",
},
{
uses = 50,
wear_color = {
color_stops = {
[0] = "red",
[0.5] = "yellow",
[1.0] = "blue"
},
blend = "linear"
},
wear_description = "Ranges from blue to yellow to red",
},
{
uses = 100,
wear_color = {
color_stops = {
[0] = "#ffff00",
[0.2] = "#ff00ff",
[0.3] = "#ffff00",
[0.45] = "#c0ffee",
[0.6] = {r=255, g=255, b=0, a=100}, -- continues until the end
},
blend = "constant"
},
wear_description = "Misc. colors, constant interpolation",
},
{uses = 1e3},
{uses = 1e4},
{uses = 65535},
}

for i, params in ipairs(tool_params) do
local uses = params.uses
local ustring = uses.."-Use"..(uses == 1 and "" or "s")
local color = string.format("#FF00%02X", math.floor(((i-1)/#tool_params) * 255))
minetest.register_tool("basetools:pick_uses_"..string.format("%05d", uses), {
description = ustring.." Pickaxe".."\n"..
"Digs cracky=3",
"Digs cracky=3"..
(params.wear_description and "\n".."Wear bar: " .. params.wear_description or ""),
inventory_image = "basetools_usespick.png^[colorize:"..color..":127",
tool_capabilities = {
max_drop_level=0,
groupcaps={
cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=u, maxlevel=0}
cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=uses, maxlevel=0}
},
},
wear_color = params.wear_color
})

minetest.register_tool("basetools:sword_uses_"..string.format("%05d", u), {
minetest.register_tool("basetools:sword_uses_"..string.format("%05d", uses), {
description = ustring.." Sword".."\n"..
"Damage: fleshy=1",
inventory_image = "basetools_usessword.png^[colorize:"..color..":127",
tool_capabilities = {
damage_groups = {fleshy=1},
punch_attack_uses = u,
punch_attack_uses = uses,
},
})
end

minetest.register_chatcommand("wear_color", {
params = "[idx]",
description = "Set wear bar color override",
func = function(player_name, param)
local player = minetest.get_player_by_name(player_name)
if not player then return end

local wear_color = nil
local wear_desc = "Reset override"

if param ~= "" then
local params = tool_params[tonumber(param)]
if not params then
return false, "idx out of bounds"
end
wear_color = params.wear_color
wear_desc = "Set override: "..(params.wear_description or "Default behavior")
end
local tool = player:get_wielded_item()
if tool:get_count() == 0 then
return false, "Tool not found"
end
tool:get_meta():set_wear_bar_params(wear_color)
player:set_wielded_item(tool)
return true, wear_desc
end
})

-- Punch handler to set random color & wear
local wear_on_use = function(itemstack, user, pointed_thing)
local meta = itemstack:get_meta()
local color = math.random(0, 0xFFFFFF)
local colorstr = string.format("#%06x", color)
meta:set_wear_bar_params(colorstr)
minetest.log("action", "[basetool] Wear bar color of "..itemstack:get_name().." changed to "..colorstr)
itemstack:set_wear(math.random(0, 65535))
return itemstack
end

-- Place handler to clear item metadata color
local wear_on_place = function(itemstack, user, pointed_thing)
local meta = itemstack:get_meta()
meta:set_wear_bar_params(nil)
return itemstack
end

minetest.register_tool("basetools:random_wear_bar", {
description = "Wear Bar Color Test\n" ..
"Punch: Set random color & wear\n" ..
"Place: Clear color",
-- Base texture: A grayscale square (can be colorized)
inventory_image = "basetools_usespick.png^[colorize:#FFFFFF:127",
tool_capabilities = {
max_drop_level=0,
groupcaps={
cracky={times={[3]=0.1, [2]=0.2, [1]=0.3}, uses=1000, maxlevel=0}
},
},

on_use = wear_on_use,
on_place = wear_on_place,
on_secondary_use = wear_on_place,
})
23 changes: 23 additions & 0 deletions games/devtest/mods/util_commands/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,29 @@ minetest.register_chatcommand("dump_item", {
end,
})

minetest.register_chatcommand("dump_itemdef", {
params = "",
description = "Prints a dump of the wielded item's definition in table form",
func = function(name, param)
local player = minetest.get_player_by_name(name)
local str = dump(player:get_wielded_item():get_definition())
print(str)
return true, str
end,
})

minetest.register_chatcommand("dump_wear_bar", {
params = "",
description = "Prints a dump of the wielded item's wear bar parameters in table form",
func = function(name, param)
local player = minetest.get_player_by_name(name)
local item = player:get_wielded_item()
local str = dump(item:get_wear_bar_params())
print(str)
return true, str
end,
})

core.register_chatcommand("set_saturation", {
params = "<saturation>",
description = "Set the saturation for current player.",
Expand Down
23 changes: 16 additions & 7 deletions src/client/hud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1179,17 +1179,26 @@ void drawItemStack(
(1 - wear) * progressrect.LowerRightCorner.X;

// Compute progressbar color
// old scheme:
grorp marked this conversation as resolved.
Show resolved Hide resolved
// wear = 0.0: green
// wear = 0.5: yellow
// wear = 1.0: red
video::SColor color(255, 255, 255, 255);
int wear_i = MYMIN(std::floor(wear * 600), 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);
video::SColor color;
auto barParams = item.getWearBarParams(client->idef());
if (barParams.has_value()) {
f32 durabilityPercent = 1.0 - wear;
color = barParams->getWearBarColor(durabilityPercent);
} else {
color = video::SColor(255, 255, 255, 255);
int wear_i = MYMIN(std::floor(wear * 600), 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);
}

core::rect<s32> progressrect2 = progressrect;
progressrect2.LowerRightCorner.X = progressmid;
Expand Down
9 changes: 9 additions & 0 deletions src/inventory.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ struct ItemStack
return metadata.getToolCapabilities(*item_cap); // Check for override
}

const std::optional<WearBarParams> &getWearBarParams(
const IItemDefManager *itemdef) const
{
auto &meta_override = metadata.getWearBarParamOverride();
if (meta_override.has_value())
return meta_override;
return itemdef->get(name).wear_bar_params;
}

// Wear out (only tools)
// Returns true if the item is (was) a tool
bool addWear(s32 amount, const IItemDefManager *itemdef)
Expand Down
14 changes: 14 additions & 0 deletions src/itemdef.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
pointabilities = def.pointabilities;
if (def.tool_capabilities)
tool_capabilities = new ToolCapabilities(*def.tool_capabilities);
wear_bar_params = def.wear_bar_params;
groups = def.groups;
node_placement_prediction = def.node_placement_prediction;
place_param2 = def.place_param2;
Expand All @@ -149,6 +150,7 @@ void ItemDefinition::resetInitial()
{
// Initialize pointers to NULL so reset() does not delete undefined pointers
tool_capabilities = NULL;
wear_bar_params = std::nullopt;
reset();
}

Expand All @@ -171,6 +173,7 @@ void ItemDefinition::reset()
pointabilities = std::nullopt;
delete tool_capabilities;
tool_capabilities = NULL;
wear_bar_params.reset();
groups.clear();
sound_place = SoundSpec();
sound_place_failed = SoundSpec();
Expand Down Expand Up @@ -251,6 +254,13 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
pointabilities_s = tmp_os.str();
}
os << serializeString16(pointabilities_s);

if (wear_bar_params.has_value()) {
writeU8(os, 1);
wear_bar_params->serialize(os);
} else {
writeU8(os, 0);
}
}

void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
Expand Down Expand Up @@ -333,6 +343,10 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
pointabilities = std::make_optional<Pointabilities>();
pointabilities->deSerialize(tmp_is);
}

if (readU8(is)) {
wear_bar_params = WearBarParams::deserialize(is);
}
} catch(SerializationError &e) {};
}

Expand Down
3 changes: 3 additions & 0 deletions src/itemdef.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "itemgroup.h"
#include "sound.h"
#include "texture_override.h" // TextureOverride
#include "tool.h"
#include "util/pointabilities.h"
class IGameDef;
class Client;
Expand Down Expand Up @@ -103,6 +104,8 @@ struct ItemDefinition
// They may be NULL. If non-NULL, deleted by destructor
ToolCapabilities *tool_capabilities;

std::optional<WearBarParams> wear_bar_params;

ItemGroupList groups;
SoundSpec sound_place;
SoundSpec sound_place_failed;
Expand Down