code:
--Craft Recipes

minetest.register_craft({
      output = 'drinks:juice_press',
      recipe = {
         {'default:stick', 'default:steel_ingot', 'default:stick'},
         {'default:stick', 'mesecraft_bucket:bucket_empty', 'default:stick'},
         {'stairs:slab_wood', 'stairs:slab_wood', 'vessels:drinking_glass'},
         }
})

minetest.register_craft({
      output = 'drinks:liquid_barrel',
      recipe = {
         {'group:wood', 'group:wood', 'group:wood'},
         {'group:wood', 'group:wood', 'group:wood'},
         {'stairs:slab_wood', '', 'stairs:slab_wood'},
         }
})

minetest.register_craft({
      output = 'drinks:liquid_silo',
      recipe = {
         {'drinks:liquid_barrel'},
         {'drinks:liquid_barrel'},
         {'drinks:liquid_barrel'}
      }
})

local press_idle_formspec =
   'size[8,7]'..
   'label[1.5,0;Organic juice is just a squish away.]' ..
   'label[4.3,.75;Put fruit here ->]'..
   'label[3.5,1.75;Put container here ->]'..
   'label[0.2,1.8;4 fruits to a glass,]'..
   'label[0.2,2.1;8 fruits to a bottle,]'..
   'label[0.2,2.4;16 fruits to a bucket.]'..
   'button[1,1;2,1;press;Start Juicing]'..
   'list[current_name;src;6.5,.5;1,1;]'..
   'list[current_name;dst;6.5,1.5;1,1;]'..
   'list[current_player;main;0,3;8,4;]'

local press_running_formspec =
   'size[8,7]'..
   'label[1.5,0;Organic juice coming right up.]' ..
   'label[4.3,.75;Put fruit here ->]'..
   'label[3.5,1.75;Put container here ->]'..
   'label[0.2,1.8;4 fruits to a glass,]'..
   'label[0.2,2.1;8 fruits to a bottle,]'..
   'label[0.2,2.4;16 fruits to a bucket.]'..
   'button[1,1;2,1;press;Start Juicing]'..
   'list[current_name;src;6.5,.5;1,1;]'..
   'list[current_name;dst;6.5,1.5;1,1;]'..
   'list[current_player;main;0,3;8,4;]'

local press_error_formspec =
   'size[8,7]'..
   'label[1.5,0;You need to add more fruit.]' ..
   'label[4.3,.75;Put fruit here ->]'..
   'label[3.5,1.75;Put container here ->]'..
   'label[0.2,1.8;4 fruits to a glass,]'..
   'label[0.2,2.1;8 fruits to a bottle,]'..
   'label[0.2,2.4;16 fruits to a bucket.]'..
   'button[1,1;2,1;press;Start Juicing]'..
   'list[current_name;src;6.5,.5;1,1;]'..
   'list[current_name;dst;6.5,1.5;1,1;]'..
   'list[current_player;main;0,3;8,4;]'

minetest.register_node('drinks:juice_press', {
   description = 'Juice Press',
   _doc_items_longdesc = "A machine for creating drinks out of various fruits and vegetables.",
   _doc_items_usagehelp = "Right-click the press to access inventory and begin juicing.",
   drawtype = 'mesh',
   mesh = 'drinks_press.obj',
   tiles = {name='drinks_press.png'},
   groups = {choppy=2, dig_immediate=2,},
   paramtype = 'light',
   paramtype2 = 'facedir',
   selection_box = {
      type = 'fixed',
      fixed = {-.5, -.5, -.5, .5, .5, .5},
      },
   collision_box = {
      type = 'fixed',
      fixed = {-.5, -.5, -.5, .5, .5, .5},
      },
   on_construct = function(pos)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      inv:set_size('main', 8*4)
      inv:set_size('src', 1)
      inv:set_size('dst', 1)
      meta:set_string('infotext', 'Empty Juice Press')
      meta:set_string('formspec', press_idle_formspec)
   end,
   on_receive_fields = function(pos, formname, fields, sender)
      if fields ['press'] then
         local meta = minetest.get_meta(pos)
         local inv = meta:get_inventory()
         local timer = minetest.get_node_timer(pos)
         local instack = inv:get_stack("src", 1)
         local fruitstack = instack:get_name()
         local mod, fruit = fruitstack:match("([^:]+):([^:]+)")
         if drinks.juiceable[fruit] then
            if string.find(fruit, '_') then
               local fruit, junk = fruit:match('([^_]+)_([^_]+)')
               meta:set_string('fruit', fruit)
            else
               meta:set_string('fruit', fruit)
            end
            local outstack = inv:get_stack("dst", 1)
            local vessel = outstack:get_name()
            if vessel == 'vessels:drinking_glass' then
               if instack:get_count() >= 4 then
                  meta:set_string('container', 'jcu_')
                  meta:set_string('fruitnumber', 4)
                  meta:set_string('infotext', 'Juicing...')
                  meta:set_string('formspec', press_running_formspec)
                  timer:start(4)
               else
                  meta:set_string('infotext', 'You need more fruit.')
                  meta:set_string('formspec', press_error_formspec)
               end
            elseif vessel == 'vessels:glass_bottle' then
               if instack:get_count() >= 8 then
                  meta:set_string('container', 'jbo_')
                  meta:set_string('fruitnumber', 8)
                  meta:set_string('infotext', 'Juicing...')
                  meta:set_string('formspec', press_running_formspec)
                  timer:start(8)
               else
                  meta:set_string('infotext', 'You need more fruit.')
                  meta:set_string('formspec', press_error_formspec)
               end
            elseif vessel == 'vessels:steel_bottle' then
               if instack:get_count() >= 8 then
                  meta:set_string('container', 'jsb_')
                  meta:set_string('fruitnumber', 8)
                  meta:set_string('infotext', 'Juicing...')
                  meta:set_string('formspec', press_running_formspec)
                  timer:start(8)
               else
                  meta:set_string('infotext', 'You need more fruit.')
                  meta:set_string('formspec', press_error_formspec)
               end
            elseif vessel == 'mesecraft_bucket:bucket_empty' then
               if instack:get_count() >= 16 then
                  meta:set_string('container', 'jbu_')
                  meta:set_string('fruitnumber', 16)
                  meta:set_string('infotext', 'Juicing...')
                  meta:set_string('formspec', press_running_formspec)
                  timer:start(16)
               else
                  meta:set_string('infotext', 'You need more fruit.')
                  meta:set_string('formspec', press_error_formspec)
               end
            elseif vessel == 'default:papyrus' then
               if instack:get_count() >= 2 then
                  local under_node = {x=pos.x, y=pos.y-1, z=pos.z}
                  local under_node_name = minetest.get_node_or_nil(under_node)
                  local under_node_2 = {x=pos.x, y=pos.y-2, z=pos.z}
                  local under_node_name_2 = minetest.get_node_or_nil(under_node_2)
                  if under_node_name.name == 'drinks:liquid_barrel' then
                     local meta_u = minetest.get_meta(under_node)
                     local stored_fruit = meta_u:get_string('fruit')
                     if fruit == stored_fruit or stored_fruit == 'empty' then
                        meta:set_string('container', 'tube')
                        meta:set_string('fruitnumber', 2)
                        meta:set_string('infotext', 'Juicing...')
                        meta_u:set_string('fruit', fruit)
                        timer:start(4)
                     else
                        meta:set_string('infotext', "You can't mix juices.")
                     end
                  elseif under_node_name_2.name == 'drinks:liquid_silo' then
                     local meta_u = minetest.get_meta(under_node_2)
                     local stored_fruit = meta_u:get_string('fruit')
                     if fruit == stored_fruit or stored_fruit == 'empty' then
                        meta:set_string('container', 'tube')
                        meta:set_string('fruitnumber', 2)
                        meta:set_string('infotext', 'Juicing...')
                        meta_u:set_string('fruit', fruit)
                        timer:start(4)
                     else
                        meta:set_string('infotext', "You can't mix juices.")
                     end
                  else
                     meta:set_string('infotext', 'You need more fruit.')
                     meta:set_string('formspec', press_error_formspec)
                  end
               end
            end
         end
      end
   end,
   on_timer = function(pos)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      local container = meta:get_string('container')
      local instack = inv:get_stack("src", 1)
      local outstack = inv:get_stack("dst", 1)
      local fruit = meta:get_string('fruit')
      local fruitnumber = tonumber(meta:get_string('fruitnumber'))
      if container == 'tube' then
         local timer = minetest.get_node_timer(pos)
         local under_node = {x=pos.x, y=pos.y-1, z=pos.z}
         local under_node_name = minetest.get_node_or_nil(under_node)
         local under_node_2 = {x=pos.x, y=pos.y-2, z=pos.z}
         local under_node_name_2 = minetest.get_node_or_nil(under_node_2)
         if under_node_name.name == 'drinks:liquid_barrel' then
            local meta_u = minetest.get_meta(under_node)
            local fullness = tonumber(meta_u:get_string('fullness'))
            instack:take_item(tonumber(fruitnumber))
            inv:set_stack('src', 1, instack)
            if fullness + 2 > 128 then
               timer:stop()
               meta:set_string('infotext', 'Barrel is full of juice.')
               return
            else
               local fullness = fullness + 2
               meta_u:set_string('fullness', fullness)
               meta_u:set_string('infotext', (math.floor((fullness/128)*100))..' % full of '..fruit..' juice.')
               meta_u:set_string('formspec', drinks.liquid_storage_formspec(fruit, fullness, 128))
               if instack:get_count() >= 2 then
                  timer:start(4)
               else
                  meta:set_string('infotext', 'You need more fruit.')
               end
            end
         elseif under_node_name_2.name == 'drinks:liquid_silo' then
            local meta_u = minetest.get_meta(under_node_2)
            local fullness = tonumber(meta_u:get_string('fullness'))
            instack:take_item(tonumber(fruitnumber))
            inv:set_stack('src', 1, instack)
            if fullness + 2 > 256 then
               timer:stop()
               meta:set_string('infotext', 'Silo is full of juice.')
               return
            else
               local fullness = fullness + 2
               meta_u:set_string('fullness', fullness)
               meta_u:set_string('infotext', (math.floor((fullness/256)*100))..' % full of '..fruit..' juice.')
               meta_u:set_string('formspec', drinks.liquid_storage_formspec(fruit, fullness, 256))
               if instack:get_count() >= 2 then
                  timer:start(4)
               else
                  meta:set_string('infotext', 'You need more fruit.')
               end
            end
         end
      else
      meta:set_string('infotext', 'Collect your juice.')
      meta:set_string('formspec', press_idle_formspec)
      instack:take_item(tonumber(fruitnumber))
      inv:set_stack('src', 1, instack)
      inv:set_stack('dst', 1 ,'drinks:'..container..fruit)
      end
   end,
   on_metadata_inventory_take = function(pos, listname, index, stack, player)
      local timer = minetest.get_node_timer(pos)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      timer:stop()
      meta:set_string('infotext', 'Ready for more juicing.')
      meta:set_string('formspec', press_idle_formspec)
   end,
   on_metadata_inventory_put = function(pos, listname, index, stack, player)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      meta:set_string('infotext', 'Ready for juicing.')
   end,
   can_dig = function(pos)
      local meta = minetest.get_meta(pos);
      local inv = meta:get_inventory()
      if inv:is_empty("src") and
         inv:is_empty("dst") then
         return true
      else
         return false
      end
   end,
   allow_metadata_inventory_put = function(pos, listname, index, stack, player)
      if listname == 'dst' then
         if stack:get_name() == ('mesecraft_bucket:bucket_empty') then
            return 1
         elseif stack:get_name() == ('vessels:drinking_glass') then
            return 1
         elseif stack:get_name() == ('vessels:glass_bottle') then
            return 1
         elseif stack:get_name() == ('vessels:steel_bottle') then
            return 1
         elseif stack:get_name() == ('default:papyrus') then
            return 1
         else
            return 0
         end
      else
         return 99
      end
   end,
})

