VoxeLibre/mods/ITEMS/REDSTONE/mcl_comparators/init.lua

357 lines
11 KiB
Lua

local S = minetest.get_translator(minetest.get_current_modname())
mcl_comparators = {}
local mod = mcl_comparators
-- Functions that get the input/output rules of the comparator
local function comparator_get_output_rules(node)
local rules = {{x = -1, y = 0, z = 0, spread=true}}
for i = 0, node.param2 do
rules = mesecon.rotate_rules_left(rules)
end
return rules
end
local function comparator_get_input_rules(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
rules = mesecon.rotate_rules_left(rules)
end
return rules
end
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)
print("Updating comparator at "..vector.to_string(pos))
node = node or minetest.get_node(pos)
local nodedef = minetest.registered_nodes[node.name]
-- 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]
-- Get the comparator mode
local mode = nodedef.comparator_mode
-- 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)
else
power_level = vl_redstone.get_power(back_pos)
end
-- Get the maximum side power level
local side_power_level = 0
for i=2,3 do
local pl = vl_redstone.get_power(vector.add(pos,input_rules[i]))
if pl > side_power_level then
side_power_level = pl
end
end
-- 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
end
-- Update output power level
vl_redstone.set_power(pos, power_level)
-- 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
end
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
-- 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)
end
end
end
function mod.read_inventory(inv, inv_name)
local stacks = inv:get_list(inv_name)
if not stacks then return 0 end
local count = 0
for i=1,#stacks do
count = count + ( stacks[i]:get_count() / stacks[i]:get_stack_max() )
end
return math.floor(count * 16 / #stacks)
end
-- compute tile depending on state and mode
local function get_tiles(state, mode)
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"
return {
top, "mcl_stairs_stone_slab_top.png",
sides, sides.."^[transformFX",
ends, ends,
}
end
-- Given one mode, get the other mode
local function flipmode(mode)
if mode == "comp" then return "sub"
elseif mode == "sub" then return "comp"
end
end
local function make_rightclick_handler(state, mode)
local newnodename =
"mcl_comparators:comparator_"..state.."_"..flipmode(mode)
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
minetest.swap_node(pos, {name = newnodename, param2 = node.param2 })
end
end
-- Register the 2 (states) x 2 (modes) comparators
local icon = "mcl_comparators_item.png"
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,
mcl_comparator = 1,
}
local on_rotate
if minetest.get_modpath("screwdriver") then
on_rotate = screwdriver.disallow
end
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 =
"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:\nThe 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:\nThe 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
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],
},
groups = groups,
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = false,
is_ground_content = false,
drop = "mcl_comparators:comparator_off_comp",
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,
}
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
end
minetest.register_node(nodename, nodedef)
--mcl_wip.register_wip_item(nodename)
end
end
-- Register recipies
local rstorch = "mesecons_torch:mesecon_torch_on"
local quartz = "mcl_nether:quartz"
local stone = "mcl_core:stone"
minetest.register_craft({
output = "mcl_comparators:comparator_off_comp",
recipe = {
{ "", rstorch, "" },
{ rstorch, quartz, rstorch },
{ stone, stone, stone },
}
})
--[[
-- Register active block handlers
minetest.register_abm({
label = "Comparator signal input check (comparator is off)",
nodenames = {
"mcl_comparators:comparator_off_comp",
"mcl_comparators:comparator_off_sub",
},
neighbors = {"group:container", "group:comparator_signal"},
interval = 1,
chance = 1,
action = update_self,
})
minetest.register_abm({
label = "Comparator signal input check (comparator is on)",
nodenames = {
"mcl_comparators:comparator_on_comp",
"mcl_comparators:comparator_on_sub",
},
-- 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
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")
end