5 -- Directory delimeter fallback (normally comes from builtin)
9 local export_path_full
= table.concat({minetest
.get_worldpath(), "schems"}, DIR_DELIM
)
11 local text_color
= "#D79E9E"
12 local text_color_number
= 0xD79E9E
16 -- [local function] Renumber table
17 local function renumber(t
)
19 for _
, i
in pairs(t
) do
33 local displayed_waypoints
= {}
35 -- [function] Add form
36 function advschem
.add_form(name
, def
)
41 tabs
[#tabs
+ 1] = name
45 -- [function] Generate tabs
46 function advschem
.generate_tabs(current
)
47 local retval
= "tabheader[0,0;tabs;"
48 for _
, t
in pairs(tabs
) do
50 if f
.tab
~= false and f
.caption
then
51 retval
= retval
..f
.caption
..","
53 if type(current
) ~= "number" and current
== f
.name
then
58 retval
= retval
:sub(1, -2) -- Strip last comma
59 retval
= retval
..";"..current
.."]" -- Close tabheader
63 -- [function] Handle tabs
64 function advschem
.handle_tabs(pos
, name
, fields
)
65 local tab
= tonumber(fields
.tabs
)
66 if tab
and tabs
[tab
] and forms
[tabs
[tab]]
then
67 advschem
.show_formspec(pos
, name
, forms
[tabs
[tab]]
.name
)
72 -- [function] Show formspec
73 function advschem
.show_formspec(pos
, player
, tab
, show
, ...)
75 if type(player
) == "string" then
76 player
= minetest
.get_player_by_name(player
)
78 local name
= player
:get_player_name()
81 if not form_data
[name
] then
85 local form
= forms
[tab
].get(form_data
[name
], pos
, name
, ...)
86 if forms
[tab
].tab
then
87 form
= form
..advschem
.generate_tabs(tab
)
90 minetest
.show_formspec(name
, "advschem:"..tab
, form
)
93 -- Update player attribute
94 if forms
[tab
].cache_name
~= false then
95 player
:set_attribute("advschem:tab", tab
)
98 minetest
.close_formspec(pname
, "advschem:"..tab
)
103 -- [event] On receive fields
104 minetest
.register_on_player_receive_fields(function(player
, formname
, fields
)
105 local formname
= formname
:split(":")
107 if formname
[1] == "advschem" and forms
[formname
[2]]
then
108 local handle
= forms
[formname
[2]]
.handle
109 local name
= player
:get_player_name()
110 if contexts
[name
] then
111 if not form_data
[name
] then
115 if not advschem
.handle_tabs(contexts
[name
], name
, fields
) and handle
then
116 handle(form_data
[name
], contexts
[name
], name
, fields
)
122 -- Helper function. Scans probabilities of all nodes in the given area and returns a prob_list
123 advschem
.scan_metadata
= function(pos1
, pos2
)
126 for x
=pos1
.x
, pos2
.x
do
127 for y
=pos1
.y
, pos2
.y
do
128 for z
=pos1
.z
, pos2
.z
do
129 local scanpos
= {x
=x
, y
=y
, z
=z
}
130 local node
= minetest
.get_node_or_nil(scanpos
)
132 local prob
, force_place
133 if node
== nil or node
.name
== "advschem:void" then
137 local meta
= minetest
.get_meta(scanpos
)
139 prob
= tonumber(meta
:get_string("advschem_prob")) or 127
140 local fp
= meta
:get_string("advschem_force_place")
148 local ostrpos
= minetest
.pos_to_string(scanpos
)
149 prob_list
[ostrpos
] = {
152 force_place
= force_place
,
161 -- Sets probability and force_place metadata of an item.
162 -- Also updates item description.
163 -- The itemstack is updated in-place.
164 local function set_item_metadata(itemstack
, prob
, force_place
)
165 local smeta
= itemstack
:get_meta()
166 local prob_desc
= "\nProbability: "..(prob
) or
167 smeta
:get_string("advschem_prob") or "Not Set"
168 -- Update probability
169 if prob
and prob
>= 0 and prob
< 127 then
170 smeta
:set_string("advschem_prob", tostring(prob
))
171 elseif prob
and prob
== 127 then
172 -- Clear prob metadata for default probability
174 smeta
:set_string("advschem_prob", nil)
176 prob_desc
= "\nProbability: "..(smeta
:get_string("advschem_prob") or
180 -- Update force place
181 if force_place
== true then
182 smeta
:set_string("advschem_force_place", "true")
183 elseif force_place
== false then
184 smeta
:set_string("advschem_force_place", nil)
187 -- Update description
188 local desc
= minetest
.registered_items
[itemstack
:get_name()].description
189 local meta_desc
= smeta
:get_string("description")
190 if meta_desc
and meta_desc
~= "" then
194 local original_desc
= smeta
:get_string("original_description")
195 if original_desc
and original_desc
~= "" then
198 smeta
:set_string("original_description", desc
)
201 local force_desc
= ""
202 if smeta
:get_string("advschem_force_place") == "true" then
203 force_desc
= "\n".."Force placement"
206 desc
= desc
..minetest
.colorize(text_color
, prob_desc
..force_desc
)
208 smeta
:set_string("description", desc
)
217 advschem
.add_form("main", {
220 get
= function(self
, pos
, name
)
221 local meta
= minetest
.get_meta(pos
):to_table().fields
222 local strpos
= minetest
.pos_to_string(pos
)
225 if meta
.schem_border
== "true" and advschem
.markers
[strpos
] then
226 border_button
= "button[3.5,7.5;3,1;border;Hide border]"
228 border_button
= "button[3.5,7.5;3,1;border;Show border]"
231 -- TODO: Show information regarding volume, pos1, pos2, etc... in formspec
234 label[0.5,-0.1;Position: ]]..strpos
..[[]
235 label[3,-0.1;Owner: ]]..name
..[[]
237 field[0.8,1;5,1;name;Schematic name:;]]..minetest
.formspec_escape(meta
.schem_name
or "")..[[]
238 button[5.3,0.69;1.2,1;save_name;Save]
239 tooltip[save_name;Save schematic name]
240 field_close_on_enter[name;false]
242 button[0.5,1.5;6,1;export;Export schematic]
243 textarea[0.8,2.5;6.2,5;;The schematic will be exported as a .mts file and stored in]]..
244 "\n" .. export_path_full
.. DIR_DELIM
.. [[<name>.mts.;]
245 field[0.8,7;2,1;x;X size:;]]..meta
.x_size
..[[]
246 field[2.8,7;2,1;y;Y size:;]]..meta
.y_size
..[[]
247 field[4.8,7;2,1;z;Z size:;]]..meta
.z_size
..[[]
248 field_close_on_enter[x;false]
249 field_close_on_enter[y;false]
250 field_close_on_enter[z;false]
252 button[0.5,7.5;3,1;save;Save size]
256 handle
= function(self
, pos
, name
, fields
)
257 local realmeta
= minetest
.get_meta(pos
)
258 local meta
= realmeta
:to_table().fields
259 local strpos
= minetest
.pos_to_string(pos
)
262 if fields
.border
then
263 if meta
.schem_border
== "true" and advschem
.markers
[strpos
] then
265 meta
.schem_border
= "false"
268 meta
.schem_border
= "true"
272 -- Save size vector values
273 if (fields
.save
or fields
.key_enter_field
== "x" or
274 fields
.key_enter_field
== "y" or fields
.key_enter_field
== "z")
275 and (fields
.x
and fields
.y
and fields
.z
and fields
.x
~= ""
276 and fields
.y
~= "" and fields
.z
~= "") then
277 local x
, y
, z
= tonumber(fields
.x
), tonumber(fields
.y
), tonumber(fields
.z
)
280 meta
.x_size
= math
.max(x
, 1)
283 meta
.y_size
= math
.max(y
, 1)
286 meta
.z_size
= math
.max(z
, 1)
290 -- Save schematic name
291 if fields
.save_name
or fields
.key_enter_field
== "name" and fields
.name
and
292 fields
.name
~= "" then
293 meta
.schem_name
= fields
.name
297 if fields
.export
and meta
.schem_name
and meta
.schem_name
~= "" then
298 local pos1
, pos2
= advschem
.size(pos
)
299 pos1
, pos2
= advschem
.sort_pos(pos1
, pos2
)
300 local path
= export_path_full
.. DIR_DELIM
303 local plist
= advschem
.scan_metadata(pos1
, pos2
)
304 local probability_list
= {}
305 for _
, i
in pairs(plist
) do
307 if i
.force_place
== true then
311 probability_list
[#probability_list
+ 1] = {
312 pos
= minetest
.string_to_pos(_
),
317 local slist
= minetest
.deserialize(meta
.slices
)
318 local slice_list
= {}
319 for _
, i
in pairs(slist
) do
320 slice_list
[#slice_list
+ 1] = {
321 ypos
= pos
.y
+ i
.ypos
,
326 local filepath
= path
..meta
.schem_name
..".mts"
327 local res
= minetest
.create_schematic(pos1
, pos2
, probability_list
, filepath
, slice_list
)
330 minetest
.chat_send_player(name
, minetest
.colorize("#00ff00",
331 "Exported schematic to "..filepath
))
333 minetest
.chat_send_player(name
, minetest
.colorize("red",
334 "Failed to export schematic to "..filepath
))
338 -- Save meta before updating visuals
339 local inv
= realmeta
:get_inventory():get_lists()
340 realmeta
:from_table({fields
= meta
, inventory
= inv
})
343 if not fields
.border
and meta
.schem_border
== "true" then
348 if not fields
.quit
then
349 advschem
.show_formspec(pos
, minetest
.get_player_by_name(name
), "main")
354 advschem
.add_form("slice", {
355 caption
= "Y Slices",
357 get
= function(self
, pos
, name
, visible_panel
)
358 local meta
= minetest
.get_meta(pos
):to_table().fields
360 self
.selected
= self
.selected
or 1
361 local selected
= tostring(self
.selected
)
362 local slice_list
= minetest
.deserialize(meta
.slices
)
364 for _
, i
in pairs(slice_list
) do
365 local insert
= "Y = "..tostring(i
.ypos
).."; Probability = "..tostring(i
.prob
)
366 slices
= slices
..minetest
.formspec_escape(insert
)..","
368 slices
= slices
:sub(1, -2) -- Remove final comma
372 table[0,0;6.8,6;slices;]]..slices
..[[;]]..selected
..[[]
375 if self
.panel_add
or self
.panel_edit
then
376 local ypos_default
, prob_default
= "", ""
377 local done_button
= "button[5,7.18;2,1;done_add;Done]"
378 if self
.panel_edit
then
379 done_button
= "button[5,7.18;2,1;done_edit;Done]"
380 ypos_default
= slice_list
[self
.selected
].ypos
381 prob_default
= slice_list
[self
.selected
].prob
385 field[0.3,7.5;2.5,1;ypos;Y position (max. ]]..(meta
.y_size
- 1)..[[):;]]..ypos_default
..[[]
386 field[2.8,7.5;2.5,1;prob;Probability (0-127):;]]..prob_default
..[[]
387 field_close_on_enter[ypos;false]
388 field_close_on_enter[prob;false]
392 if not self
.panel_edit
then
393 form
= form
.."button[0,6;2,1;add;+ Add slice]"
396 if slices
~= "" and self
.selected
and not self
.panel_add
then
397 if not self
.panel_edit
then
399 button[2,6;2,1;remove;- Remove slice]
400 button[4,6;2,1;edit;+/- Edit slice]
404 button[2,6;2,1;remove;- Remove slice]
405 button[4,6;2,1;edit;+/- Edit slice]
412 handle
= function(self
, pos
, name
, fields
)
413 local meta
= minetest
.get_meta(pos
)
414 local player
= minetest
.get_player_by_name(name
)
416 if fields
.slices
then
417 local slices
= fields
.slices
:split(":")
418 self
.selected
= tonumber(slices
[2])
422 if not self
.panel_add
then
423 self
.panel_add
= true
424 advschem
.show_formspec(pos
, player
, "slice")
427 advschem
.show_formspec(pos
, player
, "slice")
431 local ypos
, prob
= tonumber(fields
.ypos
), tonumber(fields
.prob
)
432 if (fields
.done_add
or fields
.done_edit
) and fields
.ypos
and fields
.prob
and
433 fields
.ypos
~= "" and fields
.prob
~= "" and ypos
and prob
and
434 ypos
<= (meta
:get_int("y_size") - 1) and prob
>= 0 and prob
<= 255 then
435 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
436 local index
= #slice_list
+ 1
437 if fields
.done_edit
then
438 index
= self
.selected
441 slice_list
[index
] = {ypos
= ypos
, prob
= prob
}
443 meta
:set_string("slices", minetest
.serialize(slice_list
))
445 -- Update and show formspec
447 advschem
.show_formspec(pos
, player
, "slice")
450 if fields
.remove and self
.selected
then
451 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
452 slice_list
[self
.selected
] = nil
453 meta
:set_string("slices", minetest
.serialize(renumber(slice_list
)))
457 self
.panel_edit
= nil
458 advschem
.show_formspec(pos
, player
, "slice")
462 if not self
.panel_edit
then
463 self
.panel_edit
= true
464 advschem
.show_formspec(pos
, player
, "slice")
466 self
.panel_edit
= nil
467 advschem
.show_formspec(pos
, player
, "slice")
473 advschem
.add_form("probtool", {
475 caption
= "Schematic Node Probability Tool",
476 get
= function(self
, pos
, name
)
477 local player
= minetest
.get_player_by_name(name
)
481 local probtool
= player
:get_wielded_item()
482 if probtool
:get_name() ~= "advschem:probtool" then
486 local meta
= probtool
:get_meta()
487 local prob
= tonumber(meta
:get_string("advschem_prob"))
488 local force_place
= meta
:get_string("advschem_force_place")
493 if force_place
== nil or force_place
== "" then
494 force_place
= "false"
496 local form
= "size[5,4]"..
497 "label[0,0;Schematic Node Probability Tool]"..
498 "field[0.75,1;4,1;prob;Probability (0-127);"..prob
.."]"..
499 "checkbox[0.60,1.5;force_place;Force placement;" .. force_place
.. "]" ..
500 "button_exit[0.25,3;2,1;cancel;Cancel]"..
501 "button_exit[2.75,3;2,1;submit;Apply]"..
502 "tooltip[prob;Probability that the node will be placed]"..
503 "tooltip[force_place;If enabled, the node will replace nodes other than air and ignore]"..
504 "field_close_on_enter[prob;false]"
507 handle
= function(self
, pos
, name
, fields
)
508 if fields
.submit
then
509 local prob
= tonumber(fields
.prob
)
511 local player
= minetest
.get_player_by_name(name
)
515 local probtool
= player
:get_wielded_item()
516 if probtool
:get_name() ~= "advschem:probtool" then
520 local force_place
= self
.force_place
== true
522 set_item_metadata(probtool
, prob
, force_place
)
524 player
:set_wielded_item(probtool
)
527 if fields
.force_place
== "true" then
528 self
.force_place
= true
529 elseif fields
.force_place
== "false" then
530 self
.force_place
= false
539 --- Copies and modifies positions `pos1` and `pos2` so that each component of
540 -- `pos1` is less than or equal to the corresponding component of `pos2`.
541 -- Returns the new positions.
542 function advschem
.sort_pos(pos1
, pos2
)
543 if not pos1
or not pos2
then
547 pos1
, pos2
= table.copy(pos1
), table.copy(pos2
)
548 if pos1
.x
> pos2
.x
then
549 pos2
.x
, pos1
.x
= pos1
.x
, pos2
.x
551 if pos1
.y
> pos2
.y
then
552 pos2
.y
, pos1
.y
= pos1
.y
, pos2
.y
554 if pos1
.z
> pos2
.z
then
555 pos2
.z
, pos1
.z
= pos1
.z
, pos2
.z
560 -- [function] Prepare size
561 function advschem
.size(pos
)
562 local pos1
= vector
.new(pos
)
563 local meta
= minetest
.get_meta(pos
)
564 local node
= minetest
.get_node(pos
)
565 local param2
= node
.param2
567 x
= meta
:get_int("x_size"),
568 y
= math
.max(meta
:get_int("y_size") - 1, 0),
569 z
= meta
:get_int("z_size"),
573 local new_pos
= vector
.add({x
= size
.z
, y
= size
.y
, z
= -size
.x
}, pos
)
575 new_pos
.z
= new_pos
.z
+ 1
577 elseif param2
== 2 then
578 local new_pos
= vector
.add({x
= -size
.x
, y
= size
.y
, z
= -size
.z
}, pos
)
580 new_pos
.x
= new_pos
.x
+ 1
582 elseif param2
== 3 then
583 local new_pos
= vector
.add({x
= -size
.z
, y
= size
.y
, z
= size
.x
}, pos
)
585 new_pos
.z
= new_pos
.z
- 1
588 local new_pos
= vector
.add(size
, pos
)
590 new_pos
.x
= new_pos
.x
- 1
595 -- [function] Mark region
596 function advschem
.mark(pos
)
599 local id
= minetest
.pos_to_string(pos
)
600 local owner
= minetest
.get_meta(pos
):get_string("owner")
601 local pos1
, pos2
= advschem
.size(pos
)
602 pos1
, pos2
= advschem
.sort_pos(pos1
, pos2
)
604 local thickness
= 0.2
605 local sizex
, sizey
, sizez
= (1 + pos2
.x
- pos1
.x
) / 2, (1 + pos2
.y
- pos1
.y
) / 2, (1 + pos2
.z
- pos1
.z
) / 2
611 for _
, z
in ipairs({pos1
.z
- 0.5, pos2
.z
+ 0.5}) do
617 local marker
= minetest
.add_entity({x
= pos1
.x
+ sizex
- 0.5, y
= pos1
.y
+ sizey
- 0.5, z
= z
+ offset
}, "advschem:display")
618 if marker
~= nil then
619 marker
:set_properties({
620 visual_size
={x
=(sizex
+0.01) * 2, y
=sizey
* 2},
622 marker
:get_luaentity().id
= id
623 marker
:get_luaentity().owner
= owner
624 table.insert(m
, marker
)
631 for _
, x
in ipairs({pos1
.x
- 0.5, pos2
.x
+ 0.5}) do
638 local marker
= minetest
.add_entity({x
= x
+ offset
, y
= pos1
.y
+ sizey
- 0.5, z
= pos1
.z
+ sizez
- 0.5}, "advschem:display")
639 if marker
~= nil then
640 marker
:set_properties({
641 visual_size
={x
=(sizez
+0.01) * 2, y
=sizey
* 2},
643 marker
:set_yaw(math
.pi
/ 2)
644 marker
:get_luaentity().id
= id
645 marker
:get_luaentity().owner
= owner
646 table.insert(m
, marker
)
651 advschem
.markers
[id
] = m
655 -- [function] Unmark region
656 function advschem
.unmark(pos
)
657 local id
= minetest
.pos_to_string(pos
)
658 if advschem
.markers
[id
] then
660 for _
, entity
in ipairs(advschem
.markers
[id
]) do
669 --- Mark node probability values near player
672 -- Show probability and force_place status of a particular position for player in HUD.
673 -- Probability is shown as a number followed by “[F]” if the node is force-placed.
674 -- The distance to the node is also displayed below that. This can't be avoided and is
675 -- and artifact of the waypoint HUD element. TODO: Hide displayed distance.
676 function advschem
.display_node_prob(player
, pos
, prob
, force_place
)
678 if prob
and force_place
== true then
679 wpstring
= string.format("%d [F]", prob
)
682 elseif force_place
== true then
686 return player
:hud_add({
687 hud_elem_type
= "waypoint",
689 text
= "m", -- For the distance artifact
690 number = text_color_number
,
696 -- Display the node probabilities and force_place status of the nodes near the player.
697 function advschem
.display_node_probs_around_player(player
)
698 local playername
= player
:get_player_name()
699 local pos
= vector
.round(player
:getpos())
701 for x
=pos
.x
-dist
, pos
.x
+dist
do
702 for y
=pos
.y
-dist
, pos
.y
+dist
do
703 for z
=pos
.z
-dist
, pos
.z
+dist
do
704 local checkpos
= {x
=x
, y
=y
, z
=z
}
705 local nodehash
= minetest
.hash_node_position(checkpos
)
707 -- If node is already displayed, remove it so it can re replaced later
708 if displayed_waypoints
[playername
][nodehash
] then
709 player
:hud_remove(displayed_waypoints
[playername
][nodehash
])
710 displayed_waypoints
[playername
][nodehash
] = nil
713 local prob
, force_place
714 local meta
= minetest
.get_meta(checkpos
)
715 prob
= tonumber(meta
:get_string("advschem_prob"))
716 force_place
= meta
:get_string("advschem_force_place") == "true"
717 local hud_id
= advschem
.display_node_prob(player
, checkpos
, prob
, force_place
)
719 displayed_waypoints
[playername
][nodehash
] = hud_id
726 -- Remove all active displayed node statuses.
727 function advschem
.clear_displayed_node_probs(player
)
728 for nodehash
, hud_id
in pairs(displayed_waypoints
[player
:get_player_name()]) do
729 player
:hud_remove(hud_id
)
730 displayed_waypoints
[player
:get_player_name()][nodehash
] = nil
734 minetest
.register_on_joinplayer(function(player
)
735 displayed_waypoints
[player
:get_player_name()] = {}
738 minetest
.register_on_leaveplayer(function(player
)
739 displayed_waypoints
[player
:get_player_name()] = nil
746 -- [priv] schematic_override
747 minetest
.register_privilege("schematic_override", {
748 description
= "Allows you to access advschem nodes not owned by you",
749 give_to_singleplayer
= false,
752 -- [node] Schematic creator
753 minetest
.register_node("advschem:creator", {
754 description
= "Schematic Creator",
755 _doc_items_longdesc
= "The schematic creator is used to save a region of the world into a schematic file (.mts).",
756 _doc_items_usagehelp
= "To get started, place the block facing directly in front of any bottom left corner of the structure you want to save. This block can only be accessed by the placer or by anyone with the “schematic_override” privilege.".."\n"..
757 "To save a region, rightclick the block, enter the size, a schematic name and hit “Export schematic”. The file will always be saved in the world directory. You can use this name in the /placeschem command.".."\n"..
758 "The other features of the schematic creator are optional and are used to allow to add randomness and fine-tuning.".."\n"..
759 "Y slices are used to remove entire slices based on chance. For each slice of the schematic region along the Y axis, you can specify that it occours only with a certain chance. In the Y slice tab, you have to specify the Y slice height (0 = bottom) and a probability from 0 to 127 (127 is for 100%). By default, all Y slices occour always.".."\n"..
760 "With a schematic node probability tool, you can set a probability for each node and enable them to overwrite all nodes when placed as schematic. This tool must be used prior to the file export.",
761 tiles
= {"advschem_creator_top.png", "advschem_creator_bottom.png",
762 "advschem_creator_sides.png"},
763 groups
= { dig_immediate
= 2},
764 paramtype2
= "facedir",
765 is_ground_content
= false,
767 after_place_node
= function(pos
, player
)
768 local name
= player
:get_player_name()
769 local meta
= minetest
.get_meta(pos
)
771 meta
:set_string("owner", name
)
772 meta
:set_string("infotext", "Schematic Creator\n(owned by "..name
..")")
773 meta
:set_string("prob_list", minetest
.serialize({}))
774 meta
:set_string("slices", minetest
.serialize({}))
776 local node
= minetest
.get_node(pos
)
777 local dir
= minetest
.facedir_to_dir(node
.param2
)
779 meta
:set_int("x_size", 1)
780 meta
:set_int("y_size", 1)
781 meta
:set_int("z_size", 1)
783 -- Don't take item from itemstack
786 can_dig
= function(pos
, player
)
787 local name
= player
:get_player_name()
788 local meta
= minetest
.get_meta(pos
)
789 if meta
:get_string("owner") == name
or
790 minetest
.check_player_privs(player
, "schematic_override") == true then
796 on_rightclick
= function(pos
, node
, player
)
797 local meta
= minetest
.get_meta(pos
)
798 local name
= player
:get_player_name()
799 if meta
:get_string("owner") == name
or
800 minetest
.check_player_privs(player
, "schematic_override") == true then
801 -- Get player attribute
802 local tab
= player
:get_attribute("advschem:tab")
803 if not forms
[tab
] or not tab
then
807 advschem
.show_formspec(pos
, player
, tab
, true)
810 after_destruct
= function(pos
)
815 minetest
.register_tool("advschem:probtool", {
816 description
= "Schematic Node Probability Tool",
817 _doc_items_longdesc
=
818 "This tool can be used together with a schematic creator to finetune the way how nodes from a schematic are placed.".."\n"..
819 "It allows you to do two things:".."\n"..
820 "1) Set a chance for a particular node not to be placed in schematic".."\n"..
821 "2) Enable a node to replace blocks other than air and ignored when placed in a schematic",
822 _doc_items_usagehelp
= "Leftclick to select a probability (0-127; 127 is for 100%) and to enable or disable force placement. Now rightclick any node with this tool to apply these settings to the node. This information is preserved in the node until it is destroyed or the tool is used again. Now you can use a schematic creator to save a region as usual, the nodes will now be saved with the special node settings applied.".."\n"..
823 "Note that this tool only has an effect on the nodes with regards to schematics. The node behaviour itself is not changed at all.",
824 wield_image
= "advschem_probtool.png",
825 inventory_image
= "advschem_probtool.png",
826 on_use
= function(itemstack
, user
, pointed_thing
)
827 -- Open dialog to change the probability to apply to nodes.
828 advschem
.show_formspec(user
:getpos(), user
, "probtool", true)
830 on_secondary_use
= function(itemstack
, user
, pointed_thing
)
831 advschem
.clear_displayed_node_probs(user
)
833 on_place
= function(itemstack
, placer
, pointed_thing
)
835 -- This sets the node probability of pointed node to the
836 -- currently used probability stored in the tool.
838 local pos
= pointed_thing
.under
839 local node
= minetest
.get_node(pos
)
840 -- Schematic void are ignored, they always have probability 0
841 if node
.name
== "advschem:void" then
844 local nmeta
= minetest
.get_meta(pos
)
845 local imeta
= itemstack
:get_meta()
846 local prob
= tonumber(imeta
:get_string("advschem_prob"))
847 local force_place
= imeta
:get_string("advschem_force_place")
849 if not prob
or prob
== 127 then
850 nmeta
:set_string("advschem_prob", nil)
852 nmeta
:set_string("advschem_prob", prob
)
854 if force_place
== "true" then
855 nmeta
:set_string("advschem_force_place", "true")
857 nmeta
:set_string("advschem_force_place", nil)
860 -- Enable node probablity display
861 advschem
.display_node_probs_around_player(placer
)
867 minetest
.register_node("advschem:void", {
868 description
= "Schematic Void",
869 _doc_items_longdesc
= "This is an utility block used in the creation of schematic files. It should be used together with a schematic creator. When saving a schematic, all nodes with a schematic void will be left unchanged when the schematic is placed again. Technically, this is equivalent to a block with the node probability set to 0.",
870 _doc_items_usagehelp
= "Just place the schematic void like any other block and use the schematic creator to save a portion of the world.",
871 tiles
= { "advschem_void.png" },
872 drawtype
= "nodebox",
873 is_ground_content
= false,
876 sunlight_propagates
= true,
880 { -4/16, -4/16, -4/16, 4/16, 4/16, 4/16 },
883 groups
= { dig_immediate
= 3},
886 -- [entity] Visible schematic border
887 minetest
.register_entity("advschem:display", {
888 visual
= "upright_sprite",
889 textures
= {"advschem_border.png"},
890 visual_size
= {x
=10, y
=10},
891 collisionbox
= {0,0,0,0,0,0},
894 on_step
= function(self
, dtime
)
897 elseif not advschem
.markers
[self
.id
] then
901 on_activate
= function(self
)
902 self
.object
:set_armor_groups({immortal
= 1})
906 -- [chatcommand] Place schematic
907 minetest
.register_chatcommand("placeschem", {
908 description
= "Place schematic at the position specified or the current "..
909 "player position (loaded from "..export_path_full
..".",
910 privs
= {debug
= true},
911 params
= "<schematic name>[.mts] [<x> <y> <z>]",
912 func
= function(name
, param
)
913 local schem
, p
= string.match(param
, "^([^ ]+) *(.*)$")
914 local pos
= minetest
.string_to_pos(p
)
917 return false, "No schematic file specified."
921 pos
= minetest
.get_player_by_name(name
):get_pos()
924 -- Automatiically add file name suffix if omitted
926 if string.sub(schem
, string.len(schem
)-3, string.len(schem
)) == ".mts" then
929 schem_full
= schem
.. ".mts"
932 local success
= minetest
.place_schematic(pos
, export_path_full
.. DIR_DELIM
.. schem_full
, "random", nil, false)
934 if success
== nil then
935 return false, "Schematic file could not be loaded!"