From 396778892156b5cb9eb71e3b780da9874536a90a Mon Sep 17 00:00:00 2001 From: Joachim Stolberg Date: Sat, 15 Jun 2019 16:14:46 +0200 Subject: [PATCH] lamp3 and power switch box added --- basis/lib.lua | 32 +++ furnace/booster.lua | 2 +- init.lua | 21 +- lamps/industriallamp3.lua | 66 +++++ lamps/lib.lua | 25 +- power/junctionbox.lua | 1 + power/power.lua | 41 +-- power/power2.lua | 330 +++++++++++++++++++++++ power/powerswitch.lua | 49 ++++ recipe_checker.lua | 34 +++ test/akku.lua | 104 +++++++ test/consumer.lua | 159 ----------- test/consumer2.lua | 140 ---------- test/generator.lua | 123 --------- test/perf_test.lua | 91 ------- test/sink.lua | 87 ++++++ test/source.lua | 56 ++++ test/switch.lua | 48 ++++ test/test.lua | 10 - textures/techage_appl_switch_off.png | Bin 687 -> 1274 bytes textures/techage_appl_switch_on.png | Bin 713 -> 1254 bytes textures/techage_electric_switch.png | Bin 0 -> 548 bytes textures/techage_industriallamp3.png | Bin 0 -> 262 bytes textures/techage_industriallamp3_on.png | Bin 0 -> 283 bytes textures/techage_industriallamp_inv3.png | Bin 0 -> 9466 bytes 25 files changed, 840 insertions(+), 579 deletions(-) create mode 100644 lamps/industriallamp3.lua create mode 100644 power/power2.lua create mode 100644 recipe_checker.lua create mode 100644 test/akku.lua delete mode 100644 test/consumer.lua delete mode 100644 test/consumer2.lua delete mode 100644 test/generator.lua delete mode 100644 test/perf_test.lua create mode 100644 test/sink.lua create mode 100644 test/source.lua create mode 100644 test/switch.lua delete mode 100644 test/test.lua create mode 100644 textures/techage_electric_switch.png create mode 100644 textures/techage_industriallamp3.png create mode 100644 textures/techage_industriallamp3_on.png create mode 100644 textures/techage_industriallamp_inv3.png diff --git a/basis/lib.lua b/basis/lib.lua index 925a3b0..7db8af6 100644 --- a/basis/lib.lua +++ b/basis/lib.lua @@ -21,6 +21,38 @@ local M = minetest.get_meta local MP = minetest.get_modpath("techage") local I,_ = dofile(MP.."/intllib.lua") +-- Input data to generate the Param2ToDir table +local Input = { + 8,9,10,11, -- 1 + 16,17,18,19, -- 2 + 4,5,6,7, -- 3 + 12,13,14,15, -- 4 + 0,1,2,3, -- 5 + 20,21,22,23, -- 6 +} + +-- translation from param2 to dir (out of the node upwards) +local Param2Dir = {} +for idx,val in ipairs(Input) do + Param2Dir[val] = math.floor((idx - 1) / 4) + 1 +end + +-- used by lamps and power switches +function techage.determine_node_bottom_as_dir(node) + return tubelib2.Turn180Deg[Param2Dir[node.param2] or 1] +end + +function techage.determine_node_top_as_dir(node) + return Param2Dir[node.param2] or 1 +end + +-- rotation rules (screwdriver) for wallmounted "facedir" nodes +function techage.rotate_wallmounted(param2) + local offs = math.floor(param2 / 4) * 4 + local rot = ((param2 % 4) + 1) % 4 + return offs + rot +end + function techage.range(val, min, max) val = tonumber(val) if val < min then return min end diff --git a/furnace/booster.lua b/furnace/booster.lua index ff73c39..32d570b 100644 --- a/furnace/booster.lua +++ b/furnace/booster.lua @@ -187,7 +187,7 @@ minetest.register_craft({ output = "techage:ta3_booster", recipe = { {"basic_materials:steel_bar", "default:wood", "basic_materials:steel_bar"}, - {"techage:steam_pipeS", "basic_materials:gear_steel", "techage:steam_pipeS"}, + {"", "basic_materials:gear_steel", ""}, {"basic_materials:steel_bar", "default:wood", "basic_materials:steel_bar"}, }, }) diff --git a/init.lua b/init.lua index bfee2fc..44934ca 100644 --- a/init.lua +++ b/init.lua @@ -47,6 +47,7 @@ else -- Power networks dofile(MP.."/power/power.lua") + dofile(MP.."/power/power2.lua") dofile(MP.."/power/junction.lua") dofile(MP.."/power/drive_axle.lua") dofile(MP.."/power/steam_pipe.lua") @@ -119,6 +120,7 @@ else dofile(MP.."/lamps/ceilinglamp.lua") dofile(MP.."/lamps/industriallamp1.lua") dofile(MP.."/lamps/industriallamp2.lua") + dofile(MP.."/lamps/industriallamp3.lua") -- Oil dofile(MP.."/oil/help.lua") @@ -132,17 +134,10 @@ else dofile(MP.."/nodes/basalt.lua") end - - --dofile(MP.."/test/generator.lua") - --dofile(MP.."/test/lamp.lua") --- dofile(MP.."/test/consumer.lua") - --dofile(MP.."/test/consumer2.lua") - --dofile(MP.."/test/test.lua") - - - --dofile(MP.."/fermenter/gasflare.lua") - - - --dofile(MP.."/nodes/test.lua") - --dofile(MP.."/mechanic/perf_test.lua") + -- Test + dofile(MP.."/recipe_checker.lua") + dofile(MP.."/test/sink.lua") + dofile(MP.."/test/source.lua") + dofile(MP.."/test/akku.lua") + dofile(MP.."/test/switch.lua") end \ No newline at end of file diff --git a/lamps/industriallamp3.lua b/lamps/industriallamp3.lua new file mode 100644 index 0000000..5a5034d --- /dev/null +++ b/lamps/industriallamp3.lua @@ -0,0 +1,66 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA3/TA4 Industrial Lamp 3 + +]]-- + +-- Load support for intllib. +local MP = minetest.get_modpath("techage") +local I,_ = dofile(MP.."/intllib.lua") + +local size = {x = 6/32, y = 4/32, z = 6/32} + +techage.register_lamp("techage:industriallamp3", { + description = "TA Industrial Lamp 3", + inventory_image = 'techage_industriallamp_inv3.png', + tiles = { + -- up, down, right, left, back, front + 'techage_industriallamp3.png', + 'techage_industriallamp3.png', + 'techage_industriallamp3.png^[transformR180', + 'techage_industriallamp3.png^[transformR180', + 'techage_industriallamp3.png', + 'techage_industriallamp3.png', + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.1875, -0.5, -0.1875, 0.1875, -0.375, 0.1875}, + }, + }, +},{ + tiles = { + -- up, down, right, left, back, front + 'techage_industriallamp3_on.png', + 'techage_industriallamp3_on.png', + 'techage_industriallamp3_on.png^[transformR180', + 'techage_industriallamp3_on.png^[transformR180', + 'techage_industriallamp3_on.png', + 'techage_industriallamp3_on.png', + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-0.1875, -0.5, -0.1875, 0.1875, -0.375, 0.1875}, + }, + }, +}) + +minetest.register_craft({ + output = "techage:industriallamp3_off 2", + recipe = { + {"default:glass", "default:glass", ""}, + {"techage:simplelamp_off", "dye:red", ""}, + {"basic_materials:steel_bar", "basic_materials:steel_bar", ""}, + }, +}) diff --git a/lamps/lib.lua b/lamps/lib.lua index 1c68204..6ad6f0b 100644 --- a/lamps/lib.lua +++ b/lamps/lib.lua @@ -7,27 +7,6 @@ local POWER_CONSUMPTION = 0.5 local Power = techage.ElectricCable --- Input data to generate the Param2ToDir table -local Input = { - 8,9,10,11, -- 1 - 16,17,18,19, -- 2 - 4,5,6,7, -- 3 - 12,13,14,15, -- 4 - 0,1,2,3, -- 5 - 20,21,22,23, -- 6 -} - -local Param2Dir = {} -for idx,val in ipairs(Input) do - Param2Dir[val] = math.floor((idx - 1) / 4) + 1 -end - -local function rotate(param2) - local offs = math.floor(param2 / 4) * 4 - local rot = ((param2 % 4) + 1) % 4 - return offs + rot -end - local function swap_node(pos, postfix) local node = techage.get_node_lvm(pos) local parts = string.split(node.name, "_") @@ -74,7 +53,7 @@ local function on_rotate(pos, node, user, mode, new_param2) if minetest.is_protected(pos, user:get_player_name()) then return false end - node.param2 = rotate(node.param2) + node.param2 = techage.rotate_wallmounted(node.param2) minetest.swap_node(pos, node) return true end @@ -87,7 +66,7 @@ local function on_place(itemstack, placer, pointed_thing) end local function determine_power_side(pos, node) - return {tubelib2.Turn180Deg[Param2Dir[node.param2] or 1]} + return {techage.determine_node_bottom_as_dir(node)} end function techage.register_lamp(basename, ndef_off, ndef_on) diff --git a/power/junctionbox.lua b/power/junctionbox.lua index d184201..e933186 100644 --- a/power/junctionbox.lua +++ b/power/junctionbox.lua @@ -39,6 +39,7 @@ techage.register_junction("techage:electric_junction", 2/8, Boxes, techage.Elect after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir) local name = "techage:electric_junction"..techage.junction_type(pos, techage.ElectricCable) minetest.swap_node(pos, {name = name, param2 = 0}) + techage.power2.on_network_change(pos) ------------ TODO end, }) diff --git a/power/power.lua b/power/power.lua index f350e6a..8596058 100644 --- a/power/power.lua +++ b/power/power.lua @@ -74,7 +74,6 @@ local function valid_indir(pos, in_dir) end end if s ~= "" then - local tbl for _,dir in ipairs(minetest.deserialize(s)) do if dir == in_dir then return true @@ -101,15 +100,16 @@ local function get_clbk(pos, clbk_name) end local function connection_walk(pos, clbk_name, sum) + local mem = tubelib2.get_mem(pos) + mem.interrupted_dirs = mem.interrupted_dirs or {} local clbk = get_clbk(pos, clbk_name) --print("connection_walk", S(pos), sum, clbk) if clbk then - local mem = tubelib2.get_mem(pos) sum = sum - (clbk(pos, mem, sum) or 0) end - local mem = tubelib2.get_mem(pos) - for _,item in pairs(mem.connections or {}) do - if item.pos and not pos_already_reached(item.pos) then + for out_dir,item in pairs(mem.connections or {}) do + if item.pos and not pos_already_reached(item.pos) and + not mem.interrupted_dirs[out_dir] then sum = connection_walk(item.pos, clbk_name, sum) end end @@ -155,29 +155,32 @@ techage.power = {} techage.power.power_distribution = power_distribution --- User to turn on/off the power by means of a power switch +-- Used to turn on/off the power by means of a power switch function techage.power.power_cut(pos, dir, cable, cut) local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0]) - local meta = M(npos) + + local node = minetest.get_node(npos) + if node.name ~= "techage:powerswitch_box" and + M(npos):get_string("techage_hidden_nodename") ~= "techage:powerswitch_box" then + return + end + + local mem = tubelib2.get_mem(npos) + mem.interrupted_dirs = mem.interrupted_dirs or {} if cut then - local param2 = meta:get_int("tl2_param2") - if param2 ~= 0 then - meta:set_int("cable_cut", param2) - meta:set_int("tl2_param2", 0) - cable:after_dig_tube(npos, {param2 = param2}) + mem.interrupted_dirs = {true, true, true, true, true, true} + for dir,_ in pairs(mem.connections) do + mem.interrupted_dirs[dir] = false + power_distribution(npos) + mem.interrupted_dirs[dir] = true end else - local param2 = meta:get_int("cable_cut") - if param2 ~= 0 then - meta:set_int("tl2_param2", param2) - meta:set_int("cable_cut", 0) - cable:tool_repair_tube(npos) - end + mem.interrupted_dirs = {} + power_distribution(npos) end end - function techage.power.register_node(names, pwr_def) for _,name in ipairs(names) do local ndef = minetest.registered_nodes[name] diff --git a/power/power2.lua b/power/power2.lua new file mode 100644 index 0000000..54e58a3 --- /dev/null +++ b/power/power2.lua @@ -0,0 +1,330 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Power distribution and consumption calculation + for any kind of power distribution network + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta +-- Techage Related Data +local PWR = function(pos) return (minetest.registered_nodes[minetest.get_node(pos).name] or {}).power end +local PWRN = function(node) return (minetest.registered_nodes[node.name] or {}).power end + +-- Used to determine the already passed nodes while power distribution +local Route = {} + +local function pos_already_reached(pos) + local key = minetest.hash_node_position(pos) + if not Route[key] then + Route[key] = true + return false + end + return true +end + +local SideToDir = {B=1, R=2, F=3, L=4, D=5, U=6} + +local function side_to_dir(param2, side) + local dir = SideToDir[side] + if dir < 5 then + dir = (((dir - 1) + (param2 % 4)) % 4) + 1 + end + return dir +end + +--function techage.get_pos(pos, side) +-- local node = minetest.get_node(pos) +-- local dir = nil +-- if node.name ~= "air" and node.name ~= "ignore" then +-- dir = side_to_dir(node.param2, side) +-- end +-- return tubelib2.get_pos(pos, dir) +--end + +local function set_conn_dirs(pos, sides) + local tbl = {} + local node = minetest.get_node(pos) + if type(sides) == "function" then + tbl = sides(pos, node) + else + for _,side in ipairs(sides) do + tbl[#tbl+1] = tubelib2.Turn180Deg[side_to_dir(node.param2, side)] + end + end + M(pos):set_string("power_dirs", minetest.serialize(tbl)) +end + +local function valid_indir(pos, in_dir) + local s = M(pos):get_string("power_dirs") + if s == "" then + local pwr = PWR(pos) + if pwr then + set_conn_dirs(pos, pwr.conn_sides) + end + end + if s ~= "" then + for _,dir in ipairs(minetest.deserialize(s)) do + if dir == in_dir then + return true + end + end + end + return false +end + +local function valid_outdir(pos, out_dir) + return valid_indir(pos, tubelib2.Turn180Deg[out_dir]) +end + +-- Both nodes are from the same power network type? +local function matching_nodes(pos, peer_pos) + local tube_type1 = pos and PWR(pos) and PWR(pos).power_network.tube_type + local tube_type2 = peer_pos and PWR(peer_pos) and PWR(peer_pos).power_network.tube_type + return not tube_type1 or not tube_type2 or tube_type1 == tube_type2 +end + +local function connection_walk(pos, clbk) + local mem = tubelib2.get_mem(pos) + mem.interrupted_dirs = mem.interrupted_dirs or {} + if clbk then + clbk(pos, mem) + end + for out_dir,item in pairs(mem.connections or {}) do + if item.pos and not pos_already_reached(item.pos) and + not mem.interrupted_dirs[out_dir] then + connection_walk(item.pos, clbk) + end + end +end + + +-- determine one "generating" node as master (largest hash number) +local function determine_master(pos) + Route = {} + pos_already_reached(pos) + local hash = 0 + local master = nil + connection_walk(pos, function(pos, mem) + if mem.generating then + local new = minetest.hash_node_position(pos) + if hash <= new then + hash = new + master = pos + end + end + end) + return master +end + +-- store master position on all network nodes +local function store_master(pos, master_pos) + Route = {} + pos_already_reached(pos) + connection_walk(pos, function(pos, mem) + mem.master_pos = master_pos + mem.is_master = false + end) +end + +-- called from master every 2 seconds +local function accounting(mem) + mem.debit2 = mem.debit1 or 0 + mem.credit2 = mem.credit1 or 0 + mem.secondary = mem.secondary or 0 + + mem.gap = mem.debit2 - mem.credit2 + mem.credit2 = mem.credit2 + mem.secondary + --print("needed = "..(mem.debit2 or 0)..", available = "..(mem.credit2 or 0)..", gap = "..mem.gap..", secondary = "..mem.secondary) + mem.debit1 = 0 + mem.credit1 = 0 + mem.secondary = 0 +end + +-- called from any generator +local function on_power_switch(pos) + print("on_power_change"..S(pos)) + local mem = tubelib2.get_mem(pos) + mem.master_pos = nil + mem.is_master = nil + + local mpos = determine_master(pos) + store_master(pos, mpos) + if mpos then + print("master = "..S(mpos)) + local mem = tubelib2.get_mem(mpos) + mem.is_master = true + return mem + end +end + +-- called from tubelib2.after_tube_update +local function on_network_change(pos) + local mem = on_power_switch(pos) + if mem then + accounting(mem) + end +end + +-- +-- Generic API functions +-- +techage.power2 = {} + +techage.power2.power_switched = on_power_switch +techage.power2.on_network_change = on_network_change + +function techage.power2.register_node(names, pwr_def) + for _,name in ipairs(names) do + local ndef = minetest.registered_nodes[name] + if ndef then + minetest.override_item(name, { + power = { + conn_sides = pwr_def.conn_sides or {"L", "R", "U", "D", "F", "B"}, + power_network = pwr_def.power_network, + after_place_node = ndef.after_place_node, + after_dig_node = ndef.after_dig_node, + after_tube_update = ndef.after_tube_update, + }, + -- after_place_node decorator + after_place_node = function(pos, placer, itemstack, pointed_thing) + local pwr = PWR(pos) + set_conn_dirs(pos, pwr.conn_sides) + pwr.power_network:after_place_node(pos) + if pwr.after_place_node then + return pwr.after_place_node(pos, placer, itemstack, pointed_thing) + end + end, + -- after_dig_node decorator + after_dig_node = function(pos, oldnode, oldmetadata, digger) + local pwr = PWRN(oldnode) + pwr.power_network:after_dig_node(pos) + minetest.after(0.1, tubelib2.del_mem, pos) -- At latest... + if pwr.after_dig_node then + return pwr.after_dig_node(pos, oldnode, oldmetadata, digger) + end + end, + -- tubelib2 callback, called after any connection change + after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir) + local pwr = PWR(pos) + local mem = tubelib2.get_mem(pos) + mem.connections = mem.connections or {} + if not peer_pos or not valid_indir(peer_pos, peer_in_dir) + or not valid_outdir(pos, out_dir) + or not matching_nodes(pos, peer_pos) then + mem.connections[out_dir] = nil -- del connection + else + mem.connections[out_dir] = {pos = peer_pos, in_dir = peer_in_dir} + end + -- To be called delayed, so that all network connections have been established + minetest.after(0.2, on_network_change, pos) + if pwr.after_tube_update then + return pwr.after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) + end + end, + }) + pwr_def.power_network:add_secondary_node_names({name}) + end + end +end + +function techage.power2.consume_power(pos, needed) + local master_pos = tubelib2.get_mem(pos).master_pos + if master_pos then + local mem = tubelib2.get_mem(master_pos) + mem.debit1 = mem.debit1 or 0 + mem.credit2 = mem.credit2 or 0 + + mem.debit1 = mem.debit1 + needed + if mem.credit2 >= needed then + mem.credit2 = mem.credit2 - needed + return needed + end + end + return 0 +end + +function techage.power2.provide_power(pos, available) + local mem = tubelib2.get_mem(pos) + if mem.is_master then + accounting(mem) + elseif mem.master_pos then + mem = tubelib2.get_mem(mem.master_pos) + else + return 0 + end + + mem.credit1 = mem.credit1 or 0 + mem.debit2 = mem.debit2 or 0 + + mem.credit1 = mem.credit1 + available + if mem.debit2 > available then + mem.debit2 = mem.debit2 - available + return available + else + local rest = mem.debit2 + mem.debit2 = 0 + return rest + end +end + +function techage.power2.secondary_power(pos, provide, consume) + local mem = tubelib2.get_mem(pos) + if mem.is_master then + accounting(mem) + elseif mem.master_pos then + mem = tubelib2.get_mem(mem.master_pos) + else + return 0 + end + + mem.gap = mem.gap or 0 + mem.secondary = (mem.secondary or 0) + provide + if mem.gap > 0 then + local val = math.min(mem.gap, provide) + mem.gap = mem.gap - val + return val + elseif mem.gap < 0 then + return math.max(mem.gap, -consume) + end + return 0 + +-- mem.debit2 = mem.debit2 or 0 + +-- mem.credit1 = mem.credit1 + available +-- if mem.debit2 > available then +-- mem.debit2 = mem.debit2 - available +-- return available +-- else +-- local rest = mem.debit2 +-- mem.debit2 = 0 +-- return rest +-- end +end + +function techage.power2.formspec_load_bar(charging, max_val) + local percent + charging = charging or 0 + max_val = max_val or 1 + if charging ~= 0 then + percent = 50 + math.ceil((charging * 50.0) / max_val) + end + + if charging > 0 then + return "techage_form_level_off.png^[lowpart:"..percent..":techage_form_level_charge.png" + elseif charging < 0 then + return "techage_form_level_unload.png^[lowpart:"..percent..":techage_form_level_off.png" + else + return "techage_form_level_off.png" + end +end diff --git a/power/powerswitch.lua b/power/powerswitch.lua index 6cd127e..93563e5 100644 --- a/power/powerswitch.lua +++ b/power/powerswitch.lua @@ -74,6 +74,7 @@ minetest.register_node("techage:powerswitch", { type = "fixed", fixed = { { -1/4, -8/16, -1/4, 1/4, -7/16, 1/4}, + { -1/6, -12/16, -1/6, 1/6, -8/16, 1/6}, }, }, @@ -103,6 +104,7 @@ minetest.register_node("techage:powerswitch_on", { type = "fixed", fixed = { { -1/4, -8/16, -1/4, 1/4, -7/16, 1/4}, + { -1/6, -12/16, -1/6, 1/6, -8/16, 1/6}, }, }, @@ -120,6 +122,53 @@ minetest.register_node("techage:powerswitch_on", { sounds = default.node_sound_wood_defaults(), }) +local function on_place(itemstack, placer, pointed_thing) + if pointed_thing.type ~= "node" then + return itemstack + end + return minetest.rotate_and_place(itemstack, placer, pointed_thing) +end + +local function on_rotate(pos, node, user, mode, new_param2) + if minetest.is_protected(pos, user:get_player_name()) then + return false + end + node.param2 = techage.rotate_wallmounted(node.param2) + minetest.swap_node(pos, node) + return true +end + +minetest.register_node("techage:powerswitch_box", { + description = I("TA Power Switch Box"), + tiles = { + -- up, down, right, left, back, front + 'techage_electric_switch.png', + 'techage_electric_switch.png', + 'techage_electric_junction.png', + 'techage_electric_junction.png', + 'techage_electric_switch.png', + 'techage_electric_switch.png', + }, + + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + { -2/4, -1/4, -1/4, 2/4, 1/4, 1/4}, + }, + }, + + on_place = on_place, + on_rotate = on_rotate, + paramtype2 = "facedir", + groups = {choppy=2, cracky=2, crumbly=2, techage_trowel = 1}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +techage.power.register_node({"techage:powerswitch_box"}, { + power_network = techage.ElectricCable}) + minetest.register_craft({ output = "techage:powerswitch 2", recipe = { diff --git a/recipe_checker.lua b/recipe_checker.lua new file mode 100644 index 0000000..f8ee53a --- /dev/null +++ b/recipe_checker.lua @@ -0,0 +1,34 @@ +-- +-- Script to check recipe overlaps +-- +local Recipes = {} + +local function recipe_key(items) + local tbl = {} + for idx = 1,9 do + tbl[#tbl + 1] = items[idx] or "#" + end + return table.concat(tbl, "-") +end + +minetest.after(1, function() + for name,_ in pairs(minetest.registered_items) do + local mod = string.split(name, ":")[1] + if mod == "techage" or mod == "signs_bot" then + local recipes = minetest.get_all_craft_recipes(name) + if recipes then + for _,recipe in ipairs(recipes) do + if recipe and recipe.items then + --print(dump(recipe.items)) + local key = recipe_key(recipe.items) + if Recipes[key] then + local text = Recipes[key].." and "..name.." have the same incredients" + minetest.log("error", text) + end + Recipes[key] = name + end + end + end + end + end +end) diff --git a/test/akku.lua b/test/akku.lua new file mode 100644 index 0000000..8664c87 --- /dev/null +++ b/test/akku.lua @@ -0,0 +1,104 @@ +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +local CYCLE_TIME = 2 +local PWR_PERF = 10 +local PWR_CAPA = 300 + +local Cable = techage.ElectricCable +local secondary_power = techage.power2.secondary_power + +local function in_range(val, min, max) + if val < min then return min end + if val > max then return max end + return val +end + +local function formspec(pos, mem) + return "size[5,3]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "image[0,0.5;1,2;"..techage.power.formspec_power_bar(PWR_CAPA, mem.capa or 0).."]".. + "label[0.2,2.5;Load]".. + "button[1.1,1;1.8,1;update;Update]".. + "image[4,0.5;1,2;"..techage.power2.formspec_load_bar(-(mem.delivered or 0), PWR_PERF).."]".. + "label[4.2,2.5;Flow]" +end + +local function node_timer(pos, elapsed) + --print("node_timer akku "..S(pos)) + local mem = tubelib2.get_mem(pos) + mem.capa = mem.capa or 0 + if mem.generating then + local delivered + if mem.capa >= PWR_CAPA then + delivered = secondary_power(pos, PWR_PERF, 0) + elseif mem.capa <= 0 then + delivered = secondary_power(pos, 0, PWR_PERF) + else + delivered = secondary_power(pos, PWR_PERF, PWR_PERF) + end + if delivered ~= mem.delivered then + mem.delivered = delivered + --techage.power2.power_switched(pos) + end + mem.capa = mem.capa - mem.delivered + mem.capa = in_range(mem.capa, 0, PWR_CAPA) + --print("delivered = "..mem.delivered) + return true + end + return false +end + +local function on_receive_fields(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + return + end + + if fields.update then + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(pos, mem)) + end +end + +local function on_rightclick(pos) + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(pos, mem)) +end + +local function after_place_node(pos, placer) + local mem = tubelib2.get_mem(pos) + mem.generating = true + mem.capa = 0 + minetest.get_node_timer(pos):start(CYCLE_TIME) + techage.power2.power_switched(pos) + on_rightclick(pos) +end + + +minetest.register_node("techage:akku", { + description = "Akku", + tiles = { + -- up, down, right, left, back, front + 'techage_electric_button.png^techage_appl_source.png', + 'techage_electric_button.png^techage_appl_source.png', + 'techage_electric_button.png^techage_appl_source.png^techage_electric_plug.png', + 'techage_electric_button.png^techage_appl_source.png', + 'techage_electric_button.png^techage_appl_source.png', + 'techage_electric_button.png^techage_appl_source.png', + }, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + after_place_node = after_place_node, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + on_timer = node_timer, +}) + +techage.power2.register_node({"techage:akku"}, { + power_network = Cable, +}) diff --git a/test/consumer.lua b/test/consumer.lua deleted file mode 100644 index 535d341..0000000 --- a/test/consumer.lua +++ /dev/null @@ -1,159 +0,0 @@ --- for lazy programmers -local S = function(pos) if pos then return minetest.pos_to_string(pos) end end -local P = minetest.string_to_pos -local M = minetest.get_meta - --- Load support for intllib. -local MP = minetest.get_modpath("techage") -local I,_ = dofile(MP.."/intllib.lua") - -local POWER_CONSUMPTION = 2 -local STANDBY_TICKS = 4 -local CYCLE_TIME = 4 - -local Cable = techage.ElectricCable -local consumer = techage.consumer - -local function formspec(self, pos, mem) - return "size[8,7]".. - default.gui_bg.. - default.gui_bg_img.. - default.gui_slots.. - "image_button[3,1.2;1,1;".. self:get_state_button_image(mem) ..";state_button;]".. - "list[current_player;main;0,3;8,4;]".. - default.get_hotbar_bg(0, 3) -end - -local function valid_power_dir(pos, power_dir, in_dir) - return power_dir == in_dir -end - -local function start_node(pos, mem, state) - consumer.turn_power_on(pos, POWER_CONSUMPTION) -end - -local function stop_node(pos, mem, state) - consumer.turn_power_on(pos, 0) -end - -local State = techage.NodeStates:new({ - node_name_passive = "techage:ele_consumer", - node_name_active = "techage:ele_consumer_on", - cycle_time = CYCLE_TIME, - standby_ticks = STANDBY_TICKS, - formspec_func = formspec, - start_node = start_node, - stop_node = stop_node, -}) - -local function lamp_turn_on_clbk(pos, in_dir, sum) - local mem = tubelib2.get_mem(pos) - local state = State:get_state(mem) - - if sum > 0 and state == techage.FAULT then - State:stop(pos, mem) - State:start(pos, mem) - elseif sum <= 0 and state == techage.RUNNING then - State:fault(pos, mem) - end -end - -local function node_timer(pos, elapsed) - local mem = tubelib2.get_mem(pos) - return State:is_active(mem) -end - -local function on_receive_fields(pos, formname, fields, player) - if minetest.is_protected(pos, player:get_player_name()) then - return - end - local mem = tubelib2.get_mem(pos) - State:state_button_event(pos, mem, fields) - - if fields.update then - M(pos):set_string("formspec", formspec(State, pos, mem)) - end -end - -local function on_rightclick(pos) - local mem = tubelib2.get_mem(pos) - M(pos):set_string("formspec", formspec(State, pos, mem)) -end - -minetest.register_node("techage:ele_consumer", { - description = "Consumer", - tiles = { - -- up, down, right, left, back, front - 'techage_electric_button.png', - 'techage_electric_button.png', - 'techage_electric_button.png', - 'techage_electric_button.png^techage_electric_plug.png', - 'techage_electric_button.png', - 'techage_electric_button.png', - }, - techage = { - turn_on = lamp_turn_on_clbk, - read_power_consumption = consumer.read_power_consumption, - power_network = Cable, - power_side = "L", - valid_power_dir = valid_power_dir, - }, - - after_place_node = function(pos, placer) - local mem = consumer.after_place_node(pos, placer) - State:node_init(pos, mem, "") - on_rightclick(pos) - end, - - after_dig_node = function(pos, oldnode, oldmetadata, digger) - State:after_dig_node(pos, oldnode, oldmetadata, digger) - consumer.after_dig_node(pos, oldnode) - end, - - after_tube_update = consumer.after_tube_update, - on_timer = node_timer, - on_receive_fields = on_receive_fields, - on_rightclick = on_rightclick, - - paramtype = "light", - light_source = 0, - sunlight_propagates = true, - paramtype2 = "facedir", - groups = {choppy=2, cracky=2, crumbly=2}, - is_ground_content = false, - sounds = default.node_sound_wood_defaults(), -}) - -minetest.register_node("techage:ele_consumer_on", { - description = "Consumer", - tiles = { - 'techage_electric_button.png', - }, - techage = { - turn_on = lamp_turn_on_clbk, - read_power_consumption = consumer.read_power_consumption, - power_network = Cable, - power_side = "L", - valid_power_dir = valid_power_dir, - }, - - after_dig_node = function(pos, oldnode, oldmetadata, digger) - State:after_dig_node(pos, oldnode, oldmetadata, digger) - consumer.after_dig_node(pos, oldnode) - end, - - after_tube_update = consumer.after_tube_update, - on_timer = node_timer, - on_receive_fields = on_receive_fields, - on_rightclick = on_rightclick, - - paramtype = "light", - light_source = LIGHT_MAX, - sunlight_propagates = true, - paramtype2 = "facedir", - drop = "techage:ele_consumer", - groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, - is_ground_content = false, - sounds = default.node_sound_wood_defaults(), -}) - diff --git a/test/consumer2.lua b/test/consumer2.lua deleted file mode 100644 index 4e9c455..0000000 --- a/test/consumer2.lua +++ /dev/null @@ -1,140 +0,0 @@ ---[[ - - TechAge - ======= - - Copyright (C) 2019 Joachim Stolberg - - LGPLv2.1+ - See LICENSE.txt for more information - - TA2 Consumer - -]]-- - --- for lazy programmers -local S = function(pos) if pos then return minetest.pos_to_string(pos) end end -local P = minetest.string_to_pos -local M = minetest.get_meta - -local POWER_CONSUMPTION = 4 - -local Axle = techage.Axle -local consumer = techage.consumer - -local function swap_node(pos, name) - local node = minetest.get_node(pos) - if node.name == name then - return - end - node.name = name - minetest.swap_node(pos, node) -end - --- To be able to check if power connection is on the --- correct node side (power_dir == in_dir) -local function valid_power_dir(pos, power_dir, in_dir) - return power_dir == in_dir -end - -local function turn_on_clbk(pos, in_dir, sum) - local mem = tubelib2.get_mem(pos) - if sum > 0 then - swap_node(pos, "techage:consumer_on") - else - swap_node(pos, "techage:consumer") - end -end - -minetest.register_node("techage:consumer", { - description = "TechAge Consumer", - tiles = { - -- up, down, right, left, back, front - 'techage_filling_ta2.png^techage_frame_ta2.png', - 'techage_filling_ta2.png^techage_frame_ta2.png', - 'techage_filling_ta2.png^techage_frame_ta2.png', - 'techage_filling_ta2.png^techage_frame_ta2.png^techage_axle_clutch.png', - 'techage_filling_ta2.png^techage_frame_ta2.png', - 'techage_filling_ta2.png^techage_frame_ta2.png', - }, - techage = { - turn_on = turn_on_clbk, - read_power_consumption = consumer.read_power_consumption, - power_network = Axle, - power_side = "L", - valid_power_dir = valid_power_dir, - animated_power_network = true, - }, - - after_place_node = function(pos, placer) - local mem = consumer.after_place_node(pos, placer) - mem.power_consumption = POWER_CONSUMPTION - end, - - after_tube_update = consumer.after_tube_update, - after_dig_node = consumer.after_dig_node, - - paramtype2 = "facedir", - groups = {choppy=2, cracky=2, crumbly=2}, - is_ground_content = false, - sounds = default.node_sound_wood_defaults(), -}) - -minetest.register_node("techage:consumer_on", { - description = "TechAge Consumer", - tiles = { - -- up, down, right, left, back, front - 'techage_filling_ta2.png^techage_frame_ta2.png', - 'techage_filling_ta2.png^techage_frame_ta2.png', - { - image = "techage_filling4_ta2.png^techage_frame4_ta2.png^techage_appl_compressor4.png", - backface_culling = false, - animation = { - type = "vertical_frames", - aspect_w = 32, - aspect_h = 32, - length = 0.6, - }, - }, - 'techage_filling_ta2.png^techage_frame_ta2.png^techage_axle_clutch.png', - { - image = "techage_filling4_ta2.png^techage_frame4_ta2.png^techage_appl_compressor4.png", - backface_culling = false, - animation = { - type = "vertical_frames", - aspect_w = 32, - aspect_h = 32, - length = 0.6, - }, - }, - { - image = "techage_filling4_ta2.png^techage_frame4_ta2.png^techage_appl_compressor4.png", - backface_culling = false, - animation = { - type = "vertical_frames", - aspect_w = 32, - aspect_h = 32, - length = 0.6, - }, - }, - }, - techage = { - turn_on = turn_on_clbk, - read_power_consumption = consumer.read_power_consumption, - power_network = Axle, - power_side = "L", - valid_power_dir = valid_power_dir, - animated_power_network = true, - }, - - after_tube_update = consumer.after_tube_update, - after_dig_node = consumer.after_dig_node, - - paramtype2 = "facedir", - groups = {not_in_creative_inventory=1}, - diggable = false, - drop = "techage:consumer", - is_ground_content = false, - sounds = default.node_sound_wood_defaults(), -}) - diff --git a/test/generator.lua b/test/generator.lua deleted file mode 100644 index c64ebff..0000000 --- a/test/generator.lua +++ /dev/null @@ -1,123 +0,0 @@ --- for lazy programmers -local S = function(pos) if pos then return minetest.pos_to_string(pos) end end -local P = minetest.string_to_pos -local M = minetest.get_meta - --- Load support for intllib. -local MP = minetest.get_modpath("techage") -local I,_ = dofile(MP.."/intllib.lua") - -local STANDBY_TICKS = 4 -local COUNTDOWN_TICKS = 4 -local CYCLE_TIME = 16 -local POWER_CAPACITY = 30 - -local Power = techage.ElectricCable ---local Power = techage.SteamPipe -local generator = techage.generator - -local function formspec(self, pos, mem) - return "size[8,7]".. - default.gui_bg.. - default.gui_bg_img.. - default.gui_slots.. - "image[6,0.5;1,2;"..generator.formspec_level(mem, mem.power_result).. - "image_button[5,1;1,1;".. self:get_state_button_image(mem) ..";state_button;]".. - "button[2.5,1;1.8,1;update;"..I("Update").."]".. - "list[current_player;main;0,3;8,4;]".. - default.get_hotbar_bg(0, 3) -end - -local function start_node(pos, mem, state) - generator.turn_power_on(pos, POWER_CAPACITY) -end - -local function stop_node(pos, mem, state) - generator.turn_power_on(pos, 0) -end - -local State = techage.NodeStates:new({ - node_name_passive = "techage:test_generator", - cycle_time = CYCLE_TIME, - standby_ticks = STANDBY_TICKS, - formspec_func = formspec, - start_node = start_node, - stop_node = stop_node, -}) - - -local function node_timer(pos, elapsed) - local mem = tubelib2.get_mem(pos) - return State:is_active(mem) -end - -local function turn_power_on(pos, in_dir, sum) - local mem = tubelib2.get_mem(pos) - -- store result for formspec - mem.power_result = sum - if State:is_active(mem) and sum <= 0 then - State:fault(pos, mem) - -- No automatic turn on - mem.power_capacity = 0 - end - M(pos):set_string("formspec", formspec(State, pos, mem)) -end - -local function on_receive_fields(pos, formname, fields, player) - if minetest.is_protected(pos, player:get_player_name()) then - return - end - local mem = tubelib2.get_mem(pos) - State:state_button_event(pos, mem, fields) - - if fields.update then - M(pos):set_string("formspec", formspec(State, pos, mem)) - end -end - -local function on_rightclick(pos) - local mem = tubelib2.get_mem(pos) - M(pos):set_string("formspec", formspec(State, pos, mem)) -end - -minetest.register_node("techage:test_generator", { - description = "TechAge Test Generator", - tiles = { - -- up, down, right, left, back, front - 'techage_electric_button.png^techage_electric_power.png', - 'techage_electric_button.png^techage_electric_power.png', - 'techage_electric_button.png^techage_electric_power.png^techage_electric_plug.png', - 'techage_electric_button.png^techage_electric_power.png', - 'techage_electric_button.png^techage_electric_power.png', - 'techage_electric_button.png^techage_electric_power.png', - }, - paramtype2 = "facedir", - groups = {cracky=2, crumbly=2, choppy=2}, - on_rotate = screwdriver.disallow, - is_ground_content = false, - - techage = { - turn_on = turn_power_on, - read_power_consumption = generator.read_power_consumption, - power_network = Power, - power_side = "R", - }, - - after_place_node = function(pos, placer) - local mem = generator.after_place_node(pos) - State:node_init(pos, mem, "") - on_rightclick(pos) - end, - - after_dig_node = function(pos, oldnode, oldmetadata, digger) - State:after_dig_node(pos, oldnode, oldmetadata, digger) - generator.after_dig_node(pos, oldnode) - end, - - after_tube_update = generator.after_tube_update, - on_receive_fields = on_receive_fields, - on_rightclick = on_rightclick, - on_timer = node_timer, -}) - -Power:add_secondary_node_names({"techage:test_generator"}) diff --git a/test/perf_test.lua b/test/perf_test.lua deleted file mode 100644 index e415d04..0000000 --- a/test/perf_test.lua +++ /dev/null @@ -1,91 +0,0 @@ ---[[ - - TechAge - ======= - - Copyright (C) 2019 Joachim Stolberg - - LGPLv2.1+ - See LICENSE.txt for more information - - distributor.lua: - -]]-- - --- for lazy programmers -local S = function(pos) if pos then return minetest.pos_to_string(pos) end end -local P = minetest.string_to_pos -local M = minetest.get_meta -local N = minetest.get_node - -local function formspec() - return "size[10.5,8.5]".. - default.gui_bg.. - default.gui_bg_img.. - default.gui_slots.. - "list[context;src;0,0;2,4;]".. - "list[current_player;main;1.25,4.5;8,4;]".. - "listring[context;src]".. - "listring[current_player;main]" -end - - --- move items to the output slots -local function keep_running(pos, elapsed) - local meta = M(pos) - local inv = meta:get_inventory() - local name, num - - for i = 1,10 do - --local list = inv:get_list("src") - for i = 1,8 do - --local stack = list[i] - local stack = inv:get_stack("src", i) - if stack:get_count() > 0 then - local taken = inv:remove_item("src", stack) - num = taken:get_count() - inv:add_item("src", taken) - break - end - end - end - return true -end - -local function after_place_node(pos, placer) - local meta = M(pos) - local inv = meta:get_inventory() - inv:set_size('src', 8) - inv:add_item("src", ItemStack("wool:blue")) - inv:add_item("src", ItemStack("wool:red")) - inv:add_item("src", ItemStack("wool:green")) - meta:set_string("formspec", formspec()) - - minetest.get_node_timer(pos):start(0.1) -end - -minetest.register_node("techage:perf_test", { - description = "perf_test", - tiles = {"techage_filling_ta2.png"}, - - after_place_node = after_place_node, - - on_timer = keep_running, - - paramtype = "light", - sunlight_propagates = true, - paramtype2 = "facedir", - groups = {choppy=2, cracky=2, crumbly=2}, - is_ground_content = false, -}) - - -minetest.register_lbm({ - label = "[TechAge] Node update", - name = "techage:perf_test", - nodenames = {"techage:perf_test"}, - run_at_every_load = true, - action = function(pos, node) - after_place_node(pos) - end -}) diff --git a/test/sink.lua b/test/sink.lua new file mode 100644 index 0000000..6b098d9 --- /dev/null +++ b/test/sink.lua @@ -0,0 +1,87 @@ +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +local PWR_NEEDED = 2 +local CYCLE_TIME = 2 + +local Cable = techage.ElectricCable +local consume_power = techage.power2.consume_power + +local function swap_node(pos, name) + local node = minetest.get_node(pos) + if node.name == name then + return + end + node.name = name + minetest.swap_node(pos, node) +end + + +local function node_timer(pos, elapsed) + --print("node_timer sink "..S(pos)) + local mem = tubelib2.get_mem(pos) + if mem.running then + local got = consume_power(pos, PWR_NEEDED) + if got < PWR_NEEDED then + swap_node(pos, "techage:sink") + else + swap_node(pos, "techage:sink_on") + end + return true + end + swap_node(pos, "techage:sink") + return false +end + + +local function on_rightclick(pos, node, clicker) + local mem = tubelib2.get_mem(pos) + if not mem.running then + mem.running = true + node_timer(pos, 2) + minetest.get_node_timer(pos):start(CYCLE_TIME) + else + swap_node(pos, "techage:sink") + minetest.get_node_timer(pos):stop() + mem.running = false + end +end + + +minetest.register_node("techage:sink", { + description = "Sink", + tiles = {'techage_electric_button.png'}, + on_timer = node_timer, + on_rightclick = on_rightclick, + + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {choppy=2, cracky=2, crumbly=2}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_node("techage:sink_on", { + description = "Sink", + tiles = {'techage_electric_button.png'}, + on_timer = node_timer, + on_rightclick = on_rightclick, + + paramtype = "light", + light_source = minetest.LIGHT_MAX, + sunlight_propagates = true, + paramtype2 = "facedir", + diggable = false, + drop = "", + groups = {not_in_creative_inventory=1}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +techage.power2.register_node({"techage:sink", "techage:sink_on"}, { + power_network = Cable, +}) diff --git a/test/source.lua b/test/source.lua new file mode 100644 index 0000000..1aff7df --- /dev/null +++ b/test/source.lua @@ -0,0 +1,56 @@ +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +local CYCLE_TIME = 2 +local PWR_CAPA = 15 + +local Cable = techage.ElectricCable +local provide_power = techage.power2.provide_power + +local function node_timer(pos, elapsed) + --print("node_timer source "..S(pos)) + local mem = tubelib2.get_mem(pos) + if mem.generating then + local delivered = provide_power(pos, PWR_CAPA) + return true + end + return false +end + +local function on_rightclick(pos, node, clicker) + local mem = tubelib2.get_mem(pos) + if not mem.generating then + mem.generating = true + node_timer(pos, 2) + minetest.get_node_timer(pos):start(CYCLE_TIME) + else + minetest.get_node_timer(pos):stop() + mem.generating = false + end + techage.power2.power_switched(pos) +end + + +minetest.register_node("techage:source", { + description = "Source", + tiles = { + -- up, down, right, left, back, front + 'techage_electric_button.png^techage_appl_electronic_fab.png', + 'techage_electric_button.png^techage_appl_electronic_fab.png', + 'techage_electric_button.png^techage_appl_electronic_fab.png^techage_electric_plug.png', + 'techage_electric_button.png^techage_appl_electronic_fab.png', + 'techage_electric_button.png^techage_appl_electronic_fab.png', + 'techage_electric_button.png^techage_appl_electronic_fab.png', + }, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + on_rightclick = on_rightclick, + on_timer = node_timer, +}) + +techage.power2.register_node({"techage:source"}, { + power_network = Cable, +}) diff --git a/test/switch.lua b/test/switch.lua new file mode 100644 index 0000000..6a3cd27 --- /dev/null +++ b/test/switch.lua @@ -0,0 +1,48 @@ +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +local Cable = techage.ElectricCable +local on_network_change = techage.power2.on_network_change +local power_switched = techage.power2.power_switched + +local function on_rightclick(pos, node, clicker) + local mem = tubelib2.get_mem(pos) + + mem.interrupted_dirs = mem.interrupted_dirs or {} + mem.interrupted = not mem.interrupted + print("switch", mem.interrupted) + if mem.interrupted then + mem.interrupted_dirs = {true, true, true, true, true, true} + for dir,_ in pairs(mem.connections) do + mem.interrupted_dirs[dir] = false + --on_network_change(pos) + power_switched(pos) + mem.interrupted_dirs[dir] = true + end + else + mem.interrupted_dirs = {} + power_switched(pos) + end +end + + +minetest.register_node("techage:switch", { + description = "Switch", + tiles = {'techage_appl_switch_inv.png'}, + on_timer = node_timer, + on_rightclick = on_rightclick, + + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {choppy=2, cracky=2, crumbly=2}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +techage.power2.register_node({"techage:switch"}, { + power_network = Cable, +}) diff --git a/test/test.lua b/test/test.lua deleted file mode 100644 index abbba07..0000000 --- a/test/test.lua +++ /dev/null @@ -1,10 +0,0 @@ -local function bubble(pos1, pos2, posc, amount) - local radius = math.floor(math.pow(amount, 0.333)) - local xc = pos2.x - pos1.x - for x = posc.x - radius, posc.x + radius do - for y = posc.y - radius, posc.y + radius do - for z = posc.z - radius, posc.z + radius do - local idx = x - pos1.x + - (y - pos1.y) * 16 + - (z - pos1.z) * 16 * 16 -print() diff --git a/textures/techage_appl_switch_off.png b/textures/techage_appl_switch_off.png index 513b1e992447fdcd47eec98c59c07df7f0d95c3c..76bd2faeea92ec349e9c0cb8ee3319cbbfc1fa91 100644 GIT binary patch delta 1168 zcmV;B1aJGV1^Nk)QV9kR4Kx@lx!#D8TPuGAUP(kjRCwC$TECCfMil;L#-3en?<9U7 zr$`jnMY$#d2`OC!5};JMg8zVu8qwt+prDFUpj7EM)hR*(8W0eP@+gu>hd^3jj{~7I zyK8$ro)p|JYwy@QWAColI={-l-pqU7%y{1S=8@)a7v#B(W5_Cv)ywF#@1Y3E+joDf z-Gg;*;~362sE)@WW9RN%9kgPX}eiO~%)t_5`$-Uw*QkkI$uaA>>W(=f#YEqD*p@f60-O29eLs?k3KwoLwgvc^S6YvRY)BF@bODr zy!MB_t-HTQrrU>4KCDjrUOrCkYys|eHuTq1 z$eqn05DUWwTspo20J#6r!FNaNSi8Mp+*aJyBs)3hz_&SOKr*Xq8#@kQ==|XN{znHd zu0PKVL{|1mlk*tUWoU12ZbuxB%jmPwZztzl^z2NsTM2*+L)eyur6Yewz9cYHSe>c! z)7H_oGs$ix0EAKmLJXDCSluj~H__F#lO~WXnl!C+S=}PZB=)HdfDS_&u)0}|eo`Ar zVe=$AlhW?WT|fu{Ap{s>=((;E*;)bARmq2ZKbM_K066CmLV!{_)~I!Gq9cTe%#@;p zbDmCCM^2)QB0);c@o&Qyb3{(k8VLCs4X}daj#~4Nij# z%tIEA3*?yvI=ZFc0$D)N)meBhkOc&jZ}W12#BFeNi<=e~Si66{fi(cQ^^fxXrMu^6 z#s!Q*;kDDJ47&ZV(4Stse#rQzi9;?`TDSbU#&Z+eeD<3|7X>>~K>`8Qgq;TOO4Etd91{!URVtg9pr}e^7o}8I6$%^2*RcxBxk=8xU$?py8>|30^!b2RyO*nh#;T7_ iyIY9ydHZg)d-xxBW{({8!ik0 delta 577 zcmV-H0>1tF39ki^QV9hW5g#NUp2%5|TPuG86G=otRCwC$n@vjsQ51&XnFa|*p^t@0 z(86u*41#u{(B4J=pq~)^2p26|M633*b0JtOiGr54h`?47BO$@0P6ke!lu0a{5AU7y zJuAJA_uTW$Id{&vhf$2PT_=}ag8CIxF5jDOr%`n10R%t*1V8`;KmY_l00i)z3hSu@AQAJO zVmj8H1FI}$@z=FyIl4dH{g$`u->-gr z?U-G8A)sUoIE^BlMsWm!djJ7McIv(C^6xNaefNI;q z_R2ESrR;nI7#a>>Wc=`RwQb?y=w3JoRiyE5gU7`^=Dv0=0QfOT6a+Pj#*2i&L@26v z|F=29PMhfg1V8`;KmfCnFjCs_Il?ZW=mE1<5X1{a6h%Tjz>guKDnel);KhF+k>#+^ z^MG_Ii!=b7JWl+*aK6LGAOR47SkdI)g2CbexM^)n9N%LINbrE5?eXi{v!^bt_3EIz zufoO$27^y5E$h%@NpyQE?AsRLAtFvMp1fH*i}tvu{=967QhS__K>{EE0w4eaAOHd& zfGC#3lisfSGrt~7F@;G8x*0731hD9PK-266nm|wW(J{MH79Ui1)9m33Ob4ceCSd_# P00000NkvXXu0mjf-%AGN diff --git a/textures/techage_appl_switch_on.png b/textures/techage_appl_switch_on.png index 115dd334c9771c1b82b59da33c073d068bacd36c..a2ac8127d4cd1ce132b90a4747bdd5149296a3e7 100644 GIT binary patch delta 1148 zcmV-?1cUp@1?CBmQV9kR4K@nuHqAnjTPuGAN=ZaPRCwC$TD_0lL=^wccx-QO@09O5 zP7xGGg>nr_fs{5Ug@}qP=qXaAPRl=#A{C-Qh=P8pPAF1HLj*#i3=K#Z5ot*_9ML74 z{TQ!jCPj8PYp=h?b8D})e%0FR=XvipZ)V=_`H|K)pQtOlXOUN$Ti4O++(Q{+I(C1% zGXT4?dlnyDTFY<6x`XpF%pnux%R_W1y76vaHk*-n)1qaQ*pbH%NHwETRtgRVDxyCBE;&b{sSsjUeEJKq(cg zQ+NcNq;Sqb08mOnEeo#e24_Vn5JGa#qvt$kp6fmT`scu9uhTD&4U(M})ZsqV z;rt_PG#heO=ld=IEH0A)L#2tZUN#~+QrYHMZ~UYQIa%){hJ#AfDBAj2#dEp9hG%43x&#F;14Dr8%GXSyNjh0Z>xHW(;em zPQ5K)Ca}6iIHaRvYHK6_gi`oIOka@1>K5U=j;^V#EH0#GSy>XROOt;;kB3I=6AwVge3n@1Kq9cR|aQQ`okR+zI z#^wQ*Wnp8ziP6XpgjIjyml`SoSvPmk}Oo_FHrA7;m3C@l^XC|z3% z=K^UnxCmJ)E?`1zvg$597svxbX6Kik3*-So=h>y_0#S~gWI=yqAzWa4^P+|ce;2YO zV&A1@tHvg{DpCWm>HtLU0q^?oGHgQsex=HEK+D#MKEXNynp@X_btiN?`^U-$4YuFaUxEqvYBArDqFY8)~&L!#fiFAc3E0w zqf%k*{5sTuS(q4({iNNm*q>w~JY@nw~pA*Q`Q@@%nC=4z%rJ78`3x_FkkCSkaPF0L*{G zw~Ck>0f;*Q&Fi94FqGMwFeG;0pKrHWa%lt*00KY&2mk>f00e*l5P+c+)^i6yD#0u< zUF*h$Rgu#9dgBp$hnKU<_SD|QenKBw*R1l@)K&%P-7&qY6pWEZXY<{E@%Q}u)!5gr zY0H}eO2L5JEWvG-zCln9AOIoR4-kK0KZibZ4u~=U*?~(1@Kp9*HS#CL{|(@`z$fpE z1fhl{c-Chl0e~eSQ4rKH1}_pq;5p#_t;h9#J_-P?k2(N=-PPx*d;802=5}js3s2>{ zv7|VX(6La0`^#zW5fphT%rk<-15gK449(kWZ7aS2YHbT!ON-%O1$Hko!%u&i=^&18 z`If;6Sv>CoDsUYT^e-6tDZo?Mh$xDLZ<+!GAnrM! pWjb69=(#?+rY)uOyuMqegD-s(tzKNNmw3+?W zXZ6pRJpqVj&Y1{Av*u2kJ#R7)&6z)C?t-a6G;iTFAez5uIuI>bJOhXpE}02Li zylgfQEm=MXh?cIHyKLpWWh)mfTRwmJswHdIEnmBS#k%#YH*Q|D35d3=-Mn?(79iTT zek%}d->_}RrX9Ps?ApC`_nsq1cK`luoB(tgV@Z%-FoVOh8)+a;lDE4HLkFv@2av;A z;1OBOz`!jG!i)^F=12eq*-JcqUD+SA@$-nu3$IPL0qSA*ba4!^IQ=%TG3<~5&mEV$ zyYjYPdEK>Wlh&bzt`Gn1(dLn{Bo?W|qq7yaXBMh0*KG6lD%cKDpsqeTHe> z1bdHEx9stMb+ztnf4|?f5yBHdy`nf@=m9M~R=c^qq zaVAmPfs+p3m~v=-$^5nlGdw;XbUnlsNApm~Uf0ZDYTf+uhkNFBUbG zi-nqIZSZzGS#;&q7rXxzne1!pHy>Co|NVoGM8=)?`+v<^{-&PzJ^w=f@2i^@7v4Yc agZa`Ky}1q1_AJ0qV(@hJb6Mw<&;$UUmH*=a literal 0 HcmV?d00001 diff --git a/textures/techage_industriallamp3.png b/textures/techage_industriallamp3.png new file mode 100644 index 0000000000000000000000000000000000000000..d2dd29774ac1f83069bc5c87b1147f879b4f3281 GIT binary patch literal 262 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzwj^(N7l!{JxM1({$v_d#0*}aI z1_o|n5N2eUHAey{$X?><>&pI^jh9nLGqgr+4^U{br;B4q#NoHoHuAO_@VMy5uVG03 z^CfYS&y>yuQx|@XvU5GxC>RM$cxmG(pw| z3FRQK>9$+HeZ6P0d$ZmJe%o!O)>ll+r4*m&PuY|qcF~aKtWwmeqKfphR@dh+Z#}<0 zYxTEcp?#&?5=YOjR}^2so9XPtQSWkg@s6LScNo{sS`oMQMm;;wc?_PeelF{r5}E)_ CNL~B@ literal 0 HcmV?d00001 diff --git a/textures/techage_industriallamp3_on.png b/textures/techage_industriallamp3_on.png new file mode 100644 index 0000000000000000000000000000000000000000..6c3bf6435a731c53f2086074296eb0e86150a414 GIT binary patch literal 283 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

a^`!06tH6z8#}s04cl)#cJ_QvC;j^0n z#Ihg{S5I6mo+y9sveY`sqrw?rm`cSGm3t7hl8bx>yn}w$l9+lnRQ?ND46cy z)6@=Jku0uB$_)W!rlyXsbEILXXA@NthDmA}ccc&Zy4X0-Y9yhnX+;mA;8$9gvWYl1 zVtvrEXw5ETRles5efwfdO2qjMlHTpojY)1RLzXM(Hr2JpXBx`3;hOd0ALn@V`(6J5 z@zjqXtM}V#+P5sT;9;NT*SY9wI-!(2;-ZXpboQr%u{$}C#LiYj9TDlh~ z=*Rb+&8{%=k5$XNPpeX#KbV7Ebc#zY$YXy__#Bt?pV7R&K9)aWz^q_sdNbU(Tp?+R zfpKR1d|i(m_F+`);m+CcXwEn*BRg%Rn;P&Qdg?0xRcbNV$?-+p5r0)zN5`94pdZ&% z&Md)+!GE}@u%Y&GC$I9)a|Tjd+fIK!eY7`9u8?iY#iM4^sr<`M?5_*baKeP-I$jya z!*dIhNS_5+M{>4y>!JnOuQ+N$HaJ}uu*XeRe3B*!MnEcno_8wl>{%4t-IW~<_ zN?C{Cw)UoZs&Bm{^KLg*ga3Ymi)-Uk;EU7qc2z?|!}I=O)58tr?h@HV0<8F9B<7ay zcuN49sTriXTfy-jTT|2qO2z*;f@6^fmXp(WbDJ1DUAkF0$jVHgZV|Mi?z`O(%b6S+ zpyffoc2^DM`+IWYD%kRhVpqs})!OOlaeE}hf(zyq{S};E#LYv?Ff)&TFdN5}R?=Mk zhn&2cwlB4O1=#V#9=LI)NzZ0*%iwcSzwE$?8S3h23SABKm@2%liv*x>!XRcb<@+o5 z73}x$W4pDz4I8R28(Cgyd|Sm$Tv^d%YzXsm3pO;>#^B^^c(@wP+4ttSYih}vk{zx= zQN?io+2AXZ?agr;bltG{8gr1=Kt<)`0KINHX#TaEA_I%Fmw+P=KlaqraZaZ1_Pp}W zy)ylXY|pYNTTP0clan1D4MUd+q4ES4x`P3wCUfDwNppR7XsVHv(&E;k-KSLR)QvE& z3@fXpJtaJLE@T~!#U);TemGYU^(WDh61%DK&HBm^H|Qzmxf{krMO;nnrB1f(C6Yl2 zDrj3*Vv~!rEZ45&M-F=0vU5b$Dt;`qz-`IubIEE~Cg+>YyJ*xsweTL^TRq2toimu%Cqx!WY9CectvIg`#Pb14Q{z!cbh@+eP78N?{rW*U#wa2u)c&fx-@ii zy2NKXMXh*W8p>VA8}txiym#2*vzbsvh}Cv5{*6aLDz+ zUvx3G3=RdSf)2qmb?urRB8pWR>Pjt{?;@6Upzl)jpZ`L1u; zEAPg?X5;baV>;P7=I2-bWIWk~e)^qpa;b)?J^|OQq3z1iuWY$umcSM}jGf*DQxYeN zQMMR|FKNsMEKCNTMz}@*O(N_m7;5~HE)zoNMZl<&*x#L;oj~Idrpi0V{nGj7Ge|zv zqq9C3GT3!N#*WOv2+e2kHphwKBUL9DctKO%9gE(~wy1%>X#&X0s7O~znS}lq$`%B5 literal 0 HcmV?d00001