Automatically display node meta when use probtool
[minetest_schemedit.git] / init.lua
blob5f76d810cfe1c909bf32c2a568fb5c9f96b5947f
1 -- advschem/init.lua
3 local advschem = {}
5 -- Directory delimeter fallback (normally comes from builtin)
6 if not DIR_DELIM then
7 DIR_DELIM = "/"
8 end
9 local export_path_full = table.concat({minetest.get_worldpath(), "schems"}, DIR_DELIM)
11 local text_color = "#D79E9E"
12 local text_color_number = 0xD79E9E
14 advschem.markers = {}
16 -- [local function] Renumber table
17 local function renumber(t)
18 local res = {}
19 for _, i in pairs(t) do
20 res[#res + 1] = i
21 end
22 return res
23 end
25 ---
26 --- Formspec API
27 ---
29 local contexts = {}
30 local form_data = {}
31 local tabs = {}
32 local forms = {}
33 local displayed_waypoints = {}
35 -- [function] Add form
36 function advschem.add_form(name, def)
37 def.name = name
38 forms[name] = def
40 if def.tab then
41 tabs[#tabs + 1] = name
42 end
43 end
45 -- [function] Generate tabs
46 function advschem.generate_tabs(current)
47 local retval = "tabheader[0,0;tabs;"
48 for _, t in pairs(tabs) do
49 local f = forms[t]
50 if f.tab ~= false and f.caption then
51 retval = retval..f.caption..","
53 if type(current) ~= "number" and current == f.name then
54 current = _
55 end
56 end
57 end
58 retval = retval:sub(1, -2) -- Strip last comma
59 retval = retval..";"..current.."]" -- Close tabheader
60 return retval
61 end
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)
68 return true
69 end
70 end
72 -- [function] Show formspec
73 function advschem.show_formspec(pos, player, tab, show, ...)
74 if forms[tab] then
75 if type(player) == "string" then
76 player = minetest.get_player_by_name(player)
77 end
78 local name = player:get_player_name()
80 if show ~= false then
81 if not form_data[name] then
82 form_data[name] = {}
83 end
85 local form = forms[tab].get(form_data[name], pos, name, ...)
86 if forms[tab].tab then
87 form = form..advschem.generate_tabs(tab)
88 end
90 minetest.show_formspec(name, "advschem:"..tab, form)
91 contexts[name] = pos
93 -- Update player attribute
94 if forms[tab].cache_name ~= false then
95 player:set_attribute("advschem:tab", tab)
96 end
97 else
98 minetest.close_formspec(pname, "advschem:"..tab)
99 end
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
112 form_data[name] = {}
115 if not advschem.handle_tabs(contexts[name], name, fields) and handle then
116 handle(form_data[name], contexts[name], name, fields)
120 end)
122 -- Helper function. Scans probabilities of all nodes in the given area and returns a prob_list
123 advschem.scan_metadata = function(pos1, pos2)
124 local prob_list = {}
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
134 prob = 0
135 force_place = false
136 else
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")
141 if fp == "true" then
142 force_place = true
143 else
144 force_place = false
148 local ostrpos = minetest.pos_to_string(scanpos)
149 prob_list[ostrpos] = {
150 pos = scanpos,
151 prob = prob,
152 force_place = force_place,
158 return prob_list
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
173 prob_desc = ""
174 smeta:set_string("advschem_prob", nil)
175 else
176 prob_desc = "\nProbability: "..(smeta:get_string("advschem_prob") or
177 "Not Set")
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
191 desc = meta_desc
194 local original_desc = smeta:get_string("original_description")
195 if original_desc and original_desc ~= "" then
196 desc = original_desc
197 else
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)
210 return itemstack
214 --- Formspec Tabs
217 advschem.add_form("main", {
218 tab = true,
219 caption = "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)
224 local border_button
225 if meta.schem_border == "true" and advschem.markers[strpos] then
226 border_button = "button[3.5,7.5;3,1;border;Hide border]"
227 else
228 border_button = "button[3.5,7.5;3,1;border;Show border]"
231 -- TODO: Show information regarding volume, pos1, pos2, etc... in formspec
232 return [[
233 size[7,8]
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]
253 ]]..
254 border_button
255 end,
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)
261 -- Toggle border
262 if fields.border then
263 if meta.schem_border == "true" and advschem.markers[strpos] then
264 advschem.unmark(pos)
265 meta.schem_border = "false"
266 else
267 advschem.mark(pos)
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)
280 if x then
281 meta.x_size = math.max(x, 1)
283 if y then
284 meta.y_size = math.max(y, 1)
286 if z then
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
300 -- Export schematic
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
304 minetest.mkdir(path)
306 local plist = advschem.scan_metadata(pos1, pos2)
307 local probability_list = {}
308 for _, i in pairs(plist) do
309 local prob = i.prob
310 if i.force_place == true then
311 prob = prob + 128
314 probability_list[#probability_list + 1] = {
315 pos = minetest.string_to_pos(_),
316 prob = prob,
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,
325 prob = i.prob,
329 local filepath = path..meta.schem_name..".mts"
330 local res = minetest.create_schematic(pos1, pos2, probability_list, filepath, slice_list)
332 if res then
333 minetest.chat_send_player(name, minetest.colorize("#00ff00",
334 "Exported schematic to "..filepath))
335 else
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})
345 -- Update border
346 if not fields.border and meta.schem_border == "true" then
347 advschem.mark(pos)
350 -- Update formspec
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)
359 end,
362 advschem.add_form("slice", {
363 caption = "Y Slices",
364 tab = true,
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)
371 local 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
378 local form = [[
379 size[7,8]
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
392 form = form..[[
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]
397 ]]..done_button
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
406 form = form..[[
407 button[2,6;2,1;remove;- Remove slice]
408 button[4,6;2,1;edit;+/- Edit slice]
410 else
411 form = form..[[
412 button[2,6;2,1;remove;- Remove slice]
413 button[4,6;2,1;edit;+/- Edit slice]
418 return form
419 end,
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])
429 if fields.add then
430 if not self.panel_add then
431 self.panel_add = true
432 advschem.show_formspec(pos, player, "slice")
433 else
434 self.panel_add = nil
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
454 self.panel_add = nil
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)))
463 -- Update formspec
464 self.selected = 1
465 self.panel_edit = nil
466 advschem.show_formspec(pos, player, "slice")
469 if fields.edit then
470 if not self.panel_edit then
471 self.panel_edit = true
472 advschem.show_formspec(pos, player, "slice")
473 else
474 self.panel_edit = nil
475 advschem.show_formspec(pos, player, "slice")
478 end,
481 advschem.add_form("probtool", {
482 cache_name = false,
483 caption = "Schematic Node Probability Tool",
484 get = function(self, pos, name)
485 local player = minetest.get_player_by_name(name)
486 if not player then
487 return
489 local probtool = player:get_wielded_item()
490 if probtool:get_name() ~= "advschem:probtool" then
491 return
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")
498 if not prob then
499 prob = 127
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]"
513 return form
514 end,
515 handle = function(self, pos, name, fields)
516 if fields.submit then
517 local prob = tonumber(fields.prob)
518 if prob then
519 local player = minetest.get_player_by_name(name)
520 if not player then
521 return
523 local probtool = player:get_wielded_item()
524 if probtool:get_name() ~= "advschem:probtool" then
525 return
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
540 end,
544 --- API
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
552 return
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
565 return pos1, pos2
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
574 local size = {
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"),
580 if param2 == 1 then
581 local new_pos = vector.add({x = size.z, y = size.y, z = -size.x}, pos)
582 pos1.x = pos1.x + 1
583 new_pos.z = new_pos.z + 1
584 return pos1, new_pos
585 elseif param2 == 2 then
586 local new_pos = vector.add({x = -size.x, y = size.y, z = -size.z}, pos)
587 pos1.z = pos1.z - 1
588 new_pos.x = new_pos.x + 1
589 return pos1, new_pos
590 elseif param2 == 3 then
591 local new_pos = vector.add({x = -size.z, y = size.y, z = size.x}, pos)
592 pos1.x = pos1.x - 1
593 new_pos.z = new_pos.z - 1
594 return pos1, new_pos
595 else
596 local new_pos = vector.add(size, pos)
597 pos1.z = pos1.z + 1
598 new_pos.x = new_pos.x - 1
599 return pos1, new_pos
603 -- [function] Mark region
604 function advschem.mark(pos)
605 advschem.unmark(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
614 local m = {}
615 local low = true
616 local offset
618 -- XY plane markers
619 for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do
620 if low then
621 offset = -0.01
622 else
623 offset = 0.01
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)
634 low = false
637 low = true
638 -- YZ plane markers
639 for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do
640 if low then
641 offset = -0.01
642 else
643 offset = 0.01
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)
656 low = false
659 advschem.markers[id] = m
660 return true
663 -- [function] Unmark region
664 function advschem.unmark(pos)
665 local id = minetest.pos_to_string(pos)
666 if advschem.markers[id] then
667 local retval
668 for _, entity in ipairs(advschem.markers[id]) do
669 entity:remove()
670 retval = true
672 return retval
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)
685 local wpstring
686 if prob and force_place == true then
687 wpstring = string.format("%d [F]", prob)
688 elseif prob then
689 wpstring = prob
690 elseif force_place == true then
691 wpstring = "[F]"
693 if wpstring then
694 return player:hud_add({
695 hud_elem_type = "waypoint",
696 name = wpstring,
697 text = "m", -- For the distance artifact
698 number = text_color_number,
699 world_pos = pos,
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())
708 local dist = 5
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)
726 if hud_id then
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()] = {}
744 end)
746 minetest.register_on_leaveplayer(function(player)
747 displayed_waypoints[player:get_player_name()] = nil
748 end)
751 --- Registrations
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
797 return true
798 end,
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
804 return true
807 return false
808 end,
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
817 tab = "main"
820 advschem.show_formspec(pos, player, tab, true)
822 end,
823 after_destruct = function(pos)
824 advschem.unmark(pos)
825 end,
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)
842 end,
843 on_secondary_use = function(itemstack, user, pointed_thing)
844 advschem.clear_displayed_node_probs(user)
845 end,
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
855 return itemstack
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)
864 else
865 nmeta:set_string("advschem_prob", prob)
867 if force_place == "true" then
868 nmeta:set_string("advschem_force_place", "true")
869 else
870 nmeta:set_string("advschem_force_place", nil)
873 -- Enable node probablity display
874 advschem.display_node_probs_around_player(placer)
876 return itemstack
877 end,
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,
887 paramtype = "light",
888 walkable = false,
889 sunlight_propagates = true,
890 node_box = {
891 type = "fixed",
892 fixed = {
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},
905 physical = false,
907 on_step = function(self, dtime)
908 if not self.id then
909 self.object:remove()
910 elseif not advschem.markers[self.id] then
911 self.object:remove()
913 end,
914 on_activate = function(self)
915 self.object:set_armor_groups({immortal = 1})
916 end,
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)
929 if not schem then
930 return false, "No schematic file specified."
933 if not pos then
934 pos = minetest.get_player_by_name(name):get_pos()
937 -- Automatiically add file name suffix if omitted
938 local schem_full
939 if string.sub(schem, string.len(schem)-3, string.len(schem)) == ".mts" then
940 schem_full = schem
941 else
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!"
949 else
950 return true
952 end,