function drinks.drinks_liquid_sub(liq_vol, ves_typ, ves_vol, pos, able_to_fill, leftover_count, outputstack)
   local meta = minetest.get_meta(pos)
   local fullness = tonumber(meta:get_string('fullness'))
   local fruit = meta:get_string('fruit')
   local fruit_name = meta:get_string('fruit_name')
   local inv = meta:get_inventory()
   local fullness = fullness - (liq_vol*able_to_fill)
   meta:set_string('fullness', fullness)
   meta:set_string('infotext', (math.floor((fullness/ves_vol)*100))..' % full of '..fruit_name..' juice.')
   if ves_vol == 128 then
      meta:set_string('formspec', drinks.liquid_storage_formspec(fruit_name, fullness, 128))
   elseif ves_vol == 256 then
      meta:set_string('formspec', drinks.liquid_storage_formspec(fruit_name, fullness, 256))
   end
   if ves_typ == 'jcu' or ves_typ == 'jbo' or ves_typ == 'jsb' or ves_typ == 'jbu' then
      inv:set_stack('dst', 1, 'drinks:'..ves_typ..'_'..fruit..' '..able_to_fill)
      inv:set_stack('src', 1, outputstack..' '..leftover_count)
   elseif ves_typ == 'thirsty:bronze_canteen' then
      inv:set_stack('dst', 1, {name="thirsty:bronze_canteen", count=1, wear=60, metadata=""})
   elseif ves_typ == 'thirsty:steel_canteen' then
      inv:set_stack('dst', 1, {name="thirsty:steel_canteen", count=1, wear=40, metadata=""})
   end
end

function drinks.drinks_liquid_avail_sub(liq_vol, ves_typ, ves_vol, outputstack, pos, count)
   local meta = minetest.get_meta(pos)
   local fullness = tonumber(meta:get_string('fullness'))
   if fullness - (liq_vol*count) < 0 then
      local able_to_fill = math.floor(fullness/liq_vol)
      local leftover_count = count - able_to_fill
      drinks.drinks_liquid_sub(liq_vol, ves_typ, ves_vol, pos, able_to_fill, leftover_count, outputstack)
   elseif fullness - (liq_vol*count) >= 0 then
      drinks.drinks_liquid_sub(liq_vol, ves_typ, ves_vol, pos, count, 0, outputstack)
   end
end

function drinks.drinks_liquid_add(liq_vol, ves_typ, ves_vol, pos, inputcount, leftover_count, inputstack)
   local meta = minetest.get_meta(pos)
   local fullness = tonumber(meta:get_string('fullness'))
   local fruit = meta:get_string('fruit')
   local fruit_name = meta:get_string('fruit_name')
   local inv = meta:get_inventory()
   local fullness = fullness + (liq_vol*inputcount)
   meta:set_string('fullness', fullness)
   inv:set_stack('src', 1, ves_typ..' '..inputcount)
   inv:set_stack('dst', 1, inputstack..' '..leftover_count)
   meta:set_string('infotext', (math.floor((fullness/ves_vol)*100))..' % full of '..fruit_name..' juice.')
   if ves_vol == 256 then
      meta:set_string('formspec', drinks.liquid_storage_formspec(fruit_name, fullness, 256))
   elseif ves_vol == 128 then
      meta:set_string('formspec', drinks.liquid_storage_formspec(fruit_name, fullness, 128))
   end
end

function drinks.drinks_liquid_avail_add(liq_vol, ves_typ, ves_vol, pos, inputstack, inputcount)
   local meta = minetest.get_meta(pos)
   local fullness = tonumber(meta:get_string('fullness'))
   if fullness + (liq_vol*inputcount) > ves_vol then
      local avail_ves_vol = ves_vol - fullness
      local can_empty = math.floor(avail_ves_vol/liq_vol)
      local leftover_count = inputcount - can_empty
      drinks.drinks_liquid_add(liq_vol, ves_typ, ves_vol, pos, can_empty, leftover_count, inputstack)
   elseif fullness + (liq_vol*inputcount) <= ves_vol then
      drinks.drinks_liquid_add(liq_vol, ves_typ, ves_vol, pos, inputcount, 0, inputstack)
   end
end

function drinks.drinks_barrel(pos, inputstack, inputcount)
   local meta = minetest.get_meta(pos)
   local vessel = string.sub(inputstack, 8, 10)
   drinks.drinks_liquid_avail_add(drinks.shortname[vessel].size, drinks.shortname[vessel].name, 128, pos, inputstack, inputcount)
end

function drinks.drinks_silo(pos, inputstack, inputcount)
   local meta = minetest.get_meta(pos)
   local vessel = string.sub(inputstack, 8, 10)
   drinks.drinks_liquid_avail_add(drinks.shortname[vessel].size, drinks.shortname[vessel].name, 256, pos, inputstack, inputcount)
end

minetest.register_node('drinks:liquid_barrel', {
   description = 'Barrel of Liquid',
   _doc_items_longdesc = "A node that provides a simple way to store juice.",
   _doc_items_usagehelp = "Add or remove liquids from the barrel using buckets, bottles, or cups.",
   drawtype = 'mesh',
   mesh = 'drinks_liquid_barrel.obj',
   tiles = {name='drinks_barrel.png'},
   groups = {choppy=2, dig_immediate=2,},
   paramtype = 'light',
   paramtype2 = 'facedir',
   selection_box = {
      type = 'fixed',
      fixed = {-.5, -.5, -.5, .5, .5, .5},
      },
   collision_box = {
      type = 'fixed',
      fixed = {-.5, -.5, -.5, .5, .5, .5},
      },
   on_construct = function(pos)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      inv:set_size('main', 8*4)
      inv:set_size('src', 1)
      inv:set_size('dst', 1)
      meta:set_string('fullness', 0)
      meta:set_string('fruit', 'empty')
      meta:set_string('infotext', 'Empty Drink Barrel')
      meta:set_string('formspec', 'size[8,8]'..
      'label[0,0;Fill with the drink of your choice,]'..
      'label[0,.4;you can only add more of the same type of drink.]'..
      'label[4.5,1.2;Add liquid ->]'..
      'label[.75,1.75;The barrel is empty]'..
      'label[4.5,2.25;Take liquid ->]'..
      'label[2,3.2;(This empties the barrel completely)]'..
      'button[0,3;2,1;purge;Purge]'..
      'list[current_name;src;6.5,1;1,1;]'..
      'list[current_name;dst;6.5,2;1,1;]'..
      'list[current_player;main;0,4;8,5;]')
   end,
   on_metadata_inventory_put = function(pos, listname, index, stack, player)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      local instack = inv:get_stack('src', 1)
      local outstack = inv:get_stack('dst', 1)
      local outputstack = outstack:get_name()
      local inputstack = instack:get_name()
      local outputcount = outstack:get_count()
      local inputcount = instack:get_count()
      local fruit = string.sub(inputstack, 12, -1)
      local fruit_in = meta:get_string('fruit')
      if fruit_in == 'empty' then
         meta:set_string('fruit', fruit)
         local fruit_name = minetest.registered_nodes[instack:get_name()]
         meta:set_string('fruit_name', string.lower(fruit_name.juice_type))
         local vessel = string.sub(inputstack, 8, 10)
         drinks.drinks_barrel(pos, inputstack, inputcount)
      end
      if fruit == fruit_in then
         local vessel = string.sub(inputstack, 8, 10)
         drinks.drinks_barrel(pos, inputstack, inputcount)
      end
      if drinks.longname[outputstack] then
         drinks.drinks_liquid_avail_sub(drinks.longname[outputstack].size, drinks.longname[outputstack].name, 128, outputstack, pos, outputcount)
      end
   end,
   on_receive_fields = function(pos, formname, fields, sender)
      if fields['purge'] then
         local meta = minetest.get_meta(pos)
         local fullness = 0
         local fruit_name = 'no'
         meta:set_string('fullness', 0)
         meta:set_string('fruit', 'empty')
         meta:set_string('infotext', 'Empty Drink Barrel')
         meta:set_string('formspec', drinks.liquid_storage_formspec(fruit_name, fullness, 128))
      end
   end,
   can_dig = function(pos)
      local meta = minetest.get_meta(pos);
      local inv = meta:get_inventory()
      if inv:is_empty("src") and
         inv:is_empty("dst") and
         tonumber(meta:get_string('fullness')) == 0 then
         return true
      else
         return false
      end
   end,
   allow_metadata_inventory_put = function(pos, listname, index, stack, player)
      local meta = minetest.get_meta(pos)
      if listname == 'src' then --adding liquid
         local inputstack = stack:get_name()
         local inputcount = stack:get_count()
         local valid = string.sub(inputstack, 1, 8)
         if valid == 'drinks:j' then
            return inputcount
         else
            return 0
         end
      elseif listname == 'dst' then --removing liquid
         --make sure there is a liquid to remove
         local juice = meta:get_string('fruit')
         if juice ~= 'empty' then
            local inputstack = stack:get_name()
            local inputcount = stack:get_count()
            local valid = string.sub(inputstack, 1, 7)
            if valid == 'vessels' or valid == 'mesecraft_bucket:' then
               return inputcount
            else
               return 0
            end
         else
            return 0
         end
      end
   end,
})

