Skip to content

Commit 324240a

Browse files
TurkeyMcMacsfence
authored andcommitted
Add node texture variants
1 parent bdaabad commit 324240a

29 files changed

+969
-199
lines changed

builtin/common/item_s.lua

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,25 @@ function core.strip_param2_color(param2, paramtype2)
180180
end
181181
end
182182

183+
function core.strip_param2_variant(param2, def)
184+
if not def or def.variant_count <= 1 or not def.param2_variant then
185+
return 0
186+
end
187+
local bf = def.param2_variant
188+
local right_mask = bit.lshift(1, bf.width) - 1
189+
return bit.band(bit.rshift(param2, bf.offset), right_mask) % def.variant_count
190+
end
191+
192+
function core.set_param2_variant(param2, variant, def)
193+
if not def or not def.param2_variant then
194+
return param2
195+
end
196+
local bf = def.param2_variant
197+
local mask = bit.lshift(bit.lshift(1, bf.width) - 1, bf.offset)
198+
local new_bits = bit.band(bit.lshift(variant, bf.offset), mask)
199+
return bit.bor(bit.band(param2, bit.bnot(mask)), new_bits)
200+
end
201+
183202
-- Content ID caching
184203

185204
local old_get_content_id = core.get_content_id

builtin/game/features.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ core.features = {
4646
biome_weights = true,
4747
particle_blend_clip = true,
4848
remove_item_match_meta = true,
49+
texture_variants = true,
4950
}
5051

5152
function core.has_feature(arg)

