2021-05-29 16:12:33 +02:00
local S = minetest.get_translator ( minetest.get_current_modname ( ) )
2024-04-15 00:35:32 +02:00
mcl_comparators = { }
local mod = mcl_comparators
2019-03-08 01:07:41 +01:00
2017-08-28 01:51:21 +02:00
-- Functions that get the input/output rules of the comparator
2021-05-29 16:12:33 +02:00
local function comparator_get_output_rules ( node )
2018-01-13 03:37:41 +01:00
local rules = { { x = - 1 , y = 0 , z = 0 , spread = true } }
2017-08-28 01:51:21 +02:00
for i = 0 , node.param2 do
2017-09-18 23:34:08 +02:00
rules = mesecon.rotate_rules_left ( rules )
2017-08-28 01:51:21 +02:00
end
return rules
end
2021-05-29 16:12:33 +02:00
local function comparator_get_input_rules ( node )
2017-08-28 01:51:21 +02:00
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-18 23:34:08 +02:00
rules = mesecon.rotate_rules_left ( rules )
2017-08-28 01:51:21 +02:00
end
return rules
end
2024-04-15 00:35:32 +02:00
local POSSIBLE_COMPARATOR_POSITIONS = {
vector.new ( 1 , 0 , 0 ) ,
vector.new ( - 1 , 0 , 0 ) ,
vector.new ( 0 , 0 , 1 ) ,
vector.new ( 0 , 0 , - 1 ) ,
}
-- update comparator state, if needed
local function update_self ( pos , node )
2024-04-15 13:44:36 +02:00
print ( " Updating comparator at " .. vector.to_string ( pos ) )
2024-04-15 00:35:32 +02:00
node = node or minetest.get_node ( pos )
2024-04-15 09:11:38 +02:00
local nodedef = minetest.registered_nodes [ node.name ]
2017-08-28 01:51:21 +02:00
2024-04-15 00:35:32 +02:00
-- Find the node we are pointing at
local input_rules = comparator_get_input_rules ( node ) ;
local back_rule = input_rules [ 1 ]
local back_pos = vector.add ( pos , back_rule )
local back_node = minetest.get_node ( back_pos )
local back_nodedef = minetest.registered_nodes [ back_node.name ]
2017-08-28 01:51:21 +02:00
2024-04-15 00:35:32 +02:00
-- Get the comparator mode
2024-04-15 09:11:38 +02:00
local mode = nodedef.comparator_mode
2017-08-28 01:51:21 +02:00
2024-04-15 00:35:32 +02:00
-- Get a comparator reading from the block at the back of the comparator
local power_level = 0
if back_nodedef and back_nodedef._mcl_comparators_get_reading then
power_level = back_nodedef._mcl_comparators_get_reading ( back_pos )
2024-04-15 13:44:36 +02:00
else
power_level = vl_redstone.get_power ( back_pos )
2022-02-21 16:51:52 +01:00
end
2017-08-28 01:51:21 +02:00
2024-04-15 00:35:32 +02:00
-- Get the maximum side power level
local side_power_level = 0
for i = 2 , 3 do
2024-04-15 13:44:36 +02:00
local pl = vl_redstone.get_power ( vector.add ( pos , input_rules [ i ] ) )
2024-04-15 00:35:32 +02:00
if pl > side_power_level then
side_power_level = pl
end
2022-02-21 16:51:52 +01:00
end
2017-08-28 01:51:21 +02:00
2024-04-15 00:35:32 +02:00
-- Apply subtraction or comparison
if mode == " sub " then
power_level = power_level - side_power_level
if power_level < 0 then power_level = 0 end
elseif mode == " comp " then
if side_power_level > power_level then
power_level = 0
end
2017-08-28 01:51:21 +02:00
end
2024-04-15 09:11:38 +02:00
-- Update output power level
2024-04-15 00:35:32 +02:00
vl_redstone.set_power ( pos , power_level )
2024-04-15 09:11:38 +02:00
-- Update node
if power_level ~= 0 then
node.name = nodedef.comparator_onstate
minetest.swap_node ( pos , node )
else
node.name = nodedef.comparator_offstate
minetest.swap_node ( pos , node )
end
2018-05-13 00:23:34 +02:00
end
2024-04-28 15:04:07 +02:00
local node_readings = { }
function mod . trigger_update ( pos , node )
local node = node or minetest.get_node ( pos )
local nodedef = minetest.registered_nodes [ node.name ] or { }
-- If this position can't provide a comparator reading, we should
-- never trigger a comparator update
local get_reading = nodedef._mcl_comparators_get_reading
if not get_reading then return end
-- Only update if the reading has changed
local pos_hash = minetest.hash_node_position ( pos )
local old_reading = node_readings [ pos_hash ]
local new_reading = get_reading ( pos )
if old_reading == new_reading then return end
node_readings [ pos_hash ] = new_reading
2024-04-15 00:35:32 +02:00
-- try to find a comparator with the back rule leading to pos
for i = 1 , # POSSIBLE_COMPARATOR_POSITIONS do
local candidate = vector.add ( pos , POSSIBLE_COMPARATOR_POSITIONS [ i ] )
local node = minetest.get_node ( candidate )
if minetest.get_item_group ( node.name , " mcl_comparator " ) ~= 0 then
update_self ( candidate , node )
2017-09-18 23:34:08 +02:00
end
2017-08-28 01:51:21 +02:00
end
end
2024-04-15 00:35:32 +02:00
function mod . read_inventory ( inv , inv_name )
local stacks = inv : get_list ( inv_name )
2024-04-22 00:50:40 +02:00
if not stacks then return 0 end
2024-04-15 00:35:32 +02:00
local count = 0
for i = 1 , # stacks do
count = count + ( stacks [ i ] : get_count ( ) / stacks [ i ] : get_stack_max ( ) )
2017-08-28 01:51:21 +02:00
end
2024-04-28 15:04:07 +02:00
return math.floor ( count * 16 / # stacks )
2017-08-28 01:51:21 +02:00
end
-- compute tile depending on state and mode
2021-05-29 16:12:33 +02:00
local function get_tiles ( state , mode )
2017-08-28 01:58:07 +02: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 01:51:21 +02:00
return {
top , " mcl_stairs_stone_slab_top.png " ,
sides , sides .. " ^[transformFX " ,
ends , ends ,
}
end
-- Given one mode, get the other mode
2021-05-29 16:12:33 +02:00
local function flipmode ( mode )
2017-08-28 01:51:21 +02:00
if mode == " comp " then return " sub "
elseif mode == " sub " then return " comp "
end
end
2021-05-29 16:12:33 +02:00
local function make_rightclick_handler ( state , mode )
2017-08-28 01:51:21 +02:00
local newnodename =
2017-08-28 01:58:07 +02:00
" mcl_comparators:comparator_ " .. state .. " _ " .. flipmode ( mode )
2019-02-08 21:59:01 +01: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-18 23:34:08 +02:00
minetest.swap_node ( pos , { name = newnodename , param2 = node.param2 } )
2017-08-28 01:51:21 +02:00
end
end
-- Register the 2 (states) x 2 (modes) comparators
2017-08-28 01:58:07 +02:00
local icon = " mcl_comparators_item.png "
2017-08-28 01:51:21 +02: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 ,
2024-04-15 00:35:32 +02:00
mcl_comparator = 1 ,
2017-08-28 01:51:21 +02:00
}
2017-12-05 14:09:39 +01:00
local on_rotate
if minetest.get_modpath ( " screwdriver " ) then
on_rotate = screwdriver.disallow
end
2017-08-28 01:51:21 +02:00
for _ , mode in pairs { " comp " , " sub " } do
2021-05-23 11:53:05 +02:00
for _ , state in pairs { mesecon.state . on , mesecon.state . off } do
local state_str = state_strs [ state ]
local nodename =
" mcl_comparators:comparator_ " .. state_str .. " _ " .. mode
-- Help
local longdesc , usagehelp , use_help
if state_str == " off " and mode == " comp " then
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 " ..
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 " ..
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. " )
else
use_help = false
end
2017-08-28 02:05:31 +02:00
2021-05-23 11:53:05 +02:00
local nodedef = {
description = S ( " Redstone Comparator " ) ,
inventory_image = icon ,
wield_image = icon ,
_doc_items_create_entry = use_help ,
_doc_items_longdesc = longdesc ,
_doc_items_usagehelp = usagehelp ,
drawtype = " nodebox " ,
tiles = get_tiles ( state_str , mode ) ,
use_texture_alpha = minetest.features . use_texture_alpha_string_modes and " opaque " or false ,
--wield_image = "mcl_comparators_off.png",
walkable = true ,
selection_box = collision_box ,
collision_box = collision_box ,
node_box = {
type = " fixed " ,
fixed = node_boxes [ mode ] ,
2017-08-28 01:51:21 +02:00
} ,
2021-05-23 11:53:05 +02:00
groups = groups ,
paramtype = " light " ,
paramtype2 = " facedir " ,
sunlight_propagates = false ,
is_ground_content = false ,
2021-05-29 16:12:33 +02:00
drop = " mcl_comparators:comparator_off_comp " ,
2021-05-23 11:53:05 +02:00
on_construct = update_self ,
on_rightclick =
make_rightclick_handler ( state_str , mode ) ,
comparator_mode = mode ,
comparator_onstate = " mcl_comparators:comparator_on_ " .. mode ,
comparator_offstate = " mcl_comparators:comparator_off_ " .. mode ,
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 ,
}
} ,
on_rotate = on_rotate ,
}
2017-08-28 01:51:21 +02:00
2021-05-23 11:53:05 +02: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 = {}
if mode == " sub " or state == mesecon.state . on then
nodedef.inventory_image = nil
end
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) " )
end
nodedef.description = desc
2017-08-28 01:51:21 +02:00
end
2021-05-23 11:53:05 +02:00
minetest.register_node ( nodename , nodedef )
2024-04-15 00:35:32 +02:00
--mcl_wip.register_wip_item(nodename)
2021-05-23 11:53:05 +02:00
end
2017-08-28 01:51:21 +02:00
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 01:58:07 +02:00
output = " mcl_comparators:comparator_off_comp " ,
2017-08-28 01:51:21 +02:00
recipe = {
{ " " , rstorch , " " } ,
{ rstorch , quartz , rstorch } ,
{ stone , stone , stone } ,
}
} )
2024-04-15 00:35:32 +02:00
--[[
2017-08-28 01:51:21 +02:00
-- Register active block handlers
minetest.register_abm ( {
2018-05-13 00:23:34 +02:00
label = " Comparator signal input check (comparator is off) " ,
2017-08-28 01:51:21 +02:00
nodenames = {
2017-08-28 01:58:07 +02:00
" mcl_comparators:comparator_off_comp " ,
" mcl_comparators:comparator_off_sub " ,
2017-08-28 01:51:21 +02:00
} ,
2018-05-13 00:23:34 +02:00
neighbors = { " group:container " , " group:comparator_signal " } ,
2017-08-28 01:51:21 +02:00
interval = 1 ,
chance = 1 ,
action = update_self ,
} )
minetest.register_abm ( {
2018-05-13 00:23:34 +02:00
label = " Comparator signal input check (comparator is on) " ,
2017-08-28 01:51:21 +02:00
nodenames = {
2017-08-28 01:58:07 +02:00
" mcl_comparators:comparator_on_comp " ,
" mcl_comparators:comparator_on_sub " ,
2017-08-28 01:51:21 +02:00
} ,
-- needs to run regardless of neighbors to make sure we detect when a
-- container is dug
interval = 1 ,
chance = 1 ,
action = update_self ,
} )
2024-04-15 00:35:32 +02:00
] ]
2017-08-28 01:51:21 +02:00
-- Add entry aliases for the Help
if minetest.get_modpath ( " doc " ) then
2017-08-28 01:58:07 +02:00
doc.add_entry_alias ( " nodes " , " mcl_comparators:comparator_off_comp " ,
2021-05-23 11:53:05 +02:00
" nodes " , " mcl_comparators:comparator_off_sub " )
2017-08-28 01:58:07 +02:00
doc.add_entry_alias ( " nodes " , " mcl_comparators:comparator_off_comp " ,
2021-05-23 11:53:05 +02:00
" nodes " , " mcl_comparators:comparator_on_comp " )
2017-08-28 01:58:07 +02:00
doc.add_entry_alias ( " nodes " , " mcl_comparators:comparator_off_comp " ,
2021-05-23 11:53:05 +02:00
" nodes " , " mcl_comparators:comparator_on_sub " )
2017-08-28 01:51:21 +02:00
end