minetest.register_node('drinks:liquid_silo', {
   description = 'Silo of Liquid',
   _doc_items_longdesc = "A node that provides a simple way to store juice.",
   _doc_items_usagehelp = "Add or remove liquids from the silo using buckets, bottles, or cups.",
   drawtype = 'mesh',
   mesh = 'drinks_silo.obj',
   tiles = {name='drinks_silo.png'},
   groups = {choppy=2, dig_immediate=2,},
   paramtype = 'light',
   paramtype2 = 'facedir',
   selection_box = {
      type = 'fixed',
      fixed = {-.5, -.5, -.5, .5, 1.5, .5},
      },
   collision_box = {
      type = 'fixed',
      fixed = {-.5, -.5, -.5, .5, 1.5, .5},
      },
   on_construct = function(pos)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      inv:set_size('main', 8*4)
      inv:set_size('src', 1)
      inv:set_size('dst', 1)
      meta:set_string('fullness', 0)
      meta:set_string('fruit', 'empty')
      meta:set_string('infotext', 'Empty Drink Silo')
      meta:set_string('formspec', 'size[8,8]'..
      'label[0,0;Fill with the drink of your choice,]'..
      'label[0,.4;you can only add more of the same type of drink.]'..
      'label[4.5,1.2;Add liquid ->]'..
      'label[.75,1.75;The Silo is empty]'..
      'label[4.5,2.25;Take liquid ->]'..
      'label[2,3.2;(This empties the silo completely)]'..
      'button[0,3;2,1;purge;Purge]'..
      'list[current_name;src;6.5,1;1,1;]'..
      'list[current_name;dst;6.5,2;1,1;]'..
      'list[current_player;main;0,4;8,5;]')
   end,
   on_metadata_inventory_put = function(pos, listname, index, stack, player)
      local meta = minetest.get_meta(pos)
      local inv = meta:get_inventory()
      local instack = inv:get_stack("src", 1)
      local outstack = inv:get_stack('dst', 1)
      local outputstack = outstack:get_name()
      local inputstack = instack:get_name()
      local outputcount = outstack:get_count()
      local inputcount = instack:get_count()
      local fruit = string.sub(inputstack, 12, -1)
      local fruit_in = meta:get_string('fruit')
      if fruit_in == 'empty' then
         meta:set_string('fruit', fruit)
         local fruit_name = minetest.registered_nodes[instack:get_name()]
         meta:set_string('fruit_name', string.lower(fruit_name.juice_type))
         local vessel = string.sub(inputstack, 8, 10)
         drinks.drinks_silo(pos, inputstack, inputcount)
      end
      if fruit == fruit_in then
         local vessel = string.sub(inputstack, 8, 10)
         drinks.drinks_silo(pos, inputstack, inputcount)
      end
      if drinks.longname[outputstack] then
         drinks.drinks_liquid_avail_sub(drinks.longname[outputstack].size, drinks.longname[outputstack].name, 256, outputstack, pos, outputcount)
      end
   end,
   on_receive_fields = function(pos, formname, fields, sender)
      if fields['purge'] then
         local meta = minetest.get_meta(pos)
         local fullness = 0
         local fruit_name = 'no'
         meta:set_string('fullness', 0)
         meta:set_string('fruit', 'empty')
         meta:set_string('infotext', 'Empty Drink Silo')
         meta:set_string('formspec', drinks.liquid_storage_formspec(fruit_name, fullness, 256))
      end
   end,
   can_dig = function(pos)
      local meta = minetest.get_meta(pos);
      local inv = meta:get_inventory()
      if inv:is_empty("src") and
         inv:is_empty("dst") and
         tonumber(meta:get_string('fullness')) == 0 then
         return true
      else
         return false
      end
   end,
   allow_metadata_inventory_put = function(pos, listname, index, stack, player)
      local meta = minetest.get_meta(pos)
      if listname == 'src' then --adding liquid
         local inputstack = stack:get_name()
         local inputcount = stack:get_count()
         local valid = string.sub(inputstack, 1, 8)
         if valid == 'drinks:j' then
            return inputcount
         else
            return 0
         end
      elseif listname == 'dst' then --removing liquid
         --make sure there is liquid to take_item
         local juice = meta:get_string('fruit')
         if juice ~= 'empty' then
            local inputstack = stack:get_name()
            local inputcount = stack:get_count()
            local valid = string.sub(inputstack, 1, 7)
            if valid == 'vessels' or valid == 'mesecraft_bucket:' then
               return inputcount
            else
               return 0
            end
         else
            return 0
         end
      end
   end,
})


code:
--Parse Table
for i in ipairs (drinks.drink_table) do
   local desc = drinks.drink_table[i][1]
   local craft = drinks.drink_table[i][2]
   local color = drinks.drink_table[i][3]
   local health = drinks.drink_table[i][4]
   health = health or 1
   -- The color of the drink is all done in code, so we don't need to have multiple images.

--Actual Node registration
minetest.register_node('drinks:jbu_'..desc..'', {
	description = 'Bucket of '..craft..' Juice',
	drawtype = "plantlike",
	tiles = {'mesecraft_bucket.png^(drinks_bucket_contents.png^[colorize:'..color..':200)'},
	inventory_image = 'mesecraft_bucket.png^(drinks_bucket_contents.png^[colorize:'..color..':200)',
	wield_image = 'mesecraft_bucket.png^(drinks_bucket_contents.png^[colorize:'..color..':200)',
	paramtype = "light",
   juice_type = craft,
	is_ground_content = false,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.25, -0.5, -0.25, 0.25, 0.4, 0.25}
	},
	groups = {vessel=1,dig_immediate=3,attached_node=1, drink = 1},
	sounds = default.node_sound_defaults(),
})

drinks.register_item('drinks:jcu_'..desc,  'vessels:drinking_glass', {
   description = 'Cup of '..craft..' Juice',
   groups = {drink=1},
   juice_type = craft,
   inventory_image = 'drinks_glass_contents.png^[colorize:'..color..':200^drinks_drinking_glass.png',
   on_use = function(itemstack, user, pointed_thing)
      thirsty.drink(user, 4, 20)
      local eat_func = minetest.item_eat(health, 'vessels:drinking_glass')
      return eat_func(itemstack, user, pointed_thing)
   end,
})

drinks.register_item('drinks:jbo_'..desc, 'vessels:glass_bottle',{
   description = 'Bottle of '..craft..' Juice',
   groups = {drink = 1},
   juice_type = craft,
   inventory_image = 'drinks_bottle_contents.png^[colorize:'..color..':200^drinks_glass_bottle.png',
   on_use = function(itemstack, user, pointed_thing)
      thirsty.drink(user, 8, 20)
      local eat_func = minetest.item_eat((health*2), 'vessels:glass_bottle')
      return eat_func(itemstack, user, pointed_thing)
   end,
})

drinks.register_item('drinks:jsb_'..desc, 'vessels:steel_bottle',{
   description = 'Heavy Steel Bottle ('..craft..' Juice)',
   groups = {drink = 1},
   juice_type = craft,
   inventory_image = 'vessels_steel_bottle.png',
   on_use = function(itemstack, user, pointed_thing)
      thirsty.drink(user, 8, 20)
      local eat_func = minetest.item_eat((health*2), 'vessels:steel_bottle')
      return eat_func(itemstack, user, pointed_thing)
   end,
})

end

repo_name: korkut/MeseCraft

license: mit


code:
-- This code is for if Thirst isn't enabled.
--Parse Table
for i in ipairs (drinks.drink_table) do
   local desc = drinks.drink_table[i][1]
   local craft = drinks.drink_table[i][2]
   local color = drinks.drink_table[i][3]
   local health = drinks.drink_table[i][4]
   health = health or 1

--Actual Node registration
minetest.register_node('drinks:jbu_'..desc..'', {
	description = 'Bucket of '..craft..' Juice',
	drawtype = "plantlike",
	tiles = {'mesecraft_bucket.png^(drinks_bucket_contents.png^[colorize:'..color..':200)'},
	inventory_image = 'mesecraft_bucket.png^(drinks_bucket_contents.png^[colorize:'..color..':200)',
	wield_image = 'mesecraft_bucket.png^(drinks_bucket_contents.png^[colorize:'..color..':200)',
	paramtype = "light",
   juice_type = craft,
	is_ground_content = false,
	walkable = false,
	selection_box = {
		type = "fixed",
		fixed = {-0.25, -0.5, -0.25, 0.25, 0.4, 0.25}
	},
	groups = {vessel=1,dig_immediate=3,attached_node=1, drink = 1},
	sounds = default.node_sound_defaults(),
})

drinks.register_item( 'drinks:jcu_'..desc, 'vessels:drinking_glass', {
   description = 'Cup of '..craft..' Juice',
   juice_type = craft,
   inventory_image = 'drinks_glass_contents.png^[colorize:'..color..':200^drinks_drinking_glass.png',
   on_use = function(itemstack, user, pointed_thing)
      local eat_func = minetest.item_eat(health, 'vessels:drinking_glass')
      return eat_func(itemstack, user, pointed_thing)
   end,
})

drinks.register_item( 'drinks:jbo_'..desc, 'vessels:glass_bottle', {
   description = 'Bottle of '..craft..' Juice',
   groups = {drink=1},
   juice_type = craft,
   inventory_image = 'drinks_bottle_contents.png^[colorize:'..color..':200^drinks_glass_bottle.png',
   on_use = function(itemstack, user, pointed_thing)
      local eat_func = minetest.item_eat((health*2), 'vessels:glass_bottle')
      return eat_func(itemstack, user, pointed_thing)
   end,
})

drinks.register_item( 'drinks:jsb_'..desc, 'vessels:steel_bottle', {
   description = 'Heavy Steel Bottle ('..craft..' Juice)',
   groups = {drink=1},
   juice_type = craft,
   inventory_image = 'vessels_steel_bottle.png',
   on_use = function(itemstack, user, pointed_thing)
      local eat_func = minetest.item_eat((health*2), 'vessels:steel_bottle')
      return eat_func(itemstack, user, pointed_thing)
   end,
})

end


code:
function drinks.liquid_storage_formspec(fruit_name, fullness, max)
	local formspec =
   'size[8,8]'..
      'label[0,0;Fill with the drink of your choice,]'..
      'label[0,.4;you can only add more of the same type of drink.]'..
      'label[4.5,1.2;Add liquid ->]'..
      'label[.5,1.2;Storing '..fruit_name..' juice.]'..
      'label[.5,1.65;Holding '..(fullness/2)..' of '..(max/2)..' cups.]'..
      'label[4.5,2.25;Take liquid ->]'..
      'label[2,3.2;(This empties the container completely)]'..
      'button[0,3;2,1;purge;Purge]'..
      'list[current_name;src;6.5,1;1,1;]'..
      'list[current_name;dst;6.5,2;1,1;]'..
      'list[current_player;main;0,4;8,5;]'
   return formspec
end


code:
-- mod support (moreblocks/technic_worldgen)
local slab_str = "stairs:slab_wood"

function applyModSupport()
   local moreblocks_found = false
   local technic_worldgen = false

   local modnames = minetest.get_modnames()

   for i, name in ipairs(modnames) do
      -- minetest.log("[Mod] " .. name)
      if name == "moreblocks" then
         moreblocks_found = true
      end

      if name == "technic_worldgen" then
         technic_worldgen = true
      end
   end

   if moreblocks_found == true and technic_worldgen == true then
      minetest.log("applying patch to mod " .. minetest.get_current_modname())
      minetest.log("converting '" .. slab_str .. "' to 'moreblocks:slab_wood'")
      slab_str = "moreblocks:slab_wood"
   end
end
--applyModSupport()

--Craft Recipes

-- added mod-support
minetest.register_craft({
      output = 'drinks:juice_press',
      recipe = {
         {'default:stick', 'default:steel_ingot', 'default:stick'},
         {'default:stick', 'mesecraft_bucket:bucket_empty', 'default:stick'},
         {slab_str, slab_str, 'vessels:drinking_glass'},
         }
})

