diff --git a/breaker.lua b/breaker.lua new file mode 100644 index 0000000..ba21911 --- /dev/null +++ b/breaker.lua @@ -0,0 +1,669 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +local break_interval = 1.0 + + + +local function get_breaker_side (pos, param2, side) + local base = nil + + if side == "left" then + base = { x = -1, y = pos.y, z = 0 } + elseif side == "right" then + base = { x = 1, y = pos.y, z = 0 } + elseif side == "back" then + base = { x = 0, y = pos.y, z = -1 } + else -- "front" + base = { x = 0, y = pos.y, z = 1 } + end + + if param2 == 3 then -- +x + return { x = base.z + pos.x, y = base.y, z = (base.x * -1) + pos.z } + elseif param2 == 0 then -- -z + return { x = (base.x * -1) + pos.x, y = base.y, z = (base.z * -1) + pos.z } + elseif param2 == 1 then -- -x + return { x = (base.z * -1) + pos.x, y = base.y, z = base.x + pos.z } + else -- param2 == 2 +z + return { x = base.x + pos.x, y = base.y, z = base.z + pos.z } + end +end + + + +local function send_break_message (pos, action, name) + if utils.digilines_supported then + local meta = minetest.get_meta (pos) + + if meta then + local channel = meta:get_string ("channel") + + if channel:len () > 0 then + utils.digilines_receptor_send (pos, + utils.digilines_default_rules, + channel, + { action = action, + name = name }) + end + end + end +end + + + +local function get_tool (pos) + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + local stack = inv:get_stack ("tool", 1) + + if stack and not stack:is_empty () then + return stack + end + end + end + + return nil +end + + + +local function add_wear (pos, wear) + if wear > 0 then + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + local stack = inv:get_stack ("tool", 1) + + if stack and not stack:is_empty () then + local cur_wear = stack:get_wear () + + if (cur_wear + wear) >= 65535 then + inv:set_stack ("tool", 1, nil) + send_break_message (pos, "tool", stack:get_name ()) + else + stack:set_wear (cur_wear + wear) + inv:set_stack ("tool", 1, stack) + end + end + end + end + end +end + + + +local function play_dug_sound (pos, nodename) + local def = utils.find_item_def (nodename) + + if def and def.sounds and def.sounds.dug then + minetest.sound_play (def.sounds.dug, { pos = pos }) + end +end + + + +local function can_break_node (pos) + local node = minetest.get_node (pos) + + if node then + local dig_node = minetest.get_node (get_breaker_side (pos, node.param2, "front")) + + if dig_node and dig_node.name ~= "air" then + local node_def = minetest.registered_nodes[dig_node.name] + + if node_def then + -- try tool first + local tool = get_tool (pos) + + if tool then + local dig_params = nil + local tool_def = minetest.registered_items[tool:get_name ()] + + if tool_def then + dig_params = + minetest.get_dig_params (node_def.groups, + tool_def.tool_capabilities) + + if dig_params.diggable then + return true, tool:get_name (), dig_params.wear + end + end + end + + -- then try hand + dig_params = + minetest.get_dig_params (node_def.groups, + minetest.registered_items[""].tool_capabilities) + + if dig_params.diggable then + return true, nil, 0 + end + end + end + end + + return false +end + + + +local function break_node (pos) + local diggable, tool, wear = can_break_node (pos) + local node = minetest.get_node (pos) + + if diggable and node then + local break_pos = get_breaker_side (pos, node.param2, "front") + local break_node = minetest.get_node (break_pos) + + if break_node then + local items = minetest.get_node_drops (break_node, tool) + local break_name = break_node.name + + if items then + local eject_pos = get_breaker_side (pos, node.param2, "back") + + for i = 1, #items do + local stack = ItemStack (items[i]) + + if stack and not stack:is_empty () then + local item_def = utils.find_item_def (stack:get_name ()) + + if item_def and item_def.preserve_metadata then + item_def.preserve_metadata (pos, node, minetest.get_meta (pos), { stack }) + end + + utils.item_drop (stack, nil, eject_pos) + end + end + end + + minetest.remove_node (break_pos) + play_dug_sound (break_pos, break_name) + add_wear (pos, wear) + send_break_message (pos, "break", break_name) + end + end +end + + + +local function breaker_off (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:breaker_on" then + node.name = "lwcomponents:breaker" + + minetest.get_node_timer (pos):stop () + minetest.swap_node (pos, node) + + elseif node.name == "lwcomponents:breaker_locked_on" then + node.name = "lwcomponents:breaker_locked" + + minetest.get_node_timer (pos):stop () + minetest.swap_node (pos, node) + + end + end +end + + + +local function breaker_on (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:breaker" then + node.name = "lwcomponents:breaker_on" + + minetest.swap_node (pos, node) + break_node (pos) + minetest.get_node_timer (pos):start (break_interval) + + elseif node.name == "lwcomponents:breaker_locked" then + node.name = "lwcomponents:breaker_locked_on" + + minetest.swap_node (pos, node) + break_node (pos) + minetest.get_node_timer (pos):start (break_interval) + + end + end +end + + + +local function eject_tool (pos, side) + local node = minetest.get_node (pos) + local meta = minetest.get_meta (pos) + + if meta and node then + local inv = meta:get_inventory () + + if inv then + local stack = inv:get_stack ("tool", 1) + + if stack and not stack:is_empty () then + utils.item_drop (stack, nil, get_breaker_side (pos, node.param2, side)) + inv:set_stack ("tool", 1, nil) + end + end + end +end + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local spec = + "formspec_version[3]\n".. + "size[11.75,10.75;true]\n".. + "field[1.0,1.0;4.0,0.8;channel;Channel;${channel}]\n".. + "button[5.5,1.0;2.0,0.8;setchannel;Set]\n".. + "list[context;tool;5.0,2.75;1,1;]\n".. + "list[current_player;main;1.0,5.0;8,4;]\n".. + "listring[]" + + meta:set_string ("inventory", "{ tool = { } }") + meta:set_string ("formspec", spec) + + local inv = meta:get_inventory () + + inv:set_size ("tool", 1) + inv:set_width ("tool", 1) + + -- If return true no item is taken from itemstack + return false +end + + + +local function after_place_node_locked (pos, placer, itemstack, pointed_thing) + after_place_node (pos, placer, itemstack, pointed_thing) + + if placer and placer:is_player () then + local meta = minetest.get_meta (pos) + + meta:set_string ("owner", placer:get_player_name ()) + meta:set_string ("infotext", "Dropper (owned by "..placer:get_player_name ()..")") + end + + -- If return true no item is taken from itemstack + return false +end + + + +local function on_receive_fields (pos, formname, fields, sender) + if not utils.can_interact_with_node (pos, sender) then + return + end + + if fields.setchannel then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("channel", fields.channel) + end + end +end + + + +local function can_dig (pos, player) + if not utils.can_interact_with_node (pos, player) then + return false + end + + local meta = minetest.get_meta (pos) + + if meta then + local inv = meta:get_inventory () + + if inv then + if not inv:is_empty ("tool") then + return false + end + end + end + + return true +end + + + +local function on_rightclick (pos, node, clicker, itemstack, pointed_thing) + if not utils.can_interact_with_node (pos, clicker) then + if clicker and clicker:is_player () then + local owner = "" + local meta = minetest.get_meta (pos) + + if meta then + owner = meta:get_string ("owner") + end + + local spec = + "formspec_version[3]".. + "size[8.0,4.0,false]".. + "label[1.0,1.0;Owned by "..minetest.formspec_escape (owner).."]".. + "button_exit[3.0,2.0;2.0,1.0;close;Close]" + + minetest.show_formspec (clicker:get_player_name (), + "lwcomponents:component_privately_owned", + spec) + end + end + + return itemstack +end + + + +local function on_blast (pos, intensity) + local meta = minetest.get_meta (pos) + + if meta then + if intensity >= 1.0 then + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("tool") + + for slot = 1, slots do + local stack = inv:get_stack ("tool", slot) + + if stack and not stack:is_empty () then + if math.floor (math.random (0, 5)) == 3 then + utils.item_drop (stack, nil, pos) + else + utils.on_destroy (stack) + end + end + end + end + + minetest.remove_node (pos) + + else -- intensity < 1.0 + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("tool") + + for slot = 1, slots do + local stack = inv:get_stack ("tool", slot) + + if stack and not stack:is_empty () then + utils.item_drop (stack, nil, pos) + end + end + end + + local node = minetest.get_node_or_nil (pos) + if node then + local items = minetest.get_node_drops (node, nil) + + if items and #items > 0 then + local stack = ItemStack (items[1]) + + if stack then + preserve_metadata (pos, node, meta, { stack }) + utils.item_drop (stack, nil, pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function on_timer (pos, elapsed) + breaker_off (pos) +end + + + +local function allow_metadata_inventory_put (pos, listname, index, stack, player) + if listname == "tool" then + if stack and not stack:is_empty () then + local def = utils.find_item_def (stack:get_name ()) + + if def and def.tool_capabilities then + return 1 + end + end + end + + return 0 +end + + + +local function digilines_support () + if utils.digilines_supported then + return + { + wire = + { + rules = utils.digilines_default_rules, + }, + + effector = + { + action = function (pos, node, channel, msg) + local meta = minetest.get_meta(pos) + + if meta then + local this_channel = meta:get_string ("channel") + + if this_channel ~= "" and this_channel == channel and + type (msg) == "string" then + + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if m[1] == "break" then + breaker_on (pos) + + elseif m[1] == "eject" then + eject_tool (pos, m[2]) + + end + end + end + end, + } + } + end + + return nil +end + + + +local function mesecon_support () + if utils.mesecon_supported then + return + { + effector = + { + rules = utils.mesecon_default_rules, + + action_on = function (pos, node) + -- do something to turn the effector on + breaker_on (pos) + end, + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:breaker", { + description = S("Breaker"), + tiles = { "lwbreaker.png", "lwbreaker.png", "lwbreaker.png", + "lwbreaker.png", "lwbreaker.png", "lwbreaker_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:breaker", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick, + allow_metadata_inventory_put = allow_metadata_inventory_put +}) + + + +minetest.register_node("lwcomponents:breaker_locked", { + description = S("Breaker (locked)"), + tiles = { "lwbreaker.png", "lwbreaker.png", "lwbreaker.png", + "lwbreaker.png", "lwbreaker.png", "lwbreaker_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:breaker_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick, + allow_metadata_inventory_put = allow_metadata_inventory_put +}) + + + + +minetest.register_node("lwcomponents:breaker_on", { + description = S("Breaker"), + tiles = { "lwbreaker.png", "lwbreaker.png", "lwbreaker.png", + "lwbreaker.png", "lwbreaker.png", "lwbreaker_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + light_source = 3, + floodable = false, + drop = "lwcomponents:breaker", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick, + allow_metadata_inventory_put = allow_metadata_inventory_put +}) + + + +minetest.register_node("lwcomponents:breaker_locked_on", { + description = S("Breaker (locked)"), + tiles = { "lwbreaker.png", "lwbreaker.png", "lwbreaker.png", + "lwbreaker.png", "lwbreaker.png", "lwbreaker_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + light_source = 3, + floodable = false, + drop = "lwcomponents:breaker_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick, + allow_metadata_inventory_put = allow_metadata_inventory_put +}) + + + +utils.hopper_add_container({ + {"bottom", "lwcomponents:breaker", "tool"}, -- insert items below from hopper above + {"side", "lwcomponents:breaker", "tool"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"bottom", "lwcomponents:breaker_locked", "tool"}, -- insert items below from hopper above + {"side", "lwcomponents:breaker_locked", "tool"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"bottom", "lwcomponents:breaker_on", "tool"}, -- insert items below from hopper above + {"side", "lwcomponents:breaker_on", "tool"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"bottom", "lwcomponents:breaker_locked_on", "tool"}, -- insert items below from hopper above + {"side", "lwcomponents:breaker_locked_on", "tool"}, -- insert items from hopper at side +}) + + + +end -- utils.digilines_supported or utils.mesecon_supported diff --git a/change.log b/change.log index 2c39c40..4508b96 100644 --- a/change.log +++ b/change.log @@ -31,3 +31,9 @@ v0.1.4 v0.1.5 * Added setting Spawn mobs. * Added lwcomponents.register_spawner api. + + +v0.1.6 +* Added holograms. +* Added breakers. +* Added fans. diff --git a/collector.lua b/collector.lua index 5bdc731..fb46bc0 100644 --- a/collector.lua +++ b/collector.lua @@ -400,7 +400,9 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel ~= "" and this_channel == channel then + if this_channel ~= "" and this_channel == channel and + type (msg) == "string" then + local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w diff --git a/crafting.lua b/crafting.lua index d2873b6..d9e3cff 100644 --- a/crafting.lua +++ b/crafting.lua @@ -94,6 +94,42 @@ minetest.register_craft( { }, }) + +minetest.register_craft( { + output = "lwcomponents:breaker", + recipe = { + { "default:chest", "default:pick_stone" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:breaker_locked", + recipe = { + { "default:chest_locked", "default:pick_stone" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:fan", + recipe = { + { "default:chest", "default:steel_ingot" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:fan_locked", + recipe = { + { "default:chest_locked", "default:steel_ingot" }, + { "default:copper_ingot", "default:steel_ingot" }, + }, +}) + end -- utils.digilines_supported or utils.mesecon_supported @@ -117,6 +153,26 @@ minetest.register_craft( { }, }) + +minetest.register_craft( { + output = "lwcomponents:hologram", + recipe = { + { "dye:red", "dye:green", "dye:blue" }, + { "default:copper_ingot", "default:steel_ingot", "default:copper_ingot" }, + { "default:chest", "default:stone", "default:glass" }, + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:hologram_locked", + recipe = { + { "dye:red", "dye:green", "dye:blue" }, + { "default:copper_ingot", "default:steel_ingot", "default:copper_ingot" }, + { "default:chest_locked", "default:stone", "default:glass" }, + }, +}) + end -- utils.digilines_supported diff --git a/detector.lua b/detector.lua index 00fd91e..9b88128 100644 --- a/detector.lua +++ b/detector.lua @@ -613,7 +613,9 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel ~= "" and this_channel == channel then + if this_channel ~= "" and this_channel == channel and + type (msg) == "string" then + local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w diff --git a/dispenser.lua b/dispenser.lua index c2b3190..9429c4f 100644 --- a/dispenser.lua +++ b/dispenser.lua @@ -427,7 +427,9 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel ~= "" and this_channel == channel then + if this_channel ~= "" and this_channel == channel and + type (msg) == "string" then + local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w diff --git a/dropper.lua b/dropper.lua index 3fc0078..e7ab25f 100644 --- a/dropper.lua +++ b/dropper.lua @@ -301,7 +301,9 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel ~= "" and this_channel == channel then + if this_channel ~= "" and this_channel == channel and + type (msg) == "string" then + local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w diff --git a/fan.lua b/fan.lua new file mode 100644 index 0000000..60a321a --- /dev/null +++ b/fan.lua @@ -0,0 +1,421 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported or utils.mesecon_supported then + + + +local fan_interval = 0.2 +local fan_force = 15.0 + + + +local function direction_vector (node) + if node.param2 == 0 then + return { x = 0, y = 0, z = -1 } + elseif node.param2 == 1 then + return { x = -1, y = 0, z = 0 } + elseif node.param2 == 2 then + return { x = 0, y = 0, z = 1 } + elseif node.param2 == 3 then + return { x = 1, y = 0, z = 0 } + else + return { x = 0, y = 0, z = 0 } + end +end + + + +local function blow (pos) + local meta = minetest.get_meta (pos) + local node = minetest.get_node (pos) + local dir = direction_vector (node) + local reach = 5 + + for r = 1, reach, 1 do + local tpos = vector.add (pos, vector.multiply (dir, r)) + local tnode = minetest.get_node_or_nil (tpos) + + if tnode and tnode.name ~= "air" then + return + end + + local object = minetest.get_objects_inside_radius (tpos, 1.5) + local vel = vector.multiply (dir, fan_force) + + for i = 1, #object do + if object[i].add_velocity then + local opos = object[i]:get_pos () + + if opos.x >= (tpos.x - 0.5) and opos.x <= (tpos.x + 0.5) and + opos.z >= (tpos.z - 0.5) and opos.z <= (tpos.z + 0.5) and + opos.y >= (tpos.y - 0.5) and opos.y <= (tpos.y + 0.5) then + + if object[i].get_luaentity and object[i]:get_luaentity () and + object[i]:get_luaentity ().name and + object[i]:get_luaentity ().name == "__builtin:item" then + + object[i]:add_velocity (vector.multiply (vel, 5)) + else + object[i]:add_velocity (vel) + end + end + end + end + end +end + + + +local function fan_off (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:fan_on" then + node.name = "lwcomponents:fan" + + minetest.get_node_timer (pos):stop () + minetest.swap_node (pos, node) + + elseif node.name == "lwcomponents:fan_locked_on" then + node.name = "lwcomponents:fan_locked" + + minetest.get_node_timer (pos):stop () + minetest.swap_node (pos, node) + + end + end +end + + + +local function fan_on (pos) + local node = minetest.get_node (pos) + + if node then + if node.name == "lwcomponents:fan" then + node.name = "lwcomponents:fan_on" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (fan_interval) + + elseif node.name == "lwcomponents:fan_locked" then + node.name = "lwcomponents:fan_locked_on" + + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (fan_interval) + + end + end +end + + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local spec = + "size[7.5,3]".. + "field[1,1;6,2;channel;Channel;${channel}]".. + "button_exit[2.5,2;3,1;submit;Set]" + + meta:set_string ("formspec", spec) + + -- If return true no item is taken from itemstack + return false +end + + + +local function after_place_node_locked (pos, placer, itemstack, pointed_thing) + after_place_node (pos, placer, itemstack, pointed_thing) + + if placer and placer:is_player () then + local meta = minetest.get_meta (pos) + + meta:set_string ("owner", placer:get_player_name ()) + meta:set_string ("infotext", "Hologram (owned by "..placer:get_player_name ()..")") + end + + -- If return true no item is taken from itemstack + return false +end + + + +local function on_receive_fields (pos, formname, fields, sender) + if not utils.can_interact_with_node (pos, sender) then + return + end + + local meta = minetest.get_meta(pos) + + if fields.submit then + meta:set_string ("channel", fields.channel) + end +end + + + +local function on_blast (pos, intensity) + local meta = minetest.get_meta (pos) + + if meta then + if intensity >= 1.0 then + + clear_map (pos) + + minetest.remove_node (pos) + + else -- intensity < 1.0 + + clear_map (pos) + + local node = minetest.get_node_or_nil (pos) + if node then + local items = minetest.get_node_drops (node, nil) + + if items and #items > 0 then + local stack = ItemStack (items[1]) + + if stack then + preserve_metadata (pos, node, meta, { stack }) + utils.item_drop (stack, nil, pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function can_dig (pos, player) + if not utils.can_interact_with_node (pos, player) then + return false + end + + return true +end + + + +local function on_rightclick (pos, node, clicker, itemstack, pointed_thing) + if not utils.can_interact_with_node (pos, clicker) then + if clicker and clicker:is_player () then + local owner = "" + local meta = minetest.get_meta (pos) + + if meta then + owner = meta:get_string ("owner") + end + + local spec = + "formspec_version[3]".. + "size[8.0,4.0,false]".. + "label[1.0,1.0;Owned by "..minetest.formspec_escape (owner).."]".. + "button_exit[3.0,2.0;2.0,1.0;close;Close]" + + minetest.show_formspec (clicker:get_player_name (), + "lwcomponents:component_privately_owned", + spec) + end + end + + return itemstack +end + + + +local function on_timer (pos, elapsed) + blow (pos) + + return true +end + + + +local function digilines_support () + if utils.digilines_supported then + return + { + wire = + { + rules = utils.digilines_default_rules, + }, + + effector = + { + action = function (pos, node, channel, msg) + local meta = minetest.get_meta(pos) + + if meta then + local this_channel = meta:get_string ("channel") + + if this_channel ~= "" and this_channel == channel then + if type (msg) == "string" then + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if m[1] == "start" then + fan_on (pos) + + elseif m[1] == "stop" then + fan_off (pos) + + end + end + end + end + end, + } + } + end + + return nil +end + + + +local function mesecon_support () + if utils.mesecon_supported then + return + { + effector = + { + rules = utils.mesecon_default_rules, + + action_on = function (pos, node) + -- do something to turn the effector on + fan_on (pos) + end, + + action_off = function (pos, node) + -- do something to turn the effector off + fan_off (pos) + end, + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:fan", { + description = S("Breaker"), + tiles = { "lwfan.png", "lwfan.png", "lwfan.png", + "lwfan.png", "lwfan.png", "lwfan_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:fan", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_rightclick = on_rightclick, +}) + + + +minetest.register_node("lwcomponents:fan_locked", { + description = S("Breaker (locked)"), + tiles = { "lwfan.png", "lwfan.png", "lwfan.png", + "lwfan.png", "lwfan.png", "lwfan_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:fan_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_rightclick = on_rightclick, +}) + + + +minetest.register_node("lwcomponents:fan_on", { + description = S("Breaker"), + tiles = { "lwfan.png", "lwfan.png", "lwfan.png", + "lwfan.png", "lwfan.png", "lwfan_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:fan", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick, +}) + + + +minetest.register_node("lwcomponents:fan_locked_on", { + description = S("Breaker (locked)"), + tiles = { "lwfan.png", "lwfan.png", "lwfan.png", + "lwfan.png", "lwfan.png", "lwfan_face_on.png"}, + is_ground_content = false, + groups = { cracky = 3, not_in_creative_inventory = 1 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + drop = "lwcomponents:fan_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node_locked, + on_blast = on_blast, + on_timer = on_timer, + on_rightclick = on_rightclick, +}) + + + +end -- utils.digilines_supported or utils.mesecon_supported diff --git a/hologram.lua b/hologram.lua new file mode 100644 index 0000000..4619f33 --- /dev/null +++ b/hologram.lua @@ -0,0 +1,424 @@ +local utils = ... +local S = utils.S + + + +if utils.digilines_supported then + + + +local hologram_block = +{ + black = { + node = "lwcomponents:hologram_black", + image = "lwhologram_black.png", + color = { a = 128, r = 0, g = 0, b = 0 } }, + orange = { + node = "lwcomponents:hologram_orange", + image = "lwhologram_orange.png", + color = { a = 128, r = 255, g = 128, b = 0 } }, + magenta = { + node = "lwcomponents:hologram_magenta", + image = "lwhologram_magenta.png", + color = { a = 128, r = 255, g = 0, b = 255 } }, + sky = { + node = "lwcomponents:hologram_sky", + image = "lwhologram_sky.png", + color = { a = 128, r = 0, g = 128, b = 255 } }, + yellow = { + node = "lwcomponents:hologram_yellow", + image = "lwhologram_yellow.png", + color = { a = 128, r = 255, g = 255, b = 0 } }, + pink = { + node = "lwcomponents:hologram_pink", + image = "lwhologram_pink.png", + color = { a = 128, r = 255, g = 128, b = 128 } }, + cyan = { + node = "lwcomponents:hologram_cyan", + image = "lwhologram_cyan.png", + color = { a = 128, r = 0, g = 255, b = 255 } }, + gray = { + node = "lwcomponents:hologram_gray", + image = "lwhologram_gray.png", + color = { a = 128, r = 128, g = 128, b = 128 } }, + silver = { + node = "lwcomponents:hologram_silver", + image = "lwhologram_silver.png", + color = { a = 128, r = 192, g = 192, b = 192 } }, + red = { + node = "lwcomponents:hologram_red", + image = "lwhologram_red.png", + color = { a = 128, r = 255, g = 0, b = 0 } }, + green = { + node = "lwcomponents:hologram_green", + image = "lwhologram_green.png", + color = { a = 128, r = 0, g = 128, b = 0 } }, + blue = { + node = "lwcomponents:hologram_blue", + image = "lwhologram_blue.png", + color = { a = 128, r = 0, g = 0, b = 255 } }, + brown = { + node = "lwcomponents:hologram_brown", + image = "lwhologram_brown.png", + color = { a = 128, r = 128, g = 64, b = 0 } }, + lime = { + node = "lwcomponents:hologram_lime", + image = "lwhologram_lime.png", + color = { a = 128, r = 0, g = 255, b = 0 } }, + purple = { + node = "lwcomponents:hologram_purple", + image = "lwhologram_purple.png", + color = { a = 128, r = 128, g = 0, b = 128 } }, + white = { + node = "lwcomponents:hologram_white", + image = "lwhologram_white.png", + color = { a = 128, r = 255, g = 255, b = 255 } }, +} + + + +local function rotate_to_dir (center, param2, point) + local base = vector.subtract (point, center) + + if param2 == 1 then + base = vector.rotate (base, { x = 0, y = (math.pi * 1.5), z = 0 }) + elseif param2 == 2 then + base = vector.rotate (base, { x = 0, y = math.pi, z = 0 }) + elseif param2 == 3 then + base = vector.rotate (base, { x = 0, y = (math.pi * 0.5), z = 0 }) + end + + return vector.add (base, center) +end + + + +local function draw_map (pos, map) + local meta = minetest.get_meta (pos) + local holonode = minetest.get_node (pos) + + if meta and holonode and type (map) == "table" then + local id = meta:get_int ("block_id") + + for y = 1, 15 do + local layer = (type (map[y]) == "table" and map[y]) or { } + + for x = 1, 15 do + local line = (type (layer[x]) == "table" and layer[x]) or { } + + for z = 1, 15 do + local map_point = { x = z + pos.x - 8, y = y + pos.y + 1, z = (16 - x) + pos.z - 8 } + local holopos = rotate_to_dir (pos, holonode.param2, map_point) + + local node = utils.get_far_node (holopos) + local draw = false + + if node and node.name ~= "air" then + if node.name:sub (1, 22) == "lwcomponents:hologram_" then + local nodemeta = minetest.get_meta (holopos) + + if nodemeta and nodemeta:get_int ("block_id") == id then + draw = true + end + end + else + draw = true + end + + if draw then + local holonode = hologram_block[line[z]] + + if node then + utils.destroy_node (holopos) + end + + if holonode then + minetest.set_node (holopos, { name = holonode.node }) + + local nodemeta = minetest.get_meta (holopos) + + if nodemeta then + nodemeta:set_int ("block_id", id) + end + end + end + end + end + end + end +end + + + +local function clear_map (pos) + draw_map (pos, { }) +end + + + +local function on_destruct (pos) + clear_map (pos) +end + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + local spec = + "size[7.5,3]".. + "field[1,1;6,2;channel;Channel;${channel}]".. + "button_exit[2.5,2;3,1;submit;Set]" + local id = math.random (1000000) + + meta:set_string ("formspec", spec) + meta:set_int ("block_id", id) + + -- If return true no item is taken from itemstack + return false +end + + + +local function after_place_node_locked (pos, placer, itemstack, pointed_thing) + after_place_node (pos, placer, itemstack, pointed_thing) + + if placer and placer:is_player () then + local meta = minetest.get_meta (pos) + + meta:set_string ("owner", placer:get_player_name ()) + meta:set_string ("infotext", "Hologram (owned by "..placer:get_player_name ()..")") + end + + -- If return true no item is taken from itemstack + return false +end + + + +local function on_receive_fields (pos, formname, fields, sender) + if not utils.can_interact_with_node (pos, sender) then + return + end + + local meta = minetest.get_meta(pos) + + if fields.submit then + meta:set_string ("channel", fields.channel) + end +end + + + +local function on_blast (pos, intensity) + local meta = minetest.get_meta (pos) + + if meta then + if intensity >= 1.0 then + + clear_map (pos) + + minetest.remove_node (pos) + + else -- intensity < 1.0 + + clear_map (pos) + + local node = minetest.get_node_or_nil (pos) + if node then + local items = minetest.get_node_drops (node, nil) + + if items and #items > 0 then + local stack = ItemStack (items[1]) + + if stack then + preserve_metadata (pos, node, meta, { stack }) + utils.item_drop (stack, nil, pos) + minetest.remove_node (pos) + end + end + end + end + end +end + + + +local function can_dig (pos, player) + if not utils.can_interact_with_node (pos, player) then + return false + end + + return true +end + + + +local function on_rightclick (pos, node, clicker, itemstack, pointed_thing) + if not utils.can_interact_with_node (pos, clicker) then + if clicker and clicker:is_player () then + local owner = "" + local meta = minetest.get_meta (pos) + + if meta then + owner = meta:get_string ("owner") + end + + local spec = + "formspec_version[3]".. + "size[8.0,4.0,false]".. + "label[1.0,1.0;Owned by "..minetest.formspec_escape (owner).."]".. + "button_exit[3.0,2.0;2.0,1.0;close;Close]" + + minetest.show_formspec (clicker:get_player_name (), + "lwcomponents:component_privately_owned", + spec) + end + end + + return itemstack +end + + + +local function digilines_support () + if utils.digilines_supported then + return + { + wire = + { + rules = utils.digilines_default_rules, + }, + + effector = + { + action = function (pos, node, channel, msg) + local meta = minetest.get_meta(pos) + + if meta then + local this_channel = meta:get_string ("channel") + + if this_channel ~= "" and this_channel == channel then + if type (msg) == "string" then + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if m[1] == "clear" then + clear_map (pos) + end + + elseif type (msg) == "table" then + draw_map (pos, msg) + + end + end + end + end, + } + } + end + + return nil +end + + + +minetest.register_node("lwcomponents:hologram", { + description = S("Hologram"), + tiles = { "lwhologram.png", "lwhologram.png", "lwhologram.png", + "lwhologram.png", "lwhologram.png", "lwhologram_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_destruct = on_destruct, + after_place_node = after_place_node, + on_receive_fields = on_receive_fields, + on_blast = on_blast, + can_dig = can_dig, + on_rightclick = on_rightclick +}) + + + +minetest.register_node("lwcomponents:hologram_locked", { + description = S("Hologram (locked)"), + tiles = { "lwhologram.png", "lwhologram.png", "lwhologram.png", + "lwhologram.png", "lwhologram.png", "lwhologram_face.png"}, + is_ground_content = false, + groups = { cracky = 3 }, + sounds = default.node_sound_stone_defaults (), + paramtype = "light", + param1 = 0, + paramtype2 = "facedir", + param2 = 1, + floodable = false, + _digistuff_channelcopier_fieldname = "channel", + + digiline = digilines_support (), + + on_destruct = on_destruct, + after_place_node = after_place_node_locked, + on_receive_fields = on_receive_fields, + on_blast = on_blast, + can_dig = can_dig, + on_rightclick = on_rightclick +}) + + + +local function register_hologram_block (block) + local bc = hologram_block[block] + + minetest.register_node(bc.node, { + description = S("Hologram "..block), + tiles = { bc.image }, + drawtype = "glasslike", + light_source = 7, + use_texture_alpha = "blend", + sunlight_propagates = true, + walkable = false, + pointable = false, + diggable = false, + climbable = false, + buildable_to = true, + floodable = true, + is_ground_content = false, + groups = { not_in_creative_inventory = 1 }, + paramtype = "light", + param1 = 255, + post_effect_color = bc.color, + }) +end + + + +register_hologram_block ("black") +register_hologram_block ("orange") +register_hologram_block ("magenta") +register_hologram_block ("sky") +register_hologram_block ("yellow") +register_hologram_block ("pink") +register_hologram_block ("cyan") +register_hologram_block ("gray") +register_hologram_block ("silver") +register_hologram_block ("red") +register_hologram_block ("green") +register_hologram_block ("blue") +register_hologram_block ("brown") +register_hologram_block ("lime") +register_hologram_block ("purple") +register_hologram_block ("white") + + + +end -- utils.digilines_supported diff --git a/init.lua b/init.lua index 62b9594..b164c47 100644 --- a/init.lua +++ b/init.lua @@ -1,4 +1,4 @@ -local version = "0.1.5" +local version = "0.1.6" local mod_storage = minetest.get_mod_storage () @@ -25,6 +25,9 @@ loadfile (modpath.."/detector.lua") (utils) loadfile (modpath.."/siren.lua") (utils) loadfile (modpath.."/puncher.lua") (utils) loadfile (modpath.."/player_button.lua") (utils) +loadfile (modpath.."/hologram.lua") (utils) +loadfile (modpath.."/breaker.lua") (utils) +loadfile (modpath.."/fan.lua") (utils) loadfile (modpath.."/extras.lua") (utils) loadfile (modpath.."/digiswitch.lua") (utils) loadfile (modpath.."/movefloor.lua") (utils) diff --git a/license.txt b/license.txt index 051713c..92a7047 100644 --- a/license.txt +++ b/license.txt @@ -64,6 +64,9 @@ Media license siren images derived from images from https://openclipart.org, which is public domain. +fan images derived from images from https://openclipart.org, which is +public domain. + player button images derived from mesecons button image. All other media, or media not covered by a licence, is licensed diff --git a/puncher.lua b/puncher.lua index 962048b..0f07aef 100644 --- a/puncher.lua +++ b/puncher.lua @@ -421,7 +421,9 @@ local function digilines_support () if meta then local this_channel = meta:get_string ("channel") - if this_channel ~= "" and this_channel == channel then + if this_channel ~= "" and this_channel == channel and + type (msg) == "string" then + local m = { } for w in string.gmatch(msg, "[^%s]+") do m[#m + 1] = w diff --git a/readme.txt b/readme.txt index 9f7a56c..db5897a 100644 --- a/readme.txt +++ b/readme.txt @@ -13,7 +13,7 @@ CC BY-SA 3.0 Version ======= -0.1.5 +0.1.6 Minetest Version @@ -52,433 +52,28 @@ Description =========== Various components for mesecons and digilines. - - -Dropper -------- -* This block is only available if digilines and/or mesecons are loaded. - -Contains an inventory and drops an item on command. Also acts as a -digilines conductor. If the hopper mod is loaded, will take items from the -top and sides, and release them from the bottom. - -UI - -Channel - digilines channel of dropper. -Top 16 slot inventory - storage of items to drop. -Bottom 32 slot inventory - player's inventory. - -Mesecons - Drops the next item when power is turned on. - -Digilines messages - -"drop" - Drops the next item. No drop if dropper is empty. - -"drop " - Drops 1 item from the given slot (1 to 16). No drop if slot is empty. - eg. "drop 7" - -"drop " - Drops 1 item of the given name. No drop if dropper does not contain the - item. - eg. "drop default:stone" - -When an item is dropped a digilines message is sent with the dropper's -channel. The message is a table with the following keys: -{ - action = "drop", - name = "", -- name of dropped item - slot = -- slot number the item was taken from (1 to 16). -} - - - -Dispenser ---------- -* This block is only available if digilines and/or mesecons are loaded. - -Contains an inventory and dispenses (with velocity) an item on command. -Also acts as a digilines conductor. If the hopper mod is loaded, will take -items from the top and sides, and release them from the bottom. - -Dispensers support mobs mod if loaded and Spawn mobs setting is enabled. -Will spawn the entity from an 'egg' if possible, or the 'egg' is dispensed. -If a chicken egg is dispensed a 10% chance a chicken is dispensed instead. -If the spawned entity can be owned (or tamed) and the dispenser is owned -the owner of the dispenser is set as the owner of the entity. - -UI - -Channel - digilines channel of dispenser. -Top 16 slot inventory - storage of items to dispense. -Bottom 32 slot inventory - player's inventory. - -Mesecons - Dispenses the next item when power is turned on. - -Digilines messages - -"dispense" - Dispenses the next item. No dispense if dispenser is empty. - -"dispense " - Dispenses 1 item from the given slot (1 to 16). No dispense if slot is - empty. - eg. "dispense 7" - -"dispense " - Dispenses 1 item of the given name. No dispense if dispenser does not - contain the item. - eg. "dispense default:stone" - -When an item is dropped a digilines message is sent with the dropper's -channel. The message is a table with the following keys: -{ - action = "dispense", - name = "", -- name of dropped item - slot = -- slot number the item was taken from (1 to 16). -} - - - -Collector ---------- -* This block is only available if digilines is loaded. - -Picks up dropped items in adjacent block, with optional filtering. Also -acts as a digilines conductor. If the hopper mod is loaded, will take items -from the top and sides, and release them from the bottom. - -UI - -Channel - digilines channel of collector. -Left 16 slot inventory - storage of picked up items. -Right 8 slot inventory - Filter list. Place what items should be picked - up in this list. Leave empty to pick up all. -Bottom 32 slot inventory - player's inventory. - -Digilines messages - -"start" - Start the collector. - -"stop" - Stop the collector. - -When items are picked up a digilines message is sent with the collector's -channel. The message is a table with the following keys: -{ - action = "collect", - name = "", -- name of picked up items. - count = -- number of the item picked up. -} - - - -Detector --------- -* This block is only available if digilines and/or mesecons are loaded. - -Detects items or entities within a given radius. Also acts as a -digilines conductor. - -UI - -Channel - digilines channel of detector. -Radius - block distance from detector to detect. -Entities - if checked detects entities. -Players - if checked detects players. -Drops - if checked detects drops. -Nodes - if checked detects nodes. - -mode: - All - detects to radius in all directions, including diagonal. - Forward - detects to radius directly in front of the detector (one block high). - Up - detects to radius directly above the detector (one block wide). - Down - detects to radius directly below the detector (one block wide). - -Mesecons - Mesecons power is turned on when something is detected, and turned off - when nothing is detected. - -Digilines messages - -"start" - Start the detector. - -"stop" - Stop the detector. - -"radius " - Set radius of the detector. should be a number from 1 to 5, and is - trimmed to this range. - -"entities " - Set detection of entities on or off. - -"players " - Set detection of players on or off. - -"drops " - Set detection of drops on or off. - -"nodes " - Set detection of nodes on or off. - -"mode all" -"mode forward" -"mode up" -"mode down" - Set the detector's mode. - -When items or entities are detected a digilines message is sent with the -detector's channel. A message is sent for each found item/entity. The -message is a table with the following keys: -{ - action = "detect", - type = "", -- will be "entity", "player", "drop" or "node" - name = "", - label = "