Use color for named items
[MineClone/MineClone2.git] / mods / ITEMS / mcl_banners / init.lua
blob699063262374923766cb32933fea3c7d40c8477c
1 local node_sounds
2 if minetest.get_modpath("mcl_sounds") then
3 node_sounds = mcl_sounds.node_sound_wood_defaults()
4 end
6 -- Helper function
7 local function round(num, idp)
8 local mult = 10^(idp or 0)
9 return math.floor(num * mult + 0.5) / mult
10 end
12 mcl_banners = {}
14 mcl_banners.colors = {
15 -- Format:
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()
53 end
54 end
55 end
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()
65 end
66 end
67 end
69 local make_banner_texture = function(base_color, layers)
70 local colorize
71 if mcl_banners.colors[base_color] then
72 colorize = mcl_banners.colors[base_color][4]
73 end
74 if colorize then
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
79 if layers then
80 local finished_banner = base
81 for l=1, #layers do
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
90 end
91 return { finished_banner }
92 end
93 return { base }
94 else
95 return { "mcl_banners_banner_base.png" }
96 end
97 end
99 local on_rotate
100 if minetest.get_modpath("screwdriver") then
101 on_rotate = screwdriver.disallow
104 -- Banner nodes.
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.",
116 walkable = false,
117 is_ground_content = false,
118 paramtype = "light",
119 sunlight_propagates = true,
120 drawtype = "airlike",
121 inventory_image = "mcl_banners_item_base.png",
122 wield_image = "mcl_banners_item_base.png",
123 tiles = { "blank.png" },
124 selection_box = {type = "fixed", fixed= {-0.3, -0.5, -0.3, 0.3, 0.5, 0.3} },
125 groups = {axey=1,handy=1, attached_node = 1, not_in_creative_inventory = 1, not_in_craft_guide = 1, material_wood=1 },
126 stack_max = 16,
127 sounds = node_sounds,
128 drop = "", -- Item drops are handled in entity code
130 on_destruct = on_destruct_standing_banner,
131 _mcl_hardness = 1,
132 _mcl_blast_resistance = 5,
135 -- Hanging banner node
136 minetest.register_node("mcl_banners:hanging_banner", {
137 walkable = false,
138 is_ground_content = false,
139 paramtype = "light",
140 paramtype2 = "wallmounted",
141 sunlight_propagates = true,
142 drawtype = "airlike",
143 inventory_image = "mcl_banners_item_base.png",
144 wield_image = "mcl_banners_item_base.png",
145 tiles = { "blank.png" },
146 selection_box = {type = "wallmounted", wall_side = {-0.5, -0.5, -0.5, -4/16, 0.5, 0.5} },
147 groups = {axey=1,handy=1, attached_node = 1, not_in_creative_inventory = 1, not_in_craft_guide = 1, material_wood=1 },
148 stack_max = 16,
149 sounds = node_sounds,
150 drop = "", -- Item drops are handled in entity code
152 on_destruct = on_destruct_hanging_banner,
153 _mcl_hardness = 1,
154 _mcl_blast_resistance = 5,
155 on_rotate = on_rotate,
158 for colorid, colortab in pairs(mcl_banners.colors) do
159 local itemid = colortab[1]
160 local desc = colortab[2]
161 local wool = colortab[3]
162 local colorize = colortab[4]
164 local itemstring = "mcl_banners:banner_item_"..itemid
165 local inv
166 if colorize then
167 inv = "mcl_banners_item_base.png^(mcl_banners_item_overlay.png^[colorize:"..colorize..")"
168 else
169 inv = "mcl_banners_item_base.png^mcl_banners_item_overlay.png"
172 -- Banner items.
173 -- This is the player-visible banner item. It comes in 16 base colors.
174 -- The multiple items are really only needed for the different item images.
175 -- TODO: Combine the items into only 1 item.
176 minetest.register_craftitem(itemstring, {
177 description = desc,
178 _doc_items_create_entry = false,
179 inventory_image = inv,
180 wield_image = inv,
181 -- Banner group groups together the banner items, but not the nodes.
182 -- Used for crafting.
183 groups = { banner = 1, deco_block = 1, },
184 stack_max = 16,
186 on_place = function(itemstack, placer, pointed_thing)
187 local above = pointed_thing.above
188 local under = pointed_thing.under
190 local node_under = minetest.get_node(under)
191 if placer and not placer:get_player_control().sneak then
192 -- Use pointed node's on_rightclick function first, if present
193 if minetest.registered_nodes[node_under.name] and minetest.registered_nodes[node_under.name].on_rightclick then
194 return minetest.registered_nodes[node_under.name].on_rightclick(under, node_under, placer, itemstack) or itemstack
197 if minetest.get_modpath("mcl_cauldrons") then
198 -- Use banner on cauldron to remove the top-most layer. This reduces the water level by 1.
199 local new_node
200 if node_under.name == "mcl_cauldrons:cauldron_3" then
201 new_node = "mcl_cauldrons:cauldron_2"
202 elseif node_under.name == "mcl_cauldrons:cauldron_2" then
203 new_node = "mcl_cauldrons:cauldron_1"
204 elseif node_under.name == "mcl_cauldrons:cauldron_1" then
205 new_node = "mcl_cauldrons:cauldron"
206 elseif node_under.name == "mcl_cauldrons:cauldron_3r" then
207 new_node = "mcl_cauldrons:cauldron_2r"
208 elseif node_under.name == "mcl_cauldrons:cauldron_2r" then
209 new_node = "mcl_cauldrons:cauldron_1r"
210 elseif node_under.name == "mcl_cauldrons:cauldron_1r" then
211 new_node = "mcl_cauldrons:cauldron"
213 if new_node then
214 local imeta = itemstack:get_meta()
215 local layers_raw = imeta:get_string("layers")
216 local layers = minetest.deserialize(layers_raw)
217 if type(layers) == "table" and #layers > 0 then
218 table.remove(layers)
219 imeta:set_string("layers", minetest.serialize(layers))
220 local newdesc = mcl_banners.make_advanced_banner_description(itemstack:get_definition().description, layers)
221 local mname = imeta:get_string("name")
222 -- Don't change description if item has a name
223 if mname == "" then
224 imeta:set_string("description", newdesc)
228 -- Washing off reduces the water level by 1.
229 -- (It is possible to waste water if the banner had 0 layers.)
230 minetest.set_node(pointed_thing.under, {name=new_node})
232 -- Play sound (from mcl_potions mod)
233 minetest.sound_play("mcl_potions_bottle_pour", {pos=pointed_thing.under, gain=0.5, max_hear_range=16})
235 return itemstack
240 -- Place the node!
241 local hanging = false
243 -- Standing or hanging banner. The placement rules are enforced by the node definitions
244 local _, success = minetest.item_place_node(ItemStack("mcl_banners:standing_banner"), placer, pointed_thing)
245 if not success then
246 -- Forbidden on ceiling
247 if pointed_thing.under.y ~= pointed_thing.above.y then
248 return itemstack
250 _, success = minetest.item_place_node(ItemStack("mcl_banners:hanging_banner"), placer, pointed_thing)
251 if not success then
252 return itemstack
254 hanging = true
257 local place_pos
258 if minetest.registered_nodes[node_under.name].buildable_to then
259 place_pos = under
260 else
261 place_pos = above
263 if hanging then
264 place_pos = vector.add(place_pos, hanging_banner_entity_offset)
265 else
266 place_pos = vector.add(place_pos, standing_banner_entity_offset)
269 local banner
270 if hanging then
271 banner = minetest.add_entity(place_pos, "mcl_banners:hanging_banner")
272 else
273 banner = minetest.add_entity(place_pos, "mcl_banners:standing_banner")
275 local imeta = itemstack:get_meta()
276 local layers_raw = imeta:get_string("layers")
277 local layers = minetest.deserialize(layers_raw)
278 banner:get_luaentity():_set_textures(colorid, layers)
279 local mname = imeta:get_string("name")
280 if mname ~= nil and mname ~= "" then
281 banner:get_luaentity()._item_name = mname
282 banner:get_luaentity()._item_description = imeta:get_string("description")
285 -- Set rotation
286 local final_yaw
287 if hanging then
288 local pdir = vector.direction(pointed_thing.under, pointed_thing.above)
289 final_yaw = minetest.dir_to_yaw(pdir)
290 else
291 -- Determine the rotation based on player's yaw
292 local yaw = placer:get_look_horizontal()
293 -- Select one of 16 possible rotations (0-15)
294 local rotation_level = round((yaw / (math.pi*2)) * 16)
295 final_yaw = (rotation_level * (math.pi/8)) + math.pi
297 banner:set_yaw(final_yaw)
299 if not minetest.settings:get_bool("creative_mode") then
300 itemstack:take_item()
302 minetest.sound_play({name="default_place_node_hard", gain=1.0}, {pos = place_pos})
304 return itemstack
305 end,
307 _mcl_generate_description = function(itemstack)
308 local meta = itemstack:get_meta()
309 local layers_raw = meta:get_string("layers")
310 if not layers_raw then
311 return nil
313 local layers = minetest.deserialize(layers_raw)
314 local desc = itemstack:get_definition().description
315 local newdesc = mcl_banners.make_advanced_banner_description(desc, layers)
316 meta:set_string("description", newdesc)
317 return newdesc
318 end,
321 if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_wool") then
322 minetest.register_craft({
323 output = itemstring,
324 recipe = {
325 { wool, wool, wool },
326 { wool, wool, wool },
327 { "", "mcl_core:stick", "" },
332 if minetest.get_modpath("doc") then
333 -- Add item to node alias
334 doc.add_entry_alias("nodes", "mcl_banners:standing_banner", "craftitems", itemstring)
338 if minetest.get_modpath("doc") then
339 -- Add item to node alias
340 doc.add_entry_alias("nodes", "mcl_banners:standing_banner", "nodes", "mcl_banners:hanging_banner")
344 -- Banner entities.
345 local entity_standing = {
346 physical = false,
347 collide_with_objects = false,
348 visual = "mesh",
349 mesh = "amc_banner.b3d",
350 visual_size = { x=2.499, y=2.499 },
351 textures = make_banner_texture(),
352 collisionbox = { 0, 0, 0, 0, 0, 0 },
354 _base_color = nil, -- base color of banner
355 _layers = nil, -- table of layers painted over the base color.
356 -- This is a table of tables with each table having the following fields:
357 -- color: layer color ID (see colors table above)
358 -- pattern: name of pattern (see list above)
360 get_staticdata = function(self)
361 local out = { _base_color = self._base_color, _layers = self._layers, _name = self._name }
362 return minetest.serialize(out)
363 end,
364 on_activate = function(self, staticdata)
365 if staticdata and staticdata ~= "" then
366 local inp = minetest.deserialize(staticdata)
367 self._base_color = inp._base_color
368 self._layers = inp._layers
369 self._name = inp._name
370 self.object:set_properties({
371 textures = make_banner_texture(self._base_color, self._layers),
374 -- Make banner slowly swing
375 self.object:set_animation({x=0, y=80}, 25)
376 self.object:set_armor_groups({immortal=1})
377 end,
379 -- This is a custom function which causes the banner to be dropped as item and destroys the entity.
380 _drop = function(self)
381 local pos = self.object:getpos()
382 pos.y = pos.y + 1
384 if not minetest.settings:get_bool("creative_mode") and self._base_color then
385 -- Spawn item
386 local banner = ItemStack("mcl_banners:banner_item_"..mcl_banners.colors[self._base_color][1])
387 local meta = banner:get_meta()
388 meta:set_string("layers", minetest.serialize(self._layers))
389 if self._item_name ~= nil and self._item_name ~= "" then
390 meta:set_string("description", self._item_description)
391 meta:set_string("name", self._item_name)
392 else
393 meta:set_string("description", mcl_banners.make_advanced_banner_description(banner:get_definition().description, self._layers))
396 minetest.add_item(pos, banner)
399 -- Destroy entity
400 self.object:remove()
401 end,
403 -- Set the banner textures. This function can be used by external mods.
404 -- Meaning of parameters:
405 -- * self: Lua entity reference to entity.
406 -- * other parameters: Same meaning as in make_banner_texture
407 _set_textures = function(self, base_color, layers)
408 if base_color then
409 self._base_color = base_color
411 if layers then
412 self._layers = layers
414 self.object:set_properties({textures = make_banner_texture(self._base_color, self._layers)})
415 end,
417 minetest.register_entity("mcl_banners:standing_banner", entity_standing)
419 local entity_hanging = table.copy(entity_standing)
420 entity_hanging.mesh = "amc_banner_hanging.b3d"
421 minetest.register_entity("mcl_banners:hanging_banner", entity_hanging)
423 minetest.register_craft({
424 type = "fuel",
425 recipe = "group:banner",
426 burntime = 15,