-- added mod-support
minetest.register_craft({
      output = 'drinks:liquid_barrel',
      recipe = {
         {'group:wood', 'group:wood', 'group:wood'},
         {'group:wood', 'group:wood', 'group:wood'},
         {slab_str, '', slab_str},
         }
})

drinks = {
drink_table = {},
juiceable = {},
shortname = {
   ['jcu'] = {size = 2, name = 'vessels:drinking_glass'},
   ['jbo'] = {size = 4, name = 'vessels:glass_bottle'},
   ['jsb'] = {size = 4, name = 'vessels:steel_bottle'},
   ['jbu'] = {size = 16, name = 'mesecraft_bucket:bucket_empty'}
},
longname = {
   ['vessels:drinking_glass'] = {size = 2, name = 'jcu'},
   ['vessels:glass_bottle'] = {size = 4, name = 'jbo'},
   ['vessels:steel_bottle'] = {size = 4, name = 'jsb'},
   ['mesecraft_bucket:bucket_empty'] = {size = 16, name = 'jbu'},
   ['thirsty:steel_canteen'] = {size = 20, name = 'thirsty:steel_canteen'},
   ['thirsty:bronze_canteen'] = {size = 30, name = 'thirsty:bronze_canteen'},
},
}

-- Honestly not needed for default, but used as an example to add support to other mods.
-- Basically to use this all you need to do is add the name of the fruit to make juiceable (see line 14 for example)
-- Add the new fruit to a table like I've done in line 16.
-- The table should follow this scheme: internal name, Displayed name, colorize code.
-- Check out the drinks.lua file for more info how how the colorize code is used.

if minetest.get_modpath('default') then
   drinks.juiceable['apple'] = true -- Name of fruit to make juiceable.
   drinks.juiceable['cactus'] = true
   table.insert(drinks.drink_table, {'apple', 'Apple', '#ecff56'})
   table.insert(drinks.drink_table, {'cactus', 'Cactus', '#96F97B'})
end

if minetest.get_modpath('bushes_classic') then
   drinks.juiceable['blackberry'] = true
   drinks.juiceable['blueberry'] = true
   drinks.juiceable['gooseberry'] = true
   drinks.juiceable['raspberry'] = true
   drinks.juiceable['strawberry'] = true
   table.insert(drinks.drink_table, {'blackberry', 'Blackberry', '#581845'})
   table.insert(drinks.drink_table, {'blueberry', 'Blueberry', '#521dcb'})
   table.insert(drinks.drink_table, {'gooseberry', 'Gooseberry', '#9cf57c'})
   table.insert(drinks.drink_table, {'raspberry', 'Raspberry', '#C70039'})
   table.insert(drinks.drink_table, {'strawberry', 'Strawberry', '#ff3636'})
end

if minetest.get_modpath('farming_plus') then
   drinks.juiceable['banana'] = true
   drinks.juiceable['melon'] = true
   drinks.juiceable['lemon_item'] = true
   drinks.juiceable['orange_item'] = true
   drinks.juiceable['peach_item'] = true
   drinks.juiceable['rhubarb_item'] = true
   drinks.juiceable['tomato_item'] = true
   drinks.juiceable['strawberry_item'] = true
   drinks.juiceable['raspberry_item'] = true
   table.insert(drinks.drink_table, {'banana', 'Banana', '#eced9f'})
   table.insert(drinks.drink_table, {'lemon', 'Lemon', '#feffaa'})
   table.insert(drinks.drink_table, {'melon', 'Melon', '#ef4646'})
   table.insert(drinks.drink_table, {'orange', 'Orange', '#ffc417'})
   table.insert(drinks.drink_table, {'peach', 'Peach', '#f2bc1e'})
   table.insert(drinks.drink_table, {'rhubarb', 'Rhubarb', '#fb8461'})
   table.insert(drinks.drink_table, {'tomato', 'Tomato', '#d03a0e'})
   table.insert(drinks.drink_table, {'strawberry', 'Strawberry', '#ff3636'})
   table.insert(drinks.drink_table, {'raspberry', 'Raspberry', '#C70039'})
end

if minetest.get_modpath('crops') then
   drinks.juiceable['melon'] = true
   drinks.juiceable['melon_slice'] = true
   drinks.juiceable['tomato'] = true
   drinks.juiceable['pumpkin'] = true
   table.insert(drinks.drink_table, {'melon', 'Melon', '#ef4646'})
   table.insert(drinks.drink_table, {'tomato', 'Tomato', '#d03a0e'})
   table.insert(drinks.drink_table, {'pumpkin', 'Pumpkin', '#ffc04c'})
end

if minetest.get_modpath('farming') then
   drinks.juiceable['melon_8'] = true
   drinks.juiceable['melon_slice'] = true
   drinks.juiceable['tomato'] = true
   drinks.juiceable['carrot'] = true
   drinks.juiceable['cucumber'] = true
   drinks.juiceable['grapes'] = true
   drinks.juiceable['pumpkin'] = true
   drinks.juiceable['raspberries'] = true
   drinks.juiceable['rhubarb'] = true
   table.insert(drinks.drink_table, {'melon', 'Melon', '#ef4646'})
   table.insert(drinks.drink_table, {'tomato', 'Tomato', '#990000'})
   table.insert(drinks.drink_table, {'carrot', 'Carrot', '#ed9121'})
   table.insert(drinks.drink_table, {'cucumber', 'Cucumber', '#73af59'})
   table.insert(drinks.drink_table, {'grapes', 'Grape', '#b20056'})
   table.insert(drinks.drink_table, {'pumpkin', 'Pumpkin', '#ffc04c'})
   table.insert(drinks.drink_table, {'raspberries', 'Raspberry', '#C70039'})
   table.insert(drinks.drink_table, {'rhubarb', 'Rhubarb', '#fb8461'})
end

if minetest.get_modpath('fruit') then
   drinks.juiceable['pear'] = true
   drinks.juiceable['plum'] = true
   drinks.juiceable['peach'] = true
   drinks.juiceable['orange'] = true
   table.insert(drinks.drink_table, {'pear', 'Pear', '#ecff56'})
   table.insert(drinks.drink_table, {'plum', 'Plum', '#8e4585'})
   table.insert(drinks.drink_table, {'peach', 'Peach', '#f2bc1e'})
   table.insert(drinks.drink_table, {'orange', 'Orange', '#ffc417'})
end

if minetest.get_modpath('ethereal') then
   drinks.juiceable['banana'] = true
   drinks.juiceable['coconut'] = true
   drinks.juiceable['orange'] = true
   drinks.juiceable['strawberry'] = true
   table.insert(drinks.drink_table, {'banana', 'Banana', '#eced9f'})
   table.insert(drinks.drink_table, {'coconut', 'Coconut', '#ffffff'})
   table.insert(drinks.drink_table, {'orange', 'Orange', '#ffc417'})
   table.insert(drinks.drink_table, {'strawberry', 'Strawberry', '#ff3636'})
end

-- replace craftitem to node definition
-- use existing node as template (e.g. 'vessel:glass_bottle')
drinks.register_item = function( name, template, def )
   local template_def = minetest.registered_nodes[template]
   if template_def then
   local drinks_def = table.copy(template_def)

   -- replace/add values
   for k,v in pairs(def) do
      if k == "groups" then
         -- special handling for groups: merge instead replace
         for g,n in pairs(v) do
            drinks_def[k][g] = n
         end
      else
         drinks_def[k]=v
      end
   end

   if def.inventory_image then
      drinks_def.wield_image = drinks_def.inventory_image
      drinks_def.tiles = { drinks_def.inventory_image }
   end

   minetest.register_node( name, drinks_def )
   end
end

if minetest.get_modpath('thirsty') then
   dofile(minetest.get_modpath('drinks')..'/drinks.lua')
else
   dofile(minetest.get_modpath('drinks')..'/drinks2.lua')
end
dofile(minetest.get_modpath('drinks')..'/drink_machines.lua')
dofile(minetest.get_modpath('drinks')..'/formspecs.lua')


code:
name = drinks
depends= mesecraft_bucket,default,vessels,stairs
description = Adds a juice press and two juice storage containers. Most fruits and several veggies can be juiced. The resulting drinks can be 'ate' to gain health, or if you are using the thirsty mod, to regain hydration.
author = Nathan, Napiophelios
optional_depends = hunger,thirsty,plantlife,farming_plus,crops,farming,minetest_doc_modpack,fruits,ethereal
title = Drinks


code:
This mod aims to add more drinks to the world, and be easily expanded to support other mods that have fruits and veggies.

There is optional support for the thirsty mod, if you have it installed drinking juices will fill your hydration.


code:
# Realtime Elevators for Minetest

## Dependencies
This mod has no dependencies other than default, but has recipes designed for use with technic and either homedecor/chains (for chains) or farming redo (for rope) and will detect if those are installed.

## Usage
Each shaft begins with a motor, and extends down through shaft and elevator nodes. Each elevator node can be used to travel to any other elevator node below the motor and through an unbroken shaft. Only one player can use each shaft at once.


code:
default
technic?
homedecor?
chains?
farming?


code:
-- Initial speed of a box.
local SPEED = 10
-- Acceleration of a box.
local ACCEL = 0.1
-- Elevator interface/database version.
local VERSION = 8
-- Maximum time a box can go without players nearby.
local PTIMEOUT = 120

-- Detect optional mods.
local technic_path = minetest.get_modpath("technic")
local chains_path = minetest.get_modpath("chains")
local homedecor_path = minetest.get_modpath("homedecor")
local armor_path = minetest.get_modpath("3d_armor")

-- Central "network" table.
local elevator = {
    motors = {},
}

local str = minetest.get_mod_storage and minetest.get_mod_storage()

local elevator_file = minetest.get_worldpath() .. "/elevator"

local function load_elevator()
    if str and ((str.contains and str:contains("data")) or (str:get_string("data") and str:get_string("data") ~= "")) then
        elevator = minetest.deserialize(str:get_string("data"))
        return
    end
    local file = io.open(elevator_file)
    if file then
        elevator = minetest.deserialize(file:read("*all")) or {}
        file:close()
    end
end

local function save_elevator()
    if str then
        str:set_string("data", minetest.serialize(elevator))
        return
    end
    local f = io.open(elevator_file, "w")
    f:write(minetest.serialize(elevator))
    f:close()
end

load_elevator()

-- Elevator boxes in action.
local boxes = {}
-- Player formspecs.
local formspecs = {}
-- Player near box timeout.
local lastboxes = {}
-- Players riding boxes.
local riding = {}
-- Globalstep timer.
local time = 0

-- Helper function to read unloaded nodes.
local function get_node(pos)
    local node = minetest.get_node_or_nil(pos)
    if node then return node end
    local _,_ = VoxelManip():read_from_map(pos, pos)
    return minetest.get_node_or_nil(pos)
end

-- Use homedecor's placeholder if possible.
local placeholder = homedecor_path and "homedecor:expansion_placeholder" or "elevator:placeholder"
if homedecor_path then
    minetest.register_alias("elevator:placeholder", "homedecor:expansion_placeholder")
