133 lines
3.6 KiB
Lua
133 lines
3.6 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local error, math, minetest, nodecore, pairs, type
|
|
= error, math, minetest, nodecore, pairs, type
|
|
local math_floor, math_sqrt
|
|
= math.floor, math.sqrt
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local function metaclear(meta, def)
|
|
local tbl = meta:to_table()
|
|
if not (tbl.fields[def.fieldname .. "qty"] or
|
|
tbl.fields[def.fieldname .. "time"]) then return end
|
|
tbl.fields[def.fieldname .. "qty"] = nil
|
|
tbl.fields[def.fieldname .. "time"] = nil
|
|
meta:from_table(tbl)
|
|
end
|
|
|
|
local function soaking_core(def, reg, getmeta)
|
|
if not def.fieldname then error("soaking def missing fieldname") end
|
|
|
|
def.soakinterval = def.soakinterval or ((def.interval or 1) * (def.chance or 1))
|
|
|
|
if not def.soakrate then error("soaking missing soakrate callback") end
|
|
if not def.soakcheck then error("soaking missing soakcheck callback") end
|
|
|
|
def.soakvary = def.soakvary or 0.25
|
|
if not def.soakrand then
|
|
if def.soakvary then
|
|
def.soakrand = function(rate, ticks)
|
|
return rate * (1 + def.soakvary * nodecore.boxmuller()
|
|
/ math_sqrt(ticks)) * ticks
|
|
end
|
|
else
|
|
def.soakrand = function(rate, ticks) return rate * ticks end
|
|
end
|
|
end
|
|
|
|
local rateadj = nodecore.rate_adjustment("speed", "soaking", def.label)
|
|
def.action = function(...)
|
|
local now = nodecore.gametime
|
|
|
|
local meta = getmeta(...)
|
|
local total = meta:get_float(def.fieldname .. "qty") or 0
|
|
local start = meta:get_float(def.fieldname .. "time")
|
|
start = start and start ~= 0 and start or now
|
|
|
|
local rate = 0
|
|
local delta = 0
|
|
if start <= now then
|
|
rate = def.soakrate(...)
|
|
if rate == false then
|
|
metaclear(meta, def)
|
|
return ...
|
|
end
|
|
rate = rate or 0
|
|
local ticks = 1 + math_floor((now - start) / def.soakinterval)
|
|
delta = def.soakrand(rate, ticks)
|
|
total = total + delta * rateadj
|
|
start = start + ticks * def.soakinterval
|
|
end
|
|
|
|
local function helper(set, ...)
|
|
if set == false then
|
|
metaclear(meta, def)
|
|
return ...
|
|
end
|
|
meta:set_float(def.fieldname .. "qty",
|
|
set and type(set) == "number" and set or total)
|
|
meta:set_float(def.fieldname .. "time", start)
|
|
return ...
|
|
end
|
|
return helper(def.soakcheck({
|
|
rate = rate,
|
|
delta = delta,
|
|
total = total
|
|
}, ...))
|
|
end
|
|
|
|
return reg(def)
|
|
end
|
|
|
|
local soaking_abm_by_fieldname = {}
|
|
function nodecore.register_soaking_abm(def)
|
|
soaking_abm_by_fieldname[def.fieldname] = def
|
|
return soaking_core(def,
|
|
nodecore.register_limited_abm,
|
|
function(pos) return minetest.get_meta(pos) end
|
|
)
|
|
end
|
|
function nodecore.register_soaking_aism(def)
|
|
return soaking_core(def,
|
|
nodecore.register_aism,
|
|
function(stack) return stack:get_meta() end
|
|
)
|
|
end
|
|
|
|
local pending
|
|
function nodecore.soaking_abm_push(pos, fieldname, qty)
|
|
local abm = soaking_abm_by_fieldname[fieldname]
|
|
if not abm then return end
|
|
|
|
local node = minetest.get_node(pos)
|
|
|
|
local found
|
|
for _, v in pairs(abm.nodenames or {}) do
|
|
if node.name == v then
|
|
found = true
|
|
elseif v:sub(1, 6) == "group:" then
|
|
found = found or minetest.get_item_group(node.name, v:sub(7)) ~= 0
|
|
end
|
|
end
|
|
if not found then return end
|
|
|
|
local meta = minetest.get_meta(pos)
|
|
local qf = fieldname .. "qty"
|
|
meta:set_float(qf, (meta:get_float(qf) or 0) + qty)
|
|
local tf = fieldname .. "time"
|
|
if (meta:get_float(tf) or 0) == 0 then meta:set_float(tf, nodecore.gametime) end
|
|
|
|
local func = abm.limited_action or abm.action
|
|
if pending then
|
|
pending[#pending + 1] = function() return func(pos, node) end
|
|
else
|
|
pending = {}
|
|
func(pos, node)
|
|
while #pending > 0 do
|
|
local batch = pending
|
|
pending = {}
|
|
for _, f in pairs(batch) do f() end
|
|
end
|
|
pending = nil
|
|
end
|
|
end
|