2 if minetest
.get_modpath("mcl_sounds") then
3 node_sounds
= mcl_sounds
.node_sound_wood_defaults()
7 local function round(num
, idp
)
8 local mult
= 10^
(idp
or 0)
9 return math
.floor(num
* mult
+ 0.5) / mult
14 mcl_banners
.colors
= {
16 -- [ID] = { banner description, wool, unified dyes color group, overlay color, dye, color name for emblazonings }
17 ["unicolor_white"] = {"white", "White Banner", "mcl_wool:white", "#FFFFFF", "mcl_dye:white", "White" },
18 ["unicolor_darkgrey"] = {"grey", "Grey Banner", "mcl_wool:grey", "#303030", "mcl_dye:dark_grey", "Grey" },
19 ["unicolor_grey"] = {"silver", "Light Grey Banner", "mcl_wool:silver", "#5B5B5B", "mcl_dye:grey", "Light Grey" },
20 ["unicolor_black"] = {"black", "Black Banner", "mcl_wool:black", "#000000", "mcl_dye:black", "Black" },
21 ["unicolor_red"] = {"red", "Red Banner", "mcl_wool:red", "#BC0000", "mcl_dye:red", "Red" },
22 ["unicolor_yellow"] = {"yellow", "Yellow Banner", "mcl_wool:yellow", "#E6CD00", "mcl_dye:yellow", "Yellow" },
23 ["unicolor_dark_green"] = {"green", "Green Banner", "mcl_wool:green", "#006000", "mcl_dye:dark_green", "Green" },
24 ["unicolor_cyan"] = {"cyan", "Cyan Banner", "mcl_wool:cyan", "#00ACAC", "mcl_dye:cyan", "Cyan" },
25 ["unicolor_blue"] = {"blue", "Blue Banner", "mcl_wool:blue", "#0000AC", "mcl_dye:blue", "Blue" },
26 ["unicolor_red_violet"] = {"magenta", "Magenta Banner", "mcl_wool:magenta", "#AC007C", "mcl_dye:magenta", "Magenta"},
27 ["unicolor_orange"] = {"orange", "Orange Banner", "mcl_wool:orange", "#E67300", "mcl_dye:orange", "Orange" },
28 ["unicolor_violet"] = {"purple", "Purple Banner", "mcl_wool:purple", "#6400AC", "mcl_dye:violet", "Violet" },
29 ["unicolor_brown"] = {"brown", "Brown Banner", "mcl_wool:brown", "#603000", "mcl_dye:brown", "Brown" },
30 ["unicolor_pink"] = {"pink", "Pink Banner", "mcl_wool:pink", "#DE557C", "mcl_dye:pink", "Pink" },
31 ["unicolor_lime"] = {"lime", "Lime Banner", "mcl_wool:lime", "#30AC00", "mcl_dye:green", "Lime" },
32 ["unicolor_light_blue"] = {"light_blue", "Light Blue Banner", "mcl_wool:light_blue", "#4040CF", "mcl_dye:lightblue", "Light Blue" },
35 -- Add pattern/emblazoning crafting recipes
36 dofile(minetest
.get_modpath("mcl_banners").."/patterncraft.lua")
38 -- Overlay ratios (0-255)
39 local base_color_ratio
= 224
40 local layer_ratio
= 255
42 local standing_banner_entity_offset
= { x
=0, y
=-0.499, z
=0 }
43 local hanging_banner_entity_offset
= { x
=0, y
=-1.7, z
=0 }
45 local on_destruct_standing_banner
= function(pos
)
46 -- Find this node's banner entity and make it drop as an item
47 local checkpos
= vector
.add(pos
, standing_banner_entity_offset
)
48 local objects
= minetest
.get_objects_inside_radius(checkpos
, 0.5)
49 for _
, v
in ipairs(objects
) do
50 local ent
= v
:get_luaentity()
51 if ent
and ent
.name
== "mcl_banners:standing_banner" then
52 v
:get_luaentity():_drop()
57 local on_destruct_hanging_banner
= function(pos
)
58 -- Find this node's banner entity and make it drop as an item
59 local checkpos
= vector
.add(pos
, hanging_banner_entity_offset
)
60 local objects
= minetest
.get_objects_inside_radius(checkpos
, 0.5)
61 for _
, v
in ipairs(objects
) do
62 local ent
= v
:get_luaentity()
63 if ent
and ent
.name
== "mcl_banners:hanging_banner" then
64 v
:get_luaentity():_drop()
69 local make_banner_texture
= function(base_color
, layers
)
71 if mcl_banners
.colors
[base_color
] then
72 colorize
= mcl_banners
.colors
[base_color
][4]
75 -- Base texture with base color
76 local base
= "(mcl_banners_banner_base.png^[mask:mcl_banners_base_inverted.png)^((mcl_banners_banner_base.png^[colorize:"..colorize
..":"..base_color_ratio
..")^[mask:mcl_banners_base.png)"
78 -- Optional pattern layers
80 local finished_banner
= base
82 local layerinfo
= layers
[l
]
83 local pattern
= "mcl_banners_" .. layerinfo
.pattern
.. ".png"
84 local color
= mcl_banners
.colors
[layerinfo
.color
][4]
86 -- Generate layer texture
87 local layer
= "(("..pattern
.."^[colorize:"..color
..":"..layer_ratio
..")^[mask:"..pattern
..")"
89 finished_banner
= finished_banner
.. "^" .. layer
91 return { finished_banner
}
95 return { "mcl_banners_banner_base.png" }
100 if minetest
.get_modpath("screwdriver") then
101 on_rotate
= screwdriver
.disallow
105 -- These are an invisible nodes which are only used to destroy the banner entity.
106 -- All the important banner information (such as color) is stored in the entity.
107 -- It is used only used internally.
109 -- Standing banner node
110 -- This one is also used for the help entry to avoid spamming the help with 16 entries.
111 minetest
.register_node("mcl_banners:standing_banner", {
112 _doc_items_entry_name
= "Banner",
113 _doc_items_image
= "mcl_banners_item_base.png^mcl_banners_item_overlay.png",
114 _doc_items_longdesc
= "Banners are tall colorful decorative blocks. They can be placed on the floor and at walls. Banners can be emblazoned with a variety of patterns using a lot of dye in crafting.",
115 _doc_items_usagehelp
= "Use crafting to draw a pattern on top of the banner. Emblazoned banners can be emblazoned again to combine various patterns. You can draw up to 6 layers on a banner that way. You can copy the pattern of a banner by placing two banners of the same color in the crafting grid—one needs to be emblazoned, the other one must be clean. Finally, you can use a banner on a cauldron with water to wash off its top-most layer.",
117 is_ground_content
= false,
119 sunlight_propagates
= true,
120 drawtype
= "nodebox",
121 -- Nodebox is drawn as fallback when the entity is missing, so that the
122 -- banner node is never truly invisible.
123 -- If the entity is drawn, the nodebox disappears within the real banner mesh.
126 fixed
= { -1/32, -0.49, -1/32, 1/32, 1.49, 1/32 },
128 -- This texture is based on the banner base texture
129 tiles
= { "mcl_banners_fallback_wood.png" },
131 inventory_image
= "mcl_banners_item_base.png",
132 wield_image
= "mcl_banners_item_base.png",
134 selection_box
= {type = "fixed", fixed
= {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3} },
135 groups
= {axey
=1,handy
=1, attached_node
= 1, not_in_creative_inventory
= 1, not_in_craft_guide
= 1, material_wood
=1 },
137 sounds
= node_sounds
,
138 drop
= "", -- Item drops are handled in entity code
140 on_destruct
= on_destruct_standing_banner
,
142 _mcl_blast_resistance
= 5,
145 -- Hanging banner node
146 minetest
.register_node("mcl_banners:hanging_banner", {
148 is_ground_content
= false,
150 paramtype2
= "wallmounted",
151 sunlight_propagates
= true,
152 drawtype
= "nodebox",
153 inventory_image
= "mcl_banners_item_base.png",
154 wield_image
= "mcl_banners_item_base.png",
155 tiles
= { "mcl_banners_fallback_wood.png" },
157 type = "wallmounted",
158 wall_side
= { -0.49, 0.41, -0.49, -0.41, 0.49, 0.49 },
159 wall_top
= { -0.49, 0.41, -0.49, -0.41, 0.49, 0.49 },
160 wall_bottom
= { -0.49, -0.49, -0.49, -0.41, -0.41, 0.49 },
162 selection_box
= {type = "wallmounted", wall_side
= {-0.5, -0.5, -0.5, -4/16, 0.5, 0.5} },
163 groups
= {axey
=1,handy
=1, attached_node
= 1, not_in_creative_inventory
= 1, not_in_craft_guide
= 1, material_wood
=1 },
165 sounds
= node_sounds
,
166 drop
= "", -- Item drops are handled in entity code
168 on_destruct
= on_destruct_hanging_banner
,
170 _mcl_blast_resistance
= 5,
171 on_rotate
= on_rotate
,
174 for colorid
, colortab
in pairs(mcl_banners
.colors
) do
175 local itemid
= colortab
[1]
176 local desc
= colortab
[2]
177 local wool
= colortab
[3]
178 local colorize
= colortab
[4]
180 local itemstring
= "mcl_banners:banner_item_"..itemid
183 inv
= "mcl_banners_item_base.png^(mcl_banners_item_overlay.png^[colorize:"..colorize
..")"
185 inv
= "mcl_banners_item_base.png^mcl_banners_item_overlay.png"
189 -- This is the player-visible banner item. It comes in 16 base colors.
190 -- The multiple items are really only needed for the different item images.
191 -- TODO: Combine the items into only 1 item.
192 minetest
.register_craftitem(itemstring
, {
194 _doc_items_create_entry
= false,
195 inventory_image
= inv
,
197 -- Banner group groups together the banner items, but not the nodes.
198 -- Used for crafting.
199 groups
= { banner
= 1, deco_block
= 1, },
202 on_place
= function(itemstack
, placer
, pointed_thing
)
203 local above
= pointed_thing
.above
204 local under
= pointed_thing
.under
206 local node_under
= minetest
.get_node(under
)
207 if placer
and not placer
:get_player_control().sneak
then
208 -- Use pointed node's on_rightclick function first, if present
209 if minetest
.registered_nodes
[node_under
.name
] and minetest
.registered_nodes
[node_under
.name
].on_rightclick
then
210 return minetest
.registered_nodes
[node_under
.name
].on_rightclick(under
, node_under
, placer
, itemstack
) or itemstack
213 if minetest
.get_modpath("mcl_cauldrons") then
214 -- Use banner on cauldron to remove the top-most layer. This reduces the water level by 1.
216 if node_under
.name
== "mcl_cauldrons:cauldron_3" then
217 new_node
= "mcl_cauldrons:cauldron_2"
218 elseif node_under
.name
== "mcl_cauldrons:cauldron_2" then
219 new_node
= "mcl_cauldrons:cauldron_1"
220 elseif node_under
.name
== "mcl_cauldrons:cauldron_1" then
221 new_node
= "mcl_cauldrons:cauldron"
222 elseif node_under
.name
== "mcl_cauldrons:cauldron_3r" then
223 new_node
= "mcl_cauldrons:cauldron_2r"
224 elseif node_under
.name
== "mcl_cauldrons:cauldron_2r" then
225 new_node
= "mcl_cauldrons:cauldron_1r"
226 elseif node_under
.name
== "mcl_cauldrons:cauldron_1r" then
227 new_node
= "mcl_cauldrons:cauldron"
230 local imeta
= itemstack
:get_meta()
231 local layers_raw
= imeta
:get_string("layers")
232 local layers
= minetest
.deserialize(layers_raw
)
233 if type(layers
) == "table" and #layers
> 0 then
235 imeta
:set_string("layers", minetest
.serialize(layers
))
236 local newdesc
= mcl_banners
.make_advanced_banner_description(itemstack
:get_definition().description
, layers
)
237 local mname
= imeta
:get_string("name")
238 -- Don't change description if item has a name
240 imeta
:set_string("description", newdesc
)
244 -- Washing off reduces the water level by 1.
245 -- (It is possible to waste water if the banner had 0 layers.)
246 minetest
.set_node(pointed_thing
.under
, {name
=new_node
})
248 -- Play sound (from mcl_potions mod)
249 minetest
.sound_play("mcl_potions_bottle_pour", {pos
=pointed_thing
.under
, gain
=0.5, max_hear_range
=16})
257 local hanging
= false
259 -- Standing or hanging banner. The placement rules are enforced by the node definitions
260 local _
, success
= minetest
.item_place_node(ItemStack("mcl_banners:standing_banner"), placer
, pointed_thing
)
262 -- Forbidden on ceiling
263 if pointed_thing
.under
.y
~= pointed_thing
.above
.y
then
266 _
, success
= minetest
.item_place_node(ItemStack("mcl_banners:hanging_banner"), placer
, pointed_thing
)
274 if minetest
.registered_nodes
[node_under
.name
].buildable_to
then
280 place_pos
= vector
.add(place_pos
, hanging_banner_entity_offset
)
282 place_pos
= vector
.add(place_pos
, standing_banner_entity_offset
)
287 banner
= minetest
.add_entity(place_pos
, "mcl_banners:hanging_banner")
289 banner
= minetest
.add_entity(place_pos
, "mcl_banners:standing_banner")
291 local imeta
= itemstack
:get_meta()
292 local layers_raw
= imeta
:get_string("layers")
293 local layers
= minetest
.deserialize(layers_raw
)
294 banner
:get_luaentity():_set_textures(colorid
, layers
)
295 local mname
= imeta
:get_string("name")
296 if mname
~= nil and mname
~= "" then
297 banner
:get_luaentity()._item_name
= mname
298 banner
:get_luaentity()._item_description
= imeta
:get_string("description")
304 local pdir
= vector
.direction(pointed_thing
.under
, pointed_thing
.above
)
305 final_yaw
= minetest
.dir_to_yaw(pdir
)
307 -- Determine the rotation based on player's yaw
308 local yaw
= placer
:get_look_horizontal()
309 -- Select one of 16 possible rotations (0-15)
310 local rotation_level
= round((yaw
/ (math
.pi
*2)) * 16)
311 final_yaw
= (rotation_level
* (math
.pi
/8)) + math
.pi
313 banner
:set_yaw(final_yaw
)
315 if not minetest
.settings
:get_bool("creative_mode") then
316 itemstack
:take_item()
318 minetest
.sound_play({name
="default_place_node_hard", gain
=1.0}, {pos
= place_pos
})
323 _mcl_generate_description
= function(itemstack
)
324 local meta
= itemstack
:get_meta()
325 local layers_raw
= meta
:get_string("layers")
326 if not layers_raw
then
329 local layers
= minetest
.deserialize(layers_raw
)
330 local desc
= itemstack
:get_definition().description
331 local newdesc
= mcl_banners
.make_advanced_banner_description(desc
, layers
)
332 meta
:set_string("description", newdesc
)
337 if minetest
.get_modpath("mcl_core") and minetest
.get_modpath("mcl_wool") then
338 minetest
.register_craft({
341 { wool
, wool
, wool
},
342 { wool
, wool
, wool
},
343 { "", "mcl_core:stick", "" },
348 if minetest
.get_modpath("doc") then
349 -- Add item to node alias
350 doc
.add_entry_alias("nodes", "mcl_banners:standing_banner", "craftitems", itemstring
)
354 if minetest
.get_modpath("doc") then
355 -- Add item to node alias
356 doc
.add_entry_alias("nodes", "mcl_banners:standing_banner", "nodes", "mcl_banners:hanging_banner")
361 local entity_standing
= {
363 collide_with_objects
= false,
365 mesh
= "amc_banner.b3d",
366 visual_size
= { x
=2.499, y
=2.499 },
367 textures
= make_banner_texture(),
368 collisionbox
= { 0, 0, 0, 0, 0, 0 },
370 _base_color
= nil, -- base color of banner
371 _layers
= nil, -- table of layers painted over the base color.
372 -- This is a table of tables with each table having the following fields:
373 -- color: layer color ID (see colors table above)
374 -- pattern: name of pattern (see list above)
376 get_staticdata
= function(self
)
377 local out
= { _base_color
= self
._base_color
, _layers
= self
._layers
, _name
= self
._name
}
378 return minetest
.serialize(out
)
380 on_activate
= function(self
, staticdata
)
381 if staticdata
and staticdata
~= "" then
382 local inp
= minetest
.deserialize(staticdata
)
383 self
._base_color
= inp
._base_color
384 self
._layers
= inp
._layers
385 self
._name
= inp
._name
386 self
.object
:set_properties({
387 textures
= make_banner_texture(self
._base_color
, self
._layers
),
390 -- Make banner slowly swing
391 self
.object
:set_animation({x
=0, y
=80}, 25)
392 self
.object
:set_armor_groups({immortal
=1})
395 -- This is a custom function which causes the banner to be dropped as item and destroys the entity.
396 _drop
= function(self
)
397 local pos
= self
.object
:get_pos()
400 if not minetest
.settings
:get_bool("creative_mode") and self
._base_color
then
402 local banner
= ItemStack("mcl_banners:banner_item_"..mcl_banners
.colors
[self
._base_color
][1])
403 local meta
= banner
:get_meta()
404 meta
:set_string("layers", minetest
.serialize(self
._layers
))
405 if self
._item_name
~= nil and self
._item_name
~= "" then
406 meta
:set_string("description", self
._item_description
)
407 meta
:set_string("name", self
._item_name
)
409 meta
:set_string("description", mcl_banners
.make_advanced_banner_description(banner
:get_definition().description
, self
._layers
))
412 minetest
.add_item(pos
, banner
)
419 -- Set the banner textures. This function can be used by external mods.
420 -- Meaning of parameters:
421 -- * self: Lua entity reference to entity.
422 -- * other parameters: Same meaning as in make_banner_texture
423 _set_textures
= function(self
, base_color
, layers
)
425 self
._base_color
= base_color
428 self
._layers
= layers
430 self
.object
:set_properties({textures
= make_banner_texture(self
._base_color
, self
._layers
)})
433 minetest
.register_entity("mcl_banners:standing_banner", entity_standing
)
435 local entity_hanging
= table.copy(entity_standing
)
436 entity_hanging
.mesh
= "amc_banner_hanging.b3d"
437 minetest
.register_entity("mcl_banners:hanging_banner", entity_hanging
)
439 minetest
.register_craft({
441 recipe
= "group:banner",