Skip to content

Commit 0fcaf9f

Browse files
juhdanadnerzhul
authored andcommittedJun 20, 2017
Automatic item and node colorization (#5640)
* Automatic item and node colorization Now nodes with a palette yield colored item stacks, and colored items place colored nodes by default. The client predicts the colorization. * Backwards compatibility * Use nil * Style fixes * Fix code style * Document changes
1 parent 7c07cb4 commit 0fcaf9f

File tree

8 files changed

+114
-38
lines changed

8 files changed

+114
-38
lines changed
 

‎builtin/game/falling.lua

+3-3
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ core.register_entity(":__builtin:falling_node", {
9393
core.remove_node(np)
9494
if nd and nd.buildable_to == false then
9595
-- Add dropped items
96-
local drops = core.get_node_drops(n2.name, "")
96+
local drops = core.get_node_drops(n2, "")
9797
for _, dropped_item in pairs(drops) do
9898
core.add_item(np, dropped_item)
9999
end
@@ -145,9 +145,9 @@ function core.spawn_falling_node(pos)
145145
end
146146

147147
local function drop_attached_node(p)
148-
local nn = core.get_node(p).name
148+
local n = core.get_node(p)
149149
core.remove_node(p)
150-
for _, item in pairs(core.get_node_drops(nn, "")) do
150+
for _, item in pairs(core.get_node_drops(n, "")) do
151151
local pos = {
152152
x = p.x + math.random()/2 - 0.25,
153153
y = p.y + math.random()/2 - 0.25,

‎builtin/game/item.lua

+46-4
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,35 @@ function core.yaw_to_dir(yaw)
155155
return {x = -math.sin(yaw), y = 0, z = math.cos(yaw)}
156156
end
157157

158-
function core.get_node_drops(nodename, toolname)
158+
function core.get_node_drops(node, toolname)
159+
-- Compatibility, if node is string
160+
local nodename = node
161+
local param2 = 0
162+
-- New format, if node is table
163+
if (type(node) == "table") then
164+
nodename = node.name
165+
param2 = node.param2
166+
end
159167
local def = core.registered_nodes[nodename]
160168
local drop = def and def.drop
161169
if drop == nil then
162170
-- default drop
163-
return {nodename}
171+
local stack = ItemStack(nodename)
172+
if def then
173+
local type = def.paramtype2
174+
if (type == "color") or (type == "colorfacedir") or
175+
(type == "colorwallmounted") then
176+
local meta = stack:get_meta()
177+
local color_part = param2
178+
if (type == "colorfacedir") then
179+
color_part = math.floor(color_part / 32) * 32;
180+
elseif (type == "colorwallmounted") then
181+
color_part = math.floor(color_part / 8) * 8;
182+
end
183+
meta:set_int("palette_index", color_part)
184+
end
185+
end
186+
return {stack:to_string()}
164187
elseif type(drop) == "string" then
165188
-- itemstring drop
166189
return {drop}
@@ -258,7 +281,7 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
258281
.. def.name .. " at " .. core.pos_to_string(place_to))
259282

260283
local oldnode = core.get_node(place_to)
261-
local newnode = {name = def.name, param1 = 0, param2 = param2}
284+
local newnode = {name = def.name, param1 = 0, param2 = param2 or 0}
262285

263286
-- Calculate direction for wall mounted stuff like torches and signs
264287
if def.place_param2 ~= nil then
@@ -286,6 +309,25 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2)
286309
end
287310
end
288311

312+
local metatable = itemstack:get_meta():to_table().fields
313+
314+
-- Transfer color information
315+
if metatable.palette_index and not def.place_param2 then
316+
local color_divisor = nil
317+
if def.paramtype2 == "color" then
318+
color_divisor = 1
319+
elseif def.paramtype2 == "colorwallmounted" then
320+
color_divisor = 8
321+
elseif def.paramtype2 == "colorfacedir" then
322+
color_divisor = 32
323+
end
324+
if color_divisor then
325+
local color = math.floor(metatable.palette_index / color_divisor)
326+
local other = newnode.param2 % color_divisor
327+
newnode.param2 = color * color_divisor + other
328+
end
329+
end
330+
289331
-- Check if the node is attached and if it can be placed there
290332
if core.get_item_group(def.name, "attached_node") ~= 0 and
291333
not builtin_shared.check_attached_node(place_to, newnode) then
@@ -474,7 +516,7 @@ function core.node_dig(pos, node, digger)
474516
.. node.name .. " at " .. core.pos_to_string(pos))
475517

476518
local wielded = digger:get_wielded_item()
477-
local drops = core.get_node_drops(node.name, wielded:get_name())
519+
local drops = core.get_node_drops(node, wielded:get_name())
478520

479521
local wdef = wielded:get_definition()
480522
local tp = wielded:get_tool_capabilities()

‎doc/lua_api.txt

+8-5
Original file line numberDiff line numberDiff line change
@@ -531,9 +531,11 @@ for conversion.
531531
If the `ItemStack`'s metadata contains the `color` field, it will be
532532
lost on placement, because nodes on the map can only use palettes.
533533

534-
If the `ItemStack`'s metadata contains the `palette_index` field, you
535-
currently must manually convert between it and the node's `param2` with
536-
custom `on_place` and `on_dig` callbacks.
534+
If the `ItemStack`'s metadata contains the `palette_index` field, it is
535+
automatically transferred between node and item forms by the engine,
536+
when a player digs or places a colored node.
537+
You can disable this feature by setting the `drop` field of the node
538+
to itself (without metadata).
537539

538540
### Colored items in craft recipes
539541
Craft recipes only support item strings, but fortunately item strings
@@ -3326,8 +3328,9 @@ An `InvRef` is a reference to an inventory.
33263328
* `add_item(listname, stack)`: add item somewhere in list, returns leftover `ItemStack`
33273329
* `room_for_item(listname, stack):` returns `true` if the stack of items
33283330
can be fully added to the list
3329-
* `contains_item(listname, stack)`: returns `true` if the stack of items
3330-
can be fully taken from the list
3331+
* `contains_item(listname, stack, [match_meta])`: returns `true` if
3332+
the stack of items can be fully taken from the list.
3333+
If `match_meta` is false, only the items' names are compared (default: `false`).
33313334
* `remove_item(listname, stack)`: take as many items as specified from the list,
33323335
returns the items that were actually removed (as an `ItemStack`) -- note that
33333336
any item metadata is ignored, so attempting to remove a specific unique

‎src/game.cpp

+42-15
Original file line numberDiff line numberDiff line change
@@ -774,8 +774,8 @@ class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactor
774774
};
775775

