diff --git a/basis/command.lua b/basis/command.lua new file mode 100644 index 0000000..062521e --- /dev/null +++ b/basis/command.lua @@ -0,0 +1,434 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Basis functions for inter-node communication + +]]-- + +--- 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 + + +------------------------------------------------------------------ +-- Data base storage +------------------------------------------------------------------- +local storage = minetest.get_mod_storage() +local NextNumber = minetest.deserialize(storage:get_string("NextNumber")) or 1 +local Version = minetest.deserialize(storage:get_string("Version")) or 1 +local Number2Pos = minetest.deserialize(storage:get_string("Number2Pos")) or {} + +local function update_mod_storage() + minetest.log("action", "[TechAge] Store data...") + storage:set_string("NextNumber", minetest.serialize(NextNumber)) + storage:set_string("Version", minetest.serialize(Version)) + storage:set_string("Number2Pos", minetest.serialize(Number2Pos)) + storage:set_string("Key2Number", nil) -- not used any more + -- store data each hour + minetest.after(60*59, update_mod_storage) + minetest.log("action", "[TechAge] Data stored") +end + +minetest.register_on_shutdown(function() + update_mod_storage() +end) + +-- store data after one hour +minetest.after(60*59, update_mod_storage) + +-- Key2Number will be generated at runtine +local Key2Number = {} + +local Name2Name = {} -- translation table + +------------------------------------------------------------------- +-- Local helper functions +------------------------------------------------------------------- + +-- Localize functions to avoid table lookups (better performance). +local string_split = string.split +local NodeDef = techage.NodeDef +local Tube = techage.Tube + +-- Determine position related node number for addressing purposes +local function get_number(pos) + local key = minetest.hash_node_position(pos) + if not Key2Number[key] then + Key2Number[key] = NextNumber + NextNumber = NextNumber + 1 + end + return string.format("%u", Key2Number[key]) +end + +local function generate_Key2Number() + local key + for num,item in pairs(Number2Pos) do + key = minetest.hash_node_position(item.pos) + Key2Number[key] = num + end +end + +local function not_protected(pos, placer_name, clicker_name) + local meta = minetest.get_meta(pos) + if meta then + local cached_name = meta:get_string("techage_cached_name") + if placer_name and (placer_name == cached_name or not minetest.is_protected(pos, placer_name)) then + meta:set_string("techage_cached_name", placer_name) + if clicker_name == nil or not minetest.is_protected(pos, clicker_name) then + return true + end + end + end + return false +end + +local function register_lbm(name, nodenames) + minetest.register_lbm({ + label = "[Tubelib] Node update", + name = name.."update", + nodenames = nodenames, + run_at_every_load = true, + action = function(pos, node) + local name = Name2Name[node.name] + if NodeDef[name] and NodeDef[name].on_node_load then + NodeDef[name].on_node_load(pos) + end + end + }) +end + + +local DirToSide = {"B", "R", "F", "L", "D", "U"} + +local function dir_to_side(dir, param2) + if dir < 5 then + dir = (((dir - 1) - (param2 % 4)) % 4) + 1 + end + return DirToSide[dir] +end + +local SideToDir = {B=1, R=2, F=3, L=4, D=5, U=6} + +local function side_to_dir(side, param2) + local dir = SideToDir[side] + if dir < 5 then + dir = (((dir - 1) + (param2 % 4)) % 4) + 1 + end + return dir +end + +local function get_dest_node(pos, side) + -- TODO die Daten aus dem Cache holen und ueber die node callback wieder loeschen + local _,node = Tube:get_node(pos) + local dir = side_to_dir(side, node.param2) + local spos, sdir = Tube:get_connected_node_pos(pos, dir) + _,node = Tube:get_node(spos) + local in_side = dir_to_side(sdir, node.param2) + return spos, in_side, Name2Name[node.name] or node.name +end + +local function item_handling_node(name) + local node_def = name and NodeDef[name] + if node_def then + return node_def.on_pull_item or node_def.on_push_item or node_def.is_pusher + end +end + +------------------------------------------------------------------- +-- API helper functions +------------------------------------------------------------------- + +-- Check the given list of numbers. +-- Returns true if number(s) is/are valid and point to real nodes. +function techage.check_numbers(numbers) + if numbers then + for _,num in ipairs(string_split(numbers, " ")) do + if Number2Pos[num] == nil then + return false + end + end + return true + end + return false +end + +-- Function returns { pos, name } for the node on the given position number. +function techage.get_node_info(dest_num) + if Number2Pos[dest_num] then + return Number2Pos[dest_num] + end + return nil +end + +-- Function returns the node number from the given position or +-- nil, if no node number for this position is assigned. +function techage.get_node_number(pos) + local key = minetest.hash_node_position(pos) + local num = Key2Number[key] + if num then + num = string.format("%u", num) + if Number2Pos[num] and Number2Pos[num].name then + return num + end + end + return nil +end + +-- Function is used for available nodes with lost numbers, only. +function techage.get_new_number(pos, name) + -- store position + local number = get_number(pos) + Number2Pos[number] = { + pos = pos, + name = name, + } + return number +end + +------------------------------------------------------------------- +-- Node construction/destruction functions +------------------------------------------------------------------- + +-- Add node to the techage lists. +-- Function determines and returns the node position number, +-- needed for message communication. +function techage.add_node(pos, name) + if item_handling_node(name) then + Tube:after_place_node(pos) + end + -- store position + local number = get_number(pos) + Number2Pos[number] = { + pos = pos, + name = name, + } + return number +end + +-- Function removes the node from the techage lists. +function techage.remove_node(pos) + local number = get_number(pos) + local name + if Number2Pos[number] then + name = Number2Pos[number].name + Number2Pos[number] = { + pos = pos, + name = nil, + time = minetest.get_day_count() -- used for reservation timeout + } + end + if item_handling_node(name) then + Tube:after_dig_node(pos) + end +end + + +------------------------------------------------------------------- +-- Node register function +------------------------------------------------------------------- + +-- Register node for techage communication +-- Call this function only at load time! +-- Param name: The node name like "techage:pusher" +-- Param add_names: Alternativ node names if needded, e.g.: "techage:pusher_active" +-- Param node_definition: A table according to: +-- { +-- on_pull_item = func(pos, side, player_name, num), +-- on_push_item = func(pos, side, item, player_name), +-- on_unpull_item = func(pos, side, item, player_name), +-- on_recv_message = func(pos, topic, payload), +-- on_node_load = func(pos), -- LBM function +-- on_node_repair = func(pos), -- repair defect (feature!) nodes +-- } +function techage.register_node(name, add_names, node_definition) + NodeDef[name] = node_definition + -- store facedir table for all known node names + Name2Name[name] = name + for _,n in ipairs(add_names) do + Name2Name[n] = name + end + if node_definition.on_pull_item or node_definition.on_push_item or + node_definition.is_pusher then + Tube:add_secondary_node_names({name}) + Tube:add_secondary_node_names(add_names) + + techage.KnownNodes[name] = true + for _,n in ipairs(add_names) do + techage.KnownNodes[n] = true + end + end + -- register LBM + if node_definition.on_node_load then + local nodenames = {name} + for _,n in ipairs(add_names) do + nodenames[#nodenames + 1] = n + end + register_lbm(name, nodenames) + end +end + +------------------------------------------------------------------- +-- Send message functions +------------------------------------------------------------------- + +function techage.send_message(numbers, placer_name, clicker_name, topic, payload) + for _,num in ipairs(string_split(numbers, " ")) do + if Number2Pos[num] and Number2Pos[num].name then + local data = Number2Pos[num] + if not_protected(data.pos, placer_name, clicker_name) then + if NodeDef[data.name] and NodeDef[data.name].on_recv_message then + NodeDef[data.name].on_recv_message(data.pos, topic, payload) + end + end + end + end +end + +function techage.send_request(number, topic, payload) + if Number2Pos[number] and Number2Pos[number].name then + local data = Number2Pos[number] + if NodeDef[data.name] and NodeDef[data.name].on_recv_message then + return NodeDef[data.name].on_recv_message(data.pos, topic, payload) + end + end + return false +end + +-- for defect nodes +function techage.repair_node(pos) + local node = minetest.get_node(pos) + local name = Name2Name[node.name] + if NodeDef[name] and NodeDef[name].on_node_repair then + return NodeDef[name].on_node_repair(pos) + end + return false +end + +------------------------------------------------------------------- +-- Client side Push/Pull item functions +------------------------------------------------------------------- + +function techage.pull_items(pos, side, num) + local npos, nside, name = get_dest_node(pos, side) + if npos == nil then return end + if NodeDef[name] and NodeDef[name].on_pull_item then + return NodeDef[name].on_pull_item(npos, nside, num) + end + return nil +end + +function techage.push_items(pos, side, stack) + local npos, nside, name = get_dest_node(pos, side) + if npos == nil then return end + if NodeDef[name] and NodeDef[name].on_push_item then + return NodeDef[name].on_push_item(npos, nside, stack) + elseif name == "air" then + minetest.add_item(npos, stack) + return true + end + return false +end + +function techage.unpull_items(pos, side, items) + local npos, nside, name = get_dest_node(pos, side) + if npos == nil then return end + if NodeDef[name] and NodeDef[name].on_unpull_item then + return NodeDef[name].on_unpull_item(npos, nside, items) + end + return false +end + + +------------------------------------------------------------------- +-- Server side helper functions +------------------------------------------------------------------- + +-- Get the given number of items from the inve. The position within the list +-- is random so that different item stacks will be considered. +-- Returns nil if ItemList is empty. +function techage.get_items(inv, listname, num) + if inv:is_empty(listname) then + return nil + end + local size = inv:get_size(listname) + local startpos = math.random(1, size) + for idx = startpos, startpos+size do + idx = (idx % size) + 1 + local items = inv:get_stack(listname, idx) + if items:get_count() > 0 then + local taken = items:take_item(num) + inv:set_stack(listname, idx, items) + return taken + end + end + return nil +end + +-- Put the given stack into the given ItemList. +-- Function returns false if ItemList is full. +function techage.put_item(inv, listname, stack) + if inv:room_for_item(listname, stack) then + inv:add_item(listname, stack) + return true + end + return false +end + + +-- Return "full", "loaded", or "empty" depending +-- on the inventory load. +-- Full is returned, when no empty stack is available. +function techage.get_inv_state(inv, listname) + local state + if inv:is_empty(listname) then + state = "empty" + else + local list = inv:get_list(listname) + state = "full" + for _, item in ipairs(list) do + if item:is_empty() then + return "loaded" + end + end + end + return state +end + + +------------------------------------------------------------------------------- +-- Data Maintenance +------------------------------------------------------------------------------- +local function data_maintenance() + minetest.log("info", "[TechAge] Data maintenance started") + -- Remove old unused positions + local Tbl = table.copy(Number2Pos) + Number2Pos = {} + local day_cnt = minetest.get_day_count() + for num,item in pairs(Tbl) do + if item.name then + Number2Pos[num] = item + -- data not older than 5 real days + elseif item.time and (item.time + (72*5)) > day_cnt then + Number2Pos[num] = item + else + minetest.log("info", "Position deleted", num) + end + end + minetest.log("info", "[TechAge] Data maintenance finished") +end + +generate_Key2Number() + +-- maintain data after 5 seconds +-- (minetest.get_day_count() will not be valid at start time) +minetest.after(5, data_maintenance) + + diff --git a/basis/intllib.lua b/basis/intllib.lua new file mode 100644 index 0000000..6669d72 --- /dev/null +++ b/basis/intllib.lua @@ -0,0 +1,45 @@ + +-- Fallback functions for when `intllib` is not installed. +-- Code released under Unlicense . + +-- Get the latest version of this file at: +-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua + +local function format(str, ...) + local args = { ... } + local function repl(escape, open, num, close) + if escape == "" then + local replacement = tostring(args[tonumber(num)]) + if open == "" then + replacement = replacement..close + end + return replacement + else + return "@"..open..num..close + end + end + return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl)) +end + +local gettext, ngettext +if minetest.get_modpath("intllib") then + if intllib.make_gettext_pair then + -- New method using gettext. + gettext, ngettext = intllib.make_gettext_pair() + else + -- Old method using text files. + gettext = intllib.Getter() + end +end + +-- Fill in missing functions. + +gettext = gettext or function(msgid, ...) + return format(msgid, ...) +end + +ngettext = ngettext or function(msgid, msgid_plural, n, ...) + return format(n==1 and msgid or msgid_plural, ...) +end + +return gettext, ngettext diff --git a/basis/junction.lua b/basis/junction.lua new file mode 100644 index 0000000..3ea13e5 --- /dev/null +++ b/basis/junction.lua @@ -0,0 +1,79 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Junction for power distribution + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + + +local function bit(p) + return 2 ^ (p - 1) -- 1-based indexing +end + +-- Typical call: if hasbit(x, bit(3)) then ... +local function hasbit(x, p) + return x % (p + p) >= p +end + +local function setbit(x, p) + return hasbit(x, p) and x or x + p +end + +local function get_node_box(val, size, boxes) + local fixed = {{-size, -size, -size, size, size, size}} + for i = 1,6 do + if hasbit(val, bit(i)) then + for _,box in ipairs(boxes[i]) do + table.insert(fixed, box) + end + end + end + return { + type = "fixed", + fixed = fixed, + } +end + +-- 'size' is the size of the junction cube without any connection, e.g. 1/8 +-- 'boxes' is a table with 6 table elements for the 6 possible connection arms +-- 'network' is the tubelib2 instance +-- 'node' is the node definition with tiles, callback functions, and so on +function techage.register_junction(name, size, boxes, network, node) + for idx = 0,63 do + node.groups.techage_trowel = 1 + node.groups.not_in_creative_inventory = idx + node.drawtype = "nodebox" + node.node_box = get_node_box(idx, size, boxes) + node.paramtype2 = "facedir" -- important! + node.on_rotate = screwdriver.disallow -- important! + node.paramtype = "light" + node.sunlight_propagates = true + node.is_ground_content = false + node.drop = name.."0" + + minetest.register_node(name..idx, table.copy(node)) + network:add_secondary_node_names({name..idx}) + end +end + +function techage.junction_type(conn) + local val = 0 + for idx = 1,6 do + if conn[idx] then + val = setbit(val, bit(idx)) + end + end + return val +end diff --git a/basis/node_states.lua b/basis/node_states.lua new file mode 100644 index 0000000..438c530 --- /dev/null +++ b/basis/node_states.lua @@ -0,0 +1,496 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + A state model/class for TechAge nodes. + +]]-- + + +--[[ + +Node states: + + +-----------------------------------+ +------------+ + | | | | + | V V | + | +---------+ | + | | | | + | +---------| STOPPED | | + | | | | | + | button | +---------+ | + | | ^ | + repair | V | button | + | +---------+ | | button + | | |---------+ | + | | RUNNING | | + | +--------| |---------+ | + | | +---------+ | | + | | ^ | | | + | | | | | | + | V | V V | + | +---------+ +----------+ +---------+ | + | | | | | | | | + +---| DEFECT | | STANDBY/ | | FAULT |----------+ + | | | BLOCKED | | | + +---------+ +----------+ +---------+ + +Node metadata: + "tubelib_number" - string with tubelib number, like "123" + "tubelib_state" - node state, like "RUNNING" + "tubelib_item_meter" - node item/runtime counter + "tubelib_countdown" - countdown to stadby mode + "tubelib_aging" - aging counter +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + + +-- +-- TechAge machine states +-- + +techage.STOPPED = 1 -- not operational/turned off +techage.RUNNING = 2 -- in normal operation/turned on +techage.STANDBY = 3 -- nothing to do (e.g. no input items), or blocked anyhow (output jammed), + -- or node (world) not loaded +techage.FAULT = 4 -- any fault state (e.g. no fuel), which can be fixed by the player +techage.BLOCKED = 5 -- a pushing node is blocked due to a full destination inventory +techage.DEFECT = 6 -- a defect (broken), which has to be repaired by the player + +techage.StatesImg = { + "tubelib_inv_button_off.png", + "tubelib_inv_button_on.png", + "tubelib_inv_button_standby.png", + "tubelib_inv_button_error.png", + "tubelib_inv_button_warning.png", + "tubelib_inv_button_off.png", +} + +-- Return state button image for the node inventory +function techage.state_button(state) + if state and state < 7 and state > 0 then + return techage.StatesImg[state] + end + return "tubelib_inv_button_off.png" +end + +-- State string based on button states +techage.StateStrings = {"stopped", "running", "standby", "fault", "blocked", "defect"} + +-- +-- Local States +-- +local STOPPED = techage.STOPPED +local RUNNING = techage.RUNNING +local STANDBY = techage.STANDBY +local FAULT = techage.FAULT +local BLOCKED = techage.BLOCKED +local DEFECT = techage.DEFECT + + +local AGING_FACTOR = 4 -- defect random factor + +-- +-- NodeStates Class Functions +-- +techage.NodeStates = {} +local NodeStates = techage.NodeStates + +local function start_condition_fullfilled(pos, meta) + return true +end + +function NodeStates:new(attr) + local o = { + -- mandatory + cycle_time = attr.cycle_time, -- for running state + standby_ticks = attr.standby_ticks, -- for standby state + has_item_meter = attr.has_item_meter, -- true/false + -- optional + node_name_passive = attr.node_name_passive, + node_name_active = attr.node_name_active, + node_name_defect = attr.node_name_defect, + infotext_name = attr.infotext_name, + start_condition_fullfilled = attr.start_condition_fullfilled or start_condition_fullfilled, + on_start = attr.on_start, + on_stop = attr.on_stop, + formspec_func = attr.formspec_func, + } + if attr.aging_factor then + o.aging_level1 = attr.aging_factor * techage.machine_aging_value + o.aging_level2 = attr.aging_factor * techage.machine_aging_value * AGING_FACTOR + end + setmetatable(o, self) + self.__index = self + return o +end + +function NodeStates:node_init(pos, number) + local meta = M(pos) + meta:set_int("tubelib_state", STOPPED) + meta:set_string("tubelib_number", number) + if self.infotext_name then + meta:set_string("infotext", self.infotext_name.." "..number..": stopped") + end + if self.has_item_meter then + meta:set_int("tubelib_item_meter", 0) + end + if self.aging_level1 then + meta:set_int("tubelib_aging", 0) + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end +end + +function NodeStates:stop(pos, meta) + local state = meta:get_int("tubelib_state") + if state ~= DEFECT then + if self.on_stop then + self.on_stop(pos, meta, state) + end + meta:set_int("tubelib_state", STOPPED) + if self.node_name_passive then + local node = minetest.get_node(pos) + node.name = self.node_name_passive + minetest.swap_node(pos, node) + end + if self.infotext_name then + local number = meta:get_string("tubelib_number") + meta:set_string("infotext", self.infotext_name.." "..number..": stopped") + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end + minetest.get_node_timer(pos):stop() + return true + end + return false +end + +function NodeStates:start(pos, meta, called_from_on_timer) + local state = meta:get_int("tubelib_state") + if state == STOPPED or state == STANDBY or state == BLOCKED then + if not self.start_condition_fullfilled(pos, meta) then + return false + end + if self.on_start then + self.on_start(pos, meta, state) + end + meta:set_int("tubelib_state", RUNNING) + meta:set_int("tubelib_countdown", 4) + if called_from_on_timer then + -- timer has to be stopped once to be able to be restarted + self.stop_timer = true + end + if self.node_name_active then + local node = minetest.get_node(pos) + node.name = self.node_name_active + minetest.swap_node(pos, node) + end + if self.infotext_name then + local number = meta:get_string("tubelib_number") + meta:set_string("infotext", self.infotext_name.." "..number..": running") + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end + minetest.get_node_timer(pos):start(self.cycle_time) + return true + end + return false +end + +function NodeStates:standby(pos, meta) + if meta:get_int("tubelib_state") == RUNNING then + meta:set_int("tubelib_state", STANDBY) + -- timer has to be stopped once to be able to be restarted + self.stop_timer = true + if self.node_name_passive then + local node = minetest.get_node(pos) + node.name = self.node_name_passive + minetest.swap_node(pos, node) + end + if self.infotext_name then + local number = meta:get_string("tubelib_number") + meta:set_string("infotext", self.infotext_name.." "..number..": standby") + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end + minetest.get_node_timer(pos):start(self.cycle_time * self.standby_ticks) + return true + end + return false +end + +-- special case of standby for pushing nodes +function NodeStates:blocked(pos, meta) + if meta:get_int("tubelib_state") == RUNNING then + meta:set_int("tubelib_state", BLOCKED) + -- timer has to be stopped once to be able to be restarted + self.stop_timer = true + if self.node_name_passive then + local node = minetest.get_node(pos) + node.name = self.node_name_passive + minetest.swap_node(pos, node) + end + if self.infotext_name then + local number = meta:get_string("tubelib_number") + meta:set_string("infotext", self.infotext_name.." "..number..": blocked") + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end + minetest.get_node_timer(pos):start(self.cycle_time * self.standby_ticks) + return true + end + return false +end + +function NodeStates:fault(pos, meta) + if meta:get_int("tubelib_state") == RUNNING then + meta:set_int("tubelib_state", FAULT) + if self.node_name_passive then + local node = minetest.get_node(pos) + node.name = self.node_name_passive + minetest.swap_node(pos, node) + end + if self.infotext_name then + local number = meta:get_string("tubelib_number") + meta:set_string("infotext", self.infotext_name.." "..number..": fault") + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end + minetest.get_node_timer(pos):stop() + return true + end + return false +end + +function NodeStates:defect(pos, meta) + meta:set_int("tubelib_state", DEFECT) + if self.node_name_defect then + local node = minetest.get_node(pos) + node.name = self.node_name_defect + minetest.swap_node(pos, node) + end + if self.infotext_name then + local number = meta:get_string("tubelib_number") + meta:set_string("infotext", self.infotext_name.." "..number..": defect") + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end + minetest.get_node_timer(pos):stop() + return true +end + +function NodeStates:get_state(meta) + return meta:get_int("tubelib_state") +end + +function NodeStates:get_state_string(meta) + return techage.StateStrings[meta:get_int("tubelib_state")] +end + +function NodeStates:is_active(meta) + local state = meta:get_int("tubelib_state") + if self.stop_timer == true then + self.stop_timer = false + return false + end + return state == RUNNING or state == STANDBY or state == BLOCKED +end + +-- To be called if node is idle. +-- If countdown reaches zero, the node is set to STANDBY. +function NodeStates:idle(pos, meta) + local countdown = meta:get_int("tubelib_countdown") - 1 + meta:set_int("tubelib_countdown", countdown) + if countdown < 0 then + self:standby(pos, meta) + end +end + +-- To be called after successful node action to raise the timer +-- and keep the node in state RUNNING +function NodeStates:keep_running(pos, meta, val, num_items) + num_items = num_items or 1 + -- set to RUNNING if not already done + self:start(pos, meta, true) + meta:set_int("tubelib_countdown", val) + meta:set_int("tubelib_item_meter", meta:get_int("tubelib_item_meter") + (num_items or 1)) + if self.aging_level1 then + local cnt = meta:get_int("tubelib_aging") + num_items + meta:set_int("tubelib_aging", cnt) + if (cnt > (self.aging_level1) and math.random(self.aging_level2/num_items) == 1) + or cnt >= 999999 then + self:defect(pos, meta) + end + end +end + +-- Start/stop node based on button events. +-- if function returns false, no button was pressed +function NodeStates:state_button_event(pos, fields) + if fields.state_button ~= nil then + local state = self:get_state(M(pos)) + if state == STOPPED or state == STANDBY or state == BLOCKED then + self:start(pos, M(pos)) + elseif state == RUNNING or state == FAULT then + self:stop(pos, M(pos)) + end + return true + end + return false +end + +function NodeStates:get_state_button_image(meta) + local state = meta:get_int("tubelib_state") + return techage.state_button(state) +end + +-- command interface +function NodeStates:on_receive_message(pos, topic, payload) + if topic == "on" then + self:start(pos, M(pos)) + return true + elseif topic == "off" then + self:stop(pos, M(pos)) + return true + elseif topic == "state" then + local node = minetest.get_node(pos) + if node.name == "ignore" then -- unloaded node? + return "blocked" + end + return self:get_state_string(M(pos)) + elseif self.has_item_meter and topic == "counter" then + return M(pos):get_int("tubelib_item_meter") + elseif self.has_item_meter and topic == "clear_counter" then + M(pos):set_int("tubelib_item_meter", 0) + return true + elseif self.aging_level1 and topic == "aging" then + return M(pos):get_int("tubelib_aging") + end +end + +-- repair corrupt node data and/or migrate node to state2 +function NodeStates:on_node_load(pos, not_start_timer) + local meta = minetest.get_meta(pos) + + -- legacy node number/state/counter? + local number = meta:get_string("number") + if number ~= "" and number ~= nil then + meta:set_string("tubelib_number", number) + meta:set_int("tubelib_state", techage.state(meta:get_int("running"))) + if self.has_item_meter then + meta:set_int("tubelib_item_meter", meta:get_int("counter")) + end + if self.aging_level1 then + meta:set_int("tubelib_aging", 0) + end + meta:set_string("number", nil) + meta:set_int("running", 0) + meta:set_int("counter", 0) + end + + -- node number corrupt? + number = meta:get_string("tubelib_number") + if number == "" then + number = techage.get_new_number(pos, self.node_name_passive) + meta:set_string("tubelib_number", number) + else + local info = techage.get_node_info(number) + if not info or info.pos ~= pos then + number = techage.get_new_number(pos, self.node_name_passive) + meta:set_string("tubelib_number", number) + end + end + + -- state corrupt? + local state = meta:get_int("tubelib_state") + if state == 0 then + if minetest.get_node_timer(pos):is_started() then + meta:set_int("tubelib_state", RUNNING) + else + meta:set_int("tubelib_state", STOPPED) + end + elseif state == RUNNING and not not_start_timer then + minetest.get_node_timer(pos):start(self.cycle_time) + elseif state == STANDBY then + minetest.get_node_timer(pos):start(self.cycle_time * self.standby_ticks) + elseif state == BLOCKED then + minetest.get_node_timer(pos):start(self.cycle_time * self.standby_ticks) + end + + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end +end + +-- Repair of defect (feature!) nodes +function NodeStates:on_node_repair(pos) + local meta = M(pos) + if meta:get_int("tubelib_state") == DEFECT then + meta:set_int("tubelib_state", STOPPED) + if self.node_name_passive then + local node = minetest.get_node(pos) + node.name = self.node_name_passive + minetest.swap_node(pos, node) + end + if self.aging_level1 then + meta:set_int("tubelib_aging", 0) + end + if self.infotext_name then + local number = meta:get_string("tubelib_number") + meta:set_string("infotext", self.infotext_name.." "..number..": stopped") + end + if self.formspec_func then + meta:set_string("formspec", self.formspec_func(self, pos, meta)) + end + return true + end + return false +end + +-- Return working or defect machine, depending on machine lifetime +function NodeStates:after_dig_node(pos, oldnode, oldmetadata, digger) + local inv = minetest.get_inventory({type="player", name=digger:get_player_name()}) + local cnt = oldmetadata.fields.tubelib_aging and tonumber(oldmetadata.fields.tubelib_aging) or 0 + local is_defect = cnt > self.aging_level1 and math.random(self.aging_level2 / cnt) == 1 + if self.node_name_defect and is_defect then + inv:add_item("main", ItemStack(self.node_name_defect)) + else + inv:add_item("main", ItemStack(self.node_name_passive)) + end +end + +-- Return "full", "loaded", or "empty" depending +-- on the number of fuel stack items. +-- Function only works on fuel inventories with one stacks/99 items +function techage.fuelstate(meta, listname, item) + if meta == nil or meta.get_inventory == nil then return nil end + local inv = meta:get_inventory() + if inv:is_empty(listname) then + return "empty" + end + local list = inv:get_list(listname) + if #list == 1 and list[1]:get_count() == 99 then + return "full" + else + return "loaded" + end +end + diff --git a/basis/power.lua b/basis/power.lua new file mode 100644 index 0000000..fb2ed7c --- /dev/null +++ b/basis/power.lua @@ -0,0 +1,244 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Power consumption for any kind of power distribution network + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta +local TP = function(pos) return minetest.registered_nodes[minetest.get_node(pos).name].techage end +local TN = function(node) return minetest.registered_nodes[node.name].techage end + + +-- Table to register the different power distribution network instances for global use +techage.Networks = {} + +-- Used to determine the already passed nodes while power distribution +local Route = {} + +local function pos_already_reached(pos) + local key = minetest.hash_node_position(pos) + if not Route[key] then + Route[key] = true + return false + end + return true +end + +local DirToSide = {"B", "R", "F", "L", "D", "U"} + +local function dir_to_side(pos, dir) + local node = minetest.get_node(pos) + if dir < 5 then + dir = (((dir - 1) - (node.param2 % 4)) % 4) + 1 + end + return DirToSide[dir] +end + +local SideToDir = {B=1, R=2, F=3, L=4, D=5, U=6} + +local function side_to_dir(pos, side) + local node = minetest.get_node(pos) + local dir = SideToDir[side] + if dir < 5 then + dir = (((dir - 1) + (node.param2 % 4)) % 4) + 1 + end + return dir +end + +-- Calculate the power consumption on the given network +local function power_consumption(pos, dir) + if pos_already_reached(pos) then return 0 end + local mem = tubelib2.get_mem(pos) + local conn = mem.connections or {} + local val = 0 + for fdir,fpos in pairs(conn) do + if fdir ~= tubelib2.Turn180Deg[dir or 0] then + local this = TP(fpos) + if this and this.power_consumption then + val = val + this.power_consumption(fpos, fdir) + else + val = val + power_consumption(fpos, fdir) + end + end + end + return val +end + +local function turn_tube_on(pos, dir, network, on) + if network.switch_tube_line then + if on then + network:switch_tube_line(pos, dir, "on") + else + network:switch_tube_line(pos, dir, "off") + end + end +end + +local function turn_on(pos, dir, on) + if pos_already_reached(pos) then return end + local mem = tubelib2.get_mem(pos) + local conn = mem.connections or {} + for fdir,fpos in pairs(conn) do + if fdir ~= tubelib2.Turn180Deg[dir or 0] then + local this = TP(fpos) + if this and this.turn_on then + this.turn_on(fpos, fdir, on) + end + if this and this.network then + turn_tube_on(pos, fdir, this.network, on) + end + turn_on(fpos, fdir, on) + end + end +end + +-- power source: power > 0 +-- power sink: power < 0 +-- switched off: power = 0 +local function sink_power_consumption(pos, power) + Route = {} + local sum = power + power_consumption(pos) + Route = {} + turn_on(pos, nil, sum > 0) + return sum +end + +-- To be called from any generator +local function source_power_consumption(pos, mem) + local power = mem.power_produce or 0 + mem.power_result = sink_power_consumption(pos, power) + return mem.power_result > 0 +end + +techage.sink_power_consumption = sink_power_consumption +techage.source_power_consumption = source_power_consumption + + +-- +-- Generator with on power output side +-- +function techage.generator_on(pos, power, network) + local mem = tubelib2.get_mem(pos) + mem.power_produce = power + return source_power_consumption(pos, mem) +end + +function techage.generator_off(pos, network) + local mem = tubelib2.get_mem(pos) + mem.power_produce = 0 + return source_power_consumption(pos, mem) +end + +function techage.generator_power_consumption(pos, dir) + local mem = tubelib2.get_mem(pos) + if dir == tubelib2.Turn180Deg[mem.power_dir or 0] then + return mem.power_produce + end + return 0 +end + +function techage.generator_after_place_node(pos) + local mem = tubelib2.init_mem(pos) + mem.power_dir = side_to_dir(pos, TP(pos).side or 'R') + mem.power_produce = 0 -- will be set via generator_on + mem.power_result = 0 + local network = TP(pos).network + network:after_place_node(pos) +end + +function techage.generator_after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) + -- check if contact side is correct + local mem = tubelib2.get_mem(pos) + if out_dir == mem.power_dir then + -- store connection for 'source_power_consumption()' + mem.connections = {[out_dir] = peer_pos} + source_power_consumption(pos, mem) + end +end + +function techage.generator_on_destruct(pos) + techage.generator_off(pos) +end + +function techage.generator_after_dig_node(pos, oldnode, oldmetadata, digger) + TN(oldnode).network:after_dig_node(pos) + tubelib2.del_mem(pos) +end + +function techage.generator_formspec_level(mem) + print("generator_formspec_level", mem.power_result, mem.power_produce) + local percent = ((mem.power_result or 0) * 100) / (mem.power_produce or 1) + return "techage_form_level_bg.png^[lowpart:"..percent..":techage_form_level_fg.png]" +end + + +-- +-- Distributor with 6 power input/output sides +-- +function techage.distributor_power_consumption(pos, dir) + return power_consumption(pos, dir) - TP(pos).power_consume +end + +function techage.distributor_after_place_node(pos, placer) + local this = TP(pos) + this.network:after_place_node(pos) + sink_power_consumption(pos, -this.power_consume) +end + +function techage.distributor_after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) + local mem = tubelib2.get_mem(pos) + mem.connections = mem.connections or {} + mem.connections[out_dir] = peer_pos + local sum = sink_power_consumption(pos, -TP(pos).power_consume) +end + +function techage.distributor_on_destruct(pos) + sink_power_consumption(pos, -TP(pos).power_consume) +end + +function techage.distributor_after_dig_node(pos, oldnode, oldmetadata, digger) + TN(oldnode).network:after_dig_node(pos) + tubelib2.del_mem(pos) +end + +-- +-- Consumer with on power input side (default) +-- +function techage.consumer_power_consumption(pos) + return -TP(pos).power_consume +end + +function techage.consumer_after_place_node(pos, placer) + local mem = tubelib2.init_mem(pos) + mem.power_dir = tubelib2.Turn180Deg[side_to_dir(pos, TP(pos).side or 'L')] + local this = TP(pos) + this.network:after_place_node(pos) + sink_power_consumption(pos, -this.power_consume) +end + +function techage.consumer_after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) + local mem = tubelib2.get_mem(pos) + mem.connections = mem.connections or {} + mem.connections[out_dir] = peer_pos + sink_power_consumption(pos, -TP(pos).power_consume) +end + +function techage.consumer_on_destruct(pos) + sink_power_consumption(pos, -TP(pos).power_consume) +end + +function techage.consumer_after_dig_node(pos, oldnode, oldmetadata, digger) + TN(oldnode).network:after_dig_node(pos) + tubelib2.del_mem(pos) +end diff --git a/basis/trowel.lua b/basis/trowel.lua new file mode 100644 index 0000000..660285c --- /dev/null +++ b/basis/trowel.lua @@ -0,0 +1,112 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Trowel tool to hide/open cable/pipe/tube nodes + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +-- Overridden method of tubelib2! +function techage.get_primary_node_param2(pos, dir) + local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0]) + local param2 = M(npos):get_int("tl2_param2") + if param2 ~= 0 then + return param2, npos + end +end + +-- Overridden method of tubelib2! +function techage.is_primary_node(pos, dir) + local npos = vector.add(pos, tubelib2.Dir6dToVector[dir or 0]) + local param2 = M(npos):get_int("tl2_param2") + return param2 ~= 0 +end + +-- Determine if one node in the surrounding is a hidden tube/cable/pipe +local function other_hidden_nodes(pos, node_name) + return M({x=pos.x+1, y=pos.y, z=pos.z}):get_string(node_name) ~= "" or + M({x=pos.x-1, y=pos.y, z=pos.z}):get_string(node_name) ~= "" or + M({x=pos.x, y=pos.y+1, z=pos.z}):get_string(node_name) ~= "" or + M({x=pos.x, y=pos.y-1, z=pos.z}):get_string(node_name) ~= "" or + M({x=pos.x, y=pos.y, z=pos.z+1}):get_string(node_name) ~= "" or + M({x=pos.x, y=pos.y, z=pos.z-1}):get_string(node_name) ~= "" +end + +local function hide_node(pos, node, meta, placer) + local inv = placer:get_inventory() + local stack = inv:get_stack("main", 1) + local taken = stack:take_item(1) + -- test if it is a simple node without logic + if taken:get_count() == 1 + and minetest.registered_nodes[taken:get_name()] + and not minetest.registered_nodes[taken:get_name()].after_place_node + and not minetest.registered_nodes[taken:get_name()].on_construct then + meta:set_string("techage_hidden_nodename", node.name) + meta:set_string("techage_hidden_param2", node.param2) + local param2 = minetest.dir_to_facedir(placer:get_look_dir(), true) + minetest.swap_node(pos, {name = taken:get_name(), param2 = param2}) + inv:set_stack("main", 1, stack) + end +end + +local function open_node(pos, node, meta, placer) + local name = meta:get_string("techage_hidden_nodename") + local param2 = meta:get_string("techage_hidden_param2") + minetest.swap_node(pos, {name = name, param2 = param2}) + meta:set_string("techage_hidden_nodename", "") + meta:set_string("techage_hidden_param2", "") + local inv = placer:get_inventory() + inv:add_item("main", ItemStack(node.name)) +end + +-- Hide or open a node +local function replace_node(itemstack, placer, pointed_thing) + if pointed_thing.type == "node" then + local pos = pointed_thing.under + local meta = M(pos) + local node = minetest.get_node(pos) + if minetest.get_item_group(node.name, "techage_trowel") == 1 then + hide_node(pos, node, meta, placer) + elseif meta:get_string("techage_hidden_nodename") ~= "" then + open_node(pos, node, meta, placer) + end + end +end + +minetest.register_node("techage:trowel", { + description = I("TechAge Trowel (uses items from the first, left inventory stack)"), + inventory_image = "techage_trowel.png", + wield_image = "techage_trowel.png", + use_texture_alpha = true, + groups = {cracky=1}, + on_use = replace_node, + on_place = replace_node, + node_placement_prediction = "", + stack_max = 1, +}) + +minetest.register_on_dignode(function(pos, oldnode, digger) + -- If hidden nodes are arround, the removed one was probably + -- a hidden node, too. + if other_hidden_nodes(pos, "techage_hidden_nodename") then + -- test both hidden networks + techage.ElectricCable:after_dig_node("techage_hidden_nodename") + techage.BiogasPipe:after_dig_node("techage_hidden_nodename") + end +end) diff --git a/basis/tubes.lua b/basis/tubes.lua new file mode 100644 index 0000000..11c44ea --- /dev/null +++ b/basis/tubes.lua @@ -0,0 +1,132 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Tubes based on tubelib2 + +]]-- + + + +-- used for registered nodes +techage.KnownNodes = { + ["techage:tubeS"] = true, + ["techage:tubeA"] = true, +} + + +local Tube = tubelib2.Tube:new({ + -- North, East, South, West, Down, Up + dirs_to_check = {1,2,3,4,5,6}, + max_tube_length = 200, + show_infotext = false, + primary_node_names = {"techage:tubeS", "techage:tubeA"}, + after_place_tube = function(pos, param2, tube_type, num_tubes, tbl) + minetest.swap_node(pos, {name = "techage:tube"..tube_type, param2 = param2}) + end, +}) + +techage.Tube = Tube + +minetest.register_node("techage:tubeS", { + description = "TechAge Tube", + tiles = { -- Top, base, right, left, front, back + "techage_tube.png^[transformR90", + "techage_tube.png^[transformR90", + "techage_tube.png", + "techage_tube.png", + "techage_hole.png", + "techage_hole.png", + }, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + if not Tube:after_place_tube(pos, placer, pointed_thing) then + minetest.remove_node(pos) + return true + end + return false + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Tube:after_dig_tube(pos, oldnode, oldmetadata) + end, + + paramtype2 = "facedir", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-2/8, -2/8, -4/8, 2/8, 2/8, 4/8}, + }, + }, + selection_box = { + type = "fixed", + fixed = { -1/4, -1/4, -1/2, 1/4, 1/4, 1/2 }, + }, + collision_box = { + type = "fixed", + fixed = { -1/4, -1/4, -1/2, 1/4, 1/4, 1/2 }, + }, + on_rotate = screwdriver.disallow, + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {choppy=2, cracky=3, stone=1}, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_node("techage:tubeA", { + description = "TechAge Tube", + tiles = { -- Top, base, right, left, front, back + "techage_knee2.png", + "techage_hole2.png^[transformR180", + "techage_knee.png^[transformR270", + "techage_knee.png", + "techage_knee2.png", + "techage_hole2.png", + }, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Tube:after_dig_tube(pos, oldnode, oldmetadata) + end, + + paramtype2 = "facedir", + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-2/8, -4/8, -2/8, 2/8, 2/8, 2/8}, + {-2/8, -2/8, -4/8, 2/8, 2/8, -2/8}, + }, + }, + selection_box = { + type = "fixed", + fixed = { -1/4, -1/2, -1/2, 1/4, 1/4, 1/4 }, + }, + collision_box = { + type = "fixed", + fixed = { -1/4, -1/2, -1/2, 1/4, 1/4, 1/4 }, + }, + on_rotate = screwdriver.disallow, + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {choppy=2, cracky=3, stone=1, not_in_creative_inventory=1}, + sounds = default.node_sound_wood_defaults(), + drop = "techage:tubeS", +}) + +minetest.register_craft({ + output = "techage:tubeS 4", + recipe = { + {"default:steel_ingot", "", "group:wood"}, + {"", "group:wood", ""}, + {"group:wood", "", "default:tin_ingot"}, + }, +}) diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..6bd4325 --- /dev/null +++ b/depends.txt @@ -0,0 +1,4 @@ +default +tubelib2 +basic_materials + diff --git a/electric/electric_cable.lua b/electric/electric_cable.lua new file mode 100644 index 0000000..1ea61b1 --- /dev/null +++ b/electric/electric_cable.lua @@ -0,0 +1,176 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Cable and junction box for electrical power distribution + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +local Cable = tubelib2.Tube:new({ + dirs_to_check = {1,2,3,4,5,6}, + max_tube_length = 1000, + show_infotext = false, + primary_node_names = {"techage:electric_cableS", "techage:electric_cableA"}, + secondary_node_names = {"techage:lamp", "techage:lamp_on", "techage:power"}, + after_place_tube = function(pos, param2, tube_type, num_tubes) + minetest.swap_node(pos, {name = "techage:electric_cable"..tube_type, param2 = param2 % 32}) + M(pos):set_int("tl2_param2", param2) + end, +}) + +techage.ElectricCable = Cable + + +-- Overridden method of tubelib2! +function Cable:get_primary_node_param2(pos, dir) + return techage.get_primary_node_param2(pos, dir) +end + +function Cable:is_primary_node(pos, dir) + return techage.is_primary_node(pos, dir) +end + +Cable:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir) + minetest.registered_nodes[node.name].after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) +end) + + +minetest.register_node("techage:electric_cableS", { + description = I("TA4 Electric Cable"), + tiles = { + -- up, down, right, left, back, front + "techage_electric_cable.png", + "techage_electric_cable.png", + "techage_electric_cable.png", + "techage_electric_cable.png", + "techage_electric_cable_end.png", + "techage_electric_cable_end.png", + }, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + if not Cable:after_place_tube(pos, placer, pointed_thing) then + minetest.remove_node(pos) + return true + end + return false + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + if oldmetadata and oldmetadata.fields and oldmetadata.fields.tl2_param2 then + oldnode.param2 = oldmetadata.fields.tl2_param2 + Cable:after_dig_tube(pos, oldnode) + end + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-3/32, -3/32, -4/8, 3/32, 3/32, 4/8}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, techage_trowel = 1}, + sounds = default.node_sound_defaults(), +}) + +minetest.register_node("techage:electric_cableA", { + description = I("TA4 Electric Cable"), + tiles = { + -- up, down, right, left, back, front + "techage_electric_cable.png", + "techage_electric_cable_end.png", + "techage_electric_cable.png", + "techage_electric_cable.png", + "techage_electric_cable.png", + "techage_electric_cable_end.png", + }, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + if oldmetadata and oldmetadata.fields and oldmetadata.fields.tl2_param2 then + oldnode.param2 = oldmetadata.fields.tl2_param2 + Cable:after_dig_tube(pos, oldnode) + end + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-3/32, -4/8, -3/32, 3/32, 3/32, 3/32}, + {-3/32, -3/32, -4/8, 3/32, 3/32, -3/32}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, techage_trowel = 1}, + sounds = default.node_sound_defaults(), + drop = "techage:electric_cableS", +}) + + + + +local size = 3/32 +local Boxes = { + {{-size, -size, size, size, size, 0.5 }}, -- z+ + {{-size, -size, -size, 0.5, size, size}}, -- x+ + {{-size, -size, -0.5, size, size, size}}, -- z- + {{-0.5, -size, -size, size, size, size}}, -- x- + {{-size, -0.5, -size, size, size, size}}, -- y- + {{-size, -size, -size, size, 0.5, size}}, -- y+ +} + +techage.register_junction("techage:electric_junction", 2/8, Boxes, Cable, { + description = "Electricity Junction Box", + tiles = {"techage_electric_junction.png"}, + groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, techage_trowel = 1}, + sounds = default.node_sound_defaults(), + + after_place_node = function(pos, placer, itemstack, pointed_thing) + tubelib2.init_mem(pos) + Cable:after_place_node(pos) + techage.sink_power_consumption(pos, 0) + end, + + after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir) + local mem = tubelib2.get_mem(pos) + mem.connections = mem.connections or {} + mem.connections[out_dir] = peer_pos + local name = "techage:electric_junction"..techage.junction_type(mem.connections) + minetest.swap_node(pos, {name = name, param2 = 0}) + techage.sink_power_consumption(pos, 0) + end, + + on_destruct = function(pos) + techage.sink_power_consumption(pos, 0) + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Cable:after_dig_node(pos) + end, +}) + diff --git a/electric/test.lua b/electric/test.lua new file mode 100644 index 0000000..74db740 --- /dev/null +++ b/electric/test.lua @@ -0,0 +1,128 @@ +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +local POWER_CONSUME = 1 + + +local Cable = techage.ElectricCable + +local function swap_node(pos, name) + local node = minetest.get_node(pos) + if node.name == name then + return + end + node.name = name + minetest.swap_node(pos, node) +end + +local function lamp_turn_on(pos, dir, on) + local mem = tubelib2.get_mem(pos) + if mem.power_dir == dir or mem.power_dir == tubelib2.Turn180Deg[dir] then + if on then + swap_node(pos, "techage:lamp_on") + else + swap_node(pos, "techage:lamp") + end + end +end + +minetest.register_node("techage:lamp", { + description = "TechAge Lamp", + tiles = { + -- up, down, right, left, back, front + 'techage_electric_button.png', + 'techage_electric_button.png', + 'techage_electric_button.png', + 'techage_electric_button.png', + 'techage_electric_button.png^techage_electric_plug.png', + 'techage_electric_button.png^techage_electric_plug.png', + }, + techage = { + turn_on = lamp_turn_on, + power_consumption = techage.consumer_power_consumption, + network = techage.ElectricCable, + power_consume = POWER_CONSUME, + side = 'B', + }, + + after_place_node = techage.consumer_after_place_node, + after_tube_update = techage.consumer_after_tube_update, + on_destruct = techage.consumer_on_destruct, + after_dig_node = techage.consumer_after_dig_node, + + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {choppy=2, cracky=2, crumbly=2}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_node("techage:lamp_on", { + description = "TechAge Lamp", + tiles = { + 'techage_electric_button.png', + }, + techage = { + turn_on = lamp_turn_on, + power_consumption = techage.consumer_power_consumption, + network = techage.ElectricCable, + power_consume = POWER_CONSUME, + }, + + after_place_node = techage.consumer_after_place_node, + after_tube_update = techage.consumer_after_tube_update, + on_destruct = techage.consumer_on_destruct, + after_dig_node = techage.consumer_after_dig_node, + + paramtype = "light", + light_source = LIGHT_MAX, + sunlight_propagates = true, + paramtype2 = "facedir", + drop = "techage:lamp", + groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + + +minetest.register_node("techage:power", { + description = "TechAge Power", + tiles = { + -- up, down, right, left, back, front + 'techage_electric_button.png^techage_electric_power.png', + 'techage_electric_button.png^techage_electric_power.png', + 'techage_electric_button.png^techage_electric_power.png^techage_electric_plug.png', + 'techage_electric_button.png^techage_electric_power.png', + 'techage_electric_button.png^techage_electric_power.png', + 'techage_electric_button.png^techage_electric_power.png', + }, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + on_rotate = screwdriver.disallow, + is_ground_content = false, + + techage = { + network = Cable, + power_consumption = techage.generator_power_consumption, + }, + + after_place_node = techage.generator_after_place_node, + after_tube_update = techage.generator_after_tube_update, + on_destruct = techage.generator_on_destruct, + after_dig_node = techage.generator_after_dig_node, + + on_rightclick = function(pos, node, clicker) + local mem = tubelib2.get_mem(pos) + print("on_rightclick", mem.power) + if mem.power_produce and mem.power_produce > 0 then + techage.generator_off(pos) + else + techage.generator_on(pos, 8) + end + end, +}) + diff --git a/fermenter/biogas_pipe.lua b/fermenter/biogas_pipe.lua new file mode 100644 index 0000000..e9436f4 --- /dev/null +++ b/fermenter/biogas_pipe.lua @@ -0,0 +1,205 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Biogas pipes + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +local Pipe = tubelib2.Tube:new({ + dirs_to_check = {1,2,3,4,5,6}, + max_tube_length = 1000, + show_infotext = false, + primary_node_names = {"techage:biogas_pipeS", "techage:biogas_pipeA"}, + secondary_node_names = {"techage:gasflare", "techage:compressor"}, + after_place_tube = function(pos, param2, tube_type, num_tubes, tbl) + minetest.swap_node(pos, {name = "techage:biogas_pipe"..tube_type, param2 = param2}) + M(pos):set_int("tl2_param2", param2) + end, +}) + +Pipe:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir) + minetest.registered_nodes[node.name].after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) +end) + +techage.BiogasPipe = Pipe + + +-- Overridden method of tubelib2! +function Pipe:get_primary_node_param2(pos, dir) + return techage.get_primary_node_param2(pos, dir) +end + +function Pipe:is_primary_node(pos, dir) + return techage.is_primary_node(pos, dir) +end + +Pipe:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir) + local clbk = minetest.registered_nodes[node.name].after_tube_update + if clbk then + clbk(node, pos, out_dir, peer_pos, peer_in_dir) + else + techage.after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) + end +end) + + + +minetest.register_node("techage:biogas_pipeS", { + description = I("TA3 Biogas Pipe"), + tiles = { + "techage_gaspipe.png^[transformR90", + "techage_gaspipe.png^[transformR90", + "techage_gaspipe.png", + "techage_gaspipe.png", + "techage_gaspipe_hole2.png", + "techage_gaspipe_hole2.png", + }, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + if not Pipe:after_place_tube(pos, placer, pointed_thing) then + minetest.remove_node(pos) + return true + end + return false + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + if oldmetadata and oldmetadata.fields and oldmetadata.fields.tl2_param2 then + oldnode.param2 = oldmetadata.fields.tl2_param2 + Pipe:after_dig_tube(pos, oldnode) + end + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-1/8, -1/8, -4/8, 1/8, 1/8, 4/8}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {crumbly = 3, cracky = 3, snappy = 3, techage_trowel = 1}, + sounds = default.node_sound_glass_defaults(), +}) + +minetest.register_node("techage:biogas_pipeA", { + description = I("TA3 Biogas Pipe"), + tiles = { + "techage_gaspipe_knee2.png", + "techage_gaspipe_hole2.png^[transformR180", + "techage_gaspipe_knee.png^[transformR270", + "techage_gaspipe_knee.png", + "techage_gaspipe_knee2.png", + "techage_gaspipe_hole2.png", + }, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + if oldmetadata and oldmetadata.fields and oldmetadata.fields.tl2_param2 then + oldnode.param2 = oldmetadata.fields.tl2_param2 + Pipe:after_dig_tube(pos, oldnode) + end + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-1/8, -4/8, -1/8, 1/8, 1/8, 1/8}, + {-2/8, -0.5, -2/8, 2/8, -13/32, 2/8}, + {-1/8, -1/8, -4/8, 1/8, 1/8, -1/8}, + {-2/8, -2/8, -0.5, 2/8, 2/8, -13/32}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {crumbly = 3, cracky = 3, snappy = 3, techage_trowel = 1, not_in_creative_inventory=1}, + sounds = default.node_sound_glass_defaults(), + drop = "techage:biogas_pipeS", +}) + + +local size1 = 1/8 +local size2 = 2/8 +local size3 = 13/32 +local Boxes = { + { + {-size1, -size1, size1, size1, size1, 0.5 }, -- z+ + {-size2, -size2, size3, size2, size2, 0.5 }, -- z+ + }, + { + {-size1, -size1, -size1, 0.5, size1, size1}, -- x+ + { size3, -size2, -size2, 0.5, size2, size2}, -- x+ + }, + { + {-size1, -size1, -0.5, size1, size1, size1}, -- z- + {-size2, -size2, -0.5, size2, size2, -size3}, -- z- + }, + { + {-0.5, -size1, -size1, size1, size1, size1}, -- x- + {-0.5, -size2, -size2, -size3, size2, size2}, -- x- + }, + { + {-size1, -0.5, -size1, size1, size1, size1}, -- y- + {-size2, -0.5, -size2, size2, -size3, size2}, -- y- + }, + { + {-size1, -size1, -size1, size1, 0.5, size1}, -- y+ + {-size2, size3, -size2, size2, 0.5, size2}, -- y+ + } +} + +techage.register_junction("techage:biogas_junction", 1/8, Boxes, Pipe, { + description = "TA3 Biogas Junction", + tiles = {"techage_gaspipe_junction.png"}, + groups = {crumbly = 3, cracky = 3, snappy = 3, techage_trowel = 1}, + sounds = default.node_sound_metal_defaults(), + + after_place_node = function(pos, placer, itemstack, pointed_thing) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Position "..S(pos)) + Pipe:after_place_node(pos) + techage.sink_power_consumption(pos, 0) + end, + + after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir) + local conn = minetest.deserialize(M(pos):get_string("connections")) or {} + conn[out_dir] = peer_pos + M(pos):set_string("connections", minetest.serialize(conn)) + local name = "techage:biogas_junction"..techage.junction_type(conn) + minetest.swap_node(pos, {name = name, param2 = 0}) + techage.sink_power_consumption(pos, 0) + end, + + on_destruct = function(pos) + techage.sink_power_consumption(pos, 0) + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Pipe:after_dig_node(pos) + end, +}) + diff --git a/fermenter/gasflare.lua b/fermenter/gasflare.lua new file mode 100644 index 0000000..0ff7714 --- /dev/null +++ b/fermenter/gasflare.lua @@ -0,0 +1,182 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + Biogas flare + +]]-- + + +local HEIGHT = 7 + +local function remove_flame(pos) + local idx + for idx=HEIGHT,1,-1 do + pos = {x=pos.x, y=pos.y+1, z=pos.z} + local node = minetest.get_node(pos) + if string.find(node.name, "techage:flame") then + minetest.remove_node(pos) + end + end +end + +local function flame(pos) + local idx + for idx=HEIGHT,1,-1 do + pos = {x=pos.x, y=pos.y+1, z=pos.z} + idx = math.min(idx, 12) + local node = minetest.get_node(pos) + if node.name ~= "air" then + return + end + minetest.add_node(pos, {name = "techage:flame"..math.min(idx,7)}) + local meta = minetest.get_meta(pos) + end +end + + +local lRatio = {120, 110, 95, 75, 55, 28, 0} +local lColor = {"000080", "400040", "800000", "800000", "800000", "800000", "800000"} +for idx,ratio in ipairs(lRatio) do + local color = "techage_flame_animated.png^[colorize:#"..lColor[idx].."B0:"..ratio + minetest.register_node("techage:flame"..idx, { + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-3/8, -4/8, -2/8, 3/8, 4/8, 2/8}, + {-2/8, -4/8, -3/8, 2/8, 4/8, 3/8}, + }, + }, + tiles = { + { + name = color, + animation = { + type = "vertical_frames", + aspect_w = 16, + aspect_h = 16, + length = 1 + }, + }, + }, + + after_destruct = function(pos, oldnode) + pos.y = pos.y + 1 + local node = minetest.get_node(pos) + if minetest.get_item_group(node.name, "techage_flame") > 0 then + minetest.remove_node(pos) + end + end, + + use_texture_alpha = true, + inventory_image = "techage_flame.png", + paramtype = "light", + light_source = 13, + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drop = "", + drowning = 1, + damage_per_second = 4 + idx, + groups = {igniter = 2, dig_immediate = 3, techage_flame=1, not_in_creative_inventory=1}, + drop = "", + }) +end + +local function start_flarestack(pos, playername) + if minetest.is_protected( + {x=pos.x, y=pos.y+1, z=pos.z}, + playername) then + return + end + local meta = minetest.get_meta(pos) + flame({x=pos.x, y=pos.y+1, z=pos.z}) + local handle = minetest.sound_play("gasflare", { + pos = pos, + max_hear_distance = 20, + gain = 1, + loop = true}) + print("handle", handle) + meta:set_int("handle", handle) +end + +local function stop_flarestack(pos, handle) + remove_flame({x=pos.x, y=pos.y+1, z=pos.z}) + minetest.sound_stop(handle) +end + +minetest.register_node("techage:gasflare", { + description = "gas flare", + tiles = { + "techage_gasflare.png", + "techage_gasflare.png", + "techage_gasflare.png", + "techage_gasflare.png", + "techage_gasflare.png", + "techage_gasflare.png^techage_appl_hole2.png", + }, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + local node = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}) + if node.name ~= "air" then + return + end + minetest.add_node({x=pos.x, y=pos.y+1, z=pos.z}, {name = "techage:gasflare2"}) + end, + + on_punch = function(pos, node, puncher) + local meta = minetest.get_meta(pos) + local handle = meta:get_int("handle") + minetest.sound_stop(handle) + start_flarestack(pos, puncher:get_player_name()) + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + print(dump(oldmetadata)) + stop_flarestack(pos, oldmetadata.fields.handle) + local node = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}) + if node.name == "techage:gasflare2" then + minetest.remove_node({x=pos.x, y=pos.y+1, z=pos.z}) + end + end, + + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:gasflare2", { + description = "", + tiles = { + "techage_gasflare.png^techage_appl_hole2.png", + "techage_gasflare.png" + }, + + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-1/8, -4/8, -1/8, 1/8, 4/8, 1/8}, + {-4/8, 3/8, -4/8, 4/8, 4/8, 4/8}, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + diggable = false, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), +}) diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..f0b7e53 --- /dev/null +++ b/init.lua @@ -0,0 +1,38 @@ +techage = { + NodeDef = {}, -- node registration info +} + + +techage.max_num_forceload_blocks = tonumber(minetest.setting_get("techage_max_num_forceload_blocks")) or 12 +techage.basalt_stone_enabled = minetest.setting_get("techage_basalt_stone_enabled") == "true" +techage.machine_aging_value = tonumber(minetest.setting_get("techage_machine_aging_value")) or 100 + + +local MP = minetest.get_modpath("techage") + +-- Load support for intllib. +dofile(MP.."/basis/intllib.lua") + +dofile(MP.."/basis/power.lua") -- power distribution +dofile(MP.."/basis/trowel.lua") -- hidden networks +dofile(MP.."/basis/junction.lua") -- network junction box + +-- Steam Engine +dofile(MP.."/steam_engine/drive_axle.lua") +dofile(MP.."/steam_engine/steam_pipe.lua") +dofile(MP.."/steam_engine/firebox.lua") +dofile(MP.."/steam_engine/boiler.lua") +--dofile(MP.."/steam_engine/cylinder.lua") +dofile(MP.."/steam_engine/flywheel.lua") +dofile(MP.."/steam_engine/gearbox.lua") + +dofile(MP.."/electric/electric_cable.lua") +dofile(MP.."/electric/test.lua") + + +dofile(MP.."/fermenter/biogas_pipe.lua") +dofile(MP.."/fermenter/gasflare.lua") + + +dofile(MP.."/nodes/test.lua") +dofile(MP.."/mechanic/perf_test.lua") diff --git a/intllib.sh b/intllib.sh new file mode 100755 index 0000000..5b9294f --- /dev/null +++ b/intllib.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +../intllib/tools/xgettext.sh ./tube_api.lua ./internal1.lua ./internal2.lua \ No newline at end of file diff --git a/mechanic/distributor.lua b/mechanic/distributor.lua new file mode 100644 index 0000000..498a943 --- /dev/null +++ b/mechanic/distributor.lua @@ -0,0 +1,521 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + distributor.lua: + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta +local N = minetest.get_node + +local NUM_FILTER_ELEM = 6 +local NUM_FILTER_SLOTS = 4 + +local COUNTDOWN_TICKS = 4 +local STANDBY_TICKS = 8 +local CYCLE_TIME = 2 + +local function formspec(self, pos, meta) + local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false} + return "size[10.5,8.5]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "list[context;src;0,0;2,4;]".. + "image[2,1.5;1,1;tubelib_gui_arrow.png]".. + "image_button[2,3;1,1;"..self:get_state_button_image(meta)..";state_button;]".. + "checkbox[3,0;filter1;On;"..dump(filter[1]).."]".. + "checkbox[3,1;filter2;On;"..dump(filter[2]).."]".. + "checkbox[3,2;filter3;On;"..dump(filter[3]).."]".. + "checkbox[3,3;filter4;On;"..dump(filter[4]).."]".. + "image[4,0;0.3,1;tubelib_red.png]".. + "image[4,1;0.3,1;tubelib_green.png]".. + "image[4,2;0.3,1;tubelib_blue.png]".. + "image[4,3;0.3,1;tubelib_yellow.png]".. + "list[context;red;4.5,0;6,1;]".. + "list[context;green;4.5,1;6,1;]".. + "list[context;blue;4.5,2;6,1;]".. + "list[context;yellow;4.5,3;6,1;]".. + "list[current_player;main;1.25,4.5;8,4;]".. + "listring[context;src]".. + "listring[current_player;main]" +end + +local State = tubelib.NodeStates:new({ + node_name_passive = "tubelib:distributor", + node_name_active = "tubelib:distributor_active", + node_name_defect = "tubelib:distributor_defect", + infotext_name = "Tubelib Distributor", + cycle_time = CYCLE_TIME, + standby_ticks = STANDBY_TICKS, + aging_factor = 10, + formspec_func = formspec, +}) + +-- Return a key/value table with all items and the corresponding stack numbers +local function invlist_content_as_kvlist(list) + local res = {} + for idx,items in ipairs(list) do + local name = items:get_name() + if name ~= "" then + res[name] = idx + end + end + return res +end + +-- Return the total number of list entries +local function invlist_num_entries(list) + local res = 0 + for _,items in ipairs(list) do + local name = items:get_name() + if name ~= "" then + res = res + items:get_count() + end + end + return res +end + +-- Return a gapless table with all items +local function invlist_entries_as_list(list) + local res = {} + for _,items in ipairs(list) do + if items:get_count() > 0 then + res[#res+1] = {items:get_name(), items:get_count()} + end + end + return res +end + + +local function AddToTbl(kvTbl, new_items) + for _, l in ipairs(new_items) do + kvTbl[l[1]] = true + end + return kvTbl +end + +-- return the number of items to be pushed to an unconfigured slot +local function num_items(moved_items, name, filter_item_names, rejected_item_names) + if filter_item_names[name] == nil then -- not configured in one filter? + if moved_items < MAX_NUM_PER_CYC then + return math.min(4, MAX_NUM_PER_CYC - moved_items) + end + end + if rejected_item_names[name] then -- rejected item from another slot? + if moved_items < MAX_NUM_PER_CYC then + return math.min(rejected_item_names[name], MAX_NUM_PER_CYC - moved_items) + end + end +end + +local function allow_metadata_inventory_put(pos, listname, index, stack, player) + local meta = M(pos) + local inv = meta:get_inventory() + local list = inv:get_list(listname) + + if minetest.is_protected(pos, player:get_player_name()) then + return 0 + end + if listname == "src" then + if State:get_state(M(pos)) == tubelib.STANDBY then + State:start(pos, meta) + end + return stack:get_count() + elseif invlist_num_entries(list) < MAX_NUM_PER_CYC then + return stack:get_count() + end + return 0 +end + +local function allow_metadata_inventory_take(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then + return 0 + end + return stack:get_count() +end + +local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player) + local meta = M(pos) + local inv = meta:get_inventory() + local stack = inv:get_stack(from_list, from_index) + return allow_metadata_inventory_put(pos, to_list, to_index, stack, player) +end + +local SlotColors = {"red", "green", "blue", "yellow"} +local Num2Ascii = {"B", "L", "F", "R"} -- color to side translation +local FilterCache = {} -- local cache for filter settings + +local function filter_settings(pos) + local hash = minetest.hash_node_position(pos) + local meta = M(pos) + local inv = meta:get_inventory() + local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false} + local kvFilterItemNames = {} -- { = true,...} + local kvSide2ItemNames = {} -- {"F" = {,...},...} + + -- collect all filter settings + for idx,slot in ipairs(SlotColors) do + local side = Num2Ascii[idx] + if filter[idx] == true then + local list = inv:get_list(slot) + local filter = invlist_entries_as_list(list) + AddToTbl(kvFilterItemNames, filter) + kvSide2ItemNames[side] = filter + end + end + + FilterCache[hash] = { + kvFilterItemNames = kvFilterItemNames, + kvSide2ItemNames = kvSide2ItemNames, + kvRejectedItemNames = {}, + } +end + +-- move items from configured filters to the output +local function distributing(pos, meta) + local player_name = meta:get_string("player_name") + local slot_idx = meta:get_int("slot_idx") or 1 + meta:set_int("slot_idx", (slot_idx + 1) % NUM_FILTER_SLOTS) + local side = Num2Ascii[slot_idx+1] + local listname = SlotColors[slot_idx+1] + local inv = meta:get_inventory() + local list = inv:get_list("src") + local kvSrc = invlist_content_as_kvlist(list) + local counter = minetest.deserialize(meta:get_string("item_counter")) or + {red=0, green=0, blue=0, yellow=0} + + -- calculate the filter settings only once + local hash = minetest.hash_node_position(pos) + if FilterCache[hash] == nil then + filter_settings(pos) + end + + -- read data from Cache + -- all filter items as key/value { = true,...} + local kvFilterItemNames = FilterCache[hash].kvFilterItemNames + -- filter items of one slot as list {{, },...} + local items = FilterCache[hash].kvSide2ItemNames[side] + -- rejected items from other filter slots + local rejected = FilterCache[hash].kvRejectedItemNames + + if items == nil then return end + + local moved_items_total = 0 + if next(items) then + for _,item in ipairs(items) do + local name, num = item[1], item[2] + if kvSrc[name] then + local item = tubelib.get_this_item(meta, "src", kvSrc[name], num) -- <<=== tubelib + if item then + if not tubelib.push_items(pos, side, item, player_name) then -- <<=== tubelib + tubelib.put_item(meta, "src", item) + rejected[name] = num + else + counter[listname] = counter[listname] + num + moved_items_total = moved_items_total + num + end + end + end + end + end + + -- move additional items from unconfigured filters to the output + if next(items) == nil then + local moved_items = 0 + for name,_ in pairs(kvSrc) do + local num = num_items(moved_items, name, kvFilterItemNames, rejected) + if num then + local item = tubelib.get_this_item(meta, "src", kvSrc[name], num) -- <<=== tubelib + if item then + if not tubelib.push_items(pos, side, item, player_name) then -- <<=== tubelib + tubelib.put_item(meta, "src", item) + else + counter[listname] = counter[listname] + num + moved_items = moved_items + num + moved_items_total = moved_items_total + num + end + end + end + end + -- delete list for next slot round + if moved_items > 0 then + FilterCache[hash].kvRejectedItemNames = {} + end + end + meta:set_string("item_counter", minetest.serialize(counter)) + if moved_items_total > 0 then + State:keep_running(pos, meta, COUNTDOWN_TICKS, moved_items_total) + else + State:idle(pos, meta) + end +end + +-- move items to the output slots +local function keep_running(pos, elapsed) + local meta = M(pos) + distributing(pos, meta) + return State:is_active(meta) +end + +local function on_receive_fields(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + return + end + local meta = M(pos) + local filter = minetest.deserialize(meta:get_string("filter")) + if fields.filter1 ~= nil then + filter[1] = fields.filter1 == "true" + elseif fields.filter2 ~= nil then + filter[2] = fields.filter2 == "true" + elseif fields.filter3 ~= nil then + filter[3] = fields.filter3 == "true" + elseif fields.filter4 ~= nil then + filter[4] = fields.filter4 == "true" + end + meta:set_string("filter", minetest.serialize(filter)) + + filter_settings(pos) + + if fields.state_button ~= nil then + State:state_button_event(pos, fields) + else + meta:set_string("formspec", formspec(State, pos, meta)) + end +end + +-- tubelib command to turn on/off filter channels +local function change_filter_settings(pos, slot, val) + local slots = {["red"] = 1, ["green"] = 2, ["blue"] = 3, ["yellow"] = 4} + local meta = M(pos) + local filter = minetest.deserialize(meta:get_string("filter")) + local num = slots[slot] or 1 + if num >= 1 and num <= 4 then + filter[num] = val == "on" + end + meta:set_string("filter", minetest.serialize(filter)) + + filter_settings(pos) + + meta:set_string("formspec", formspec(State, pos, meta)) + return true +end + +minetest.register_node("tubelib:distributor", { + description = "Tubelib Distributor", + tiles = { + -- up, down, right, left, back, front + 'tubelib_distributor.png', + 'tubelib_front.png', + 'tubelib_distributor_yellow.png', + 'tubelib_distributor_green.png', + "tubelib_distributor_red.png", + "tubelib_distributor_blue.png", + }, + + after_place_node = function(pos, placer) + local meta = M(pos) + local number = tubelib.add_node(pos, "tubelib:distributor") -- <<=== tubelib + local filter = {false,false,false,false} + meta:set_string("filter", minetest.serialize(filter)) + State:node_init(pos, number) + meta:set_string("player_name", placer:get_player_name()) + + local inv = meta:get_inventory() + inv:set_size('src', 8) + inv:set_size('yellow', 6) + inv:set_size('green', 6) + inv:set_size('red', 6) + inv:set_size('blue', 6) + meta:set_string("item_counter", minetest.serialize({red=0, green=0, blue=0, yellow=0})) + end, + + on_receive_fields = on_receive_fields, + + can_dig = function(pos, player) + if minetest.is_protected(pos, player:get_player_name()) then + return false + end + local inv = M(pos):get_inventory() + return inv:is_empty("src") + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + tubelib.remove_node(pos) -- <<=== tubelib + State:after_dig_node(pos, oldnode, oldmetadata, digger) + end, + + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_take = allow_metadata_inventory_take, + allow_metadata_inventory_move = allow_metadata_inventory_move, + + on_timer = keep_running, + on_rotate = screwdriver.disallow, + + drop = "", + paramtype = "light", + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {choppy=2, cracky=2, crumbly=2}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + + +minetest.register_node("tubelib:distributor_active", { + description = "Tubelib Distributor", + tiles = { + -- up, down, right, left, back, front + { + image = "tubelib_distributor_active.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + 'tubelib_front.png', + 'tubelib_distributor_yellow.png', + 'tubelib_distributor_green.png', + "tubelib_distributor_red.png", + "tubelib_distributor_blue.png", + }, + + on_receive_fields = on_receive_fields, + + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_take = allow_metadata_inventory_take, + allow_metadata_inventory_move = allow_metadata_inventory_move, + + on_timer = keep_running, + on_rotate = screwdriver.disallow, + + paramtype = "light", + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {crumbly=0, not_in_creative_inventory=1}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + +minetest.register_node("tubelib:distributor_defect", { + description = "Tubelib Distributor", + tiles = { + -- up, down, right, left, back, front + 'tubelib_distributor.png', + 'tubelib_front.png', + 'tubelib_distributor_yellow.png^tubelib_defect.png', + 'tubelib_distributor_green.png^tubelib_defect.png', + "tubelib_distributor_red.png^tubelib_defect.png", + "tubelib_distributor_blue.png^tubelib_defect.png", + }, + + after_place_node = function(pos, placer) + local meta = M(pos) + local number = tubelib.add_node(pos, "tubelib:distributor") -- <<=== tubelib + State:node_init(pos, number) + meta:set_string("player_name", placer:get_player_name()) + + local filter = {false,false,false,false} + meta:set_string("filter", minetest.serialize(filter)) + local inv = meta:get_inventory() + inv:set_size('src', 8) + inv:set_size('yellow', 6) + inv:set_size('green', 6) + inv:set_size('red', 6) + inv:set_size('blue', 6) + meta:set_string("item_counter", minetest.serialize({red=0, green=0, blue=0, yellow=0})) + State:defect(pos, meta) + end, + + on_receive_fields = on_receive_fields, + + can_dig = function(pos, player) + if minetest.is_protected(pos, player:get_player_name()) then + return false + end + local inv = M(pos):get_inventory() + return inv:is_empty("src") + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + tubelib.remove_node(pos) -- <<=== tubelib + end, + + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_take = allow_metadata_inventory_take, + allow_metadata_inventory_move = allow_metadata_inventory_move, + + on_rotate = screwdriver.disallow, + + paramtype = "light", + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + + +minetest.register_craft({ + output = "tubelib:distributor 2", + recipe = { + {"group:wood", "default:steel_ingot", "group:wood"}, + {"tubelib:tubeS", "default:mese_crystal", "tubelib:tubeS"}, + {"group:wood", "default:steel_ingot", "group:wood"}, + }, +}) + + +--------------------------------------------------------------- tubelib +tubelib.register_node("tubelib:distributor", + {"tubelib:distributor_active", "tubelib:distributor_defect"}, { + on_pull_item = function(pos, side) + return tubelib.get_item(M(pos), "src") + end, + on_push_item = function(pos, side, item) + return tubelib.put_item(M(pos), "src", item) + end, + on_unpull_item = function(pos, side, item) + return tubelib.put_item(M(pos), "src", item) + end, + on_recv_message = function(pos, topic, payload) + if topic == "filter" then + return change_filter_settings(pos, payload.slot, payload.val) + elseif topic == "counter" then + local meta = minetest.get_meta(pos) + return minetest.deserialize(meta:get_string("item_counter")) or + {red=0, green=0, blue=0, yellow=0} + elseif topic == "clear_counter" then + local meta = minetest.get_meta(pos) + meta:set_string("item_counter", minetest.serialize({red=0, green=0, blue=0, yellow=0})) + else + local resp = State:on_receive_message(pos, topic, payload) + if resp then + return resp + else + return "unsupported" + end + end + end, + + on_node_load = function(pos) + State:on_node_load(pos) + end, + on_node_repair = function(pos) + return State:on_node_repair(pos) + end, +}) +--------------------------------------------------------------- tubelib diff --git a/mechanic/perf_test.lua b/mechanic/perf_test.lua new file mode 100644 index 0000000..e415d04 --- /dev/null +++ b/mechanic/perf_test.lua @@ -0,0 +1,91 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + distributor.lua: + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta +local N = minetest.get_node + +local function formspec() + return "size[10.5,8.5]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "list[context;src;0,0;2,4;]".. + "list[current_player;main;1.25,4.5;8,4;]".. + "listring[context;src]".. + "listring[current_player;main]" +end + + +-- move items to the output slots +local function keep_running(pos, elapsed) + local meta = M(pos) + local inv = meta:get_inventory() + local name, num + + for i = 1,10 do + --local list = inv:get_list("src") + for i = 1,8 do + --local stack = list[i] + local stack = inv:get_stack("src", i) + if stack:get_count() > 0 then + local taken = inv:remove_item("src", stack) + num = taken:get_count() + inv:add_item("src", taken) + break + end + end + end + return true +end + +local function after_place_node(pos, placer) + local meta = M(pos) + local inv = meta:get_inventory() + inv:set_size('src', 8) + inv:add_item("src", ItemStack("wool:blue")) + inv:add_item("src", ItemStack("wool:red")) + inv:add_item("src", ItemStack("wool:green")) + meta:set_string("formspec", formspec()) + + minetest.get_node_timer(pos):start(0.1) +end + +minetest.register_node("techage:perf_test", { + description = "perf_test", + tiles = {"techage_filling_ta2.png"}, + + after_place_node = after_place_node, + + on_timer = keep_running, + + paramtype = "light", + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {choppy=2, cracky=2, crumbly=2}, + is_ground_content = false, +}) + + +minetest.register_lbm({ + label = "[TechAge] Node update", + name = "techage:perf_test", + nodenames = {"techage:perf_test"}, + run_at_every_load = true, + action = function(pos, node) + after_place_node(pos) + end +}) diff --git a/mod.conf b/mod.conf new file mode 100644 index 0000000..6f5624b --- /dev/null +++ b/mod.conf @@ -0,0 +1 @@ +name = techage \ No newline at end of file diff --git a/models/techage_boiler.obj b/models/techage_boiler.obj new file mode 100644 index 0000000..ab56cfb --- /dev/null +++ b/models/techage_boiler.obj @@ -0,0 +1,124 @@ +# Blender v2.78 (sub 0) OBJ File: 'test.blend' +# www.blender.org +o Cylinder +v 0.000000 -0.500000 -0.450000 +v 0.000000 0.500000 -0.450000 +v 0.172208 -0.500000 -0.415746 +v 0.172208 0.500000 -0.415746 +v 0.318198 -0.500000 -0.318198 +v 0.318198 0.500000 -0.318198 +v 0.415746 -0.500000 -0.172208 +v 0.415746 0.500000 -0.172208 +v 0.450000 -0.500000 0.000000 +v 0.450000 0.500000 0.000000 +v 0.415746 -0.500000 0.172208 +v 0.415746 0.500000 0.172208 +v 0.318198 -0.500000 0.318198 +v 0.318198 0.500000 0.318198 +v 0.172208 -0.500000 0.415746 +v 0.172208 0.500000 0.415746 +v 0.000000 -0.500000 0.450000 +v 0.000000 0.500000 0.450000 +v -0.172207 -0.500000 0.415746 +v -0.172207 0.500000 0.415746 +v -0.318198 -0.500000 0.318198 +v -0.318198 0.500000 0.318198 +v -0.415746 -0.500000 0.172208 +v -0.415746 0.500000 0.172208 +v -0.450000 -0.500000 -0.000000 +v -0.450000 0.500000 -0.000000 +v -0.415746 -0.500000 -0.172208 +v -0.415746 0.500000 -0.172208 +v -0.318198 -0.500000 -0.318198 +v -0.318198 0.500000 -0.318198 +v -0.172207 -0.500000 -0.415746 +v -0.172207 0.500000 -0.415746 +vt 0.5486 0.5000 +vt 0.5486 1.0000 +vt 0.4725 1.0000 +vt 0.4725 0.5000 +vt 0.4142 1.0000 +vt 0.4142 0.5000 +vt 1.0000 0.5000 +vt 1.0000 1.0000 +vt 0.9239 1.0000 +vt 0.9239 0.5000 +vt 0.8415 1.0000 +vt 0.8415 0.5000 +vt 0.7654 1.0000 +vt 0.7654 0.5000 +vt 0.4142 0.5000 +vt 0.4142 0.0000 +vt 0.4725 0.0000 +vt 0.4725 0.5000 +vt 0.5486 0.0000 +vt 0.5486 0.5000 +vt 0.6310 0.0000 +vt 0.6310 0.5000 +vt 0.7071 0.0000 +vt 0.7071 0.5000 +vt 0.7654 0.0000 +vt 0.7654 0.5000 +vt 0.8415 0.0000 +vt 0.8415 0.5000 +vt 0.9239 0.0000 +vt 0.9239 0.5000 +vt 1.0000 0.0000 +vt 1.0000 0.5000 +vt 0.7654 0.5000 +vt 0.7654 1.0000 +vt 0.7071 1.0000 +vt 0.7071 0.5000 +vt 0.3244 0.4749 +vt 0.3827 0.5370 +vt 0.4142 0.6181 +vt 0.4142 0.7059 +vt 0.3827 0.7870 +vt 0.3244 0.8491 +vt 0.2483 0.8827 +vt 0.1659 0.8827 +vt 0.0898 0.8491 +vt 0.0315 0.7870 +vt 0.0000 0.7059 +vt 0.0000 0.6181 +vt 0.0315 0.5370 +vt 0.0898 0.4749 +vt 0.1659 0.4414 +vt 0.2483 0.4414 +vt 0.6310 1.0000 +vt 0.6310 0.5000 +vt 0.0000 0.2646 +vt 0.0000 0.1768 +vt 0.0315 0.0957 +vt 0.0898 0.0336 +vt 0.1659 0.0000 +vt 0.2483 0.0000 +vt 0.3244 0.0336 +vt 0.3827 0.0957 +vt 0.4142 0.1768 +vt 0.4142 0.2646 +vt 0.3827 0.3457 +vt 0.3244 0.4078 +vt 0.2483 0.4414 +vt 0.1659 0.4414 +vt 0.0898 0.4078 +vt 0.0315 0.3457 +s off +f 1/1 2/2 4/3 3/4 +f 3/4 4/3 6/5 5/6 +f 5/7 6/8 8/9 7/10 +f 7/10 8/9 10/11 9/12 +f 9/12 10/11 12/13 11/14 +f 11/15 12/16 14/17 13/18 +f 13/18 14/17 16/19 15/20 +f 15/20 16/19 18/21 17/22 +f 17/22 18/21 20/23 19/24 +f 19/24 20/23 22/25 21/26 +f 21/26 22/25 24/27 23/28 +f 23/28 24/27 26/29 25/30 +f 25/30 26/29 28/31 27/32 +f 27/33 28/34 30/35 29/36 +f 4/37 2/38 32/39 30/40 28/41 26/42 24/43 22/44 20/45 18/46 16/47 14/48 12/49 10/50 8/51 6/52 +f 29/36 30/35 32/53 31/54 +f 31/54 32/53 2/2 1/1 +f 1/55 3/56 5/57 7/58 9/59 11/60 13/61 15/62 17/63 19/64 21/65 23/66 25/67 27/68 29/69 31/70 diff --git a/nodes/test.lua b/nodes/test.lua new file mode 100644 index 0000000..13651e9 --- /dev/null +++ b/nodes/test.lua @@ -0,0 +1,549 @@ +minetest.register_node("techage:block1", { + description = "block1", + tiles = {"techage_filling_ta2.png^techage_frame_ta2.png"}, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:block2", { + description = "block2", + tiles = {"techage_filling_ta3.png^techage_frame_ta3.png"}, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:block3", { + description = "block3", + tiles = { + "techage_top_ta4.png", + "techage_filling_ta4.png^techage_frame_ta4.png", + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + + + + +minetest.register_node("techage:block4", { + description = "block4", + tiles = { + -- up, down, right, left, back, front + 'techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_arrow.png', + 'techage_filling_ta2.png^techage_frame_ta2.png', + 'techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_outp.png', + 'techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_inp.png', + { + image = "techage_pusher14.png^[transformR180]^techage_frame14_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + { + image = "techage_pusher14.png^techage_frame14_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:block5", { + description = "block5", + tiles = { + -- up, down, right, left, back, front + 'techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_arrow.png', + 'techage_filling_ta3.png^techage_frame_ta3.png', + 'techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_outp.png', + 'techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_inp.png', + { + image = "techage_pusher14.png^[transformR180]^techage_frame14_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + { + image = "techage_pusher14.png^techage_frame14_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:block6", { + description = "block6", + tiles = { + -- up, down, right, left, back, front + 'techage_filling_ta4.png^techage_top_ta4.png^techage_appl_arrow.png', + 'techage_filling_ta4.png^techage_frame_ta4.png', + 'techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_outp.png', + 'techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_inp.png', + { + image = "tubelib_pusher.png^[transformR180]^techage_frame14_ta4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + { + image = "tubelib_pusher.png^techage_frame14_ta4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + + + + + + +minetest.register_node("techage:block7", { + description = "block7", + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png", + "techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png", + "techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png", + "techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_front_ta3.png", + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:block8", { + description = "block8", + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta3.png^techage_frame_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_front_ta3.png", + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:block9", { + description = "block9", + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta4.png^techage_top_ta4.png", + "techage_filling_ta4.png^techage_frame_ta4.png", + "techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png", + "techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png", + "techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png", + "techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_front_ta4.png", + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + + + +minetest.register_node("techage:sieve", { + description = "sieve", + drawtype = "nodebox", + tiles = { + -- up, down, right, left, back, front + 'techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_inp.png', + 'techage_filling_ta2.png^techage_frame_ta2.png', + 'techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_outp.png', + 'techage_filling_ta2.png^techage_frame_ta2.png', + { + image = "techage_filling4_ta2.png^techage_appl_sieve4.png^techage_frame4_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2, + }, + }, + { + image = "techage_filling4_ta2.png^techage_appl_sieve4.png^techage_frame4_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:sieve2", { + description = "sieve", + drawtype = "nodebox", + tiles = { + -- up, down, right, left, back, front + 'techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_inp.png', + 'techage_filling_ta3.png^techage_frame_ta3.png', + 'techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_outp.png', + 'techage_filling_ta3.png^techage_frame_ta3.png', + { + image = "techage_filling4_ta3.png^techage_appl_sieve4.png^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2, + }, + }, + { + image = "techage_filling4_ta3.png^techage_appl_sieve4.png^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + + +minetest.register_node("techage:sieve3", { + description = "sieve", + drawtype = "nodebox", + tiles = { + -- up, down, right, left, back, front + 'techage_filling_ta4.png^techage_top_ta4.png^techage_appl_inp.png', + 'techage_filling_ta4.png^techage_frame_ta4.png', + 'techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_outp.png', + 'techage_filling_ta4.png^techage_frame_ta4.png', + { + image = "techage_filling4_ta4.png^techage_appl_sieve4.png^techage_frame4_ta4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2, + }, + }, + { + image = "techage_filling4_ta4.png^techage_appl_sieve4.png^techage_frame4_ta4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + + +minetest.register_node("techage:filler", { + description = "filler", + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_arrow.png", + "techage_filling_ta3.png^techage_frame_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_hole2.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_inp.png", + --"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_filler.png", + --"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_filler.png", + { + image = "techage_filling4_ta3.png^techage_appl_filler4.png^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + { + image = "techage_filling4_ta3.png^techage_appl_filler4.png^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 2.0, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:compressor", { + description = "compressor", + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_arrow.png", + "techage_filling_ta3.png^techage_frame_ta3.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_hole2.png", + "techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_hole2.png", + --"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_compressor.png", + --"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_compressor.png^[transformFX]", + { + image = "techage_filling4_ta3.png^techage_appl_compressor4.png^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.2, + }, + }, + { + image = "techage_filling4_ta3.png^techage_appl_compressor4.png^[transformFX]^techage_frame4_ta3.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.2, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + + +minetest.register_node("techage:fermenter", { + description = "fermenter", + tiles = {"techage_fermenter.png"}, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + +minetest.register_node("techage:fermenter_foil", { + description = "fermenter_foil", + tiles = {"techage_fermenter_foil.png"}, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2}, + is_ground_content = false, + --sounds = default.node_sound_stone_defaults(), +}) + + +minetest.register_node("techage:biomass", { + description = "biomass", + drawtype = "liquid", + tiles = { + { + name = "techage_biomass.png", + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 4.0, + }, + }, + }, + special_tiles = { + -- New-style water source material (mostly unused) + { + name = "techage_biomass.png", + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 4.0, + }, + backface_culling = false, + }, + }, + + on_timer = function(pos) + minetest.remove_node(pos) + return false + end, + + after_place_node = function(pos, placer) + minetest.get_node_timer(pos):start(5) + end, + + --alpha = 160, + paramtype = "light", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drop = "", + drowning = 1, + liquidtype = "source", + liquid_alternative_flowing = "techage:biomass_flowing", + liquid_alternative_source = "techage:biomass", + liquid_viscosity = 1, + post_effect_color = {a = 103, r = 30, g = 60, b = 90}, + groups = {water = 3, liquid = 3, puts_out_fire = 1, cools_lava = 1}, + sounds = default.node_sound_water_defaults(), +}) + +minetest.register_node("techage:biomass_flowing", { + description = "biomass", + drawtype = "flowingliquid", + tiles = {"default_water.png"}, + special_tiles = { + { + name = "techage_biomass.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 4, + }, + }, + { + name = "techage_biomass.png", + backface_culling = true, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 4, + }, + }, + }, + --alpha = 220, + paramtype = "light", + paramtype2 = "flowingliquid", + walkable = false, + pointable = false, + diggable = false, + buildable_to = true, + is_ground_content = false, + drop = "", + drowning = 1, + liquidtype = "flowing", + liquid_alternative_flowing = "techage:biomass_flowing", + liquid_alternative_source = "techage:biomass", + liquid_viscosity = 1, + post_effect_color = {a = 103, r = 30, g = 60, b = 90}, + groups = {water = 3, liquid = 3, puts_out_fire = 1, + not_in_creative_inventory = 0, cools_lava = 1}, + sounds = default.node_sound_water_defaults(), +}) + diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..f8b3cc3 Binary files /dev/null and b/screenshot.png differ diff --git a/settingtypes.txt b/settingtypes.txt new file mode 100644 index 0000000..b44b4c1 --- /dev/null +++ b/settingtypes.txt @@ -0,0 +1,10 @@ +# Maximim number of Forceload Blocks per player (default 12) +tubelib_max_num_forceload_blocks (max number of Forceload Blocks) int 12 + +# Enable Basalt Stone (and disable ore generation via cobble generator) +tubelib_basalt_stone_enabled (Basalt Stone enabled) bool true + +# This aging value is used to calculate the lifetime of machines before +# they go defect . The value 200 (default) results in a lifetime +# for standard machines of about 2000 - 8000 item processing cycles. +tubelib_machine_aging_value (machine aging value) int 200 \ No newline at end of file diff --git a/sounds/techage_button.ogg b/sounds/techage_button.ogg new file mode 100644 index 0000000..3d6b28c Binary files /dev/null and b/sounds/techage_button.ogg differ diff --git a/sounds/techage_gasflare.ogg b/sounds/techage_gasflare.ogg new file mode 100644 index 0000000..a1cd9d7 Binary files /dev/null and b/sounds/techage_gasflare.ogg differ diff --git a/steam_engine/boiler.lua b/steam_engine/boiler.lua new file mode 100644 index 0000000..acdc50b --- /dev/null +++ b/steam_engine/boiler.lua @@ -0,0 +1,293 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA2 Steam Engine Boiler + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +local CYCLE_TIME = 4 +local HEAT_STEP = 10 +local WATER_CONSUMPTION = 2 +local MAX_WATER = 10 +local POWER = 10 + +local Water = { + ["bucket:bucket_river_water"] = true, + ["bucket:bucket_water"] = true, + ["bucket:bucket_empty"] = true, +} + +local function formspec(mem) + local temp = mem.temperature or 20 + local button = mem.running and I("Stop") or I("Start") + return "size[8,7]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "image_button[0,0.2;1,1;techage_form_inventory.png;storage;;true;false;]".. + "list[context;water;1,0.2;1,1;]".. + "image_button[0,1.6;1,1;techage_form_input.png;input;;true;false;]".. + "list[context;input;1,1.6;1,1;]".. + "image[1,1.6;1,1;bucket_water.png]".. + "image[1,1.6;1,1;techage_form_mask.png]".. + "image[3,0.5;1,2;techage_form_temp_bg.png^[lowpart:".. + temp..":techage_form_temp_fg.png]".. + "image[4,0.5;1,2;"..techage.generator_formspec_level(mem).. + "button[6,0.5;2,1;start;"..button.."]".. + "button[6,1.5;2,1;update;"..I("Update").."]".. + "list[current_player;main;0,3;8,4;]".. + "listring[current_name;water]".. + "listring[current_player;main]".. + default.get_hotbar_bg(0, 3) +end + +local function can_dig(pos, player) + local inv = M(pos):get_inventory() + local mem = tubelib2.get_mem(pos) + return inv:is_empty("water") and inv:is_empty("input") and not mem.running +end + +local function move_to_water(pos) + local inv = M(pos):get_inventory() + local water_stack = inv:get_stack("water", 1) + local input_stack = inv:get_stack("input", 1) + + if input_stack:get_name() == "bucket:bucket_empty" and input_stack:get_count() == 1 then + if water_stack:get_count() > 0 then + water_stack:set_count(water_stack:get_count() - 1) + input_stack = ItemStack("bucket:bucket_water") + inv:set_stack("water", 1, water_stack) + inv:set_stack("input", 1, input_stack) + end + elseif water_stack:get_count() < MAX_WATER then + if water_stack:get_count() == 0 then + water_stack = ItemStack("default:water_source") + else + water_stack:set_count(water_stack:get_count() + 1) + end + input_stack = ItemStack("bucket:bucket_empty") + inv:set_stack("water", 1, water_stack) + inv:set_stack("input", 1, input_stack) + end +end + +local function start_boiler(pos) + local mem = tubelib2.get_mem(pos) + mem.water_level = mem.water_level or 0 + local inv = M(pos):get_inventory() + local water_stack = inv:get_stack("water", 1) + print("trigger_boiler", mem.fire_trigger, mem.water_level, water_stack:get_count()) + if mem.fire_trigger and (mem.water_level > 0 or water_stack:get_count() > 0) then + if not minetest.get_node_timer(pos):is_started() then + minetest.get_node_timer(pos):start(CYCLE_TIME) + end + end +end + +local function allow_metadata_inventory_put(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then + return 0 + end + if listname == "input" and Water[stack:get_name()] then + start_boiler(pos) + return stack:get_count() + end + return 0 +end + +local function allow_metadata_inventory_take(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then + return 0 + end + if listname == "input" then + return stack:get_count() + end + return 0 +end + +local function on_receive_fields(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + return + end + + if fields.update then + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(mem)) + end + + if fields.start then + local mem = tubelib2.get_mem(pos) + mem.running = not (mem.running or false) + if mem.running then + techage.generator_on(pos, POWER) + else + techage.generator_off(pos) + end + M(pos):set_string("formspec", formspec(mem)) + end +end + +local function on_rightclick(pos, node, clicker) + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(mem)) +end + +local function get_water(pos) + local inv = M(pos):get_inventory() + local items = inv:get_stack("water", 1) + if items:get_count() > 0 then + local taken = items:take_item(1) + inv:set_stack("water", 1, items) + return true + end + return false +end + +local function node_timer(pos) + local mem = tubelib2.get_mem(pos) + mem.temperature = mem.temperature or 20 + mem.water_level = math.max((mem.water_level or 0) - WATER_CONSUMPTION, 0) + + print(mem.fire_trigger, mem.running, mem.temperature, mem.water_level) + + if mem.fire_trigger then + mem.temperature = math.min(mem.temperature + HEAT_STEP, 100) + else + mem.temperature = math.max(mem.temperature - HEAT_STEP, 20) + end + + if mem.water_level == 0 then + if get_water(pos) then + mem.water_level = 100 + else + mem.temperature = 20 + end + end + + if mem.temperature > 80 and mem.running then + techage.generator_on(pos, POWER) + else + techage.generator_off(pos) + end + mem.fire_trigger = false + return mem.temperature > 20 +end + + +minetest.register_node("techage:boiler", { + description = I("TA2 Boiler"), + tiles = {"techage_boiler.png"}, + drawtype = "mesh", + mesh = "techage_boiler.obj", + selection_box = { + type = "fixed", + fixed = {-10/32, -16/32, -10/32, 10/32, 46/32, 10/32}, + }, + + can_dig = can_dig, + on_timer = node_timer, + allow_metadata_inventory_put = allow_metadata_inventory_put, + allow_metadata_inventory_take = allow_metadata_inventory_take, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + + techage = { + network = techage.SteamPipe, + power_consumption = function(pos, dir) + techage.generator_power_consumption(pos, dir) + end, + trigger_boiler = function(pos) + local mem = tubelib2.get_mem(pos) + mem.fire_trigger = true + start_boiler(pos) + end, + }, + + on_destruct = function(pos) + techage.generator_on_destruct({x=pos.x, y=pos.y+1, z=pos.z}) + end, + + on_construct = function(pos) + local inv = M(pos):get_inventory() + inv:set_size('water', 1) + inv:set_size('input', 1) + local node = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}) + if node.name ~= "air" then + return + end + minetest.add_node({x=pos.x, y=pos.y+1, z=pos.z}, {name = "techage:boiler2", param2 = minetest.get_node(pos).param2}) + end, + + after_place_node = function(pos, placer, pointed_thing) + techage.generator_after_place_node({x=pos.x, y=pos.y+1, z=pos.z}, placer) + local mem = tubelib2.get_mem(pos) + mem.running = false + mem.water_level = 0 + mem.temperatur = 20 + M(pos):set_string("formspec", formspec(mem)) + end, + + on_metadata_inventory_put = function(pos) + minetest.after(0.5, move_to_water, pos) + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + local node = minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}) + if node.name == "techage:boiler2" then + minetest.remove_node({x=pos.x, y=pos.y+1, z=pos.z}) + techage.generator_after_dig_node({x=pos.x, y=pos.y+1, z=pos.z}, oldnode, oldmetadata, digger) + end + end, + + paramtype2 = "facedir", + groups = {cracky=1}, + on_rotate = screwdriver.disallow, + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), +}) + +-- boiler2 +minetest.register_node("techage:boiler2", { + description = ("TA2 Boiler"), + tiles = {"techage_boiler2.png"}, + drawtype = "mesh", + mesh = "techage_boiler.obj", + selection_box = { + type = "fixed", + fixed = {-10/32, -16/32, -10/32, 10/32, 16/32, 10/32}, + }, + + techage = { + network = techage.SteamPipe, + power_consumption = function(pos, dir) + techage.generator_power_consumption({x=pos.x, y=pos.y-1, z=pos.z}, dir) + end, + }, + + after_tube_update = function(node, pos, out_dir, peer_pos, peer_in_dir) + techage.generator_after_tube_update(node, + {x=pos.x, y=pos.y-1, z=pos.z}, out_dir, peer_pos, peer_in_dir) + end, + + diggable = false, + --pointable = false, + groups = {not_in_creative_inventory = 1}, +}) + diff --git a/steam_engine/cylinder.lua b/steam_engine/cylinder.lua new file mode 100644 index 0000000..5c1ba6d --- /dev/null +++ b/steam_engine/cylinder.lua @@ -0,0 +1,103 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA2 Steam Engine Cylinder + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +local function can_dig(pos, player) + local inv = M(pos):get_inventory() + return inv:is_empty("fuel") +end + +local function swap_node(pos, name) + local node = minetest.get_node(pos) + if node.name == name then + return + end + node.name = name + minetest.swap_node(pos, node) +end + +local function node_timer(pos, elapsed) + local mem = tubelib2.get_mem(pos) + local inv = M(pos):get_inventory() + local fuellist = inv:get_list("fuel") +end + +minetest.register_node("techage:cylinder", { + description = I("TA2 Cylinder"), + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_cylinder.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_cylinder.png^techage_frame_ta2.png", + }, + + 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:cylinder_on", { + description = I("TA2 Cylinder"), + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + { + image = "techage_filling4_ta2.png^techage_cylinder4.png^techage_frame4_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.4, + }, + }, + { + image = "techage_filling4_ta2.png^techage_cylinder4.png^techage_frame4_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.4, + }, + }, + }, + paramtype = "light", + light_source = 0, + sunlight_propagates = true, + paramtype2 = "facedir", + on_rotate = screwdriver.disallow, + groups = {cracky=2, crumbly=2, choppy=2, not_in_creative_inventory = 1}, + drop = "techage:cylinder", + is_ground_content = false, + sounds = default.node_sound_metal_defaults(), +}) + diff --git a/steam_engine/drive_axle.lua b/steam_engine/drive_axle.lua new file mode 100644 index 0000000..967912d --- /dev/null +++ b/steam_engine/drive_axle.lua @@ -0,0 +1,177 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA2 Drive Axles for the Steam Engine + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +local Axle = tubelib2.Tube:new({ + dirs_to_check = {1,2,3,4,5,6}, + max_tube_length = 5, + show_infotext = false, + primary_node_names = {"techage:axle", "techage:axle_on"}, + secondary_node_names = {"techage:flywheel", "techage:flywheel_on", "techage:gearbox", "techage:gearbox_on"}, + after_place_tube = function(pos, param2, tube_type, num_tubes, state) + if state == "on" then + minetest.swap_node(pos, {name = "techage:axle_on", param2 = param2}) + else + minetest.swap_node(pos, {name = "techage:axle", param2 = param2}) + end + end, +}) + +Axle:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir) + minetest.registered_nodes[node.name].after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) +end) + +techage.Axle = Axle + +minetest.register_node("techage:axle", { + description = I("TA2 Drive Axle"), + tiles = { + "techage_axleR.png", + "techage_axleR.png", + "techage_axle.png", + "techage_axle.png", + "techage_axle_clutch.png", + "techage_axle_clutch.png", + }, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + if not Axle:after_place_tube(pos, placer, pointed_thing) then + minetest.remove_node(pos) + return true + end + return false + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Axle:after_dig_tube(pos, oldnode, oldmetadata) + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-3/16, -3/16, -4/8, 3/16, 3/16, 4/8}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {crumbly = 3, cracky = 3, snappy = 3}, + sounds = default.node_sound_metal_defaults(), +}) + +minetest.register_node("techage:axle_on", { + description = I("TA2 Drive Axle"), + tiles = { + { + image = "techage_axle4R.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + { + image = "techage_axle4R.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + { + image = "techage_axle4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + { + image = "techage_axle4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + { + image = "techage_axle_clutch4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + { + image = "techage_axle_clutch4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + }, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + if not Axle:after_place_tube(pos, placer, pointed_thing) then + minetest.remove_node(pos) + return true + end + return false + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Axle:after_dig_tube(pos, oldnode, oldmetadata) + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-3/16, -3/16, -4/8, 3/16, 3/16, 4/8}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + diggable = false, + groups = {not_in_creative_inventory = 1}, + sounds = default.node_sound_metal_defaults(), +}) diff --git a/steam_engine/firebox.lua b/steam_engine/firebox.lua new file mode 100644 index 0000000..0646d46 --- /dev/null +++ b/steam_engine/firebox.lua @@ -0,0 +1,208 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA2 Steam Engine Firebox + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta +local TP = function(pos) return minetest.registered_nodes[minetest.get_node(pos).name].techage end +local TN = function(node) return minetest.registered_nodes[node.name].techage end + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + +local CYCLE_TIME = 2 +local BURN_CYCLES = 10 + + +local Fuels = { + ["techage:charcoal"] = true, + ["default:coal_lump"] = true, + ["default:coalblock"] = true, +} + +local function formspec(mem) + local fuel_percent = 0 + if mem.running then + fuel_percent = (mem.burn_cycles * 100) / BURN_CYCLES + end + return "size[8,6]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "list[current_name;fuel;1,0.5;1,1;]".. + "image[3,0.5;1,1;default_furnace_fire_bg.png^[lowpart:".. + fuel_percent..":default_furnace_fire_fg.png]".. + "button[5,0.5;1.8,1;update;"..I("Update").."]".. + "list[current_player;main;0,2;8,4;]".. + "listring[current_name;fuel]".. + "listring[current_player;main]".. + default.get_hotbar_bg(0, 2) +end + +local function can_dig(pos, player) + local inv = M(pos):get_inventory() + return inv:is_empty("fuel") +end + +local function allow_metadata_inventory(pos, listname, index, stack, player) + if minetest.is_protected(pos, player:get_player_name()) then + return 0 + end + if Fuels[stack:get_name()] then + return stack:get_count() + end + return 0 +end + +local function on_receive_fields(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + return + end + + if fields.update then + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(mem)) + end +end + +local function on_rightclick(pos, node, clicker) + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(mem)) +end + +local function swap_node(pos, name) + local node = minetest.get_node(pos) + if node.name == name then + return + end + node.name = name + minetest.swap_node(pos, node) +end + +local function get_fuel(pos) + local inv = M(pos):get_inventory() + local items = inv:get_stack("fuel", 1) + if items:get_count() > 0 then + local taken = items:take_item(1) + inv:set_stack("fuel", 1, items) + return taken + end +end + +local function node_timer(pos, elapsed) + local mem = tubelib2.get_mem(pos) + if mem.running then + local this = TP({x=pos.x, y=pos.y+1, z=pos.z}) + if this and this.trigger_boiler then + this.trigger_boiler({x=pos.x, y=pos.y+1, z=pos.z}) + end + mem.burn_cycles = (mem.burn_cycles or 0) - 1 + if mem.burn_cycles <= 0 then + if get_fuel(pos) then + mem.burn_cycles = BURN_CYCLES + else + mem.running = false + swap_node(pos, "techage:firebox") + M(pos):set_string("formspec", formspec(mem)) + return false + end + end + return true + end +end + +minetest.register_node("techage:firebox", { + description = I("TA2 Firebox"), + tiles = { + -- up, down, right, left, back, front + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_appl_firehole.png^techage_frame_ta2.png", + }, + 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 = can_dig, + allow_metadata_inventory_put = allow_metadata_inventory, + allow_metadata_inventory_take = allow_metadata_inventory, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + + on_construct = function(pos) + local mem = tubelib2.init_mem(pos) + mem.running = false + mem.burn_cycles = 0 + local meta = M(pos) + meta:set_string("formspec", formspec(mem)) + local inv = meta:get_inventory() + inv:set_size('fuel', 1) + end, + + on_metadata_inventory_put = function(pos) + local mem = tubelib2.init_mem(pos) + mem.running = true + -- activate the formspec fire temporarily + mem.burn_cycles = BURN_CYCLES + M(pos):set_string("formspec", formspec(mem)) + mem.burn_cycles = 0 + swap_node(pos, "techage:firebox_on") + minetest.get_node_timer(pos):start(CYCLE_TIME) + end, +}) + +minetest.register_node("techage:firebox_on", { + description = I("TA2 Firebox"), + tiles = { + -- up, down, right, left, back, front + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + "techage_firebox.png^techage_frame_ta2.png", + { + image = "techage_firebox4.png^techage_appl_firehole4.png^techage_frame4_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.4, + }, + }, + }, + paramtype2 = "facedir", + light_source = 8, + on_rotate = screwdriver.disallow, + groups = {cracky=2, not_in_creative_inventory=1}, + is_ground_content = false, + sounds = default.node_sound_stone_defaults(), + drop = "techage:firebox", + + on_timer = node_timer, + can_dig = can_dig, + allow_metadata_inventory_put = allow_metadata_inventory, + allow_metadata_inventory_take = allow_metadata_inventory, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, +}) + diff --git a/steam_engine/flywheel.lua b/steam_engine/flywheel.lua new file mode 100644 index 0000000..05cd0cb --- /dev/null +++ b/steam_engine/flywheel.lua @@ -0,0 +1,180 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA2 Steam Engine Flywheel + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + +local CYCLE_TIME = 10 +local POWER = 8 + +local function swap_node(pos, name) + local node = minetest.get_node(pos) + if node.name == name then + return + end + node.name = name + minetest.swap_node(pos, node) +end + +local function formspec(mem) + return "size[8,7]".. + default.gui_bg.. + default.gui_bg_img.. + default.gui_slots.. + "image[3,0.5;1,2;"..techage.generator_formspec_level(mem).. + "button[5.5,1.2;1.8,1;update;"..I("Update").."]".. + "list[current_player;main;0,3;8,4;]".. + "listring[current_name;water]".. + "listring[current_player;main]".. + default.get_hotbar_bg(0, 3) +end + +local function on_receive_fields(pos, formname, fields, player) + if minetest.is_protected(pos, player:get_player_name()) then + return + end + + if fields.update then + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(mem)) + end +end + +local function on_rightclick(pos, node, clicker) + local mem = tubelib2.get_mem(pos) + M(pos):set_string("formspec", formspec(mem)) +end + + +local function node_timer(pos, elapsed) + local mem = tubelib2.get_mem(pos) + techage.generator_on(pos, POWER, techage.Axle) + return true +end + +local function on_punch(pos, node, puncher, pointed_thing) + local mem = tubelib2.get_mem(pos) + if mem.power_produce and mem.power_produce > 0 then + swap_node(pos, "techage:flywheel") + techage.generator_off(pos, techage.Axle) + --techage.generator_off(pos) + minetest.get_node_timer(pos):stop() + else + swap_node(pos, "techage:flywheel_on") + techage.generator_on(pos, POWER, techage.Axle) + --techage.generator_on(pos, POWER) + minetest.get_node_timer(pos):start(CYCLE_TIME) + end +end + +minetest.register_node("techage:flywheel", { + description = I("TA2 Flywheel"), + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_axle_clutch.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_appl_open.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png^techage_flywheel.png", + "techage_filling_ta2.png^techage_frame_ta2.png^techage_flywheel.png^[transformFX]", + }, + techage = { + network = techage.Axle, + power_consumption = techage.generator_power_consumption, + }, + + after_place_node = techage.generator_after_place_node, + after_tube_update = techage.generator_after_tube_update, + on_destruct = techage.generator_on_destruct, + after_dig_node = techage.generator_after_dig_node, + + on_timer = node_timer, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + on_punch = on_punch, + + 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:flywheel_on", { + description = I("TA2 Flywheel"), + tiles = { + -- up, down, right, left, back, front + "techage_filling_ta2.png^techage_frame_ta2.png", + "techage_filling_ta2.png^techage_frame_ta2.png", + { + image = "techage_filling4_ta2.png^techage_axle_clutch4.png^techage_frame4_ta2.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + "techage_filling_ta2.png^techage_appl_open.png^techage_frame_ta2.png", + { + image = "techage_filling4_ta2.png^techage_frame4_ta2.png^techage_flywheel4.png", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + { + image = "techage_filling4_ta2.png^techage_frame4_ta2.png^techage_flywheel4.png^[transformFX]", + backface_culling = false, + animation = { + type = "vertical_frames", + aspect_w = 32, + aspect_h = 32, + length = 0.6, + }, + }, + }, + techage = { + network = techage.Axle, + power_consumption = techage.generator_power_consumption, + }, + + after_place_node = techage.generator_after_place_node, + after_tube_update = techage.generator_after_tube_update, + on_destruct = techage.generator_on_destruct, + after_dig_node = techage.generator_after_dig_node, + + on_timer = node_timer, + on_receive_fields = on_receive_fields, + on_rightclick = on_rightclick, + on_punch = on_punch, + + paramtype2 = "facedir", + groups = {cracky=2, crumbly=2, choppy=2, not_in_creative_inventory=1}, + --diggable = false, + --drop = "techage:flywheel", + on_rotate = screwdriver.disallow, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) diff --git a/steam_engine/gearbox.lua b/steam_engine/gearbox.lua new file mode 100644 index 0000000..658750c --- /dev/null +++ b/steam_engine/gearbox.lua @@ -0,0 +1,100 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA2 Gearbox + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +local POWER_CONSUME = 1 + +local function swap_node(pos, name) + local node = minetest.get_node(pos) + if node.name == name then + return + end + node.name = name + minetest.swap_node(pos, node) +end + +local function turn_on(pos, dir, on) + if on then + swap_node(pos, "techage:gearbox_on") + else + swap_node(pos, "techage:gearbox") + end +end + +minetest.register_node("techage:gearbox", { + description = "TA2 Gearbox", + tiles = {"techage_filling_ta2.png^techage_axle_gearbox.png^techage_frame_ta2.png"}, + techage = { + turn_on = turn_on, + power_consumption = techage.distributor_power_consumption, + network = techage.Axle, + power_consume = POWER_CONSUME, + }, + + after_place_node = techage.distributor_after_place_node, + after_tube_update = techage.distributor_after_tube_update, + on_destruct = techage.distributor_on_destruct, + after_dig_node = techage.distributor_after_dig_node, + + 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, + }, + }, + }, + techage = { + turn_on = turn_on, + power_consumption = techage.distributor_power_consumption, + network = techage.Axle, + power_consume = POWER_CONSUME, + }, + + after_place_node = techage.distributor_after_place_node, + after_tube_update = techage.distributor_after_tube_update, + on_destruct = techage.distributor_on_destruct, + after_dig_node = techage.distributor_after_dig_node, + + paramtype2 = "facedir", + groups = {not_in_creative_inventory=1}, + diggable = false, + on_rotate = screwdriver.disallow, + is_ground_content = false, + sounds = default.node_sound_wood_defaults(), +}) + diff --git a/steam_engine/steam_pipe.lua b/steam_engine/steam_pipe.lua new file mode 100644 index 0000000..b30a788 --- /dev/null +++ b/steam_engine/steam_pipe.lua @@ -0,0 +1,114 @@ +--[[ + + TechAge + ======= + + Copyright (C) 2019 Joachim Stolberg + + LGPLv2.1+ + See LICENSE.txt for more information + + TA2 Steam pipes for the Steam Engine + +]]-- + +-- for lazy programmers +local S = function(pos) if pos then return minetest.pos_to_string(pos) end end +local P = minetest.string_to_pos +local M = minetest.get_meta + +-- Load support for intllib. +local MP = minetest.get_modpath("tubelib2") +local I,_ = dofile(MP.."/intllib.lua") + + +local Pipe = tubelib2.Tube:new({ + dirs_to_check = {1,2,3,4,5,6}, + max_tube_length = 1000, + show_infotext = false, + primary_node_names = {"techage:steam_pipeS", "techage:steam_pipeA"}, + secondary_node_names = {"techage:cylinder", "techage:boiler2"}, + after_place_tube = function(pos, param2, tube_type, num_tubes) + minetest.swap_node(pos, {name = "techage:steam_pipe"..tube_type, param2 = param2}) + end, +}) + +Pipe:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir) + minetest.registered_nodes[node.name].after_tube_update(node, pos, out_dir, peer_pos, peer_in_dir) +end) + +techage.SteamPipe = Pipe + + +minetest.register_node("techage:steam_pipeS", { + description = I("TA2 Steam Pipe"), + tiles = { + "techage_steam_pipe.png^[transformR90", + "techage_steam_pipe.png^[transformR90", + "techage_steam_pipe.png", + "techage_steam_pipe.png", + "techage_steam_hole.png", + "techage_steam_hole.png", + }, + + after_place_node = function(pos, placer, itemstack, pointed_thing) + if not Pipe:after_place_tube(pos, placer, pointed_thing) then + minetest.remove_node(pos) + return true + end + return false + end, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Pipe:after_dig_tube(pos, oldnode) + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-1/8, -1/8, -4/8, 1/8, 1/8, 4/8}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {crumbly=3, cracky=3, snappy=3}, + sounds = default.node_sound_metal_defaults(), +}) + +minetest.register_node("techage:steam_pipeA", { + description = I("TA2 Steam Pipe"), + tiles = { + "techage_steam_knee2.png", + "techage_steam_hole2.png^[transformR180", + "techage_steam_knee.png^[transformR270", + "techage_steam_knee.png", + "techage_steam_knee2.png", + "techage_steam_hole2.png", + }, + + after_dig_node = function(pos, oldnode, oldmetadata, digger) + Pipe:after_dig_tube(pos, oldnode) + end, + + paramtype2 = "facedir", -- important! + drawtype = "nodebox", + node_box = { + type = "fixed", + fixed = { + {-1/8, -4/8, -1/8, 1/8, 1/8, 1/8}, + {-1/8, -1/8, -4/8, 1/8, 1/8, -1/8}, + }, + }, + on_rotate = screwdriver.disallow, -- important! + paramtype = "light", + sunlight_propagates = true, + is_ground_content = false, + groups = {crumbly=3, cracky=3, snappy=3, not_in_creative_inventory=1}, + sounds = default.node_sound_metal_defaults(), + drop = "techage:steam_pipeS", +}) + diff --git a/textures/shrink.py b/textures/shrink.py new file mode 100644 index 0000000..f6c6300 --- /dev/null +++ b/textures/shrink.py @@ -0,0 +1,16 @@ +import os, fnmatch + + +print ">>> Convert" +for filename in os.listdir("./"): + if fnmatch.fnmatch(filename, "*.png"): + print(filename) + os.system("pngquant --skip-if-larger --quality=8-16 --output ./%s.new ./%s" % (filename, filename)) + +print "\n>>> Copy" +for filename in os.listdir("./"): + if fnmatch.fnmatch(filename, "*.new"): + print(filename) + os.remove("./" + filename[:-4]) + os.rename("./" + filename, "./" + filename[:-4]) + diff --git a/textures/techage_appl_arrow.png b/textures/techage_appl_arrow.png new file mode 100644 index 0000000..bfc710e Binary files /dev/null and b/textures/techage_appl_arrow.png differ diff --git a/textures/techage_appl_chest_back_ta3.png b/textures/techage_appl_chest_back_ta3.png new file mode 100644 index 0000000..348152f Binary files /dev/null and b/textures/techage_appl_chest_back_ta3.png differ diff --git a/textures/techage_appl_chest_back_ta4.png b/textures/techage_appl_chest_back_ta4.png new file mode 100644 index 0000000..8331455 Binary files /dev/null and b/textures/techage_appl_chest_back_ta4.png differ diff --git a/textures/techage_appl_chest_front_ta3.png b/textures/techage_appl_chest_front_ta3.png new file mode 100644 index 0000000..071b643 Binary files /dev/null and b/textures/techage_appl_chest_front_ta3.png differ diff --git a/textures/techage_appl_chest_front_ta4.png b/textures/techage_appl_chest_front_ta4.png new file mode 100644 index 0000000..6e2322e Binary files /dev/null and b/textures/techage_appl_chest_front_ta4.png differ diff --git a/textures/techage_appl_compressor.png b/textures/techage_appl_compressor.png new file mode 100644 index 0000000..1d6c586 Binary files /dev/null and b/textures/techage_appl_compressor.png differ diff --git a/textures/techage_appl_compressor4.png b/textures/techage_appl_compressor4.png new file mode 100644 index 0000000..862ec5c Binary files /dev/null and b/textures/techage_appl_compressor4.png differ diff --git a/textures/techage_appl_filler.png b/textures/techage_appl_filler.png new file mode 100644 index 0000000..bc2c433 Binary files /dev/null and b/textures/techage_appl_filler.png differ diff --git a/textures/techage_appl_filler4.png b/textures/techage_appl_filler4.png new file mode 100644 index 0000000..e1c9ee8 Binary files /dev/null and b/textures/techage_appl_filler4.png differ diff --git a/textures/techage_appl_firehole.png b/textures/techage_appl_firehole.png new file mode 100644 index 0000000..66f2566 Binary files /dev/null and b/textures/techage_appl_firehole.png differ diff --git a/textures/techage_appl_firehole4.png b/textures/techage_appl_firehole4.png new file mode 100644 index 0000000..b0ccec3 Binary files /dev/null and b/textures/techage_appl_firehole4.png differ diff --git a/textures/techage_appl_hole_biogas.png b/textures/techage_appl_hole_biogas.png new file mode 100644 index 0000000..4cf887a Binary files /dev/null and b/textures/techage_appl_hole_biogas.png differ diff --git a/textures/techage_appl_hole_tube.png b/textures/techage_appl_hole_tube.png new file mode 100644 index 0000000..6891272 Binary files /dev/null and b/textures/techage_appl_hole_tube.png differ diff --git a/textures/techage_appl_inp.png b/textures/techage_appl_inp.png new file mode 100644 index 0000000..80ceab5 Binary files /dev/null and b/textures/techage_appl_inp.png differ diff --git a/textures/techage_appl_open.png b/textures/techage_appl_open.png new file mode 100644 index 0000000..0643ca1 Binary files /dev/null and b/textures/techage_appl_open.png differ diff --git a/textures/techage_appl_outp.png b/textures/techage_appl_outp.png new file mode 100644 index 0000000..fc42104 Binary files /dev/null and b/textures/techage_appl_outp.png differ diff --git a/textures/techage_appl_sieve4.png b/textures/techage_appl_sieve4.png new file mode 100644 index 0000000..f8cad63 Binary files /dev/null and b/textures/techage_appl_sieve4.png differ diff --git a/textures/techage_appl_warehouse.png b/textures/techage_appl_warehouse.png new file mode 100644 index 0000000..4645541 Binary files /dev/null and b/textures/techage_appl_warehouse.png differ diff --git a/textures/techage_axle.png b/textures/techage_axle.png new file mode 100644 index 0000000..0f5d8f7 Binary files /dev/null and b/textures/techage_axle.png differ diff --git a/textures/techage_axle4.png b/textures/techage_axle4.png new file mode 100644 index 0000000..8bcdab8 Binary files /dev/null and b/textures/techage_axle4.png differ diff --git a/textures/techage_axle4R.png b/textures/techage_axle4R.png new file mode 100644 index 0000000..33af2a6 Binary files /dev/null and b/textures/techage_axle4R.png differ diff --git a/textures/techage_axleR.png b/textures/techage_axleR.png new file mode 100644 index 0000000..0ae48e4 Binary files /dev/null and b/textures/techage_axleR.png differ diff --git a/textures/techage_axle_clutch.png b/textures/techage_axle_clutch.png new file mode 100644 index 0000000..994d3d3 Binary files /dev/null and b/textures/techage_axle_clutch.png differ diff --git a/textures/techage_axle_clutch4.png b/textures/techage_axle_clutch4.png new file mode 100644 index 0000000..c5c7e59 Binary files /dev/null and b/textures/techage_axle_clutch4.png differ diff --git a/textures/techage_axle_gearbox.png b/textures/techage_axle_gearbox.png new file mode 100644 index 0000000..4221a99 Binary files /dev/null and b/textures/techage_axle_gearbox.png differ diff --git a/textures/techage_axle_gearbox4.png b/textures/techage_axle_gearbox4.png new file mode 100644 index 0000000..8846cd4 Binary files /dev/null and b/textures/techage_axle_gearbox4.png differ diff --git a/textures/techage_biomass.png b/textures/techage_biomass.png new file mode 100644 index 0000000..f7fd217 Binary files /dev/null and b/textures/techage_biomass.png differ diff --git a/textures/techage_boiler.png b/textures/techage_boiler.png new file mode 100644 index 0000000..ad30c08 Binary files /dev/null and b/textures/techage_boiler.png differ diff --git a/textures/techage_boiler2.png b/textures/techage_boiler2.png new file mode 100644 index 0000000..3aaea83 Binary files /dev/null and b/textures/techage_boiler2.png differ diff --git a/textures/techage_box_back.png b/textures/techage_box_back.png new file mode 100644 index 0000000..067d9e6 Binary files /dev/null and b/textures/techage_box_back.png differ diff --git a/textures/techage_box_front.png b/textures/techage_box_front.png new file mode 100644 index 0000000..fc1cef2 Binary files /dev/null and b/textures/techage_box_front.png differ diff --git a/textures/techage_box_side.png b/textures/techage_box_side.png new file mode 100644 index 0000000..f80246f Binary files /dev/null and b/textures/techage_box_side.png differ diff --git a/textures/techage_cylinder.png b/textures/techage_cylinder.png new file mode 100644 index 0000000..09ad565 Binary files /dev/null and b/textures/techage_cylinder.png differ diff --git a/textures/techage_cylinder4.png b/textures/techage_cylinder4.png new file mode 100644 index 0000000..2ef66b6 Binary files /dev/null and b/textures/techage_cylinder4.png differ diff --git a/textures/techage_electric_button.png b/textures/techage_electric_button.png new file mode 100644 index 0000000..a3e6959 Binary files /dev/null and b/textures/techage_electric_button.png differ diff --git a/textures/techage_electric_button_off.png b/textures/techage_electric_button_off.png new file mode 100644 index 0000000..7517f85 Binary files /dev/null and b/textures/techage_electric_button_off.png differ diff --git a/textures/techage_electric_button_on.png b/textures/techage_electric_button_on.png new file mode 100644 index 0000000..5289658 Binary files /dev/null and b/textures/techage_electric_button_on.png differ diff --git a/textures/techage_electric_cable.png b/textures/techage_electric_cable.png new file mode 100644 index 0000000..de4f505 Binary files /dev/null and b/textures/techage_electric_cable.png differ diff --git a/textures/techage_electric_cable_end.png b/textures/techage_electric_cable_end.png new file mode 100644 index 0000000..bb92b04 Binary files /dev/null and b/textures/techage_electric_cable_end.png differ diff --git a/textures/techage_electric_junction.png b/textures/techage_electric_junction.png new file mode 100644 index 0000000..78f8596 Binary files /dev/null and b/textures/techage_electric_junction.png differ diff --git a/textures/techage_electric_plug.png b/textures/techage_electric_plug.png new file mode 100644 index 0000000..dc3e0a4 Binary files /dev/null and b/textures/techage_electric_plug.png differ diff --git a/textures/techage_electric_power.png b/textures/techage_electric_power.png new file mode 100644 index 0000000..f5a7a2b Binary files /dev/null and b/textures/techage_electric_power.png differ diff --git a/textures/techage_electric_trowel.png b/textures/techage_electric_trowel.png new file mode 100644 index 0000000..c69e3ca Binary files /dev/null and b/textures/techage_electric_trowel.png differ diff --git a/textures/techage_fermenter.png b/textures/techage_fermenter.png new file mode 100644 index 0000000..a74d409 Binary files /dev/null and b/textures/techage_fermenter.png differ diff --git a/textures/techage_fermenter_foil.png b/textures/techage_fermenter_foil.png new file mode 100644 index 0000000..c867ab1 Binary files /dev/null and b/textures/techage_fermenter_foil.png differ diff --git a/textures/techage_filling4_ta2.png b/textures/techage_filling4_ta2.png new file mode 100644 index 0000000..14beb40 Binary files /dev/null and b/textures/techage_filling4_ta2.png differ diff --git a/textures/techage_filling4_ta3.png b/textures/techage_filling4_ta3.png new file mode 100644 index 0000000..96ed62d Binary files /dev/null and b/textures/techage_filling4_ta3.png differ diff --git a/textures/techage_filling4_ta4.png b/textures/techage_filling4_ta4.png new file mode 100644 index 0000000..ace57ec Binary files /dev/null and b/textures/techage_filling4_ta4.png differ diff --git a/textures/techage_filling_ta2.png b/textures/techage_filling_ta2.png new file mode 100644 index 0000000..38ab367 Binary files /dev/null and b/textures/techage_filling_ta2.png differ diff --git a/textures/techage_filling_ta3.png b/textures/techage_filling_ta3.png new file mode 100644 index 0000000..631d424 Binary files /dev/null and b/textures/techage_filling_ta3.png differ diff --git a/textures/techage_filling_ta4.png b/textures/techage_filling_ta4.png new file mode 100644 index 0000000..73234bc Binary files /dev/null and b/textures/techage_filling_ta4.png differ diff --git a/textures/techage_filter_inv.png b/textures/techage_filter_inv.png new file mode 100644 index 0000000..7b99c93 Binary files /dev/null and b/textures/techage_filter_inv.png differ diff --git a/textures/techage_firebox.png b/textures/techage_firebox.png new file mode 100644 index 0000000..f57de4c Binary files /dev/null and b/textures/techage_firebox.png differ diff --git a/textures/techage_firebox4.png b/textures/techage_firebox4.png new file mode 100644 index 0000000..31dc55f Binary files /dev/null and b/textures/techage_firebox4.png differ diff --git a/textures/techage_flame_animated.png b/textures/techage_flame_animated.png new file mode 100644 index 0000000..05ef6f6 Binary files /dev/null and b/textures/techage_flame_animated.png differ diff --git a/textures/techage_flywheel.png b/textures/techage_flywheel.png new file mode 100644 index 0000000..840651a Binary files /dev/null and b/textures/techage_flywheel.png differ diff --git a/textures/techage_flywheel4.png b/textures/techage_flywheel4.png new file mode 100644 index 0000000..8d8c15a Binary files /dev/null and b/textures/techage_flywheel4.png differ diff --git a/textures/techage_form_arrow.png b/textures/techage_form_arrow.png new file mode 100644 index 0000000..e2fdcb8 Binary files /dev/null and b/textures/techage_form_arrow.png differ diff --git a/textures/techage_form_gear_bg.png b/textures/techage_form_gear_bg.png new file mode 100644 index 0000000..ce2bc64 Binary files /dev/null and b/textures/techage_form_gear_bg.png differ diff --git a/textures/techage_form_gear_fg.png b/textures/techage_form_gear_fg.png new file mode 100644 index 0000000..b7a762a Binary files /dev/null and b/textures/techage_form_gear_fg.png differ diff --git a/textures/techage_form_input.png b/textures/techage_form_input.png new file mode 100644 index 0000000..e0df9fe Binary files /dev/null and b/textures/techage_form_input.png differ diff --git a/textures/techage_form_inventory.png b/textures/techage_form_inventory.png new file mode 100644 index 0000000..721a860 Binary files /dev/null and b/textures/techage_form_inventory.png differ diff --git a/textures/techage_form_level_bg.png b/textures/techage_form_level_bg.png new file mode 100644 index 0000000..f77ee21 Binary files /dev/null and b/textures/techage_form_level_bg.png differ diff --git a/textures/techage_form_level_fg.png b/textures/techage_form_level_fg.png new file mode 100644 index 0000000..f5969b1 Binary files /dev/null and b/textures/techage_form_level_fg.png differ diff --git a/textures/techage_form_mask.png b/textures/techage_form_mask.png new file mode 100644 index 0000000..5091649 Binary files /dev/null and b/textures/techage_form_mask.png differ diff --git a/textures/techage_form_temp_bg.png b/textures/techage_form_temp_bg.png new file mode 100644 index 0000000..c252965 Binary files /dev/null and b/textures/techage_form_temp_bg.png differ diff --git a/textures/techage_form_temp_fg.png b/textures/techage_form_temp_fg.png new file mode 100644 index 0000000..8c30d1c Binary files /dev/null and b/textures/techage_form_temp_fg.png differ diff --git a/textures/techage_frame14_ta2.png b/textures/techage_frame14_ta2.png new file mode 100644 index 0000000..6764db4 Binary files /dev/null and b/textures/techage_frame14_ta2.png differ diff --git a/textures/techage_frame14_ta3.png b/textures/techage_frame14_ta3.png new file mode 100644 index 0000000..315b124 Binary files /dev/null and b/textures/techage_frame14_ta3.png differ diff --git a/textures/techage_frame14_ta4.png b/textures/techage_frame14_ta4.png new file mode 100644 index 0000000..be5f397 Binary files /dev/null and b/textures/techage_frame14_ta4.png differ diff --git a/textures/techage_frame4_ta2.png b/textures/techage_frame4_ta2.png new file mode 100644 index 0000000..1100752 Binary files /dev/null and b/textures/techage_frame4_ta2.png differ diff --git a/textures/techage_frame4_ta3.png b/textures/techage_frame4_ta3.png new file mode 100644 index 0000000..e0866fd Binary files /dev/null and b/textures/techage_frame4_ta3.png differ diff --git a/textures/techage_frame4_ta4.png b/textures/techage_frame4_ta4.png new file mode 100644 index 0000000..e4ba63e Binary files /dev/null and b/textures/techage_frame4_ta4.png differ diff --git a/textures/techage_frame_ta2.png b/textures/techage_frame_ta2.png new file mode 100644 index 0000000..593a37a Binary files /dev/null and b/textures/techage_frame_ta2.png differ diff --git a/textures/techage_frame_ta3.png b/textures/techage_frame_ta3.png new file mode 100644 index 0000000..2b2e58b Binary files /dev/null and b/textures/techage_frame_ta3.png differ diff --git a/textures/techage_frame_ta4.png b/textures/techage_frame_ta4.png new file mode 100644 index 0000000..26f47dd Binary files /dev/null and b/textures/techage_frame_ta4.png differ diff --git a/textures/techage_gasflare.png b/textures/techage_gasflare.png new file mode 100644 index 0000000..b9d9609 Binary files /dev/null and b/textures/techage_gasflare.png differ diff --git a/textures/techage_gaspipe.png b/textures/techage_gaspipe.png new file mode 100644 index 0000000..0911f2f Binary files /dev/null and b/textures/techage_gaspipe.png differ diff --git a/textures/techage_gaspipe_hole2.png b/textures/techage_gaspipe_hole2.png new file mode 100644 index 0000000..42e663b Binary files /dev/null and b/textures/techage_gaspipe_hole2.png differ diff --git a/textures/techage_gaspipe_junction.png b/textures/techage_gaspipe_junction.png new file mode 100644 index 0000000..e347ed5 Binary files /dev/null and b/textures/techage_gaspipe_junction.png differ diff --git a/textures/techage_gaspipe_knee.png b/textures/techage_gaspipe_knee.png new file mode 100644 index 0000000..5d4077d Binary files /dev/null and b/textures/techage_gaspipe_knee.png differ diff --git a/textures/techage_gaspipe_knee2.png b/textures/techage_gaspipe_knee2.png new file mode 100644 index 0000000..68abdf5 Binary files /dev/null and b/textures/techage_gaspipe_knee2.png differ diff --git a/textures/techage_pusher14.png b/textures/techage_pusher14.png new file mode 100644 index 0000000..b0ad092 Binary files /dev/null and b/textures/techage_pusher14.png differ diff --git a/textures/techage_steam_hole.png b/textures/techage_steam_hole.png new file mode 100644 index 0000000..4946dac Binary files /dev/null and b/textures/techage_steam_hole.png differ diff --git a/textures/techage_steam_hole2.png b/textures/techage_steam_hole2.png new file mode 100644 index 0000000..d6bfd5a Binary files /dev/null and b/textures/techage_steam_hole2.png differ diff --git a/textures/techage_steam_knee.png b/textures/techage_steam_knee.png new file mode 100644 index 0000000..1a7a6e1 Binary files /dev/null and b/textures/techage_steam_knee.png differ diff --git a/textures/techage_steam_knee2.png b/textures/techage_steam_knee2.png new file mode 100644 index 0000000..e1813f5 Binary files /dev/null and b/textures/techage_steam_knee2.png differ diff --git a/textures/techage_steam_pipe.png b/textures/techage_steam_pipe.png new file mode 100644 index 0000000..e3eb641 Binary files /dev/null and b/textures/techage_steam_pipe.png differ diff --git a/textures/techage_top_ta4.png b/textures/techage_top_ta4.png new file mode 100644 index 0000000..8a3f38a Binary files /dev/null and b/textures/techage_top_ta4.png differ diff --git a/textures/techage_trowel.png b/textures/techage_trowel.png new file mode 100644 index 0000000..c69e3ca Binary files /dev/null and b/textures/techage_trowel.png differ diff --git a/textures/tubelib_distributor_active_frame.png b/textures/tubelib_distributor_active_frame.png new file mode 100644 index 0000000..23fc309 Binary files /dev/null and b/textures/tubelib_distributor_active_frame.png differ