builtin/game/item.lua

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,18 @@ function core.get_node_drops(node, toolname)
5252
local ptype = def and def.paramtype2
5353
-- get color, if there is color (otherwise nil)
5454
local palette_index = core.strip_param2_color(param2, ptype)
55+
-- get variant (always a number)
56+
local variant = core.strip_param2_variant(param2, def)
5557
if drop == nil then
5658
-- default drop
59+
local itemstring = nodename
5760
if palette_index then
58-
local stack = ItemStack(nodename)
59-
stack:get_meta():set_int("palette_index", palette_index)
60-
return {stack:to_string()}
61+
itemstring = core.itemstring_with_palette(itemstring, palette_index)
6162
end
62-
return {nodename}
63+
if variant > 0 then
64+
itemstring = core.itemstring_with_variant(itemstring, variant)
65+
end
66+
return {itemstring}
6367
elseif type(drop) == "string" then
6468
-- itemstring drop
6569
return drop ~= "" and {drop} or {}
@@ -118,9 +122,10 @@ function core.get_node_drops(node, toolname)
118122
for _, add_item in ipairs(item.items) do
119123
-- add color, if necessary
120124
if item.inherit_color and palette_index then
121-
local stack = ItemStack(add_item)
122-
stack:get_meta():set_int("palette_index", palette_index)
123-
add_item = stack:to_string()
125+
add_item = core.itemstring_with_palette(add_item, palette_index)
126+
end
127+
if item.inherit_variant then
128+
add_item = core.itemstring_with_variant(add_item, variant)
124129
end
125130
got_items[#got_items+1] = add_item
126131
end
@@ -270,6 +275,13 @@ function core.item_place_node(itemstack, placer, pointed_thing, param2,
270275
end
271276
end
272277

278+
-- Transfer variant
279+
if not def.place_param2 and def.variant_count > 1 then
280+
local variant = math.min(math.max(math.floor(metatable.variant or 0), 0),
281+
def.variant_count - 1)
282+
newnode.param2 = core.set_param2_variant(newnode.param2, variant, def)
283+
end
284+
273285
-- Check if the node is attached and if it can be placed there
274286
local an = core.get_item_group(def.name, "attached_node")
275287
if an ~= 0 and
@@ -603,6 +615,12 @@ function core.itemstring_with_color(item, colorstring)
603615
return stack:to_string()
604616
end
605617

618+
function core.itemstring_with_variant(item, variant)
619+
local stack = ItemStack(item) -- convert to ItemStack
620+
stack:get_meta():set_string("variant", variant > 0 and variant or "")
621+
return stack:to_string()
622+
end
623+
606624
-- This is used to allow mods to redefine core.item_place and so on
607625
-- NOTE: This is not the preferred way. Preferred way is to provide enough
608626
-- callbacks to not require redefining global functions. -celeron55
@@ -624,6 +642,7 @@ core.nodedef_default = {
624642
-- name intentionally not defined here
625643
description = "",
626644
groups = {},
645+
variant_count = 1,
627646
inventory_image = "",
628647
wield_image = "",
629648
wield_scale = vector.new(1, 1, 1),
@@ -654,6 +673,7 @@ core.nodedef_default = {
654673
post_effect_color = {a=0, r=0, g=0, b=0},
655674
paramtype = "none",
656675
paramtype2 = "none",
676+
param2_variant = {width = 0, offset = 0},
657677
is_ground_content = true,
658678
sunlight_propagates = false,
659679
walkable = true,
@@ -679,6 +699,7 @@ core.craftitemdef_default = {
679699
-- name intentionally not defined here
680700
description = "",
681701
groups = {},
702+
variant_count = 1,
682703
inventory_image = "",
683704
wield_image = "",
684705
wield_scale = vector.new(1, 1, 1),
@@ -699,6 +720,7 @@ core.tooldef_default = {
699720
-- name intentionally not defined here
700721
description = "",
701722
groups = {},
723+
variant_count = 1,
702724
inventory_image = "",
703725
wield_image = "",
704726
wield_scale = vector.new(1, 1, 1),
@@ -719,6 +741,7 @@ core.noneitemdef_default = { -- This is used for the hand and unknown items
719741
-- name intentionally not defined here
720742
description = "",
721743
groups = {},
744+
variant_count = 1,
722745
inventory_image = "",
723746
wield_image = "",
724747
wield_scale = vector.new(1, 1, 1),

doc/lua_api.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,63 @@ core.register_node("default:dirt_with_grass", {
10941094
})
10951095
```
10961096

1097+
Variants
1098+
--------
1099+
1100+
Items can have "variants", which are numbered states that can determine certain
1101+
properties. The number of variants is specified with the item definition field
1102+
`variant_count`. Each variant is numbered from 0 to `variant_count` - 1. Every
1103+
item has at least one variant (numbered 0). Nodes can specify a part of param2
1104+
to read as the variant number by setting the `param2_variant` field to a
1105+
`BitField`.
1106+
1107+
The currently supported variant properties are:
1108+
* `tiles`
1109+
* `overlay_tiles`
1110+
* `special_tiles`
1111+
The `variants` table in the item definition is a mapping from
1112+
variant numbers to variant tables. These tables can include the aforementioned
1113+
fields to set the properties for particular variants. Variants not present in
1114+
the mapping default to the values of the aforementioned fields specified in the
1115+
item definition table. Old clients will receive only the variant 0 tiles.
1116+
1117+
Items with multiple variants can specify a variant number with the "variant" key
1118+
in their metadata. The absence of the key indicates a variant number of 0, and
1119+
this is the canonical representation of the 0 variant. The helper function
1120+
`core.itemstring_with_variant` is a shortcut for creating such itemstrings.
1121+
The variant is preserved when a node is placed or dug. Custom drops will inherit
1122+
the variant only if `inherit_variant` is set to `true` in their specification.
1123+
1124+
Example node with variants:
1125+
1126+
core.register_node("mod:grass", {
1127+
description = "Grass",
1128+
drawtype = "plantlike",
1129+
paramtype = "light",
1130+
sunlight_propagates = true,
1131+
walkable = false,
1132+
groups = {dig_immediate = 3},
1133+
-- There are 4 variants numbered 0 to 3.
1134+
variant_count = 4,
1135+
-- The lowest 2 bits store the variant number.
1136+
param2_variant = {width = 2, offset = 0},
1137+
-- These tiles will be used for variants not otherwise specified,
1138+
-- in this case variant 0.
1139+
tiles = {"mod_grass1.png"},
1140+
-- Tiles for variants 1, 2, and 3 are specified here.
1141+
variants = {
1142+
{tiles = {"mod_grass2.png"}},
1143+
{tiles = {"mod_grass3.png"}},
1144+
{tiles = {"mod_grass4.png"}},
1145+
},
1146+
drop = {
1147+
items = {
1148+
-- The seeds will inherit the variant of the grass.
1149+
{items = {"mod:seeds"}, inherit_variant = true},
1150+
}
1151+
}
1152+
})
1153+
10971154

10981155

10991156
Sounds
@@ -1976,6 +2033,13 @@ Exact pointing location (currently only `Raycast` supports these fields):
19762033
For entities with rotated selection boxes, this will be rotated properly
19772034
by the entity's rotation - it will always be in absolute world space.
19782035

2036+
`BitField`
2037+
----------
2038+
2039+
A `BitField` specifies a part of an unsigned integer whose bits represent
2040+
another unsigned integer. A `BitField` is represented as follows:
2041+
2042+
{width = <integer bit width>, offset = <offset from least significant bit>}
19792043

19802044

19812045

@@ -2700,6 +2764,8 @@ Some of the values in the key-value store are handled specially:
27002764
* `color`: A `ColorString`, which sets the stack's color.
27012765
* `palette_index`: If the item has a palette, this is used to get the
27022766
current color from the palette.
2767+
* `variant`: If the item has more than one variant, this is the variant number.
2768+
The canonical form of variant 0 is the absence of this key.
27032769
* `count_meta`: Replace the displayed count with any string.
27042770
* `count_alignment`: Set the alignment of the displayed count value. This is an
27052771
int value. The lowest 2 bits specify the alignment in x-direction, the 3rd and
@@ -5750,6 +5816,8 @@ Utilities
57505816
particle_blend_clip = true,
57515817
-- The `match_meta` optional parameter is available for `InvRef:remove_item()` (5.12.0)
57525818
remove_item_match_meta = true,
5819+
-- Node/Item texture variants is supported (5.12.0)
5820+
texture_variants = true,
57535821
}
57545822
```
57555823

@@ -6901,6 +6969,12 @@ Item handling
69016969
given `param2` value.
69026970
* Returns `nil` if the given `paramtype2` does not contain color
69036971
information.
6972+
* `core.strip_param2_variant(param2, def)`
6973+
* Returns the variant from `param2` with the given node definition `def`.
6974+
* Always returns a non-negative integer less than `def.variant_count`.
6975+
* `core.set_param2_variant(param2, variant, def)`
6976+
* Returns a modified `param2` with the variant bitfield set to `variant`
6977+
with the given node definition `def`.
69046978
* `core.get_node_drops(node, toolname)`
69056979
* Returns list of itemstrings that are dropped by `node` when dug
69066980
with the item `toolname` (not limited to tools).
@@ -6969,6 +7043,11 @@ Item handling
69697043
* `item`: the item stack which becomes colored. Can be in string,
69707044
table and native form.
69717045
* `colorstring`: the new color of the item stack
7046+
* `core.itemstring_with_variant(item, variant)`: returns an item string
7047+
* Creates an item string with an associated item variant.
7048+
* `item`: the item stack which is given the variant. Can be in string,
7049+
table or native form.
7050+
* `variant`: the new variant of the item stack
69727051

69737052
Rollback
69747053
--------
@@ -9696,6 +9775,9 @@ Used by `core.register_node`, `core.register_craftitem`, and
96969775
-- {bendy = 2, snappy = 1},
96979776
-- {hard = 1, metal = 1, spikes = 1}
96989777

9778+
variant_count = 1,
9779+
-- The number item variants, a positive integer.
9780+
96999781
inventory_image = "",
97009782
-- Texture shown in the inventory GUI
97019783
-- Defaults to a 3D rendering of the node if left empty.
@@ -9920,6 +10002,9 @@ Used by `core.register_node`.
992010002
{
992110003
-- <all fields allowed in item definitions>
992210004

10005+
param2_variant = BitField,
10006+
-- The part of param2 from which to read the variant number.
10007+
992310008
drawtype = "normal", -- See "Node drawtypes"
992410009

992510010
visual_scale = 1.0,
@@ -9933,17 +10018,45 @@ Used by `core.register_node`.
993310018
tiles = {tile definition 1, def2, def3, def4, def5, def6},
993410019
-- Textures of node; +Y, -Y, +X, -X, +Z, -Z
993510020
-- List can be shortened to needed length.
10021+
-- This field is also used for Variant number 0, see "Variants" for details.
993610022

993710023
overlay_tiles = {tile definition 1, def2, def3, def4, def5, def6},
993810024
-- Same as `tiles`, but these textures are drawn on top of the base
993910025
-- tiles. You can use this to colorize only specific parts of your
994010026
-- texture. If the texture name is an empty string, that overlay is not
994110027
-- drawn. Since such tiles are drawn twice, it is not recommended to use
994210028
-- overlays on very common nodes.
10029+
-- This field is also used for Variant number 0, see "Variants" for details.
994310030

994410031
special_tiles = {tile definition 1, Tile definition 2},
994510032
-- Special textures of node; used rarely.
994610033
-- List can be shortened to needed length.
10034+
-- This field is also used for Variant number 0, see "Variants" for details.
10035+
10036+
-- See "Variants"
10037+
-- This field is optional.
10038+
variants = {
10039+
-- Variant number 0 is created from fields
10040+
-- tiles, overlay_tiles and special_tiles
10041+
-- defined above (outside from variants table).
10042+
{ -- Variant number 1.
10043+
-- this field is reused from above definition if is not specified.
10044+
tiles = {tile definition 1, def2, def3, def4, def5, def6},
10045+
10046+
-- this field is reused from above definition if is not specified.
10047+
overlay_tiles = {def1, def2, def3, def4, def5, def6},
10048+
10049+
-- this field is reused from above definition if is not specified.
10050+
special_tiles = {def1, def2},
10051+
},
10052+
{ -- Variant number 2.
10053+
-- reuse tiles and special_tiles from variant number 0
10054+
-- no overlay_tiles
10055+
overlay_tiles = {},
10056+
...
10057+
},
10058+
...
10059+
},
994710060

994810061
color = ColorSpec,
994910062
-- The node's original color will be multiplied with this color.
@@ -10221,6 +10334,9 @@ Used by `core.register_node`.
1022110334
-- hardware coloring palette color from the dug node.
1022210335
-- Default is 'false'.
1022310336
inherit_color = true,
10337+
-- variant of the dug node.
10338+
-- Default is 'false'.
10339+
inherit_variant = true,
1022410340
},
1022510341
{
1022610342
-- Only drop if using an item whose name contains

games/devtest/mods/testnodes/drawtypes.lua

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,23 @@ core.register_node("testnodes:normal", {
2525
groups = { dig_immediate = 3 },
2626
})
2727

28+
-- A regular cube with tiles using color
29+
core.register_node("testnodes:normal_with_color", {
30+
description = S("\"normal\" Drawtype Test Node").."\n"..
31+
S("Opaque texture with color parameter"),
32+
drawtype = "normal",
33+
tiles = {
34+
{ name = "testnodes_normal.png", color = "#A00" },
35+
{ name = "testnodes_normal.png", color = "#00B" },
36+
{ name = "testnodes_normal.png", color = "#0C0" },
37+
{ name = "testnodes_normal.png", color = "#DD0" },
38+
{ name = "testnodes_normal.png", color = "#0EE" },
39+
{ name = "testnodes_normal.png", color = "#F0F" },
40+
},
41+
42+
groups = { dig_immediate = 3 },
43+
})
44+
2845
-- Standard glasslike node
2946
core.register_node("testnodes:glasslike", {
3047
description = S("\"glasslike\" Drawtype Test Node").."\n"..

games/devtest/mods/testnodes/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ dofile(path.."/liquids.lua")
1010
dofile(path.."/light.lua")
1111
dofile(path.."/textures.lua")
1212
dofile(path.."/overlays.lua")
13+
dofile(path.."/variants.lua")
1314
dofile(path.."/commands.lua")

games/devtest/mods/testnodes/overlays.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ core.register_node("testnodes:overlay", {
77
overlay_tiles = {{name = "testnodes_overlay.png"}},
88
groups = { dig_immediate = 2 },
99
})
10+
core.register_node("testnodes:overlay_tile_colors", {
11+
description = S("Texture Overlay Test Node, Tile Colors") .. "\n" ..
12+
S("Uncolorized"),
13+
tiles = {{name = "testnodes_overlayable.png"}},
14+
overlay_tiles = {
15+
{name = "testnodes_overlay.png", color = "#F00"},
16+
{name = "testnodes_overlay.png", color = "#0F0"},
17+
{name = "testnodes_overlay.png", color = "#00F"},
18+
{name = "testnodes_overlay.png", color = "#FF0"},
19+
{name = "testnodes_overlay.png", color = "#0FF"},
20+
{name = "testnodes_overlay.png", color = "#F0F"},
21+
},
22+
groups = { dig_immediate = 2 },
23+
})
1024
core.register_node("testnodes:overlay_color_all", {
1125
description = S("Texture Overlay Test Node, Colorized") .. "\n" ..
1226
S("param2 changes color"),

0 commit comments

Comments
 (0)