diff --git a/mesecons_transistors/init.lua b/mesecons_transistors/init.lua new file mode 100644 index 0000000..76273c5 --- /dev/null +++ b/mesecons_transistors/init.lua @@ -0,0 +1,499 @@ +local node_box = { + type = "fixed", + fixed = { + {-8/16, -8/16, -8/16, 8/16, -7/16, 8/16}, + }, +} + +local function rotate_rules(node, rules) + for rotations = 0, node.param2 - 1 do + rules = mesecon.rotate_rules_left(rules) + end + return rules +end + +local function get_switcher_inputrules(node) + return rotate_rules(node, {{x = 0, y = 0, z = -1}, {x = 1, y = 0, z = 0}, + {x = -1, y = 0, z = 0}}) +end + +local function get_conductorrules(node) + return rotate_rules(node, {{x = 1, y = 0, z = 0}, {x = -1, y = 0, z = 0}}) +end + +local function get_dir_to_switcher(node) + return rotate_rules(node, {{x = 0, y = 0, z = -1}})[1] +end + +local function get_dir_to_switched(node) + return rotate_rules(node, {{x = 0, y = 0, z = 1}})[1] +end + +local is_transistor_node = { + ["mesecons_transistors:normally_open_contact_open"] = true, + ["mesecons_transistors:normally_open_contact_closed_off" ] = true, + ["mesecons_transistors:normally_open_contact_closed_on" ] = true, + ["mesecons_transistors:normally_closed_contact_open" ] = true, + ["mesecons_transistors:normally_closed_contact_closed_off"] = true, + ["mesecons_transistors:normally_closed_contact_closed_on"] = true, +} + +local function handle_overheat(pos, node) + local conductor_pos = vector.add(pos, get_dir_to_switched(node)) + local conductor_node = mesecon.get_node_force(conductor_pos) + + if not conductor_node or conductor_node.param2 ~= node.param2 or + not is_transistor_node[conductor_node.name] then + minetest.remove_node(pos) + mesecon.on_dignode(pos, node) + return + end + + minetest.remove_node(pos) + minetest.remove_node(conductor_pos) + + mesecon.on_dignode(pos, node) + mesecon.on_dignode(conductor_pos, conductor_node) + + minetest.add_item(conductor_pos, minetest.registered_nodes[conductor_node.name].drop) +end + +local function switcher_on(pos, node) + if mesecon.do_overheat(pos) then + handle_overheat(pos, node) + return + end + + node.name = "mesecons_transistors:switcher_on" + minetest.swap_node(pos, node) + + local dir = get_dir_to_switched(node) + local switched_pos = vector.add(pos, dir) + local switched_node = mesecon.get_node_force(switched_pos) + if not switched_node then + return + end + local old_switched_node = {name = switched_node.name, param1 = switched_node.param1, + param2 = switched_node.param2} + + if switched_node.name == "mesecons_transistors:normally_open_contact_open" then + switched_node.name = "mesecons_transistors:normally_open_contact_closed_off" + elseif switched_node.name == "mesecons_transistors:normally_closed_contact_closed_off" or + switched_node.name == "mesecons_transistors:normally_closed_contact_closed_on" then + switched_node.name = "mesecons_transistors:normally_closed_contact_open" + else + minetest.log("info", "invalid mesecons_transistors:swicher was triggered at " + .. minetest.pos_to_string(pos) .. ", node at front is " .. switched_node.name) + return + end + if old_switched_node.param2 ~= node.param2 then + minetest.log("info", "invalid mesecons_transistors:swicher was triggered at " + .. minetest.pos_to_string(pos) .. ", node at front is from another switcher") + return + end + + minetest.swap_node(switched_pos, switched_node) + mesecon.on_dignode(switched_pos, old_switched_node) + mesecon.on_placenode(switched_pos, switched_node) +end + +local function switcher_off(pos, node) + if mesecon.do_overheat(pos) then + handle_overheat(pos, node) + return + end + + node.name = "mesecons_transistors:switcher_off" + minetest.swap_node(pos, node) + + local dir = get_dir_to_switched(node) + local switched_pos = vector.add(pos, dir) + local switched_node = mesecon.get_node_force(switched_pos) + if not switched_node then + return + end + local old_switched_node = {name = switched_node.name, param1 = switched_node.param1, + param2 = switched_node.param2} + + if switched_node.name == "mesecons_transistors:normally_open_contact_closed_off" or + switched_node.name == "mesecons_transistors:normally_open_contact_closed_on" then + switched_node.name = "mesecons_transistors:normally_open_contact_open" + elseif switched_node.name == "mesecons_transistors:normally_closed_contact_open" then + switched_node.name = "mesecons_transistors:normally_closed_contact_closed_off" + else + minetest.log("info", "invalid mesecons_transistors:swicher was triggered at " + .. minetest.pos_to_string(pos) .. ", node at front is " .. switched_node.name) + return + end + if old_switched_node.param2 ~= node.param2 then + minetest.log("info", "invalid mesecons_transistors:swicher was triggered at " + .. minetest.pos_to_string(pos) .. ", node at front is from another switcher") + return + end + + minetest.swap_node(switched_pos, switched_node) + mesecon.on_dignode(switched_pos, old_switched_node) + mesecon.on_placenode(switched_pos, switched_node) +end + +local function do_nothing() + -- nothing +end + +-- returns a fuction for on_place +-- does pretty much the same as minetest.item_place, but also places the switcher +local function get_on_place_callback(is_nc) + return function(itemstack, placer, pointed_thing) + -- Call on_rightclick if the pointed node defines it + if pointed_thing.type == "node" and placer and + not placer:get_player_control().sneak then + local n = minetest.get_node(pointed_thing.under) + local nn = n.name + if minetest.registered_nodes[nn] and minetest.registered_nodes[nn].on_rightclick then + return minetest.registered_nodes[nn].on_rightclick(pointed_thing.under, n, + placer, itemstack, pointed_thing) or itemstack + end + end + + if pointed_thing.type ~= "node" then + return itemstack + end + + local nodename = is_nc and "mesecons_transistors:normally_closed_contact_closed_off" or + "mesecons_transistors:normally_open_contact_open" + + local under = pointed_thing.under + local oldnode_under = minetest.get_node_or_nil(under) + local above = pointed_thing.above + local oldnode_above = minetest.get_node_or_nil(above) + local playername = placer and placer:get_player_name() or "" + local log = playername ~= "" and minetest.log or do_nothing + + if not oldnode_under or not oldnode_above then + log("info", playername .. " tried to place" + .. " node in unloaded position " .. minetest.pos_to_string(above)) + return itemstack + end + + local olddef_under = minetest.registered_nodes[oldnode_under.name] + olddef_under = olddef_under or minetest.nodedef_default + local olddef_above = minetest.registered_nodes[oldnode_above.name] + olddef_above = olddef_above or minetest.nodedef_default + + if not olddef_above.buildable_to and not olddef_under.buildable_to then + log("info", playername .. " tried to place node in invalid position " .. + minetest.pos_to_string(above) .. ", replacing " .. + oldnode_above.name) + return itemstack + end + + -- Place above pointed node + local place_to = vector.new(above) + local oldnode = oldnode_above + + -- If node under is buildable_to, place into it instead (eg. snow) + if olddef_under.buildable_to then + log("info", "node under is buildable to") + place_to = vector.new(under) + oldnode = oldnode_under + end + + local newnode = {name = nodename, param1 = 0, param2 = 0} + + -- Calculate direction + local placer_pos = placer and placer:get_pos() + if placer_pos then + local dir = vector.subtract(above, placer_pos) + newnode.param2 = minetest.dir_to_facedir(dir) + log("info", "facedir: " .. newnode.param2) + end + + -- pos for switcher + local switcher_dir = get_dir_to_switcher(newnode) + local switcher_pos = vector.add(place_to, switcher_dir) + + local switcher_name = "mesecons_transistors:switcher_off" + local switcher_node = {name = switcher_name, param1 = 0, param2 = newnode.param2} + local switcher_pointed_thing = {type = "node", above = vector.add(above, switcher_dir), + under = vector.add(under, switcher_dir)} + + -- check if switcher pos is loaded and buildable to + local switcher_oldnode = minetest.get_node_or_nil(switcher_pos) + if not oldnode_under or not oldnode_above then + log("info", playername .. " tried to place node in unloaded position " .. + minetest.pos_to_string(switcher_pos)) + return itemstack + end + local switcher_oldnode_def = minetest.registered_nodes[switcher_oldnode.name] + switcher_oldnode_def = switcher_oldnode_def or minetest.nodedef_default + if not switcher_oldnode_def.buildable_to then + log("info", playername .. " tried to place node in invalid position " .. + minetest.pos_to_string(switcher_pos) .. ", replacing " .. + switcher_oldnode.name) + return itemstack + end + + -- check protection + if minetest.is_protected(place_to, playername) then + log("action", playername .. " tried to place " .. nodename .. + " at protected position " .. minetest.pos_to_string(place_to)) + minetest.record_protection_violation(place_to, playername) + return itemstack + end + if minetest.is_protected(switcher_pos, playername) then + log("action", playername .. " tried to place " .. switcher_name .. + " at protected position " .. minetest.pos_to_string(switcher_pos)) + minetest.record_protection_violation(switcher_pos, playername) + return itemstack + end + + log("action", playername .. " places node " .. nodename .. " at " .. + minetest.pos_to_string(place_to)) + + -- Add nodes and update + minetest.set_node(place_to, newnode) + minetest.set_node(switcher_pos, switcher_node) + + -- Play sound if it was done by a player + if playername ~= "" then + local sounds = default.node_sound_stone_defaults() + minetest.sound_play(sounds.place, { + pos = place_to, + exclude_player = playername, + }, true) + end + + local take_item = true + + -- Run script hook + for _, callback in ipairs(core.registered_on_placenodes) do + -- Deepcopy pos, node and pointed_thing because callback can modify them + local place_to_copy = vector.new(place_to) + local newnode_copy = {name = newnode.name, param1 = newnode.param1, param2 = newnode.param2} + local oldnode_copy = {name = oldnode.name, param1 = oldnode.param1, param2 = oldnode.param2} + local pointed_thing_copy = {type = "node", above = vector.new(above), under = vector.new(under)} + if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then + take_item = false + end + end + for _, callback in ipairs(core.registered_on_placenodes) do + -- Deepcopy pos, node and pointed_thing because callback can modify them + local place_to_copy = vector.new(switcher_pos) + local newnode_copy = {name = switcher_node.name, param1 = switcher_node.param1, param2 = switcher_node.param2} + local oldnode_copy = {name = switcher_oldnode.name, param1 = switcher_oldnode.param1, param2 = switcher_oldnode.param2} + local pointed_thing_copy = {type = "node", above = vector.new(switcher_pointed_thing.above), + under = vector.new(switcher_pointed_thing.under)} + if callback(place_to_copy, newnode_copy, placer, oldnode_copy, itemstack, pointed_thing_copy) then + take_item = false + end + end + + if take_item then + itemstack:take_item() + end + return itemstack + end +end + +local function transistor_after_dig_node(pos, oldnode, _, _) + local switcher_pos = vector.add(pos, get_dir_to_switcher(oldnode)) + local old_switcher_node = mesecon.get_node_force(switcher_pos) + if not old_switcher_node or old_switcher_node.param2 ~= oldnode.param2 or + (old_switcher_node.name ~= "mesecons_transistors:switcher_off" and + old_switcher_node.name ~= "mesecons_transistors:switcher_on") then + return + end + minetest.remove_node(switcher_pos) + mesecon.do_cooldown(switcher_pos) + mesecon.on_dignode(pos, old_switcher_node) +end + +local function switcher_on_dig(pos, node, digger) + local conductor_pos = vector.add(pos, get_dir_to_switched(node)) + local conductor_node = mesecon.get_node_force(conductor_pos) + if not conductor_node or conductor_node.param2 ~= node.param2 or + not is_transistor_node[conductor_node.name] then + return false + end + return minetest.node_dig(conductor_pos, conductor_node, digger) +end + +local function generate_tiles(is_nc, is_closed, is_on) + local tiles = { + "jeija_microcontroller_bottom.png^mesecons_transistors_basepattern.png".. + "^(mesecons_wire_off.png^[mask:mesecons_transistors_wire_mask.png)".. + "^mesecons_transistors_no_open.png", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png^jeija_gate_side_output_off.png", + "jeija_microcontroller_bottom.png^jeija_gate_side_output_off.png", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png^jeija_gate_side_output_off.png", + } + + -- conductor wire + local is_on_str = is_on and "on" or "off" + tiles[1] = tiles[1] .. "^(mesecons_wire_" .. is_on_str .. + ".png^[mask:mesecons_transistors_wire_mask.png)" + tiles[3] = tiles[3] .. "^jeija_gate_side_output_" .. is_on_str .. ".png" + tiles[4] = tiles[4] .. "^jeija_gate_side_output_" .. is_on_str .. ".png" + + -- effector wire and closed/opened stuff + tiles[1] = tiles[1] .. "^mesecons_transistors_" .. (is_nc and "nc" or "no") + .. "_" .. (is_closed and "closed" or "open") .. ".png" + tiles[6] = tiles[6] .. "^jeija_gate_side_output_" .. + (is_nc == is_closed and "off" or "on") .. ".png" + + return tiles +end + +mesecon.register_node("mesecons_transistors:switcher", { + description = "You hacker you!", + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + drawtype = "nodebox", + node_box = node_box, + sounds = default.node_sound_stone_defaults(), + groups = {dig_immediate = 2, not_in_creative_inventory = 1, overheat = 1}, + drop = "", + on_dig = switcher_on_dig, + on_rotate = false, +}, { + tiles = { + "jeija_microcontroller_bottom.png".. + "^(mesecons_wire_off.png^[mask:mesecons_transistors_switcher_mask.png)", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png^jeija_gate_side_output_off.png", + "jeija_microcontroller_bottom.png", + }, + mesecons = {effector = { + rules = get_switcher_inputrules, + action_on = switcher_on, + }}, +}, { + tiles = { + "jeija_microcontroller_bottom.png".. + "^(mesecons_wire_on.png^[mask:mesecons_transistors_switcher_mask.png)", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png", + "jeija_microcontroller_bottom.png^jeija_gate_side_output_on.png", + "jeija_microcontroller_bottom.png", + }, + mesecons = {effector = { + rules = get_switcher_inputrules, + action_off = switcher_off, + }} +}) + +minetest.register_node("mesecons_transistors:normally_open_contact_open", { + description = "N/O contact", + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + drawtype = "nodebox", + node_box = node_box, + node_placement_prediction = "", + sounds = default.node_sound_stone_defaults(), + groups = {dig_immediate = 2}, + tiles = generate_tiles(false, false, false), + mesecons = {receptor = { -- pseudo-receptor to keep wires connected + state = mesecon.state.off, + rules = get_conductorrules, + }}, + on_place = get_on_place_callback(false), + after_dig_node = transistor_after_dig_node, + on_rotate = false, + on_blast = mesecon.on_blastnode, +}) + +mesecon.register_node("mesecons_transistors:normally_open_contact_closed", { + description = "N/O contact (You hacker you!)", + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + drawtype = "nodebox", + drop = "mesecons_transistors:normally_open_contact_open", + node_box = node_box, + sounds = default.node_sound_stone_defaults(), + groups = {dig_immediate = 2, not_in_creative_inventory = 1}, + after_dig_node = transistor_after_dig_node, + on_rotate = false, +}, { + tiles = generate_tiles(false, true, false), + mesecons = {conductor = { + state = mesecon.state.off, + onstate = "mesecons_transistors:normally_open_contact_closed_on", + rules = get_conductorrules, + }}, +}, { + tiles = generate_tiles(false, true, true), + mesecons = {conductor = { + state = mesecon.state.on, + offstate = "mesecons_transistors:normally_open_contact_closed_off", + rules = get_conductorrules, + }}, +}) + +minetest.register_node("mesecons_transistors:normally_closed_contact_open", { + description = "N/C contact (You hacker you!)", + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + drawtype = "nodebox", + drop = "mesecons_transistors:normally_closed_contact_closed_off", + node_box = node_box, + sounds = default.node_sound_stone_defaults(), + groups = {dig_immediate = 2, not_in_creative_inventory = 1}, + tiles = generate_tiles(true, false, false), + mesecons = {receptor = { -- pseudo-receptor to keep wires connected + state = mesecon.state.off, + rules = get_conductorrules, + }}, + after_dig_node = transistor_after_dig_node, + on_rotate = false, + on_blast = mesecon.on_blastnode, +}) + +mesecon.register_node("mesecons_transistors:normally_closed_contact_closed", { + paramtype = "light", + paramtype2 = "facedir", + is_ground_content = false, + drawtype = "nodebox", + drop = "mesecons_transistors:normally_closed_contact_closed_off", + node_box = node_box, + sounds = default.node_sound_stone_defaults(), + after_dig_node = transistor_after_dig_node, + on_rotate = false, +}, { + description = "N/C contact", + node_placement_prediction = "", + groups = {dig_immediate = 2}, + tiles = generate_tiles(true, true, false), + mesecons = {conductor = { + state = mesecon.state.off, + onstate = "mesecons_transistors:normally_closed_contact_closed_on", + rules = get_conductorrules, + }}, + on_place = get_on_place_callback(true), +}, { + description = "N/C contact (You hacker you!)", + groups = {dig_immediate = 2, not_in_creative_inventory = 1}, + tiles = generate_tiles(true, true, true), + mesecons = {conductor = { + state = mesecon.state.on, + offstate = "mesecons_transistors:normally_closed_contact_closed_off", + rules = get_conductorrules, + }}, +}) + +mesecon.register_mvps_stopper("mesecons_transistors:switcher_on") +mesecon.register_mvps_stopper("mesecons_transistors:switcher_off") +mesecon.register_mvps_stopper("mesecons_transistors:normally_open_contact_open") +mesecon.register_mvps_stopper("mesecons_transistors:normally_open_contact_closed_off") +mesecon.register_mvps_stopper("mesecons_transistors:normally_open_contact_closed_on") +mesecon.register_mvps_stopper("mesecons_transistors:normally_closed_contact_open") +mesecon.register_mvps_stopper("mesecons_transistors:normally_closed_contact_closed_off") +mesecon.register_mvps_stopper("mesecons_transistors:normally_closed_contact_closed_on") diff --git a/mesecons_transistors/mod.conf b/mesecons_transistors/mod.conf new file mode 100644 index 0000000..255dd81 --- /dev/null +++ b/mesecons_transistors/mod.conf @@ -0,0 +1,2 @@ +name = mesecons_transistors +depends = mesecons, default, mesecons_gates, mesecons_mvps diff --git a/mesecons_transistors/textures/mesecons_transistors_basepattern.png b/mesecons_transistors/textures/mesecons_transistors_basepattern.png new file mode 100644 index 0000000..601a8ee Binary files /dev/null and b/mesecons_transistors/textures/mesecons_transistors_basepattern.png differ diff --git a/mesecons_transistors/textures/mesecons_transistors_nc_closed.png b/mesecons_transistors/textures/mesecons_transistors_nc_closed.png new file mode 100644 index 0000000..479cb5d Binary files /dev/null and b/mesecons_transistors/textures/mesecons_transistors_nc_closed.png differ diff --git a/mesecons_transistors/textures/mesecons_transistors_nc_open.png b/mesecons_transistors/textures/mesecons_transistors_nc_open.png new file mode 100644 index 0000000..3c4678e Binary files /dev/null and b/mesecons_transistors/textures/mesecons_transistors_nc_open.png differ diff --git a/mesecons_transistors/textures/mesecons_transistors_no_closed.png b/mesecons_transistors/textures/mesecons_transistors_no_closed.png new file mode 100644 index 0000000..52eaa17 Binary files /dev/null and b/mesecons_transistors/textures/mesecons_transistors_no_closed.png differ diff --git a/mesecons_transistors/textures/mesecons_transistors_no_open.png b/mesecons_transistors/textures/mesecons_transistors_no_open.png new file mode 100644 index 0000000..b0c755e Binary files /dev/null and b/mesecons_transistors/textures/mesecons_transistors_no_open.png differ diff --git a/mesecons_transistors/textures/mesecons_transistors_switcher_mask.png b/mesecons_transistors/textures/mesecons_transistors_switcher_mask.png new file mode 100644 index 0000000..f81f121 Binary files /dev/null and b/mesecons_transistors/textures/mesecons_transistors_switcher_mask.png differ diff --git a/mesecons_transistors/textures/mesecons_transistors_wire_mask.png b/mesecons_transistors/textures/mesecons_transistors_wire_mask.png new file mode 100644 index 0000000..7c3129e Binary files /dev/null and b/mesecons_transistors/textures/mesecons_transistors_wire_mask.png differ