2017-02-14 02:23:06 +00:00
--[[ This mod registers 3 nodes:
- One node for the horizontal - facing dispensers ( mcl_dispensers : dispenser )
- One node for the upwards - facing dispensers ( mcl_dispenser : dispenser_up )
- One node for the downwards - facing dispensers ( mcl_dispenser : dispenser_down )
3 node definitions are needed because of the way the textures are defined .
All node definitions share a lot of code , so this is the reason why there
are so many weird tables below .
] ]
-- For after_place_node
local setup_dispenser = function ( pos )
-- Set formspec and inventory
local form = " size[9,8.75] " ..
" background[-0.19,-0.25;9.41,9.49;crafting_inventory_9_slots.png] " ..
2017-02-17 22:06:52 +00:00
mcl_vars.inventory_header ..
2017-02-14 02:23:06 +00:00
" image[3,-0.2;5,0.75;mcl_dispensers_fnt_dispenser.png] " ..
" list[current_player;main;0,4.5;9,3;9] " ..
" list[current_player;main;0,7.74;9,1;] " ..
" list[current_name;main;3,0.5;3,3;] " ..
" listring[current_name;main] " ..
" listring[current_player;main] "
local meta = minetest.get_meta ( pos )
meta : set_string ( " formspec " , form )
local inv = meta : get_inventory ( )
inv : set_size ( " main " , 9 )
end
-- Shared core definition table
local dispenserdef = {
is_ground_content = false ,
sounds = mcl_sounds.node_sound_stone_defaults ( ) ,
after_dig_node = function ( pos , oldnode , oldmetadata , digger )
local meta = minetest.get_meta ( pos )
local meta2 = meta
meta : from_table ( oldmetadata )
local inv = meta : get_inventory ( )
for i = 1 , inv : get_size ( " main " ) do
local stack = inv : get_stack ( " main " , i )
if not stack : is_empty ( ) then
local p = { x = pos.x + math.random ( 0 , 10 ) / 10 - 0.5 , y = pos.y , z = pos.z + math.random ( 0 , 10 ) / 10 - 0.5 }
minetest.add_item ( p , stack )
end
end
meta : from_table ( meta2 : to_table ( ) )
end ,
2017-02-27 00:52:24 +00:00
_mcl_blast_resistance = 17.5 ,
_mcl_hardness = 3.5 ,
2017-02-14 02:23:06 +00:00
mesecons = { effector = {
-- Dispense random item when triggered
action_on = function ( pos , node )
local meta = minetest.get_meta ( pos )
local inv = meta : get_inventory ( )
2017-02-14 21:04:37 +00:00
local droppos , dropdir
2017-02-14 02:23:06 +00:00
if node.name == " mcl_dispensers:dispenser " then
2017-02-14 21:04:37 +00:00
dropdir = vector.multiply ( minetest.facedir_to_dir ( node.param2 ) , - 1 )
2017-02-14 23:16:47 +00:00
droppos = vector.add ( pos , dropdir )
2017-02-14 02:23:06 +00:00
elseif node.name == " mcl_dispensers:dispenser_up " then
2017-02-14 21:04:37 +00:00
dropdir = { x = 0 , y = 1 , z = 0 }
2017-02-14 02:23:06 +00:00
droppos = { x = pos.x , y = pos.y + 1 , z = pos.z }
elseif node.name == " mcl_dispensers:dispenser_down " then
2017-02-14 21:04:37 +00:00
dropdir = { x = 0 , y =- 1 , z = 0 }
2017-02-14 02:23:06 +00:00
droppos = { x = pos.x , y = pos.y - 1 , z = pos.z }
end
local dropnode = minetest.get_node ( droppos )
-- Do not dispense into solid nodes
local dropnodedef = minetest.registered_nodes [ dropnode.name ]
if dropnodedef.walkable then
return
end
local stacks = { }
for i = 1 , inv : get_size ( " main " ) do
local stack = inv : get_stack ( " main " , i )
if not stack : is_empty ( ) then
table.insert ( stacks , { stack = stack , stackpos = i } )
end
end
if # stacks >= 1 then
local r = math.random ( 1 , # stacks )
local stack = stacks [ r ] . stack
local dropitem = ItemStack ( stack : get_name ( ) )
local stack_id = stacks [ r ] . stackpos
local iname = stack : get_name ( )
local igroups = minetest.registered_items [ iname ] . groups
--[===[ Dispense item ]===]
2017-02-14 21:04:37 +00:00
if iname == " mcl_throwing:arrow " then
-- Shoot arrow
2017-02-15 00:38:47 +00:00
local shootpos = vector.add ( pos , vector.multiply ( dropdir , 0.51 ) )
2017-02-21 21:31:39 +00:00
local yaw = math.atan2 ( dropdir.z , dropdir.x ) - math.pi / 2
2017-02-21 21:57:46 +00:00
mcl_throwing.shoot_arrow ( iname , shootpos , dropdir , yaw , nil , 19 , 3 )
2017-02-14 21:04:37 +00:00
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
2017-02-14 22:54:06 +00:00
elseif iname == " mcl_throwing:egg " or iname == " mcl_throwing:snowball " then
-- Throw egg or snowball
2017-02-15 00:38:47 +00:00
local shootpos = vector.add ( pos , vector.multiply ( dropdir , 0.51 ) )
2017-02-14 22:54:06 +00:00
mcl_throwing.throw ( iname , shootpos , dropdir )
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
2017-02-15 00:38:47 +00:00
elseif iname == " mcl_fire:fire_charge " then
-- Throw fire charge
local shootpos = vector.add ( pos , vector.multiply ( dropdir , 0.51 ) )
local fireball = minetest.add_entity ( shootpos , " mobs_mc:blaze_fireball " )
local ent = fireball : get_luaentity ( )
2017-07-24 17:53:03 +00:00
ent._shot_from_dispenser = true
2017-02-15 00:38:47 +00:00
local v = ent.velocity or 1
fireball : setvelocity ( vector.multiply ( dropdir , v ) )
ent.switch = 1
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
2017-02-14 21:04:37 +00:00
elseif iname == " mcl_fire:flint_and_steel " then
2017-02-14 02:23:06 +00:00
-- Ignite air or fire
if dropnode.name == " air " then
2017-02-19 21:48:46 +00:00
minetest.add_node ( droppos , { name = " mcl_fire:fire " } )
2017-08-09 14:17:00 +00:00
if not minetest.settings : get_bool ( " creative_mode " ) then
2017-02-14 02:23:06 +00:00
stack : add_wear ( 65535 / 65 ) -- 65 uses
end
elseif dropnode.name == " mcl_tnt:tnt " then
tnt.ignite ( droppos )
2017-08-09 14:17:00 +00:00
if not minetest.settings : get_bool ( " creative_mode " ) then
2017-02-14 02:23:06 +00:00
stack : add_wear ( 65535 / 65 ) -- 65 uses
end
end
inv : set_stack ( " main " , stack_id , stack )
2017-02-14 02:30:10 +00:00
elseif iname == " mcl_tnt:tnt " then
-- Place and ignite TNT
if dropnodedef.buildable_to then
minetest.set_node ( droppos , { name = iname } )
tnt.ignite ( droppos )
2017-02-14 02:42:11 +00:00
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
end
2017-08-03 14:36:55 +00:00
elseif iname == " mcl_buckets:bucket_empty " then
2017-02-14 02:42:11 +00:00
-- Fill empty bucket with liquid or drop bucket if no liquid
local collect_liquid = false
local bucket_id
if dropnode.name == " mcl_core:water_source " then
collect_liquid = true
2017-08-03 14:36:55 +00:00
bucket_id = " mcl_buckets:bucket_water "
2017-08-29 00:50:46 +00:00
elseif dropnode.name == " mcl_core:lava_source " or dropnode.name == " mcl_nether:nether_lava_source " then
2017-02-14 02:42:11 +00:00
collect_liquid = true
2017-08-03 14:36:55 +00:00
bucket_id = " mcl_buckets:bucket_lava "
2017-02-14 02:42:11 +00:00
end
if collect_liquid then
2017-02-14 03:12:17 +00:00
minetest.set_node ( droppos , { name = " air " } )
2017-02-14 02:42:11 +00:00
-- Fill bucket with liquid and put it back into inventory
-- if there's still space. If not, drop it.
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
local new_bucket = ItemStack ( bucket_id )
if inv : room_for_item ( " main " , new_bucket ) then
inv : add_item ( " main " , new_bucket )
else
minetest.add_item ( droppos , dropitem )
end
else
-- No liquid found: Drop empty bucket
minetest.add_item ( droppos , dropitem )
2017-02-14 02:30:10 +00:00
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
end
2017-08-03 14:36:55 +00:00
elseif iname == " mcl_buckets:bucket_water " or iname == " mcl_buckets:bucket_lava " then
2017-02-14 02:53:50 +00:00
-- Place water/lava source
if dropnodedef.buildable_to then
2017-08-29 00:50:46 +00:00
local _ , dim = mcl_util.y_to_layer ( droppos.y )
2017-08-03 14:36:55 +00:00
if iname == " mcl_buckets:bucket_water " then
2017-08-29 00:50:46 +00:00
if dim == " nether " then
minetest.sound_play ( " fire_extinguish_flame " , { pos = droppos , gain = 0.25 , max_hear_distance = 16 } )
else
minetest.set_node ( droppos , { name = " mcl_core:water_source " } )
end
2017-08-03 14:36:55 +00:00
elseif iname == " mcl_buckets:bucket_lava " then
2017-08-29 00:50:46 +00:00
if dim == " nether " then
minetest.set_node ( droppos , { name = " mcl_nether:nether_lava_source " } )
else
minetest.set_node ( droppos , { name = " mcl_core:lava_source " } )
end
2017-02-14 02:53:50 +00:00
end
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
2017-08-03 14:36:55 +00:00
if inv : room_for_item ( " main " , " mcl_buckets:bucket_empty " ) then
inv : add_item ( " main " , " mcl_buckets:bucket_empty " )
2017-02-14 02:53:50 +00:00
else
minetest.add_item ( droppos , dropitem )
end
end
2017-02-14 17:20:54 +00:00
elseif iname == " mcl_dye:white " then
-- Apply bone meal, if possible
2017-08-29 00:36:50 +00:00
local pointed_thing
2017-02-14 17:20:54 +00:00
if dropnode.name == " air " then
pointed_thing = { above = droppos , under = { x = droppos.x , y = droppos.y - 1 , z = droppos.z } }
else
pointed_thing = { above = pos , under = droppos }
end
local success = mcl_dye.apply_bone_meal ( pointed_thing )
if success then
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
end
2017-08-29 00:36:50 +00:00
elseif minetest.get_item_group ( iname , " minecart " ) == 1 then
2017-02-14 03:12:17 +00:00
-- Place minecart as entity on rail
2017-08-29 00:36:50 +00:00
local placed
2017-02-14 03:12:17 +00:00
if dropnodedef.groups . rail then
2017-08-29 00:36:50 +00:00
-- FIXME: This places minecarts even if the spot is already occupied
local pointed_thing = { under = droppos , above = { x = droppos.x , y = droppos.y + 1 , z = droppos.z } }
placed = mcl_minecarts.place_minecart ( stack , pointed_thing )
end
if placed == nil then
2017-02-14 03:12:17 +00:00
-- Drop item
minetest.add_item ( droppos , dropitem )
end
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
elseif igroups.boat then
local below = { x = droppos.x , y = droppos.y - 1 , z = droppos.z }
local belownode = minetest.get_node ( below )
-- Place boat as entity on or in water
if dropnodedef.groups . water or ( dropnode.name == " air " and minetest.registered_nodes [ belownode.name ] . groups.water ) then
minetest.add_entity ( droppos , " mcl_boats:boat " )
else
minetest.add_item ( droppos , dropitem )
end
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
2017-02-14 18:45:02 +00:00
elseif igroups.armor_head or igroups.armor_torso or igroups.armor_legs or igroups.armor_feet then
local armor_type , armor_slot
2017-06-11 20:53:12 +00:00
local armor_dispensed = false
2017-02-14 18:45:02 +00:00
if igroups.armor_head then
armor_type = " armor_head "
armor_slot = 2
elseif igroups.armor_torso then
armor_type = " armor_torso "
armor_slot = 3
elseif igroups.armor_legs then
armor_type = " armor_legs "
armor_slot = 4
elseif igroups.armor_feet then
armor_type = " armor_feet "
armor_slot = 5
end
local droppos_below = { x = droppos.x , y = droppos.y - 1 , z = droppos.z }
local dropnode_below = minetest.get_node ( droppos_below )
-- Put armor on player or armor stand
local standpos
if dropnode.name == " 3d_armor_stand:armor_stand " then
standpos = droppos
elseif dropnode_below.name == " 3d_armor_stand:armor_stand " then
standpos = droppos_below
end
if standpos then
local dropmeta = minetest.get_meta ( standpos )
local dropinv = dropmeta : get_inventory ( )
if dropinv : room_for_item ( armor_type , dropitem ) then
dropinv : add_item ( armor_type , dropitem )
--[[ FIXME: For some reason, this function is not called after calling add_item,
so we call it manually to update the armor stand entity .
This may need investigation and the following line may be a small hack . ] ]
minetest.registered_nodes [ " 3d_armor_stand:armor_stand " ] . on_metadata_inventory_put ( standpos )
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
2017-06-11 20:53:12 +00:00
armor_dispensed = true
2017-02-14 18:45:02 +00:00
end
else
-- Put armor on nearby player
-- First search for player in front of dispenser (check 2 nodes)
local objs1 = minetest.get_objects_inside_radius ( droppos , 1 )
local objs2 = minetest.get_objects_inside_radius ( droppos_below , 1 )
local objs_table = { objs1 , objs2 }
local player
for oi = 1 , # objs_table do
local objs_inner = objs_table [ oi ]
for o = 1 , # objs_inner do
--[[ First player in list is the lucky one. The other player get nothing :-(
If multiple players are close to the dispenser , it can be a bit
-- unpredictable on who gets the armor. ]]
if objs_inner [ o ] : is_player ( ) then
player = objs_inner [ o ]
break
end
end
if player then
break
end
end
-- If player found, add armor
if player then
local ainv = minetest.get_inventory ( { type = " detached " , name = player : get_player_name ( ) .. " _armor " } )
local pinv = player : get_inventory ( )
if ainv : get_stack ( " armor " , armor_slot ) : is_empty ( ) and pinv : get_stack ( " armor " , armor_slot ) : is_empty ( ) then
ainv : set_stack ( " armor " , armor_slot , dropitem )
pinv : set_stack ( " armor " , armor_slot , dropitem )
armor : set_player_armor ( player )
armor : update_inventory ( player )
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
2017-06-11 20:53:12 +00:00
armor_dispensed = true
2017-02-14 18:45:02 +00:00
end
end
2017-06-11 20:53:12 +00:00
-- Place head or pumpkin as node, if equipping it as armor has failed
if not armor_dispensed then
if igroups.head or iname == " mcl_farming:pumpkin_face " then
if dropnodedef.buildable_to then
minetest.set_node ( droppos , { name = iname , param2 = node.param2 } )
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
end
end
end
end
2017-06-12 20:25:21 +00:00
elseif igroups.shulker_box then
-- Place shulker box as node
2017-06-11 20:53:12 +00:00
if dropnodedef.buildable_to then
minetest.set_node ( droppos , { name = iname , param2 = node.param2 } )
2017-06-12 20:25:21 +00:00
local imeta = stack : get_metadata ( )
local iinv_main = minetest.deserialize ( imeta )
local ninv = minetest.get_inventory ( { type = " node " , pos = droppos } )
ninv : set_list ( " main " , iinv_main )
2017-06-11 20:53:12 +00:00
stack : take_item ( )
2017-02-14 18:45:02 +00:00
end
2017-02-14 23:31:00 +00:00
elseif igroups.spawn_egg then
-- Place spawn egg
if not dropnodedef.walkable then
pointed_thing = { above = droppos , under = { x = droppos.x , y = droppos.y - 1 , z = droppos.z } }
minetest.registered_items [ iname ] . on_place ( ItemStack ( iname ) , nil , pointed_thing )
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
end
2017-02-14 02:23:06 +00:00
-- TODO: Many other dispenser actions
else
-- Drop item
minetest.add_item ( droppos , dropitem )
stack : take_item ( )
inv : set_stack ( " main " , stack_id , stack )
end
end
2017-09-14 00:20:47 +00:00
end ,
rules = mesecon.rules . alldirs ,
2017-02-14 02:23:06 +00:00
} }
}
-- Horizontal dispenser
local horizontal_def = table.copy ( dispenserdef )
horizontal_def.description = " Dispenser "
2017-03-11 01:50:12 +00:00
horizontal_def._doc_items_longdesc = " A dispenser is a block which acts as a redstone component which, when powered with redstone power, dispenses an item. It has a container with 9 inventory slots. "
horizontal_def._doc_items_usagehelp = [ [ Place the dispenser in one of 6 possible directions . The “ hole ” is where items will fly out of the dispenser . Rightclick the dispenser to access its inventory . Insert the items you wish to dispense . Supply the dispenser with redstone energy once to dispense a single random item .
The dispenser will do different things , depending on the dispensed item :
• Arrows : Are launched
• Eggs and snowballs : Are thrown
• Fire charges : Are fired in a straight line
• Armor : Will be equipped to players and armor stands
• Boats : Are placed on water or are dropped
• Minecart : Are placed on rails or are dropped
• Bone meal : Is applied on the block it is facint
• Empty buckets : Are used to collect a liquid source
• Filled buckets : Are used to place a liquid source
2017-06-11 20:54:46 +00:00
• Heads , pumpkins : Equipped to players and armor stands , or placed as a block
• Shulker boxes : Are placed as a block
2017-03-11 01:50:12 +00:00
• TNT : Is placed and ignited
• Flint and steel : Is used to ignite a fire in air and to ignite TNT
• Spawn eggs : Will summon the mob they contain
• Other items : Are simply dropped ] ]
2017-02-14 02:23:06 +00:00
horizontal_def.after_place_node = function ( pos , placer , itemstack , pointed_thing )
setup_dispenser ( pos )
-- When placed up and down, convert node to up/down dispenser
if pointed_thing.above . y < pointed_thing.under . y then
minetest.swap_node ( pos , { name = " mcl_dispensers:dispenser_down " } )
elseif pointed_thing.above . y > pointed_thing.under . y then
minetest.swap_node ( pos , { name = " mcl_dispensers:dispenser_up " } )
end
-- Else, the normal facedir logic applies
end
horizontal_def.tiles = {
" default_furnace_top.png " , " default_furnace_bottom.png " ,
" default_furnace_side.png " , " default_furnace_side.png " ,
" default_furnace_side.png " , " mcl_dispensers_dispenser_front_horizontal.png "
}
horizontal_def.paramtype2 = " facedir "
2017-03-11 04:34:58 +00:00
horizontal_def.groups = { pickaxey = 1 , container = 2 , material_stone = 1 }
2017-02-14 02:23:06 +00:00
minetest.register_node ( " mcl_dispensers:dispenser " , horizontal_def )
-- Down dispenser
local down_def = table.copy ( dispenserdef )
down_def.description = " Downwards-Facing Dispenser "
down_def.after_place_node = setup_dispenser
down_def.tiles = {
" default_furnace_top.png " , " mcl_dispensers_dispenser_front_vertical.png " ,
" default_furnace_side.png " , " default_furnace_side.png " ,
" default_furnace_side.png " , " default_furnace_side.png "
}
2017-03-11 04:34:58 +00:00
down_def.groups = { pickaxey = 1 , container = 2 , not_in_creative_inventory = 1 , material_stone = 1 }
2017-03-02 18:53:53 +00:00
down_def._doc_items_create_entry = false
2017-02-14 02:23:06 +00:00
down_def.drop = " mcl_dispensers:dispenser "
minetest.register_node ( " mcl_dispensers:dispenser_down " , down_def )
-- Up dispenser
-- The up dispenser is almost identical to the down dispenser , it only differs in textures
2017-08-01 23:34:12 +00:00
local up_def = table.copy ( down_def )
2017-02-14 02:23:06 +00:00
up_def.description = " Upwards-Facing Dispenser "
up_def.tiles = {
" mcl_dispensers_dispenser_front_vertical.png " , " default_furnace_bottom.png " ,
" default_furnace_side.png " , " default_furnace_side.png " ,
" default_furnace_side.png " , " default_furnace_side.png "
}
minetest.register_node ( " mcl_dispensers:dispenser_up " , up_def )
minetest.register_craft ( {
output = ' mcl_dispensers:dispenser ' ,
recipe = {
{ " mcl_core:cobble " , " mcl_core:cobble " , " mcl_core:cobble " , } ,
{ " mcl_core:cobble " , " mcl_throwing:bow " , " mcl_core:cobble " , } ,
{ " mcl_core:cobble " , " mesecons:redstone " , " mcl_core:cobble " , } ,
}
} )
2017-02-14 03:39:37 +00:00
-- Only allow crafting if the bow is intact
2017-02-14 03:46:44 +00:00
local check_craft = function ( itemstack , player , old_craft_grid , craft_inv )
2017-02-14 03:39:37 +00:00
if itemstack : get_name ( ) == " mcl_dispensers:dispenser " then
local bow , id
for i = 1 , craft_inv : get_size ( " craft " ) do
local item = craft_inv : get_stack ( " craft " , i )
if item : get_name ( ) == " mcl_throwing:bow " then
bow = item
id = i
break
end
end
2017-02-14 03:46:44 +00:00
if bow and bow : get_wear ( ) ~= 0 then
2017-02-14 03:39:37 +00:00
return " "
end
end
return nil
2017-02-14 03:46:44 +00:00
end
2017-02-14 03:39:37 +00:00
2017-02-14 03:46:44 +00:00
minetest.register_on_craft ( check_craft )
minetest.register_craft_predict ( check_craft )
2017-03-21 03:27:50 +00:00
-- Add entry aliases for the Help
if minetest.get_modpath ( " doc " ) then
doc.add_entry_alias ( " nodes " , " mcl_dispensers:dispenser " , " nodes " , " mcl_dispensers:dispenser_down " )
doc.add_entry_alias ( " nodes " , " mcl_dispensers:dispenser " , " nodes " , " mcl_dispensers:dispenser_up " )
end