else
    -- Placeholder node, in the style of homedecor.
    minetest.register_node(placeholder, {
        description = "Expansion Placeholder",
        selection_box = {
            type = "fixed",
            fixed = {0, 0, 0, 0, 0, 0},
        },
        groups = {
            not_in_creative_inventory=1
        },
        drawtype = "airlike",
        paramtype = "light",
        sunlight_propagates = true,

        walkable = false,
        buildable_to = false,
        is_ground_content = false,

        on_dig = function(pos, node, player)
            minetest.remove_node(pos)
            minetest.set_node(pos, {name=placeholder})
        end
    })
end

local VISUAL_INCREASE = 1.75

-- Cause <sender> to ride <motorhash> beginning at <pos> and targetting <target>.
local function create_box(motorhash, pos, target, sender)
    -- First create the box.
    local obj = minetest.add_entity(pos, "elevator:box")
    obj:setpos(pos)
    -- Attach the player.
    sender:setpos(pos)
    sender:set_attach(obj, "", {x=0, y=9, z=0}, {x=0, y=0, z=0})
    sender:set_eye_offset({x=0, y=-9, z=0},{x=0, y=-9, z=0})
    sender:set_properties({visual_size = {x=VISUAL_INCREASE, y=VISUAL_INCREASE}})
    if armor_path then
        armor:update_player_visuals(sender)
    end
    -- Set the box properties.
    obj:get_luaentity().motor = motorhash
    obj:get_luaentity().uid = math.floor(math.random() * 1000000)
    obj:get_luaentity().attached = sender:get_player_name()
    obj:get_luaentity().start = pos
    obj:get_luaentity().target = target
    obj:get_luaentity().halfway = {x=pos.x, y=(pos.y+target.y)/2, z=pos.z}
    obj:get_luaentity().vmult = (target.y < pos.y) and -1 or 1
    -- Set the speed.
    obj:setvelocity({x=0, y=SPEED*obj:get_luaentity().vmult, z=0})
    obj:setacceleration({x=0, y=ACCEL*obj:get_luaentity().vmult, z=0})
    -- Set the tables.
    boxes[motorhash] = obj
    riding[sender:get_player_name()] = {
        motor = motorhash,
        pos = pos,
        target = target,
        box = obj,
    }
    return obj
end

-- Try to teleport player away from any closed (on) elevator node.
local function teleport_player_from_elevator(player)
    local function solid(pos)
        if not minetest.registered_nodes[minetest.get_node(pos).name] then
            return true
        end
        return minetest.registered_nodes[minetest.get_node(pos).name].walkable
    end
    local pos = vector.round(player:getpos())
    local node = minetest.get_node(pos)
    -- elevator_off is like a shaft, so the player would already be falling.
    if node.name == "elevator:elevator_on" then
        local front = vector.subtract(pos, minetest.facedir_to_dir(node.param2))
        local front_above = vector.add(front, {x=0, y=1, z=0})
        local front_below = vector.subtract(front, {x=0, y=1, z=0})
        -- If the front isn't solid, it's ok to teleport the player.
        if not solid(front) and not solid(front_above) then
            player:setpos(front)
        end
    end
end

minetest.register_globalstep(function(dtime)
    -- Don't want to run this too often.
    time = time + dtime
    if time < 0.5 then
        return
    end
    time = 0
    -- Only count riders who are still logged in.
    local newriding = {}
    for _,p in ipairs(minetest.get_connected_players()) do
        local pos = p:getpos()
        local name = p:get_player_name()
        newriding[name] = riding[name]
        -- If the player is indeed riding, update their position.
        if newriding[name] then
            newriding[name].pos = pos
        end
    end
    riding = newriding
    for name,r in pairs(riding) do
        -- If the box is no longer loaded or existent, create another.
        local ok = r.box and r.box.getpos and r.box:getpos() and r.box:get_luaentity() and r.box:get_luaentity().attached == name
        if not ok then
            minetest.log("action", "[elevator] "..minetest.pos_to_string(r.pos).." created due to lost rider.")
            minetest.after(0, create_box, r.motor, r.pos, r.target, minetest.get_player_by_name(name))
        end
    end
    -- Ensure boxes are deleted after <PTIMEOUT> seconds if there are no players nearby.
    for motor,obj in pairs(boxes) do
        if type(obj) ~= "table" then
            return
        end
        lastboxes[motor] = lastboxes[motor] and math.min(lastboxes[motor], PTIMEOUT) or PTIMEOUT
        lastboxes[motor] = math.max(lastboxes[motor] - 1, 0)
        local pos = obj:getpos()
        if pos then
            for _,object in ipairs(minetest.get_objects_inside_radius(pos, 5)) do
                if object.is_player and object:is_player() then
                    lastboxes[motor] = PTIMEOUT
                    break
                end
            end
            if lastboxes[motor] < 1 then
                minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to lack of players.")
                boxes[motor] = false
            end
        else
            minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to lack of position during player check.")
            boxes[motor] = false
        end
    end
end)

minetest.register_on_leaveplayer(function(player)
    -- We don't want players potentially logging into open elevators.
    teleport_player_from_elevator(player)
end)

local function phash(pos)
    return minetest.pos_to_string(pos)
end

local function punhash(pos)
    return minetest.string_to_pos(pos)
end

-- Starting from <pos>, locate a motor hash.
local function locate_motor(pos)
    local p = vector.new(pos)
    while true do
        local node = get_node(p)
        if node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
            p.y = p.y + 2
        elseif node.name == "elevator:shaft" then
            p.y = p.y + 1
        elseif node.name == "elevator:motor" then
            return phash(p)
        else
            return nil
        end
    end
end

local function build_motor(hash)
    local need_saving = false
    local motor = elevator.motors[hash]
    -- Just ignore motors that don't exist.
    if not motor then
        return
    end
    local p = punhash(hash)
    local node = get_node(p)
    -- And ignore motors that aren't motors.
    if node.name ~= "elevator:motor" then
        return
    end
    p.y = p.y - 1
    motor.elevators = {}
    motor.pnames = {}
    motor.labels = {}
    -- Run down through the shaft, storing information about elevators.
    while true do
        local node = get_node(p)
        if node.name == "elevator:shaft" then
            p.y = p.y - 1
        else
            p.y = p.y - 1
            local node = get_node(p)
            if node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
                table.insert(motor.elevators, phash(p))
                table.insert(motor.pnames, tostring(p.y))
                table.insert(motor.labels, "")
                p.y = p.y - 1
                need_saving = true
            else
                break
            end
        end
    end
    -- Set the elevators fully.
    for i,m in ipairs(motor.elevators) do
        local pos = punhash(m)
        local meta = minetest.get_meta(pos)
        meta:set_int("version", VERSION)
        if meta:get_string("motor") ~= hash then
            build_motor(meta:get_string("motor"))
        end
        motor.labels[i] = meta:get_string("label")
        meta:set_string("motor", hash)
        if motor.labels[i] ~= meta:get_string("infotext") then
            meta:set_string("infotext", motor.labels[i])
        end
    end
    if need_saving then
        save_elevator()
    end
end

local function unbuild(pos, add)
    local need_saving = false
    local p = table.copy(pos)
    p.y = p.y - 1
    -- Loop down through the network, set any elevators below this to the off position.
    while true do
        local node = get_node(p)
        if node.name == "elevator:shaft" then
            p.y = p.y - 1
        else
            p.y = p.y - 1
            local node = get_node(p)
            if node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
                local meta = minetest.get_meta(p)
                meta:set_string("motor", "")
                p.y = p.y - 1
            else
                break
            end
        end
    end
    -- After a short delay, build the motor and handle box removal.
    minetest.after(0.01, function(p2, add)
        if not p2 or not add then
            return
        end
        p2.y = p2.y + add
        local motorhash = locate_motor(p2)
        build_motor(motorhash)
        -- If there's a box below this point, break it.
        if boxes[motorhash] and boxes[motorhash]:getpos() and p2.y >= boxes[motorhash]:getpos().y then
            boxes[motorhash] = nil
        end
        -- If the box does not exist, just clear it.
        if boxes[motorhash] and not boxes[motorhash]:getpos() then
            boxes[motorhash] = nil
        end
    end, table.copy(pos), add)
end

minetest.register_node("elevator:motor", {
    description = "Elevator Motor",
    tiles = {
        "default_steel_block.png",
        "default_steel_block.png",
        "elevator_motor.png",
        "elevator_motor.png",
        "elevator_motor.png",
        "elevator_motor.png",
    },
    groups = {cracky=1},
    sounds = default.node_sound_stone_defaults(),
    after_place_node = function(pos, placer, itemstack)
        -- Set up the motor table.
        elevator.motors[phash(pos)] = {
            elevators = {},
            pnames = {},
            labels = {},
        }
        save_elevator()
        build_motor(phash(pos))
    end,
    on_destruct = function(pos)
        -- Destroy everything related to this motor.
        boxes[phash(pos)] = nil
        elevator.motors[phash(pos)] = nil
        save_elevator()
    end,
})

