mirror of
https://github.com/minetest-mods/digtron.git
synced 2024-11-19 21:33:43 +01:00
252 lines
12 KiB
Lua
252 lines
12 KiB
Lua
-- Note: builders go in group 4 and have both test_build and execute_build methods.
|
|
|
|
-- Builds objects in the targeted node. This is a complicated beastie.
|
|
minetest.register_node("digtron:builder", {
|
|
description = "Digtron Builder Module",
|
|
_doc_items_longdesc = digtron.doc.builder_longdesc,
|
|
_doc_items_usagehelp = digtron.doc.builder_usagehelp,
|
|
groups = {cracky = 3, oddly_breakable_by_hand=3, digtron = 4},
|
|
drop = "digtron:builder",
|
|
sounds = digtron.metal_sounds,
|
|
paramtype = "light",
|
|
paramtype2= "facedir",
|
|
is_ground_content = false,
|
|
tiles = {
|
|
"digtron_plate.png^[transformR90",
|
|
"digtron_plate.png^[transformR270",
|
|
"digtron_plate.png",
|
|
"digtron_plate.png^[transformR180",
|
|
"digtron_plate.png^digtron_builder.png",
|
|
"digtron_plate.png",
|
|
},
|
|
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "fixed",
|
|
fixed = {
|
|
{-0.25, 0.3125, 0.3125, 0.25, 0.5, 0.5}, -- FrontFrame_top
|
|
{-0.25, -0.5, 0.3125, 0.25, -0.3125, 0.5}, -- FrontFrame_bottom
|
|
{0.3125, -0.25, 0.3125, 0.5, 0.25, 0.5}, -- FrontFrame_right
|
|
{-0.5, -0.25, 0.3125, -0.3125, 0.25, 0.5}, -- FrontFrame_left
|
|
{-0.5, 0.25, -0.5, -0.25, 0.5, 0.5}, -- edge_topright
|
|
{-0.5, -0.5, -0.5, -0.25, -0.25, 0.5}, -- edge_bottomright
|
|
{0.25, 0.25, -0.5, 0.5, 0.5, 0.5}, -- edge_topleft
|
|
{0.25, -0.5, -0.5, 0.5, -0.25, 0.5}, -- edge_bottomleft
|
|
{-0.25, 0.4375, -0.5, 0.25, 0.5, -0.4375}, -- backframe_top
|
|
{-0.25, -0.5, -0.5, 0.25, -0.4375, -0.4375}, -- backframe_bottom
|
|
{-0.5, -0.25, -0.5, -0.4375, 0.25, -0.4375}, -- backframe_left
|
|
{0.4375, -0.25, -0.5, 0.5, 0.25, -0.4375}, -- Backframe_right
|
|
{-0.0625, -0.3125, 0.3125, 0.0625, 0.3125, 0.375}, -- frontcross_vertical
|
|
{-0.3125, -0.0625, 0.3125, 0.3125, 0.0625, 0.375}, -- frontcross_horizontal
|
|
}
|
|
},
|
|
|
|
on_construct = function(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
meta:set_string("formspec",
|
|
"size[8,5.2]" ..
|
|
default.gui_bg ..
|
|
default.gui_bg_img ..
|
|
default.gui_slots ..
|
|
"list[current_name;main;0.5,0;1,1;]" ..
|
|
-- "tooltip[main;Builder will build the type of block in this slot.\nNote that only one item needs to be placed here, to 'program' it.\nThe builder will draw construction materials from the central inventory when building.]" ..
|
|
"label[0.5,0.8;Block to build]" ..
|
|
"field[2.5,0.8;1,0.1;period;Periodicity;${period}]" ..
|
|
"tooltip[period;Builder will build once every n steps.\nThese steps are globally aligned, so all builders with the\nsame period and offset will build on the same location.]" ..
|
|
"field[3.5,0.8;1,0.1;offset;Offset;${offset}]" ..
|
|
"tooltip[offset;Offsets the start of periodicity counting by this amount.\nFor example, a builder with period 2 and offset 0 builds\nevery even-numbered block and one with period 2 and\noffset 1 builds every odd-numbered block.]" ..
|
|
"button_exit[4.2,0.5;1,0.1;set;Save &\nShow]" ..
|
|
"tooltip[set;Saves settings]" ..
|
|
"field[5.7,0.8;1,0.1;build_facing;Facing;${build_facing}]" ..
|
|
"tooltip[build_facing;Value from 0-23. Not all block types make use of this.\nUse the 'Read & Save' button to copy the facing of the block\ncurrently in the builder output location]" ..
|
|
"button_exit[6.4,0.5;1,0.1;read;Read &\nSave]" ..
|
|
"tooltip[read;Reads the facing of the block currently in the build location,\nthen saves all settings]" ..
|
|
"list[current_player;main;0,1.3;8,1;]" ..
|
|
"list[current_player;main;0,1.3;8,1;]" ..
|
|
default.get_hotbar_bg(0,1.3) ..
|
|
"list[current_player;main;0,2.5;8,3;8]" ..
|
|
"listring[current_player;main]"
|
|
)
|
|
meta:set_int("period", 1)
|
|
meta:set_int("offset", 0)
|
|
meta:set_int("build_facing", 0)
|
|
|
|
local inv = meta:get_inventory()
|
|
inv:set_size("main", 1)
|
|
end,
|
|
|
|
on_receive_fields = function(pos, formname, fields, sender)
|
|
local meta = minetest.get_meta(pos)
|
|
local period = tonumber(fields.period)
|
|
local offset = tonumber(fields.offset)
|
|
local build_facing = tonumber(fields.build_facing)
|
|
if period and period > 0 then
|
|
meta:set_int("period", math.floor(tonumber(fields.period)))
|
|
else
|
|
period = meta:get_int("period")
|
|
end
|
|
if offset then
|
|
meta:set_int("offset", math.floor(tonumber(fields.offset)))
|
|
else
|
|
offset = meta:get_int("offset")
|
|
end
|
|
if build_facing and build_facing >= 0 and build_facing < 24 then
|
|
-- TODO: wallmounted facings only run from 0-5, a player could theoretically put a wallmounted item into the builder and then manually set the build facing to an invalid number
|
|
-- Should prevent that somehow. But not tonight.
|
|
meta:set_int("build_facing", math.floor(build_facing))
|
|
end
|
|
|
|
if fields.set then
|
|
local buildpos = digtron.find_new_pos(pos, minetest.get_node(pos).param2)
|
|
local x_pos = math.floor((buildpos.x+offset)/period)*period - offset
|
|
minetest.add_entity({x=x_pos, y=buildpos.y, z=buildpos.z}, "digtron:marker")
|
|
if x_pos >= buildpos.x then
|
|
minetest.add_entity({x=x_pos - period, y=buildpos.y, z=buildpos.z}, "digtron:marker")
|
|
end
|
|
if x_pos <= buildpos.x then
|
|
minetest.add_entity({x=x_pos + period, y=buildpos.y, z=buildpos.z}, "digtron:marker")
|
|
end
|
|
|
|
local y_pos = math.floor((buildpos.y+offset)/period)*period - offset
|
|
minetest.add_entity({x=buildpos.x, y=y_pos, z=buildpos.z}, "digtron:marker_vertical")
|
|
if y_pos >= buildpos.y then
|
|
minetest.add_entity({x=buildpos.x, y=y_pos - period, z=buildpos.z}, "digtron:marker_vertical")
|
|
end
|
|
if y_pos <= buildpos.y then
|
|
minetest.add_entity({x=buildpos.x, y=y_pos + period, z=buildpos.z}, "digtron:marker_vertical")
|
|
end
|
|
|
|
local z_pos = math.floor((buildpos.z+offset)/period)*period - offset
|
|
minetest.add_entity({x=buildpos.x, y=buildpos.y, z=z_pos}, "digtron:marker"):setyaw(1.5708)
|
|
if z_pos >= buildpos.z then
|
|
minetest.add_entity({x=buildpos.x, y=buildpos.y, z=z_pos - period}, "digtron:marker"):setyaw(1.5708)
|
|
end
|
|
if z_pos <= buildpos.z then
|
|
minetest.add_entity({x=buildpos.x, y=buildpos.y, z=z_pos + period}, "digtron:marker"):setyaw(1.5708)
|
|
end
|
|
|
|
elseif fields.read then
|
|
local meta = minetest.get_meta(pos)
|
|
local facing = minetest.get_node(pos).param2
|
|
local buildpos = digtron.find_new_pos(pos, facing)
|
|
meta:set_int("build_facing", minetest.get_node(buildpos).param2)
|
|
end
|
|
|
|
digtron.update_builder_item(pos)
|
|
end,
|
|
|
|
on_destruct = function(pos)
|
|
digtron.remove_builder_item(pos)
|
|
end,
|
|
|
|
after_place_node = function(pos)
|
|
digtron.update_builder_item(pos)
|
|
end,
|
|
|
|
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
|
if minetest.get_item_group(stack:get_name(), "digtron") ~= 0 then
|
|
return 0 -- don't allow builders to be set to build Digtron nodes, they'll just clog the output.
|
|
end
|
|
local inv = minetest.get_inventory({type="node", pos=pos})
|
|
inv:set_stack(listname, index, stack:take_item(1))
|
|
return 0
|
|
end,
|
|
|
|
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
|
return 0
|
|
end,
|
|
|
|
-- "builder at pos, imagine that you're in test_pos. If you're willing and able to build from there, take the item you need from inventory.
|
|
-- return the item you took and the inventory location you took it from so it can be put back after all the other builders have been tested.
|
|
-- If you couldn't get the item from inventory, return an error code so we can abort the cycle.
|
|
-- If you're not supposed to build at all, or the location is obstructed, return 0 to let us know you're okay and we shouldn't abort."
|
|
|
|
--return code and accompanying value:
|
|
-- 0, nil -- not supposed to build, no error
|
|
-- 1, {itemstack, source inventory pos} -- can build, took an item from inventory
|
|
-- 2, itemstack -- was supposed to build, but couldn't get the item from inventory
|
|
-- 3, nil -- builder configuration error
|
|
test_build = function(pos, test_pos, inventory_positions, protected_nodes, nodes_dug, controlling_coordinate, controller_pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local facing = minetest.get_node(pos).param2
|
|
local buildpos = digtron.find_new_pos(test_pos, facing)
|
|
|
|
if (buildpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then
|
|
--It's not the builder's turn to build right now.
|
|
return 0, nil
|
|
end
|
|
|
|
if not digtron.can_move_to(buildpos, protected_nodes, nodes_dug) then
|
|
--using "can_move_to" instead of "can_build_to" test case in case the builder is pointed "backward", and will thus
|
|
--be building into the space that it's currently in and will be vacating after moving, or in case the builder is aimed
|
|
--sideways and a fellow digtron node was ahead of it (will also be moving out of the way).
|
|
|
|
--If the player has built his digtron stupid (eg has another digtron node in the place the builder wants to build) this
|
|
--assumption is wrong, but I can't hold the player's hand through *every* possible bad design decision. Worst case,
|
|
--the digtron will think its inventory can't handle the next build step and abort the build when it actually could have
|
|
--managed one more cycle. That's not a bad outcome for a digtron array that was built stupidly to begin with.
|
|
--The player should be thanking me for all the error-checking I *do* do, really.
|
|
--Ungrateful wretch.
|
|
return 0, nil
|
|
end
|
|
|
|
local inv = minetest.get_inventory({type="node", pos=pos})
|
|
local item_stack = inv:get_stack("main", 1)
|
|
if not item_stack:is_empty() then
|
|
local source_location = digtron.take_from_inventory(item_stack:get_name(), inventory_positions)
|
|
if source_location ~= nil then
|
|
return 1, {item=item_stack, location=source_location}
|
|
end
|
|
return 2, item_stack -- error code for "needed an item but couldn't get it from inventory"
|
|
else
|
|
return 3, nil -- error code for "this builder's item slot is unset"
|
|
end
|
|
end,
|
|
|
|
execute_build = function(pos, player, inventory_positions, protected_nodes, nodes_dug, controlling_coordinate, controller_pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local build_facing = meta:get_int("build_facing")
|
|
local facing = minetest.get_node(pos).param2
|
|
local buildpos = digtron.find_new_pos(pos, facing)
|
|
local oldnode = minetest.get_node(buildpos)
|
|
|
|
if (buildpos[controlling_coordinate] + meta:get_int("offset")) % meta:get_int("period") ~= 0 then
|
|
return nil
|
|
end
|
|
|
|
if digtron.can_build_to(buildpos, protected_nodes, nodes_dug) then
|
|
local inv = minetest.get_inventory({type="node", pos=pos})
|
|
local item_stack = inv:get_stack("main", 1)
|
|
if not item_stack:is_empty() then
|
|
|
|
if digtron.creative_mode then
|
|
local returned_stack, success = digtron.item_place_node(item_stack, player, buildpos, tonumber(build_facing))
|
|
if success == true then
|
|
minetest.log("action", string.format("%s uses Digtron to build %s at (%d, %d, %d), displacing %s", player:get_player_name(), item_stack:get_name(), buildpos.x, buildpos.y, buildpos.z, oldnode.name))
|
|
nodes_dug:set(buildpos.x, buildpos.y, buildpos.z, false)
|
|
return true
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local sourcepos = digtron.take_from_inventory(item_stack:get_name(), inventory_positions)
|
|
if sourcepos == nil then
|
|
-- item not in inventory! Need to sound the angry buzzer to let the player know, so return false.
|
|
return false
|
|
end
|
|
local returned_stack, success = digtron.item_place_node(item_stack, player, buildpos, tonumber(build_facing))
|
|
if success == true then
|
|
minetest.log("action", string.format("%s uses Digtron to build %s at (%d, %d, %d), displacing %s", player:get_player_name(), item_stack:get_name(), buildpos.x, buildpos.y, buildpos.z, oldnode.name))
|
|
--flag this node as *not* to be dug.
|
|
nodes_dug:set(buildpos.x, buildpos.y, buildpos.z, false)
|
|
digtron.award_item_built(item_stack:get_name(), player:get_player_name())
|
|
return true
|
|
else
|
|
--failed to build, target node probably obstructed. Put the item back in inventory.
|
|
digtron.place_in_specific_inventory(item_stack, sourcepos, inventory_positions, controller_pos)
|
|
return nil
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
}) |