1 -- Pattern crafting. This file contains the code for crafting all the
2 -- emblazonings you can put on the banners. It's quite complicated;
3 -- normal 08/15 crafting won't work here.
5 -- Maximum number of layers which can be put on a banner by crafting.
6 local max_layers_crafting
= 6
8 -- Max. number lines in the descriptions for the banner layers.
9 -- This is done to avoid huge tooltips.
10 local max_layer_lines
= 6
12 -- List of patterns with crafting rules
13 local d
= "group:dye" -- dye
14 local e
= "" -- empty slot (one of them must contain the banner)
25 { e
, "mcl_core:brick_block", d
},
34 name
= "%s Creeper Charge",
36 { e
, "mcl_heads:creeper", d
},
45 name
= "%s Bordure Indented",
47 { e
, "mcl_core:vine", d
},
49 ["diagonal_up_left"] = {
50 name
= "%s Per Bend Inverted",
55 ["diagonal_up_right"] = {
56 name
= "%s Per Bend Sinister Inverted",
61 ["diagonal_right"] = {
68 name
= "%s Per Bend Sinister",
74 name
= "%s Flower Charge",
76 { e
, "mcl_flowers:oxeye_daisy", d
},
85 name
= "%s Base Gradient",
90 ["half_horizontal_bottom"] = {
91 name
= "%s Per Fess Inverted",
96 ["half_horizontal"] = {
102 ["half_vertical"] = {
103 name
= "%s Per Pale",
108 ["half_vertical_right"] = {
109 name
= "%s Per Pale Inverted",
115 -- Symbol used for the “Thing”: U+1F65D 🙝
117 name
= "%s Thing Charge",
119 -- TODO: Replace with enchanted golden apple
120 { e
, "mcl_core:apple_gold", d
},
129 name
= "%s Skull Charge",
131 { e
, "mcl_heads:wither_skeleton", d
},
133 ["small_stripes"] = {
139 ["square_bottom_left"] = {
140 name
= "%s Base Dexter Canton",
145 ["square_bottom_right"] = {
146 name
= "%s Base Sinister Canton",
151 ["square_top_left"] = {
152 name
= "%s Chief Dexter Canton",
157 ["square_top_right"] = {
158 name
= "%s Chief Sinister Canton",
163 ["straight_cross"] = {
169 ["stripe_bottom"] = {
175 ["stripe_center"] = {
181 ["stripe_downleft"] = {
182 name
= "%s Bend Sinister",
187 ["stripe_downright"] = {
194 name
= "%s Pale Dexter",
199 ["stripe_middle"] = {
206 name
= "%s Pale Sinister",
217 ["triangle_bottom"] = {
224 name
= "%s Chevron Inverted",
229 ["triangles_bottom"] = {
230 name
= "%s Base Indented",
235 ["triangles_top"] = {
236 name
= "%s Chief Indented",
243 -- Just a simple reverse-lookup table from dye itemstring to banner color ID
244 -- to avoid some pointless future iterations.
245 local dye_to_colorid_mapping
= {}
246 for colorid
, colortab
in pairs(mcl_banners
.colors
) do
247 dye_to_colorid_mapping
[colortab
[5]]
= colorid
250 -- Create a banner description containing all the layer names
251 mcl_banners
.make_advanced_banner_description
= function(description
, layers
)
252 if layers
== nil or #layers
== 0 then
253 -- No layers, revert to default
256 local layerstrings
= {}
258 -- Prevent excess length description
259 if l
> max_layer_lines
then
263 local color
= mcl_banners
.colors
[layers
[l
].color
][6]
264 local pattern_name
= patterns
[layers
[l
].pattern
].name
265 -- The pattern name is a format string (e.g. “%s Base”)
266 table.insert(layerstrings
, string.format(pattern_name
, color
))
268 -- Warn about missing information
269 if #layers
== max_layer_lines
+ 1 then
270 table.insert(layerstrings
, "And one addional layer")
271 elseif #layers
> max_layer_lines
+ 1 then
272 table.insert(layerstrings
, string.format("And %d addional layers", #layers
- max_layer_lines
))
275 -- Final string concatenations: Just a list of strings
276 local append
= table.concat(layerstrings
, "\n")
277 description
= description
.. "\n" .. core
.colorize("#8F8F8F", append
)
282 --[[ This is for handling all those complex pattern crafting recipes.
283 Parameters same as for minetest.register_craft_predict.
284 craft_predict is set true when called from minetest.craft_preview, in this case, this function
285 MUST NOT change the crafting grid.
287 local banner_pattern_craft
= function(itemstack
, player
, old_craft_grid
, craft_inv
, craft_predict
)
288 if minetest
.get_item_group(itemstack
:get_name(), "banner") ~= 1 then
292 --[[ Basic item checks: Banners and dyes ]]
293 local banner
-- banner item
294 local banner2
-- second banner item (used when copying)
295 local dye
-- itemstring of the dye being used
296 local banner_index
-- crafting inventory index of the banner
298 for i
= 1, player
:get_inventory():get_size("craft") do
299 local itemname
= old_craft_grid
[i
]:get_name()
300 if minetest
.get_item_group(itemname
, "banner") == 1 then
302 banner
= old_craft_grid
[i
]
304 elseif not banner2
then
305 banner2
= old_craft_grid
[i
]
310 -- Check if all dyes are equal
311 elseif minetest
.get_item_group(itemname
, "dye") == 1 then
314 elseif itemname
~= dye
then
325 -- Two banners found: This means copying!
327 local b1meta
= banner
:get_meta()
328 local b2meta
= banner2
:get_meta()
329 local b1layers_raw
= b1meta
:get_string("layers")
330 local b2layers_raw
= b2meta
:get_string("layers")
331 local b1layers
= minetest
.deserialize(b1layers_raw
)
332 local b2layers
= minetest
.deserialize(b2layers_raw
)
333 if type(b1layers
) ~= "table" then
336 if type(b2layers
) ~= "table" then
340 -- For copying to be allowed, one banner has to have no layers while the other one has at least 1 layer.
341 -- The banner with layers will be used as a source.
342 local src_banner
, src_layers
, src_layers_raw
, src_desc
, src_index
343 if #b1layers
== 0 and #b2layers
> 0 then
345 src_layers
= b2layers
346 src_layers_raw
= b2layers_raw
347 src_desc
= minetest
.registered_items
[src_banner
:get_name()].description
348 src_index
= banner2_index
349 elseif #b2layers
== 0 and #b1layers
> 0 then
351 src_layers
= b1layers
352 src_layers_raw
= b1layers_raw
353 src_desc
= minetest
.registered_items
[src_banner
:get_name()].description
354 src_index
= banner_index
359 -- Set output metadata
360 local imeta
= itemstack
:get_meta()
361 imeta
:set_string("layers", src_layers_raw
)
362 -- Generate new description. This clears any (anvil) name from the original banners.
363 imeta
:set_string("description", mcl_banners
.make_advanced_banner_description(src_desc
, src_layers
))
365 if not craft_predict
then
366 -- Don't destroy source banner so this recipe is a true copy
367 craft_inv
:set_stack("craft", src_index
, src_banner
)
373 -- No two banners found
374 -- From here on we check which banner pattern should be added
376 --[[ Check patterns ]]
379 local ometa
= banner
:get_meta()
380 local layers_raw
= ometa
:get_string("layers")
381 local layers
= minetest
.deserialize(layers_raw
)
382 if type(layers
) ~= "table" then
385 -- Disallow crafting when a certain number of layers is reached or exceeded
386 if #layers
>= max_layers_crafting
then
390 local matching_pattern
391 local max_i
= player
:get_inventory():get_size("craft")
392 -- Find the matching pattern
393 for pattern_name
, pattern
in pairs(patterns
) do
395 if pattern
.type == nil then
396 local pattern_ok
= true
398 -- This complex code just iterates through the pattern slots one-by-one and compares them with the pattern
400 local row
= pattern
[p
]
402 local itemname
= old_craft_grid
[inv_i
]:get_name()
404 if (pitem
== d
and minetest
.get_item_group(itemname
, "dye") == 0) or (pitem
== e
and itemname
~= e
and inv_i
~= banner_index
) then
410 if inv_i
> max_i
then
414 if inv_i
> max_i
then
418 -- Everything matched! We found our pattern!
420 matching_pattern
= pattern_name
424 elseif pattern
.type == "shapeless" then
425 local orig
= pattern
[1]
426 local no_mismatches_so_far
= true
427 -- This code compares the craft grid with the required items
429 local item_ok
= false
431 local itemname
= old_craft_grid
[i
]:get_name()
432 if (orig
[o
] == e
) or -- Empty slot: Always wins
433 (orig
[o
] ~= e
and orig
[o
] == itemname
) or -- non-empty slot: Exact item match required
434 (orig
[o
] == d
and minetest
.get_item_group(itemname
, "dye") == 1) then -- Dye slot
439 -- Sorry, item not found. :-(
441 no_mismatches_so_far
= false
445 -- Ladies and Gentlemen, we have a winner!
446 if no_mismatches_so_far
then
447 matching_pattern
= pattern_name
452 if matching_pattern
then
456 if not matching_pattern
then
460 -- Add the new layer and update other metadata
461 local color
= dye_to_colorid_mapping
[dye
]
462 table.insert(layers
, {pattern
=matching_pattern
, color
=color
})
464 local imeta
= itemstack
:get_meta()
465 imeta
:set_string("layers", minetest
.serialize(layers
))
467 local mname
= ometa
:get_string("name")
468 -- Only change description if banner does not have a name
470 local odesc
= itemstack
:get_definition().description
471 local description
= mcl_banners
.make_advanced_banner_description(odesc
, layers
)
472 imeta
:set_string("description", description
)
474 imeta
:set_string("description", mname
)
475 imeta
:set_string("name", mname
)
480 minetest
.register_craft_predict(function(itemstack
, player
, old_craft_grid
, craft_inv
)
481 return banner_pattern_craft(itemstack
, player
, old_craft_grid
, craft_inv
, true)
483 minetest
.register_on_craft(function(itemstack
, player
, old_craft_grid
, craft_inv
)
484 return banner_pattern_craft(itemstack
, player
, old_craft_grid
, craft_inv
, false)
487 -- Register crafting recipes for all the patterns
488 for pattern_name
, pattern
in pairs(patterns
) do
489 -- Shaped and fixed recipes
490 if pattern
.type == nil then
491 for colorid
, colortab
in pairs(mcl_banners
.colors
) do
492 local banner
= "mcl_banners:banner_item_"..colortab
[1]
493 local bannered
= false
495 for row_id
=1, #pattern
do
496 local row
= pattern
[row_id
]
499 if row
[r
] == e
and not bannered
then
506 table.insert(recipe
, newrow
)
508 minetest
.register_craft({
514 elseif pattern
.type == "shapeless" then
515 for colorid
, colortab
in pairs(mcl_banners
.colors
) do
516 local banner
= "mcl_banners:banner_item_"..colortab
[1]
517 local orig
= pattern
[1]
527 minetest
.register_craft({
536 -- Register crafting recipe for copying the banner pattern
537 for colorid
, colortab
in pairs(mcl_banners
.colors
) do
538 local banner
= "mcl_banners:banner_item_"..colortab
[1]
539 minetest
.register_craft({
542 recipe
= { banner
, banner
},