From 7643cb29875b720c3a5f56a7cbca4491f1e7a8de Mon Sep 17 00:00:00 2001 From: GreenDimond <24834740+GreenXenith@users.noreply.github.com> Date: Mon, 18 Mar 2019 10:02:49 -0700 Subject: [PATCH] Initial commit --- LICENSE.txt | 21 + depends.txt | 5 + init.lua | 442 ++++++++++++++++++ textures/mesecons_wireless_antenna.png | Bin 0 -> 1198 bytes textures/mesecons_wireless_dish.png | Bin 0 -> 1139 bytes textures/mesecons_wireless_metal.png | Bin 0 -> 1836 bytes textures/mesecons_wireless_receiver_off.png | Bin 0 -> 1992 bytes textures/mesecons_wireless_receiver_on.png | Bin 0 -> 2060 bytes .../mesecons_wireless_receiver_signal.png | Bin 0 -> 2052 bytes textures/mesecons_wireless_signal_palette.png | Bin 0 -> 122 bytes .../mesecons_wireless_transmitter_off.png | Bin 0 -> 2023 bytes textures/mesecons_wireless_transmitter_on.png | Bin 0 -> 2081 bytes .../mesecons_wireless_transmitter_signal.png | Bin 0 -> 2024 bytes 13 files changed, 468 insertions(+) create mode 100644 LICENSE.txt create mode 100644 depends.txt create mode 100644 init.lua create mode 100644 textures/mesecons_wireless_antenna.png create mode 100644 textures/mesecons_wireless_dish.png create mode 100644 textures/mesecons_wireless_metal.png create mode 100644 textures/mesecons_wireless_receiver_off.png create mode 100644 textures/mesecons_wireless_receiver_on.png create mode 100644 textures/mesecons_wireless_receiver_signal.png create mode 100644 textures/mesecons_wireless_signal_palette.png create mode 100644 textures/mesecons_wireless_transmitter_off.png create mode 100644 textures/mesecons_wireless_transmitter_on.png create mode 100644 textures/mesecons_wireless_transmitter_signal.png diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..0be36ef --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (C) 2019 GreenXenith/GreenDimond + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..2392460 --- /dev/null +++ b/depends.txt @@ -0,0 +1,5 @@ +digilines? +default +mesecons +mesecons_luacontroller +mesecons_materials diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..4c1cb81 --- /dev/null +++ b/init.lua @@ -0,0 +1,442 @@ +--[[ + + The MIT License (MIT) + + Copyright (C) 2019 GreenXenith/GreenDimond + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to + deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + IN THE SOFTWARE. + +]]-- + +local storage = minetest.get_mod_storage() +local range_max = 20 +local connection_max = 100 +local rules = { + {x = 1, y = 0, z = 0}, + {x =-1, y = 0, z = 0}, + {x = 0, y = 1, z = 0}, + {x = 0, y =-1, z = 0}, + {x = 0, y = 0, z = 1}, + {x = 0, y = 0, z =-1}, +} + +local function hash(pos) + return minetest.hash_node_position(pos) +end + +local function dehash(hash) + return minetest.get_position_from_hash(hash) +end + +-- Run function on each position in channel +local function do_for_each(owner, channel, origin, func) + -- Put data in table + local data = minetest.deserialize(storage:get_string(owner..":"..channel)) + if not data then + return + end + -- Get range + local range = minetest.get_meta(origin):get_int("range") + for pos in pairs(data) do + pos = dehash(pos) + if pos ~= origin then + local dist = vector.distance(origin, pos) + if dist <= range then + func(pos, dist) + end + end + end +end + +-- Modify storage as table +local function storage_table(owner, channel, func) + local t = minetest.deserialize(storage:get_string(owner..":"..channel)) or {} + local set = func(t) + if type(set) == "table" then + set = minetest.serialize(set) + end + storage:set_string(owner..":"..channel, set) +end + +-- Limit digiline actions to 20 per second +local function overheat(pos) + local actions = minetest.get_meta(pos):get_int("actions") + if actions >= 20 then + local node = minetest.get_node(pos) + -- Burnout + if node.param2 ~= 192 then + minetest.swap_node(pos, {name = node.name, param2 = 192}) + end + return true, actions + end + return false, actions +end + +-- Signal Transmitter +mesecon.register_node("mesecons_wireless:transmitter", { + description = "Wireless Mesecon Transmitter", + overlay_tiles = { + "", + "", + {name = "mesecons_wireless_transmitter_signal.png"}, + }, + paramtype2 = "color", + palette = "mesecons_wireless_signal_palette.png", + digiline = { + effector = { + action = function(pos, node, dchannel, msg) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local channel = meta:get_string("channel") + local overheated, actions = overheat(pos) + if overheated then + return + end + -- Signal indicator + if node.param2 ~= 128 then + minetest.swap_node(pos, {name = node.name, param2 = 128}) + end + meta:set_int("actions", actions + 1) + do_for_each(owner, channel, pos, function(each) + digiline:receptor_send(each, digiline.rules.default, dchannel, msg) + local enode = minetest.get_node(each) + minetest.swap_node(each, {name = enode.name, param2 = 128}) + end) + end + } + }, + on_timer = function(pos) + minetest.get_meta(pos):set_int("actions", 0) + local node = minetest.get_node(pos) + if node.param2 ~= minetest.registered_nodes[node.name].place_param2 then + minetest.swap_node(pos, {name = node.name, param2 = minetest.registered_nodes[node.name].place_param2}) + end + minetest.get_node_timer(pos):set(1, 0) + end, + sounds = default.node_sound_metal_defaults(), + on_construct = function(pos) + local meta = minetest.get_meta(pos) + meta:set_int("range", 15) + meta:set_string("formspec", "field[channel;Channel;${channel}]field[range;Range (1-"..range_max..");${range}") + minetest.get_node_timer(pos):set(1, 0) + end, + on_destruct = function(pos) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local channel = meta:get_string("channel") + if channel and channel ~= "" then + do_for_each(owner, channel, pos, function(each) + minetest.swap_node(each, {name = "mesecons_wireless:receiver_off", param2 = 0}) + mesecon.receptor_off(each, rules) + end) + end + end, + after_place_node = function(pos, placer) + minetest.get_meta(pos):set_string("owner", placer:get_player_name()) + end, + on_receive_fields = function(pos, _, fields, player) + local meta = minetest.get_meta(pos) + local setter = player:get_player_name() + local owner = meta:get_string("owner") + local channel = meta:get_string("channel") + + if setter == owner then + if fields.channel then + meta:set_string("channel", fields.channel) + end + local newrange + if fields.range then + newrange = tonumber(fields.range) + end + if newrange and newrange >= 1 and newrange <= range_max then + local current = meta:get_int("range") + if minetest.get_node(pos).name ~= "mesecons_wireless:transmitter_on" then + return + end + if current and current ~= 0 then + if current > newrange then + do_for_each(owner, channel, pos, function(each, dist) + if dist > newrange then + minetest.swap_node(each, {name = "mesecons_wireless:receiver_off", param2 = 0}) + mesecon.receptor_off(each, rules) + end + end) + end + end + meta:set_int("range", newrange) + do_for_each(owner, channel, pos, function(each) + local name = minetest.get_node(each).name + if name == "mesecons_wireless:receiver_on" then + return + end + minetest.swap_node(each, {name = "mesecons_wireless:receiver_on", param2 = 64}) + mesecon.receptor_on(each, rules) + end) + end + end + end, + }, + { + tiles = { + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_transmitter_off.png", color = "white"} + }, + color = 0, + place_param2 = 0, + groups = {cracky=1}, + mesecons = { + effector = { + action_on = function(pos) + local overheated, actions = overheat(pos) + if overheated then + return + end + minetest.swap_node(pos, {name = "mesecons_wireless:transmitter_on", param2 = 64}) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local channel = meta:get_string("channel") + meta:set_int("actions", actions + 5) + do_for_each(owner, channel, pos, function(each) + minetest.swap_node(each, {name = "mesecons_wireless:receiver_on", param2 = 64}) + mesecon.receptor_on(each, rules) + end) + end + }, + }, + }, + { + tiles = { + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_transmitter_on.png", color = "white"} + }, + color = 64, + place_param2 = 64, + paramtype = "light", + light_source = 2, + groups = {cracky=1, not_in_creative_inventory=1}, + mesecons = { + effector = { + action_off = function(pos) + minetest.swap_node(pos, {name = "mesecons_wireless:transmitter_off", param2 = 0}) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local channel = meta:get_string("channel") + do_for_each(owner, channel, pos, function(each) + minetest.swap_node(each, {name = "mesecons_wireless:receiver_off", param2 = 0}) + mesecon.receptor_off(each, rules) + end) + end + }, + }, + } +) + +mesecon.register_node("mesecons_wireless:receiver", { + description = "Wireless Mesecon Receiver", + overlay_tiles = { + "", + "", + {name = "mesecons_wireless_receiver_signal.png"}, + }, + paramtype2 = "color", + palette = "mesecons_wireless_signal_palette.png", + digiline = { + receptor = {}, + }, + sounds = default.node_sound_metal_defaults(), + on_timer = function(pos) + local node = minetest.get_node(pos) + if node.param2 ~= minetest.registered_nodes[node.name].place_param2 then + minetest.swap_node(pos, {name = node.name, param2 = minetest.registered_nodes[node.name].place_param2}) + end + minetest.get_node_timer(pos):set(1, 0) + end, + on_construct = function(pos) + minetest.get_meta(pos):set_string("formspec", "field[channel;Channel;${channel}]") + minetest.get_node_timer(pos):set(1, 0) + end, + on_destruct = function(pos) + local meta = minetest.get_meta(pos) + local owner = meta:get_string("owner") + local channel = meta:get_string("channel") + if channel and channel ~= "" then + storage_table(owner, channel, function(data) + data[hash(pos)] = nil + if not next(data) then + return "" + end + return data + end) + end + end, + after_place_node = function(pos, placer) + minetest.get_meta(pos):set_string("owner", placer:get_player_name()) + end, + on_receive_fields = function(pos, _, fields, player) + local meta = minetest.get_meta(pos) + local setter = player:get_player_name() + local owner = meta:get_string("owner") + + if setter == owner then + if not fields.channel then + return + end + local t = minetest.deserialize(storage:get_string(owner..":"..fields.channel)) + if t then + local connections = 0 + for c in pairs(t) do + connections = connections + 1 + end + if connections >= connection_max then + return + end + end + local current = meta:get_string("channel") + if current and current ~= "" and current ~= fields.channel then + storage_table(owner, current, function(data) + data[hash(pos)] = nil + if not next(data) then + return "" + end + return data + end) + end + meta:set_string("channel", fields.channel) + if fields.channel == "" then + return + end + storage_table(owner, fields.channel, function(data) + data[hash(pos)] = 0 + return data + end) + end + end, + }, + { + tiles = { + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_receiver_off.png", color = "white"}, + }, + color = 0, + place_param2 = 0, + groups = {cracky=1}, + mesecons = { + receptor = { + state = mesecon.state.off, + rules = rules + } + }, + }, + { + tiles = { + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_metal.png", color = "white"}, + {name = "mesecons_wireless_receiver_on.png", color = "white"}, + }, + color = 64, + place_param2 = 64, + paramtype = "light", + light_source = 2, + groups = {cracky=1, not_in_creative_inventory=1}, + mesecons = { + receptor = { + state = mesecon.state.on, + rules = rules + } + }, + } +) + +minetest.register_lbm({ + label = "Refresh Wireless Transmitters", + name = "mesecons_wireless:clear_actions", + nodenames = { + "mesecons_wireless:transmitter_on", + "mesecons_wireless:transmitter_off", + "mesecons_wireless:receiver_on", + "mesecons_wireless:receiver_off", + }, + run_at_every_load = true, + action = function(pos) + minetest.get_node_timer(pos):set(1, 0) + end, +}) + +minetest.register_craftitem("mesecons_wireless:antenna", { + description = "Antenna", + inventory_image = "mesecons_wireless_antenna.png", +}) + +minetest.register_craftitem("mesecons_wireless:dish", { + description = "Radio Dish", + inventory_image = "mesecons_wireless_dish.png", +}) + +minetest.register_craft({ + output = "mesecons_wireless:antenna", + recipe = { + {"default:steel_ingot"}, + {"default:steel_ingot"}, + {"mesecons_materials:fiber"} + } +}) + +minetest.register_craft({ + output = "mesecons_wireless:dish", + recipe = { + {"", "", "default:steel_ingot"}, + {"", "default:steel_ingot", "default:steel_ingot"}, + {"default:steel_ingot", "default:steel_ingot", "mesecons_materials:fiber"} + } +}) + +minetest.register_craft({ + output = "mesecons_wireless:dish", + recipe = { + {"default:steel_ingot", "", ""}, + {"default:steel_ingot", "default:steel_ingot", ""}, + {"mesecons_materials:fiber", "default:steel_ingot", "default:steel_ingot"} + } +}) + +local wire = "mesecons:wire_00000000_off" +if minetest.get_modpath("digilines") then + wire = "digilines:wire_std_00000000" +end + +minetest.register_craft({ + output = "mesecons_wireless:transmitter_off", + recipe = { + {"", "mesecons_wireless:antenna", ""}, + {"default:steel_ingot", "default:diamond", "default:steel_ingot"}, + {"mesecons:wire_00000000_off", "mesecons_luacontroller:luacontroller0000", wire} + } +}) + +minetest.register_craft({ + output = "mesecons_wireless:receiver_off", + recipe = { + {"", "mesecons_wireless:dish", ""}, + {"default:steel_ingot", "default:diamond", "default:steel_ingot"}, + {"mesecons:wire_00000000_off", "mesecons_luacontroller:luacontroller0000", wire} + } +}) diff --git a/textures/mesecons_wireless_antenna.png b/textures/mesecons_wireless_antenna.png new file mode 100644 index 0000000000000000000000000000000000000000..338a7bde03cf8ead6469b1e8f7eb677f5c38ad03 GIT binary patch literal 1198 zcmV;f1X25mP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U1sQa^xrshW~SlJpz&tg5&U6wKv$~&kJ_DCo{>U zJDopMYswadu_W{eVK@H!ce;OYQBrn6EyWz8#}#tOT+m7WevMNeW8}kr-YzkpoR0Toa49o@sd#V&I{6CK9I`{|7_UGInJKncvS0UxpRqLi6ONJHWSgUG68a0)o23oUp*+>!G45-E&KJ0tn_Q}b-&X53sk0eTwyfGV55KaJ8;5kd+3Ie3c9Zqr(iC~kEN3e}=jP_hsKE|+AOyd||hsPeparnG+4^5QO z`?2IG*u$MxC|a}@cTaJYx}>}B{fp9l^n#}OZCNX2?Df5R)T*X-IuX<14oVWFyLDcb zZxmUt=bBrLq3$k@vcY`jgoF>H>AueCEeGQ{Xwf3_G@ib2@Bh9-7ypx` z`SOjY{|_CzN}lE$cVGQKef} zaB^>EX>4U6ba`-PAZ2)IW&i+q+T~VTa_cq>{pTvW1mZ=I%Ypcq=?=R5f_lY~?byl9 zPp6_Ka3Byk!~rOW{?Fed{N)#9=D?ykm6W1iT)3e5B*%}d?{d>Y zC2dOO{VnbK!#0Uesl3e_<(c2t*$6=~IF zQY=_05)jV;1C3T20r>7= z&z0jsoIJoxBh9CqIUz81-egViG0rR85KGRWtZB}^L5s`gViEYLEm{kJ+)i9>^#$4{ zYq(Ykdu(wqKu$qSKmrM(A}RnAW+%uTITGXI41fqW2Z>CSoSW=*%^9nUJuEBvHvy#6qIPNm5FhX3v?DvWg~E&05qlWzLdQ)|_+6 zR6$21Nm=Bv#u%obc|;YF6T=#rPRSoQL$T&1R}HP=$DVWY-PT58&I zE6qA|FQw4AOHW;U?xoxCS{pQY$Vfv+9%b0c+Rp0T_y%jRvxX~E(@sy;T&p@AtuA1m zGcq}I#*o1o*U5kvG)~T924?E!0To`iK}Z0o|A(@5TZ{=R3!Y@Te~CVwjb7mkLAt@9Y4NUCN} zy@j8_xQ`mJ3b6lTd?G>m)eG7^=J4fa&P_E*yO(8kFW#)X^lq7x@S(Z0(2GL!i)4kj zlX2$2`}?waZ+{M-G;lTXFYqN%4-=GUj&2fRhjlIQ!MIU`dzO9%D|`#i6~>JsJje09 zJcVBZB;l6;?Uj8C=rF>g{E#op@KJY%PZoGxLdRq|aCMP~o$#PV)a+Q5jsh>&im&;C z*Z&^#wY&47uyOb<^5Tp*=|7X)Z8>^0UpIOri{R*4J(gJ*KkD^+8QweqU!H&A%>(eI z3~wHc&o(010qNS#tmYE+YT{E+YYWr9XB6005&&L_t(I%gvI_4Z|P|h3(3gxFfL{Q!o!x zFd3Nu$%yo11Sv_IL)E?ue|n#uu>pQ9JbUoo@8db8gdYLaXpEWeODRhb9?fbbq6tuI z?Vu6zSjAE^GXpa(fm4DI0&Ls18)GK(C=uyN#uUp0Y913DftMxRQ3oL6OI#6|_HWy$ zUC@8XIRg=awRTzmX$ji0)>_}|`rSdg8fE7k#u(xM^aPE@RUw{|S0DfY002ovPDHLk FV1jf|5!L_z literal 0 HcmV?d00001 diff --git a/textures/mesecons_wireless_metal.png b/textures/mesecons_wireless_metal.png new file mode 100644 index 0000000000000000000000000000000000000000..65d372af11542e3e4c4125ce89e1326940004869 GIT binary patch literal 1836 zcmV+{2h;e8P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1Y;k}N3*{_iQ~2m<1A9MdIYZZO9m%uJ|PW>c&5RPxqI6P<(cQQ;sD@FJCC3V3kGouW#J*MFStsfBoS82lwR$%1koT zMNHw|rJJt&(Pk0!#Rl+pfJ_bksW{b}jvn7Enq)d&?%cmQd!BP#-qjE#4DoX6QsGpL zFdh;mI*&yeNq#KrbdDk;`|2A6-+l9kj0Ob|$WI8+9aNYwpv&B#iH!jVCYcc%(n-sd zuwWxkKw%A-n5a;vOh^p}5d}wRw!o>kocbDM(cD2;5kwfsk#C3liQzY6&gLaX0OCE| zu@buw7Y?w(Nb6tN1Q3`vZhQt2m-*2U5vy)6a||sc_g~!OoEda885n& zir~W(DG4@(s(=*&Oa7#gC5I{#2WF0(ICJ6ZBE=9n#+c%&xtb`2#3`nfaw@52${}-( zIpv&7t_2H8NpguLm0U`xRdm+iDpxsOg`?I34?4oZM?BJzk8;!|>C-~v7F%k$l~y}; z>Y;OwJ@wp6uLFjpk{e<0h$D?W%BWLvZPE;rXPjx~S!OlVZd4!RKcEITYIq{G_>!R} z)RdN>Qv|F&!wWM(3#+|Ec@;dM>vs()4 zTET~zR7HHZPufUGdsx~d+8UOGhN?nB9hV#KEsH4buI+PFWqm_O9ufS>Ad3q;5wUt2 zEvajAjjOquOV~E#fa{*$!4mf5bz!CDP0~8_bI&TX2@Sof*dJE3OqSGs%TNC)Uuh4T zdqw2IlgAe6VYSu3FiRIrVpP@cSU#n$e_-`M(e)pVHk+wfnpk(5;og{;7;hcuR4ckI?ZbFZQbm{Chk-jI5t z!>2TaU5nvhCJwjjjeToluSJ19d~w)4;H*K|F8$O2@>d(~+Y@#((DN=k-n*8&@3#-W zn=|xNnA|rJ)z}29HQSqPmZ8;Jf8JKs-}`nYT8ckfk24aewraPo0YUI+ec9d+^y-ir zk;G(2_ZtD^L8RR^J$i7M?x9p3YgSunzX|JW&&-wgQ;}Nx7j?RBjIF?2b^rhX24YJ` zL;#WimH?;YsXYb&000SaNLh0L01FcU01FcV0GgZ_0006{NklL!ey(Yn zvRbW}OeQ=%J)x?M$75!*8N=ZaRpsU7g~?>X!@~n%7&0D@xw*N)tk-KEA0Gjz>zcAG zvDQ+SC2wzUR8@s@j=HXC+m?@y4~#JsMZx#?H>PPC+P3BB=!m9i$g&Jo#W~0B?k>(b z0KE68Dx=W|Rb{zc{#|x_e9U68Ktzb+7=W(pFvcJvv~7#3;=KnzL~zcLBnhXdr}N%;Rc=kVSW$1z>k^{SetA&Mf(vgGsg6O*PXbzNhO*;*hXc<+0tDyoXL7Jxj@ zX_^LeczB5S9&2r{;^*gwG)+-e%CaO$5~`}gTH9LzIQ#XB;Qs!es;aguBuPRL1OV*p z><~o}S(Xt+5ylvdF<5K)eRYM|-`^*SBCNHjDk9RSZj9-(@ZRH`>yhU<2L}h3EX(-% z`XWh^t+Y43H~-C|wr%OU4(~m~;Slrs`igT75kXb^FgL7W81~h9?-3D#ARy0k=JPqS zTrPFBS}_<5wgTEn77@ZQ>{+|6qw6}JpPy;lmSDA7@&5jfh-@JY!`{N?m_!6?ZC~Zx z-5o|%NsZO;h&?f74ao7qO$#zGi|NKq786a{;GdpPI%V`pU=tj9J#o-(7|B9VzFR4opO77i>fl6PPx3i1mNW4g!z2V+1VM_*Vl*$qtS@j aY{nn+A%}i;nu;m_0000 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 i>vfvVCIIz%oqqt)OEhX9&#hSi0000 zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1bxk|ik!{dX0!1OXLYj?akM!7P8s2jnfYy1OP~ z(x|H;3P%p0<>o(sFZY*xP!hTjIj546moJo1u*x9&=bLwb&4CZ+uYPd(%UbkFPhWW6Z*MgVc!KxPI%4RgKc(c>3HaxC_rUw;198hNdGovwx?Zb;XmE)!1G z$igOGqH|l4#_|_s%x5dovah~F@WXfCq;)8OKz>1h;h@5T2}Al0T`&fm$kHQrq;oD4 zW5G_IfWpyWAyc72nUFe8A_|Vsu)t}zoc20ou{=PQB8W1OEx&Ex-79(Es zk}ASaq(m&-6skg22vOpPLzWn-kz$B3rpPhJ601v;LW(IRPDjpFCJr1qF>~g^HB$~b z=9D?-TyiZ~NK!JERJi0)O08nE23KR1-Bni9+OSazEw;!2BGUS(Bd?Pm4W{sU`pvxX;A$2vCF zWS03*bgF=(&cuT=VGIKZ<9sk60gVS|K0^q2Fn4g~3rbc*6p-=YW`M5_l28~XU|e9_ zJHz&US6L0tv=G;Nu|G*r&NgHq9ur`iL*iXi;EIfSbNwRNNF^^f~uE{|mL@BfJ z=%sE!W1SK=o>RW)OuuKKHyzbegA*yLhGH6quF7hM=52bEqsQ#2*ht*sE@_|loWFY< zsy(DzL(u`7;dWbzrMhb$DE0sQdX(Y45lM1)|EP1rNLq`2jJQ%>%64P|l)2l7&lqu;oV&e|4o4HZGb8 zz&nSJmn-eS7h1loNXFXZ2U0h11Iw(b>sZ5W6|Z5o8ce6AcTTvd>Bn~!e(AgF;E9l> z-th_~=@Ew|Oi|hRYRDZYgl^|fFIPbAv76_)6Sv3a3ArurQuWMs5b>Oc{kja1!T2q2s`6X zAhlO{8_Ekri7j`rs~Erv)$jaaHPD3esx@iax4wM!RJ3mKGW+d7_i+tbMWYvYm>z@a zTZ7`G-p!{)QLTip!|KMKHOkShBed&i*>&*J@MIs3y)Hj#;x?L|PU@wyKwBB z(=FWZo}rheU3P|K+O@}3i_u5%-!C4{4=Dq70`!W2pb+LEyW(g?yT;Lgg%>c3$?*3QQsy8xkJj832miJ zJzD&&e54kYY})P7P);`8C;DLo7kg&Nd#$*C0i3XsE<1IVoB#j-24YJ`L;#WimH?;Y zsXYb&000SaNLh0L01FcU01FcV0GgZ_00082NklG89`oFbAy!X8jbLMA2}EdFvcK+z*?KY1UNiA z1mNuK4B#(_xm=ERyN&2{IyMLb^7(u+y;iHSyStk-8Vm;1YBj3WDsdcRjN$$LohXWs zK@jlu^_4u+w~mjG`MTT3W&wgS9pR_U$(eLmbCJ2*G4B!F63;US9rt^ZWaI^7%Z?W|K;#!qd|e z)> zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1bvvg{@d{bv!&sFaWciV8kh|@hyqBuj zbsHLlrgJ3vy7AYa)BT|jHeFur#Z(JUE!c<2v<;-3Afd# zU{zIg9t-+L{S$wvKHjWv^2Ik`-+l8{-v${F$WLJC4hl>d&~5wRmQU*NQxPkRlLTJE5t2&xS9$iKesR|3ED&e^=?2tcEU zl`HiN@xTF&G}7^@VhRY%g`1oS-mg9G16Wc7XPz(GO?F&s5lB1Mi8LyR%S9803aNm58L zrIb_2m`O`ga>y~KoO3B^v#={t^mI{)QYu!eT&0E@YpS`Hru1ptq=gn+YPpq;ojP~v zp~s$j?q$GGD!IW!Mi_CVkw=-zwMmnw%rN6jGtXkEEmU9ce?ScuYIq`b$VEdjPIqx@$fluhskH6TSf+bEMogZv%``3U+HGU4`88tS+Bqx@ zc~7DluFHz#;;wVBeP`HvWvfj4oOup=B3t(;ZhPG3_EU>+sem;BTtZwPb}fm`Jwk%* z?Kbb_k?*LWZY;KEqjqQ8TSG|wTLtthDKb+&DkSvlTMb$1xv_BJ>Gjhk=&gV1flobK zCgtrBo9CA95KNWdZ0PuhFTyzDTro9X27uc5l3>R_;=puT7)*JiD^{iGT3!h706lR;^`cxtpO|?(~<%Z0<<@ zC&JU6QWx&>+b1p9w)b!!Fqv+9>%!CDN8_!*Sk3j-)8XgQd9iz_=O_P?T~_qIUTgcj zw%opQzbWa>lYF~ZcOe??vvkQH5eR{HF*swJ4hhW`oGImbioIASAvAJKEF%ZW z^40B7A`1O2XE=v*&hI1lzjcek2sEl5CWwXo12^1VHlF8DM~5czWbIBf1I(jv_z#+ z;r#rZi;D}4F#!De>o0uu{T`)KiRpC8a5%&+m&wN~MC7lHJ{1;y5ObW45-oP)ebc0x06AZ{A=JheMJi zK`F)Eb%&4Neo+)R8V!8kFShz@>l^<1{kMY2hi(_^`uf^LQB=^iEDM0Mvoo5_CIF|W zr)+O;7pK-*GZ+k*OeP4=^N8aZr4+^(@;v8N$H&J2g~cq(a9x+hVnLqgRI63QcsxdH zjb&LFV+xo6`}_LG#CR#sMu>8)0aot>Sc(Re(j)oQV~w@04m zXsvmCd?d><#Aq~PGMN<5^s2+dL;kB*m;j5#0)W9_fH20OltM~bSOGXVI4Gt89335D z+ctTg7Zx1H!3x9Bgki|q+8SDGj4=hUSHDS;VB0p9Wigx0a9x*PulH}0X0!RT<2c;h z+)yr;>2|vWL4fCZ`2IWI|MWSZeDpV0S64WWL#NZB-|qv^>2&x9N*hGAYRkzJ00000 LNkvXXu0mjfWJ~@m literal 0 HcmV?d00001 diff --git a/textures/mesecons_wireless_transmitter_signal.png b/textures/mesecons_wireless_transmitter_signal.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb78a318a31a410b7ccda69914c47a238facbcb GIT binary patch literal 2024 zcmVP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1b>lI|!7{qHJf2?F9{ImSoT>|mBZmAv?) z)x`Rutq90DVA_p;{+{kHd7yB1fm4npMlVk&pHjtUYPs6G1dGz?jBV?ca=acW>oITGuuG7^JB@FR8)Mdh{ z8WB7sN_1|E(vtjG#_4QDTK3g92)_H~4`~ewAdsIBpgX8AVL+F@K@%SX4ouP`Hl%Yd zQ^JCcJOPE(U}B;|oiZUc97Gfxq4@%*-E!J%ki~Kb5k(MXAX|Rf-0uYbq|Mp9#0Wr6 z4?9-k7vhRLtT57gi`8FSBI7kL zsUr9=MM{EAp(aAUV~i=ToU4gaNStCyDW{TZrW`Wo zm{ZQV-DW z*_4)|Qw6L#!wWM(3709v#h&A4hn&#%<}4` zZo!dsO2T+f`Jyvz&pnWF?%XDlCZdIwT(UJcaL4Q zhqyHq9V9b+Y%5%@H>K@gp$8D;gl7w3Y>;d2w3iV;vg4o>S zNTA-TdC!k{CjnKlP|wEEoh_?|nEO{1Fsg!(E^VKBX`jyB?mm54c2q&04+kMzRIh#6jKL5Nj`%SjOvShauur_l?Ou9 zN_2bmC=b_|ZWT%qWx?cftEd|6k(5=f+TN^tN7CKLw9{Jv8|jE$eOg2Rbi4HLC+{nt#nuZ||!|F33lP`fw-S z7UK^FTiY`3PCGv%K8qaLhOC7g?GD1va__#@d(&`J9PMc1hq`w5Zi%E}KE>z#X4S-7 zPTD!gRHw4lqdtDRX<&rAd};<7>rC94P`*Lw@B|>fQf=7URpzc3p-%Sq6=Mx#g@B}Vc>lL000JJOGiWik^q(f zr{k$T1^@s632;bRa{vGf6951U69E94oEQKA0;x$vK~y-)Ws)&VBV81RpF4AJh8xC= zMw^gr48kY-^^0|WK|n0TLKJ_%T3h=g1hKFQ#8xd7>?Em#O=Z#~L6LQ0FbRnyncTTs zEEzud-sTkNaL#$3b3~<5A)QXs?RLrM^Sr&iVXY;X%kljDOg5XvTFb}B2l;%SmzNh@ z*Cm(B(QG!6gp=iTH5V4gTVl2V`GDCHj5Af&-2*Z z+p|h3JkLW)Nh*~>N{Ld6qoX71x-L>mwAO5HZsLSt$aFeIYt8odw!OW*6-!G?WHK4< z?(Vq1zsFh&z~$woI5;@4sZ@&La7d@q!AYmn_`Z*n5&$VBQc5b73cl~-`#uj34=ANj zN&(gtM} zot@;=#u!?y7X5x7>H9vT(Fm`5?W})-*bAZHR6h(NR$9z5~j$^XfEV9?@VT?ft zfweY)32=OT48X<31;GCxHX02?yWOVS?Xt46l1wj`%k1v%CXIT%9_4bG!^1=3IK~*m z`};dl6d}9aF8zK#d8UOxDzO4^a&nSP12{W7!*Lwq zI8H3Mu8Ro6kT48cTU)~zgS9pRw(vWdOmG|rAq3;`7|-))G#Y=K6pKY%*X8NyiF7(m zy