Aaron Suen 8a74b34907 Lantern rebalancing and nonlinear charge response
Using Oblomov's proposed sinusoidal brightness profile, making
the bright and dim extremes of the lantern's range shorter, and
stretching the middle.  Also nerfed the total useful lifetime
of lanterns since we no longer have to inflate it by as large of
a "useless" range at the bottom end.
2022-10-28 07:48:11 -04:00

113 lines
3.3 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, string
= math, minetest, nodecore, string
local math_asin, math_ceil, math_pi, math_sin, string_format
= math.asin, math.ceil, math.pi, math.sin, string.format
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local discharge_rate = 20
local max_charge = discharge_rate * 2400
local function charge_to_level(charge)
local i = math_ceil(4 * (1 + 2 * math_asin(charge * 2 / max_charge - 1) / math_pi))
return i > 7 and 7 or i
end
local function level_to_charge(level)
return (math_sin((level / 4 - 1) / 2 * math_pi) / 2 + 0.5) * max_charge
end
local fluid_water = nodecore.group_expand("group:water", true)
local fluid_flux = nodecore.group_expand("group:lux_fluid", true)
local function influid(pos)
local n = minetest.get_node(pos).name
if fluid_water[n] then return fluid_water end
if fluid_flux[n] then return fluid_flux end
pos = {x = pos.x, y = pos.y + 1, z = pos.z}
n = minetest.get_node(pos).name
if fluid_water[n] then return fluid_water end
if fluid_flux[n] then return fluid_flux end
end
local function floateq(a, b)
if not (a and b) then return true end
if a == 0 and b == 0 then return a == b end
local ratio = a / b
return ratio > 0.999 and ratio < 1.001
end
local function floatrpt(a, b)
if floateq(a, b) then return string_format("%0.2f", a) end
return string_format("%0.2f -> %0.2f", a, b)
end
local function getlevel(name)
return minetest.get_item_group(name, modname) - 1
end
local function chargecalc(pos, oldname, meta)
local fluid = influid(pos)
local rate = (nodecore.lux_soak_rate(pos) or 0)
- discharge_rate * ((fluid == fluid_water) and 2 or 1)
if rate < 0 and fluid == fluid_flux then rate = 0 end
local oldrate = meta:get_float("rate")
local oldqty = meta:get_float("qty")
local oldtime = meta:get_float("time")
local now = nodecore.gametime
local qty = (oldtime == 0)
and level_to_charge(getlevel(oldname))
or (oldqty + oldrate * (now - oldtime))
if qty <= 0 then
qty = 0
if rate < 0 then rate = 0 end
end
if qty >= max_charge then
qty = max_charge
if rate > 0 then rate = 0 end
end
local level = charge_to_level(qty)
local name = modname .. ":lamp" .. level
if name == oldname and floateq(rate, oldrate) then return end
nodecore.log("action", string_format("lantern level %s rate %s charge %s at %s",
floatrpt(getlevel(oldname), level),
floatrpt(oldrate, rate), floatrpt(oldqty, qty),
minetest.pos_to_string(pos, 0)))
meta:set_float("rate", rate)
meta:set_float("qty", qty)
meta:set_float("time", now)
return name
end
nodecore.register_aism({
label = "lantern charge",
interval = 2,
arealoaded = 14,
itemnames = "group:" .. modname,
action = function(stack, data)
local pos = data.pos or data.player and data.player:get_pos()
local name = chargecalc(pos, stack:get_name(), stack:get_meta())
if name then
stack:set_name(name)
return stack
end
end
})
nodecore.register_abm({
label = "lantern charge",
interval = 2,
arealoaded = 14,
nodenames = {"group:" .. modname},
action = function(pos, node)
local name = chargecalc(pos, node.name, minetest.get_meta(pos))
if name then
node.name = name
return minetest.swap_node(pos, node)
end
end
})