Aaron Suen 334946fcdd Increase effect of player wield light leading
When the player is moving forward, take autorun
speed into account for distance to lead, to better
counteract server latency and let players run at higher
speeds.  When standing still, still extend the light in
the player's looking direction a little bit, to get more
favorable "rounding".
2022-10-19 22:36:51 -04:00

232 lines
6.6 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, minetest, nodecore, pairs, setmetatable, vector
= ItemStack, minetest, nodecore, pairs, setmetatable, vector
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
-- Register nodes that can be replaced by dynamic lights
local canreplace = {air = 0}
local true_airlike = {
drawtype = "airlike",
pointable = false,
walkable = false,
climbable = false,
buildable_to = true,
floodable = true,
air_equivalent = true,
paramtype = "light",
light_source = 0,
sunlight_propagates = true,
}
minetest.after(0, function()
for k, v in pairs(minetest.registered_nodes) do
local ok = not canreplace[k]
for dk, dv in pairs(true_airlike) do
ok = ok and v[dk] == dv
end
if ok then canreplace[k] = 0 end
end
end)
-- API for checking if dynamic lights are valid
local ttl = 0.25
local active_lights = {}
local function setup_light(pos, check)
active_lights[minetest.hash_node_position(pos)] = {
exp = nodecore.gametime + ttl,
check = check
}
nodecore.dnt_set(pos, modname .. ":dynalight_check")
end
local function check_light(pos)
local data = active_lights[minetest.hash_node_position(pos)]
if not data then return minetest.remove_node(pos) end
if nodecore.gametime < data.exp then return end
if data.check and data.check() then
data.exp = nodecore.gametime + ttl
nodecore.dnt_set(pos, modname .. ":dynalight_check")
return
end
minetest.remove_node(pos)
return true
end
nodecore.register_dnt({
name = modname .. ":dynalight_check",
nodenames = {"group:dynamic_light"},
ignore_stasis = true,
time = ttl,
autostart = true,
autostart_time = 0,
action = check_light
})
-- Register dynamic light nodes
local function dynamic_light_node(level) return modname .. ":light" .. level end
nodecore.dynamic_light_node = dynamic_light_node
for level = 1, nodecore.light_sun - 1 do
local name = dynamic_light_node(level)
local def = {
description = minetest.registered_nodes.air.description,
light_source = level,
air_equivalent = true,
groups = {dynamic_light = level}
}
for k, v in pairs(true_airlike) do def[k] = def[k] or v end
minetest.register_node(":" .. name, def)
canreplace[name] = level
end
minetest.register_alias("nc_torch:wield_light", dynamic_light_node(8))
-- API for adding dynamic lights to world
local function dynamic_light_add(pos, level, check, exact)
if not pos then return end
local old = minetest.get_node(pos)
local name = old.name
local curlight = canreplace[name]
if not curlight then
if exact then return end
return dynamic_light_add({x = pos.x, y = pos.y - 1, z = pos.z}, level, check, true)
or dynamic_light_add({x = pos.x, y = pos.y + 1, z = pos.z}, level, check, true)
or dynamic_light_add({x = pos.x + 1, y = pos.y, z = pos.z}, level, check, true)
or dynamic_light_add({x = pos.x - 1, y = pos.y, z = pos.z}, level, check, true)
or dynamic_light_add({x = pos.x, y = pos.y, z = pos.z + 1}, level, check, true)
or dynamic_light_add({x = pos.x, y = pos.y, z = pos.z - 1}, level, check, true)
end
if level < 1 then return end
if level > nodecore.light_sun - 1 then level = nodecore.light_sun - 1 end
local setname = dynamic_light_node(level)
pos = vector.round(pos)
if curlight <= level then
local ll = nodecore.get_node_light(pos)
if ll and ll > level then return end
end
if curlight > level and not check_light(pos) then return end
if name ~= setname then nodecore.set_node_check(pos, {name = setname}, old) end
setup_light(pos, check)
return true
end
nodecore.dynamic_light_add = dynamic_light_add
-- Automatic player wield lights
local function lightsrc(stack)
local def = minetest.registered_items[stack:get_name()] or {}
return def.light_source or 0
end
local function player_wield_light_pos(player, speed)
local pos = player:get_pos()
pos.y = pos.y + player:get_properties().eye_height
local ld = player:get_look_dir()
speed = player:get_player_control().up and speed or 0.5
pos.x = pos.x + ld.x * speed
pos.z = pos.z + ld.z * speed
return vector.round(pos)
end
local function player_wield_light(player, data)
local glow = 0
local srcidx, srcstack
for idx, stack in pairs(player:get_inventory():get_list("main")) do
local src = lightsrc(stack)
if src > glow then
glow = src
srcidx = idx
srcstack = stack:get_name()
end
end
if glow < 1 then return end
local speed = data and data.physics and data.physics.speed or 1
local pos = player_wield_light_pos(player, speed)
local pname = player:get_player_name()
return dynamic_light_add(pos, glow, function()
local pl = minetest.get_player_by_name(pname)
if not pl then return end
local pp = player_wield_light_pos(pl, speed)
if not vector.equals(pos, pp) then return end
return pl:get_inventory():get_stack("main", srcidx)
:get_name() == srcstack
end)
end
nodecore.register_playerstep({
label = "player wield light",
action = function(player, data)
if nodecore.player_visible(player) then
return player_wield_light(player, data)
end
end
})
-- Automatic entity light sources
local function entlight(self, ...)
local stack = ItemStack(self.node and self.node.name or self.itemstring or "")
local src = lightsrc(stack)
if src > 0 then
local pos = self.object:get_pos()
if not pos then return ... end
pos = vector.round(pos)
nodecore.dynamic_light_add(pos, src, function()
local curpos = self.object and self.object:get_pos()
return curpos and vector.equals(vector.round(curpos), pos)
end)
end
return ...
end
for _, name in pairs({"item", "falling_node"}) do
local def = minetest.registered_entities["__builtin:" .. name]
local ndef = {
on_step = function(self, ...)
return entlight(self, def.on_step(self, ...))
end
}
setmetatable(ndef, def)
minetest.register_entity(":__builtin:" .. name, ndef)
end
-- shade for light-scattering effect
local shadenode = modname .. ":shade"
minetest.register_node(shadenode, nodecore.underride({
description = minetest.registered_nodes.air.description,
air_equivalent = true,
sunlight_propagates = false
}, true_airlike))
nodecore.register_dnt({
name = modname .. ":shadenode_check",
nodenames = {shadenode},
time = 4,
autostart = true,
action = function(pos)
return minetest.remove_node(pos)
end
})
function nodecore.dynamic_shade_add(pos, above)
local old = minetest.get_node(pos)
local name = old.name
if canreplace[name] then
return nodecore.set_node_check(pos, {name = shadenode}, old)
end
if above and above > 0 then
return nodecore.dynamic_shade_add({
x = pos.x, y = pos.y + 1, z = pos.z
}, above - 1)
end
end