From e4b3ae935d71e4301eba10da2c71ff7f6a8c0330 Mon Sep 17 00:00:00 2001 From: loosewheel <76670709+loosewheel@users.noreply.github.com> Date: Sun, 27 Feb 2022 10:03:39 +1000 Subject: [PATCH] Add files via upload --- cannon.lua | 2 +- cannon_shell.lua | 8 +- change.log | 9 + crafting.lua | 20 + detector.lua | 18 +- digiswitch.lua | 149 ++++++- explode.lua | 20 +- force_field.lua | 1052 ++++++++++++++++++++++++++++++++++++++++++++++ init.lua | 3 +- license.txt | 10 + readme.txt | 3 +- storage.lua | 7 +- utils.lua | 4 +- 13 files changed, 1253 insertions(+), 52 deletions(-) create mode 100644 force_field.lua diff --git a/cannon.lua b/cannon.lua index 99d8e64..0bdd509 100644 --- a/cannon.lua +++ b/cannon.lua @@ -15,7 +15,7 @@ local function get_cannon_barrel (pos) local barrel_pos = { x = pos.x, y = pos.y + 0.65, z = pos.z } local objects = minetest.get_objects_inside_radius (barrel_pos, 0.1) - for i = 1, #objects do + for i = 1, #objects, 1 do if not objects[i]:is_player () then if objects[i].get_luaentity and objects[i]:get_luaentity () and objects[i]:get_luaentity ().name and diff --git a/cannon_shell.lua b/cannon_shell.lua index 4da4f4c..f771169 100644 --- a/cannon_shell.lua +++ b/cannon_shell.lua @@ -153,8 +153,13 @@ local function register_shell (name, description, texture, inventory_image, end elseif c.type == "object" then + local c_name = (c.object.get_luaentity and + c.object:get_luaentity () and + c.object:get_luaentity ().name) or "" + local s_name = (self.name) or "" + -- explode at this pos - if c.object:get_armor_groups ().immortal then + if c.object:get_armor_groups ().immortal or s_name == c_name then self.object:set_velocity (c.old_velocity) else explode_pos = vector.new (c.object:get_pos ()) @@ -198,7 +203,6 @@ local function register_shell (name, description, texture, inventory_image, local obj = minetest.add_entity (spawn_pos, name.."_entity") if obj then - obj:set_armor_groups ({ immortal = 1 }) obj:set_acceleration ({ x = 0, y = -9.81, z = 0 }) obj:set_rotation (vector.dir_to_rotation (vector.multiply (spawner_dir, shell_speed))) obj:set_velocity (vector.multiply (spawner_dir, shell_speed)) diff --git a/change.log b/change.log index 7e93554..f4e9684 100644 --- a/change.log +++ b/change.log @@ -135,3 +135,12 @@ v0.1.23 * Fixed conduits sending items in groups. * Fixed player attached to controller moving forever when they get blown up. * Fixed pistons being powered from the pusher side. + + +v0.1.24 +* Limited requested count from storage indexer from digilines message to max stack. +* Fixed receptor state in detector. +* Fixed receptor state in digiswitch. +* Fixed bug in utils.is_same_item (). +* Added force field generators. +* Removed immortal from cannon shells (shells test for same type of shell and ignore). diff --git a/crafting.lua b/crafting.lua index 6a1d92a..7feaaa0 100644 --- a/crafting.lua +++ b/crafting.lua @@ -61,6 +61,26 @@ minetest.register_craft( { }) +minetest.register_craft( { + output = "lwcomponents:force_field", + recipe = { + { "default:steel_ingot", "default:mese_crystal", "group:wood" }, + { "default:mese_crystal", "default:diamondblock", "default:mese_crystal" }, + { "default:stone", "default:mese_crystal", "default:chest" } + }, +}) + + +minetest.register_craft( { + output = "lwcomponents:force_field_locked", + recipe = { + { "default:steel_ingot", "default:mese_crystal", "group:wood" }, + { "default:mese_crystal", "default:diamondblock", "default:mese_crystal" }, + { "default:stone", "default:mese_crystal", "default:chest_locked" } + }, +}) + + minetest.register_craft( { output = "lwcomponents:cannon_shell 10", recipe = { diff --git a/detector.lua b/detector.lua index 0dfca7b..372d376 100644 --- a/detector.lua +++ b/detector.lua @@ -18,6 +18,12 @@ local function mesecons_on (pos) if meta:get_int ("power_on") == 0 then utils.mesecon_receptor_on (pos, utils.mesecon_default_rules) meta:set_int ("power_on", 1) + local node = utils.get_far_node (pos) + + if node then + node.param1 = 1 + minetest.swap_node (pos, node) + end end end end @@ -31,6 +37,12 @@ local function mesecons_off (pos) if meta:get_int ("power_on") ~= 0 then utils.mesecon_receptor_off (pos, utils.mesecon_default_rules) meta:set_int ("power_on", 0) + local node = utils.get_far_node (pos) + + if node then + node.param1 = 0 + minetest.swap_node (pos, node) + end end end end @@ -692,8 +704,10 @@ local function mesecon_support () { receptor = { - state = utils.mesecon_state_off, - rules = utils.mesecon_default_rules + state = utils.mesecon_state_on, + rules = function (node) + return (node.param1 == 0 and { }) or utils.mesecon_default_rules + end } } end diff --git a/digiswitch.lua b/digiswitch.lua index 193e260..03c05e3 100644 --- a/digiswitch.lua +++ b/digiswitch.lua @@ -13,7 +13,7 @@ local function get_mesecon_rule_for_side (side) if side == "white" then return { { x = 0, y = 1, z = 0 } } elseif side == "black" then - return { { x = 0, y = -1, z = 0 } } -- down doesn't work + return { { x = 0, y = -1, z = 0 } } elseif side == "red" then return { { x = -1, y = 0, z = 0 } } elseif side == "green" then @@ -32,13 +32,106 @@ local function get_mesecon_rule_for_side (side) { x = 0, y = 0, z = 1 }, { x = 0, y = 0, z = -1 }, { x = 0, y = 1, z = 0 }, - { x = 0, y = -1, z = 0 }, -- down doesn't work + { x = 0, y = -1, z = 0 }, } end end +local side_bits = +{ + ["white"] = 0, + ["black"] = 1, + ["red"] = 2, + ["green"] = 3, + ["blue"] = 4, + ["yellow"] = 5 +} + + + +local function get_side_bit (side) + if side then + if side == "switch" then + return 64 + end + + local bit = side_bits[side] + + if bit then + return math.pow (2, bit) + end + end + + return 63 +end + + + +local function is_side_on (bits, side) + local bit = get_side_bit (side) + + for i = 0, 6, 1 do + if (bit % 2) == 1 and (bits % 2) ~= 1 then + return false + end + + bit = math.floor (bit / 2) + bits = math.floor (bits / 2) + end + + return true +end + + + +local function set_side_bit (bits, side, on) + local bit = get_side_bit (side) + local result = 0 + + for i = 0, 6, 1 do + if (bit % 2) == 1 then + if on then + result = result + math.pow (2, i) + end + elseif (bits % 2) == 1 then + result = result + math.pow (2, i) + end + + bit = math.floor (bit / 2) + bits = math.floor (bits / 2) + end + + return result +end + + + +local function switch_on (pos, side) + utils.mesecon_receptor_on (pos, get_mesecon_rule_for_side (side)) + + local node = utils.get_far_node (pos) + if node then + node.param1 = set_side_bit (node.param1, side, true) + minetest.swap_node (pos, node) + end +end + + + +local function switch_off (pos, side) + utils.mesecon_receptor_off (pos, get_mesecon_rule_for_side (side)) + + local node = utils.get_far_node (pos) + if node then + node.param1 = set_side_bit (node.param1, side, false) + minetest.swap_node (pos, node) + end +end + + + local function digilines_support () return { @@ -80,9 +173,9 @@ local function digilines_support () end if words[1] == "on" then - utils.mesecon_receptor_on (pos, get_mesecon_rule_for_side (words[2])) + switch_on (pos, words[2]) elseif words[1] == "off" then - utils.mesecon_receptor_off (pos, get_mesecon_rule_for_side (words[2])) + switch_off (pos, words[2]) end end end @@ -99,16 +192,32 @@ local function mesecon_support () { receptor = { - state = mesecon.state.off, - rules = - { - { x = 1, y = 0, z = 0 }, - { x = -1, y = 0, z = 0 }, - { x = 0, y = 0, z = 1 }, - { x = 0, y = 0, z = -1 }, - { x = 0, y = 1, z = 0 }, - { x = 0, y = -1, z = 0 }, -- down doesn't work - } + state = mesecon.state.on, + + rules = function (node) + if is_side_on (node.param1, "switch") then + return utils.mesecon_default_rules + end + + local r = { } + local sides = + { + "white", + "black", + "red", + "green", + "blue", + "yellow", + } + + for _, side in ipairs (sides) do + if is_side_on (node.param1, side) then + r[#r + 1] = get_mesecon_rule_for_side (side)[1] + end + end + + return r + end }, } end @@ -124,7 +233,7 @@ local function on_construct (pos) local formspec = "formspec_version[3]\n".. "size[6.0,4.0]\n".. - "field[1.0,0.8;4.0,1.0;channel;Channel;]\n".. + "field[1.0,0.8;4.0,1.0;channel;Channel;${channel}]\n".. "button_exit[2.0,2.5;2.0,1.0;set;Set]\n" meta:set_string ("formspec", formspec) @@ -145,14 +254,6 @@ local function on_receive_fields (pos, formname, fields, sender) if meta then meta:set_string ("channel", fields.channel or "") - - local formspec = - "formspec_version[3]\n".. - "size[6.0,4.0]\n".. - "field[1.0,0.8;4.0,1.0;channel;Channel;"..minetest.formspec_escape (meta:get_string ("channel")).."]\n".. - "button_exit[2.0,2.5;2.0,1.0;set;Set]\n" - - meta:set_string ("formspec", formspec) end end end @@ -172,6 +273,8 @@ minetest.register_node ("lwcomponents:digiswitch", { {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, } }, + paramtype = "none", + param1 = 0, groups = { cracky = 2, oddly_breakable_by_hand = 2 }, sounds = default.node_sound_stone_defaults (), mesecons = mesecon_support (), diff --git a/explode.lua b/explode.lua index 971ac28..e43b908 100644 --- a/explode.lua +++ b/explode.lua @@ -70,24 +70,6 @@ end ---local function is_same_item (stack1, stack2) - --local copy1 = ItemStack (stack1) - --local copy2 = ItemStack (stack2) - - --if copy1 and copy2 then - --copy1:set_count (1) - --copy2:set_count (1) - - --if copy1:to_string () == copy2:to_string () then - --return true - --end - --end - - --return false ---end - - - local function dig_node (pos, toolname) local node = utils.get_far_node (pos) local dig = false @@ -335,7 +317,7 @@ local function add_effects (pos, radius, drops) collisiondetection = false, vertical = false, texture = "lwcomponents_boom.png", - glow = 15, + glow = 14, }) minetest.add_particlespawner ({ diff --git a/force_field.lua b/force_field.lua new file mode 100644 index 0000000..c725dfe --- /dev/null +++ b/force_field.lua @@ -0,0 +1,1052 @@ +local utils = ... +local S = utils.S + + + +local field_pulse_interval = 0.2 + + + +local function get_bubble (pos) + local objs = minetest.get_objects_inside_radius (pos, 0.1) + + for _, obj in ipairs (objs) do + if not obj:is_player () and + obj.get_luaentity and obj:get_luaentity () and + obj:get_luaentity ().name and + obj:get_luaentity ().name == "lwcomponents:force_field_bubble" then + + return obj + end + end + + return nil +end + + + +local function spawn_glitter (pos) + local meta = minetest.get_meta (pos) + + if meta then + local radius = tonumber (meta:get_string ("radius") or 5) + + minetest.add_particlespawner ({ + amount = radius * 5, + time = 0.5, + minpos = vector.subtract (pos, radius * 0.707), + maxpos = vector.add (pos, radius * 0.707), + minvel = vector.new ({ x = -1, y = -1, z = -1 }), + maxvel = vector.new ({ x = 1, y = 1, z = 1 }), + minacc = vector.new (), + maxacc = vector.new (), + minexptime = 0.2, + maxexptime = 0.5, + minsize = 3, + maxsize = 6, + glow = 14, + texture = "lwcomponents_force_field_zap_1.png", + }) + + minetest.add_particlespawner ({ + amount = radius * 5, + time = 0.5, + minpos = vector.subtract (pos, radius * 0.707), + maxpos = vector.add (pos, radius * 0.707), + minvel = vector.new ({ x = -1, y = -1, z = -1 }), + maxvel = vector.new ({ x = 1, y = 1, z = 1 }), + minacc = vector.new (), + maxacc = vector.new (), + minexptime = 0.2, + maxexptime = 0.5, + minsize = 3, + maxsize = 6, + glow = 14, + texture = "lwcomponents_force_field_zap_2.png", + }) + end +end + + + +local function update_bubble (pos) + local meta = minetest.get_meta (pos) + local node = utils.get_far_node (pos) + + if meta and node and (node.name == "lwcomponents:force_field_on" or + node.name == "lwcomponents:force_field_locked_on") then + local radius = tonumber (meta:get_string ("radius") or 10) + local bubble = get_bubble (pos) + + if not bubble then + local staticdata = { } + + bubble = minetest.add_entity (pos, + "lwcomponents:force_field_bubble", + minetest.serialize (staticdata)) + end + + if bubble then + local props = bubble:get_properties () + props.visual_size = { x = radius * 2, y = radius * 2, z = radius * 2 } + bubble:set_properties (props) + bubble:set_armor_groups ({ immortal = 1 }) + end + end +end + + + +local function remove_bubble (pos) + local bubble = get_bubble (pos) + + if bubble then + bubble:remove () + end +end + + + +local function check_fuel (pos, used) + local meta = minetest.get_meta (pos) + + if meta then + local power = meta:get_float ("power") + + if used > power then + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + while power < used do + local fuel, afterfuel = + minetest.get_craft_result ({ method = "fuel", + width = 1, + items = inv:get_list ("fuel") }) + + if fuel.time > 0 then + -- Take fuel from fuel list + inv:set_stack ("fuel", 1, afterfuel.items[1]) + power = power + fuel.time + else + -- No valid fuel in fuel list + break + end + end + end + end + + if used > power then + power = 0 + meta:set_float ("power", 0) + + return false + else + meta:set_float ("power", power - used) + end + end + + return true +end + + + +local function update_formspec (pos) + local meta = minetest.get_meta (pos) + + if meta then + local stopstart = minetest.get_node_timer (pos):is_started () and + "button[8.7,1.0;2.0,0.8;stop;Stop]" or + "button[8.7,1.0;2.0,0.8;start;Start]" + + local spec = + "formspec_version[3]".. + "size[11.7,12.8;true]".. + "field[1.0,1.0;4.0,0.8;channel;Channel;${channel}]".. + "button[5.0,1.0;2.0,0.8;setchannel;Set]".. + stopstart.. + "field[1.0,2.5;4.0,0.8;radius;Radius;${radius}]".. + "button[5.0,2.5;2.0,0.8;setradius;Set]".. + "textarea[1.0,4.0;6.5,2.4;exclude;Permit;${exclude}]".. + "button[7.5,4.0;2.0,0.8;setexclude;Set]".. + "list[context;fuel;9.2,2.5;1,1;]".. + "list[current_player;main;1.0,7.0;8,4;]\n".. + "listring[]" + + meta:set_string ("formspec", spec) + end +end + + + +local function turn_on (pos) + local node = utils.get_far_node (pos) + + if node then + if node.name == "lwcomponents:force_field" then + if check_fuel (pos, 0.001) then + node.name = "lwcomponents:force_field_on" + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (field_pulse_interval) + update_formspec (pos) + update_bubble (pos) + spawn_glitter (pos) + end + + elseif node.name == "lwcomponents:force_field_locked" then + if check_fuel (pos, 0.001) then + node.name = "lwcomponents:force_field_locked_on" + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):start (field_pulse_interval) + update_formspec (pos) + update_bubble (pos) + spawn_glitter (pos) + end + end + end +end + + + +local function turn_off (pos) + local node = utils.get_far_node (pos) + + if node then + if node.name == "lwcomponents:force_field_on" then + remove_bubble (pos) + node.name = "lwcomponents:force_field" + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):stop () + update_formspec (pos) + spawn_glitter (pos) + + elseif node.name == "lwcomponents:force_field_locked_on" then + remove_bubble (pos) + node.name = "lwcomponents:force_field_locked" + minetest.swap_node (pos, node) + minetest.get_node_timer (pos):stop () + update_formspec (pos) + spawn_glitter (pos) + end + end +end + + + +local function is_drop (obj) + if obj then + local entity = obj.get_luaentity and obj:get_luaentity () + + return (entity and entity.name and entity.name == "__builtin:item") + end + + return false +end + + + +local function run_zap (pos) + minetest.sound_play ("lwforce_field_zap", { pos = pos, max_hear_distance = 10, gain = 1.0 }) + + minetest.add_particle ({ + pos = pos, + velocity = vector.new (), + acceleration = vector.new (), + expirationtime = 0.1, + size = 10, + collisiondetection = false, + vertical = false, + texture = (math.random (10) < 6 and "lwcomponents_force_field_zap_1.png") or + "lwcomponents_force_field_zap_2.png", + glow = 14, + }) +end + + + +local function run_field (pos, elapsed) + local meta = minetest.get_meta (pos) + + if meta then + local radius = tonumber (meta:get_string ("radius") or 10) + local exclude = { } + local count = 0 + local owner = meta:get_string ("owner") + + update_bubble (pos) + + for player in string.gmatch (meta:get_string ("exclude"), "[^\n]+") do + if player and player ~= "" then + exclude[player] = true + end + end + + if owner ~= "" then + exclude[owner] = true + end + + local objs = minetest.get_objects_inside_radius (pos, radius + 1) + + for _, obj in ipairs (objs) do + local obj_pos = (obj.get_pos and obj:get_pos ()) or nil + + if obj_pos and not obj:get_armor_groups ().immortal then + if not is_drop (obj) and not minetest.is_protected (obj_pos, owner) then + + if obj:is_player () then + if not exclude[obj:get_player_name ()] then + local vel = vector.multiply (vector.direction (pos, obj_pos), 30) + + obj:punch (obj, + 1.0, + { full_punch_interval = 1.0, + damage_groups = { fleshy = 1 } }, + vector.direction (pos, obj_pos)) + + obj:add_velocity (vel) + count = count + 1 + run_zap (obj_pos) + end + else + local luaent = (obj.get_luaentity and obj:get_luaentity ()) or nil + local name = obj:get_nametag_attributes () + local label = "" + + if type (name) == "table" then + label = tostring (name.text or "") + end + + name = (luaent and luaent.name) or "" + + if (name == "" and label == "") or + ((not (name ~= "" and exclude[name])) and + (not (label ~= "" and exclude[label]))) then + + local vel = vector.multiply (vector.direction (pos, obj_pos), 30) + + obj:punch (obj, + 1.0, + { full_punch_interval = 1.0, + damage_groups = { fleshy = 2 } }, + vector.direction (pos, obj_pos)) + + obj:set_velocity (vel) + count = count + 1 + run_zap (obj_pos) + end + end + end + end + end + + if not check_fuel (pos, ((radius * 0.16) * elapsed) + count) then + turn_off (pos) + + return false + end + end + + return true +end + + + +local function set_radius (pos, radius) + local meta = minetest.get_meta (pos) + + if meta then + radius = math.min (math.max (tonumber (radius) or 5, 5), 25) + + meta:set_string ("radius", tostring (radius)) + update_bubble (pos) + end +end + + + +local function add_exclude (pos, name) + local meta = minetest.get_meta (pos) + name = tostring (name or "") + + if meta and name ~= "" then + local exclude = { } + + for player in string.gmatch (meta:get_string ("exclude"), "[^\n]+") do + if player and player ~= "" then + if player == name then + return + end + + exclude[#exclude + 1] = player + end + end + + exclude[#exclude + 1] = name + + meta:set_string ("exclude", table.concat (exclude, "\n")) + end +end + + + +local function remove_exclude (pos, name) + local meta = minetest.get_meta (pos) + name = tostring (name or "") + + if meta and name ~= "" then + local exclude = { } + + for player in string.gmatch (meta:get_string ("exclude"), "[^\n]+") do + if player and player ~= "" and player ~= name then + exclude[#exclude + 1] = player + end + end + + meta:set_string ("exclude", table.concat (exclude, "\n")) + end +end + + + +local function send_status_message (pos) + if utils.digilines_supported then + local node = utils.get_far_node (pos) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if node and meta and inv then + local channel = meta:get_string ("channel") + local state = "off" + local exclude = { } + local radius = tonumber (meta:get_string ("radius")) + local fuel = { name = "", count = 0 } + + if node.name == "lwcomponents:force_field_on" or + node.name == "lwcomponents:force_field_locked_on" then + + state = "on" + end + + for player in string.gmatch (meta:get_string ("exclude"), "[^\n]+") do + if player and player ~= "" then + exclude[#exclude + 1] = player + end + end + + local stack = inv:get_stack ("fuel", 1) + if stack and not stack:is_empty () then + fuel.name = stack:get_name () + fuel.count = stack:get_count () + end + + if channel:len () > 0 then + utils.digilines_receptor_send (pos, + utils.digilines_default_rules, + channel, + { action = "status", + state = state, + radius = radius, + permit = exclude, + fuel = fuel }) + end + end + end +end + + + +local function on_destruct (pos) + remove_bubble (pos) +end + + + +local function after_place_node (pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta (pos) + + meta:set_string ("radius", "10") + meta:set_string ("exclude", "") + meta:set_string ("inventory", "{ fuel = { } }") + update_formspec (pos) + + local inv = meta:get_inventory () + + inv:set_size ("fuel", 1) + inv:set_width ("fuel", 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", "Force Field Generator (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 + + if fields.setradius then + local meta = minetest.get_meta (pos) + + if meta then + local radius = math.min (math.max (tonumber (fields.radius) or 5, 5), 25) + fields.radius = tostring (radius) + + meta:set_string ("radius", tostring (radius)) + update_bubble (pos) + end + end + + if fields.setexclude then + local meta = minetest.get_meta (pos) + + if meta then + meta:set_string ("exclude", fields.exclude) + end + end + + if fields.start then + turn_on (pos) + end + + if fields.stop then + turn_off (pos) + 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 ("fuel") then + return false + end + end + end + + return true +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 ("fuel") + + for slot = 1, slots do + local stack = inv:get_stack ("fuel", 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 + + on_destruct (pos) + minetest.remove_node (pos) + + else -- intensity < 1.0 + local inv = meta:get_inventory () + + if inv then + local slots = inv:get_size ("fuel") + + for slot = 1, slots do + local stack = inv:get_stack ("fuel", 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) + on_destruct (pos) + minetest.remove_node (pos) + end + end + end + end + end +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) + local result = run_field (pos, elapsed) + + return result +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 ~= "" then + if type (msg) == "string" then + local m = { } + for w in string.gmatch(msg, "[^%s]+") do + m[#m + 1] = w + end + + if this_channel == channel then + if m[1] == "start" then + turn_on (pos) + + elseif m[1] == "stop" then + turn_off (pos) + + elseif m[1] == "radius" then + set_radius (pos, m[2]) + + elseif m[1] == "add" then + add_exclude (pos, msg:sub (5, -1)) + + elseif m[1] == "remove" then + remove_exclude (pos, msg:sub (8, -1)) + + elseif m[1] == "status" then + send_status_message (pos) + + end + 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 + turn_on (pos) + end, + + action_off = function (pos, node) + -- do something to turn the effector off + turn_off (pos) + end, + } + } + end + + return nil +end + + + +local function pipeworks_support () + if utils.pipeworks_supported then + return + { + priority = 100, + input_inventory = "fuel", + connect_sides = { left = 1, right = 1, front = 1, back = 1, bottom = 1 }, + + insert_object = function (pos, node, stack, direction) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + return inv:add_item ("fuel", stack) + end + + return stack + end, + + can_insert = function (pos, node, stack, direction) + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + return inv:room_for_item ("fuel", stack) + end + + return false + end, + + can_remove = function (pos, node, stack, dir) + -- returns the maximum number of items of that stack that can be removed + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + + if inv then + local slots = inv:get_size ("fuel") + + for i = 1, slots, 1 do + local s = inv:get_stack ("fuel", i) + + if s and not s:is_empty () and utils.is_same_item (stack, s) then + return s:get_count () + end + end + end + + return 0 + end, + + remove_items = function (pos, node, stack, dir, count) + -- removes count items and returns them + local meta = minetest.get_meta (pos) + local inv = (meta and meta:get_inventory ()) or nil + local left = count + + if inv then + local slots = inv:get_size ("fuel") + + for i = 1, slots, 1 do + local s = inv:get_stack ("fuel", i) + + if s and not s:is_empty () and utils.is_same_item (s, stack) then + if s:get_count () > left then + s:set_count (s:get_count () - left) + inv:set_stack ("fuel", i, s) + left = 0 + else + left = left - s:get_count () + inv:set_stack ("fuel", i, nil) + end + end + + if left == 0 then + break + end + end + end + + local result = ItemStack (stack) + result:set_count (count - left) + + return result + end + } + end + + return nil +end + + + +local force_field_groups = { cracky = 3 } +if utils.pipeworks_supported then + force_field_groups.tubedevice = 1 + force_field_groups.tubedevice_receiver = 1 +end + + + +local force_field_on_groups = { cracky = 3, not_in_creative_inventory = 1 } +if utils.pipeworks_supported then + force_field_on_groups.tubedevice = 1 + force_field_on_groups.tubedevice_receiver = 1 +end + + + +minetest.register_node("lwcomponents:force_field", { + description = S("Force Field Generator"), + tiles = { "lwcomponents_force_field.png", "lwcomponents_force_field.png", "lwcomponents_force_field.png", + "lwcomponents_force_field.png", "lwcomponents_force_field.png", "lwcomponents_force_field.png"}, + is_ground_content = false, + groups = table.copy (force_field_groups), + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + drop = "lwcomponents:force_field", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_rightclick = on_rightclick, + on_timer = on_timer +}) + + + +minetest.register_node("lwcomponents:force_field_locked", { + description = S("Force Field Generator (locked)"), + tiles = { "lwcomponents_force_field.png", "lwcomponents_force_field.png", "lwcomponents_force_field.png", + "lwcomponents_force_field.png", "lwcomponents_force_field.png", "lwcomponents_force_field.png"}, + is_ground_content = false, + groups = table.copy (force_field_groups), + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + floodable = false, + drop = "lwcomponents:force_field_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + 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, + on_timer = on_timer +}) + + + +minetest.register_node("lwcomponents:force_field_on", { + description = S("Force Field Generator"), + tiles = { "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png", + "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png"}, + is_ground_content = false, + groups = table.copy (force_field_on_groups), + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + light_source = 3, + floodable = false, + drop = "lwcomponents:force_field", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + on_receive_fields = on_receive_fields, + can_dig = can_dig, + after_place_node = after_place_node, + on_blast = on_blast, + on_rightclick = on_rightclick, + on_timer = on_timer +}) + + + +minetest.register_node("lwcomponents:force_field_locked_on", { + description = S("Force Field Generator (locked)"), + tiles = { "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png", + "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png", "lwcomponents_force_field_on.png"}, + is_ground_content = false, + groups = table.copy (force_field_on_groups), + sounds = default.node_sound_stone_defaults (), + paramtype = "none", + param1 = 0, + paramtype2 = "none", + param2 = 0, + light_source = 3, + floodable = false, + drop = "lwcomponents:force_field_locked", + _digistuff_channelcopier_fieldname = "channel", + + mesecons = mesecon_support (), + digiline = digilines_support (), + tube = pipeworks_support (), + + on_destruct = on_destruct, + 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, + on_timer = on_timer +}) + + + +minetest.register_entity ("lwcomponents:force_field_bubble", { + initial_properties = { + physical = false, + collide_with_objects = false, + collisionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, + selectionbox = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }, + pointable = false, + visual = "mesh", + visual_size = { x = 1, y = 1, z = 1 }, + mesh = "lwcomponents_force_field_bubble.obj", + textures = { "lwcomponents_force_field_bubble.png" }, + use_texture_alpha = true, + is_visible = true, + makes_footstep_sound = false, + automatic_rotate = 0.35, + backface_culling = false, + damage_texture_modifier = "", + glow = 14, + static_save = true, + shaded = true, + show_on_minimap = false, + }, + + on_activate = function (self, staticdata, dtime_s) + self.staticdata = staticdata + end, + + get_staticdata = function (self) + return self.staticdata + end, + + on_step = function (self, dtime, moveresult) + end, + + on_punch = function (self, puncher, time_from_last_punch, tool_capabilities, dir) + return true + end, + + on_blast = function (self, damage) + return false, false, nil + end, +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:force_field", "fuel"}, -- take items from above into hopper below + {"bottom", "lwcomponents:force_field", "fuel"}, -- insert items below from hopper above + {"side", "lwcomponents:force_field", "fuel"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:force_field_locked", "fuel"}, -- take items from above into hopper below + {"bottom", "lwcomponents:force_field_locked", "fuel"}, -- insert items below from hopper above + {"side", "lwcomponents:force_field_locked", "fuel"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:force_field_on", "fuel"}, -- take items from above into hopper below + {"bottom", "lwcomponents:force_field_on", "fuel"}, -- insert items below from hopper above + {"side", "lwcomponents:force_field_on", "fuel"}, -- insert items from hopper at side +}) + + + +utils.hopper_add_container({ + {"top", "lwcomponents:force_field_locked_on", "fuel"}, -- take items from above into hopper below + {"bottom", "lwcomponents:force_field_locked_on", "fuel"}, -- insert items below from hopper above + {"side", "lwcomponents:force_field_locked_on", "fuel"}, -- insert items from hopper at side +}) + + + +-- diff --git a/init.lua b/init.lua index 99fa989..d5b4e48 100644 --- a/init.lua +++ b/init.lua @@ -1,4 +1,4 @@ -local version = "0.1.23" +local version = "0.1.24" local mod_storage = minetest.get_mod_storage () @@ -38,6 +38,7 @@ loadfile (modpath.."/pistons.lua") (utils) loadfile (modpath.."/through_wire.lua") (utils) loadfile (modpath.."/camera.lua") (utils) loadfile (modpath.."/storage.lua") (utils) +loadfile (modpath.."/force_field.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 39dbd48..0071bb8 100644 --- a/license.txt +++ b/license.txt @@ -15,6 +15,8 @@ https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html Mesecons through wire code was adapted from mesecons_receiver. +Fragments of code were gleaned from tnt mod. + lwsiren-buzz.ogg @@ -70,6 +72,14 @@ sound effects free of charge and royalty free in your multimedia projects for commercial or non-commercial purposes. +lwforce_field_zap.ogg +--------------------- +https://orangefreesounds.com/electricity-zap/ + +Licence: The sound effect is permitted for non-commercial use under license +Attribution-NonCommercial 4.0 International (CC BY-NC 4.0) + + Media license ------------- diff --git a/readme.txt b/readme.txt index f96d00a..76bbc6b 100644 --- a/readme.txt +++ b/readme.txt @@ -13,7 +13,7 @@ CC BY-SA 3.0 Version ======= -0.1.23 +0.1.24 Minetest Version @@ -69,6 +69,7 @@ Various components for mesecons and digilines. * Movefloor, similar to vertical mesecons movestone. * Camera, takes a representative image. * Storage, indexed storage units. +* Force Field Generator, repels players and mobs within a radius. * Mesecons Through Wire, transmits through 1 to 2 solid blocks. * Solid color conductor blocks, same as Solid Color Block but also mesecons and digilines conductor. diff --git a/storage.lua b/storage.lua index a9e6688..a599f7e 100644 --- a/storage.lua +++ b/storage.lua @@ -389,6 +389,11 @@ local function output_items (pos, name, count) end local stack = ItemStack (name) + + if stack:get_stack_max () < count then + count = stack:get_stack_max () + end + stack:set_count (count) while stack:get_count () > 0 do @@ -738,7 +743,7 @@ local function get_formspec_list (pos) } end - local foo = table.sort (list , function (e1, e2) + table.sort (list , function (e1, e2) return (e1.description:lower () < e2.description:lower ()) end) diff --git a/utils.lua b/utils.lua index c765378..e38f187 100644 --- a/utils.lua +++ b/utils.lua @@ -226,8 +226,8 @@ end function utils.is_same_item (item1, item2) - local copy1 = ItemStack (stack1) - local copy2 = ItemStack (stack2) + local copy1 = ItemStack (item1) + local copy2 = ItemStack (item2) if copy1 and copy2 then copy1:set_count (1)