3 -- Directory delimeter fallback (normally comes from builtin)
7 local export_path_full
= table.concat({minetest
.get_worldpath(), "schems"}, DIR_DELIM
)
9 local text_color
= "#D79E9E"
10 local text_color_number
= 0xD79E9E
12 schemedit
.markers
= {}
14 -- [local function] Renumber table
15 local function renumber(t
)
17 for _
, i
in pairs(t
) do
31 local displayed_waypoints
= {}
33 -- Sadly, the probabilities presented in Lua (0-255) are not identical to the REAL probabilities in the
34 -- schematic file (0-127). There are two converter functions to convert from one probability type to another.
35 -- This mod tries to retain the “Lua probability” as long as possible and only switches to “schematic probability”
36 -- on an actual export to a schematic.
38 function schemedit
.lua_prob_to_schematic_prob(lua_prob
)
39 return math
.floor(lua_prob
/ 2)
42 function schemedit
.schematic_prob_to_lua_prob(schematic_prob
)
43 return schematic_prob
* 2
47 -- [function] Add form
48 function schemedit
.add_form(name
, def
)
53 tabs
[#tabs
+ 1] = name
57 -- [function] Generate tabs
58 function schemedit
.generate_tabs(current
)
59 local retval
= "tabheader[0,0;tabs;"
60 for _
, t
in pairs(tabs
) do
62 if f
.tab
~= false and f
.caption
then
63 retval
= retval
..f
.caption
..","
65 if type(current
) ~= "number" and current
== f
.name
then
70 retval
= retval
:sub(1, -2) -- Strip last comma
71 retval
= retval
..";"..current
.."]" -- Close tabheader
75 -- [function] Handle tabs
76 function schemedit
.handle_tabs(pos
, name
, fields
)
77 local tab
= tonumber(fields
.tabs
)
78 if tab
and tabs
[tab
] and forms
[tabs
[tab]]
then
79 schemedit
.show_formspec(pos
, name
, forms
[tabs
[tab]]
.name
)
84 -- [function] Show formspec
85 function schemedit
.show_formspec(pos
, player
, tab
, show
, ...)
87 if type(player
) == "string" then
88 player
= minetest
.get_player_by_name(player
)
90 local name
= player
:get_player_name()
93 if not form_data
[name
] then
97 local form
= forms
[tab
].get(form_data
[name
], pos
, name
, ...)
98 if forms
[tab
].tab
then
99 form
= form
..schemedit
.generate_tabs(tab
)
102 minetest
.show_formspec(name
, "schemedit:"..tab
, form
)
105 -- Update player attribute
106 if forms
[tab
].cache_name
~= false then
107 player
:set_attribute("schemedit:tab", tab
)
110 minetest
.close_formspec(pname
, "schemedit:"..tab
)
115 -- [event] On receive fields
116 minetest
.register_on_player_receive_fields(function(player
, formname
, fields
)
117 local formname
= formname
:split(":")
119 if formname
[1] == "schemedit" and forms
[formname
[2]]
then
120 local handle
= forms
[formname
[2]]
.handle
121 local name
= player
:get_player_name()
122 if contexts
[name
] then
123 if not form_data
[name
] then
127 if not schemedit
.handle_tabs(contexts
[name
], name
, fields
) and handle
then
128 handle(form_data
[name
], contexts
[name
], name
, fields
)
134 -- Helper function. Scans probabilities of all nodes in the given area and returns a prob_list
135 schemedit
.scan_metadata
= function(pos1
, pos2
)
138 for x
=pos1
.x
, pos2
.x
do
139 for y
=pos1
.y
, pos2
.y
do
140 for z
=pos1
.z
, pos2
.z
do
141 local scanpos
= {x
=x
, y
=y
, z
=z
}
142 local node
= minetest
.get_node_or_nil(scanpos
)
144 local prob
, force_place
145 if node
== nil or node
.name
== "schemedit:void" then
149 local meta
= minetest
.get_meta(scanpos
)
151 prob
= tonumber(meta
:get_string("schemedit_prob")) or 255
152 local fp
= meta
:get_string("schemedit_force_place")
160 local hashpos
= minetest
.hash_node_position(scanpos
)
161 prob_list
[hashpos
] = {
164 force_place
= force_place
,
173 -- Sets probability and force_place metadata of an item.
174 -- Also updates item description.
175 -- The itemstack is updated in-place.
176 local function set_item_metadata(itemstack
, prob
, force_place
)
177 local smeta
= itemstack
:get_meta()
178 local prob_desc
= "\nProbability: "..(prob
) or
179 smeta
:get_string("schemedit_prob") or "Not Set"
180 -- Update probability
181 if prob
and prob
>= 0 and prob
< 255 then
182 smeta
:set_string("schemedit_prob", tostring(prob
))
183 elseif prob
and prob
== 255 then
184 -- Clear prob metadata for default probability
186 smeta
:set_string("schemedit_prob", nil)
188 prob_desc
= "\nProbability: "..(smeta
:get_string("schemedit_prob") or
192 -- Update force place
193 if force_place
== true then
194 smeta
:set_string("schemedit_force_place", "true")
195 elseif force_place
== false then
196 smeta
:set_string("schemedit_force_place", nil)
199 -- Update description
200 local desc
= minetest
.registered_items
[itemstack
:get_name()].description
201 local meta_desc
= smeta
:get_string("description")
202 if meta_desc
and meta_desc
~= "" then
206 local original_desc
= smeta
:get_string("original_description")
207 if original_desc
and original_desc
~= "" then
210 smeta
:set_string("original_description", desc
)
213 local force_desc
= ""
214 if smeta
:get_string("schemedit_force_place") == "true" then
215 force_desc
= "\n".."Force placement"
218 desc
= desc
..minetest
.colorize(text_color
, prob_desc
..force_desc
)
220 smeta
:set_string("description", desc
)
229 schemedit
.add_form("main", {
232 get
= function(self
, pos
, name
)
233 local meta
= minetest
.get_meta(pos
):to_table().fields
234 local strpos
= minetest
.pos_to_string(pos
)
235 local hashpos
= minetest
.hash_node_position(pos
)
238 if meta
.schem_border
== "true" and schemedit
.markers
[hashpos
] then
239 border_button
= "button[3.5,7.5;3,1;border;Hide border]"
241 border_button
= "button[3.5,7.5;3,1;border;Show border]"
244 -- TODO: Show information regarding volume, pos1, pos2, etc... in formspec
247 label[0.5,-0.1;Position: ]]..strpos
..[[]
248 label[3,-0.1;Owner: ]]..name
..[[]
250 field[0.8,1;5,1;name;Schematic name:;]]..minetest
.formspec_escape(meta
.schem_name
or "")..[[]
251 button[5.3,0.69;1.2,1;save_name;Save]
252 tooltip[save_name;Save schematic name]
253 field_close_on_enter[name;false]
255 button[0.5,1.5;6,1;export;Export schematic]
256 textarea[0.8,2.5;6.2,5;;The schematic will be exported as a .mts file and stored in]]..
257 "\n" .. export_path_full
.. DIR_DELIM
.. [[<name>.mts.;]
258 field[0.8,7;2,1;x;X size:;]]..meta
.x_size
..[[]
259 field[2.8,7;2,1;y;Y size:;]]..meta
.y_size
..[[]
260 field[4.8,7;2,1;z;Z size:;]]..meta
.z_size
..[[]
261 field_close_on_enter[x;false]
262 field_close_on_enter[y;false]
263 field_close_on_enter[z;false]
265 button[0.5,7.5;3,1;save;Save size]
268 if minetest
.get_modpath("doc") then
269 form
= form
.. "image_button[6.4,-0.2;0.8,0.8;doc_button_icon_lores.png;doc;]" ..
274 handle
= function(self
, pos
, name
, fields
)
275 local realmeta
= minetest
.get_meta(pos
)
276 local meta
= realmeta
:to_table().fields
277 local hashpos
= minetest
.hash_node_position(pos
)
280 doc
.show_entry(name
, "nodes", "schemedit:creator", true)
285 if fields
.border
then
286 if meta
.schem_border
== "true" and schemedit
.markers
[hashpos
] then
287 schemedit
.unmark(pos
)
288 meta
.schem_border
= "false"
291 meta
.schem_border
= "true"
295 -- Save size vector values
296 if (fields
.save
or fields
.key_enter_field
== "x" or
297 fields
.key_enter_field
== "y" or fields
.key_enter_field
== "z")
298 and (fields
.x
and fields
.y
and fields
.z
and fields
.x
~= ""
299 and fields
.y
~= "" and fields
.z
~= "") then
300 local x
, y
, z
= tonumber(fields
.x
), tonumber(fields
.y
), tonumber(fields
.z
)
303 meta
.x_size
= math
.max(x
, 1)
306 meta
.y_size
= math
.max(y
, 1)
309 meta
.z_size
= math
.max(z
, 1)
313 -- Save schematic name
314 if fields
.save_name
or fields
.key_enter_field
== "name" and fields
.name
and
315 fields
.name
~= "" then
316 meta
.schem_name
= fields
.name
320 if fields
.export
and meta
.schem_name
and meta
.schem_name
~= "" then
321 local pos1
, pos2
= schemedit
.size(pos
)
322 pos1
, pos2
= schemedit
.sort_pos(pos1
, pos2
)
323 local path
= export_path_full
.. DIR_DELIM
326 local plist
= schemedit
.scan_metadata(pos1
, pos2
)
327 local probability_list
= {}
328 for hash
, i
in pairs(plist
) do
329 local prob
= schemedit
.lua_prob_to_schematic_prob(i
.prob
)
330 if i
.force_place
== true then
334 table.insert(probability_list
, {
335 pos
= minetest
.get_position_from_hash(hash
),
340 local slist
= minetest
.deserialize(meta
.slices
)
341 local slice_list
= {}
342 for _
, i
in pairs(slist
) do
343 slice_list
[#slice_list
+ 1] = {
344 ypos
= pos
.y
+ i
.ypos
,
345 prob
= schemedit
.lua_prob_to_schematic_prob(i
.prob
),
349 local filepath
= path
..meta
.schem_name
..".mts"
350 local res
= minetest
.create_schematic(pos1
, pos2
, probability_list
, filepath
, slice_list
)
353 minetest
.chat_send_player(name
, minetest
.colorize("#00ff00",
354 "Exported schematic to "..filepath
))
356 minetest
.chat_send_player(name
, minetest
.colorize("red",
357 "Failed to export schematic to "..filepath
))
361 -- Save meta before updating visuals
362 local inv
= realmeta
:get_inventory():get_lists()
363 realmeta
:from_table({fields
= meta
, inventory
= inv
})
366 if not fields
.border
and meta
.schem_border
== "true" then
371 if not fields
.quit
then
372 schemedit
.show_formspec(pos
, minetest
.get_player_by_name(name
), "main")
377 schemedit
.add_form("slice", {
378 caption
= "Y Slices",
380 get
= function(self
, pos
, name
, visible_panel
)
381 local meta
= minetest
.get_meta(pos
):to_table().fields
383 self
.selected
= self
.selected
or 1
384 local selected
= tostring(self
.selected
)
385 local slice_list
= minetest
.deserialize(meta
.slices
)
387 for _
, i
in pairs(slice_list
) do
388 local insert
= "Y = "..tostring(i
.ypos
).."; Probability = "..tostring(i
.prob
)
389 slices
= slices
..minetest
.formspec_escape(insert
)..","
391 slices
= slices
:sub(1, -2) -- Remove final comma
395 table[0,0;6.8,6;slices;]]..slices
..[[;]]..selected
..[[]
398 if self
.panel_add
or self
.panel_edit
then
399 local ypos_default
, prob_default
= "", ""
400 local done_button
= "button[5,7.18;2,1;done_add;Done]"
401 if self
.panel_edit
then
402 done_button
= "button[5,7.18;2,1;done_edit;Done]"
403 if slice_list
[self
.selected
] then
404 ypos_default
= slice_list
[self
.selected
].ypos
405 prob_default
= slice_list
[self
.selected
].prob
410 field[0.3,7.5;2.5,1;ypos;Y position (max. ]]..(meta
.y_size
- 1)..[[):;]]..ypos_default
..[[]
411 field[2.8,7.5;2.5,1;prob;Probability (0-255):;]]..prob_default
..[[]
412 field_close_on_enter[ypos;false]
413 field_close_on_enter[prob;false]
417 if not self
.panel_edit
then
418 form
= form
.."button[0,6;2,1;add;+ Add slice]"
421 if slices
~= "" and self
.selected
and not self
.panel_add
then
422 if not self
.panel_edit
then
424 button[2,6;2,1;remove;- Remove slice]
425 button[4,6;2,1;edit;+/- Edit slice]
429 button[2,6;2,1;remove;- Remove slice]
430 button[4,6;2,1;edit;+/- Edit slice]
437 handle
= function(self
, pos
, name
, fields
)
438 local meta
= minetest
.get_meta(pos
)
439 local player
= minetest
.get_player_by_name(name
)
441 if fields
.slices
then
442 local slices
= fields
.slices
:split(":")
443 self
.selected
= tonumber(slices
[2])
447 if not self
.panel_add
then
448 self
.panel_add
= true
449 schemedit
.show_formspec(pos
, player
, "slice")
452 schemedit
.show_formspec(pos
, player
, "slice")
456 local ypos
, prob
= tonumber(fields
.ypos
), tonumber(fields
.prob
)
457 if (fields
.done_add
or fields
.done_edit
) and fields
.ypos
and fields
.prob
and
458 fields
.ypos
~= "" and fields
.prob
~= "" and ypos
and prob
and
459 ypos
<= (meta
:get_int("y_size") - 1) and prob
>= 0 and prob
<= 255 then
460 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
461 local index
= #slice_list
+ 1
462 if fields
.done_edit
then
463 index
= self
.selected
466 slice_list
[index
] = {ypos
= ypos
, prob
= prob
}
468 meta
:set_string("slices", minetest
.serialize(slice_list
))
470 -- Update and show formspec
472 schemedit
.show_formspec(pos
, player
, "slice")
475 if fields
.remove and self
.selected
then
476 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
477 slice_list
[self
.selected
] = nil
478 meta
:set_string("slices", minetest
.serialize(renumber(slice_list
)))
482 self
.panel_edit
= nil
483 schemedit
.show_formspec(pos
, player
, "slice")
487 if not self
.panel_edit
then
488 self
.panel_edit
= true
489 schemedit
.show_formspec(pos
, player
, "slice")
491 self
.panel_edit
= nil
492 schemedit
.show_formspec(pos
, player
, "slice")
498 schemedit
.add_form("probtool", {
500 caption
= "Schematic Node Probability Tool",
501 get
= function(self
, pos
, name
)
502 local player
= minetest
.get_player_by_name(name
)
506 local probtool
= player
:get_wielded_item()
507 if probtool
:get_name() ~= "schemedit:probtool" then
511 local meta
= probtool
:get_meta()
512 local prob
= tonumber(meta
:get_string("schemedit_prob"))
513 local force_place
= meta
:get_string("schemedit_force_place")
518 if force_place
== nil or force_place
== "" then
519 force_place
= "false"
521 local form
= "size[5,4]"..
522 "label[0,0;Schematic Node Probability Tool]"..
523 "field[0.75,1;4,1;prob;Probability (0-255);"..prob
.."]"..
524 "checkbox[0.60,1.5;force_place;Force placement;" .. force_place
.. "]" ..
525 "button_exit[0.25,3;2,1;cancel;Cancel]"..
526 "button_exit[2.75,3;2,1;submit;Apply]"..
527 "tooltip[prob;Probability that the node will be placed]"..
528 "tooltip[force_place;If enabled, the node will replace nodes other than air and ignore]"..
529 "field_close_on_enter[prob;false]"
532 handle
= function(self
, pos
, name
, fields
)
533 if fields
.submit
then
534 local prob
= tonumber(fields
.prob
)
536 local player
= minetest
.get_player_by_name(name
)
540 local probtool
= player
:get_wielded_item()
541 if probtool
:get_name() ~= "schemedit:probtool" then
545 local force_place
= self
.force_place
== true
547 set_item_metadata(probtool
, prob
, force_place
)
549 player
:set_wielded_item(probtool
)
552 if fields
.force_place
== "true" then
553 self
.force_place
= true
554 elseif fields
.force_place
== "false" then
555 self
.force_place
= false
564 --- Copies and modifies positions `pos1` and `pos2` so that each component of
565 -- `pos1` is less than or equal to the corresponding component of `pos2`.
566 -- Returns the new positions.
567 function schemedit
.sort_pos(pos1
, pos2
)
568 if not pos1
or not pos2
then
572 pos1
, pos2
= table.copy(pos1
), table.copy(pos2
)
573 if pos1
.x
> pos2
.x
then
574 pos2
.x
, pos1
.x
= pos1
.x
, pos2
.x
576 if pos1
.y
> pos2
.y
then
577 pos2
.y
, pos1
.y
= pos1
.y
, pos2
.y
579 if pos1
.z
> pos2
.z
then
580 pos2
.z
, pos1
.z
= pos1
.z
, pos2
.z
585 -- [function] Prepare size
586 function schemedit
.size(pos
)
587 local pos1
= vector
.new(pos
)
588 local meta
= minetest
.get_meta(pos
)
589 local node
= minetest
.get_node(pos
)
590 local param2
= node
.param2
592 x
= meta
:get_int("x_size"),
593 y
= math
.max(meta
:get_int("y_size") - 1, 0),
594 z
= meta
:get_int("z_size"),
598 local new_pos
= vector
.add({x
= size
.z
, y
= size
.y
, z
= -size
.x
}, pos
)
600 new_pos
.z
= new_pos
.z
+ 1
602 elseif param2
== 2 then
603 local new_pos
= vector
.add({x
= -size
.x
, y
= size
.y
, z
= -size
.z
}, pos
)
605 new_pos
.x
= new_pos
.x
+ 1
607 elseif param2
== 3 then
608 local new_pos
= vector
.add({x
= -size
.z
, y
= size
.y
, z
= size
.x
}, pos
)
610 new_pos
.z
= new_pos
.z
- 1
613 local new_pos
= vector
.add(size
, pos
)
615 new_pos
.x
= new_pos
.x
- 1
620 -- [function] Mark region
621 function schemedit
.mark(pos
)
622 schemedit
.unmark(pos
)
624 local id
= minetest
.hash_node_position(pos
)
625 local owner
= minetest
.get_meta(pos
):get_string("owner")
626 local pos1
, pos2
= schemedit
.size(pos
)
627 pos1
, pos2
= schemedit
.sort_pos(pos1
, pos2
)
629 local thickness
= 0.2
630 local sizex
, sizey
, sizez
= (1 + pos2
.x
- pos1
.x
) / 2, (1 + pos2
.y
- pos1
.y
) / 2, (1 + pos2
.z
- pos1
.z
) / 2
636 for _
, z
in ipairs({pos1
.z
- 0.5, pos2
.z
+ 0.5}) do
642 local marker
= minetest
.add_entity({x
= pos1
.x
+ sizex
- 0.5, y
= pos1
.y
+ sizey
- 0.5, z
= z
+ offset
}, "schemedit:display")
643 if marker
~= nil then
644 marker
:set_properties({
645 visual_size
={x
=(sizex
+0.01) * 2, y
=sizey
* 2},
647 marker
:get_luaentity().id
= id
648 marker
:get_luaentity().owner
= owner
649 table.insert(m
, marker
)
656 for _
, x
in ipairs({pos1
.x
- 0.5, pos2
.x
+ 0.5}) do
663 local marker
= minetest
.add_entity({x
= x
+ offset
, y
= pos1
.y
+ sizey
- 0.5, z
= pos1
.z
+ sizez
- 0.5}, "schemedit:display")
664 if marker
~= nil then
665 marker
:set_properties({
666 visual_size
={x
=(sizez
+0.01) * 2, y
=sizey
* 2},
668 marker
:set_yaw(math
.pi
/ 2)
669 marker
:get_luaentity().id
= id
670 marker
:get_luaentity().owner
= owner
671 table.insert(m
, marker
)
676 schemedit
.markers
[id
] = m
680 -- [function] Unmark region
681 function schemedit
.unmark(pos
)
682 local id
= minetest
.hash_node_position(pos
)
683 if schemedit
.markers
[id
] then
685 for _
, entity
in ipairs(schemedit
.markers
[id
]) do
694 --- Mark node probability values near player
697 -- Show probability and force_place status of a particular position for player in HUD.
698 -- Probability is shown as a number followed by “[F]” if the node is force-placed.
699 -- The distance to the node is also displayed below that. This can't be avoided and is
700 -- and artifact of the waypoint HUD element. TODO: Hide displayed distance.
701 function schemedit
.display_node_prob(player
, pos
, prob
, force_place
)
703 if prob
and force_place
== true then
704 wpstring
= string.format("%d [F]", prob
)
707 elseif force_place
== true then
711 return player
:hud_add({
712 hud_elem_type
= "waypoint",
714 text
= "m", -- For the distance artifact
715 number = text_color_number
,
721 -- Display the node probabilities and force_place status of the nodes in a region.
722 -- By default, this is done for nodes near the player (distance: 5).
723 -- But the boundaries can optionally be set explicitly with pos1 and pos2.
724 function schemedit
.display_node_probs_region(player
, pos1
, pos2
)
725 local playername
= player
:get_player_name()
726 local pos
= vector
.round(player
:getpos())
729 -- Default: 5 nodes away from player in any direction
731 pos1
= vector
.subtract(pos
, dist
)
734 pos2
= vector
.add(pos
, dist
)
736 for x
=pos1
.x
, pos2
.x
do
737 for y
=pos1
.y
, pos2
.y
do
738 for z
=pos1
.z
, pos2
.z
do
739 local checkpos
= {x
=x
, y
=y
, z
=z
}
740 local nodehash
= minetest
.hash_node_position(checkpos
)
742 -- If node is already displayed, remove it so it can re replaced later
743 if displayed_waypoints
[playername
][nodehash
] then
744 player
:hud_remove(displayed_waypoints
[playername
][nodehash
])
745 displayed_waypoints
[playername
][nodehash
] = nil
748 local prob
, force_place
749 local meta
= minetest
.get_meta(checkpos
)
750 prob
= tonumber(meta
:get_string("schemedit_prob"))
751 force_place
= meta
:get_string("schemedit_force_place") == "true"
752 local hud_id
= schemedit
.display_node_prob(player
, checkpos
, prob
, force_place
)
754 displayed_waypoints
[playername
][nodehash
] = hud_id
755 displayed_waypoints
[playername
].display_active
= true
762 -- Remove all active displayed node statuses.
763 function schemedit
.clear_displayed_node_probs(player
)
764 local playername
= player
:get_player_name()
765 for nodehash
, hud_id
in pairs(displayed_waypoints
[playername
]) do
766 player
:hud_remove(hud_id
)
767 displayed_waypoints
[playername
][nodehash
] = nil
768 displayed_waypoints
[playername
].display_active
= false
772 minetest
.register_on_joinplayer(function(player
)
773 displayed_waypoints
[player
:get_player_name()] = {
774 display_active
= false -- If true, there *might* be at least one active node prob HUD display
775 -- If false, no node probabilities are displayed for sure.
779 minetest
.register_on_leaveplayer(function(player
)
780 displayed_waypoints
[player
:get_player_name()] = nil
783 -- Regularily clear the displayed node probabilities and force_place
784 -- for all players who do not wield the probtool.
785 -- This makes sure the screen is not spammed with information when it
788 minetest
.register_globalstep(function(dtime
)
789 cleartimer
= cleartimer
+ dtime
790 if cleartimer
> 2 then
791 local players
= minetest
.get_connected_players()
792 for p
= 1, #players
do
793 local player
= players
[p
]
794 local pname
= player
:get_player_name()
795 if displayed_waypoints
[pname
].display_active
then
796 local item
= player
:get_wielded_item()
797 if item
:get_name() ~= "schemedit:probtool" then
798 schemedit
.clear_displayed_node_probs(player
)
810 -- [priv] schematic_override
811 minetest
.register_privilege("schematic_override", {
812 description
= "Allows you to access schemedit nodes not owned by you",
813 give_to_singleplayer
= false,
816 -- [node] Schematic creator
817 minetest
.register_node("schemedit:creator", {
818 description
= "Schematic Creator",
819 _doc_items_longdesc
= "The schematic creator is used to save a region of the world into a schematic file (.mts).",
820 _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"..
821 "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. Note you can use this name in the /placeschem command to place the schematic again.".."\n\n"..
822 "The other features of the schematic creator are optional and are used to allow to add randomness and fine-tuning.".."\n\n"..
823 "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 255 (255 is for 100%). By default, all Y slices occour always.".."\n\n"..
824 "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.",
825 tiles
= {"schemedit_creator_top.png", "schemedit_creator_bottom.png",
826 "schemedit_creator_sides.png"},
827 groups
= { dig_immediate
= 2},
828 paramtype2
= "facedir",
829 is_ground_content
= false,
831 after_place_node
= function(pos
, player
)
832 local name
= player
:get_player_name()
833 local meta
= minetest
.get_meta(pos
)
835 meta
:set_string("owner", name
)
836 meta
:set_string("infotext", "Schematic Creator\n(owned by "..name
..")")
837 meta
:set_string("prob_list", minetest
.serialize({}))
838 meta
:set_string("slices", minetest
.serialize({}))
840 local node
= minetest
.get_node(pos
)
841 local dir
= minetest
.facedir_to_dir(node
.param2
)
843 meta
:set_int("x_size", 1)
844 meta
:set_int("y_size", 1)
845 meta
:set_int("z_size", 1)
847 -- Don't take item from itemstack
850 can_dig
= function(pos
, player
)
851 local name
= player
:get_player_name()
852 local meta
= minetest
.get_meta(pos
)
853 if meta
:get_string("owner") == name
or
854 minetest
.check_player_privs(player
, "schematic_override") == true then
860 on_rightclick
= function(pos
, node
, player
)
861 local meta
= minetest
.get_meta(pos
)
862 local name
= player
:get_player_name()
863 if meta
:get_string("owner") == name
or
864 minetest
.check_player_privs(player
, "schematic_override") == true then
865 -- Get player attribute
866 local tab
= player
:get_attribute("schemedit:tab")
867 if not forms
[tab
] or not tab
then
871 schemedit
.show_formspec(pos
, player
, tab
, true)
874 after_destruct
= function(pos
)
875 schemedit
.unmark(pos
)
879 minetest
.register_tool("schemedit:probtool", {
880 description
= "Schematic Node Probability Tool",
881 _doc_items_longdesc
=
882 "This is an advanced tool which only makes sense when used together with a schematic creator. It is used to finetune the way how nodes from a schematic are placed.".."\n"..
883 "It allows you to set two things:".."\n"..
884 "1) Set probability: Chance for any particular node to be actually placed (default: always placed)".."\n"..
885 "2) Enable force placement: These nodes replace node other than air and ignored when placed in a schematic (default: off)",
886 _doc_items_usagehelp
= "\n"..
887 "BASIC USAGE:".."\n"..
888 "Punch to configure the tool. Select a probability (0-255; 255 is for 100%) and enable or disable force placement. Now place the tool on any node to apply these values to the node. This information is preserved in the node until it is destroyed or changed by the tool again. This tool has no effect on schematic voids.".."\n"..
889 "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\n"..
891 "To help you remember the node values, the nodes with special values are labelled in the HUD. The first line shows probability and force placement (with “[F]”). The second line is the current distance to the node. Nodes with default settings and schematic voids are not labelled.".."\n"..
892 "To disable the node HUD, unselect the tool or hit “place” while not pointing anything.".."\n\n"..
893 "UPDATING THE NODE HUD:".."\n"..
894 "The node HUD is not updated automatically any may be outdated. The node HUD only updates the HUD for nodes close to you whenever you place the tool or press the punch and sneak keys simutanously. If you sneak-punch a schematic creator, then the node HUd is updated for all nodes within the schematic creator's region, even if this region is very big.",
895 wield_image
= "schemedit_probtool.png",
896 inventory_image
= "schemedit_probtool.png",
897 liquids_pointable
= true,
898 on_use
= function(itemstack
, user
, pointed_thing
)
899 local ctrl
= user
:get_player_control()
901 if not ctrl
.sneak
then
902 -- Open dialog to change the probability to apply to nodes
903 schemedit
.show_formspec(user
:getpos(), user
, "probtool", true)
907 -- Display the probability and force_place values for nodes.
909 -- If a schematic creator was punched, only enable display for all nodes
910 -- within the creator's region.
911 local use_creator_region
= false
912 if pointed_thing
and pointed_thing
.type == "node" and pointed_thing
.under
then
913 punchpos
= pointed_thing
.under
914 local node
= minetest
.get_node(punchpos
)
915 if node
.name
== "schemedit:creator" then
916 local pos1
, pos2
= schemedit
.size(punchpos
)
917 pos1
, pos2
= schemedit
.sort_pos(pos1
, pos2
)
918 schemedit
.display_node_probs_region(user
, pos1
, pos2
)
923 -- Otherwise, just display the region close to the player
924 schemedit
.display_node_probs_region(user
)
927 on_secondary_use
= function(itemstack
, user
, pointed_thing
)
928 schemedit
.clear_displayed_node_probs(user
)
930 -- Set note probability and force_place and enable node probability display
931 on_place
= function(itemstack
, placer
, pointed_thing
)
932 -- Use pointed node's on_rightclick function first, if present
933 local node
= minetest
.get_node(pointed_thing
.under
)
934 if placer
and not placer
:get_player_control().sneak
then
935 if minetest
.registered_nodes
[node
.name
] and minetest
.registered_nodes
[node
.name
].on_rightclick
then
936 return minetest
.registered_nodes
[node
.name
].on_rightclick(pointed_thing
.under
, node
, placer
, itemstack
) or itemstack
940 -- This sets the node probability of pointed node to the
941 -- currently used probability stored in the tool.
942 local pos
= pointed_thing
.under
943 local node
= minetest
.get_node(pos
)
944 -- Schematic void are ignored, they always have probability 0
945 if node
.name
== "schemedit:void" then
948 local nmeta
= minetest
.get_meta(pos
)
949 local imeta
= itemstack
:get_meta()
950 local prob
= tonumber(imeta
:get_string("schemedit_prob"))
951 local force_place
= imeta
:get_string("schemedit_force_place")
953 if not prob
or prob
== 255 then
954 nmeta
:set_string("schemedit_prob", nil)
956 nmeta
:set_string("schemedit_prob", prob
)
958 if force_place
== "true" then
959 nmeta
:set_string("schemedit_force_place", "true")
961 nmeta
:set_string("schemedit_force_place", nil)
964 -- Enable node probablity display
965 schemedit
.display_node_probs_region(placer
)
971 minetest
.register_node("schemedit:void", {
972 description
= "Schematic Void",
973 _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.",
974 _doc_items_usagehelp
= "Just place the schematic void like any other block and use the schematic creator to save a portion of the world.",
975 tiles
= { "schemedit_void.png" },
976 drawtype
= "nodebox",
977 is_ground_content
= false,
980 sunlight_propagates
= true,
984 { -4/16, -4/16, -4/16, 4/16, 4/16, 4/16 },
987 groups
= { dig_immediate
= 3},
990 -- [entity] Visible schematic border
991 minetest
.register_entity("schemedit:display", {
992 visual
= "upright_sprite",
993 textures
= {"schemedit_border.png"},
994 visual_size
= {x
=10, y
=10},
995 collisionbox
= {0,0,0,0,0,0},
998 on_step
= function(self
, dtime
)
1000 self
.object
:remove()
1001 elseif not schemedit
.markers
[self
.id
] then
1002 self
.object
:remove()
1005 on_activate
= function(self
)
1006 self
.object
:set_armor_groups({immortal
= 1})
1010 -- [chatcommand] Place schematic
1011 minetest
.register_chatcommand("placeschem", {
1012 description
= "Place schematic at the position specified or the current "..
1013 "player position (loaded from "..export_path_full
..".",
1014 privs
= {debug
= true},
1015 params
= "<schematic name>[.mts] [<x> <y> <z>]",
1016 func
= function(name
, param
)
1017 local schem
, p
= string.match(param
, "^([^ ]+) *(.*)$")
1018 local pos
= minetest
.string_to_pos(p
)
1021 return false, "No schematic file specified."
1025 pos
= minetest
.get_player_by_name(name
):get_pos()
1028 -- Automatiically add file name suffix if omitted
1030 if string.sub(schem
, string.len(schem
)-3, string.len(schem
)) == ".mts" then
1033 schem_full
= schem
.. ".mts"
1036 local success
= minetest
.place_schematic(pos
, export_path_full
.. DIR_DELIM
.. schem_full
, "random", nil, false)
1038 if success
== nil then
1039 return false, "Schematic file could not be loaded!"