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 -- weather 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 -- weather pos has an constant signal output for the comparator
79 local static_signal_output
= function(pos
)
80 local node
= minetest
.get_node(pos
)
81 local g
= minetest
.get_item_group(node
.name
, "comparator_signal")
85 -- whether the comparator should be on according to its inputs
86 local comparator_desired_on
= function(pos
, node
)
87 local my_input_rules
= comparator_get_input_rules(node
);
88 local back_rule
= my_input_rules
[1]
91 local back_pos
= vector
.add(pos
, back_rule
)
92 state
= mesecon
.is_power_on(back_pos
) or container_inventory_nonempty(back_pos
) or static_signal_output(back_pos
)
95 -- if back input if off, we don't need to check side inputs
96 if not state
then return false end
98 -- without power levels, side inputs have no influence on output in compare
100 local mode
= minetest
.registered_nodes
[node
.name
].comparator_mode
101 if mode
== "comp" then return state
end
103 -- subtract mode, subtract max(side_inputs) from back input
104 local side_state
= false
106 if my_input_rules
[ri
] then
107 side_state
= mesecon
.is_power_on(vector
.add(pos
, my_input_rules
[ri
]))
109 if side_state
then break end
111 -- state is known to be true
112 return not side_state
116 -- update comparator state, if needed
117 local update_self
= function(pos
, node
)
118 node
= node
or minetest
.get_node(pos
)
119 local old_state
= mesecon
.is_receptor_on(node
.name
)
120 local new_state
= comparator_desired_on(pos
, node
)
121 if new_state
~= old_state
then
123 comparator_activate(pos
, node
)
125 comparator_deactivate(pos
, node
)
131 -- compute tile depending on state and mode
132 local get_tiles
= function(state
, mode
)
133 local top
= "mcl_comparators_"..state
..".png^"..
134 "mcl_comparators_"..mode
..".png"
135 local sides
= "mcl_comparators_sides_"..state
..".png^"..
136 "mcl_comparators_sides_"..mode
..".png"
137 local ends
= "mcl_comparators_ends_"..state
..".png^"..
138 "mcl_comparators_ends_"..mode
..".png"
140 top
, "mcl_stairs_stone_slab_top.png",
141 sides
, sides
.."^[transformFX",
146 -- Given one mode, get the other mode
147 local flipmode
= function(mode
)
148 if mode
== "comp" then return "sub"
149 elseif mode
== "sub" then return "comp"
153 local make_rightclick_handler
= function(state
, mode
)
155 "mcl_comparators:comparator_"..state
.."_"..flipmode(mode
)
156 return function (pos
, node
)
157 minetest
.swap_node(pos
, {name
= newnodename
, param2
= node
.param2
})
162 -- Register the 2 (states) x 2 (modes) comparators
164 local icon
= "mcl_comparators_item.png"
168 { -8/16, -8/16, -8/16,
169 8/16, -6/16, 8/16 }, -- the main slab
170 { -1/16, -6/16, 6/16,
171 1/16, -4/16, 4/16 }, -- front torch
172 { -4/16, -6/16, -5/16,
173 -2/16, -1/16, -3/16 }, -- left back torch
174 { 2/16, -6/16, -5/16,
175 4/16, -1/16, -3/16 }, -- right back torch
178 { -8/16, -8/16, -8/16,
179 8/16, -6/16, 8/16 }, -- the main slab
180 { -1/16, -6/16, 6/16,
181 1/16, -3/16, 4/16 }, -- front torch (active)
182 { -4/16, -6/16, -5/16,
183 -2/16, -1/16, -3/16 }, -- left back torch
184 { 2/16, -6/16, -5/16,
185 4/16, -1/16, -3/16 }, -- right back torch
189 local collision_box
= {
191 fixed
= { -8/16, -8/16, -8/16, 8/16, -6/16, 8/16 },
195 [ mesecon
.state
.on
] = "on",
196 [ mesecon
.state
.off
] = "off",
202 destroy_by_lava_flow
= 1,
208 if minetest
.get_modpath("screwdriver") then
209 on_rotate
= screwdriver
.disallow
212 for _
, mode
in pairs
{"comp", "sub"} do
213 for _
, state
in pairs
{mesecon
.state
.on
, mesecon
.state
.off
} do
214 local state_str
= state_strs
[state
]
216 "mcl_comparators:comparator_"..state_strs
[state
].."_"..mode
219 local longdesc
, usagehelp
, use_help
220 if state_strs
[state
] == "off" and mode
== "comp" then
221 longdesc
= "Redstone comparators are multi-purpose redstone components. "..
222 "They can transmit a redstone signal, detect whether a block contains any items and compare multiple signals."
224 usagehelp
= "A redstone comparator has 1 main input, 2 side inputs and 1 output. The output is in "..
225 "arrow direction, the main input is in the opposite direction. The other 2 sides are the side inputs.".."\n"..
226 "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."..
227 "The side inputs are only powered by normal redstone power."..
228 "The redstone can operate in two modes: Transmission mode and subtraction mode. It "..
229 "starts in transmission mode and the mode can be changed by a rightclick.".."\n\n"..
230 "Transmission mode:"..
231 "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"..
232 "Subtraction mode:"..
233 "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."
239 description
= "Redstone Comparator",
240 inventory_image
= icon
,
242 _doc_items_create_entry
= use_help
,
243 _doc_items_longdesc
= longdesc
,
244 _doc_items_usagehelp
= usagehelp
,
245 drawtype
= "nodebox",
246 tiles
= get_tiles(state_strs
[state
], mode
),
247 wield_image
= "mcl_comparators_off.png",
249 selection_box
= collision_box
,
250 collision_box
= collision_box
,
253 fixed
= node_boxes
[mode
],
257 paramtype2
= "facedir",
258 sunlight_propagates
= false,
259 is_ground_content
= false,
260 drop
= 'mcl_comparators:comparator_off_comp',
261 on_construct
= update_self
,
263 make_rightclick_handler(state_strs
[state
], mode
),
264 comparator_mode
= mode
,
265 comparator_onstate
= "mcl_comparators:comparator_on_"..mode
,
266 comparator_offstate
= "mcl_comparators:comparator_off_"..mode
,
267 sounds
= mcl_sounds
.node_sound_stone_defaults(),
271 rules
= comparator_get_output_rules
,
274 rules
= comparator_get_input_rules
,
275 action_change
= update_self
,
278 on_rotate
= on_rotate
,
281 if mode
== "comp" and state
== mesecon
.state
.off
then
282 -- This is the prototype
283 nodedef
._doc_items_create_entry
= true
285 nodedef
.groups
= table.copy(nodedef
.groups
)
286 nodedef
.groups
.not_in_creative_inventory
= 1
287 local extra_desc
= {}
288 if mode
== "sub" then
289 table.insert(extra_desc
, "Subtract")
291 if state
== mesecon
.state
.on
then
292 table.insert(extra_desc
, "Powered")
294 nodedef
.description
= nodedef
.description
..
295 " ("..table.concat(extra_desc
, ", ")..")"
298 minetest
.register_node(nodename
, nodedef
)
303 local rstorch
= "mesecons_torch:mesecon_torch_on"
304 local quartz
= "mcl_nether:quartz"
305 local stone
= "mcl_core:stone"
307 minetest
.register_craft({
308 output
= "mcl_comparators:comparator_off_comp",
311 { rstorch
, quartz
, rstorch
},
312 { stone
, stone
, stone
},
316 -- Register active block handlers
317 minetest
.register_abm({
318 label
= "Comparator signal input check (comparator is off)",
320 "mcl_comparators:comparator_off_comp",
321 "mcl_comparators:comparator_off_sub",
323 neighbors
= {"group:container", "group:comparator_signal"},
326 action
= update_self
,
329 minetest
.register_abm({
330 label
= "Comparator signal input check (comparator is on)",
332 "mcl_comparators:comparator_on_comp",
333 "mcl_comparators:comparator_on_sub",
335 -- needs to run regardless of neighbors to make sure we detect when a
339 action
= update_self
,
343 -- Add entry aliases for the Help
344 if minetest
.get_modpath("doc") then
345 doc
.add_entry_alias("nodes", "mcl_comparators:comparator_off_comp",
346 "nodes", "mcl_comparators:comparator_off_sub")
347 doc
.add_entry_alias("nodes", "mcl_comparators:comparator_off_comp",
348 "nodes", "mcl_comparators:comparator_on_comp")
349 doc
.add_entry_alias("nodes", "mcl_comparators:comparator_off_comp",
350 "nodes", "mcl_comparators:comparator_on_sub")