commit 43d60e2bccb2b0580da32d43629059e2ecad7007 Author: Juraj Vajda Date: Mon Jan 22 00:46:07 2018 -0500 initial commit - Telemosaic by SaKeL - alpha diff --git a/README.md b/README.md new file mode 100644 index 0000000..6dee877 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +Telemosaic [telemosaic] +======================= + +A Minetest mod for user-generated teleportation pads. + +Version: 0.5.0 + +License: + Code: LGPL 2.1 (see included LICENSE file) + Textures: CC-BY-SA (see http://creativecommons.org/licenses/by-sa/4.0/) + +Report bugs or request help on the forum topic. + +Description +----------- + +This is a mod for MineTest. It provides teleportation pads, called +"beacons". Unlike other teleportation mods, no menus or GUIs are used; +you set the destination with a simple "key" item. There is no +tooltip for the destination either, so signs are recommended. + +Another difference is the limited default range of the beacons. +To increase the range, you need to place "extenders" around the beacon. +The extenders come in different colors, allowing the extenders to +form a pretty pattern; hence the name "telemosaic". + +Current behavior +---------------- + +Beacons are created with 2 diamonds, 3 obsidian blocks, and a wooden +door: first row diamond, door, diamond; second row the obsidian blocks. + +Right-clicking a beacon with a default mese crystal fragment remembers +the position in the fragment, which turns into a telemosaic key. +Right-clicking a second beacon with the key sets up a teleportation +route from the second beacon to the first beacon. To set up a return +path, right-click the second beacon with the fragment, and the first +beacon with the resulting key again. + +The beacons do not need to be strictly paired this way: rings or +star-shaped networks are also possible. Each beacon has only a +single destination, but can itself be the destination of several +others. + +Beacons will check that their destination is sane: the destination +still needs to be a beacon, and the two nodes above it should be +clear for walking / standing in. If your Minetest version supports +it, the beacon will emerge the area prior to checking and teleporting. +Emerging is merely a convenience, though. + +Beacons have a maximum range of 20 nodes. If the destination is +too far away, the beacon will turn red and will not function. +To extend the range for a beacon, place "extenders" next to it, +within a 7x7 horizontal square centered on the beacon. + +Extenders come in three tiers: tier 1 extends all affected beacons +by 5 nodes, tier 2 by 20 nodes, and tier 3 by 80 nodes. Placing +or digging extenders will update affected beacons. + +Tier 1 extenders are crafted by placing an obsidian block, a wooden +door, and another obsidian block in a horizontal row. Tier 2 extenders +are crafted with an obsidian block in the middle, surrounded by a cross +of four tier 1 extenders. Tier 3 extenders are crafted with an obsidian +block surrounded by four tier 2 extenders. + +Extenders can be colored with any of the dyes found in the dye mod. +Colored extenders work just like regular extenders, both for +teleporting and for recipes. To "uncolor" an extender, dye it grey. + +Future plans +------------ + +* Particle and sound effects +* Protected beacons (will not teleport if protected) + +Dependencies +------------ +* default +* doors +* dye (optional) + +Installation +------------ + +Unzip the archive, rename the folder to to `bewarethedark` and +place it in minetest/mods/ + +( Linux: If you have a linux system-wide installation place + it in ~/.minetest/mods/. ) + +( If you only want this to be used in a single world, place + the folder in worldmods/ in your worlddirectory. ) + +For further information or help see: +http://wiki.minetest.com/wiki/Installing_Mods diff --git a/_material/nodes.xcf b/_material/nodes.xcf new file mode 100644 index 0000000..f8e80b3 Binary files /dev/null and b/_material/nodes.xcf differ diff --git a/api.lua b/api.lua new file mode 100644 index 0000000..7ee4907 --- /dev/null +++ b/api.lua @@ -0,0 +1,419 @@ +local enable_particles = minetest.settings:get_bool("enable_particles") + +telemosaic = {} +telemosaic.extender_ranges = { + -- not adding beacons here, since they don"t extend + ["telemosaic:extender_one"] = 5, + ["telemosaic:extender_two"] = 20, + ["telemosaic:extender_three"] = 80 +} +telemosaic.strengths = { "one", "two", "three" } + +function telemosaic.effect_departure(pos) + minetest.sound_play("telemosaic_set", { + pos = pos, + max_hear_distance = 8, + gain = 1 + }) + + if not enable_particles then return end + + minetest.add_particlespawner({ + amount = 100, + time = 0.25, + minpos = {x=pos.x, y=pos.y+0.3, z=pos.z}, + maxpos = {x=pos.x, y=pos.y+2, z=pos.z}, + minvel = {x = 1, y = -6, z = 1}, + maxvel = {x = -1, y = -1, z = -1}, + minacc = {x = 0, y = -2, z = 0}, + maxacc = {x = 0, y = -6, z = 0}, + minexptime = 0.1, + maxexptime = 1, + minsize = 0.5, + maxsize = 1.5, + texture = "telemosaic_particle_departure.png^[transform"..math.random(0,3), + glow = 15, + }) +end + +function telemosaic.effect_arrival(pos) + minetest.sound_play("telemosaic_teleport", { + pos = pos, + max_hear_distance = 8, + gain = 1 + }) + + if not enable_particles then return end + + minetest.add_particlespawner({ + amount = 100, + time = 0.25, + minpos = {x=pos.x, y=pos.y+0.3, z=pos.z}, + maxpos = {x=pos.x, y=pos.y+2, z=pos.z}, + minvel = {x = -1, y = 1, z = -1}, + maxvel = {x = 1, y = 6, z = 1}, + minacc = {x = 0, y = -2, z = 0}, + maxacc = {x = 0, y = -6, z = 0}, + minexptime = 0.1, + maxexptime = 1, + minsize = 0.5, + maxsize = 1.5, + texture = "telemosaic_particle_arrival.png^[transform"..math.random(0,3), + glow = 15, + }) +end + +function telemosaic.get_formspec(pos, table) + local meta = minetest.get_meta(pos) + local arrivals_tbl = meta:get_string("arrivals_tbl") + local pos2_str = meta:get_string("dest_pos") + local pos2 = minetest.string_to_pos(pos2_str) + arrivals_tbl = minetest.deserialize(arrivals_tbl) + if not arrivals_tbl then arrivals_tbl = {} end + local range = table.range or 0 + local bname = meta:get_string("bname") + if not bname or bname == "" then + bname = "beacon at: "..minetest.formspec_escape(minetest.pos_to_string(pos)) + end + + local textlist = "" + for ipos, ival in pairs(arrivals_tbl) do + textlist = textlist..minetest.formspec_escape(ipos)..": "..ival.."," + end + + local button_teleport = "" + + local pos2_colorize = minetest.colorize("#ff0000", pos2_str) + + if pos2 then + local meta2 = minetest.get_meta(pos2) + local bname2 = meta2:get_string("bname") + pos2_colorize = minetest.colorize("#00ff00", bname2) + button_teleport = "button_exit[0.1,7.5;2.4,1.5;teleport;Teleport Now]" + end + + -- dynamic formspec + local formspec = "size[12,8.5]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "label[0,0.5;beacon position: "..minetest.pos_to_string(pos).."]".. + "label[0,1;destinaton: "..pos2_colorize.."]".. + "label[0,1.5;range: "..range.." blocks]".. + "field[0.34,2.7;4,1;bname;beacon name:;"..bname.."]".. + button_teleport.. + "button_exit[2.5,7.5;2.4,1.5;saveandclose;Save & Close]".. + "label[5,0;Arrivals from ( x | y | z ):]".. + "textlist[5,0.5;6.7,8;arrivals;"..textlist..";1;false]" + + return formspec +end + +minetest.register_on_player_receive_fields(function(player, formname, fields) + local mod_pos_pos2 = formname:split(":") + + if fields.bname and mod_pos_pos2[1] == "telemosaic" then + local bname = fields.bname + local pos = minetest.string_to_pos(mod_pos_pos2[2]) + local meta = minetest.get_meta(pos) + local pos2 = minetest.string_to_pos(mod_pos_pos2[3]) + + meta:set_string("bname", bname) + if pos2 then + local meta2 = minetest.get_meta(pos2) + local arrivals_tbl2 = meta2:get_string("arrivals_tbl") + arrivals_tbl2 = minetest.deserialize(arrivals_tbl2) + if not arrivals_tbl2 then arrivals_tbl2 = {} end + + arrivals_tbl2[minetest.pos_to_string(pos)] = minetest.formspec_escape(bname) + meta2:set_string("arrivals_tbl", minetest.serialize(arrivals_tbl2)) + end + + if fields.teleport then + local range = meta:get_int("range") + + if pos2 then + if math.floor(vector.distance(pos, pos2)) <= range then + pos2 = {x = pos2.x, y = pos2.y + 1, z = pos2.z} + + -- attempt to emerge the target area before the player gets there + local vpos = vector.new(pos2) + minetest.get_voxel_manip():read_from_map(vpos, vpos) + if not minetest.get_node_or_nil(pos2) then + print("emerge area") + minetest.emerge_area(vector.subtract(vpos, 80), vector.add(vpos, 80)) + end + + minetest.after(5, function() + player:setpos(pos2) + telemosaic.effect_arrival(pos2) + telemosaic.effect_departure(pos) + end) + else + minetest.chat_send_player(player:get_player_name(), "Not enough power, destination is too far. Beacon needs "..math.floor(vector.distance(pos, pos2)) - range.." more power.") + end + end + end + + end +end) + +-- extenders +function telemosaic.extender_after_place(pos, placer, itemstack, pointed_thing) + local positions = minetest.find_nodes_in_area_under_air( + {x = pos.x - 3, y = pos.y, z = pos.z - 3}, + {x = pos.x + 3, y = pos.y, z = pos.z + 3}, + {"telemosaic:beacon_off", "telemosaic:beacon", "telemosaic:beacon_err"}) + local meta = minetest.get_meta(pos) + local playername = placer:get_player_name() + local nodename = itemstack:get_name() + if not minetest.registered_nodes[nodename] then return end + + local description = minetest.registered_nodes[nodename]["description"] + + local power = telemosaic.extender_ranges[nodename] + + for i, bpos in ipairs(positions) do + local bnode = minetest.get_node(bpos) + local bmeta = minetest.get_meta(bpos) + if not bnode then return end + local bnodename = bnode.name + if not minetest.registered_nodes[bnodename] then return end + local bdescription = minetest.registered_nodes[bnodename]["description"] + local bowner = bmeta:get_string("owner") + local brange = bmeta:get_int("range") + brange = brange + power + bmeta:set_int("range", brange) + telemosaic.set_status(bpos, placer) + + bmeta:set_string("infotext", bdescription.."\nowner: "..bowner.."\nrange: "..brange.." blocks\nright-click to update info or with mese crystal fragment setup departure position") + end + + meta:set_string("owner", playername) + meta:set_string("infotext", description.."\nowner: "..playername) +end + +function telemosaic.extender_on_destruct(pos) + local node = minetest.get_node(pos) + if not node then return end + local meta = minetest.get_meta(pos) + local nodename = node.name + if not minetest.registered_nodes[nodename] then return end + local power = telemosaic.extender_ranges[nodename] + local positions = minetest.find_nodes_in_area_under_air( + {x = pos.x - 3, y = pos.y, z = pos.z - 3}, + {x = pos.x + 3, y = pos.y, z = pos.z + 3}, + {"telemosaic:beacon_off", "telemosaic:beacon", "telemosaic:beacon_err"}) + + for i, bpos in ipairs(positions) do + local bnode = minetest.get_node(bpos) + local bmeta = minetest.get_meta(bpos) + if not bmeta or not bnode then return end + local bnodename = bnode.name + if not minetest.registered_nodes[bnodename] then return end + local bdescription = minetest.registered_nodes[bnodename]["description"] + local bowner = bmeta:get_string("owner") + local brange = bmeta:get_int("range") + brange = brange - power + if brange < 0 then brange = 0 end + bmeta:set_int("range", brange) + telemosaic.set_status(bpos) + + bmeta:set_string("infotext", bdescription.."\nowner: "..bowner.."\nrange: "..brange.." blocks\nright-click to update info or with mese crystal fragment setup departure position") + end +end + +-- beacons +function telemosaic.get_range_from_extenders(be_pos) + local positions = minetest.find_nodes_in_area_under_air( + {x = be_pos.x - 3, y = be_pos.y, z = be_pos.z - 3}, + {x = be_pos.x + 3, y = be_pos.y, z = be_pos.z + 3}, + {"telemosaic:extender_one", "telemosaic:extender_two", "telemosaic:extender_three"}) + + local range = 0 + + for i, ex_pos in ipairs(positions) do + local inode = minetest.get_node(ex_pos) + if not inode then return end + + range = range + telemosaic.extender_ranges[inode.name] + end + + return range +end + +function telemosaic.set_status(pos) + local meta = minetest.get_meta(pos) + if not meta then return end + local range = meta:get_int("range") + local pos2 = meta:get_string("dest_pos") + local state = meta:get_string("state") + local arrivals_tbl = meta:get_string("arrivals_tbl") + arrivals_tbl = minetest.deserialize(arrivals_tbl) + if not arrivals_tbl then arrivals_tbl = {} end + + -- not enough energy (err) + if range <= 0 and + state ~= "err" then + minetest.swap_node(pos, { name = "telemosaic:beacon_err" }) + meta:set_string("state", "err") + + -- configured beacon (off) + elseif range > 0 and + pos2 == "not configured" and + state ~= "off" and + #arrivals_tbl == 0 then + minetest.swap_node(pos, { name = "telemosaic:beacon_off" }) + meta:set_string("state", "off") + + -- not configured beacon (on) + elseif range > 0 and + pos2 ~= "not configured" and + state ~= "on" then + minetest.swap_node(pos, { name = "telemosaic:beacon" }) + meta:set_string("state", "on") + end + +end + +function telemosaic.beacon_after_place(pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta(pos) + local nodename = itemstack:get_name() + local playername = placer:get_player_name() + local range = telemosaic.get_range_from_extenders(pos) + if not minetest.registered_nodes[nodename] then return end + + local bname = "beacon at: "..minetest.formspec_escape(minetest.pos_to_string(pos)) + + local description = minetest.registered_nodes[nodename]["description"] + + meta:set_int("range", range) + meta:set_string("bname", bname) + meta:set_string("owner", playername) + meta:set_string("state", "off") + meta:set_string("dep_pos", "") + meta:set_string("dest_pos", "not configured") + meta:set_string("arrivals_tbl", "{}") + meta:set_string("infotext", description.."\nowner: "..playername.."\nrange: "..range.." blocks\nright-click to update info or with mese crystal fragment setup departure position") + + telemosaic.set_status(pos) +end + +function telemosaic.beacon_rightclick(pos, node, clicker, itemstack, pointed_thing) + local meta = minetest.get_meta(pos) + local meta_stack = itemstack:get_meta() + local ownername = meta:get_string("owner") or "unknown" + local clickername = clicker:get_player_name() + local nodename = node.name + local stackname = itemstack:get_name() + local range = telemosaic.get_range_from_extenders(pos) + local state = meta:get_string("state") + if not minetest.registered_nodes[nodename] then return end + + local description = minetest.registered_nodes[nodename]["description"] + meta:set_int("range", range) + + -- departure + if stackname == "default:mese_crystal_fragment" and itemstack:get_count() == 1 then + + itemstack:replace("telemosaic:key") + meta_stack:set_string("dep_pos", minetest.pos_to_string(pos)) + + meta:set_string("infotext", description.."\nowner: "..ownername.."\nrange: "..range.." blocks\nstart pos: "..minetest.pos_to_string(pos).."\nright-click to update info") + + -- destination + elseif stackname == "telemosaic:key" then + -- get deprature position from item + local pos_stack = meta_stack:get_string("dep_pos") + pos_stack = minetest.string_to_pos(pos_stack) + + if math.floor(vector.distance(pos_stack, pos)) == 0 then + minetest.chat_send_player(clickername, "This is marked as your departure position, you have to mark your destination position now!") + return + elseif minetest.is_protected(pos, clickername) then + minetest.chat_send_player(clickername, "You cannot configure protected beacons!") + return + end + + -- enough extenders / range + -- if math.floor(vector.distance(pos_stack, pos)) <= range then + minetest.swap_node(pos, { name = "telemosaic:beacon" }) + meta:set_string("state", "on") + itemstack:replace("default:mese_crystal_fragment") + + -- set arrival pos to departure pos node + local meta2 = minetest.get_meta(pos_stack) + local bname2 = meta2:get_string("bname") + local pos3 = meta2:get_string("dest_pos") + pos3 = minetest.string_to_pos(pos3) + if pos3 and math.floor(vector.distance(pos3, pos)) ~= 0 then + local meta3 = minetest.get_meta(pos3) + local arrivals_tbl3 = meta3:get_string("arrivals_tbl") + arrivals_tbl3 = minetest.deserialize(arrivals_tbl3) + if not arrivals_tbl3 then arrivals_tbl3 = {} end + arrivals_tbl3[minetest.pos_to_string(pos_stack)] = nil + meta3:set_string('arrivals_tbl', arrivals_tbl3) + telemosaic.set_status(pos3) + end + local arrivals_tbl = meta:get_string("arrivals_tbl") + arrivals_tbl = minetest.deserialize(arrivals_tbl) + if not arrivals_tbl then arrivals_tbl = {} end + + arrivals_tbl[minetest.pos_to_string(pos_stack)] = bname2 + + arrivals_tbl = minetest.serialize(arrivals_tbl) + meta2:set_string("dest_pos", minetest.pos_to_string(pos)) + meta:set_string("arrivals_tbl", arrivals_tbl) + + telemosaic.set_status(pos_stack) + meta:set_string("infotext", description.."\nowner: "..ownername.."\nrange: "..range.." blocks\nend pos: "..minetest.pos_to_string(pos).."\nright-click to update info") + + -- default place_node callback + elseif itemstack:get_definition().type == "node" then + itemstack = minetest.item_place_node(itemstack, clicker, pointed_thing) + else + -- mod : pos : pos2 + -- - pos2 is from itemstack so not always set + local formspec = telemosaic.get_formspec(pos, { + range = range + }) + local pos2 = meta:get_string("dest_pos") + minetest.show_formspec(clickername, "telemosaic:"..minetest.pos_to_string(pos)..":"..pos2, formspec) + + meta:set_string("infotext", description.."\nowner: "..ownername.."\nrange: "..range.." blocks\nright-click to update info") + end + return itemstack +end + +function telemosaic.beacon_on_destruct(pos) + local meta = minetest.get_meta(pos) + local pos2 = meta:get_string("dest_pos") + pos2 = minetest.string_to_pos(pos2) + local arrivals_tbl = meta:get_string("arrivals_tbl") + arrivals_tbl = minetest.deserialize(arrivals_tbl) + if not arrivals_tbl then arrivals_tbl = {} end + + -- found destination position, will update the meta in destination + if pos2 then + local meta2 = minetest.get_meta(pos2) + local arrivals_tbl2 = meta2:get_string("arrivals_tbl") + arrivals_tbl2 = minetest.deserialize(arrivals_tbl2) + if not arrivals_tbl2 then arrivals_tbl2 = {} end + + if arrivals_tbl2[minetest.pos_to_string(pos)] then + arrivals_tbl2[minetest.pos_to_string(pos)] = nil + arrivals_tbl2 = minetest.serialize(arrivals_tbl2) + meta2:set_string("arrivals_tbl", arrivals_tbl2) + end + + meta2:set_string("dest_pos", "not configured") + telemosaic.set_status(pos2) + end + + for k, v in pairs(arrivals_tbl) do + local ipos = minetest.string_to_pos(k) + local imeta = minetest.get_meta(ipos) + imeta:set_string("dest_pos", "not configured") + telemosaic.set_status(ipos) + end +end diff --git a/crafting.lua b/crafting.lua new file mode 100644 index 0000000..8d61983 --- /dev/null +++ b/crafting.lua @@ -0,0 +1,37 @@ +minetest.register_craft({ + output = 'telemosaic:beacon_off', + recipe = { + {'default:diamond', 'doors:door_wood', 'default:diamond'}, + {'default:obsidian','default:obsidian','default:obsidian'} + } +}) +minetest.register_craft({ + output = 'telemosaic:extender_one', + recipe = { + {"default:diamond","","default:diamond"}, + {'default:obsidian','doors:door_wood','default:obsidian'} + } +}) +minetest.register_craft({ + output = 'telemosaic:extender_two', + recipe = { + {'', 'group:telemosaic_extender_one',''}, + {'group:telemosaic_extender_one','default:obsidian','group:telemosaic_extender_one'}, + {'', 'group:telemosaic_extender_one',''} + } +}) +minetest.register_craft({ + output = 'telemosaic:extender_three', + recipe = { + {'', 'group:telemosaic_extender_two',''}, + {'group:telemosaic_extender_two','default:obsidian','group:telemosaic_extender_two'}, + {'', 'group:telemosaic_extender_two',''} + } +}) + +-- how to recycle a key +minetest.register_craft({ + type = 'shapeless', + recipe = {'telemosaic:key'}, + output = 'default:mese_crystal_fragment' +}) \ No newline at end of file diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..f2c4494 --- /dev/null +++ b/depends.txt @@ -0,0 +1,2 @@ +default +doors diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..5503b39 --- /dev/null +++ b/init.lua @@ -0,0 +1,7 @@ +MOD_NAME = minetest.get_current_modname() + +dofile(minetest.get_modpath(MOD_NAME).."/api.lua") +dofile(minetest.get_modpath(MOD_NAME).."/nodes.lua") +dofile(minetest.get_modpath(MOD_NAME).."/crafting.lua") + +print ("[Mod] Telemosaic Loaded.") diff --git a/nodes.lua b/nodes.lua new file mode 100644 index 0000000..bb6d214 --- /dev/null +++ b/nodes.lua @@ -0,0 +1,81 @@ +-- extenders come in three strengths +for num, strength in ipairs(telemosaic.strengths) do + minetest.register_node("telemosaic:extender_"..strength, { + description = "Telemosaic extender, tier "..num..", extends beacon range by: "..telemosaic.extender_ranges["telemosaic:extender_"..strength].." blocks", + tiles = { "telemosaic_extender_"..strength..".png" }, + is_ground_content = false, + groups = { cracky = 2, ["telemosaic_extender_"..strength] = 1 }, + sounds = default.node_sound_stone_defaults(), + after_place_node = telemosaic.extender_after_place, + on_destruct = telemosaic.extender_on_destruct, + }) +end + +-- beacons +minetest.register_node("telemosaic:beacon_off", { + description = "Telemosaic beacon (off)", + tiles = { + "telemosaic_beacon_off.png", + "telemosaic_beacon_off.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + }, + is_ground_content = false, + groups = { cracky = 2 }, + sounds = default.node_sound_stone_defaults(), + after_place_node = telemosaic.beacon_after_place, + on_rightclick = telemosaic.beacon_rightclick, + on_destruct = telemosaic.beacon_on_destruct, + on_receive_fields = telemosaic.on_receive_fields, +}) + +minetest.register_node("telemosaic:beacon", { + description = "Telemosaic beacon (on)", + tiles = { + "telemosaic_beacon_top.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + }, + is_ground_content = false, + groups = { cracky = 2 }, + -- groups = { cracky = 2, not_in_creative_inventory = 1 }, + drop = "telemosaic:beacon_off", + sounds = default.node_sound_stone_defaults(), + on_rightclick = telemosaic.beacon_rightclick, + on_destruct = telemosaic.beacon_on_destruct, + on_receive_fields = telemosaic.on_receive_fields, + -- on_punch = telemosaic.beacon_on_punch, +}) + +minetest.register_node("telemosaic:beacon_err", { + description = "Telemosaic beacon (err)", + tiles = { + "telemosaic_beacon_err.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + "telemosaic_beacon_side.png", + }, + is_ground_content = false, + groups = { cracky = 2 }, + -- groups = { cracky = 2, not_in_creative_inventory = 1 }, + drop = "telemosaic:beacon_off", + sounds = default.node_sound_stone_defaults(), + on_rightclick = telemosaic.beacon_rightclick, + on_destruct = telemosaic.beacon_on_destruct, + on_receive_fields = telemosaic.on_receive_fields, +}) + +-- telemosaic key +minetest.register_tool("telemosaic:key", { + description = "Telemosaic key", + inventory_image = "telemosaic_key.png", + stack_max = 1, + -- groups = { not_in_creative_inventory = 1 }, +}) diff --git a/sounds/telemosaic_set.ogg b/sounds/telemosaic_set.ogg new file mode 100644 index 0000000..0335350 Binary files /dev/null and b/sounds/telemosaic_set.ogg differ diff --git a/sounds/telemosaic_teleport.ogg b/sounds/telemosaic_teleport.ogg new file mode 100644 index 0000000..4886853 Binary files /dev/null and b/sounds/telemosaic_teleport.ogg differ diff --git a/textures/src/telemosaic_extenders.xcf b/textures/src/telemosaic_extenders.xcf new file mode 100644 index 0000000..b5d0392 Binary files /dev/null and b/textures/src/telemosaic_extenders.xcf differ diff --git a/textures/telemosaic_beacon_err.png b/textures/telemosaic_beacon_err.png new file mode 100644 index 0000000..21a5eb8 Binary files /dev/null and b/textures/telemosaic_beacon_err.png differ diff --git a/textures/telemosaic_beacon_off.png b/textures/telemosaic_beacon_off.png new file mode 100644 index 0000000..51ce88f Binary files /dev/null and b/textures/telemosaic_beacon_off.png differ diff --git a/textures/telemosaic_beacon_side.png b/textures/telemosaic_beacon_side.png new file mode 100644 index 0000000..7b50751 Binary files /dev/null and b/textures/telemosaic_beacon_side.png differ diff --git a/textures/telemosaic_beacon_top.png b/textures/telemosaic_beacon_top.png new file mode 100644 index 0000000..4502f52 Binary files /dev/null and b/textures/telemosaic_beacon_top.png differ diff --git a/textures/telemosaic_extender_one.png b/textures/telemosaic_extender_one.png new file mode 100644 index 0000000..718aafb Binary files /dev/null and b/textures/telemosaic_extender_one.png differ diff --git a/textures/telemosaic_extender_three.png b/textures/telemosaic_extender_three.png new file mode 100644 index 0000000..27bb6ef Binary files /dev/null and b/textures/telemosaic_extender_three.png differ diff --git a/textures/telemosaic_extender_two.png b/textures/telemosaic_extender_two.png new file mode 100644 index 0000000..d7aab2a Binary files /dev/null and b/textures/telemosaic_extender_two.png differ diff --git a/textures/telemosaic_key.png b/textures/telemosaic_key.png new file mode 100644 index 0000000..c9bd8fe Binary files /dev/null and b/textures/telemosaic_key.png differ diff --git a/textures/telemosaic_particle_arrival.png b/textures/telemosaic_particle_arrival.png new file mode 100644 index 0000000..3a45d15 Binary files /dev/null and b/textures/telemosaic_particle_arrival.png differ diff --git a/textures/telemosaic_particle_departure.png b/textures/telemosaic_particle_departure.png new file mode 100644 index 0000000..285977b Binary files /dev/null and b/textures/telemosaic_particle_departure.png differ