diff --git a/basic_machines/consumer.lua b/basic_machines/consumer.lua new file mode 100644 index 0000000..548b926 --- /dev/null +++ b/basic_machines/consumer.lua @@ -0,0 +1,276 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Consumer node basis functionality. + It handles: + - up to 3 stages of nodes (TA2/TA3/TA4) + - power consumption + - node state handling + - registration of passive and active nodes + - Tube connections are on left and right side (from left to right) + - Power connection are on front and back side (front or back) +]]-- + +-- 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 + +-- Consumer Related Data +local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end +local CRDN = function(node) return (minetest.registered_nodes[node.name] or {}).consumer end + +local power = techage.power +local networks = techage.networks + +local function has_power(pos, nvm, state) + local crd = CRD(pos) + return power.power_available(pos, crd.power_netw) +end + +local function start_node(pos, nvm, state) + local crd = CRD(pos) + power.consumer_start(pos, crd.power_netw, crd.cycle_time) +end + +local function stop_node(pos, nvm, state) + local crd = CRD(pos) + power.consumer_stop(pos, crd.power_netw) +end + +local function on_power(pos) + local crd = CRD(pos) + local nvm = techage.get_nvm(pos) + crd.State:start(pos, nvm) +end + +local function on_nopower(pos) + local crd = CRD(pos) + local nvm = techage.get_nvm(pos) + crd.State:nopower(pos, nvm) +end + + +local function node_timer(pos, elapsed) + local crd = CRD(pos) + local nvm = techage.get_nvm(pos) + if crd.power_netw then + power.consumer_alive(pos, crd.power_netw, crd.cycle_time) + end + -- call the node timer routine + if techage.is_operational(nvm) then + crd.node_timer(pos, crd.cycle_time) + end + return crd.State:is_active(nvm) +end + +local function prepare_tiles(tiles, stage, power_png) + local tbl = {} + for _,item in ipairs(tiles) do + if type(item) == "string" then + tbl[#tbl+1] = item:gsub("#", stage):gsub("{power}", power_png) + else + local temp = table.copy(item) + temp.image = temp.image:gsub("#", stage):gsub("{power}", power_png) + tbl[#tbl+1] = temp + end + end + return tbl +end + +-- 'validStates' is optional and can be used to e.g. enable +-- only one TA2 node {false, true, false, false} +function techage.register_consumer(base_name, inv_name, tiles, tNode, validStates) + local names = {} + validStates = validStates or {true, true, true, true} + for stage = 2,4 do + local name_pas = "techage:ta"..stage.."_"..base_name.."_pas" + local name_act = "techage:ta"..stage.."_"..base_name.."_act" + local name_inv = "TA"..stage.." "..inv_name + names[#names+1] = name_pas + + if validStates[stage] then + local on_recv_message = tNode.tubing.on_recv_message + if stage > 2 then + on_recv_message = function(pos, src, topic, payload) + return "unsupported" + end + end + + local power_network + local power_png = 'techage_axle_clutch.png' + local power_used = tNode.power_consumption ~= nil + local tNetworks + -- power needed? + if power_used then + if stage > 2 then + power_network = techage.ElectricCable + power_png = 'techage_appl_hole_electric.png' + tNetworks = { + ele1 = { + sides = tNode.power_sides or {F=1, B=1}, + ntype = "con1", + nominal = tNode.power_consumption[stage], + on_power = on_power, + on_nopower = on_nopower, + }, + } + else + power_network = techage.Axle + power_png = 'techage_axle_clutch.png' + tNetworks = { + axle = { + sides = tNode.power_sides or {F=1, B=1}, + ntype = "con1", + nominal = tNode.power_consumption[stage], + on_power = on_power, + on_nopower = on_nopower, + } + } + end + end + + local tState = techage.NodeStates:new({ + node_name_passive = name_pas, + node_name_active = name_act, + infotext_name = name_inv, + cycle_time = tNode.cycle_time, + standby_ticks = tNode.standby_ticks, + formspec_func = tNode.formspec, + on_state_change = tNode.on_state_change, + can_start = tNode.can_start, + has_power = tNode.has_power or power_used and has_power or nil, + start_node = power_used and start_node or nil, + stop_node = power_used and stop_node or nil, + }) + + local tConsumer = { + stage = stage, + State = tState, + -- number of items to be processed per cycle + num_items = tNode.num_items and tNode.num_items[stage], + power_consumption = power_used and + tNode.power_consumption[stage] or 0, + node_timer = tNode.node_timer, + cycle_time = tNode.cycle_time, + power_netw = power_network, + } + + local after_place_node = function(pos, placer, itemstack, pointed_thing) + local crd = CRD(pos) + local meta = M(pos) + local nvm = techage.get_nvm(pos) + local node = minetest.get_node(pos) + meta:set_int("push_dir", techage.side_to_indir("L", node.param2)) + meta:set_int("pull_dir", techage.side_to_indir("R", node.param2)) + local number = "-" + if stage > 2 then + number = techage.add_node(pos, name_pas) + end + if crd.power_netw then + crd.power_netw:after_place_node(pos) + end + if tNode.after_place_node then + tNode.after_place_node(pos, placer, itemstack, pointed_thing) + end + crd.State:node_init(pos, nvm, number) + end + + local after_dig_node = function(pos, oldnode, oldmetadata, digger) + if tNode.after_dig_node then + tNode.after_dig_node(pos, oldnode, oldmetadata, digger) + end + local crd = CRDN(oldnode) + if crd.power_netw then + crd.power_netw:after_dig_node(pos) + end + techage.remove_node(pos) + techage.del_mem(pos) + end + + local tubelib2_on_update2 = function(pos, outdir, tlib2, node) + power.update_network(pos, outdir, tlib2) + end + + tNode.groups.not_in_creative_inventory = 0 + + minetest.register_node(name_pas, { + description = name_inv, + tiles = prepare_tiles(tiles.pas, stage, power_png), + consumer = tConsumer, + drawtype = tNode.drawtype, + node_box = tNode.node_box, + selection_box = tNode.selection_box, + + can_dig = tNode.can_dig, + on_rotate = screwdriver.disallow, + on_timer = node_timer, + on_receive_fields = tNode.on_receive_fields, + on_rightclick = tNode.on_rightclick, + after_place_node = after_place_node, + after_dig_node = after_dig_node, + tubelib2_on_update2 = tubelib2_on_update2, + allow_metadata_inventory_put = tNode.allow_metadata_inventory_put, + allow_metadata_inventory_move = tNode.allow_metadata_inventory_move, + allow_metadata_inventory_take = tNode.allow_metadata_inventory_take, + on_metadata_inventory_move = tNode.on_metadata_inventory_move, + on_metadata_inventory_put = tNode.on_metadata_inventory_put, + on_metadata_inventory_take = tNode.on_metadata_inventory_take, + networks = tNetworks and table.copy(tNetworks), + + paramtype = tNode.paramtype, + paramtype2 = "facedir", + groups = table.copy(tNode.groups), + is_ground_content = false, + sounds = tNode.sounds, + }) + + tNode.groups.not_in_creative_inventory = 1 + + minetest.register_node(name_act, { + description = name_inv, + tiles = prepare_tiles(tiles.act, stage, power_png), + consumer = tConsumer, + drawtype = tNode.drawtype, + node_box = tNode.node_box, + selection_box = tNode.selection_box, + + on_rotate = screwdriver.disallow, + on_timer = node_timer, + on_receive_fields = tNode.on_receive_fields, + on_rightclick = tNode.on_rightclick, + after_place_node = after_place_node, + after_dig_node = after_dig_node, + tubelib2_on_update2 = tubelib2_on_update2, + allow_metadata_inventory_put = tNode.allow_metadata_inventory_put, + allow_metadata_inventory_move = tNode.allow_metadata_inventory_move, + allow_metadata_inventory_take = tNode.allow_metadata_inventory_take, + on_metadata_inventory_move = tNode.on_metadata_inventory_move, + on_metadata_inventory_put = tNode.on_metadata_inventory_put, + on_metadata_inventory_take = tNode.on_metadata_inventory_take, + networks = tNetworks and table.copy(tNetworks), + + paramtype = tNode.paramtype, + paramtype2 = "facedir", + drop = "", + diggable = false, + groups = table.copy(tNode.groups), + is_ground_content = false, + sounds = tNode.sounds, + }) + + if power_used then + power_network:add_secondary_node_names({name_pas, name_act}) + end + techage.register_node({name_pas, name_act}, tNode.tubing) + end + end + return names[1], names[2], names[3] +end diff --git a/basis/boiler_lib.lua b/basis/boiler_lib.lua new file mode 100644 index 0000000..316f75c --- /dev/null +++ b/basis/boiler_lib.lua @@ -0,0 +1,122 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Boiler common functions + +]]-- + +-- for lazy programmers +local P = minetest.string_to_pos +local M = minetest.get_meta +local S = techage.S + +local HEAT_STEP = 10 +local MAX_WATER = 10 +local BLOCKING_TIME = 0.3 -- 300ms + +techage.boiler = {} + +local IsWater = { + ["bucket:bucket_river_water"] = true, + ["bucket:bucket_water"] = true, +} + +local IsBucket = { + ["bucket:bucket_empty"] = true, +} + +local function node_description(name) + name = string.split(name, " ")[1] + local ndef = minetest.registered_nodes[name] or minetest.registered_items[name] or minetest.registered_craftitems[name] + if ndef and ndef.description then + return minetest.formspec_escape(ndef.description) + end + return "" +end + +local function item_image(x, y, itemname) + return "box["..x..","..y..";0.85,0.9;#808080]".. + "item_image["..x..","..y..";1,1;"..itemname.."]" +end + +function techage.boiler.formspec(pos, nvm) + local title = S("Water Boiler") + local temp = nvm.temperature or 20 + local ratio = nvm.power_ratio or 0 + local tooltip = S("To add water punch\nthe boiler\nwith a water bucket") + return "size[5,3]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "box[0,-0.1;4.8,0.5;#c6e8ff]".. + "label[1.5,-0.1;"..minetest.colorize("#000000", title).."]".. + item_image(1, 1.5, "default:water_source "..(nvm.num_water or 0)).. + "tooltip[1,1.5;1,1;"..tooltip..";#0C3D32;#FFFFFF]".. + "image[3,1.0;1,2;techage_form_temp_bg.png^[lowpart:".. + temp..":techage_form_temp_fg.png]".. + "tooltip[3,1;1,2;"..S("water temperature")..";#0C3D32;#FFFFFF]" +end + +function techage.boiler.water_temperature(pos, nvm) + nvm.temperature = nvm.temperature or 20 + nvm.num_water = nvm.num_water or 0 + if nvm.fire_trigger then + nvm.temperature = math.min(nvm.temperature + HEAT_STEP, 100) + else + nvm.temperature = math.max(nvm.temperature - HEAT_STEP, 20) + end + nvm.fire_trigger = false + + if nvm.water_level == 0 then + if nvm.num_water > 0 then + nvm.num_water = nvm.num_water - 1 + nvm.water_level = 100 + else + nvm.temperature = 20 + end + end + return nvm.temperature +end + +function techage.boiler.on_rightclick(pos, node, clicker) + techage.set_activeformspec(pos, clicker) + local nvm = techage.get_nvm(pos) + M(pos):set_string("formspec", techage.boiler.formspec(pos, nvm)) +end + +function techage.boiler.can_dig(pos, player) + local nvm = techage.get_nvm(pos) + nvm.num_water = nvm.num_water or 0 + return nvm.num_water == 0 +end + +function techage.boiler.on_punch(pos, node, puncher, pointed_thing) + local nvm = techage.get_nvm(pos) + local mem = techage.get_mem(pos) + mem.blocking_time = mem.blocking_time or 0 + if mem.blocking_time > techage.SystemTime then + return + end + + nvm.num_water = nvm.num_water or 0 + local wielded_item = puncher:get_wielded_item():get_name() + if IsWater[wielded_item] and nvm.num_water < MAX_WATER then + mem.blocking_time = techage.SystemTime + BLOCKING_TIME + nvm.num_water = nvm.num_water + 1 + puncher:set_wielded_item(ItemStack("bucket:bucket_empty")) + M(pos):set_string("formspec", techage.boiler.formspec(pos, nvm)) + elseif IsBucket[wielded_item] and nvm.num_water > 0 then + mem.blocking_time = techage.SystemTime + BLOCKING_TIME + nvm.num_water = nvm.num_water - 1 + puncher:set_wielded_item(ItemStack("bucket:bucket_water")) + M(pos):set_string("formspec", techage.boiler.formspec(pos, nvm)) + end +end + diff --git a/basis/formspec_update.lua b/basis/formspec_update.lua new file mode 100644 index 0000000..754f654 --- /dev/null +++ b/basis/formspec_update.lua @@ -0,0 +1,42 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Keep only one formspec active per player + +]]-- + +local P2S = minetest.pos_to_string + +local ActiveFormspecs = {} +local ActivePlayer = {} + + +function techage.is_activeformspec(pos) + return ActiveFormspecs[P2S(pos)] +end + +function techage.set_activeformspec(pos, player) + local name = player and player:get_player_name() + if name then + if ActivePlayer[name] then + ActiveFormspecs[ActivePlayer[name]] = nil + end + ActivePlayer[name] = P2S(pos) + ActiveFormspecs[P2S(pos)] = true + end +end + +minetest.register_on_leaveplayer(function(player) + local name = player:get_player_name() + if ActivePlayer[name] then + ActiveFormspecs[ActivePlayer[name]] = nil + ActivePlayer[name] = nil + end +end) diff --git a/basis/storage.lua b/basis/storage.lua new file mode 100644 index 0000000..251a428 --- /dev/null +++ b/basis/storage.lua @@ -0,0 +1,126 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Memory storage system for volatile and non-volatile memory. + Non-volatile memory is stored from time to time and at shutdown + as node metadata. Volatile memory is lost at every shutdown. + +]]-- + +local NUM_NODES_PER_MIN = 100 +local NvmStore = {} +local MemStore = {} +local NumNodes = 0 +local StoredNodes = 0 + +local function set_metadata(hash, tbl) + local pos = minetest.get_position_from_hash(hash) + local data = minetest.serialize(tbl) + local meta = minetest.get_meta(pos) + meta:set_string("ta_data", data) + meta:mark_as_private("ta_data") +end + +local function get_metadata(hash) + local pos = minetest.get_position_from_hash(hash) + local meta = minetest.get_meta(pos) + local s = meta:get_string("ta_data") + if s ~= "" then + return minetest.deserialize(s) + end +end + +local function storage_loop() + local cnt = 0 + while true do + NumNodes = 0 + StoredNodes = 0 + for hash,tbl in pairs(NvmStore) do + NumNodes = NumNodes + 1 + if tbl.__used__ then + tbl.__used__ = nil + set_metadata(hash, tbl) + StoredNodes = StoredNodes + 1 + cnt = cnt + 1 + if cnt > NUM_NODES_PER_MIN then + cnt = 0 + coroutine.yield() + end + else + -- remove from memory if node is unloaded + local pos = minetest.get_position_from_hash(hash) + if minetest.get_node(pos).name == "ignore" then + NvmStore[hash] = nil + MemStore[hash] = nil + end + end + end + coroutine.yield() + end +end + +local co = coroutine.create(storage_loop) + + +local function cyclic_task() + local t = minetest.get_us_time() + coroutine.resume(co) + t = minetest.get_us_time() - t + print("[TA NVM Storage] duration="..t.."us, total="..NumNodes..", stored="..StoredNodes) + -- run every minutes + minetest.after(60, cyclic_task) +end + +minetest.register_on_shutdown(function() + NumNodes = 0 + StoredNodes = 0 + local t = minetest.get_us_time() + for k,v in pairs(NvmStore) do + NumNodes = NumNodes + 1 + if v.__used__ then + v.__used__ = nil + set_metadata(k, v) + StoredNodes = StoredNodes + 1 + end + end + t = minetest.get_us_time() - t + print("[TA NVM Storage] duration="..t.."us, total="..NumNodes..", stored="..StoredNodes) +end) + +minetest.after(60, cyclic_task) + + +-- To get the volatile node data as table +function techage.get_mem(pos, will_change) + local hash = minetest.hash_node_position(pos) + if not MemStore[hash] then + MemStore[hash] = {} + end + return MemStore[hash] +end + +-- To get the nonvolatile node data as table +function techage.get_nvm(pos) + local hash = minetest.hash_node_position(pos) + if not NvmStore[hash] then + NvmStore[hash] = get_metadata(hash) or {} + end + NvmStore[hash].__used__ = true + return NvmStore[hash] +end + +-- To be called when a node is removed +function techage.del_mem(pos) + local meta = minetest.get_meta(pos) + meta:set_string("ta_data", "") + local hash = minetest.hash_node_position(pos) + NvmStore[hash] = nil + MemStore[hash] = nil +end diff --git a/coal_power_station/oilfirebox.lua b/coal_power_station/oilfirebox.lua new file mode 100644 index 0000000..3dabb9f --- /dev/null +++ b/coal_power_station/oilfirebox.lua @@ -0,0 +1,398 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA3 Coal Power Station Firebox + +]]-- + +-- for lazy programmers +local P = minetest.string_to_pos +local M = minetest.get_meta +local S = techage.S + +local firebox = techage.firebox +local fuel = techage.fuel +local Pipe = techage.LiquidPipe +local liquid = techage.liquid + +local CYCLE_TIME = 2 +local EFFICIENCY = 0.5 + +local function firehole(pos, on) + local param2 = techage.get_node_lvm(pos).param2 + local pos2 = techage.get_pos(pos, 'F') + if on == true then + minetest.swap_node(pos2, {name="techage:coalfirehole_on", param2 = param2}) + elseif on == false then + minetest.swap_node(pos2, {name="techage:coalfirehole", param2 = param2}) + else + minetest.swap_node(pos2, {name="air"}) + end +end + +local function node_timer(pos, elapsed) + local mem = tubelib2.get_mem(pos) + if mem.running then + -- trigger generator and provide power ratio 0..1 + local ratio = techage.transfer( + {x=pos.x, y=pos.y+2, z=pos.z}, + nil, -- outdir + "trigger", -- topic + (mem.power_level or 4)/4.0, -- payload + nil, -- network + {"techage:coalboiler_top"} -- nodenames + ) + ratio = math.max((ratio or 0.02), 0.02) + mem.burn_cycles = (mem.burn_cycles or 0) - ratio + if mem.burn_cycles <= 0 then + local taken = firebox.get_fuel(pos) + if taken then + mem.burn_cycles = (firebox.Burntime[taken:get_name()] or 1) * EFFICIENCY / CYCLE_TIME + mem.burn_cycles_total = mem.burn_cycles + else + mem.running = false + firehole(pos, false) + M(pos):set_string("formspec", firebox.formspec(mem)) + return false + end + end + return true + end +end + +local function start_firebox(pos, mem) + if not mem.running then + mem.running = true + node_timer(pos, 0) + firehole(pos, true) + minetest.get_node_timer(pos):start(CYCLE_TIME) + end +end + +minetest.register_node("techage:coalfirebox", { + description = S("TA3 Power Station Firebox"), + inventory_image = "techage_coal_boiler_inv.png", + tiles = {"techage_coal_boiler_mesh_top.png"}, + drawtype = "mesh", + mesh = "techage_cylinder_12.obj", + selection_box = { + type = "fixed", + fixed = {-13/32, -16/32, -13/32, 13/32, 16/32, 13/32}, + }, + + paramtype = "light", + paramtype2 = "facedir", + on_rotate = screwdriver.disallow, + groups = {cracky=2}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), + + on_timer = node_timer, + can_dig = firebox.can_dig, + allow_metadata_inventory_put = firebox.allow_metadata_inventory_put, + allow_metadata_inventory_take = firebox.allow_metadata_inventory_take, + on_receive_fields = firebox.on_receive_fields, + on_rightclick = firebox.on_rightclick, + + on_construct = function(pos) + local mem = tubelib2.init_mem(pos) + techage.add_node(pos, "techage:coalfirebox") + mem.running = false + mem.burn_cycles = 0 + mem.power_level = 4 + local meta = M(pos) + meta:set_string("formspec", firebox.formspec(mem)) + local inv = meta:get_inventory() + inv:set_size('fuel', 1) + firehole(pos, false) + end, + + on_destruct = function(pos) + firehole(pos, nil) + end, + + on_metadata_inventory_put = function(pos, listname, index, stack, player) + local mem = tubelib2.get_mem(pos) + start_firebox(pos, mem) + M(pos):set_string("formspec", firebox.formspec(mem)) + end, +}) + +minetest.register_node("techage:coalfirehole", { + description = S("TA3 Coal Power Station Firebox"), + tiles = { + -- up, down, right, left, back, front + "techage_coal_boiler.png", + "techage_coal_boiler.png", + "techage_coal_boiler.png", + "techage_coal_boiler.png", + "techage_coal_boiler.png", + "techage_coal_boiler.png^techage_appl_firehole.png", + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-6/16, -6/16, 6/16, 6/16, 6/16, 12/16}, + }, + }, + + paramtype = "light", + paramtype2 = "facedir", + pointable = false, + diggable = false, + is_ground_content = false, + groups = {not_in_creative_inventory=1}, +}) + +minetest.register_node("techage:coalfirehole_on", { + description = S("TA3 Coal Power Station Firebox"), + tiles = { + -- up, down, right, left, back, front + "techage_coal_boiler.png^[colorize:black:80", + "techage_coal_boiler.png^[colorize:black:80", + "techage_coal_boiler.png^[colorize:black:80", + "techage_coal_boiler.png^[colorize:black:80", + "techage_coal_boiler.png^[colorize:black:80", + { + image = "techage_coal_boiler4.png^[colorize:black:80^techage_appl_firehole4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.4, + }, + }, + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-6/16, -6/16, 6/16, 6/16, 6/16, 12/16}, + }, + }, + paramtype = "light", + paramtype2 = "facedir", + light_source = 8, + pointable = false, + diggable = false, + is_ground_content = false, + groups = {not_in_creative_inventory=1}, +}) + +local function on_timer2(pos, elapsed) + local mem = tubelib2.get_mem(pos) + if mem.running then + fuel.formspec_update(pos, mem) + -- trigger generator and provide power ratio 0..1 + local ratio = techage.transfer( + {x=pos.x, y=pos.y+2, z=pos.z}, + nil, -- outdir + "trigger", -- topic + (mem.power_level or 4)/4.0, -- payload + nil, -- network + {"techage:coalboiler_top"} -- nodenames + ) + ratio = math.max((ratio or 0.02), 0.02) + mem.burn_cycles = (mem.burn_cycles or 0) - ratio + mem.liquid = mem.liquid or {} + mem.liquid.amount = mem.liquid.amount or 0 + if mem.burn_cycles <= 0 then + if mem.liquid.amount > 0 then + mem.liquid.amount = mem.liquid.amount - 1 + mem.burn_cycles = fuel.burntime(mem.liquid.name) * EFFICIENCY / CYCLE_TIME + mem.burn_cycles_total = mem.burn_cycles + else + mem.running = false + mem.liquid.name = nil + firehole(pos, false) + M(pos):set_string("formspec", fuel.formspec(mem)) + return false + end + end + return true + end +end + +local function start_firebox2(pos, mem) + if not mem.running and mem.liquid.amount > 0 then + mem.running = true + on_timer2(pos, 0) + firehole(pos, true) + minetest.get_node_timer(pos):start(CYCLE_TIME) + M(pos):set_string("formspec", fuel.formspec(mem)) + end +end + +minetest.register_node("techage:oilfirebox", { + description = S("TA3 Power Station Oil Burner"), + inventory_image = "techage_oil_boiler_inv.png", + tiles = {"techage_coal_boiler_mesh_top.png"}, + drawtype = "mesh", + mesh = "techage_cylinder_12.obj", + selection_box = { + type = "fixed", + fixed = {-13/32, -16/32, -13/32, 13/32, 16/32, 13/32}, + }, + + paramtype = "light", + paramtype2 = "facedir", + on_rotate = screwdriver.disallow, + groups = {cracky=2}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), + + on_timer = on_timer2, + can_dig = fuel.can_dig, + allow_metadata_inventory_take = fuel.allow_metadata_inventory_take, + allow_metadata_inventory_put = fuel.allow_metadata_inventory_put, + on_receive_fields = fuel.on_receive_fields, + on_rightclick = fuel.on_rightclick, + + on_construct = function(pos) + local mem = tubelib2.init_mem(pos) + techage.add_node(pos, "techage:oilfirebox") + mem.running = false + mem.burn_cycles = 0 + mem.liquid = {} + mem.liquid.amount = 0 + local meta = M(pos) + meta:set_string("formspec", fuel.formspec(mem)) + local inv = meta:get_inventory() + inv:set_size('fuel', 1) + firehole(pos, false) + end, + + on_destruct = function(pos) + firehole(pos, nil) + end, + + on_metadata_inventory_put = function(pos, listname, index, stack, player) + local mem = tubelib2.get_mem(pos) + mem.liquid = mem.liquid or {} + mem.liquid.amount = mem.liquid.amount or 0 + minetest.after(1, start_firebox2, pos, mem) + fuel.on_metadata_inventory_put(pos, listname, index, stack, player) + end, + + liquid = { + capa = fuel.CAPACITY, + fuel_cat = fuel.BT_BITUMEN, + peek = liquid.srv_peek, + put = function(pos, indir, name, amount) + if fuel.valid_fuel(name, fuel.BT_BITUMEN) then + local leftover = liquid.srv_put(pos, indir, name, amount) + local mem = tubelib2.get_mem(pos) + mem.liquid = mem.liquid or {} + mem.liquid.amount = mem.liquid.amount or 0 + start_firebox2(pos, mem) + return leftover + end + return amount + end, + take = liquid.srv_take, + }, + networks = { + pipe = { + sides = techage.networks.AllSides, -- Pipe connection sides + ntype = "tank", + }, + }, +}) + +Pipe:add_secondary_node_names({"techage:oilfirebox"}) + + +techage.register_node({"techage:coalfirebox"}, { + on_pull_item = function(pos, in_dir, num) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + return techage.get_items(inv, "fuel", num) + end, + on_push_item = function(pos, in_dir, stack) + if firebox.Burntime[stack:get_name()] then + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + local mem = tubelib2.get_mem(pos) + start_firebox(pos, mem) + return techage.put_items(inv, "fuel", stack) + end + return false + end, + on_unpull_item = function(pos, in_dir, stack) + local meta = minetest.get_meta(pos) + local inv = meta:get_inventory() + return techage.put_items(inv, "fuel", stack) + end, + on_recv_message = function(pos, src, topic, payload) + local mem = tubelib2.get_mem(pos) + if topic == "state" then + if mem.running then + return "running" + else + return "stopped" + end + elseif topic == "fuel" then + local inv = M(pos):get_inventory() + local stack = inv:get_stack("fuel", 1) + return stack:get_count() + else + return "unsupported" + end + end, +}) + +techage.register_node({"techage:oilfirebox"}, { + on_recv_message = function(pos, src, topic, payload) + local mem = tubelib2.get_mem(pos) + if topic == "state" then + if mem.running then + return "running" + else + return "stopped" + end + elseif topic == "fuel" then + return mem.liquid and mem.liquid.amount and mem.liquid.amount + else + return "unsupported" + end + end, +}) + +minetest.register_craft({ + output = "techage:coalfirebox", + recipe = { + {'default:stone', 'default:stone', 'default:stone'}, + {'default:steel_ingot', '', 'default:steel_ingot'}, + {'default:stone', 'default:stone', 'default:stone'}, + }, +}) + +minetest.register_craft({ + output = "techage:oilfirebox", + recipe = { + {'', 'techage:coalfirebox', ''}, + {'', 'techage:ta3_barrel_empty', ''}, + {'', '', ''}, + }, +}) + +minetest.register_lbm({ + label = "[techage] Power Station firebox", + name = "techage:steam_engine", + nodenames = {"techage:coalfirebox", "techage:oilfirebox"}, + run_at_every_load = true, + action = function(pos, node) + minetest.get_node_timer(pos):start(CYCLE_TIME) + end +}) + + diff --git a/hydrogen/fuelcellstack.lua b/hydrogen/fuelcellstack.lua new file mode 100644 index 0000000..10587ad --- /dev/null +++ b/hydrogen/fuelcellstack.lua @@ -0,0 +1,29 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA4 Hydrogen + +]]-- + +local S = techage.S + +minetest.register_craftitem("techage:ta4_fuelcellstack", { + description = S("TA4 Fuel Cell Stack"), + inventory_image = "techage_fc_stack_inv.png", +}) + +minetest.register_craft({ + output = "techage:ta4_fuelcellstack", + recipe = { + {'techage:baborium_ingot', 'techage:ta4_carbon_fiber', 'default:copper_ingot'}, + {'default:gold_ingot', 'techage:ta4_carbon_fiber', 'default:tin_ingot'}, + {"techage:baborium_ingot", 'techage:ta4_carbon_fiber', 'default:copper_ingot'}, + }, +}) diff --git a/liquids/liquid_lib.lua b/liquids/liquid_lib.lua new file mode 100644 index 0000000..a4ce553 --- /dev/null +++ b/liquids/liquid_lib.lua @@ -0,0 +1,142 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Liquid lib + +]]-- + +local M = minetest.get_meta +local S = techage.S +local liquid = techage.liquid +local LQD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).liquid end + + +function techage.liquid.formspec_liquid(x, y, mem) + local itemname = "techage:liquid" + if mem.liquid and mem.liquid.amount and mem.liquid.amount > 0 and mem.liquid.name then + itemname = mem.liquid.name.." "..mem.liquid.amount + end + return "container["..x..","..y.."]".. + "background[0,0;3,2.05;techage_form_grey.png]".. + "image[0,0;1,1;techage_form_input_arrow.png]".. + techage.item_image(1, 0, itemname).. + "image[2,0;1,1;techage_form_output_arrow.png]".. + "image[1,1;1,1;techage_form_arrow.png]".. + "list[context;src;0,1;1,1;]".. + "list[context;dst;2,1;1,1;]".. + "listring[current_player;main]".. + "listring[context;src]" .. + "listring[current_player;main]".. + "listring[context;dst]" .. + "listring[current_player;main]".. + "container_end[]" +end + +local function fill_container(pos, inv) + local mem = tubelib2.get_mem(pos) + mem.liquid = mem.liquid or {} + mem.liquid.amount = mem.liquid.amount or 0 + local empty_container = inv:get_stack("src", 1):get_name() + local full_container = liquid.get_full_container(empty_container, mem.liquid.name) + if empty_container and full_container then + local ldef = liquid.get_liquid_def(full_container) + if ldef and mem.liquid.amount - ldef.size >= 0 then + if inv:room_for_item("dst", ItemStack(full_container)) then + inv:remove_item("src", ItemStack(empty_container)) + inv:add_item("dst", ItemStack(full_container)) + mem.liquid.amount = mem.liquid.amount - ldef.size + if mem.liquid.amount == 0 then + mem.liquid.name = nil + end + end + end + end +end + +local function empty_container(pos, inv, size) + local mem = tubelib2.get_mem(pos) + mem.liquid = mem.liquid or {} + mem.liquid.amount = mem.liquid.amount or 0 + local stack = inv:get_stack("src", 1) + local ldef = liquid.get_liquid_def(stack:get_name()) + if ldef and (not mem.liquid.name or ldef.inv_item == mem.liquid.name) then + local amount = stack:get_count() * ldef.size + if mem.liquid.amount + amount <= size then + if inv:room_for_item("dst", ItemStack(ldef.container)) then + inv:remove_item("src", stack) + inv:add_item("dst", ItemStack(ldef.container)) + mem.liquid.amount = mem.liquid.amount + amount + mem.liquid.name = ldef.inv_item + end + end + end +end + +function techage.liquid.move_item(pos, stack, size, formspec) + local mem = tubelib2.get_mem(pos) + local inv = M(pos):get_inventory() + if liquid.is_container_empty(stack:get_name()) then + fill_container(pos, inv) + else + empty_container(pos, inv, size) + end + M(pos):set_string("formspec", formspec(pos, mem)) +end + +function techage.liquid.is_empty(pos) + local mem = tubelib2.get_mem(pos) + local inv = minetest.get_meta(pos):get_inventory() + return inv:is_empty("src") and inv:is_empty("dst") and (not mem.liquid or (mem.liquid.amount or 0) == 0) +end + +techage.liquid.tubing = { + on_pull_item = function(pos, in_dir, num) + local inv = M(pos):get_inventory() + if not inv:is_empty("dst") then + local taken = techage.get_items(inv, "dst", num) + if not inv:is_empty("src") then + fill_container(pos, inv) + end + return taken + end + end, + on_push_item = function(pos, in_dir, stack) + local inv = M(pos):get_inventory() + if inv:room_for_item("src", stack) then + inv:add_item("src", stack) + if liquid.is_container_empty(stack:get_name()) then + fill_container(pos, inv) + else + empty_container(pos, inv) + end + return true + end + return false + end, + on_unpull_item = function(pos, in_dir, stack) + local meta = M(pos) + local inv = meta:get_inventory() + return techage.put_items(inv, "dst", stack) + end, + on_recv_message = function(pos, src, topic, payload) + if topic == "load" then + local mem = tubelib2.get_mem(pos) + return techage.power.percent(LQD(pos).capa, (mem.liquid and mem.liquid.amount) or 0) + elseif topic == "size" then + return LQD(pos).capa + else + return "unsupported" + end + end, +} + + +techage.liquid.fill_container = fill_container +techage.liquid.empty_container = empty_container \ No newline at end of file diff --git a/power/distribution.lua b/power/distribution.lua new file mode 100644 index 0000000..23d65ba --- /dev/null +++ b/power/distribution.lua @@ -0,0 +1,135 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Power Distribution + +]]-- + +local net_def = techage.networks.net_def + +local STOPPED = techage.power.STOPPED +local NOPOWER = techage.power.NOPOWER +local RUNNING = techage.power.RUNNING + +local function start_consumer(tbl, tlib_type) + for _,v in pairs(tbl or {}) do + local nvm = techage.get_nvm(v.pos) + if nvm[tlib_type.."_cstate"] == NOPOWER and (nvm[tlib_type.."_calive"] or 0) > 0 then + local ndef = net_def(v.pos, tlib_type) + nvm[tlib_type.."_cstate"] = RUNNING + nvm[tlib_type.."_taken"] = v.nominal or 0 + if ndef.on_power then + ndef.on_power(v.pos) + end + end + end +end + +local function stop_consumer(tbl, tlib_type) + for _,v in pairs(tbl or {}) do + local nvm = techage.get_nvm(v.pos) + if nvm[tlib_type.."_cstate"] == RUNNING then + local ndef = net_def(v.pos, tlib_type) + nvm[tlib_type.."_cstate"] = NOPOWER + nvm[tlib_type.."_taken"] = 0 + if ndef.on_nopower then + ndef.on_nopower(v.pos) + end + end + end +end + +local function get_generator_sum(tbl, tlib_type) + local sum = 0 + for _,v in ipairs(tbl or {}) do + local nvm = techage.get_nvm(v.pos) + if nvm[tlib_type.."_gstate"] ~= STOPPED then + nvm[tlib_type.."_galive"] = (nvm[tlib_type.."_galive"] or 1) - 1 + if nvm[tlib_type.."_galive"] > 0 then + sum = sum + v.nominal + end + end + end + return sum +end + +local function get_consumer_sum(tbl, tlib_type) + local sum = 0 + for _,v in ipairs(tbl or {}) do + local nvm = techage.get_nvm(v.pos) + if nvm[tlib_type.."_cstate"] ~= STOPPED then + nvm[tlib_type.."_calive"] = (nvm[tlib_type.."_calive"] or 1) - 1 + if nvm[tlib_type.."_calive"] > 0 then + sum = sum + v.nominal + end + end + end + return sum +end + +local function set_given(pos, given, tlib_type) + local nvm = techage.get_nvm(pos) + if (nvm[tlib_type.."_galive"] or 0) > 0 then + nvm[tlib_type.."_given"] = given + return given + end + return 0 +end + +local function set_taken(pos, taken, tlib_type) + local nvm = techage.get_nvm(pos) + if (nvm[tlib_type.."_calive"] or 0) > 0 then + nvm[tlib_type.."_taken"] = taken + return taken + end + return 0 +end + +local function set_given_values(tbl, needed, tlib_type) + for _,v in ipairs(tbl or {}) do + local real = math.max(math.min(needed, v.nominal), 0) + real = set_given(v.pos, real, tlib_type) + needed = needed - real + end + return needed +end + +local function set_taken_values(tbl, taken, tlib_type) + for _,v in pairs(tbl or {}) do + local real = math.max(math.min(taken, v.nominal), 0) + real = set_taken(v.pos, real, tlib_type) + taken = taken - real + end + return taken +end + +function techage.power.power_distribution(network, tlib_type, t) + -- calc maximum power values + network.available1 = get_generator_sum(network.gen1, tlib_type) + network.available2 = get_generator_sum(network.gen2, tlib_type) + network.needed1 = get_consumer_sum(network.con1, tlib_type) + network.needed2 = get_consumer_sum(network.con2, tlib_type) + print(t, minetest.get_gametime(), network.available1, network.available2, network.needed1, network.needed2) + + -- store results + network.on = network.available1 + network.available2 >= network.needed1 + if network.on then + network.ticker = (network.ticker or 0) + 1 + set_given_values(network.gen1, network.needed1 + network.needed2, tlib_type) + set_given_values(network.gen2, network.needed1 - network.available1, tlib_type) + start_consumer(network.con1, tlib_type) + set_taken_values(network.con2, network.available1 - network.needed1, tlib_type) + else + set_given_values(network.gen1, 0, tlib_type) + set_given_values(network.gen2, 0, tlib_type) + stop_consumer(network.con1, tlib_type) + set_taken_values(network.con2, 0, tlib_type) + end +end \ No newline at end of file diff --git a/power/formspecs.lua b/power/formspecs.lua new file mode 100644 index 0000000..edc29bb --- /dev/null +++ b/power/formspecs.lua @@ -0,0 +1,75 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + Power Formspec Functions + +]]-- + +--local P2S = minetest.pos_to_string +--local M = minetest.get_meta +--local N = function(pos) return minetest.get_node(pos).name end +--local S = techage.S +local in_range = techage.in_range + +function techage.power.percent(max_val, curr_val) + return math.min(math.ceil(((curr_val or 0) * 100.0) / (max_val or 1.0)), 100) +end + +function techage.power.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 + +function techage.power.formspec_power_bar(max_power, current_power) + if (current_power or 0) == 0 then + return "techage_form_level_bg.png" + end + local percent = techage.power.percent(max_power, current_power) + percent = (percent + 5) / 1.22 -- texture correction + return "techage_form_level_bg.png^[lowpart:"..percent..":techage_form_level_fg.png" +end + +function techage.power.formspec_label_bar(x, y, label, max_power, current_power) + local percent, ypos + current_power = current_power or 0 + if current_power == 0 then + percent = 0 + ypos = 2.8 + else + percent = techage.power.percent(max_power, current_power) + -- 0.4 to 2.8 = 2.4 + local offs = 2.4 - (current_power / max_power) * 2.4 + ypos = 0.4 + in_range(offs, 0.4, 2.4) + end + percent = (percent + 5) / 1.1 -- texture correction + return "container["..x..","..y.."]".. + "box[0,0;2.3,3.3;#395c74]".. + "label[0.2,0;"..label.."]".. + "label[0.7,0.4;"..max_power.." ku]".. + "image[0,0.5;1,3;".. + "techage_form_level_bg.png^[lowpart:"..percent.. + ":techage_form_level_fg.png]".. + "label[0.7,"..ypos..";"..current_power.." ku]".. + "container_end[]" + +end + diff --git a/power/gearbox.lua b/power/gearbox.lua new file mode 100644 index 0000000..aeef934 --- /dev/null +++ b/power/gearbox.lua @@ -0,0 +1,151 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA2 Gearbox + +]]-- + +-- for lazy programmers +local P2S = minetest.pos_to_string +local M = minetest.get_meta +local S = techage.S + +local PWR_NEEDED = 1 +local CYCLE_TIME = 4 + +local Axle = techage.Axle +local power = techage.power +local networks = techage.networks + +-- Axles texture animation +local function switch_axles(pos, on) + for _,outdir in ipairs(networks.get_node_connections(pos, "axle")) do + Axle:switch_tube_line(pos, outdir, on and "on" or "off") + end +end + +local function swap_node(pos, name) + local node = techage.get_node_lvm(pos) + if node.name == name then + return + end + node.name = name + minetest.swap_node(pos, node) +end + +local function on_power(pos) + swap_node(pos, "techage:gearbox_on") + switch_axles(pos, true) +end + +local function on_nopower(pos) + swap_node(pos, "techage:gearbox") + switch_axles(pos, false) +end + +local function node_timer(pos, elapsed) + power.consumer_alive(pos, Axle, CYCLE_TIME) + return true +end + +-- to be able to restart the node after server crashes +local function techage_on_repair(pos) + minetest.get_node_timer(pos):start(CYCLE_TIME) + power.consumer_start(pos, Axle, CYCLE_TIME) +end + +local function after_place_node(pos) + Axle:after_place_node(pos) + minetest.get_node_timer(pos):start(CYCLE_TIME) + power.consumer_start(pos, Axle, CYCLE_TIME) +end + +local function after_dig_node(pos, oldnode) + Axle:after_dig_node(pos) + techage.del_mem(pos) +end + +local function tubelib2_on_update2(pos, outdir, tlib2, node) + power.update_network(pos, nil, tlib2) +end + +local net_def = { + axle = { + sides = techage.networks.AllSides, -- Cable connection sides + ntype = {"con1", "junc"}, + on_power = on_power, + on_nopower = on_nopower, + nominal = PWR_NEEDED, + }, +} + +minetest.register_node("techage:gearbox", { + description = S("TA2 Gearbox"), + tiles = {"techage_filling_ta2.png^techage_axle_gearbox.png^techage_frame_ta2.png"}, + + on_timer = node_timer, + techage_on_repair = techage_on_repair, + after_place_node = after_place_node, + after_dig_node = after_dig_node, + tubelib2_on_update2 = tubelib2_on_update2, + networks = net_def, + + paramtype = "light", + light_source = 0, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + on_rotate = screwdriver.disallow, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + + +minetest.register_node("techage:gearbox_on", { + tiles = { + -- up, down, right, left, back, front + { + image = "techage_filling4_ta2.png^techage_axle_gearbox4.png^techage_frame4_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + }, + + on_timer = node_timer, + techage_on_repair = techage_on_repair, + after_place_node = after_place_node, + after_dig_node = after_dig_node, + tubelib2_on_update2 = tubelib2_on_update2, + networks = net_def, + + paramtype2 = "facedir", + groups = {not_in_creative_inventory=1}, + diggable = false, + drop = "", + on_rotate = screwdriver.disallow, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +Axle:add_secondary_node_names({"techage:gearbox", "techage:gearbox_on"}) + +minetest.register_craft({ + output = "techage:gearbox 2", + recipe = { + {"default:junglewood", "techage:axle", "default:wood"}, + {"techage:axle", "techage:iron_ingot", "techage:axle"}, + {"default:wood", "techage:axle", "default:junglewood"}, + }, +}) + diff --git a/power/node_api.lua b/power/node_api.lua new file mode 100644 index 0000000..173b565 --- /dev/null +++ b/power/node_api.lua @@ -0,0 +1,186 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + API for Power Nodes + +]]-- + +--local P2S = minetest.pos_to_string +--local M = minetest.get_meta +--local N = function(pos) return minetest.get_node(pos).name end +--local S = techage.S + +local net_def = techage.networks.net_def +local networks = techage.networks + +-- Consumer States +local STOPPED = 1 +local NOPOWER = 2 +local RUNNING = 3 + +techage.power = {} + +techage.power.STOPPED = STOPPED +techage.power.NOPOWER = NOPOWER +techage.power.RUNNING = RUNNING + +-- determine network ID (largest hash number of all generators) +local function determine_netID(pos, outdir, Cable) + local netID = 0 + networks.connection_walk(pos, outdir, Cable, function(pos, indir, node) + local ntype = net_def(pos, Cable.tube_type).ntype + if ntype ~= "junc" then + local new = minetest.hash_node_position(pos) + if netID <= new then + netID = new + end + end + end) + return netID +end + +-- store network ID on each node +local function store_netID(pos, outdir, netID, Cable) + networks.connection_walk(pos, outdir, Cable, function(pos, indir, node) + techage.mark_position("singleplayer", pos, "store", "", 2)----------------------------------------- + --print(node.name, dump(net_def(pos, Cable.tube_type))) + if net_def(pos, Cable.tube_type) then + local nvm = techage.get_nvm(pos) + nvm[Cable.tube_type.."_netID"] = netID + end + end) +end + +-- delete network and ID on each node +local function delete_netID(pos, outdir, Cable) + local netID = 0 + networks.connection_walk(pos, outdir, Cable, function(pos, indir, node) + techage.mark_position("singleplayer", pos, "delete", "", 2)---------------------------------------- + if net_def(pos, Cable.tube_type) then + local nvm = techage.get_nvm(pos) + if nvm[Cable.tube_type.."_netID"] then + netID = nvm[Cable.tube_type.."_netID"] + nvm[Cable.tube_type.."_netID"] = nil + end + end + end) + networks.delete_network(netID, Cable) +end + +-- Keep the network up and running +local function trigger_network(pos, outdir, Cable) + local nvm = techage.get_nvm(pos) + local netID = nvm[Cable.tube_type.."_netID"] + if not netID then + print("determine_netID !!!!!!!!!!!!!!!!!!!!") + netID = determine_netID(pos, outdir, Cable) + store_netID(pos, outdir, netID, Cable) + networks.build_network(pos, outdir, Cable, netID) + elseif not networks.has_network(Cable.tube_type, netID) then + print("build_network !!!!!!!!!!!!!!!!!!!!") + networks.build_network(pos, outdir, Cable, netID) + end +end + +-- To be called from each node via 'tubelib2_on_update2' +-- 'output' is optional and only needed for nodes with dedicated +-- pipe sides (e.g. pumps). +function techage.power.update_network(pos, outdir, Cable) + networks.node_connections(pos, Cable) -- update node internal data + delete_netID(pos, outdir, Cable) -- network walk to delete all IDs +end + + +-- +-- Consumer related functions +-- + +-- check if there is a living network +function techage.power.power_available(pos, Cable) +-- for _,outdir in ipairs(techage.networks.get_node_connections(pos, Cable.tube_type)) do +-- -- generator visible? +-- if determine_netID(pos, outdir, Cable) > 0 then return true end +-- end + local nvm = techage.get_nvm(pos) + local tlib_type = Cable.tube_type + return networks.has_network(tlib_type, nvm[tlib_type.."_netID"]) +-- local mem = techage.get_mem(pos) +-- local tlib_type = Cable.tube_type +-- local netID = nvm[tlib_type.."_netID"] +-- local netw = techage.networks.get_network(tlib_type, netID) +-- if netw then -- network available +-- if not mem.new_ticker or mem.new_ticker ~= netw.ticker then +-- mem.new_ticker = netw.ticker +-- return true +-- end +-- return false +-- end +end + +-- this is more a try to start, the start will be performed by on_power() +function techage.power.consumer_start(pos, Cable, cycle_time) + local nvm = techage.get_nvm(pos) + local tlib_type = Cable.tube_type + nvm[tlib_type.."_calive"] = (cycle_time / 2) + 1 + nvm[tlib_type.."_cstate"] = NOPOWER + nvm[tlib_type.."_taken"] = 0 +end + +function techage.power.consumer_stop(pos, Cable) + local nvm = techage.get_nvm(pos) + local tlib_type = Cable.tube_type + nvm[tlib_type.."_calive"] = 0 + nvm[tlib_type.."_cstate"] = STOPPED + nvm[tlib_type.."_taken"] = 0 +end + +function techage.power.consumer_alive(pos, Cable, cycle_time) + local nvm = techage.get_nvm(pos) + local tlib_type = Cable.tube_type + if nvm[tlib_type.."_netID"] then -- network available + nvm[tlib_type.."_calive"] = (cycle_time / 2) + 1 + elseif nvm[tlib_type.."_cstate"] == RUNNING then + local ndef = net_def(pos, tlib_type) + ndef.on_nopower(pos) + nvm[tlib_type.."_cstate"] = NOPOWER + end + return nvm[tlib_type.."_taken"] or 0 +end + +-- +-- Generator related functions +-- +function techage.power.generator_start(pos, Cable, cycle_time, outdir) + local nvm = techage.get_nvm(pos) + local tlib_type = Cable.tube_type + nvm[tlib_type.."_galive"] = (cycle_time / 2) + 2 + nvm[tlib_type.."_gstate"] = RUNNING + nvm[tlib_type.."_given"] = 0 + trigger_network(pos, outdir, Cable) +end + +function techage.power.generator_stop(pos, Cable, outdir) + local nvm = techage.get_nvm(pos) + local tlib_type = Cable.tube_type + nvm[tlib_type.."_galive"] = 0 + nvm[tlib_type.."_gstate"] = STOPPED + nvm[tlib_type.."_given"] = 0 +end + +function techage.power.generator_alive(pos, Cable, cycle_time, outdir) + local nvm = techage.get_nvm(pos) + local tlib_type = Cable.tube_type + trigger_network(pos, outdir, Cable) + nvm[tlib_type.."_galive"] = (cycle_time / 2) + 2 + return nvm[tlib_type.."_given"] or 0 +end + +-- function delete_netID(pos, outdir, Cable) +techage.power.delete_netID = delete_netID diff --git a/power/power_terminal.lua b/power/power_terminal.lua new file mode 100644 index 0000000..efc06b2 --- /dev/null +++ b/power/power_terminal.lua @@ -0,0 +1,220 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA3 Power Terminal + +]]-- + +-- for lazy programmers +local S2P = 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 CYCLE_TIME = 2 + +local Cable = techage.ElectricCable +local power = techage.power +local networks = techage.networks +local STOPPED = techage.power.STOPPED +local NOPOWER = techage.power.NOPOWER +local RUNNING = techage.power.RUNNING + +local function generator_data(gen_tbl) + local tbl = { + pow_all=0, pow_on=0, pow_act=0, pow_used=0, + num_on=0, num_act=0, num_used=0 + } + for i,gen in ipairs(gen_tbl or {}) do + local nvm = techage.get_nvm(gen.pos) + tbl.pow_all = tbl.pow_all + (gen.nominal or 0) + if nvm.ele1_gstate and nvm.ele1_gstate ~= STOPPED then + tbl.num_on = tbl.num_on + 1 + tbl.pow_on = tbl.pow_on + (gen.nominal or 0) + if (nvm.ele1_galive or 0) > 0 then + tbl.num_act = tbl.num_act + 1 + tbl.pow_act = tbl.pow_act + (gen.nominal or 0) + if (nvm.ele1_given or 0) > 0 then + tbl.num_used = tbl.num_used + 1 + tbl.pow_used = tbl.pow_used + (nvm.ele1_given or 0) + end + end + end + end + + tbl.num_all = #(gen_tbl or {}) + return tbl +end + +local function consumer_data(con_tbl) + local tbl = { + pow_all=0, pow_on=0, pow_act=0, pow_used=0, + num_on=0, num_act=0, num_used=0 + } + for i,gen in ipairs(con_tbl or {}) do + local nvm = techage.get_nvm(gen.pos) + tbl.pow_all = tbl.pow_all + (gen.nominal or 0) + if nvm.ele1_cstate and nvm.ele1_cstate ~= STOPPED then + tbl.num_on = tbl.num_on + 1 + tbl.pow_on = tbl.pow_on + (gen.nominal or 0) + if (nvm.ele1_calive or 0) > 0 then + tbl.num_act = tbl.num_act + 1 + tbl.pow_act = tbl.pow_act + (gen.nominal or 0) + if (nvm.ele1_taken or 0) > 0 then + tbl.num_used = tbl.num_used + 1 + tbl.pow_used = tbl.pow_used + (nvm.ele1_taken or 0) + end + end + end + end + + tbl.num_all = #(con_tbl or {}) + return tbl +end + +local function calc_network_data(pos, nvm) + local netw = techage.networks.has_network("ele1", nvm.ele1_netID) or {} + local gen1 = generator_data(netw.gen1) + local gen2 = generator_data(netw.gen2) + local con1 = consumer_data(netw.con1) + local con2 = consumer_data(netw.con2) + + return netw, gen1, gen2, con1, con2 +end + +local function column(x,y, data) + return + "label["..x..","..(y+0.0)..";"..data.num_all.. " ("..data.pow_all.." ku)]".. + "label["..x..","..(y+0.5)..";"..data.num_on.. " ("..data.pow_on.." ku)]".. + "label["..x..","..(y+1.0)..";"..data.num_act.. " ("..data.pow_act.." ku)]".. + "label["..x..","..(y+1.5)..";"..data.num_used.." ("..data.pow_used.." ku)]" +end + +local function get_state(netw, gen1, gen2, con1, con2) + local num_nodes = gen1.num_all + gen2.num_all + con1.num_all + + con2.num_all + (#(netw.junc or {})) + (#(netw.term or {})) + local nload = (gen1.pow_act + gen2.pow_act) / con1.pow_act + local state = S("Number of all nodes")..": ".. num_nodes + if not netw.gen1 and not netw.gen2 then + state = S("No network or active generator available!") + elseif num_nodes > (techage.ELE1_MAX_CABLE_LENGHT - 50) then + state = string.format(S("With %u of a maximum of %u blocks you are almost at the limit!"), + num_nodes, techage.ELE1_MAX_CABLE_LENGHT) + elseif nload <= 1.0 then + state = S("The network is overloaded!") + elseif nload < 1.2 then + state = S("The network load is almost at the limit!") + end + return state +end + +local function formspec(pos, nvm) + local netw, gen1, gen2, con1, con2 = calc_network_data(pos, nvm) + netw.prop = ((netw.prop or 0) + 1) % 2 + local star = netw.prop == 1 and "*" or "" + local state = get_state(netw, gen1, gen2, con1, con2) + + return "size[10,7]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "box[0,-0.1;9.8,0.5;#c6e8ff]".. + "label[4,-0.1;"..minetest.colorize( "#000000", S("Network Data")).."]".. + "label[9.5,-0.1;"..minetest.colorize( "#000000", star).."]".. + power.formspec_label_bar(0, 0.7, S("Genera. 1"), gen1.pow_act, gen1.pow_used).. + power.formspec_label_bar(2.5, 0.7, S("Genera. 2"), gen2.pow_act, gen2.pow_used).. + power.formspec_label_bar(5, 0.7, S("Consum. 2"), con2.pow_act, con2.pow_used).. + power.formspec_label_bar(7.5, 0.7, S("Consum. 1"), con1.pow_act, con1.pow_used).. + "box[0,4.3;9.8,0.4;#c6e8ff]".. + "box[0,4.85;9.8,0.4;#395c74]".. + "box[0,5.35;9.8,0.4;#395c74]".. + "box[0,5.85;9.8,0.4;#395c74]".. + "box[0,6.35;9.8,0.4;#395c74]".. + "label[2,4.3;"..minetest.colorize( "#000000", S("Genera. 1")).."]".. + "label[4,4.3;"..minetest.colorize( "#000000", S("Genera. 2")).."]".. + "label[6,4.3;"..minetest.colorize( "#000000", S("Consum. 2")).."]".. + "label[8,4.3;"..minetest.colorize( "#000000", S("Consum. 1")).."]".. + "label[0.1,4.8;"..S("All nodes:").."]".. + "label[0.1,5.3;"..S("Turned on:").."]".. + "label[0.1,5.8;"..S("Active:").."]".. + "label[0.1,6.3;"..S("In use:").."]".. + "box[0,6.95;9.8,0.4;#000000]".. + "label[0.1,6.9;"..state.."]".. + column(2, 4.8, gen1).. + column(4, 4.8, gen2).. + column(6, 4.8, con2).. + column(8, 4.8, con1) +end + +minetest.register_node("techage:power_terminal", { + description = S("TA3 Power Terminal"), + inventory_image = "techage_power_terminal_front.png", + tiles = { + "techage_power_terminal_top.png", + "techage_power_terminal_top.png", + "techage_power_terminal_side.png", + "techage_power_terminal_side.png", + "techage_power_terminal_back.png", + "techage_power_terminal_front.png", + }, + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + { -8/16, -8/16, 0/16, 8/16, 8/16, 8/16}, + }, + }, + + after_place_node = function(pos) + local nvm = techage.get_nvm(pos) + M(pos):set_int("outdir", networks.side_to_outdir(pos, "B")) + Cable:after_place_node(pos) + M(pos):set_string("formspec", formspec(pos, nvm)) + end, + after_dig_node = function(pos) + Cable:after_dig_node(pos) + techage.del_mem(pos) + end, + tubelib2_on_update2 = function(pos, outdir, tlib2, node) + power.update_network(pos, outdir, tlib2) + end, + on_rightclick = function(pos, node, clicker) + techage.set_activeformspec(pos, clicker) + minetest.get_node_timer(pos):start(CYCLE_TIME) + local nvm = techage.get_nvm(pos) + M(pos):set_string("formspec", formspec(pos, nvm)) + end, + on_timer = function(pos, elapsed) + local nvm = techage.get_nvm(pos) + if techage.is_activeformspec(pos) then + M(pos):set_string("formspec", formspec(pos, nvm)) + end + return true + end, + + networks = { + ele1 = { + sides = {B = 1}, -- Cable connection side + ntype = "term", + }, + }, + + paramtype2 = "facedir", + paramtype = "light", + on_rotate = screwdriver.disallow, + sunlight_propagates = true, + is_ground_content = false, + groups = {cracky = 1, level = 2}, + sounds = default.node_sound_metal_defaults(), +}) + +Cable:add_secondary_node_names({"techage:power_terminal"}) + diff --git a/power/powerswitchbox.lua b/power/powerswitchbox.lua new file mode 100644 index 0000000..ee0cdee --- /dev/null +++ b/power/powerswitchbox.lua @@ -0,0 +1,183 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA3 Power Switch Box +]]-- + +-- for lazy programmers +local S2P = 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 Cable = techage.ElectricCable +local power = techage.power +local networks = techage.networks + +-- simpe rotation of a facedir node through all 3 axis positions +local Rotation = {[0]= + 11,8,12,10,14,13,16,15,18,15,5,19,21,20,23,22,1,0,3,2,7,4,9,6 +} + +local function get_conn_dirs(pos, node) + local tbl = {[0]= + {R=2,L=4}, {R=1,L=3}, {R=2,L=4}, {R=1,L=3}, + {R=2,L=4}, {D=5,U=6}, {R=2,L=4}, {D=5,U=6}, + {R=2,L=4}, {D=5,U=6}, {R=2,L=4}, {D=5,U=6}, + {D=5,U=6}, {R=1,L=3}, {D=5,U=6}, {R=1,L=3}, + {D=5,U=6}, {R=1,L=3}, {D=5,U=6}, {R=1,L=3}, + {R=2,L=4}, {R=1,L=3}, {R=2,L=4}, {R=1,L=3}, + } + if M(pos):get_int("turned_off") == 1 then + return {} + end + return tbl[node.param2] +end + +local function update_network(pos, node) +-- power.update_network(pos, nil, Cable) + for _,outdir in pairs(get_conn_dirs(pos, node)) do + power.update_network(pos, outdir, Cable) + end +end + +local function on_rotate(pos, node, user, mode, new_param2) + if minetest.is_protected(pos, user:get_player_name()) then + return false + end + update_network(pos, node) + node.param2 = Rotation[node.param2] + minetest.swap_node(pos, node) + return true +end + +minetest.register_node("techage:powerswitch_box", { + description = S("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}, + }, + }, + + after_place_node = function(pos) + Cable:after_place_node(pos) + end, + after_dig_node = function(pos) + Cable:after_dig_node(pos) + end, + tubelib2_on_update2 = function(pos, outdir, tlib2, node) + --print("powerswitch_box tubelib2_on_update2") + update_network(pos, node) + end, + on_rightclick = function(pos, node, clicker) + node.name = "techage:powerswitch_box_off" + minetest.swap_node(pos, node) + M(pos):set_int("turned_off", 1) + minetest.sound_play("techage_button", { + pos = pos, + gain = 0.5, + max_hear_distance = 5, + }) + Cable:after_dig_node(pos) + update_network(pos, node) + end, + + networks = { + ele1 = { + get_sides = get_conn_dirs, + --sides = networks.AllSides, + ntype = "junc", + }, + }, + + paramtype = "light", + sunlight_propagates = true, + 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(), +}) + +minetest.register_node("techage:powerswitch_box_off", { + description = S("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}, + }, + }, + + after_place_node = function(pos) + Cable:after_place_node(pos) + end, + after_dig_node = function(pos) + Cable:after_dig_node(pos) + end, + tubelib2_on_update2 = function(pos, outdir, tlib2, node) + update_network(pos, node) + end, + on_rightclick = function(pos, node, clicker) + node.name = "techage:powerswitch_box" + minetest.swap_node(pos, node) + M(pos):set_int("turned_off", 0) + minetest.sound_play("techage_button", { + pos = pos, + gain = 0.5, + max_hear_distance = 5, + }) + Cable:after_dig_node(pos) + update_network(pos, node) + end, + + networks = { + ele1 = { + --sides = {}, + --sides = networks.AllSides, + get_sides = get_conn_dirs, + ntype = "", -- unknown type, acting as switch off + }, + }, + + paramtype = "light", + sunlight_propagates = true, + on_rotate = on_rotate, + paramtype2 = "facedir", + drop = "techage:powerswitch_box", + groups = {choppy=2, cracky=2, crumbly=2, techage_trowel = 1, not_in_creative_inventory = 1}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +Cable:add_secondary_node_names({"techage:powerswitch_box"}) diff --git a/ta3_power/akkubox.lua b/ta3_power/akkubox.lua new file mode 100644 index 0000000..fb2dfc3 --- /dev/null +++ b/ta3_power/akkubox.lua @@ -0,0 +1,225 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA3 Accu Box + +]]-- + +-- for lazy programmers +local P2S = minetest.pos_to_string +local M = minetest.get_meta +local S = techage.S + +local CYCLE_TIME = 2 +local PWR_PERF = 10 +local PWR_CAPA = 2000 + +local Cable = techage.ElectricCable +local power = techage.power +local networks = techage.networks +local in_range = techage.in_range + + +local function formspec(self, pos, nvm) + local needed = nvm.needed or 0 + local capa = nvm.capa or 0 + return "size[5,4]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "box[0,-0.1;4.8,0.5;#c6e8ff]".. + "label[1,-0.1;"..minetest.colorize( "#000000", S("TA3 Akku Box")).."]".. + power.formspec_label_bar(0, 0.8, S("Load"), PWR_CAPA, capa).. + "image_button[3,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]".. + "tooltip[3,2;1,1;"..self:get_state_tooltip(nvm).."]".. + "label[4.2,1.2;Flow]".. + "image[4,1.7;1,2;"..techage.power.formspec_load_bar(needed, PWR_PERF).."]" +end + + +local function start_node(pos, nvm, state) + nvm.running = true + nvm.needed = 0 + local outdir = M(pos):get_int("outdir") + power.generator_start(pos, Cable, CYCLE_TIME, outdir) + power.consumer_start(pos, Cable, CYCLE_TIME, outdir) +end + +local function stop_node(pos, nvm, state) + nvm.running = false + nvm.needed = 0 + local outdir = M(pos):get_int("outdir") + power.generator_stop(pos, Cable, outdir) + power.consumer_stop(pos, Cable, outdir) +end + +local State = techage.NodeStates:new({ + node_name_passive = "techage:ta3_akku", + cycle_time = CYCLE_TIME, + standby_ticks = 0, + formspec_func = formspec, + start_node = start_node, + stop_node = stop_node, +}) + +local function node_timer(pos, elapsed) + local nvm = techage.get_nvm(pos) + nvm.capa = nvm.capa or 0 + local outdir = M(pos):get_int("outdir") + local taken = 0 + local given = 0 + if nvm.capa < PWR_CAPA then + taken = power.consumer_alive(pos, Cable, CYCLE_TIME) + end + if nvm.capa > 0 then + given = power.generator_alive(pos, Cable, CYCLE_TIME, outdir) + end + nvm.needed = taken - given + nvm.capa = in_range(nvm.capa + nvm.needed, 0, PWR_CAPA) + --print("node_timer accu "..P2S(pos), nvm.needed, nvm.capa) + + if techage.is_activeformspec(pos) then + M(pos):set_string("formspec", formspec(State, pos, nvm)) + end + + return true +end + +local function on_rightclick(pos, node, clicker) + techage.set_activeformspec(pos, clicker) + local nvm = techage.get_nvm(pos) + M(pos):set_string("formspec", formspec(State, pos, nvm)) +end + +local function on_receive_fields(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + return + end + local nvm = techage.get_nvm(pos) + State:state_button_event(pos, nvm, fields) + M(pos):set_string("formspec", formspec(State, pos, nvm)) +end + +local function get_capa(itemstack) + local meta = itemstack:get_meta() + if meta then + return in_range(meta:get_int("capa") * (PWR_CAPA/100), 0, 3000) + end + return 0 +end + +local function set_capa(pos, oldnode, digger, capa) + local node = ItemStack(oldnode.name) + local meta = node:get_meta() + capa = techage.power.percent(PWR_CAPA, capa) + capa = (math.floor((capa or 0) / 5)) * 5 + meta:set_int("capa", capa) + local text = S("TA3 Accu Box").." ("..capa.." %)" + meta:set_string("description", text) + local inv = minetest.get_inventory({type="player", name=digger:get_player_name()}) + local left_over = inv:add_item("main", node) + if left_over:get_count() > 0 then + minetest.add_item(pos, node) + end +end + +local function after_place_node(pos, placer, itemstack) + local meta = M(pos) + local nvm = techage.get_nvm(pos) + local own_num = techage.add_node(pos, "techage:ta3_akku") + meta:set_string("owner", placer:get_player_name()) + meta:set_string("infotext", S("TA3 Accu Box").." "..own_num) + meta:set_int("outdir", networks.side_to_outdir(pos, "R")) + meta:set_string("formspec", formspec(State, pos, nvm)) + Cable:after_place_node(pos) + State:node_init(pos, nvm, own_num) + nvm.capa = get_capa(itemstack) +end + +local function after_dig_node(pos, oldnode, oldmetadata, digger) + local nvm = techage.get_nvm(pos) + Cable:after_dig_node(pos) + set_capa(pos, oldnode, digger, nvm.capa) + techage.del_mem(pos) +end + +local function tubelib2_on_update2(pos, outdir, tlib2, node) + power.update_network(pos, outdir, tlib2) +end + +local net_def = { + ele1 = { + sides = {R = 1}, + ntype = {"gen2", "con2"}, + nominal = PWR_PERF, + }, +} + +minetest.register_node("techage:ta3_akku", { + description = S("TA3 Accu Box"), + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta3.png^techage_frame_ta3_top.png", + "techage_filling_ta3.png^techage_frame_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_hole_electric.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_source.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_source.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_source.png", + }, + + on_timer = node_timer, + on_rightclick = on_rightclick, + on_receive_fields = on_receive_fields, + after_place_node = after_place_node, + after_dig_node = after_dig_node, + tubelib2_on_update2 = tubelib2_on_update2, + networks = net_def, + + drop = "", -- don't remove, item will be added via 'set_capa' + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + on_rotate = screwdriver.disallow, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +Cable:add_secondary_node_names({"techage:ta3_akku"}) + +-- for logical communication +techage.register_node({"techage:ta3_akku"}, { + on_recv_message = function(pos, src, topic, payload) + local nvm = techage.get_nvm(pos) + if topic == "load" then + return techage.power.percent(PWR_CAPA, nvm.capa) + elseif topic == "size" then + return PWR_CAPA + else + return State:on_receive_message(pos, topic, payload) + end + end, + on_node_load = function(pos) + local meta = M(pos) + meta:set_int("outdir", networks.side_to_outdir(pos, "R")) + if meta:get_string("node_number") == "" then + local own_num = techage.add_node(pos, "techage:ta3_akku") + meta:set_string("node_number", own_num) + meta:set_string("infotext", S("TA3 Accu Box").." "..own_num) + end + end, +}) + +minetest.register_craft({ + output = "techage:ta3_akku", + recipe = { + {"default:tin_ingot", "default:tin_ingot", "default:wood"}, + {"default:copper_ingot", "default:copper_ingot", "techage:electric_cableS"}, + {"techage:iron_ingot", "techage:iron_ingot", "default:wood"}, + }, +}) diff --git a/ta3_power/tiny_generator.lua b/ta3_power/tiny_generator.lua new file mode 100644 index 0000000..3964f83 --- /dev/null +++ b/ta3_power/tiny_generator.lua @@ -0,0 +1,280 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019-2020 Joachim Stolberg + + GPL v3 + See LICENSE.txt for more information + + TA3 Tiny Power Generator + +]]-- + +-- for lazy programmers +local M = minetest.get_meta +local S = techage.S + +local Power = techage.ElectricCable +local firebox = techage.firebox +local power = techage.power +local fuel = techage.fuel +local Pipe = techage.LiquidPipe +local liquid = techage.liquid + +local CYCLE_TIME = 2 +local PWR_CAPA = 12 +local EFFICIENCY = 2.5 + +local function formspec(self, pos, mem) + local fuel_percent = 0 + if mem.running then + fuel_percent = ((mem.burn_cycles or 1) * 100) / (mem.burn_cycles_total or 1) + end + return "size[8,6]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + fuel.formspec_fuel(1, 0, mem).. + "button[1.6,1;1.8,1;update;"..S("Update").."]".. + "image_button[5.5,0.5;1,1;".. self:get_state_button_image(mem) ..";state_button;]".. + "image[6.5,0;1,2;"..power.formspec_power_bar(PWR_CAPA, mem.provided).."]".. + "list[current_player;main;0,2.3;8,4;]".. + default.get_hotbar_bg(0, 3) +end + +local function can_start(pos, mem, state) + if mem.burn_cycles > 0 or (mem.liquid and mem.liquid.amount and mem.liquid.amount > 0) then + return true + end + return false +end + +local function start_node(pos, mem, state) + mem.running = true + power.generator_start(pos, mem, PWR_CAPA) + minetest.sound_play("techage_generator", { + pos = pos, + gain = 1, + max_hear_distance = 10}) +end + +local function stop_node(pos, mem, state) + mem.running = false + mem.provided = 0 + power.generator_stop(pos, mem) +end + +local State = techage.NodeStates:new({ + node_name_passive = "techage:tiny_generator", + node_name_active = "techage:tiny_generator_on", + cycle_time = CYCLE_TIME, + standby_ticks = 0, + formspec_func = formspec, + infotext_name = "TA3 Tiny Power Generator", + can_start = can_start, + start_node = start_node, + stop_node = stop_node, +}) + +local function burning(pos, mem) + local ratio = math.max((mem.provided or PWR_CAPA) / PWR_CAPA, 0.02) + + mem.liquid = mem.liquid or {} + mem.liquid.amount = mem.liquid.amount or 0 + mem.burn_cycles = (mem.burn_cycles or 0) - ratio + if mem.burn_cycles <= 0 then + if mem.liquid.amount > 0 then + mem.liquid.amount = mem.liquid.amount - 1 + mem.burn_cycles = fuel.burntime(mem.liquid.name) * EFFICIENCY / CYCLE_TIME + mem.burn_cycles_total = mem.burn_cycles + return true + else + mem.liquid.name = nil + State:fault(pos, mem) + return false + end + else + return true + end +end + +local function node_timer(pos, elapsed) + local mem = tubelib2.get_mem(pos) + if mem.running and burning(pos, mem) then + mem.provided = power.generator_alive(pos, mem) + minetest.sound_play("techage_generator", { + pos = pos, + gain = 1, + max_hear_distance = 10}) + return true + else + mem.provided = 0 + 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 + local mem = tubelib2.get_mem(pos) + State:state_button_event(pos, mem, fields) + + M(pos):set_string("formspec", formspec(State, pos, mem)) +end + + +local function formspec_clbk(pos, mem) + return formspec(State, pos, mem) +end + +local function on_metadata_inventory_put(pos, listname, index, stack, player) + minetest.after(0.5, fuel.move_item, pos, stack, formspec_clbk) +end + +local function on_rightclick(pos) + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(State, pos, mem)) +end + +local _liquid = { + fuel_cat = fuel.BT_NAPHTHA, + capa = fuel.CAPACITY, + peek = liquid.srv_peek, + put = function(pos, indir, name, amount) + if fuel.valid_fuel(name, fuel.BT_NAPHTHA) then + return liquid.srv_put(pos, indir, name, amount) + end + return amount + end, + take = liquid.srv_take, +} + +local _networks = { + pipe = { + sides = techage.networks.AllSides, -- Pipe connection sides + ntype = "tank", + }, +} + +minetest.register_node("techage:tiny_generator", { + description = S("TA3 Tiny Power Generator"), + tiles = { + -- up, down, right, left, back, front + "techage_appl_electric_gen_top.png^techage_frame_ta3_top.png", + "techage_appl_electric_gen_top.png^techage_frame_ta3.png", + "techage_appl_electric_gen_side.png^techage_appl_hole_electric.png^techage_frame_ta3.png", + "techage_appl_electric_gen_side.png^techage_frame_ta3.png", + "techage_appl_electric_gen_front.png^[transformFX]^techage_frame_ta3.png", + "techage_appl_electric_gen_front.png^techage_frame_ta3.png", + }, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + on_rotate = screwdriver.disallow, + is_ground_content = false, + + on_construct = function(pos) + local mem = tubelib2.init_mem(pos) + local number = techage.add_node(pos, "techage:tiny_generator") + mem.running = false + mem.burn_cycles = 0 + State:node_init(pos, mem, number) + local meta = M(pos) + meta:set_string("formspec", formspec(State, pos, mem)) + local inv = meta:get_inventory() + inv:set_size('fuel', 1) + end, + + + allow_metadata_inventory_put = fuel.allow_metadata_inventory_put, + allow_metadata_inventory_take = fuel.allow_metadata_inventory_take, + on_metadata_inventory_put = on_metadata_inventory_put, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + on_timer = node_timer, + can_dig = fuel.can_dig, + liquid = _liquid, + networks = _networks, +}) + +minetest.register_node("techage:tiny_generator_on", { + description = S("TA3 Tiny Power Generator"), + tiles = { + -- up, down, right, left, back, front + "techage_appl_electric_gen_top.png^techage_frame_ta3_top.png", + "techage_appl_electric_gen_top.png^techage_frame_ta3.png", + "techage_appl_electric_gen_side.png^techage_appl_hole_electric.png^techage_frame_ta3.png", + "techage_appl_electric_gen_side.png^techage_frame_ta3.png", + { + image = "techage_appl_electric_gen_front4.png^[transformFX]^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.8, + }, + }, + { + image = "techage_appl_electric_gen_front4.png^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.8, + }, + }, + }, + + paramtype = "light", + paramtype2 = "facedir", + groups = {not_in_creative_inventory=1}, + diggable = false, + light_source = 4, + on_rotate = screwdriver.disallow, + is_ground_content = false, + + allow_metadata_inventory_put = fuel.allow_metadata_inventory_put, + allow_metadata_inventory_take = fuel.allow_metadata_inventory_take, + on_metadata_inventory_put = on_metadata_inventory_put, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + on_timer = node_timer, + can_dig = fuel.can_dig, + liquid = _liquid, + networks = _networks, +}) + +Pipe:add_secondary_node_names({"techage:tiny_generator", "techage:tiny_generator_on"}) + +techage.power.register_node({"techage:tiny_generator", "techage:tiny_generator_on"}, { + conn_sides = {"R"}, + power_network = Power, +}) + +techage.register_node({"techage:tiny_generator", "techage:tiny_generator_on"}, { + on_recv_message = function(pos, src, topic, payload) + local mem = tubelib2.get_mem(pos) + if topic == "load" then + return power.percent(PWR_CAPA, mem.provided) + else + return State:on_receive_message(pos, topic, payload) + end + end, + on_node_load = function(pos) + State:on_node_load(pos) + end, +}) + +minetest.register_craft({ + output = "techage:tiny_generator", + recipe = { + {'default:steel_ingot', 'techage:usmium_nuggets', 'default:steel_ingot'}, + {'dye:red', 'basic_materials:gear_steel', 'techage:electric_cableS'}, + {'default:steel_ingot', 'techage:vacuum_tube', 'default:steel_ingot'}, + }, +}) +