1 -- Functions that get the input/output rules of the comparator
3 local comparator_get_output_rules
= function(node
)
4 local rules
= {{x
= -1, y
= 0, z
= 0, spread
=true}}
5 for i
= 0, node
.param2
do
6 rules
= mesecon
.rotate_rules_left(rules
)
12 local comparator_get_input_rules
= function(node
)
14 -- we rely on this order in update_self below
15 {x
= 1, y
= 0, z
= 0}, -- back
16 {x
= 0, y
= 0, z
= -1}, -- side
17 {x
= 0, y
= 0, z
= 1}, -- side
19 for i
= 0, node
.param2
do
20 rules
= mesecon
.rotate_rules_left(rules
)
26 -- Functions that are called after the delay time
28 local comparator_turnon
= function(params
)
29 local rules
= comparator_get_output_rules(params
.node
)
30 mesecon
.receptor_on(params
.pos
, rules
)
34 local comparator_turnoff
= function(params
)
35 local rules
= comparator_get_output_rules(params
.node
)
36 mesecon
.receptor_off(params
.pos
, rules
)
40 -- Functions that set the correct node type an schedule a turnon/off
42 local comparator_activate
= function(pos
, node
)
43 local def
= minetest
.registered_nodes
[node
.name
]
44 minetest
.swap_node(pos
, { name
= def
.comparator_onstate
, param2
= node
.param2
})
45 minetest
.after(0.1, comparator_turnon
, {pos
= pos
, node
= node
})
49 local comparator_deactivate
= function(pos
, node
)
50 local def
= minetest
.registered_nodes
[node
.name
]
51 minetest
.swap_node(pos
, { name
= def
.comparator_offstate
, param2
= node
.param2
})
52 minetest
.after(0.1, comparator_turnoff
, {pos
= pos
, node
= node
})
56 -- wether pos has an inventory that contains at least one item
57 local container_inventory_nonempty
= function(pos
)
58 local invnode
= minetest
.get_node(pos
)
59 local invnodedef
= minetest
.registered_nodes
[invnode
.name
]
61 if not invnodedef
then return false end
63 -- Only accept containers. When a container is dug, it's inventory
64 -- seems to stay. and we don't want to accept the inventory of an air
66 if not invnodedef
.groups
.container
then return false end
68 local inv
= minetest
.get_inventory({type="node", pos
=pos
})
69 if not inv
then return false end
71 for listname
, _
in pairs(inv
:get_lists()) do
72 if not inv
:is_empty(listname
) then return true end
78 -- whether the comparator should be on according to its inputs
79 local comparator_desired_on
= function(pos
, node
)
80 local my_input_rules
= comparator_get_input_rules(node
);
81 local back_rule
= my_input_rules
[1]
84 state
= mesecon
.is_power_on(vector
.add(pos
, back_rule
)) or container_inventory_nonempty(vector
.add(pos
, back_rule
))
87 -- if back input if off, we don't need to check side inputs
88 if not state
then return false end
90 -- without power levels, side inputs have no influence on output in compare
92 local mode
= minetest
.registered_nodes
[node
.name
].comparator_mode
93 if mode
== "comp" then return state
end
95 -- subtract mode, subtract max(side_inputs) from back input
96 local side_state
= false
98 if my_input_rules
[ri
] then
99 side_state
= mesecon
.is_power_on(vector
.add(pos
, my_input_rules
[ri
]))
101 if side_state
then break end
103 -- state is known to be true
104 return not side_state
108 -- update comparator state, if needed
109 local update_self
= function(pos
, node
)
110 node
= node
or minetest
.get_node(pos
)
111 local old_state
= mesecon
.is_receptor_on(node
.name
)
112 local new_state
= comparator_desired_on(pos
, node
)
113 if new_state
~= old_state
then
115 comparator_activate(pos
, node
)
117 comparator_deactivate(pos
, node
)
123 -- compute tile depending on state and mode
124 local get_tiles
= function(state
, mode
)
125 local top
= "mcl_comparators_"..state
..".png^"..
126 "mcl_comparators_"..mode
..".png"
127 local sides
= "mcl_comparators_sides_"..state
..".png^"..
128 "mcl_comparators_sides_"..mode
..".png"
129 local ends
= "mcl_comparators_ends_"..state
..".png^"..
130 "mcl_comparators_ends_"..mode
..".png"
132 top
, "mcl_stairs_stone_slab_top.png",
133 sides
, sides
.."^[transformFX",
138 -- Given one mode, get the other mode
139 local flipmode
= function(mode
)
140 if mode
== "comp" then return "sub"
141 elseif mode
== "sub" then return "comp"
145 local make_rightclick_handler
= function(state
, mode
)
147 "mcl_comparators:comparator_"..state
.."_"..flipmode(mode
)
148 return function (pos
, node
)
149 minetest
.swap_node(pos
, {name
= newnodename
, param2
= node
.param2
})
154 -- Register the 2 (states) x 2 (modes) comparators
156 local icon
= "mcl_comparators_item.png"
160 { -8/16, -8/16, -8/16,
161 8/16, -6/16, 8/16 }, -- the main slab
162 { -1/16, -6/16, 6/16,
163 1/16, -4/16, 4/16 }, -- front torch
164 { -4/16, -6/16, -5/16,
165 -2/16, -1/16, -3/16 }, -- left back torch
166 { 2/16, -6/16, -5/16,
167 4/16, -1/16, -3/16 }, -- right back torch
170 { -8/16, -8/16, -8/16,
171 8/16, -6/16, 8/16 }, -- the main slab
172 { -1/16, -6/16, 6/16,
173 1/16, -3/16, 4/16 }, -- front torch (active)
174 { -4/16, -6/16, -5/16,
175 -2/16, -1/16, -3/16 }, -- left back torch
176 { 2/16, -6/16, -5/16,
177 4/16, -1/16, -3/16 }, -- right back torch
181 local collision_box
= {
183 fixed
= { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
187 [ mesecon
.state
.on
] = "on",
188 [ mesecon
.state
.off
] = "off",
194 destroy_by_lava_flow
= 1,
200 if minetest
.get_modpath("screwdriver") then
201 on_rotate
= screwdriver
.disallow
204 for _
, mode
in pairs
{"comp", "sub"} do
205 for _
, state
in pairs
{mesecon
.state
.on
, mesecon
.state
.off
} do
206 local state_str
= state_strs
[state
]
208 "mcl_comparators:comparator_"..state_strs
[state
].."_"..mode
211 local longdesc
, usagehelp
, use_help
212 if state_strs
[state
] == "off" and mode
== "comp" then
213 longdesc
= "Redstone comparators are multi-purpose redstone components. "..
214 "They can transmit a redstone signal, detect whether a block contains any items and compare multiple signals."
216 usagehelp
= "A redstone comparator has 1 main input, 2 side inputs and 1 output. The output is in "..
217 "arrow direction, the main input is in the opposite direction. The other 2 sides are the side inputs.".."\n"..
218 "The main input can powered in 2 ways: First, it can be powered directly by redstone power like any other component. Second, it is powered if, and only if a container (like chest) is placed in front of it and the container contains at least one item."..
219 "The side inputs are only powered by normal redstone power."..
220 "The redstone can operate in two modes: Transmission mode and subtraction mode. It "..
221 "starts in transmission mode and the mode can be changed by a rightclick.".."\n\n"..
222 "Transmission mode:"..
223 "The front torch is unlit and lowered. The output is powered if, and only if the main input is powered. The two side inputs are ignored.".."\n"..
224 "Subtraction mode:"..
225 "The front torch is lit. The output is powered if, and only if the main input is powered and none of the side inputs is powered."
231 description
= "Redstone Comparator",
232 inventory_image
= icon
,
234 _doc_items_create_entry
= use_help
,
235 _doc_items_longdesc
= longdesc
,
236 _doc_items_usagehelp
= usagehelp
,
237 drawtype
= "nodebox",
238 tiles
= get_tiles(state_strs
[state
], mode
),
239 wield_image
= "mcl_comparators_off.png",
241 selection_box
= collision_box
,
242 collision_box
= collision_box
,
245 fixed
= node_boxes
[mode
],
249 paramtype2
= "facedir",
250 sunlight_propagates
= false,
251 is_ground_content
= false,
252 drop
= 'mcl_comparators:comparator_off_comp',
253 on_construct
= update_self
,
255 make_rightclick_handler(state_strs
[state
], mode
),
256 comparator_mode
= mode
,
257 comparator_onstate
= "mcl_comparators:comparator_on_"..mode
,
258 comparator_offstate
= "mcl_comparators:comparator_off_"..mode
,
259 sounds
= mcl_sounds
.node_sound_stone_defaults(),
263 rules
= comparator_get_output_rules
,
266 rules
= comparator_get_input_rules
,
267 action_change
= update_self
,
270 on_rotate
= on_rotate
,
273 if mode
== "comp" and state
== mesecon
.state
.off
then
274 -- This is the prototype
275 nodedef
._doc_items_create_entry
= true
277 nodedef
.groups
= table.copy(nodedef
.groups
)
278 nodedef
.groups
.not_in_creative_inventory
= 1
279 local extra_desc
= {}
280 if mode
== "sub" then
281 table.insert(extra_desc
, "Subtract")
283 if state
== mesecon
.state
.on
then
284 table.insert(extra_desc
, "Powered")
286 nodedef
.description
= nodedef
.description
..
287 " ("..table.concat(extra_desc
, ", ")..")"
290 minetest
.register_node(nodename
, nodedef
)
295 local rstorch
= "mesecons_torch:mesecon_torch_on"
296 local quartz
= "mcl_nether:quartz"
297 local stone
= "mcl_core:stone"
299 minetest
.register_craft({
300 output
= "mcl_comparators:comparator_off_comp",
303 { rstorch
, quartz
, rstorch
},
304 { stone
, stone
, stone
},
308 -- Register active block handlers
309 minetest
.register_abm({
310 label
= "Comparator check for containers",
312 "mcl_comparators:comparator_off_comp",
313 "mcl_comparators:comparator_off_sub",
315 neighbors
= {"group:container"},
318 action
= update_self
,
321 minetest
.register_abm({
322 label
= "Comparator check for no containers",
324 "mcl_comparators:comparator_on_comp",
325 "mcl_comparators:comparator_on_sub",
327 -- needs to run regardless of neighbors to make sure we detect when a
331 action
= update_self
,
335 -- Add entry aliases for the Help
336 if minetest
.get_modpath("doc") then
337 doc
.add_entry_alias("nodes", "mcl_comparators:comparator_off_comp",
338 "nodes", "mcl_comparators:comparator_off_sub")
339 doc
.add_entry_alias("nodes", "mcl_comparators:comparator_off_comp",
340 "nodes", "mcl_comparators:comparator_on_comp")
341 doc
.add_entry_alias("nodes", "mcl_comparators:comparator_off_comp",
342 "nodes", "mcl_comparators:comparator_on_sub")