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 local update_positions
= false
273 -- Save size vector values
274 if (fields
.save
or fields
.key_enter_field
== "x" or
275 fields
.key_enter_field
== "y" or fields
.key_enter_field
== "z")
276 and (fields
.x
and fields
.y
and fields
.z
and fields
.x
~= ""
277 and fields
.y
~= "" and fields
.z
~= "") then
278 local x
, y
, z
= tonumber(fields
.x
), tonumber(fields
.y
), tonumber(fields
.z
)
281 meta
.x_size
= math
.max(x
, 1)
284 meta
.y_size
= math
.max(y
, 1)
287 meta
.z_size
= math
.max(z
, 1)
290 -- Set positions to be updated
291 update_positions
= true
294 -- Save schematic name
295 if fields
.save_name
or fields
.key_enter_field
== "name" and fields
.name
and
296 fields
.name
~= "" then
297 meta
.schem_name
= fields
.name
301 if fields
.export
and meta
.schem_name
and meta
.schem_name
~= "" then
302 local pos1
, pos2
= advschem
.size(pos
)
303 local path
= export_path_full
.. DIR_DELIM
306 local plist
= advschem
.scan_metadata(pos1
, pos2
)
307 local probability_list
= {}
308 for _
, i
in pairs(plist
) do
310 if i
.force_place
== true then
314 probability_list
[#probability_list
+ 1] = {
315 pos
= minetest
.string_to_pos(_
),
320 local slist
= minetest
.deserialize(meta
.slices
)
321 local slice_list
= {}
322 for _
, i
in pairs(slist
) do
323 slice_list
[#slice_list
+ 1] = {
324 ypos
= pos
.y
+ i
.ypos
,
329 local filepath
= path
..meta
.schem_name
..".mts"
330 local res
= minetest
.create_schematic(pos1
, pos2
, probability_list
, filepath
, slice_list
)
333 minetest
.chat_send_player(name
, minetest
.colorize("#00ff00",
334 "Exported schematic to "..filepath
))
336 minetest
.chat_send_player(name
, minetest
.colorize("red",
337 "Failed to export schematic to "..filepath
))
341 -- Save meta before updating visuals
342 local inv
= realmeta
:get_inventory():get_lists()
343 realmeta
:from_table({fields
= meta
, inventory
= inv
})
346 if not fields
.border
and meta
.schem_border
== "true" then
351 if not fields
.quit
then
352 advschem
.show_formspec(pos
, minetest
.get_player_by_name(name
), "main")
355 if update_positions
then
356 local pos1
, pos2
= advschem
.size(pos
)
357 pos1
, pos2
= advschem
.sort_pos(pos1
, pos2
)
362 advschem
.add_form("slice", {
363 caption
= "Y Slices",
365 get
= function(self
, pos
, name
, visible_panel
)
366 local meta
= minetest
.get_meta(pos
):to_table().fields
368 self
.selected
= self
.selected
or 1
369 local selected
= tostring(self
.selected
)
370 local slice_list
= minetest
.deserialize(meta
.slices
)
372 for _
, i
in pairs(slice_list
) do
373 local insert
= "Y = "..tostring(i
.ypos
).."; Probability = "..tostring(i
.prob
)
374 slices
= slices
..minetest
.formspec_escape(insert
)..","
376 slices
= slices
:sub(1, -2) -- Remove final comma
380 table[0,0;6.8,6;slices;]]..slices
..[[;]]..selected
..[[]
383 if self
.panel_add
or self
.panel_edit
then
384 local ypos_default
, prob_default
= "", ""
385 local done_button
= "button[5,7.18;2,1;done_add;Done]"
386 if self
.panel_edit
then
387 done_button
= "button[5,7.18;2,1;done_edit;Done]"
388 ypos_default
= slice_list
[self
.selected
].ypos
389 prob_default
= slice_list
[self
.selected
].prob
393 field[0.3,7.5;2.5,1;ypos;Y position (max. ]]..(meta
.y_size
- 1)..[[):;]]..ypos_default
..[[]
394 field[2.8,7.5;2.5,1;prob;Probability (0-127):;]]..prob_default
..[[]
395 field_close_on_enter[ypos;false]
396 field_close_on_enter[prob;false]
400 if not self
.panel_edit
then
401 form
= form
.."button[0,6;2,1;add;+ Add slice]"
404 if slices
~= "" and self
.selected
and not self
.panel_add
then
405 if not self
.panel_edit
then
407 button[2,6;2,1;remove;- Remove slice]
408 button[4,6;2,1;edit;+/- Edit slice]
412 button[2,6;2,1;remove;- Remove slice]
413 button[4,6;2,1;edit;+/- Edit slice]
420 handle
= function(self
, pos
, name
, fields
)
421 local meta
= minetest
.get_meta(pos
)
422 local player
= minetest
.get_player_by_name(name
)
424 if fields
.slices
then
425 local slices
= fields
.slices
:split(":")
426 self
.selected
= tonumber(slices
[2])
430 if not self
.panel_add
then
431 self
.panel_add
= true
432 advschem
.show_formspec(pos
, player
, "slice")
435 advschem
.show_formspec(pos
, player
, "slice")
439 local ypos
, prob
= tonumber(fields
.ypos
), tonumber(fields
.prob
)
440 if (fields
.done_add
or fields
.done_edit
) and fields
.ypos
and fields
.prob
and
441 fields
.ypos
~= "" and fields
.prob
~= "" and ypos
and prob
and
442 ypos
<= (meta
:get_int("y_size") - 1) and prob
>= 0 and prob
<= 255 then
443 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
444 local index
= #slice_list
+ 1
445 if fields
.done_edit
then
446 index
= self
.selected
449 slice_list
[index
] = {ypos
= ypos
, prob
= prob
}
451 meta
:set_string("slices", minetest
.serialize(slice_list
))
453 -- Update and show formspec
455 advschem
.show_formspec(pos
, player
, "slice")
458 if fields
.remove and self
.selected
then
459 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
460 slice_list
[self
.selected
] = nil
461 meta
:set_string("slices", minetest
.serialize(renumber(slice_list
)))
465 self
.panel_edit
= nil
466 advschem
.show_formspec(pos
, player
, "slice")
470 if not self
.panel_edit
then
471 self
.panel_edit
= true
472 advschem
.show_formspec(pos
, player
, "slice")
474 self
.panel_edit
= nil
475 advschem
.show_formspec(pos
, player
, "slice")
481 advschem
.add_form("probtool", {
483 caption
= "Schematic Node Probability Tool",
484 get
= function(self
, pos
, name
)
485 local player
= minetest
.get_player_by_name(name
)
489 local probtool
= player
:get_wielded_item()
490 if probtool
:get_name() ~= "advschem:probtool" then
494 local meta
= probtool
:get_meta()
495 local prob
= tonumber(meta
:get_string("advschem_prob"))
496 local force_place
= meta
:get_string("advschem_force_place")
501 if force_place
== nil or force_place
== "" then
502 force_place
= "false"
504 local form
= "size[5,4]"..
505 "label[0,0;Schematic Node Probability Tool]"..
506 "field[0.75,1;4,1;prob;Probability (0-127);"..prob
.."]"..
507 "checkbox[0.60,1.5;force_place;Force placement;" .. force_place
.. "]" ..
508 "button_exit[0.25,3;2,1;cancel;Cancel]"..
509 "button_exit[2.75,3;2,1;submit;Apply]"..
510 "tooltip[prob;Probability that the node will be placed]"..
511 "tooltip[force_place;If enabled, the node will replace nodes other than air and ignore]"..
512 "field_close_on_enter[prob;false]"
515 handle
= function(self
, pos
, name
, fields
)
516 if fields
.submit
then
517 local prob
= tonumber(fields
.prob
)
519 local player
= minetest
.get_player_by_name(name
)
523 local probtool
= player
:get_wielded_item()
524 if probtool
:get_name() ~= "advschem:probtool" then
528 local force_place
= self
.force_place
== true
530 set_item_metadata(probtool
, prob
, force_place
)
532 player
:set_wielded_item(probtool
)
535 if fields
.force_place
== "true" then
536 self
.force_place
= true
537 elseif fields
.force_place
== "false" then
538 self
.force_place
= false
547 --- Copies and modifies positions `pos1` and `pos2` so that each component of
548 -- `pos1` is less than or equal to the corresponding component of `pos2`.
549 -- Returns the new positions.
550 function advschem
.sort_pos(pos1
, pos2
)
551 if not pos1
or not pos2
then
555 pos1
, pos2
= table.copy(pos1
), table.copy(pos2
)
556 if pos1
.x
> pos2
.x
then
557 pos2
.x
, pos1
.x
= pos1
.x
, pos2
.x
559 if pos1
.y
> pos2
.y
then
560 pos2
.y
, pos1
.y
= pos1
.y
, pos2
.y
562 if pos1
.z
> pos2
.z
then
563 pos2
.z
, pos1
.z
= pos1
.z
, pos2
.z
568 -- [function] Prepare size
569 function advschem
.size(pos
)
570 local pos1
= vector
.new(pos
)
571 local meta
= minetest
.get_meta(pos
)
572 local node
= minetest
.get_node(pos
)
573 local param2
= node
.param2
575 x
= meta
:get_int("x_size"),
576 y
= math
.max(meta
:get_int("y_size") - 1, 0),
577 z
= meta
:get_int("z_size"),
581 local new_pos
= vector
.add({x
= size
.z
, y
= size
.y
, z
= -size
.x
}, pos
)
583 new_pos
.z
= new_pos
.z
+ 1
585 elseif param2
== 2 then
586 local new_pos
= vector
.add({x
= -size
.x
, y
= size
.y
, z
= -size
.z
}, pos
)
588 new_pos
.x
= new_pos
.x
+ 1
590 elseif param2
== 3 then
591 local new_pos
= vector
.add({x
= -size
.z
, y
= size
.y
, z
= size
.x
}, pos
)
593 new_pos
.z
= new_pos
.z
- 1
596 local new_pos
= vector
.add(size
, pos
)
598 new_pos
.x
= new_pos
.x
- 1
603 -- [function] Mark region
604 function advschem
.mark(pos
)
607 local id
= minetest
.pos_to_string(pos
)
608 local owner
= minetest
.get_meta(pos
):get_string("owner")
609 local pos1
, pos2
= advschem
.size(pos
)
610 pos1
, pos2
= advschem
.sort_pos(pos1
, pos2
)
612 local thickness
= 0.2
613 local sizex
, sizey
, sizez
= (1 + pos2
.x
- pos1
.x
) / 2, (1 + pos2
.y
- pos1
.y
) / 2, (1 + pos2
.z
- pos1
.z
) / 2
619 for _
, z
in ipairs({pos1
.z
- 0.5, pos2
.z
+ 0.5}) do
625 local marker
= minetest
.add_entity({x
= pos1
.x
+ sizex
- 0.5, y
= pos1
.y
+ sizey
- 0.5, z
= z
+ offset
}, "advschem:display")
626 if marker
~= nil then
627 marker
:set_properties({
628 visual_size
={x
=(sizex
+0.01) * 2, y
=sizey
* 2},
630 marker
:get_luaentity().id
= id
631 marker
:get_luaentity().owner
= owner
632 table.insert(m
, marker
)
639 for _
, x
in ipairs({pos1
.x
- 0.5, pos2
.x
+ 0.5}) do
646 local marker
= minetest
.add_entity({x
= x
+ offset
, y
= pos1
.y
+ sizey
- 0.5, z
= pos1
.z
+ sizez
- 0.5}, "advschem:display")
647 if marker
~= nil then
648 marker
:set_properties({
649 visual_size
={x
=(sizez
+0.01) * 2, y
=sizey
* 2},
651 marker
:set_yaw(math
.pi
/ 2)
652 marker
:get_luaentity().id
= id
653 marker
:get_luaentity().owner
= owner
654 table.insert(m
, marker
)
659 advschem
.markers
[id
] = m
663 -- [function] Unmark region
664 function advschem
.unmark(pos
)
665 local id
= minetest
.pos_to_string(pos
)
666 if advschem
.markers
[id
] then
668 for _
, entity
in ipairs(advschem
.markers
[id
]) do
677 --- Mark node probability values near player
680 -- Show probability and force_place status of a particular position for player in HUD.
681 -- Probability is shown as a number followed by “[F]” if the node is force-placed.
682 -- The distance to the node is also displayed below that. This can't be avoided and is
683 -- and artifact of the waypoint HUD element. TODO: Hide displayed distance.
684 function advschem
.display_node_prob(player
, pos
, prob
, force_place
)
686 if prob
and force_place
== true then
687 wpstring
= string.format("%d [F]", prob
)
690 elseif force_place
== true then
694 return player
:hud_add({
695 hud_elem_type
= "waypoint",
697 text
= "m", -- For the distance artifact
698 number = text_color_number
,
704 -- Display the node probabilities and force_place status of the nodes near the player.
705 function advschem
.display_node_probs_around_player(player
)
706 local playername
= player
:get_player_name()
707 local pos
= vector
.round(player
:getpos())
709 for x
=pos
.x
-dist
, pos
.x
+dist
do
710 for y
=pos
.y
-dist
, pos
.y
+dist
do
711 for z
=pos
.z
-dist
, pos
.z
+dist
do
712 local checkpos
= {x
=x
, y
=y
, z
=z
}
713 local nodehash
= minetest
.hash_node_position(checkpos
)
715 -- If node is already displayed, remove it so it can re replaced later
716 if displayed_waypoints
[playername
][nodehash
] then
717 player
:hud_remove(displayed_waypoints
[playername
][nodehash
])
718 displayed_waypoints
[playername
][nodehash
] = nil
721 local prob
, force_place
722 local meta
= minetest
.get_meta(checkpos
)
723 prob
= tonumber(meta
:get_string("advschem_prob"))
724 force_place
= meta
:get_string("advschem_force_place") == "true"
725 local hud_id
= advschem
.display_node_prob(player
, checkpos
, prob
, force_place
)
727 displayed_waypoints
[playername
][nodehash
] = hud_id
734 -- Remove all active displayed node statuses.
735 function advschem
.clear_displayed_node_probs(player
)
736 for nodehash
, hud_id
in pairs(displayed_waypoints
[player
:get_player_name()]) do
737 player
:hud_remove(hud_id
)
738 displayed_waypoints
[player
:get_player_name()][nodehash
] = nil
742 minetest
.register_on_joinplayer(function(player
)
743 displayed_waypoints
[player
:get_player_name()] = {}
746 minetest
.register_on_leaveplayer(function(player
)
747 displayed_waypoints
[player
:get_player_name()] = nil
754 -- [priv] schematic_override
755 minetest
.register_privilege("schematic_override", {
756 description
= "Allows you to access advschem nodes not owned by you",
757 give_to_singleplayer
= false,
760 -- [node] Schematic creator
761 minetest
.register_node("advschem:creator", {
762 description
= "Schematic Creator",
763 _doc_items_longdesc
= "The schematic creator is used to save a region of the world into a schematic file (.mts).",
764 _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"..
765 "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"..
766 "The other features of the schematic creator are optional and are used to allow to add randomness and fine-tuning.".."\n"..
767 "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"..
768 "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.",
769 tiles
= {"advschem_creator_top.png", "advschem_creator_bottom.png",
770 "advschem_creator_sides.png"},
771 groups
= { dig_immediate
= 2},
772 paramtype2
= "facedir",
773 is_ground_content
= false,
775 after_place_node
= function(pos
, player
)
776 local name
= player
:get_player_name()
777 local meta
= minetest
.get_meta(pos
)
779 meta
:set_string("owner", name
)
780 meta
:set_string("infotext", "Schematic Creator\n(owned by "..name
..")")
781 meta
:set_string("prob_list", minetest
.serialize({}))
782 meta
:set_string("slices", minetest
.serialize({}))
784 local node
= minetest
.get_node(pos
)
785 local dir
= minetest
.facedir_to_dir(node
.param2
)
787 meta
:set_int("x_size", 1)
788 meta
:set_int("y_size", 1)
789 meta
:set_int("z_size", 1)
791 local inv
= meta
:get_inventory()
792 inv
:set_size("probability", 1)
794 local pos1
, pos2
= advschem
.size(pos
)
796 -- Don't take item from itemstack
799 can_dig
= function(pos
, player
)
800 local name
= player
:get_player_name()
801 local meta
= minetest
.get_meta(pos
)
802 if meta
:get_string("owner") == name
or
803 minetest
.check_player_privs(player
, "schematic_override") == true then
809 on_rightclick
= function(pos
, node
, player
)
810 local meta
= minetest
.get_meta(pos
)
811 local name
= player
:get_player_name()
812 if meta
:get_string("owner") == name
or
813 minetest
.check_player_privs(player
, "schematic_override") == true then
814 -- Get player attribute
815 local tab
= player
:get_attribute("advschem:tab")
816 if not forms
[tab
] or not tab
then
820 advschem
.show_formspec(pos
, player
, tab
, true)
823 after_destruct
= function(pos
)
828 minetest
.register_tool("advschem:probtool", {
829 description
= "Schematic Node Probability Tool",
830 _doc_items_longdesc
=
831 "This tool can be used together with a schematic creator to finetune the way how nodes from a schematic are placed.".."\n"..
832 "It allows you to do two things:".."\n"..
833 "1) Set a chance for a particular node not to be placed in schematic".."\n"..
834 "2) Enable a node to replace blocks other than air and ignored when placed in a schematic",
835 _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"..
836 "Note that this tool only has an effect on the nodes with regards to schematics. The node behaviour itself is not changed at all.",
837 wield_image
= "advschem_probtool.png",
838 inventory_image
= "advschem_probtool.png",
839 on_use
= function(itemstack
, user
, pointed_thing
)
840 -- Open dialog to change the probability to apply to nodes.
841 advschem
.show_formspec(user
:getpos(), user
, "probtool", true)
843 on_secondary_use
= function(itemstack
, user
, pointed_thing
)
844 advschem
.clear_displayed_node_probs(user
)
846 on_place
= function(itemstack
, placer
, pointed_thing
)
848 -- This sets the node probability of pointed node to the
849 -- currently used probability stored in the tool.
851 local pos
= pointed_thing
.under
852 local node
= minetest
.get_node(pos
)
853 -- Schematic void are ignored, they always have probability 0
854 if node
.name
== "advschem:void" then
857 local nmeta
= minetest
.get_meta(pos
)
858 local imeta
= itemstack
:get_meta()
859 local prob
= tonumber(imeta
:get_string("advschem_prob"))
860 local force_place
= imeta
:get_string("advschem_force_place")
862 if not prob
or prob
== 127 then
863 nmeta
:set_string("advschem_prob", nil)
865 nmeta
:set_string("advschem_prob", prob
)
867 if force_place
== "true" then
868 nmeta
:set_string("advschem_force_place", "true")
870 nmeta
:set_string("advschem_force_place", nil)
873 -- Enable node probablity display
874 advschem
.display_node_probs_around_player(placer
)
880 minetest
.register_node("advschem:void", {
881 description
= "Schematic Void",
882 _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.",
883 _doc_items_usagehelp
= "Just place the schematic void like any other block and use the schematic creator to save a portion of the world.",
884 tiles
= { "advschem_void.png" },
885 drawtype
= "nodebox",
886 is_ground_content
= false,
889 sunlight_propagates
= true,
893 { -4/16, -4/16, -4/16, 4/16, 4/16, 4/16 },
896 groups
= { dig_immediate
= 3},
899 -- [entity] Visible schematic border
900 minetest
.register_entity("advschem:display", {
901 visual
= "upright_sprite",
902 textures
= {"advschem_border.png"},
903 visual_size
= {x
=10, y
=10},
904 collisionbox
= {0,0,0,0,0,0},
907 on_step
= function(self
, dtime
)
910 elseif not advschem
.markers
[self
.id
] then
914 on_activate
= function(self
)
915 self
.object
:set_armor_groups({immortal
= 1})
919 -- [chatcommand] Place schematic
920 minetest
.register_chatcommand("placeschem", {
921 description
= "Place schematic at the position specified or the current "..
922 "player position (loaded from "..export_path_full
..".",
923 privs
= {debug
= true},
924 params
= "<schematic name>[.mts] [<x> <y> <z>]",
925 func
= function(name
, param
)
926 local schem
, p
= string.match(param
, "^([^ ]+) *(.*)$")
927 local pos
= minetest
.string_to_pos(p
)
930 return false, "No schematic file specified."
934 pos
= minetest
.get_player_by_name(name
):get_pos()
937 -- Automatiically add file name suffix if omitted
939 if string.sub(schem
, string.len(schem
)-3, string.len(schem
)) == ".mts" then
942 schem_full
= schem
.. ".mts"
945 local success
= minetest
.place_schematic(pos
, export_path_full
.. DIR_DELIM
.. schem_full
, "random", nil, false)
947 if success
== nil then
948 return false, "Schematic file could not be loaded!"