2018-12-22 16:58:11 +01:00
|
|
|
--[[
|
|
|
|
|
|
|
|
Tube Library
|
|
|
|
============
|
|
|
|
|
2018-12-27 22:37:13 +01:00
|
|
|
Copyright (C) 2017-2019 Joachim Stolberg
|
2018-12-22 16:58:11 +01:00
|
|
|
|
|
|
|
LGPLv2.1+
|
|
|
|
See LICENSE.txt for more information
|
|
|
|
|
|
|
|
node_states.lua:
|
|
|
|
|
|
|
|
A state model/class for tubelib 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 "0123"
|
|
|
|
"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
|
|
|
|
|
|
|
|
|
|
|
|
local AGING_FACTOR = 3 -- defect random factor
|
|
|
|
|
|
|
|
--
|
|
|
|
-- Local States
|
|
|
|
--
|
|
|
|
local STOPPED = tubelib.STOPPED
|
|
|
|
local RUNNING = tubelib.RUNNING
|
|
|
|
local STANDBY = tubelib.STANDBY
|
|
|
|
local FAULT = tubelib.FAULT
|
|
|
|
local BLOCKED = tubelib.BLOCKED
|
|
|
|
local DEFECT = tubelib.DEFECT
|
|
|
|
|
|
|
|
|
|
|
|
--
|
|
|
|
-- NodeStates Class Functions
|
|
|
|
--
|
|
|
|
tubelib.NodeStates = {}
|
|
|
|
local NodeStates = tubelib.NodeStates
|
|
|
|
|
2018-12-24 14:31:13 +01:00
|
|
|
local function start_condition_fullfilled(pos, meta)
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2018-12-22 16:58:11 +01:00
|
|
|
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,
|
2018-12-24 14:31:13 +01:00
|
|
|
start_condition_fullfilled = attr.start_condition_fullfilled or start_condition_fullfilled,
|
2018-12-28 17:16:14 +01:00
|
|
|
on_start = attr.on_start,
|
|
|
|
on_stop = attr.on_stop,
|
2018-12-22 16:58:11 +01:00
|
|
|
}
|
|
|
|
if attr.aging_factor then
|
2018-12-23 19:36:11 +01:00
|
|
|
o.aging_level1 = attr.aging_factor * tubelib.machine_aging_value
|
|
|
|
o.aging_level2 = attr.aging_factor * tubelib.machine_aging_value * AGING_FACTOR
|
2018-12-22 16:58:11 +01:00
|
|
|
end
|
|
|
|
setmetatable(o, self)
|
|
|
|
self.__index = self
|
|
|
|
return o
|
|
|
|
end
|
|
|
|
|
|
|
|
function NodeStates:register_formspec_func(func)
|
|
|
|
self.formspec_func = func
|
|
|
|
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(pos, meta))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
function NodeStates:stop(pos, meta)
|
2018-12-28 17:16:14 +01:00
|
|
|
local state = meta:get_int("tubelib_state")
|
|
|
|
if state ~= DEFECT then
|
|
|
|
if self.on_stop then
|
|
|
|
self.on_stop(pos, meta, state)
|
|
|
|
end
|
2018-12-22 16:58:11 +01:00
|
|
|
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(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
|
2018-12-24 14:31:13 +01:00
|
|
|
if not self.start_condition_fullfilled(pos, meta) then
|
|
|
|
return false
|
|
|
|
end
|
2018-12-28 17:16:14 +01:00
|
|
|
if self.on_start then
|
|
|
|
self.on_start(pos, meta, state)
|
|
|
|
end
|
2018-12-22 16:58:11 +01:00
|
|
|
meta:set_int("tubelib_state", RUNNING)
|
|
|
|
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(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(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(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(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(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 tubelib.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
|
2018-12-25 22:06:05 +01:00
|
|
|
function NodeStates:keep_running(pos, meta, val, num_items)
|
2018-12-29 22:07:23 +01:00
|
|
|
num_items = num_items or 1
|
2018-12-22 16:58:11 +01:00
|
|
|
-- set to RUNNING if not already done
|
|
|
|
self:start(pos, meta, true)
|
|
|
|
meta:set_int("tubelib_countdown", val)
|
2018-12-25 22:06:05 +01:00
|
|
|
meta:set_int("tubelib_item_meter", meta:get_int("tubelib_item_meter") + (num_items or 1))
|
2018-12-22 16:58:11 +01:00
|
|
|
if self.aging_level1 then
|
2018-12-29 22:07:23 +01:00
|
|
|
local cnt = meta:get_int("tubelib_aging") + num_items
|
2018-12-22 16:58:11 +01:00
|
|
|
meta:set_int("tubelib_aging", cnt)
|
2018-12-29 22:07:23 +01:00
|
|
|
if cnt > (self.aging_level1) and math.random(self.aging_level2/num_items) == 1 then
|
2018-12-22 16:58:11 +01:00
|
|
|
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 tubelib.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)
|
|
|
|
local meta = minetest.get_meta(pos)
|
|
|
|
|
|
|
|
-- legacy node number/state/counter?
|
|
|
|
local number = meta:get_string("number")
|
|
|
|
if number ~= "" then
|
|
|
|
meta:set_string("tubelib_number", number)
|
|
|
|
meta:set_int("tubelib_state", tubelib.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 = tubelib.add_node(pos, self.node_name_passive)
|
|
|
|
meta:set_string("tubelib_number", number)
|
|
|
|
else
|
|
|
|
local info = tubelib.get_node_info(number)
|
|
|
|
if not info or info.pos ~= pos then
|
|
|
|
number = tubelib.add_node(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 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
|
2018-12-28 22:16:55 +01:00
|
|
|
|
|
|
|
if self.formspec_func then
|
|
|
|
meta:set_string("formspec", self.formspec_func(pos, meta))
|
|
|
|
end
|
2018-12-22 16:58:11 +01:00
|
|
|
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(pos, meta))
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2018-12-23 19:36:11 +01:00
|
|
|
-- Return working or defect machine, depending on machine lifetime
|
2018-12-22 16:58:11 +01:00
|
|
|
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
|