Dispenser craft no longer needs bow to be intact
[MineClone/MineClone2.git] / mods / ITEMS / REDSTONE / mcl_dispensers / init.lua
blob0642f7f102e183b17df451aa1cf07a3fed0250c2
1 --[[ This mod registers 3 nodes:
2 - One node for the horizontal-facing dispensers (mcl_dispensers:dispenser)
3 - One node for the upwards-facing dispensers (mcl_dispenser:dispenser_up)
4 - One node for the downwards-facing dispensers (mcl_dispenser:dispenser_down)
6 3 node definitions are needed because of the way the textures are defined.
7 All node definitions share a lot of code, so this is the reason why there
8 are so many weird tables below.
9 ]]
11 -- For after_place_node
12 local setup_dispenser = function(pos)
13 -- Set formspec and inventory
14 local form = "size[9,8.75]"..
15 "background[-0.19,-0.25;9.41,9.49;crafting_inventory_9_slots.png]"..
16 mcl_vars.inventory_header..
17 "image[3,-0.2;5,0.75;mcl_dispensers_fnt_dispenser.png]"..
18 "list[current_player;main;0,4.5;9,3;9]"..
19 "list[current_player;main;0,7.74;9,1;]"..
20 "list[current_name;main;3,0.5;3,3;]"..
21 "listring[current_name;main]"..
22 "listring[current_player;main]"
23 local meta = minetest.get_meta(pos)
24 meta:set_string("formspec", form)
25 local inv = meta:get_inventory()
26 inv:set_size("main", 9)
27 end
29 local orientate_dispenser = function(pos, placer)
30 -- Not placed by player
31 if not placer then return end
33 -- Pitch in degrees
34 local pitch = placer:get_look_vertical() * (180 / math.pi)
36 local node = minetest.get_node(pos)
37 if pitch > 55 then
38 minetest.swap_node(pos, {name="mcl_dispensers:dispenser_up", param2 = node.param2})
39 elseif pitch < -55 then
40 minetest.swap_node(pos, {name="mcl_dispensers:dispenser_down", param2 = node.param2})
41 end
42 end
44 local on_rotate
45 if minetest.get_modpath("screwdriver") then
46 on_rotate = screwdriver.rotate_simple
47 end
49 -- Shared core definition table
50 local dispenserdef = {
51 is_ground_content = false,
52 sounds = mcl_sounds.node_sound_stone_defaults(),
53 after_dig_node = function(pos, oldnode, oldmetadata, digger)
54 local meta = minetest.get_meta(pos)
55 local meta2 = meta
56 meta:from_table(oldmetadata)
57 local inv = meta:get_inventory()
58 for i=1, inv:get_size("main") do
59 local stack = inv:get_stack("main", i)
60 if not stack:is_empty() then
61 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}
62 minetest.add_item(p, stack)
63 end
64 end
65 meta:from_table(meta2:to_table())
66 end,
67 _mcl_blast_resistance = 17.5,
68 _mcl_hardness = 3.5,
69 mesecons = {effector = {
70 -- Dispense random item when triggered
71 action_on = function (pos, node)
72 local meta = minetest.get_meta(pos)
73 local inv = meta:get_inventory()
74 local droppos, dropdir
75 if node.name == "mcl_dispensers:dispenser" then
76 dropdir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
77 droppos = vector.add(pos, dropdir)
78 elseif node.name == "mcl_dispensers:dispenser_up" then
79 dropdir = {x=0, y=1, z=0}
80 droppos = {x=pos.x, y=pos.y+1, z=pos.z}
81 elseif node.name == "mcl_dispensers:dispenser_down" then
82 dropdir = {x=0, y=-1, z=0}
83 droppos = {x=pos.x, y=pos.y-1, z=pos.z}
84 end
85 local dropnode = minetest.get_node(droppos)
86 local dropnodedef = minetest.registered_nodes[dropnode.name]
87 local stacks = {}
88 for i=1,inv:get_size("main") do
89 local stack = inv:get_stack("main", i)
90 if not stack:is_empty() then
91 table.insert(stacks, {stack = stack, stackpos = i})
92 end
93 end
94 if #stacks >= 1 then
95 local r = math.random(1, #stacks)
96 local stack = stacks[r].stack
97 local dropitem = ItemStack(stack)
98 dropitem:set_count(1)
99 local stack_id = stacks[r].stackpos
100 local stackdef = stack:get_definition()
101 local iname = stack:get_name()
102 local igroups = minetest.registered_items[iname].groups
104 --[===[ Dispense item ]===]
106 -- Hardcoded dispensions --
108 -- Armor, mob heads and pumpkins
109 if igroups.armor_head or igroups.armor_torso or igroups.armor_legs or igroups.armor_feet then
110 local armor_type, armor_slot
111 local armor_dispensed = false
112 if igroups.armor_head then
113 armor_type = "armor_head"
114 armor_slot = 2
115 elseif igroups.armor_torso then
116 armor_type = "armor_torso"
117 armor_slot = 3
118 elseif igroups.armor_legs then
119 armor_type = "armor_legs"
120 armor_slot = 4
121 elseif igroups.armor_feet then
122 armor_type = "armor_feet"
123 armor_slot = 5
126 local droppos_below = {x=droppos.x, y=droppos.y-1, z=droppos.z}
127 local dropnode_below = minetest.get_node(droppos_below)
128 -- Put armor on player or armor stand
129 local standpos
130 if dropnode.name == "3d_armor_stand:armor_stand" then
131 standpos = droppos
132 elseif dropnode_below.name == "3d_armor_stand:armor_stand" then
133 standpos = droppos_below
135 if standpos then
136 local dropmeta = minetest.get_meta(standpos)
137 local dropinv = dropmeta:get_inventory()
138 if dropinv:room_for_item(armor_type, dropitem) then
139 dropinv:add_item(armor_type, dropitem)
140 --[[ FIXME: For some reason, this function is not called after calling add_item,
141 so we call it manually to update the armor stand entity.
142 This may need investigation and the following line may be a small hack. ]]
143 minetest.registered_nodes["3d_armor_stand:armor_stand"].on_metadata_inventory_put(standpos)
144 stack:take_item()
145 inv:set_stack("main", stack_id, stack)
146 armor_dispensed = true
148 else
149 -- Put armor on nearby player
150 -- First search for player in front of dispenser (check 2 nodes)
151 local objs1 = minetest.get_objects_inside_radius(droppos, 1)
152 local objs2 = minetest.get_objects_inside_radius(droppos_below, 1)
153 local objs_table = {objs1, objs2}
154 local player
155 for oi=1, #objs_table do
156 local objs_inner = objs_table[oi]
157 for o=1, #objs_inner do
158 --[[ First player in list is the lucky one. The other player get nothing :-(
159 If multiple players are close to the dispenser, it can be a bit
160 -- unpredictable on who gets the armor. ]]
161 if objs_inner[o]:is_player() then
162 player = objs_inner[o]
163 break
166 if player then
167 break
170 -- If player found, add armor
171 if player then
172 local ainv = minetest.get_inventory({type="detached", name=player:get_player_name().."_armor"})
173 local pinv = player:get_inventory()
174 if ainv:get_stack("armor", armor_slot):is_empty() and pinv:get_stack("armor", armor_slot):is_empty() then
175 ainv:set_stack("armor", armor_slot, dropitem)
176 pinv:set_stack("armor", armor_slot, dropitem)
177 armor:set_player_armor(player)
178 armor:update_inventory(player)
180 stack:take_item()
181 inv:set_stack("main", stack_id, stack)
182 armor_dispensed = true
186 -- Place head or pumpkin as node, if equipping it as armor has failed
187 if not armor_dispensed then
188 if igroups.head or iname == "mcl_farming:pumpkin_face" then
189 if dropnodedef.buildable_to then
190 minetest.set_node(droppos, {name = iname, param2 = node.param2})
191 stack:take_item()
192 inv:set_stack("main", stack_id, stack)
198 -- Spawn Egg
199 elseif igroups.spawn_egg then
200 -- Spawn mob
201 if not dropnodedef.walkable then
202 pointed_thing = { above = droppos, under = { x=droppos.x, y=droppos.y-1, z=droppos.z } }
203 minetest.add_entity(droppos, stack:get_name())
205 stack:take_item()
206 inv:set_stack("main", stack_id, stack)
209 -- Generalized dispension
210 elseif (not dropnodedef.walkable or stackdef._dispense_into_walkable) then
211 --[[ _on_dispense(stack, pos, droppos, dropnode, dropdir)
212 * stack: Itemstack which is dispense
213 * pos: Position of dispenser
214 * droppos: Position to which to dispense item
215 * dropnode: Node of droppos
216 * dropdir: Drop direction
218 _dispense_into_walkable: If true, can dispense into walkable nodes
220 if stackdef._on_dispense then
221 -- Item-specific dispension (if defined)
222 local od_ret = stackdef._on_dispense(dropitem, pos, droppos, dropnode, dropdir)
223 if od_ret then
224 local newcount = stack:get_count() - 1
225 stack:set_count(newcount)
226 inv:set_stack("main", stack_id, stack)
227 if newcount == 0 then
228 inv:set_stack("main", stack_id, od_ret)
229 elseif inv:room_for_item("main", od_ret) then
230 inv:add_item("main", od_ret)
231 else
232 minetest.add_item(droppos, dropitem)
234 else
235 stack:take_item()
236 inv:set_stack("main", stack_id, stack)
238 else
239 -- Drop item otherwise
240 minetest.add_item(droppos, dropitem)
241 stack:take_item()
242 inv:set_stack("main", stack_id, stack)
248 end,
249 rules = mesecon.rules.alldirs,
251 on_rotate = on_rotate,
254 -- Horizontal dispenser
256 local horizontal_def = table.copy(dispenserdef)
257 horizontal_def.description = "Dispenser"
258 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."
259 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.
261 The dispenser will do different things, depending on the dispensed item:
263 • Arrows: Are launched
264 • Eggs and snowballs: Are thrown
265 • Fire charges: Are fired in a straight line
266 • Armor: Will be equipped to players and armor stands
267 • Boats: Are placed on water or are dropped
268 • Minecart: Are placed on rails or are dropped
269 • Bone meal: Is applied on the block it is facint
270 • Empty buckets: Are used to collect a liquid source
271 • Filled buckets: Are used to place a liquid source
272 • Heads, pumpkins: Equipped to players and armor stands, or placed as a block
273 • Shulker boxes: Are placed as a block
274 • TNT: Is placed and ignited
275 • Flint and steel: Is used to ignite a fire in air and to ignite TNT
276 • Spawn eggs: Will summon the mob they contain
277 • Other items: Are simply dropped]]
279 horizontal_def.after_place_node = function(pos, placer, itemstack, pointed_thing)
280 setup_dispenser(pos)
281 orientate_dispenser(pos, placer)
283 horizontal_def.tiles = {
284 "default_furnace_top.png", "default_furnace_bottom.png",
285 "default_furnace_side.png", "default_furnace_side.png",
286 "default_furnace_side.png", "mcl_dispensers_dispenser_front_horizontal.png"
288 horizontal_def.paramtype2 = "facedir"
289 horizontal_def.groups = {pickaxey=1, container=2, material_stone=1}
291 minetest.register_node("mcl_dispensers:dispenser", horizontal_def)
293 -- Down dispenser
294 local down_def = table.copy(dispenserdef)
295 down_def.description = "Downwards-Facing Dispenser"
296 down_def.after_place_node = setup_dispenser
297 down_def.tiles = {
298 "default_furnace_top.png", "mcl_dispensers_dispenser_front_vertical.png",
299 "default_furnace_side.png", "default_furnace_side.png",
300 "default_furnace_side.png", "default_furnace_side.png"
302 down_def.groups = {pickaxey=1, container=2,not_in_creative_inventory=1, material_stone=1}
303 down_def._doc_items_create_entry = false
304 down_def.drop = "mcl_dispensers:dispenser"
305 minetest.register_node("mcl_dispensers:dispenser_down", down_def)
307 -- Up dispenser
308 -- The up dispenser is almost identical to the down dispenser , it only differs in textures
309 local up_def = table.copy(down_def)
310 up_def.description = "Upwards-Facing Dispenser"
311 up_def.tiles = {
312 "mcl_dispensers_dispenser_front_vertical.png", "default_furnace_bottom.png",
313 "default_furnace_side.png", "default_furnace_side.png",
314 "default_furnace_side.png", "default_furnace_side.png"
316 minetest.register_node("mcl_dispensers:dispenser_up", up_def)
319 minetest.register_craft({
320 output = 'mcl_dispensers:dispenser',
321 recipe = {
322 {"mcl_core:cobble", "mcl_core:cobble", "mcl_core:cobble",},
323 {"mcl_core:cobble", "mcl_bows:bow", "mcl_core:cobble",},
324 {"mcl_core:cobble", "mesecons:redstone", "mcl_core:cobble",},
328 -- Add entry aliases for the Help
329 if minetest.get_modpath("doc") then
330 doc.add_entry_alias("nodes", "mcl_dispensers:dispenser", "nodes", "mcl_dispensers:dispenser_down")
331 doc.add_entry_alias("nodes", "mcl_dispensers:dispenser", "nodes", "mcl_dispensers:dispenser_up")