for _,mode in ipairs({"on", "off"}) do
    local nodename = "elevator:elevator_"..mode
    local on = (mode == "on")
    local box
    local cbox
    if on then
        -- Active elevators have a ceiling and floor.
        box = {

            { 0.48, -0.5,-0.5,  0.5,  1.5, 0.5},
            {-0.5 , -0.5, 0.48, 0.48, 1.5, 0.5},
            {-0.5,  -0.5,-0.5 ,-0.48, 1.5, 0.5},

            { -0.5,-0.5,-0.5,0.5,-0.48, 0.5},
            { -0.5, 1.45,-0.5,0.5, 1.5, 0.5},
        }
        cbox = table.copy(box)
        -- But you can enter them from the top.
        cbox[5] = nil
    else
        -- Inactive elevators are almost like shafts.
        box = {

            { 0.48, -0.5,-0.5,  0.5,  1.5, 0.5},
            {-0.5 , -0.5, 0.48, 0.48, 1.5, 0.5},
            {-0.5,  -0.5,-0.5 ,-0.48, 1.5, 0.5},
            {-0.5 , -0.5, -0.48, 0.5, 1.5, -0.5},
        }
        cbox = box
    end
    minetest.register_node(nodename, {
        description = "Elevator Platform",
        drawtype = "nodebox",
        sunlight_propagates = false,
        paramtype = "light",
        paramtype2 = "facedir",
        on_rotate = screwdriver.disallow,

        selection_box = {
                type = "fixed",
                fixed = box,
        },

        collision_box = {
                type = "fixed",
                fixed = cbox,
        },

        node_box = {
                type = "fixed",
                fixed = box,
        },

        tiles = on and {
                "default_steel_block.png",
                "default_steel_block.png",
                "elevator_box.png",
                "elevator_box.png",
                "elevator_box.png",
                "elevator_box.png",
        } or {
                "elevator_box.png",
                "elevator_box.png",
                "elevator_box.png",
                "elevator_box.png",
                "elevator_box.png",
                "elevator_box.png",
        },
        groups = {cracky=1, choppy=1, snappy=1},
        drop = "elevator:elevator_off",

        -- Emit a bit of light when active.
        light_source = (on and 4 or nil),

        after_place_node  = function(pos, placer, itemstack)
            local meta = minetest.get_meta(pos)
            meta:set_int("version", VERSION)

            -- Add a placeholder to avoid nodes being placed in the top.
            local p = vector.add(pos, {x=0, y=1, z=0})
            local p2 = minetest.dir_to_facedir(placer:get_look_dir())
            minetest.set_node(p, {name=placeholder, paramtype2="facedir", param2=p2})

            -- Try to build a motor above.
            local motor = locate_motor(pos)
            if motor then
                build_motor(motor)
            end
        end,

        after_dig_node = function(pos, node, meta, digger)
            unbuild(pos, 2)
        end,

        on_place = function(itemstack, placer, pointed_thing)
            local pos  = pointed_thing.above
            local node = minetest.get_node(vector.add(pos, {x=0, y=1, z=0}))
            if (node ~= nil and node.name ~= "air" and node.name ~= placeholder) then
                return
            end
            return minetest.item_place(itemstack, placer, pointed_thing)
        end,

        on_rightclick = function(pos, node, sender)
            if not sender or not sender:is_player() then
                return
            end
            local formspec
            local meta = minetest.get_meta(pos)
            formspecs[sender:get_player_name()] = {pos}
            if on then
                if vector.distance(sender:getpos(), pos) > 1 or minetest.get_node(sender:getpos()).name ~= nodename then
                    minetest.chat_send_player(sender:get_player_name(), "You are not inside the booth.")
                    return
                end
                -- Build the formspec from the motor table.
                local tpnames = {}
                local tpnames_l = {}
                local motorhash = meta:get_string("motor")
                local motor = elevator.motors[motorhash]
                for ji,jv in ipairs(motor.pnames) do
                    if tonumber(jv) ~= pos.y then
                        table.insert(tpnames, jv)
                        table.insert(tpnames_l, (motor.labels[ji] and motor.labels[ji] ~= "") and (jv.." - "..minetest.formspec_escape(motor.labels[ji])) or jv)
                    end
                end
                formspecs[sender:get_player_name()] = {pos, tpnames}
                if #tpnames > 0 then
                    if not minetest.is_protected(pos, sender:get_player_name()) then
                        formspec = "size[4,6]"
                        .."label[0,0;Click once to travel.]"
                        .."textlist[-0.1,0.5;4,4;target;"..table.concat(tpnames_l, ",").."]"
                        .."field[0.25,5.25;4,0;label;;"..minetest.formspec_escape(meta:get_string("label")).."]"
                        .."button_exit[-0.05,5.5;4,1;setlabel;Set label]"
                    else
                        formspec = "size[4,4.4]"
                        .."label[0,0;Click once to travel.]"
                        .."textlist[-0.1,0.5;4,4;target;"..table.concat(tpnames_l, ",").."]"
                    end
                else
                    if not minetest.is_protected(pos, sender:get_player_name()) then
                        formspec = "size[4,2]"
                        .."label[0,0;No targets available.]"
                        .."field[0.25,1.25;4,0;label;;"..minetest.formspec_escape(meta:get_string("label")).."]"
                        .."button_exit[-0.05,1.5;4,1;setlabel;Set label]"
                    else
                        formspec = "size[4,0.4]"
                        .."label[0,0;No targets available.]"
                    end
                end
                minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec)
            elseif not elevator.motors[meta:get_string("motor")] then
                if not minetest.is_protected(pos, sender:get_player_name()) then
                    formspec = "size[4,2]"
                    .."label[0,0;This elevator is inactive.]"
                    .."field[0.25,1.25;4,0;label;;"..minetest.formspec_escape(meta:get_string("label")).."]"
                    .."button_exit[-0.05,1.5;4,1;setlabel;Set label]"
                else
                    formspec = "size[4,0.4]"
                    .."label[0,0;This elevator is inactive.]"
                end
                minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec)
            elseif boxes[meta:get_string("motor")] then
                if not minetest.is_protected(pos, sender:get_player_name()) then
                    formspec = "size[4,2]"
                    .."label[0,0;This elevator is in use.]"
                    .."field[0.25,1.25;4,0;label;;"..minetest.formspec_escape(meta:get_string("label")).."]"
                    .."button_exit[-0.05,1.5;4,1;setlabel;Set label]"
                else
                    formspec = "size[4,0.4]"
                    .."label[0,0;This elevator is in use.]"
                end
                minetest.show_formspec(sender:get_player_name(), "elevator:elevator", formspec)
            end
        end,

        on_destruct = function(pos)
            local p = vector.add(pos, {x=0, y=1, z=0})
            if get_node(p).name == placeholder then
                minetest.remove_node(p)
            end
        end,
    })
end

minetest.register_on_player_receive_fields(function(sender, formname, fields)
    if formname ~= "elevator:elevator" then
        return
    end
    local pos = formspecs[sender:get_player_name()] and formspecs[sender:get_player_name()][1] or nil
    if not pos then
        return true
    end
    local meta = minetest.get_meta(pos)
    if fields.setlabel then
        if minetest.is_protected(pos, sender:get_player_name()) then
            return true
        end
        meta:set_string("label", fields.label)
        meta:set_string("infotext", fields.label)
        -- Rebuild the elevator shaft so the other elevators can read this label.
        local motorhash = meta:get_string("motor")
        build_motor(elevator.motors[motorhash] and motorhash or locate_motor(pos))
        return true
    end
    -- Double check if it's ok to go.
    if vector.distance(sender:getpos(), pos) > 1 then
        return true
    end
    if fields.target then
        local closeformspec = ""
        -- HACK: With player information extensions enabled, we can check if closing formspecs are now allowed. This is specifically used on Survival in Ethereal.
        local pi = minetest.get_player_information(sender:get_player_name())
        if (not (pi.major == 0 and pi.minor == 4 and pi.patch == 15)) and (pi.protocol_version or 29) < 29 then
            closeformspec = "size[4,2] label[0,0;You are now using the elevator.\nUpgrade Minetest to avoid this dialog.] button_exit[0,1;4,1;close;Close]"
        end
        -- End hacky HACK.
        minetest.after(0.2, minetest.show_formspec, sender:get_player_name(), "elevator:elevator", closeformspec)
        -- Ensure we're connected to a motor.
        local motorhash = meta:get_string("motor")
        local motor = elevator.motors[motorhash]
        if not motor then
            motorhash = locate_motor(pos)
            motor = elevator.motors[motorhash]
            if motor then
                meta:set_string("motor", "")
                build_motor(motorhash)
                minetest.chat_send_player(sender:get_player_name(), "Recalibrated to a new motor, please try again.")
                return true
            end
        end
        if not motor then
            minetest.chat_send_player(sender:get_player_name(), "This elevator is not attached to a motor.")
            return true
        end
        if not formspecs[sender:get_player_name()][2] or not formspecs[sender:get_player_name()][2][minetest.explode_textlist_event(fields.target).index] then
            return true
        end
        -- Locate our target elevator.
        local target = nil
        local selected_target = formspecs[sender:get_player_name()][2][minetest.explode_textlist_event(fields.target).index]
        for i,v in ipairs(motor.pnames) do
            if v == selected_target then
                target = punhash(motor.elevators[i])
            end
        end
        -- Found the elevator? Then go!
        if target then
            -- Final check.
            if boxes[motorhash] then
                minetest.chat_send_player(sender:get_player_name(), "This elevator is in use.")
                return true
            end
            local obj = create_box(motorhash, pos, target, sender)
            -- Teleport anyone standing within an on elevator out, or they'd fall through the off elevators.
            for _,p in ipairs(motor.elevators) do
                local p = punhash(p)
                for _,object in ipairs(minetest.get_objects_inside_radius(p, 0.6)) do
                    if object.is_player and object:is_player() then
                        if object:get_player_name() ~= obj:get_luaentity().attached then
                            teleport_player_from_elevator(object)
                        end
                    end
                end
            end
        else
            minetest.chat_send_player(sender:get_player_name(), "This target is invalid.")
            return true
        end
        return true
    end
    return true
end)

-- Compatability with an older version.
minetest.register_alias("elevator:elevator", "elevator:elevator_off")

-- Ensure an elevator is up to the latest version.
local function upgrade_elevator(pos, meta)
    if meta:get_int("version") ~= VERSION then
        minetest.log("action", "[elevator] Updating elevator with old version at "..minetest.pos_to_string(pos))
        minetest.after(0, function(pos) build_motor(locate_motor(pos)) end, pos)
        meta:set_int("version", VERSION)
        meta:set_string("formspec", "")
        meta:set_string("infotext", meta:get_string("label"))
    end
end

-- Convert off to on when applicable.
local offabm = function(pos, node)
    local meta = minetest.get_meta(pos)
    upgrade_elevator(pos, meta)
    if not boxes[meta:get_string("motor")] and elevator.motors[meta:get_string("motor")] then
        node.name = "elevator:elevator_on"
        minetest.swap_node(pos, node)
    end
end

minetest.register_abm({
    nodenames = {"elevator:elevator_off"},
    interval = 1,
    chance = 1,
    action = offabm,
    label = "Elevator (Off)",
})

-- Convert on to off when applicable.
minetest.register_abm({
    nodenames = {"elevator:elevator_on"},
    interval = 1,
    chance = 1,
    action = function(pos, node)
        local meta = minetest.get_meta(pos)
        upgrade_elevator(pos, meta)
        if boxes[meta:get_string("motor")] or not elevator.motors[meta:get_string("motor")] then
            node.name = "elevator:elevator_off"
            minetest.swap_node(pos, node)
        end
    end,
    label = "Elevator (On)",
})

minetest.register_node("elevator:shaft", {
    description = "Elevator Shaft",
    tiles = { "elevator_shaft.png" },
    drawtype = "nodebox",
    paramtype = "light",
    on_rotate = screwdriver.disallow,
    sunlight_propagates = true,
    groups = {cracky=2, oddly_breakable_by_hand=1},
    sounds = default.node_sound_stone_defaults(),
    node_box = {
        type = "fixed",
        fixed = {
            {-8/16,-8/16,-8/16,-7/16,8/16,8/16},
            {7/16,-8/16,-8/16,8/16,8/16,8/16},
            {-7/16,-8/16,-8/16,7/16,8/16,-7/16},
            {-7/16,-8/16,8/16,7/16,8/16,7/16},
        },
    },
    collisionbox = {
        type = "fixed",
        fixed = {
            {-8/16,-8/16,-8/16,-7/16,8/16,8/16},
            {7/16,-8/16,-8/16,8/16,8/16,8/16},
            {-7/16,-8/16,-8/16,7/16,8/16,-7/16},
            {-7/16,-8/16,8/16,7/16,8/16,7/16},
        },
    },
    after_place_node = function(pos)
        -- We might have connected a motor above to an elevator below.
        build_motor(locate_motor(pos))
    end,
    on_destruct = function(pos)
        -- Remove boxes and deactivate elevators below us.
        unbuild(pos, 1)
    end,
})

local box = {
    { 0.48, -0.5,-0.5,  0.5,  1.5, 0.5},
    {-0.5 , -0.5, 0.48, 0.48, 1.5, 0.5},
    {-0.5,  -0.5,-0.5 ,-0.48, 1.5, 0.5},
    {-0.5 , -0.5, -0.48, 0.5, 1.5, -0.5},
    { -0.5,-0.5,-0.5,0.5,-0.48, 0.5},
    { -0.5, 1.45,-0.5,0.5, 1.5, 0.5},
}

