2019-03-08 03:07:41 +03:00
local S = minetest.get_translator ( " mcl_comparators " )
2017-08-28 02:51:21 +03:00
-- Functions that get the input/output rules of the comparator
local comparator_get_output_rules = function ( node )
2018-01-13 05:37:41 +03:00
local rules = { { x = - 1 , y = 0 , z = 0 , spread = true } }
2017-08-28 02:51:21 +03:00
for i = 0 , node.param2 do
2017-09-19 00:34:08 +03:00
rules = mesecon.rotate_rules_left ( rules )
2017-08-28 02:51:21 +03:00
end
return rules
end
local comparator_get_input_rules = function ( node )
local rules = {
-- we rely on this order in update_self below
{ x = 1 , y = 0 , z = 0 } , -- back
{ x = 0 , y = 0 , z = - 1 } , -- side
{ x = 0 , y = 0 , z = 1 } , -- side
}
for i = 0 , node.param2 do
2017-09-19 00:34:08 +03:00
rules = mesecon.rotate_rules_left ( rules )
2017-08-28 02:51:21 +03:00
end
return rules
end
-- Functions that are called after the delay time
local comparator_turnon = function ( params )
local rules = comparator_get_output_rules ( params.node )
2017-09-19 00:34:08 +03:00
mesecon.receptor_on ( params.pos , rules )
2017-08-28 02:51:21 +03:00
end
local comparator_turnoff = function ( params )
local rules = comparator_get_output_rules ( params.node )
2017-09-19 00:34:08 +03:00
mesecon.receptor_off ( params.pos , rules )
2017-08-28 02:51:21 +03:00
end
-- Functions that set the correct node type an schedule a turnon/off
local comparator_activate = function ( pos , node )
local def = minetest.registered_nodes [ node.name ]
2017-09-19 00:34:08 +03:00
minetest.swap_node ( pos , { name = def.comparator_onstate , param2 = node.param2 } )
2017-08-28 02:51:21 +03:00
minetest.after ( 0.1 , comparator_turnon , { pos = pos , node = node } )
end
local comparator_deactivate = function ( pos , node )
local def = minetest.registered_nodes [ node.name ]
2017-09-19 00:34:08 +03:00
minetest.swap_node ( pos , { name = def.comparator_offstate , param2 = node.param2 } )
2017-08-28 02:51:21 +03:00
minetest.after ( 0.1 , comparator_turnoff , { pos = pos , node = node } )
end
2018-05-13 01:23:34 +03:00
-- weather pos has an inventory that contains at least one item
2017-08-28 02:51:21 +03:00
local container_inventory_nonempty = function ( pos )
local invnode = minetest.get_node ( pos )
local invnodedef = minetest.registered_nodes [ invnode.name ]
-- Ignore stale nodes
if not invnodedef then return false end
-- Only accept containers. When a container is dug, it's inventory
-- seems to stay. and we don't want to accept the inventory of an air
-- block
if not invnodedef.groups . container then return false end
local inv = minetest.get_inventory ( { type = " node " , pos = pos } )
if not inv then return false end
for listname , _ in pairs ( inv : get_lists ( ) ) do
if not inv : is_empty ( listname ) then return true end
end
return false
end
2018-05-13 01:23:34 +03:00
-- weather pos has an constant signal output for the comparator
local static_signal_output = function ( pos )
local node = minetest.get_node ( pos )
local g = minetest.get_item_group ( node.name , " comparator_signal " )
return g > 0
end
2017-08-28 02:51:21 +03:00
-- whether the comparator should be on according to its inputs
local comparator_desired_on = function ( pos , node )
local my_input_rules = comparator_get_input_rules ( node ) ;
local back_rule = my_input_rules [ 1 ]
2017-09-19 00:34:08 +03:00
local state
if back_rule then
2018-05-13 01:23:34 +03:00
local back_pos = vector.add ( pos , back_rule )
state = mesecon.is_power_on ( back_pos ) or container_inventory_nonempty ( back_pos ) or static_signal_output ( back_pos )
2017-09-19 00:34:08 +03:00
end
2017-08-28 02:51:21 +03:00
-- if back input if off, we don't need to check side inputs
if not state then return false end
-- without power levels, side inputs have no influence on output in compare
-- mode
local mode = minetest.registered_nodes [ node.name ] . comparator_mode
if mode == " comp " then return state end
-- subtract mode, subtract max(side_inputs) from back input
local side_state = false
for ri = 2 , 3 do
2017-09-19 00:34:08 +03:00
if my_input_rules [ ri ] then
side_state = mesecon.is_power_on ( vector.add ( pos , my_input_rules [ ri ] ) )
end
2017-08-28 02:51:21 +03:00
if side_state then break end
end
-- state is known to be true
return not side_state
end
-- update comparator state, if needed
local update_self = function ( pos , node )
node = node or minetest.get_node ( pos )
2017-09-19 00:34:08 +03:00
local old_state = mesecon.is_receptor_on ( node.name )
2017-08-28 02:51:21 +03:00
local new_state = comparator_desired_on ( pos , node )
if new_state ~= old_state then
if new_state then
comparator_activate ( pos , node )
else
comparator_deactivate ( pos , node )
end
end
end
-- compute tile depending on state and mode
local get_tiles = function ( state , mode )
2017-08-28 02:58:07 +03:00
local top = " mcl_comparators_ " .. state .. " .png^ " ..
" mcl_comparators_ " .. mode .. " .png "
local sides = " mcl_comparators_sides_ " .. state .. " .png^ " ..
" mcl_comparators_sides_ " .. mode .. " .png "
local ends = " mcl_comparators_ends_ " .. state .. " .png^ " ..
" mcl_comparators_ends_ " .. mode .. " .png "
2017-08-28 02:51:21 +03:00
return {
top , " mcl_stairs_stone_slab_top.png " ,
sides , sides .. " ^[transformFX " ,
ends , ends ,
}
end
-- Given one mode, get the other mode
local flipmode = function ( mode )
if mode == " comp " then return " sub "
elseif mode == " sub " then return " comp "
end
end
local make_rightclick_handler = function ( state , mode )
local newnodename =
2017-08-28 02:58:07 +03:00
" mcl_comparators:comparator_ " .. state .. " _ " .. flipmode ( mode )
2019-02-08 23:59:01 +03:00
return function ( pos , node , clicker )
local protname = clicker : get_player_name ( )
if minetest.is_protected ( pos , protname ) then
minetest.record_protection_violation ( pos , protname )
return
end
2017-09-19 00:34:08 +03:00
minetest.swap_node ( pos , { name = newnodename , param2 = node.param2 } )
2017-08-28 02:51:21 +03:00
end
end
-- Register the 2 (states) x 2 (modes) comparators
2017-08-28 02:58:07 +03:00
local icon = " mcl_comparators_item.png "
2017-08-28 02:51:21 +03:00
local node_boxes = {
comp = {
{ - 8 / 16 , - 8 / 16 , - 8 / 16 ,
8 / 16 , - 6 / 16 , 8 / 16 } , -- the main slab
{ - 1 / 16 , - 6 / 16 , 6 / 16 ,
1 / 16 , - 4 / 16 , 4 / 16 } , -- front torch
{ - 4 / 16 , - 6 / 16 , - 5 / 16 ,
- 2 / 16 , - 1 / 16 , - 3 / 16 } , -- left back torch
{ 2 / 16 , - 6 / 16 , - 5 / 16 ,
4 / 16 , - 1 / 16 , - 3 / 16 } , -- right back torch
} ,
sub = {
{ - 8 / 16 , - 8 / 16 , - 8 / 16 ,
8 / 16 , - 6 / 16 , 8 / 16 } , -- the main slab
{ - 1 / 16 , - 6 / 16 , 6 / 16 ,
1 / 16 , - 3 / 16 , 4 / 16 } , -- front torch (active)
{ - 4 / 16 , - 6 / 16 , - 5 / 16 ,
- 2 / 16 , - 1 / 16 , - 3 / 16 } , -- left back torch
{ 2 / 16 , - 6 / 16 , - 5 / 16 ,
4 / 16 , - 1 / 16 , - 3 / 16 } , -- right back torch
} ,
}
local collision_box = {
type = " fixed " ,
fixed = { - 8 / 16 , - 8 / 16 , - 8 / 16 , 8 / 16 , - 6 / 16 , 8 / 16 } ,
}
local state_strs = {
[ mesecon.state . on ] = " on " ,
[ mesecon.state . off ] = " off " ,
}
local groups = {
dig_immediate = 3 ,
dig_by_water = 1 ,
destroy_by_lava_flow = 1 ,
dig_by_piston = 1 ,
attached_node = 1 ,
}
2017-12-05 16:09:39 +03:00
local on_rotate
if minetest.get_modpath ( " screwdriver " ) then
on_rotate = screwdriver.disallow
end
2017-08-28 02:51:21 +03:00
for _ , mode in pairs { " comp " , " sub " } do
for _ , state in pairs { mesecon.state . on , mesecon.state . off } do
local state_str = state_strs [ state ]
local nodename =
2017-08-28 02:58:07 +03:00
" mcl_comparators:comparator_ " .. state_strs [ state ] .. " _ " .. mode
2017-08-28 02:51:21 +03:00
2017-08-28 03:05:31 +03:00
-- Help
local longdesc , usagehelp , use_help
if state_strs [ state ] == " off " and mode == " comp " then
2019-03-08 03:07:41 +03:00
longdesc = S ( " Redstone comparators are multi-purpose redstone components. " ) .. " \n " ..
S ( " They can transmit a redstone signal, detect whether a block contains any items and compare multiple signals. " )
usagehelp = S ( " A redstone comparator has 1 main input, 2 side inputs and 1 output. The output is in arrow direction, the main input is in the opposite direction. The other 2 sides are the side inputs. " ) .. " \n " ..
S ( " 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 a chest) is placed in front of it and the container contains at least one item. " ) .. " \n " ..
2019-03-16 04:00:48 +03:00
S ( " The side inputs are only powered by normal redstone power. The redstone comparator can operate in two modes: Transmission mode and subtraction mode. It starts in transmission mode and the mode can be changed by using the block. " ) .. " \n \n " ..
2019-03-08 03:07:41 +03:00
S ( " Transmission mode: \n 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 " ..
S ( " Subtraction mode: \n 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. " )
2017-08-28 03:05:31 +03:00
else
use_help = false
end
2017-08-28 02:51:21 +03:00
local nodedef = {
2019-03-08 03:07:41 +03:00
description = S ( " Redstone Comparator " ) ,
2017-08-28 02:51:21 +03:00
inventory_image = icon ,
wield_image = icon ,
2017-08-28 03:05:31 +03:00
_doc_items_create_entry = use_help ,
2017-08-28 02:51:21 +03:00
_doc_items_longdesc = longdesc ,
_doc_items_usagehelp = usagehelp ,
drawtype = " nodebox " ,
tiles = get_tiles ( state_strs [ state ] , mode ) ,
2021-02-18 16:00:17 +03:00
use_texture_alpha = minetest.features . use_texture_alpha_string_modes and " opaque " or false ,
2017-08-28 02:58:07 +03:00
wield_image = " mcl_comparators_off.png " ,
2017-08-28 02:51:21 +03:00
walkable = true ,
selection_box = collision_box ,
collision_box = collision_box ,
node_box = {
type = " fixed " ,
fixed = node_boxes [ mode ] ,
} ,
groups = groups ,
paramtype = " light " ,
paramtype2 = " facedir " ,
sunlight_propagates = false ,
is_ground_content = false ,
2017-08-28 02:58:07 +03:00
drop = ' mcl_comparators:comparator_off_comp ' ,
2017-08-28 02:51:21 +03:00
on_construct = update_self ,
on_rightclick =
make_rightclick_handler ( state_strs [ state ] , mode ) ,
comparator_mode = mode ,
2017-08-28 02:58:07 +03:00
comparator_onstate = " mcl_comparators:comparator_on_ " .. mode ,
comparator_offstate = " mcl_comparators:comparator_off_ " .. mode ,
2017-08-28 02:51:21 +03:00
sounds = mcl_sounds.node_sound_stone_defaults ( ) ,
mesecons = {
receptor = {
state = state ,
rules = comparator_get_output_rules ,
} ,
effector = {
rules = comparator_get_input_rules ,
action_change = update_self ,
}
2017-12-05 16:09:39 +03:00
} ,
on_rotate = on_rotate ,
2017-08-28 02:51:21 +03:00
}
if mode == " comp " and state == mesecon.state . off then
-- This is the prototype
nodedef._doc_items_create_entry = true
else
nodedef.groups = table.copy ( nodedef.groups )
nodedef.groups . not_in_creative_inventory = 1
local extra_desc = { }
2019-12-10 00:02:35 +03:00
if mode == " sub " or state == mesecon.state . on then
2018-10-24 19:22:48 +03:00
nodedef.inventory_image = nil
2017-08-28 02:51:21 +03:00
end
2019-12-10 00:02:35 +03:00
local desc = nodedef.description
if mode ~= " sub " and state == mesecon.state . on then
desc = S ( " Redstone Comparator (Powered) " )
elseif mode == " sub " and state ~= mesecon.state . on then
desc = S ( " Redstone Comparator (Subtract) " )
elseif mode == " sub " and state == mesecon.state . on then
desc = S ( " Redstone Comparator (Subtract, Powered) " )
2017-08-28 02:51:21 +03:00
end
2019-12-10 00:02:35 +03:00
nodedef.description = desc
2017-08-28 02:51:21 +03:00
end
minetest.register_node ( nodename , nodedef )
end
end
-- Register recipies
local rstorch = " mesecons_torch:mesecon_torch_on "
local quartz = " mcl_nether:quartz "
local stone = " mcl_core:stone "
minetest.register_craft ( {
2017-08-28 02:58:07 +03:00
output = " mcl_comparators:comparator_off_comp " ,
2017-08-28 02:51:21 +03:00
recipe = {
{ " " , rstorch , " " } ,
{ rstorch , quartz , rstorch } ,
{ stone , stone , stone } ,
}
} )
-- Register active block handlers
minetest.register_abm ( {
2018-05-13 01:23:34 +03:00
label = " Comparator signal input check (comparator is off) " ,
2017-08-28 02:51:21 +03:00
nodenames = {
2017-08-28 02:58:07 +03:00
" mcl_comparators:comparator_off_comp " ,
" mcl_comparators:comparator_off_sub " ,
2017-08-28 02:51:21 +03:00
} ,
2018-05-13 01:23:34 +03:00
neighbors = { " group:container " , " group:comparator_signal " } ,
2017-08-28 02:51:21 +03:00
interval = 1 ,
chance = 1 ,
action = update_self ,
} )
minetest.register_abm ( {
2018-05-13 01:23:34 +03:00
label = " Comparator signal input check (comparator is on) " ,
2017-08-28 02:51:21 +03:00
nodenames = {
2017-08-28 02:58:07 +03:00
" mcl_comparators:comparator_on_comp " ,
" mcl_comparators:comparator_on_sub " ,
2017-08-28 02:51:21 +03:00
} ,
-- needs to run regardless of neighbors to make sure we detect when a
-- container is dug
interval = 1 ,
chance = 1 ,
action = update_self ,
} )
-- Add entry aliases for the Help
if minetest.get_modpath ( " doc " ) then
2017-08-28 02:58:07 +03:00
doc.add_entry_alias ( " nodes " , " mcl_comparators:comparator_off_comp " ,
" nodes " , " mcl_comparators:comparator_off_sub " )
doc.add_entry_alias ( " nodes " , " mcl_comparators:comparator_off_comp " ,
" nodes " , " mcl_comparators:comparator_on_comp " )
doc.add_entry_alias ( " nodes " , " mcl_comparators:comparator_off_comp " ,
" nodes " , " mcl_comparators:comparator_on_sub " )
2017-08-28 02:51:21 +03:00
end