From 7b180200bc1fb8b8c0794c61fbd4c06799aefb16 Mon Sep 17 00:00:00 2001 From: Joachim Stolberg Date: Thu, 17 Oct 2019 21:26:39 +0200 Subject: [PATCH] central power distribution function added power terminal improvements bauxite and reactor added --- basic_machines/forceload.lua | 75 ++++++++++----- basic_machines/grinder.lua | 1 + basis/mark.lua | 5 +- chemistry/reactor.lua | 32 +++++++ coal_power_station/power_terminal.lua | 129 ++++++++++++-------------- hydrogen/hydrogen.lua | 2 +- init.lua | 6 +- locale/techage.de.tr | 10 +- locale/template.txt | 10 +- nodes/bauxit.lua | 61 ++++++++++++ power/power.lua | 115 +++++++++++------------ power/power2.lua | 2 + power/schedule.lua | 87 +++++++++++++++++ textures/techage_bauxit_overlay.png | Bin 0 -> 203 bytes textures/techage_reactor_side.png | Bin 0 -> 22914 bytes 15 files changed, 381 insertions(+), 154 deletions(-) create mode 100644 chemistry/reactor.lua create mode 100644 nodes/bauxit.lua create mode 100644 power/schedule.lua create mode 100644 textures/techage_bauxit_overlay.png create mode 100644 textures/techage_reactor_side.png diff --git a/basic_machines/forceload.lua b/basic_machines/forceload.lua index e73e950..fb2bdc8 100644 --- a/basic_machines/forceload.lua +++ b/basic_machines/forceload.lua @@ -79,6 +79,16 @@ local function set_pos_list(player, lPos) player:set_attribute("techage_forceload_blocks", minetest.serialize(lPos)) end +local function shoe_flbs(pos, name, range) + local pos1 = {x=pos.x-range, y=pos.y-range, z=pos.z-range} + local pos2 = {x=pos.x+range, y=pos.y+range, z=pos.z+range} + for _,npos in ipairs(minetest.find_nodes_in_area(pos1, pos2, {"techage:forceload"})) do + local _pos1, _pos2 = calc_area(npos) + local owner = M(npos):get_string("owner") + techage.mark_region(name, _pos1, _pos2, owner) + end +end + local function get_data(pos, player) local pos1, pos2 = calc_area(pos) local num = #minetest.deserialize(player:get_attribute("techage_forceload_blocks")) or 0 @@ -86,24 +96,27 @@ local function get_data(pos, player) return pos1, pos2, num, max end -local function formspec(player) - local lPos = get_pos_list(player) - local tRes = {} - for idx,pos in ipairs(lPos) do - local pos1, pos2 = calc_area(pos) - local ypos = 0.2 + idx * 0.4 - tRes[#tRes+1] = idx - tRes[#tRes+1] = minetest.formspec_escape(P2S(pos1)) - tRes[#tRes+1] = "to" - tRes[#tRes+1] = minetest.formspec_escape(P2S(pos2)) +local function formspec(name) + local player = minetest.get_player_by_name(name) + if player then + local lPos = get_pos_list(player) + local tRes = {} + for idx,pos in ipairs(lPos) do + local pos1, pos2 = calc_area(pos) + local ypos = 0.2 + idx * 0.4 + tRes[#tRes+1] = idx + tRes[#tRes+1] = minetest.formspec_escape(P2S(pos1)) + tRes[#tRes+1] = "to" + tRes[#tRes+1] = minetest.formspec_escape(P2S(pos2)) + end + return "size[7,9]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "label[0,0;List of your Forceload Blocks:]".. + "tablecolumns[text,width=1.2;text,width=12;text,width=1.6;text,width=12]".. + "table[0,0.6;6.8,8.4;output;"..table.concat(tRes, ",")..";1]" end - return "size[7,9]".. - default.gui_bg.. - default.gui_bg_img.. - default.gui_slots.. - "label[0,0;List of your Forceload Blocks:]".. - "tablecolumns[text,width=1.2;text,width=12;text,width=1.6;text,width=12]".. - "table[0,0.6;6.8,8.4;output;"..table.concat(tRes, ",")..";1]" end @@ -151,10 +164,13 @@ minetest.register_node("techage:forceload", { end, on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) - if M(pos):get_string("owner") == clicker:get_player_name() or - minetest.check_player_privs(clicker:get_player_name(), "server") then - local s = formspec(clicker) - minetest.show_formspec(clicker:get_player_name(), "techage:forceload", s) + local owner = M(pos):get_string("owner") + local name = clicker:get_player_name() + if name == owner or minetest.check_player_privs(name, "server") then + local s = formspec(owner) + if s then + minetest.show_formspec(owner, "techage:forceload", s) + end end end, @@ -201,3 +217,20 @@ minetest.register_on_leaveplayer(function(player) end end) + +minetest.register_chatcommand("forceload", { + params = "", + description = "Forceloadblöcke der Umgebung 64x64x64 anzeigen", + func = function(name, param) + if minetest.check_player_privs(name, "superminer") then + local player = minetest.get_player_by_name(name) + if player then + local pos = player:get_pos() + pos = vector.round(pos) + shoe_flbs(pos, name, 64) + end + else + return false, "Priv missing" + end + end, +}) diff --git a/basic_machines/grinder.lua b/basic_machines/grinder.lua index 8e68021..4e8461b 100644 --- a/basic_machines/grinder.lua +++ b/basic_machines/grinder.lua @@ -293,4 +293,5 @@ techage.add_grinder_recipe({input="default:pine_tree", output="default:pine_need techage.add_grinder_recipe({input="default:acacia_tree", output="default:acacia_leaves 8"}) techage.add_grinder_recipe({input="default:aspen_tree", output="default:aspen_leaves 8"}) +techage.add_grinder_recipe({input="techage:bauxite_cobble", output="techage:bauxite_gravel"}) diff --git a/basis/mark.lua b/basis/mark.lua index 9878e4a..5432617 100644 --- a/basis/mark.lua +++ b/basis/mark.lua @@ -24,7 +24,7 @@ function techage.unmark_region(name) end end -function techage.mark_region(name, pos1, pos2) +function techage.mark_region(name, pos1, pos2, owner) techage.unmark_region(name) @@ -41,6 +41,9 @@ function techage.mark_region(name, pos1, pos2) --collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness}, collisionbox = {0,0,0, 0,0,0}, }) + if owner then + marker:set_nametag_attributes({text = owner}) + end marker:get_luaentity().player_name = name table.insert(markers, marker) end diff --git a/chemistry/reactor.lua b/chemistry/reactor.lua new file mode 100644 index 0000000..71bc990 --- /dev/null +++ b/chemistry/reactor.lua @@ -0,0 +1,32 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA4 Reactor + +]]-- + +local S = techage.S + +minetest.register_node("techage:ta4_reactor", { + description = S("TA4 Reactor"), + tiles = {"techage_reactor_side.png"}, + drawtype = "mesh", + mesh = "techage_boiler_large.obj", + selection_box = { + type = "fixed", + fixed = {-13/32, -16/32, -13/32, 13/32, 16/32, 13/32}, + }, + + paramtype2 = "facedir", + on_rotate = screwdriver.disallow, + groups = {cracky=2}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) diff --git a/coal_power_station/power_terminal.lua b/coal_power_station/power_terminal.lua index 4008f61..421fa59 100644 --- a/coal_power_station/power_terminal.lua +++ b/coal_power_station/power_terminal.lua @@ -13,20 +13,18 @@ ]]-- -- for lazy programmers -local P = minetest.string_to_pos +local P2P = minetest.string_to_pos +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end local M = minetest.get_meta +local N = function(pos) return minetest.get_node(pos).name end local S = techage.S -local BLOCKING_TIME = 2 +local CYCLE_TIME = 2 +local PWR_CAPA = 0.1 +local COUNTDOWN = 5 -local Param2ToDir = { - [0] = 6, - [1] = 5, - [2] = 2, - [3] = 4, - [4] = 1, - [5] = 3, -} +local Cable = techage.ElectricCable +local power = techage.power local function collect_network_data(pos, mem) local data = { @@ -39,74 +37,57 @@ local function collect_network_data(pos, mem) fcel = {}, other = {}, } - local add = function(kind, attr, val) - data[kind][attr] = (data[kind][attr] or 0) + (val or 0) - end - local max = function(kind, attr, val) - data[kind][attr] = math.max((data[kind][attr] or 0), (val or 0)) - end + local add = function(tbl, mem, nomi, real) + tbl.num = (tbl.num or 0) + 1 + tbl.load = (tbl.load or 0) + (((mem.pwr_node_alive_cnt or 0) > 0) and 1 or 0) + tbl.nomi = (tbl.nomi or 0) + (nomi or 0) + tbl.real = (tbl.real or 0) + (((mem.pwr_node_alive_cnt or 0) > 0) and (real or 0) or 0) + end local nnodes = techage.power.limited_connection_walk(pos, function(pos, node, mem, num_hops, num_nodes) if node.name == "techage:generator" or node.name == "techage:generator_on" then - add("fuel", "num", 1) - add("fuel", "nomi", mem.pwr_available) - add("fuel", "curr", mem.provided) + add(data.fuel, mem, mem.pwr_available, mem.provided) elseif node.name == "techage:ta3_akku" then - add("akku", "num", 1) - add("akku", "nomi", mem.pwr_could_provide) - add("akku", "curr", mem.delivered) + add(data.akku, mem, mem.pwr_could_provide, mem.delivered) elseif node.name == "techage:heatexchanger1" then - add("stor", "num", 1) - add("stor", "nomi", mem.pwr_could_provide) - add("stor", "curr", mem.delivered) + add(data.stor, mem, mem.pwr_could_provide, mem.delivered) elseif node.name == "techage:tiny_generator" or node.name == "techage:tiny_generator_on" then - add("fuel", "num", 1) - add("fuel", "nomi", mem.pwr_available) - add("fuel", "curr", mem.provided) + add(data.fuel, mem, mem.pwr_available, mem.provided) elseif node.name == "techage:ta4_solar_inverter" then - add("solar", "num", 1) - add("solar", "nomi", mem.pwr_available) - add("solar", "curr", mem.delivered) + add(data.solar, mem, mem.pwr_available, mem.delivered) elseif node.name == "techage:ta4_wind_turbine" then - add("wind", "num", 1) - add("wind", "nomi", mem.pwr_available) - add("wind", "curr", mem.delivered) + add(data.wind, mem, mem.pwr_available, mem.delivered) elseif node.name == "techage:ta4_fuelcell" or node.name == "techage:ta4_fuelcell_on" then - add("fcel", "num", 1) - add("fcel", "nomi", mem.pwr_available) - add("fcel", "curr", mem.provided) + add(data.fcel, mem, mem.pwr_available, mem.provided) elseif node.name == "techage:ta4_electrolyzer" or node.name == "techage:ta4_electrolyzer_on" then - add("elec", "num", 1) - add("elec", "nomi", -(mem.pwr_could_need or 0)) - add("elec", "curr", -(mem.consumed or 0)) + add(data.elec, mem, -(mem.pwr_could_need or 0), -(mem.consumed or 0)) elseif mem.pwr_needed and mem.pwr_needed > 0 and (mem.pwr_node_alive_cnt or 0) > 0 then - add("other", "num", 1) - add("other", "nomi", -mem.pwr_needed) - add("other", "curr", mem.pwr_state == 3 and -mem.pwr_needed) + add(data.other, mem, -(mem.pwr_needed or 0), (-mem.pwr_needed or 0)) end end ) return data, nnodes end -local function formspec(pos) - local jpos = minetest.deserialize(M(pos):get_string("junction_pos")) - local data, nnodes = collect_network_data(jpos, tubelib2.get_mem(jpos)) +local function formspec(pos, mem) + local data, nnodes = collect_network_data(pos, mem) local get = function(kind) - return (data[kind].num or 0).." / "..(data[kind].curr or 0).." ku / "..(data[kind].nomi or 0).. " ku" + return (data[kind].load or 0).." / "..(data[kind].num or 0).." : ".. + (data[kind].curr or 0).." / "..(data[kind].nomi or 0).. " ku" end local alarm = "" if nnodes > (techage.MAX_NUM_NODES - 50) then alarm = " (max. "..(techage.MAX_NUM_NODES).." !!!)" end + local update = mem.countdown > 0 and mem.countdown or S("Update") return "size[9.5,8.2]".. default.gui_bg.. default.gui_bg_img.. default.gui_slots.. "label[2,0.0;"..S("Network Data").."]".. - "label[3,0.7;"..S("(number / current / max.)").."]".. + "label[1,0.7;"..S("(Num. nodes loaded / max. : Power current / max.)").."]".. "label[0,1.4;"..S("TA3 Coal/oil")..":]".. "label[5,1.4;"..get("fuel").."]".. "label[0,2.1;"..S("TA3 Akku")..":]".. "label[5,2.1;"..get("akku").."]".. "label[0,2.8;"..S("TA4 Solar Inverter")..":]".. "label[5,2.8;"..get("solar").."]".. @@ -116,11 +97,20 @@ local function formspec(pos) "label[0,5.6;"..S("TA4 Fuel Cell")..":]".. "label[5,5.6;"..get("fcel").."]".. "label[0,6.3;"..S("Other consumers")..":]".. "label[5,6.3;"..get("other").."]".. "label[0,7;"..S("Number of nodes").." : "..nnodes..alarm.."]".. - "button[2.5,7.5;2,1;update;"..S("Update").."]" + "button[3.5,7.5;2,1;update;"..update.."]" end -local function update_formspec(pos) - M(pos):set_string("formspec", formspec(pos)) +local function node_timer(pos, elapsed) + local mem = tubelib2.get_mem(pos) + power.generator_alive(pos, mem) + + mem.countdown = mem.countdown or 0 + if mem.countdown > 0 then + mem.countdown = mem.countdown - 1 + M(pos):set_string("formspec", formspec(pos, mem)) + end + + return true end minetest.register_node("techage:ta3_power_terminal", { @@ -142,31 +132,19 @@ minetest.register_node("techage:ta3_power_terminal", { }, }, - after_place_node = function(pos, placer, itemstack) - local node = minetest.get_node(pos) - local outdir = techage.side_to_outdir("B", node.param2) - local jpos = tubelib2.get_pos(pos, outdir) - local mem = tubelib2.init_mem(pos) - mem.blocked_until = 0 - local meta = M(pos) - meta:set_string("junction_pos", minetest.serialize(jpos)) - meta:set_string("formspec", formspec(pos)) - end, - on_receive_fields = function(pos, formname, fields, player) local mem = tubelib2.get_mem(pos) - mem.blocked_until = mem.blocked_until or minetest.get_gametime() - if fields.update and mem.blocked_until < minetest.get_gametime() then - M(pos):set_string("formspec", formspec(pos)) - mem.blocked_until = minetest.get_gametime() + BLOCKING_TIME - minetest.after(BLOCKING_TIME + 1, update_formspec, pos) - end + mem.countdown = COUNTDOWN end, on_rightclick = function(pos, node, clicker) - M(pos):set_string("formspec", formspec(pos)) + local mem = tubelib2.get_mem(pos) + minetest.get_node_timer(pos):start(CYCLE_TIME) + power.generator_start(pos, mem, PWR_CAPA) + mem.countdown = COUNTDOWN end, + on_timer = node_timer, paramtype2 = "facedir", paramtype = "light", on_rotate = screwdriver.disallow, @@ -176,6 +154,19 @@ minetest.register_node("techage:ta3_power_terminal", { sounds = default.node_sound_metal_defaults(), }) +techage.power.register_node({"techage:ta3_power_terminal"}, { + power_network = Cable, + conn_sides = {"B"}, + after_place_node = function(pos) + local mem = tubelib2.init_mem(pos) + minetest.get_node_timer(pos):start(CYCLE_TIME) + power.generator_start(pos, mem, PWR_CAPA) + local meta = M(pos) + mem.countdown = 0 + meta:set_string("formspec", formspec(pos, mem)) + end, +}) + minetest.register_craft({ output = "techage:ta3_power_terminal", recipe = { diff --git a/hydrogen/hydrogen.lua b/hydrogen/hydrogen.lua index 868407e..2b7a456 100644 --- a/hydrogen/hydrogen.lua +++ b/hydrogen/hydrogen.lua @@ -20,7 +20,7 @@ minetest.register_craftitem("techage:hydrogen", { }) minetest.register_craftitem("techage:ta4_fuelcellstack", { - description = S("TA4 Fuell Cell Stack"), + description = S("TA4 Fuel Cell Stack"), inventory_image = "techage_fc_stack_inv.png", }) diff --git a/init.lua b/init.lua index 8561001..40f6ced 100644 --- a/init.lua +++ b/init.lua @@ -59,8 +59,12 @@ else -- Nodes1 dofile(MP.."/nodes/baborium.lua") dofile(MP.."/nodes/usmium.lua") + --dofile(MP.."/nodes/bauxit.lua") -- Power networks + dofile(MP.."/power/schedule.lua") + --dofile(MP.."/power/distribute.lua") + --dofile(MP.."/power/test.lua") dofile(MP.."/power/power.lua") dofile(MP.."/power/power2.lua") dofile(MP.."/power/junction.lua") @@ -202,7 +206,7 @@ else dofile(MP.."/energy_storage/nodes.lua") -- Chemistry - --dofile(MP.."/chemistry/reaktor.lua") + --dofile(MP.."/chemistry/reactor.lua") -- Hydrogen dofile(MP.."/hydrogen/hydrogen.lua") diff --git a/locale/techage.de.tr b/locale/techage.de.tr index 96d6751..0b2243f 100644 --- a/locale/techage.de.tr +++ b/locale/techage.de.tr @@ -1,7 +1,7 @@ # textdomain: techage #### TA3 Terminal ####@n@nSend commands to your machines@nand output text messages from your@nmachines to the Terminal.@n@nCommand syntax:@n cmd @n@nexample: cmd 181 on@n is the number of the node to which the command is sent@n'on' is the command to turn machines/nodes on@nFurther commands can be retrieved by clicking on@nmachines/nodes with the Techage Info Tool.@n@nLocal commands:@n- clear @= clear screen@n- help @= this message@n- pub @= switch to public use@n- priv @= switch to private use@nTo program a user button with a command:@n set @ne.g. 'set 1 ON cmd 123 on'@n= -(number / current / max.)=(Anzahl / aktuell / max.) +(Num. nodes loaded / max. : Power current / max.)=(Anz. Blöcke geladen / max. : Strom aktuell / max.) Allow to dig/place Techage power lines nearby power poles=Erlaubt TODO Ash=Asche Autocrafter=Autocrafter @@ -20,6 +20,9 @@ Basalt Gravel=Basaltkies Basalt Stone=Basaltgestein Basalt Stone Block=Basaltsteinblock Basalt Stone Brick=Basaltsteinziegel +Bauxite Cobblestone=Bauxit Kopfsteinpflaster +Bauxite Gravel=Bauxit Kies +Bauxite Stone=Bauxit Biome=Biom Block configured items for open ports=Blockiere konfigurierte Gegenstände für offene Ausgänge Build derrick=Errichte Ölturm @@ -62,6 +65,7 @@ Oil Drill Box=Ölbohrkiste Oil Pumpjack=Ölpumpe Oil Source=Erdöl Oil amount:=Ölmenge: +Other consumers=Weitere Verbraucher Outp=Ergeb. Plan=Plan Position=Position @@ -163,6 +167,7 @@ TA4 Electrolyzer=TA4 Elektrolyseur TA4 Energy Storage=TA4 Energiespeicher TA4 Epoxide Resin=TA4 Epoxidharz TA4 Fuel Cell=TA4 Brennstoffzelle +TA4 Fuel Cell Stack=Brennstoffzellenstapel TA4 Generator=TA4 Generator TA4 Heat Exchanger=TA4 Wärmetauscher TA4 Heat Exchanger 1=TA4 Wärmetauscher 1 @@ -176,6 +181,7 @@ TA4 Pillar=TA4 Säule TA4 Pipe=TA4 Röhre TA4 Pipe Inlet=TA4 Rohrzulauf TA4 Protected Chest=TA4 Gesicherte Kiste +TA4 Reactor=Reaktor TA4 Rotor Blade=TA4 Rotorblatt TA4 Silicon Wafer=TA4 Silizium-Wafer TA4 Solar Carrier Module=TA4 Solar Trägermodul @@ -227,6 +233,8 @@ added=hinzugefügt wird added or removed=hinzugefügt oder entfernt wird commands like: help=Kommandos wie: help connected with=verbunden mit +light=Licht +power=Strom removed=entfernt stopped=gestoppt ##### not used anymore ##### \ No newline at end of file diff --git a/locale/template.txt b/locale/template.txt index f8cfb8c..9aa9631 100644 --- a/locale/template.txt +++ b/locale/template.txt @@ -1,5 +1,5 @@ #### TA3 Terminal ####@n@nSend commands to your machines@nand output text messages from your@nmachines to the Terminal.@n@nCommand syntax:@n cmd @n@nexample: cmd 181 on@n is the number of the node to which the command is sent@n'on' is the command to turn machines/nodes on@nFurther commands can be retrieved by clicking on@nmachines/nodes with the Techage Info Tool.@n@nLocal commands:@n- clear @= clear screen@n- help @= this message@n- pub @= switch to public use@n- priv @= switch to private use@nTo program a user button with a command:@n set @ne.g. 'set 1 ON cmd 123 on'@n= -(number / current / max.)= +(Num. nodes loaded / max. : Power current / max.)= Allow to dig/place Techage power lines nearby power poles= Ash= Autocrafter= @@ -18,6 +18,9 @@ Basalt Gravel= Basalt Stone= Basalt Stone Block= Basalt Stone Brick= +Bauxite Cobblestone= +Bauxite Gravel= +Bauxite Stone= Biome= Block configured items for open ports= Build derrick= @@ -60,6 +63,7 @@ Oil Drill Box= Oil Pumpjack= Oil Source= Oil amount:= +Other consumers= Outp= Plan= Position= @@ -161,6 +165,7 @@ TA4 Electrolyzer= TA4 Energy Storage= TA4 Epoxide Resin= TA4 Fuel Cell= +TA4 Fuel Cell Stack= TA4 Generator= TA4 Heat Exchanger= TA4 Heat Exchanger 1= @@ -174,6 +179,7 @@ TA4 Pillar= TA4 Pipe= TA4 Pipe Inlet= TA4 Protected Chest= +TA4 Reactor= TA4 Rotor Blade= TA4 Silicon Wafer= TA4 Solar Carrier Module= @@ -225,5 +231,7 @@ added= added or removed= commands like: help= connected with= +light= +power= removed= stopped= \ No newline at end of file diff --git a/nodes/bauxit.lua b/nodes/bauxit.lua new file mode 100644 index 0000000..f9b6ba2 --- /dev/null +++ b/nodes/bauxit.lua @@ -0,0 +1,61 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Bauxite + +]]-- + +local S = techage.S + +minetest.register_node("techage:bauxite_stone", { + description = S("Bauxite Stone"), + tiles = {"default_desert_stone.png^techage_bauxit_overlay.png^[colorize:#800000:80"}, + groups = {cracky = 3, stone = 1}, + drop = 'techage:bauxite_cobble', + sounds = default.node_sound_stone_defaults(), + --paramtype = "light", + --light_source = minetest.LIGHT_MAX +}) + +minetest.register_node("techage:bauxite_cobble", { + description = S("Bauxite Cobblestone"), + tiles = {"default_desert_cobble.png^[colorize:#800000:80"}, + is_ground_content = false, + groups = {cracky = 3, stone = 2}, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:bauxite_gravel", { + description = S("Bauxite Gravel"), + tiles = {"default_gravel.png^[colorize:#9b1f06:180"}, + is_ground_content = false, + groups = {crumbly = 2, falling_node = 1}, + sounds = default.node_sound_gravel_defaults(), +}) + +minetest.register_ore({ + ore_type = "blob", + ore = "techage:bauxite_stone", + wherein = {"default:stone"}, + clust_scarcity = 12 * 12 * 12, + clust_size = 5, + y_max = -100, + y_min = -200, + noise_threshold = 0.0, + noise_params = { + offset = 0.5, + scale = 0.2, + spread = {x = 8, y = 8, z = 8}, + seed = 1234, --2316, + octaves = 1, + persist = 0.0 + }, + biomes = {"underground"} +}) diff --git a/power/power.lua b/power/power.lua index 5b10f42..697bf9d 100644 --- a/power/power.lua +++ b/power/power.lua @@ -220,16 +220,14 @@ local function min(val, max) end local function accounting(pos, mem) - if mem.pwr_is_master then - -- calculate the primary and secondary supply and demand - mem.mst_supply1 = min(mem.mst_needed1 + mem.mst_needed2, mem.mst_available1) - mem.mst_demand1 = min(mem.mst_needed1, mem.mst_available1 + mem.mst_available2) - mem.mst_supply2 = min(mem.mst_demand1 - mem.mst_supply1, mem.mst_available2) - mem.mst_demand2 = min(mem.mst_supply1 - mem.mst_demand1, mem.mst_available1) - mem.mst_reserve = (mem.mst_available1 + mem.mst_available2) - mem.mst_needed1 - if D.sts then D.dbg("needed = "..mem.mst_needed1.."/"..mem.mst_needed2..", available = "..mem.mst_available1.."/"..mem.mst_available2) end - if D.sts then D.dbg("supply = "..mem.mst_supply1.."/"..mem.mst_supply2..", demand = "..mem.mst_demand1.."/"..mem.mst_demand2..", reserve = "..mem.mst_reserve) end - end + -- calculate the primary and secondary supply and demand + mem.mst_supply1 = min(mem.mst_needed1 + mem.mst_needed2, mem.mst_available1) + mem.mst_demand1 = min(mem.mst_needed1, mem.mst_available1 + mem.mst_available2) + mem.mst_supply2 = min(mem.mst_demand1 - mem.mst_supply1, mem.mst_available2) + mem.mst_demand2 = min(mem.mst_supply1 - mem.mst_demand1, mem.mst_available1) + mem.mst_reserve = (mem.mst_available1 + mem.mst_available2) - mem.mst_needed1 + if D.sts then D.dbg("needed = "..mem.mst_needed1.."/"..mem.mst_needed2..", available = "..mem.mst_available1.."/"..mem.mst_available2) end + if D.sts then D.dbg("supply = "..mem.mst_supply1.."/"..mem.mst_supply2..", demand = "..mem.mst_demand1.."/"..mem.mst_demand2..", reserve = "..mem.mst_reserve) end end local function connection_walk(pos, clbk) @@ -318,10 +316,16 @@ local function store_master(pos, master_pos) pos_already_reached(pos) connection_walk(pos, function(pos, mem) mem.pwr_master_pos = master_pos - mem.pwr_is_master = false end) end +local function master_mem(mem) + if mem.pwr_master_pos then + local netkey = minetest.hash_node_position(mem.pwr_master_pos) + return techage.schedule.get_network(netkey) + end +end + local function handle_generator(mst_mem, mem, pos, power_available) -- for next cycle mst_mem.mst_available1 = (mst_mem.mst_available1 or 0) + power_available @@ -420,33 +424,32 @@ local function turn_off_nodes(mst_pos) end local function determine_new_master(pos, mem) - local was_master = mem.pwr_is_master - mem.pwr_is_master = false local mpos = determine_master(pos) store_master(pos, mpos) - if mpos then - local mmem = tubelib2.get_mem(mpos) - mmem.pwr_is_master = true - mmem.mst_num_nodes = NumNodes - elseif was_master then -- no master any more - -- delete data - mem.mst_supply1 = 0 - mem.mst_supply2 = 0 - mem.mst_reserve = 0 - end - return was_master or mem.pwr_is_master + mem.pwr_master_pos = mpos + return true end --- called from master position -local function power_distribution(pos, mem, dec) - if D.pwr then D.dbg("power_distribution") end - if mem.pwr_is_master then - mem.mst_needed1 = 0 - mem.mst_needed2 = 0 - mem.mst_available1 = 0 - mem.mst_available2 = 0 +-- called from all nodes +local function trigger_network(pos, mem) + if mem.pwr_master_pos then + local netkey = minetest.hash_node_position(mem.pwr_master_pos) + local network = techage.schedule.get_network(netkey) or + techage.schedule.add_network(netkey, {mst_pos = mem.pwr_master_pos}) + network.alive = 10 + else + print("node without master_pos "..N(pos).." at "..S(pos)) end - trigger_nodes(pos, mem, dec or 0) +end + +-- called from global timer +function techage.power.power_distribution(time, pos, mem) + if D.pwr then D.dbg("power_distribution"..math.floor(time).." "..N(pos)) end + mem.mst_needed1 = 0 + mem.mst_needed2 = 0 + mem.mst_available1 = 0 + mem.mst_available2 = 0 + trigger_nodes(pos, mem, 1) accounting(pos, mem) end @@ -458,14 +461,8 @@ end function techage.power.network_changed(pos, mem) if D.pwr then D.dbg("network_changed") end mem.pwr_node_alive_cnt = (mem.pwr_cycle_time or 2)/2 + 1 - if determine_new_master(pos, mem) then -- new master? - power_distribution(pos, mem) - elseif not mem.pwr_master_pos then -- no master? - turn_off_nodes(pos) - elseif not next(mem.connections) then -- isolated? - if mem.pwr_needed then -- consumer? - consumer_turn_off(pos, mem) - end + if determine_new_master(pos, mem) then -- new master + trigger_network(pos, mem) end end @@ -477,7 +474,7 @@ function techage.power.generator_start(pos, mem, available) mem.pwr_cycle_time = 2 mem.pwr_available = available if determine_new_master(pos, mem) then -- new master - power_distribution(pos, mem) + trigger_network(pos, mem) end end @@ -489,15 +486,13 @@ function techage.power.generator_stop(pos, mem) mem.pwr_node_alive_cnt = 0 mem.pwr_available = 0 if determine_new_master(pos, mem) then -- last available master - power_distribution(pos, mem) + trigger_network(pos, mem) end end function techage.power.generator_alive(pos, mem) mem.pwr_node_alive_cnt = 2 - if mem.pwr_is_master then - power_distribution(pos, mem, 1) - end + trigger_network(pos, mem) return mem.pwr_provided or 0 end @@ -531,8 +526,8 @@ end -- Lamp related function to speed up the turn on function techage.power.power_available(pos, mem, needed) if mem.pwr_master_pos and (mem.pwr_power_provided_cnt or 0) > 0 then - mem = tubelib2.get_mem(mem.pwr_master_pos) - if (mem.mst_reserve or 0) >= needed then + mem = master_mem(mem) + if mem and (mem.mst_reserve or 0) >= needed then mem.mst_reserve = (mem.mst_reserve or 0) - needed return true end @@ -544,14 +539,16 @@ end function techage.power.power_accounting(pos, mem) if mem.pwr_master_pos and (mem.pwr_power_provided_cnt or 0) > 0 then mem.pwr_power_provided_cnt = (mem.pwr_power_provided_cnt or 0) - 1 - mem = tubelib2.get_mem(mem.pwr_master_pos) - return { - prim_available = mem.mst_available1 or 0, - sec_available = mem.mst_available2 or 0, - prim_needed = mem.mst_needed1 or 0, - sec_needed = mem.mst_needed2 or 0, - num_nodes = mem.mst_num_nodes or 0, - } + mem = master_mem(mem) + if mem then + return { + prim_available = mem.mst_available1 or 0, + sec_available = mem.mst_available2 or 0, + prim_needed = mem.mst_needed1 or 0, + sec_needed = mem.mst_needed2 or 0, + num_nodes = mem.mst_num_nodes or 0, + } + end end return { prim_available = 0, @@ -570,7 +567,7 @@ function techage.power.secondary_start(pos, mem, available, needed) mem.pwr_could_provide = available mem.pwr_could_need = needed if determine_new_master(pos, mem) then -- new master - power_distribution(pos, mem) + trigger_network(pos, mem) end end @@ -580,7 +577,7 @@ function techage.power.secondary_stop(pos, mem) mem.pwr_could_need = 0 mem.pwr_needed2 = 0 if determine_new_master(pos, mem) then -- last available master - power_distribution(pos, mem) + trigger_network(pos, mem) end end @@ -597,7 +594,7 @@ function techage.power.secondary_alive(pos, mem, capa_curr, capa_max) mem.pwr_node_alive_cnt = 2 if mem.pwr_is_master then if D.pwr then D.dbg("secondary_alive is master") end - power_distribution(pos, mem, 1) + trigger_network(pos, mem) end if mem.pwr_master_pos then return mem.pwr_provided or 0 diff --git a/power/power2.lua b/power/power2.lua index eafcb91..347e2cc 100644 --- a/power/power2.lua +++ b/power/power2.lua @@ -102,6 +102,8 @@ local function add_connection(mem, pos, out_dir, peer_pos, peer_in_dir, power) else mem.connections[out_dir] = {pos = peer_pos, in_dir = peer_in_dir} end + -- update network for power scheduling + techage.power.network_changed(pos, mem) end function techage.power.register_node(names, pwr_def) diff --git a/power/schedule.lua b/power/schedule.lua new file mode 100644 index 0000000..b399264 --- /dev/null +++ b/power/schedule.lua @@ -0,0 +1,87 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Global power Job Scheduler + +]]-- + +-- for lazy programmers +local P2P = minetest.string_to_pos +local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local M = minetest.get_meta +local N = function(pos) return minetest.get_node(pos).name end + +local CYCLE_TIME = 2.0 + +techage.schedule = {} + +local NetList = {} +local JobQueue = {} +local first = 0 +local last = -1 +local LocalTime = 0 + +local function push(item) + last = last + 1 + item.time = LocalTime + CYCLE_TIME + JobQueue[last] = item +end + +local function pop() + if first > last then return end + local item = JobQueue[first] + if item.time <= LocalTime then + JobQueue[first] = nil -- to allow garbage collection + first = first + 1 + return item + end +end + +-- Scheduler +minetest.register_globalstep(function(dtime) + LocalTime = LocalTime + dtime + local item = pop() + while item do + local network = NetList[item.netkey] + if network and network.alive and network.alive >= 0 then + --techage.distribute.power_distribution(LocalTime, network) + techage.power.power_distribution(LocalTime, network.mst_pos, network) + network.alive = network.alive - 1 + push(item) + else + NetList[item.netkey] = nil + end + item = pop() + end +end) + +function techage.schedule.add_network(netkey, network) + if netkey then + if NetList[netkey] then -- already scheduled + NetList[netkey] = network + else + NetList[netkey] = network + push({netkey = netkey}) + end + return NetList[netkey] + end +end + +function techage.schedule.has_network(netkey) + if netkey then + return NetList[netkey] ~= nil + end +end + +function techage.schedule.get_network(netkey) + if netkey then + return NetList[netkey] + end +end diff --git a/textures/techage_bauxit_overlay.png b/textures/techage_bauxit_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..2a57f9ba7fa213dc9e76a43a6115bd2ecf4814b2 GIT binary patch literal 203 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE77|LvJo8#w1b*{8bs zosdz(#ASL1*vfQfG`?E(|NsBR4=(TCcE?rxaQn;mthXdK)SrB_`-sNA4f(h4Huk$M t;@z>Kjx+F{)S`<&{=8eeJLEGHL+|AfUh|C}-+*>7c)I$ztaD0e0swC^NXP&H literal 0 HcmV?d00001 diff --git a/textures/techage_reactor_side.png b/textures/techage_reactor_side.png new file mode 100644 index 0000000000000000000000000000000000000000..d91bc0adbc1bd6ca716d72c2d9ab1dbcb4fa18ce GIT binary patch literal 22914 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