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 hashpos
= minetest
.hash_node_position(scanpos
)
149 prob_list
[hashpos
] = {
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
)
223 local hashpos
= minetest
.hash_node_position(pos
)
226 if meta
.schem_border
== "true" and advschem
.markers
[hashpos
] then
227 border_button
= "button[3.5,7.5;3,1;border;Hide border]"
229 border_button
= "button[3.5,7.5;3,1;border;Show border]"
232 -- TODO: Show information regarding volume, pos1, pos2, etc... in formspec
235 label[0.5,-0.1;Position: ]]..strpos
..[[]
236 label[3,-0.1;Owner: ]]..name
..[[]
238 field[0.8,1;5,1;name;Schematic name:;]]..minetest
.formspec_escape(meta
.schem_name
or "")..[[]
239 button[5.3,0.69;1.2,1;save_name;Save]
240 tooltip[save_name;Save schematic name]
241 field_close_on_enter[name;false]
243 button[0.5,1.5;6,1;export;Export schematic]
244 textarea[0.8,2.5;6.2,5;;The schematic will be exported as a .mts file and stored in]]..
245 "\n" .. export_path_full
.. DIR_DELIM
.. [[<name>.mts.;]
246 field[0.8,7;2,1;x;X size:;]]..meta
.x_size
..[[]
247 field[2.8,7;2,1;y;Y size:;]]..meta
.y_size
..[[]
248 field[4.8,7;2,1;z;Z size:;]]..meta
.z_size
..[[]
249 field_close_on_enter[x;false]
250 field_close_on_enter[y;false]
251 field_close_on_enter[z;false]
253 button[0.5,7.5;3,1;save;Save size]
257 handle
= function(self
, pos
, name
, fields
)
258 local realmeta
= minetest
.get_meta(pos
)
259 local meta
= realmeta
:to_table().fields
260 local hashpos
= minetest
.hash_node_position(pos
)
263 if fields
.border
then
264 if meta
.schem_border
== "true" and advschem
.markers
[hashpos
] then
266 meta
.schem_border
= "false"
269 meta
.schem_border
= "true"
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)
291 -- Save schematic name
292 if fields
.save_name
or fields
.key_enter_field
== "name" and fields
.name
and
293 fields
.name
~= "" then
294 meta
.schem_name
= fields
.name
298 if fields
.export
and meta
.schem_name
and meta
.schem_name
~= "" then
299 local pos1
, pos2
= advschem
.size(pos
)
300 pos1
, pos2
= advschem
.sort_pos(pos1
, pos2
)
301 local path
= export_path_full
.. DIR_DELIM
304 local plist
= advschem
.scan_metadata(pos1
, pos2
)
305 local probability_list
= {}
306 for hash
, i
in pairs(plist
) do
308 if i
.force_place
== true then
312 table.insert(probability_list
, {
313 pos
= minetest
.get_position_from_hash(hash
),
318 local slist
= minetest
.deserialize(meta
.slices
)
319 local slice_list
= {}
320 for _
, i
in pairs(slist
) do
321 slice_list
[#slice_list
+ 1] = {
322 ypos
= pos
.y
+ i
.ypos
,
327 local filepath
= path
..meta
.schem_name
..".mts"
328 local res
= minetest
.create_schematic(pos1
, pos2
, probability_list
, filepath
, slice_list
)
331 minetest
.chat_send_player(name
, minetest
.colorize("#00ff00",
332 "Exported schematic to "..filepath
))
334 minetest
.chat_send_player(name
, minetest
.colorize("red",
335 "Failed to export schematic to "..filepath
))
339 -- Save meta before updating visuals
340 local inv
= realmeta
:get_inventory():get_lists()
341 realmeta
:from_table({fields
= meta
, inventory
= inv
})
344 if not fields
.border
and meta
.schem_border
== "true" then
349 if not fields
.quit
then
350 advschem
.show_formspec(pos
, minetest
.get_player_by_name(name
), "main")
355 advschem
.add_form("slice", {
356 caption
= "Y Slices",
358 get
= function(self
, pos
, name
, visible_panel
)
359 local meta
= minetest
.get_meta(pos
):to_table().fields
361 self
.selected
= self
.selected
or 1
362 local selected
= tostring(self
.selected
)
363 local slice_list
= minetest
.deserialize(meta
.slices
)
365 for _
, i
in pairs(slice_list
) do
366 local insert
= "Y = "..tostring(i
.ypos
).."; Probability = "..tostring(i
.prob
)
367 slices
= slices
..minetest
.formspec_escape(insert
)..","
369 slices
= slices
:sub(1, -2) -- Remove final comma
373 table[0,0;6.8,6;slices;]]..slices
..[[;]]..selected
..[[]
376 if self
.panel_add
or self
.panel_edit
then
377 local ypos_default
, prob_default
= "", ""
378 local done_button
= "button[5,7.18;2,1;done_add;Done]"
379 if self
.panel_edit
then
380 done_button
= "button[5,7.18;2,1;done_edit;Done]"
381 ypos_default
= slice_list
[self
.selected
].ypos
382 prob_default
= slice_list
[self
.selected
].prob
386 field[0.3,7.5;2.5,1;ypos;Y position (max. ]]..(meta
.y_size
- 1)..[[):;]]..ypos_default
..[[]
387 field[2.8,7.5;2.5,1;prob;Probability (0-127):;]]..prob_default
..[[]
388 field_close_on_enter[ypos;false]
389 field_close_on_enter[prob;false]
393 if not self
.panel_edit
then
394 form
= form
.."button[0,6;2,1;add;+ Add slice]"
397 if slices
~= "" and self
.selected
and not self
.panel_add
then
398 if not self
.panel_edit
then
400 button[2,6;2,1;remove;- Remove slice]
401 button[4,6;2,1;edit;+/- Edit slice]
405 button[2,6;2,1;remove;- Remove slice]
406 button[4,6;2,1;edit;+/- Edit slice]
413 handle
= function(self
, pos
, name
, fields
)
414 local meta
= minetest
.get_meta(pos
)
415 local player
= minetest
.get_player_by_name(name
)
417 if fields
.slices
then
418 local slices
= fields
.slices
:split(":")
419 self
.selected
= tonumber(slices
[2])
423 if not self
.panel_add
then
424 self
.panel_add
= true
425 advschem
.show_formspec(pos
, player
, "slice")
428 advschem
.show_formspec(pos
, player
, "slice")
432 local ypos
, prob
= tonumber(fields
.ypos
), tonumber(fields
.prob
)
433 if (fields
.done_add
or fields
.done_edit
) and fields
.ypos
and fields
.prob
and
434 fields
.ypos
~= "" and fields
.prob
~= "" and ypos
and prob
and
435 ypos
<= (meta
:get_int("y_size") - 1) and prob
>= 0 and prob
<= 255 then
436 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
437 local index
= #slice_list
+ 1
438 if fields
.done_edit
then
439 index
= self
.selected
442 slice_list
[index
] = {ypos
= ypos
, prob
= prob
}
444 meta
:set_string("slices", minetest
.serialize(slice_list
))
446 -- Update and show formspec
448 advschem
.show_formspec(pos
, player
, "slice")
451 if fields
.remove and self
.selected
then
452 local slice_list
= minetest
.deserialize(meta
:get_string("slices"))
453 slice_list
[self
.selected
] = nil
454 meta
:set_string("slices", minetest
.serialize(renumber(slice_list
)))
458 self
.panel_edit
= nil
459 advschem
.show_formspec(pos
, player
, "slice")
463 if not self
.panel_edit
then
464 self
.panel_edit
= true
465 advschem
.show_formspec(pos
, player
, "slice")
467 self
.panel_edit
= nil
468 advschem
.show_formspec(pos
, player
, "slice")
474 advschem
.add_form("probtool", {
476 caption
= "Schematic Node Probability Tool",
477 get
= function(self
, pos
, name
)
478 local player
= minetest
.get_player_by_name(name
)
482 local probtool
= player
:get_wielded_item()
483 if probtool
:get_name() ~= "advschem:probtool" then
487 local meta
= probtool
:get_meta()
488 local prob
= tonumber(meta
:get_string("advschem_prob"))
489 local force_place
= meta
:get_string("advschem_force_place")
494 if force_place
== nil or force_place
== "" then
495 force_place
= "false"
497 local form
= "size[5,4]"..
498 "label[0,0;Schematic Node Probability Tool]"..
499 "field[0.75,1;4,1;prob;Probability (0-127);"..prob
.."]"..
500 "checkbox[0.60,1.5;force_place;Force placement;" .. force_place
.. "]" ..
501 "button_exit[0.25,3;2,1;cancel;Cancel]"..
502 "button_exit[2.75,3;2,1;submit;Apply]"..
503 "tooltip[prob;Probability that the node will be placed]"..
504 "tooltip[force_place;If enabled, the node will replace nodes other than air and ignore]"..
505 "field_close_on_enter[prob;false]"
508 handle
= function(self
, pos
, name
, fields
)
509 if fields
.submit
then
510 local prob
= tonumber(fields
.prob
)
512 local player
= minetest
.get_player_by_name(name
)
516 local probtool
= player
:get_wielded_item()
517 if probtool
:get_name() ~= "advschem:probtool" then
521 local force_place
= self
.force_place
== true
523 set_item_metadata(probtool
, prob
, force_place
)
525 player
:set_wielded_item(probtool
)
528 if fields
.force_place
== "true" then
529 self
.force_place
= true
530 elseif fields
.force_place
== "false" then
531 self
.force_place
= false
540 --- Copies and modifies positions `pos1` and `pos2` so that each component of
541 -- `pos1` is less than or equal to the corresponding component of `pos2`.
542 -- Returns the new positions.
543 function advschem
.sort_pos(pos1
, pos2
)
544 if not pos1
or not pos2
then
548 pos1
, pos2
= table.copy(pos1
), table.copy(pos2
)
549 if pos1
.x
> pos2
.x
then
550 pos2
.x
, pos1
.x
= pos1
.x
, pos2
.x
552 if pos1
.y
> pos2
.y
then
553 pos2
.y
, pos1
.y
= pos1
.y
, pos2
.y
555 if pos1
.z
> pos2
.z
then
556 pos2
.z
, pos1
.z
= pos1
.z
, pos2
.z
561 -- [function] Prepare size
562 function advschem
.size(pos
)
563 local pos1
= vector
.new(pos
)
564 local meta
= minetest
.get_meta(pos
)
565 local node
= minetest
.get_node(pos
)
566 local param2
= node
.param2
568 x
= meta
:get_int("x_size"),
569 y
= math
.max(meta
:get_int("y_size") - 1, 0),
570 z
= meta
:get_int("z_size"),
574 local new_pos
= vector
.add({x
= size
.z
, y
= size
.y
, z
= -size
.x
}, pos
)
576 new_pos
.z
= new_pos
.z
+ 1
578 elseif param2
== 2 then
579 local new_pos
= vector
.add({x
= -size
.x
, y
= size
.y
, z
= -size
.z
}, pos
)
581 new_pos
.x
= new_pos
.x
+ 1
583 elseif param2
== 3 then
584 local new_pos
= vector
.add({x
= -size
.z
, y
= size
.y
, z
= size
.x
}, pos
)
586 new_pos
.z
= new_pos
.z
- 1
589 local new_pos
= vector
.add(size
, pos
)
591 new_pos
.x
= new_pos
.x
- 1
596 -- [function] Mark region
597 function advschem
.mark(pos
)
600 local id
= minetest
.hash_node_position(pos
)
601 local owner
= minetest
.get_meta(pos
):get_string("owner")
602 local pos1
, pos2
= advschem
.size(pos
)
603 pos1
, pos2
= advschem
.sort_pos(pos1
, pos2
)
605 local thickness
= 0.2
606 local sizex
, sizey
, sizez
= (1 + pos2
.x
- pos1
.x
) / 2, (1 + pos2
.y
- pos1
.y
) / 2, (1 + pos2
.z
- pos1
.z
) / 2
612 for _
, z
in ipairs({pos1
.z
- 0.5, pos2
.z
+ 0.5}) do
618 local marker
= minetest
.add_entity({x
= pos1
.x
+ sizex
- 0.5, y
= pos1
.y
+ sizey
- 0.5, z
= z
+ offset
}, "advschem:display")
619 if marker
~= nil then
620 marker
:set_properties({
621 visual_size
={x
=(sizex
+0.01) * 2, y
=sizey
* 2},
623 marker
:get_luaentity().id
= id
624 marker
:get_luaentity().owner
= owner
625 table.insert(m
, marker
)
632 for _
, x
in ipairs({pos1
.x
- 0.5, pos2
.x
+ 0.5}) do
639 local marker
= minetest
.add_entity({x
= x
+ offset
, y
= pos1
.y
+ sizey
- 0.5, z
= pos1
.z
+ sizez
- 0.5}, "advschem:display")
640 if marker
~= nil then
641 marker
:set_properties({
642 visual_size
={x
=(sizez
+0.01) * 2, y
=sizey
* 2},
644 marker
:set_yaw(math
.pi
/ 2)
645 marker
:get_luaentity().id
= id
646 marker
:get_luaentity().owner
= owner
647 table.insert(m
, marker
)
652 advschem
.markers
[id
] = m
656 -- [function] Unmark region
657 function advschem
.unmark(pos
)
658 local id
= minetest
.hash_node_position(pos
)
659 if advschem
.markers
[id
] then
661 for _
, entity
in ipairs(advschem
.markers
[id
]) do
670 --- Mark node probability values near player
673 -- Show probability and force_place status of a particular position for player in HUD.
674 -- Probability is shown as a number followed by “[F]” if the node is force-placed.
675 -- The distance to the node is also displayed below that. This can't be avoided and is
676 -- and artifact of the waypoint HUD element. TODO: Hide displayed distance.
677 function advschem
.display_node_prob(player
, pos
, prob
, force_place
)
679 if prob
and force_place
== true then
680 wpstring
= string.format("%d [F]", prob
)
683 elseif force_place
== true then
687 return player
:hud_add({
688 hud_elem_type
= "waypoint",
690 text
= "m", -- For the distance artifact
691 number = text_color_number
,
697 -- Display the node probabilities and force_place status of the nodes in a region.
698 -- By default, this is done for nodes near the player (distance: 5).
699 -- But the boundaries can optionally be set explicitly with pos1 and pos2.
700 function advschem
.display_node_probs_region(player
, pos1
, pos2
)
701 local playername
= player
:get_player_name()
702 local pos
= vector
.round(player
:getpos())
705 -- Default: 5 nodes away from player in any direction
707 pos1
= vector
.subtract(pos
, dist
)
710 pos2
= vector
.add(pos
, dist
)
712 for x
=pos1
.x
, pos2
.x
do
713 for y
=pos1
.y
, pos2
.y
do
714 for z
=pos1
.z
, pos2
.z
do
715 local checkpos
= {x
=x
, y
=y
, z
=z
}
716 local nodehash
= minetest
.hash_node_position(checkpos
)
718 -- If node is already displayed, remove it so it can re replaced later
719 if displayed_waypoints
[playername
][nodehash
] then
720 player
:hud_remove(displayed_waypoints
[playername
][nodehash
])
721 displayed_waypoints
[playername
][nodehash
] = nil
724 local prob
, force_place
725 local meta
= minetest
.get_meta(checkpos
)
726 prob
= tonumber(meta
:get_string("advschem_prob"))
727 force_place
= meta
:get_string("advschem_force_place") == "true"
728 local hud_id
= advschem
.display_node_prob(player
, checkpos
, prob
, force_place
)
730 displayed_waypoints
[playername
][nodehash
] = hud_id
731 displayed_waypoints
[playername
].display_active
= true
738 -- Remove all active displayed node statuses.
739 function advschem
.clear_displayed_node_probs(player
)
740 local playername
= player
:get_player_name()
741 for nodehash
, hud_id
in pairs(displayed_waypoints
[playername
]) do
742 player
:hud_remove(hud_id
)
743 displayed_waypoints
[playername
][nodehash
] = nil
744 displayed_waypoints
[playername
].display_active
= false
748 minetest
.register_on_joinplayer(function(player
)
749 displayed_waypoints
[player
:get_player_name()] = {
750 display_active
= false -- If true, there *might* be at least one active node prob HUD display
751 -- If false, no node probabilities are displayed for sure.
755 minetest
.register_on_leaveplayer(function(player
)
756 displayed_waypoints
[player
:get_player_name()] = nil
759 -- Regularily clear the displayed node probabilities and force_place
760 -- for all players who do not wield the probtool.
761 -- This makes sure the screen is not spammed with information when it
764 minetest
.register_globalstep(function(dtime
)
765 cleartimer
= cleartimer
+ dtime
766 if cleartimer
> 2 then
767 local players
= minetest
.get_connected_players()
768 for p
= 1, #players
do
769 local player
= players
[p
]
770 local pname
= player
:get_player_name()
771 if displayed_waypoints
[pname
].display_active
then
772 local item
= player
:get_wielded_item()
773 if item
:get_name() ~= "advschem:probtool" then
774 advschem
.clear_displayed_node_probs(player
)
786 -- [priv] schematic_override
787 minetest
.register_privilege("schematic_override", {
788 description
= "Allows you to access advschem nodes not owned by you",
789 give_to_singleplayer
= false,
792 -- [node] Schematic creator
793 minetest
.register_node("advschem:creator", {
794 description
= "Schematic Creator",
795 _doc_items_longdesc
= "The schematic creator is used to save a region of the world into a schematic file (.mts).",
796 _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"..
797 "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"..
798 "The other features of the schematic creator are optional and are used to allow to add randomness and fine-tuning.".."\n"..
799 "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"..
800 "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.",
801 tiles
= {"advschem_creator_top.png", "advschem_creator_bottom.png",
802 "advschem_creator_sides.png"},
803 groups
= { dig_immediate
= 2},
804 paramtype2
= "facedir",
805 is_ground_content
= false,
807 after_place_node
= function(pos
, player
)
808 local name
= player
:get_player_name()
809 local meta
= minetest
.get_meta(pos
)
811 meta
:set_string("owner", name
)
812 meta
:set_string("infotext", "Schematic Creator\n(owned by "..name
..")")
813 meta
:set_string("prob_list", minetest
.serialize({}))
814 meta
:set_string("slices", minetest
.serialize({}))
816 local node
= minetest
.get_node(pos
)
817 local dir
= minetest
.facedir_to_dir(node
.param2
)
819 meta
:set_int("x_size", 1)
820 meta
:set_int("y_size", 1)
821 meta
:set_int("z_size", 1)
823 -- Don't take item from itemstack
826 can_dig
= function(pos
, player
)
827 local name
= player
:get_player_name()
828 local meta
= minetest
.get_meta(pos
)
829 if meta
:get_string("owner") == name
or
830 minetest
.check_player_privs(player
, "schematic_override") == true then
836 on_rightclick
= function(pos
, node
, player
)
837 local meta
= minetest
.get_meta(pos
)
838 local name
= player
:get_player_name()
839 if meta
:get_string("owner") == name
or
840 minetest
.check_player_privs(player
, "schematic_override") == true then
841 -- Get player attribute
842 local tab
= player
:get_attribute("advschem:tab")
843 if not forms
[tab
] or not tab
then
847 advschem
.show_formspec(pos
, player
, tab
, true)
850 after_destruct
= function(pos
)
855 minetest
.register_tool("advschem:probtool", {
856 description
= "Schematic Node Probability Tool",
857 _doc_items_longdesc
=
858 "This tool can be used together with a schematic creator to finetune the way how nodes from a schematic are placed.".."\n"..
859 "It allows you to do two things:".."\n"..
860 "1) Set a chance for a particular node not to be placed in schematic".."\n"..
861 "2) Enable a node to replace blocks other than air and ignored when placed in a schematic",
862 _doc_items_usagehelp
= "\n"..
863 "BASIC USAGE:".."\n"..
864 "Punch to set the values to apply to nodes. Select a probability (0-127; 127 is for 100%) and to enable or disable force placement (force-placed nodes will overwrite any node). Now place the tool on any to apply these settings to the node. This information is preserved in the node until it is destroyed or changed by the tool again.".."\n"..
865 "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"..
867 "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"..
868 "To disable the node HUD, unselect the tool or hit “place” while not pointing anything.".."\n\n"..
869 "UPDATING THE NODE HUD:".."\n"..
870 "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.",
871 wield_image
= "advschem_probtool.png",
872 inventory_image
= "advschem_probtool.png",
873 liquids_pointable
= true,
874 on_use
= function(itemstack
, user
, pointed_thing
)
875 local ctrl
= user
:get_player_control()
877 if not ctrl
.sneak
then
878 -- Open dialog to change the probability to apply to nodes
879 advschem
.show_formspec(user
:getpos(), user
, "probtool", true)
883 -- Display the probability and force_place values for nodes.
885 -- If a schematic creator was punched, only enable display for all nodes
886 -- within the creator's region.
887 local use_creator_region
= false
888 if pointed_thing
and pointed_thing
.type == "node" and pointed_thing
.under
then
889 punchpos
= pointed_thing
.under
890 local node
= minetest
.get_node(punchpos
)
891 if node
.name
== "advschem:creator" then
892 local pos1
, pos2
= advschem
.size(punchpos
)
893 pos1
, pos2
= advschem
.sort_pos(pos1
, pos2
)
894 advschem
.display_node_probs_region(user
, pos1
, pos2
)
899 -- Otherwise, just display the region close to the player
900 advschem
.display_node_probs_region(user
)
903 on_secondary_use
= function(itemstack
, user
, pointed_thing
)
904 advschem
.clear_displayed_node_probs(user
)
906 -- Set note probability and force_place and enable node probability display
907 on_place
= function(itemstack
, placer
, pointed_thing
)
908 -- Use pointed node's on_rightclick function first, if present
909 local node
= minetest
.get_node(pointed_thing
.under
)
910 if placer
and not placer
:get_player_control().sneak
then
911 if minetest
.registered_nodes
[node
.name
] and minetest
.registered_nodes
[node
.name
].on_rightclick
then
912 return minetest
.registered_nodes
[node
.name
].on_rightclick(pointed_thing
.under
, node
, placer
, itemstack
) or itemstack
916 -- This sets the node probability of pointed node to the
917 -- currently used probability stored in the tool.
918 local pos
= pointed_thing
.under
919 local node
= minetest
.get_node(pos
)
920 -- Schematic void are ignored, they always have probability 0
921 if node
.name
== "advschem:void" then
924 local nmeta
= minetest
.get_meta(pos
)
925 local imeta
= itemstack
:get_meta()
926 local prob
= tonumber(imeta
:get_string("advschem_prob"))
927 local force_place
= imeta
:get_string("advschem_force_place")
929 if not prob
or prob
== 127 then
930 nmeta
:set_string("advschem_prob", nil)
932 nmeta
:set_string("advschem_prob", prob
)
934 if force_place
== "true" then
935 nmeta
:set_string("advschem_force_place", "true")
937 nmeta
:set_string("advschem_force_place", nil)
940 -- Enable node probablity display
941 advschem
.display_node_probs_region(placer
)
947 minetest
.register_node("advschem:void", {
948 description
= "Schematic Void",
949 _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.",
950 _doc_items_usagehelp
= "Just place the schematic void like any other block and use the schematic creator to save a portion of the world.",
951 tiles
= { "advschem_void.png" },
952 drawtype
= "nodebox",
953 is_ground_content
= false,
956 sunlight_propagates
= true,
960 { -4/16, -4/16, -4/16, 4/16, 4/16, 4/16 },
963 groups
= { dig_immediate
= 3},
966 -- [entity] Visible schematic border
967 minetest
.register_entity("advschem:display", {
968 visual
= "upright_sprite",
969 textures
= {"advschem_border.png"},
970 visual_size
= {x
=10, y
=10},
971 collisionbox
= {0,0,0,0,0,0},
974 on_step
= function(self
, dtime
)
977 elseif not advschem
.markers
[self
.id
] then
981 on_activate
= function(self
)
982 self
.object
:set_armor_groups({immortal
= 1})
986 -- [chatcommand] Place schematic
987 minetest
.register_chatcommand("placeschem", {
988 description
= "Place schematic at the position specified or the current "..
989 "player position (loaded from "..export_path_full
..".",
990 privs
= {debug
= true},
991 params
= "<schematic name>[.mts] [<x> <y> <z>]",
992 func
= function(name
, param
)
993 local schem
, p
= string.match(param
, "^([^ ]+) *(.*)$")
994 local pos
= minetest
.string_to_pos(p
)
997 return false, "No schematic file specified."
1001 pos
= minetest
.get_player_by_name(name
):get_pos()
1004 -- Automatiically add file name suffix if omitted
1006 if string.sub(schem
, string.len(schem
)-3, string.len(schem
)) == ".mts" then
1009 schem_full
= schem
.. ".mts"
1012 local success
= minetest
.place_schematic(pos
, export_path_full
.. DIR_DELIM
.. schem_full
, "random", nil, false)
1014 if success
== nil then
1015 return false, "Schematic file could not be loaded!"