-- Elevator box node. Not intended to be placeable.
minetest.register_node("elevator:elevator_box", {
    description = "Elevator",
    drawtype = "nodebox",
    paramtype = 'light',
    paramtype2 = "facedir",
    wield_scale = {x=0.6, y=0.6, z=0.6},

    selection_box = {
            type = "fixed",
            fixed = { -0.5, -0.5, -0.5, 0.5, 1.5, 0.5 }
    },

    collision_box = {
            type = "fixed",
            fixed = box,
    },

    node_box = {
            type = "fixed",
            fixed = box,
    },

    tiles = {
            "default_steel_block.png",
            "default_steel_block.png",
            "elevator_box.png",
            "elevator_box.png",
            "elevator_box.png",
            "elevator_box.png",
    },
    groups = {not_in_creative_inventory = 1},

    light_source = 4,
})

-- Remove the player from self, and teleport them to pos if specified.
local function detach(self, pos)
    local player = minetest.get_player_by_name(self.attached)
    local attached = player:get_attach()
    if not attached or attached:get_luaentity().uid ~= self.uid then
        return
    end
    player:set_detach()
    player:set_eye_offset({x=0, y=0, z=0},{x=0, y=0, z=0})
    player:set_properties({visual_size = {x=1, y=1}})
    if armor_path then
        armor:update_player_visuals(player)
    end
    if pos then
        player:setpos(pos)
	minetest.after(0.1, function(pl, p)
		pl:setpos(p)
	end, player, pos)
    end
    riding[self.attached] = nil
end

local box_entity = {
    physical = false,
    collisionbox = {0,0,0,0,0,0},
    visual = "wielditem",
    visual_size = {x=1, y=1},
    textures = {"elevator:elevator_box"},

    attached = "",
    motor = false,
    target = false,

    start = false,
    lastpos = false,
    halfway = false,
    vmult = 0,

    on_activate = function(self, staticdata)
        -- Don't want the box being destroyed by anything except the elevator system.
        self.object:set_armor_groups({immortal=1})
    end,

    on_step = function(self, dtime)
        local pos = self.object:getpos()
        -- First, check if this box needs removed.
        -- If the motor has a box and it isn't this box.
        if boxes[self.motor] and boxes[self.motor] ~= self.object then
            minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to duplication.")
            self.object:remove()
            return
        end
        -- If our attached player can't be found.
        if not minetest.get_player_by_name(self.attached) then
            minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to lack of attachee logged in.")
            self.object:remove()
            boxes[self.motor] = nil
            return
        end
        -- If our attached player is no longer with us.
        if not minetest.get_player_by_name(self.attached):get_attach() or minetest.get_player_by_name(self.attached):get_attach():get_luaentity().uid ~= self.uid then
            minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to lack of attachee.")
            self.object:remove()
            boxes[self.motor] = nil
            return
        end
        -- If our motor's box is nil, we should self-destruct.
        if not boxes[self.motor] then
            minetest.log("action", "[elevator] "..minetest.pos_to_string(pos).." broke due to nil entry in boxes.")
            detach(self)
            self.object:remove()
            boxes[self.motor] = nil
            return
        end

        minetest.get_player_by_name(self.attached):setpos(pos)
        -- Ensure lastpos is set to something.
        self.lastpos = self.lastpos or pos

        -- Loop through all travelled nodes.
        for y=self.lastpos.y,pos.y,((self.lastpos.y > pos.y) and -0.3 or 0.3) do
            local p = vector.round({x=pos.x, y=y, z=pos.z})
            local node = get_node(p)
            if node.name == "elevator:shaft" then
                -- Nothing, just continue on our way.
            elseif node.name == "elevator:elevator_on" or node.name == "elevator:elevator_off" then
                -- If this is our target, detach the player here, destroy this box, and update the target elevator without waiting for the abm.
                if vector.distance(p, self.target) < 1 then
                    minetest.log("action", "[elevator] "..minetest.pos_to_string(p).." broke due to arrival.")
                    detach(self, vector.add(self.target, {x=0, y=-0.4, z=0}))
                    self.object:remove()
                    boxes[self.motor] = nil
                    offabm(self.target, node)
                    return
                end
            else
                -- Check if we're in the top part of an elevator, if so it's fine.
                local below = vector.add(p, {x=0,y=-1,z=0})
                local belownode = get_node(below)
                if belownode.name ~= "elevator:elevator_on" and belownode.name ~= "elevator:elevator_off" then
                    -- If we aren't, then break the box.
                    minetest.log("action", "[elevator] "..minetest.pos_to_string(p).." broke on "..node.name)
                    boxes[self.motor] = nil
                    detach(self, p)
                    self.object:remove()
                    return
                end
            end
        end
        self.lastpos = pos
    end,
}

minetest.register_entity("elevator:box", box_entity)

if technic_path and chains_path then
    minetest.register_craft({
        output = "elevator:elevator",
        recipe = {
            {"technic:cast_iron_ingot", "chains:chain", "technic:cast_iron_ingot"},
            {"technic:cast_iron_ingot", "default:mese_crystal", "technic:cast_iron_ingot"},
            {"technic:stainless_steel_ingot", "default:glass", "technic:stainless_steel_ingot"},
        },
    })

    minetest.register_craft({
        output = "elevator:shaft",
        recipe = {
            {"technic:cast_iron_ingot", "default:glass"},
            {"default:glass", "glooptest:chainlink"},
        },
    })

    minetest.register_craft({
        output = "elevator:motor",
        recipe = {
            {"default:diamond", "technic:control_logic_unit", "default:diamond"},
            {"default:steelblock", "technic:motor", "default:steelblock"},
            {"chains:chain", "default:diamond", "chains:chain"}
        },
    })
elseif technic_path and farming and farming.mod and farming.mod == "redo" then
   -- add alternative recipe with hemp rope
       minetest.register_craft({
        output = "elevator:elevator",
        recipe = {
            {"technic:cast_iron_ingot", "farming:hemp_rope", "technic:cast_iron_ingot"},
            {"technic:cast_iron_ingot", "default:mese_crystal", "technic:cast_iron_ingot"},
            {"technic:stainless_steel_ingot", "default:glass", "technic:stainless_steel_ingot"},
        },
    })

    minetest.register_craft({
        output = "elevator:shaft",
        recipe = {
            {"technic:cast_iron_ingot", "default:glass"},
            {"default:glass", "farming:hemp_rope"},
        },
    })

    minetest.register_craft({
        output = "elevator:motor",
        recipe = {
            {"default:diamond", "technic:control_logic_unit", "default:diamond"},
            {"default:steelblock", "technic:motor", "default:steelblock"},
            {"farming:hemp_rope", "default:diamond", "farming:hemp_rope"}
        },
    })

   -- Recipes without technic & chains required.
-- Recipes for default dependency fallback.
else
    minetest.register_craft({
        output = "elevator:elevator",
        recipe = {
            {"default:steel_ingot", "farming:cotton", "default:steel_ingot"},
            {"default:steel_ingot", "default:mese_crystal", "default:steel_ingot"},
            {"xpanes:pane_flat", "default:glass", "xpanes:pane_flat"},
        },
    })

    minetest.register_craft({
        output = "elevator:shaft",
        recipe = {
            {"default:steel_ingot", "default:obsidian_glass"},
            {"default:obsidian_glass", "default:steel_ingot"},
        },
    })

    minetest.register_craft({
        output = "elevator:motor",
        recipe = {
            {"default:diamond", "default:copper_ingot", "default:diamond"},
            {"default:steelblock", "default:furnace", "default:steelblock"},
            {"farming:cotton", "default:diamond", "farming:cotton"}
        },
    })
end


code:
name = elevator
depends = default
optional_depends = technic, homedecor, chains, farming


code:
Extra Doors Mod v2.0
By Leslie Krause

Repository
----------------------

Browse source code:
  https://bitbucket.org/sorcerykid/extra_doors

Download archive:
  https://bitbucket.org/sorcerykid/extra_doors/get/master.zip
  https://bitbucket.org/sorcerykid/extra_doors/get/master.tar.gz

Revision History
----------------------

Version 1.0b (12-Jan-2018)
  - initial version within doors mod

Version 2.0 (31-Aug-2018)
  - separated common routines into standalone mod
  - included support files for public release

Compatability
----------------------

Minetest 0.4.15+ required

Dependencies
----------------------

Doors Mod (required)
  https://github.com/minetest/minetest_game/doors

Installation
----------------------

  1) Unzip the archive into the mods directory of your game
  2) Rename the extra_doors-master directory to "extra_doors"

Source Code License
----------------------

GNU Lesser General Public License v3 (LGPL-3.0)

Copyright (c) 2018, Leslie Krause (leslie@searstower.org)

This program is free software; you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.

http://www.gnu.org/licenses/lgpl-2.1.html

License of media (textures, sounds, and models)
----------------------------------------------------------

Creative Commons Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)

        /textures/doors_door_cottage1.png
        by kilbith (relicensed WTFPL)
	modified by sorcerykid

        /textures/doors_door_cottage2.png
        by kilbith (relicensed WTFPL)
	modified by sorcerykid

        /textures/doors_door_french.png
        by kilbith (relicensed WTFPL)
	modified by sorcerykid

        /textures/doors_door_japanese.png
        by kilbith (relicensed WTFPL)
	modified by sorcerykid

        /textures/doors_item_cottage1.png
	by kilbith (relicensed WTFPL)
        modified by sorcerykid

        /textures/doors_item_cottage2.png
	by kilbith (relicensed WTFPL)
        modified by sorcerykid

        /textures/doors_item_french.png
        by kilbith (relicensed WTFPL)
	modified by sorcerykid

        /textures/doors_item_japanese.png
        by kilbith (relicensed WTFPL)
	modified by sorcerykid

	/textures/default_steel_rod.png
	by sorcerykid

        /textures/doors_door_barn1.png
        by sorcerykid

        /textures/doors_door_barn2.png
        by sorcerykid

        /textures/doors_door_castle1.png
        by sorcerykid

        /textures/doors_door_castle2.png
        by sorcerykid

        /textures/doors_door_dungeon1.png
        by sorcerykid

        /textures/doors_door_dungeon2.png
        by sorcerykid

        /textures/doors_door_mansion1.png
        by sorcerykid

        /textures/doors_door_mansion2.png
        by sorcerykid

        /textures/doors_door_steelglass1.png
        by sorcerykid

        /textures/doors_door_steelglass2.png
        by sorcerykid

        /textures/doors_door_steelpanel1.png
        by sorcerykid

        /textures/doors_door_woodglass1.png
        by sorcerykid

        /textures/doors_door_woodglass2.png
        by sorcerykid

        /textures/doors_door_woodpanel1.png
        by sorcerykid

        /textures/doors_item_barn1.png
        by sorcerykid

        /textures/doors_item_barn2.png
        by sorcerykid

        /textures/doors_item_castle1.png
        by sorcerykid

        /textures/doors_item_castle2.png
        by sorcerykid

        /textures/doors_item_dungeon1.png
        by sorcerykid

        /textures/doors_item_dungeon2.png
        by sorcerykid

        /textures/doors_item_mansion1.png
        by sorcerykid

        /textures/doors_item_mansion2.png
        by sorcerykid

	/textures/doors_item_steelglass1.png
	by sorcerykid

        /textures/doors_item_steelglass2.png
        by sorcerykid

        /textures/doors_item_steelpanel1.png
        by sorcerykid

        /textures/doors_item_woodglass1.png
        by sorcerykid

        /textures/doors_item_woodglass2.png
        by sorcerykid

        /textures/doors_item_woodpanel1.png
        by sorcerykid

