Aaron Suen 5b0459e4bb Speed adjustment settings for many long-running processes.
Many processes that have a significant speed/time component can
now have speeds adjusted (as a multiplier from base rate) via
settings.

Things that can be adjusted:
- Tool speeds (including digging and pummeling)
- Cooking and pummel recipe durations
- Soaking processes like tree growth, peat fermentation

The settings are hierarchical, so groups of rates can be
adjusted together, and a further multiplier can be applied to
each member of the group.

The settings are calculated dynamically, for power users only,
and documenting them is out of scope for the project.

Specifically, this should help tuning for Kimapr's SkyBlock, and
possibly other mods involving signficant gameplay rebalancing.
2019-12-06 07:01:12 -05:00

180 lines
5.3 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, ipairs, minetest, nodecore, pairs, type
= ItemStack, ipairs, minetest, nodecore, pairs, type
-- LUALOCALS > ---------------------------------------------------------
local function addgroups(sum, pos)
local node = minetest.get_node(pos)
local def = minetest.registered_items[node.name] or {}
if not def.groups then return end
for k, v in pairs(def.groups) do
sum[k] = (sum[k] or 0) + v
end
end
local function craftcheck(recipe, pos, node, data, xx, xz, zx, zz)
local function rel(x, y, z)
return {
x = pos.x + xx * x + zx * z,
y = pos.y + y,
z = pos.z + xz * x + zz * z
}
end
data.rel = rel
data.wield = ItemStack(data.wield or data.crafter and data.crafter:get_wielded_item())
if recipe.check and not recipe.check(pos, data) then return end
if recipe.wield and (not data.wield or not nodecore.match(
{stack = data.wield}, recipe.wield)) then return end
if recipe.normal then
if data.pointed.type ~= "node" or
recipe.normal.y ~= data.pointed.above.y - data.pointed.under.y then return end
local rx = recipe.normal.x * xx + recipe.normal.z * zx
if rx ~= data.pointed.above.x - data.pointed.under.x then return end
local rz = recipe.normal.x * xz + recipe.normal.z * zz
if rz ~= data.pointed.above.z - data.pointed.under.z then return end
end
for _, v in pairs(recipe.nodes) do
if v ~= recipe.root and v.match then
local p = rel(v.x, v.y, v.z)
if not nodecore.match(p, v.match) then return end
end
end
if recipe.touchgroups then
local sum = {}
addgroups(sum, rel(1, 0, 0))
addgroups(sum, rel(-1, 0, 0))
addgroups(sum, rel(0, 1, 0))
addgroups(sum, rel(0, -1, 0))
addgroups(sum, rel(0, 0, 1))
addgroups(sum, rel(0, 0, -1))
for k, v in pairs(recipe.touchgroups) do
local w = sum[k] or 0
if v > 0 and w < v then return end
if v <= 0 and w > -v then return end
end
end
local mindur = recipe.duration or 0
if type(mindur) == "function" then mindur = mindur(recipe, pos, node, data) end
if recipe.toolgroups then
if not data.wield then return end
local dg = data.wield:get_tool_capabilities().groupcaps
local t
for gn, lv in pairs(recipe.toolgroups) do
local gt = dg[gn]
gt = gt and gt.times
gt = gt and gt[lv]
if gt and (not t or t > gt) then t = gt end
end
if not t then return end
mindur = mindur + t
end
mindur = mindur / recipe.rate_adjust
if mindur > 0 then
if not data.duration then return end
local dur = data.duration
if type(dur) == "function" then dur = dur(pos, data) end
if not dur or dur < mindur then
if data.inprogress then data.inprogress(pos, data) end
return 1
end
end
if data.before then data.before(pos, data) end
if recipe.before then recipe.before(pos, data) end
for _, v in ipairs(recipe.nodes) do
if v.replace then
local p = rel(v.x, v.y, v.z)
local r = v.replace
while type(r) == "function" do
r = r(p, v)
end
if r and type(r) == "string" then
r = {name = r}
end
if v.match.excess then
local s = nodecore.stack_get(p)
local x = s:get_count() - (v.match.count or 1)
if x > 0 then
s:set_count(x)
nodecore.item_eject(p, s, 0.001)
end
nodecore.stack_set(p, ItemStack(""))
end
if r then
local n = minetest.get_node(p)
r.param2 = n.param2
minetest.set_node(p, r)
nodecore.node_sound(p, "place")
nodecore.fallcheck(p)
end
end
end
if recipe.items then
for _, v in pairs(recipe.items) do
nodecore.item_eject(rel(v.x or 0, v.y or 0, v.z or 0),
v.name, v.scatter, v.count, v.velocity)
end
end
if recipe.consumewield then
nodecore.consume_wield(data.crafter, recipe.consumewield)
elseif recipe.toolgroups and recipe.toolwear and data.crafter then
nodecore.wear_wield(data.crafter, recipe.toolgroups, recipe.toolwear)
end
if recipe.after then recipe.after(pos, data) end
if data.after then data.after(pos, data) end
if nodecore.player_stat_add then
nodecore.player_stat_add(1, data.crafter, "craft", recipe.label)
end
if recipe.witness then
local lut = {}
for _, v in pairs(recipe.nodes) do
lut[minetest.pos_to_string(v)] = true
end
nodecore.witness(pos, recipe.label,
type(recipe.witness) == "number" and recipe.witness or nil,
function(p) return lut[minetest.pos_to_string(p)] end
)
end
minetest.log((data.crafter and data.crafter:get_player_name() or "unknown")
.. " completed recipe \"" .. recipe.label .. "\" at " ..
minetest.pos_to_string(pos) .. " upon " .. node.name)
return true
end
local function tryall(rc, pos, node, data)
local function go(xx, xz, zx, zz)
return craftcheck(rc, pos, node, data, xx, xz, zx, zz)
end
local r = go(1, 0, 0, 1)
if r then return r end
if not rc.norotate then
r = go(0, -1, 1, 0)
or go(-1, 0, 0, -1)
or go(0, 1, -1, 0)
if r then return r end
if not rc.nomirror then
r = go(-1, 0, 0, 1)
or go(0, 1, 1, 0)
or go(1, 0, 0, -1)
or go(0, -1, -1, 0)
end
end
return r
end
function nodecore.craft_check(pos, node, data)
data = data or {}
node.x = pos.x
node.y = pos.y
node.z = pos.z
data.pos = pos
data.node = node
for _, rc in ipairs(nodecore.craft_recipes) do
if data.action == rc.action
and nodecore.match(node, rc.root.match) then
data.recipe = rc
local r = tryall(rc, pos, node, data)
if r then return r == true end
end
end
end