776776

777-
bool nodePlacementPrediction(Client &client,
778-
const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos)
777+
bool nodePlacementPrediction(Client &client, const ItemDefinition &playeritem_def,
778+
const ItemStack &playeritem, v3s16 nodepos, v3s16 neighbourpos)
779779
{
780780
std::string prediction = playeritem_def.node_placement_prediction;
781781
INodeDefManager *nodedef = client.ndef();
@@ -818,11 +818,13 @@ bool nodePlacementPrediction(Client &client,
818818
return false;
819819
}
820820

821+
const ContentFeatures &predicted_f = nodedef->get(id);
822+
821823
// Predict param2 for facedir and wallmounted nodes
822824
u8 param2 = 0;
823825

824-
if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED ||
825-
nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) {
826+
if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
827+
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
826828
v3s16 dir = nodepos - neighbourpos;
827829

828830
if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
@@ -834,8 +836,8 @@ bool nodePlacementPrediction(Client &client,
834836
}
835837
}
836838

837-
if (nodedef->get(id).param_type_2 == CPT2_FACEDIR ||
838-
nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) {
839+
if (predicted_f.param_type_2 == CPT2_FACEDIR ||
840+
predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) {
839841
v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
840842

841843
if (abs(dir.X) > abs(dir.Z)) {
@@ -848,7 +850,7 @@ bool nodePlacementPrediction(Client &client,
848850
assert(param2 <= 5);
849851

850852
//Check attachment if node is in group attached_node
851-
if (((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0) {
853+
if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) {
852854
static v3s16 wallmounted_dirs[8] = {
853855
v3s16(0, 1, 0),
854856
v3s16(0, -1, 0),
@@ -859,8 +861,8 @@ bool nodePlacementPrediction(Client &client,
859861
};
860862
v3s16 pp;
861863

862-
if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED ||
863-
nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED)
864+
if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
865+
predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)
864866
pp = p + wallmounted_dirs[param2];
865867
else
866868
pp = p + v3s16(0, -1, 0);
@@ -869,6 +871,28 @@ bool nodePlacementPrediction(Client &client,
869871
return false;
870872
}
871873

874+
// Apply color
875+
if ((predicted_f.param_type_2 == CPT2_COLOR
876+
|| predicted_f.param_type_2 == CPT2_COLORED_FACEDIR
877+
|| predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) {
878+
const std::string &indexstr = playeritem.metadata.getString(
879+
"palette_index", 0);
880+
if (!indexstr.empty()) {
881+
s32 index = mystoi(indexstr);
882+
if (predicted_f.param_type_2 == CPT2_COLOR) {
883+
param2 = index;
884+
} else if (predicted_f.param_type_2
885+
== CPT2_COLORED_WALLMOUNTED) {
886+
// param2 = pure palette index + other
887+
param2 = (index & 0xf8) | (param2 & 0x07);
888+
} else if (predicted_f.param_type_2
889+
== CPT2_COLORED_FACEDIR) {
890+
// param2 = pure palette index + other
891+
param2 = (index & 0xe0) | (param2 & 0x1f);
892+
}
893+
}
894+
}
895+
872896
// Add node to client map
873897
MapNode n(id, 0, param2);
874898

@@ -1277,8 +1301,9 @@ class Game {
12771301
const core::line3d<f32> &shootline, bool liquids_pointable,
12781302
bool look_for_object, const v3s16 &camera_offset);
12791303
void handlePointingAtNothing(const ItemStack &playerItem);
1280-
void handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
1281-
const ToolCapabilities &playeritem_toolcap, f32 dtime);
1304+
void handlePointingAtNode(const PointedThing &pointed,
1305+
const ItemDefinition &playeritem_def, const ItemStack &playeritem,
1306+
const ToolCapabilities &playeritem_toolcap, f32 dtime);
12821307
void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem,
12831308
const v3f &player_position, bool show_debug);
12841309
void handleDigging(const PointedThing &pointed, const v3s16 &nodepos,
@@ -3599,7 +3624,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug)
35993624
if (playeritem.name.empty() && hand_def.tool_capabilities != NULL) {
36003625
playeritem_toolcap = *hand_def.tool_capabilities;
36013626
}
3602-
handlePointingAtNode(pointed, playeritem_def, playeritem_toolcap, dtime);
3627+
handlePointingAtNode(pointed, playeritem_def, playeritem,
3628+
playeritem_toolcap, dtime);
36033629
} else if (pointed.type == POINTEDTHING_OBJECT) {
36043630
handlePointingAtObject(pointed, playeritem, player_position, show_debug);
36053631
} else if (isLeftPressed()) {
@@ -3734,8 +3760,9 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem)
37343760
}
37353761

37363762

3737-
void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinition &playeritem_def,
3738-
const ToolCapabilities &playeritem_toolcap, f32 dtime)
3763+
void Game::handlePointingAtNode(const PointedThing &pointed,
3764+
const ItemDefinition &playeritem_def, const ItemStack &playeritem,
3765+
const ToolCapabilities &playeritem_toolcap, f32 dtime)
37393766
{
37403767
v3s16 nodepos = pointed.node_undersurface;
37413768
v3s16 neighbourpos = pointed.node_abovesurface;
@@ -3795,7 +3822,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, const ItemDefinitio
37953822
// If the wielded item has node placement prediction,
37963823
// make that happen
37973824
bool placed = nodePlacementPrediction(*client,
3798-
playeritem_def,
3825+
playeritem_def, playeritem,
37993826
nodepos, neighbourpos);
38003827

38013828
if (placed) {

‎src/inventory.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ bool InventoryList::roomForItem(const ItemStack &item_) const
658658
return false;
659659
}
660660

661-
bool InventoryList::containsItem(const ItemStack &item) const
661+
bool InventoryList::containsItem(const ItemStack &item, bool match_meta) const
662662
{
663663
u32 count = item.count;
664664
if(count == 0)
@@ -669,9 +669,9 @@ bool InventoryList::containsItem(const ItemStack &item) const
669669
{
670670
if(count == 0)
671671
break;
672-
if(i->name == item.name)
673-
{
674-
if(i->count >= count)
672+
if (i->name == item.name
673+
&& (!match_meta || (i->metadata == item.metadata))) {
674+
if (i->count >= count)
675675
return true;
676676
else
677677
count -= i->count;

‎src/inventory.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,10 @@ class InventoryList
223223
// Checks whether there is room for a given item
224224
bool roomForItem(const ItemStack &item) const;
225225

226-
// Checks whether the given count of the given item name
226+
// Checks whether the given count of the given item
227227
// exists in this inventory list.
228-
bool containsItem(const ItemStack &item) const;
228+
// If match_meta is false, only the items' names are compared.
229+
bool containsItem(const ItemStack &item, bool match_meta) const;
229230

230231
// Removes the given count of the given item name from
231232
// this inventory list. Walks the list in reverse order.

‎src/script/lua_api/l_inventory.cpp

+7-4
Original file line numberDiff line numberDiff line change
@@ -325,17 +325,20 @@ int InvRef::l_room_for_item(lua_State *L)
325325
return 1;
326326
}
327327

328-
// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
329-
// Returns true if the list contains the given count of the given item name
328+
// contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false
329+
// Returns true if the list contains the given count of the given item
330330
int InvRef::l_contains_item(lua_State *L)
331331
{
332332
NO_MAP_LOCK_REQUIRED;
333333
InvRef *ref = checkobject(L, 1);
334334
const char *listname = luaL_checkstring(L, 2);
335335
ItemStack item = read_item(L, 3, getServer(L)->idef());
336336
InventoryList *list = getlist(L, ref, listname);
337-
if(list){
338-
lua_pushboolean(L, list->containsItem(item));
337+
bool match_meta = false;
338+
if (lua_isboolean(L, 4))
339+
match_meta = lua_toboolean(L, 4);
340+
if (list) {
341+
lua_pushboolean(L, list->containsItem(item, match_meta));
339342
} else {
340343
lua_pushboolean(L, false);
341344
}

‎src/script/lua_api/l_inventory.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class InvRef : public ModApiBase {
9393
// Returns true if the item completely fits into the list
9494
static int l_room_for_item(lua_State *L);
9595

96-
// contains_item(self, listname, itemstack or itemstring or table or nil) -> true/false
96+
// contains_item(self, listname, itemstack or itemstring or table or nil, [match_meta]) -> true/false
9797
// Returns true if the list contains the given count of the given item name
9898
static int l_contains_item(lua_State *L);
9999

0 commit comments

Comments
 (0)
Please sign in to comment.