You are free to:
Share — copy and redistribute the material in any medium or format.
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
The licensor cannot revoke these freedoms as long as you follow the license terms.

Under the following terms:

Attribution — You must give appropriate credit, provide a link to the license, and
indicate if changes were made. You may do so in any reasonable manner, but not in any way
that suggests the licensor endorses you or your use.

No additional restrictions — You may not apply legal terms or technological measures that
legally restrict others from doing anything the license permits.

Notices:

You do not have to comply with the license for elements of the material in the public
domain or where your use is permitted by an applicable exception or limitation.
No warranties are given. The license may not give you all of the permissions necessary
for your intended use. For example, other rights such as publicity, privacy, or moral
rights may limit how you use the material.

For more details:
http://creativecommons.org/licenses/by-sa/3.0/


code: Extra Doors adds a set of 18 new doors to Minetest including castle, dungeon, barn, cottage, and multi-panel styles.


code:
--------------------------------------------------------
-- Minetest :: Extra Doors v2.0 (extra_doors)
--
-- See README.txt for licensing and release notes.
-- Copyright (c) 2018, Leslie E. Krause
--------------------------------------------------------

-- One of the most essential but often overlooked elements of building design is door selection.
-- Doors set the tone and character, and having the wrong style of door can make or break a build.

minetest.register_craftitem( ":default:steel_rod", {
        description = "Steel Rod",
        inventory_image = "default_steel_rod.png",
} )

minetest.register_craft( {
        output = "default:steel_rod 4",
        recipe = {
                { "default:steel_ingot" },
        }
} )

doors.register( "door_woodpanel1", {
	-- Colonial Style (6 panel)
	tiles = { { name = "doors_door_woodpanel1.png", backface_culling = true } },
	description = "Wooden Colonial Door",
	inventory_image = "doors_item_woodpanel1.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "group:wood", "group:wood", "default:stick" },
		{ "group:wood", "group:wood", "default:stick" },
		{ "group:wood", "group:wood", "default:stick" },
	}
} )

doors.register( "door_woodglass1", {
	-- Cambridge Style (2 panel)
	tiles = { { name = "doors_door_woodglass1.png", backface_culling = true } },
	description = "Wooden Single-Lite Door",
	inventory_image = "doors_item_woodglass1.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "default:glass", "default:glass", "default:stick" },
		{ "group:wood", "group:wood", "" },
		{ "group:wood", "group:wood", "" },
	}
} )

doors.register( "door_woodglass2", {
	-- Atherton Style (4 panel)
	tiles = { { name = "doors_door_woodglass2.png", backface_culling = true } },
	description = "Wooden Double-Lite Door",
	inventory_image = "doors_item_woodglass2.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "default:glass", "default:glass", "default:stick" },
		{ "group:wood", "group:wood", "default:stick" },
		{ "group:wood", "group:wood", "" },
	}
} )

doors.register( "door_japanese", {
	tiles = { { name = "doors_door_japanese.png", backface_culling = true } },
	description = "Japanese Door",
	inventory_image = "doors_item_japanese.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "default:paper", "group:wood", "default:stick" },
		{ "default:paper", "group:wood", "default:stick" },
		{ "default:paper", "group:wood", "default:stick" },
	}
} )

doors.register( "door_french", {
	tiles = { { name = "doors_door_french.png", backface_culling = true } },
	description = "French Door",
	inventory_image = "doors_item_french.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "default:glass", "group:wood", "default:stick" },
		{ "default:glass", "group:wood", "default:stick" },
		{ "default:glass", "group:wood", "default:stick" },
	}
} )

doors.register( "door_cottage1", {
	tiles = { { name = "doors_door_cottage1.png", backface_culling = true } },
	description = "Cottage Interior Door",
	inventory_image = "doors_item_cottage1.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "group:wood", "group:wood" },
		{ "default:stick", "default:stick" },
		{ "group:wood", "group:wood" },
	}
} )

doors.register( "door_cottage2", {
	tiles = { { name = "doors_door_cottage2.png", backface_culling = true } },
	description = "Cottage Exterior Door",
	inventory_image = "doors_item_cottage2.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "default:glass", "default:glass" },
		{ "default:stick", "default:stick" },
		{ "group:wood", "group:wood" },
	}
} )

doors.register( "door_barn1", {
	tiles = { { name = "doors_door_barn1.png", backface_culling = true } },
	description = "Barn Interior Door",
	inventory_image = "doors_item_barn1.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "group:wood", "group:wood", "group:wood" },
		{ "default:stick", "default:stick", "default:stick" },
		{ "group:wood", "group:wood", "group:wood" },
	}
} )

doors.register( "door_barn2", {
	tiles = { { name = "doors_door_barn2.png", backface_culling = true } },
	description = "Barn Exterior Door",
	inventory_image = "doors_item_barn2.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "group:wood", "group:wood", "group:wood" },
		{ "default:steel_rod", "default:steel_rod", "default:steel_rod" },
		{ "group:wood", "group:wood", "group:wood" },
	}
} )

doors.register( "door_castle1", {
	tiles = { { name = "doors_door_castle1.png", backface_culling = true } },
	description = "Castle Interior Door",
	inventory_image = "doors_item_castle1.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "default:stick", "group:wood", "group:wood" },
		{ "", "group:wood", "group:wood" },
		{ "default:stick", "group:wood", "group:wood" },
	}
} )

doors.register( "door_castle2", {
	tiles = { { name = "doors_door_castle2.png", backface_culling = true } },
	description = "Castle Exterior Door",
	inventory_image = "doors_item_castle2.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "default:steel_rod", "group:wood", "group:wood" },
		{ "", "group:wood", "group:wood" },
		{ "default:steel_rod", "group:wood", "group:wood" },
	}
} )

doors.register( "door_mansion1", {
	tiles = { { name = "doors_door_mansion1.png", backface_culling = true } },
	description = "Mansion Interior Door",
	inventory_image = "doors_item_mansion1.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "group:wood", "group:wood", "dye:white" },
		{ "group:wood", "group:wood", "dye:yellow" },
		{ "group:wood", "group:wood", "dye:white" },
	}
} )

doors.register( "door_mansion2", {
	tiles = { { name = "doors_door_mansion2.png", backface_culling = true } },
	description = "Mansion Exterior Door ",
	inventory_image = "doors_item_mansion2.png",
	groups = { choppy = 2, oddly_breakable_by_hand = 2, flammable = 2 },
	recipe = {
		{ "group:wood", "group:wood", "dye:black" },
		{ "group:wood", "group:wood", "dye:yellow" },
		{ "group:wood", "group:wood", "dye:black" },
	}
} )

doors.register("door_dungeon1", {
	tiles = { { name = "doors_door_dungeon1.png", backface_culling = true } },
	description = "Dungeon Interior Door",
	inventory_image = "doors_item_dungeon1.png",
	protected = true,
	groups = { cracky = 1, level = 2 },
	sounds = default.node_sound_metal_defaults( ),
	sound_open = "doors_steel_door_open",
	sound_close = "doors_steel_door_close",
	recipe = {
		{ "default:steel_ingot", "default:steel_rod", "default:steel_ingot" },
		{ "default:steel_rod", "default:steel_rod", "default:steel_rod" },
		{ "default:steel_ingot", "default:steel_rod", "default:steel_ingot" },
	}
} )

doors.register( "door_dungeon2", {
	tiles = { { name = "doors_door_dungeon2.png", backface_culling = true } },
	description = "Dungeon Exterior Door",
	inventory_image = "doors_item_dungeon2.png",
	protected = true,
	groups = { cracky = 1, level = 2 },
	sounds = default.node_sound_metal_defaults( ),
	sound_open = "doors_steel_door_open",
	sound_close = "doors_steel_door_close",
	recipe = {
		{ "default:steel_rod", "default:steel_rod", "default:steel_rod" },
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_ingot" },
	}
} )

doors.register( "door_steelpanel1", {
	tiles = { { name = "doors_door_steelpanel1.png", backface_culling = true } },
	description = "Steel Colonial Door",
	inventory_image = "doors_item_steelpanel1.png",
	protected = true,
	groups = { cracky = 1, level = 2 },
	sounds = default.node_sound_metal_defaults( ),
	sound_open = "doors_steel_door_open",
	sound_close = "doors_steel_door_close",
	recipe = {
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_rod" },
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_rod" },
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_rod" },
	}
} )

doors.register( "door_steelglass1", {
	tiles = { { name = "doors_door_steelglass1.png", backface_culling = true } },
	description = "Steel Single-Lite Door",
	inventory_image = "doors_item_steelglass1.png",
	protected = true,
	groups = { cracky = 1, level = 2 },
	sounds = default.node_sound_metal_defaults( ),
	sound_open = "doors_steel_door_open",
	sound_close = "doors_steel_door_close",
	recipe = {
		{ "default:glass", "default:glass", "default:steel_rod" },
		{ "default:steel_ingot", "default:steel_ingot", "" },
		{ "default:steel_ingot", "default:steel_ingot", "" },
	}
} )

doors.register( "door_steelglass2", {
	tiles = { { name = "doors_door_steelglass2.png", backface_culling = true } },
	description = "Steel Double-Lite Door",
	inventory_image = "doors_item_steelglass2.png",
	protected = true,
	groups = { cracky = 1, level = 2 },
	sounds = default.node_sound_metal_defaults( ),
	sound_open = "doors_steel_door_open",
	sound_close = "doors_steel_door_close",
	recipe = {
		{ "default:glass", "default:glass", "default:steel_rod" },
		{ "default:steel_ingot", "default:steel_ingot", "default:steel_rod" },
		{ "default:steel_ingot", "default:steel_ingot", "" },
	}
} )

minetest.register_craft( {
	type = "fuel",
	recipe = "doors:door_barn1",
	burntime = 14,
} )

minetest.register_craft( {
	type = "fuel",
	recipe = "doors:door_barn2",
	burntime = 16,
} )

minetest.register_craft( {
	type = "fuel",
	recipe = "doors:door_castle1",
	burntime = 8,
} )

minetest.register_craft( {
	type = "fuel",
	recipe = "doors:door_castle2",
	burntime = 12,
} )


code:
name = extra_doors
title = Extra Doors
author = sorcerykid
license = LGPL-3.0
depends = doors
description = Extra Doors adds a set of 18 new doors to Minetest including castle, dungeon, barn, cottage, and multi-panel styles.