230 lines
6.8 KiB
Lua
Raw Normal View History

-- LUALOCALS < ---------------------------------------------------------
local minetest, nodecore, pairs, string, table, tonumber, vector
= minetest, nodecore, pairs, string, table, tonumber, vector
local string_format, table_remove, table_sort
= string.format, table.remove, table.sort
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local glyph = "nc_writing:glyph1"
local maxdist = tonumber(minetest.settings:get(modname .. "_maxdist")) or 32
local minpower = tonumber(minetest.settings:get(modname .. "_minpower")) or 3
local function sparkly(posa, posb)
local minpos = {
x = (posa.x < posb.x and posa.x or posb.x) - 0.5,
y = (posa.y < posb.y and posa.y or posb.y) - 0.5,
z = (posa.z < posb.z and posa.z or posb.z) - 0.5
}
local maxpos = {
x = (posa.x > posb.x and posa.x or posb.x) + 0.5,
y = (posa.y > posb.y and posa.y or posb.y) + 1.5,
z = (posa.z > posb.z and posa.z or posb.z) + 0.5
}
local volume = (maxpos.x - minpos.x + 1) * (maxpos.y - minpos.y + 1)
* (maxpos.z - minpos.z + 1)
minetest.add_particlespawner({
amount = 5 * volume,
time = 0.25,
minpos = minpos,
maxpos = maxpos,
minvel = {x = -0.5, y = -0.5, z = -0.5},
maxvel = {x = 0.5, y = 0.5, z = 0.5},
texture = "nc_lux_base.png^[mask:nc_lux_dot_mask.png^[opacity:128",
minexptime = 0.20,
maxexptime = 0.25,
glow = 4
})
end
local function zipscan(pos, dir)
for i = 1, maxdist - 1 do
local p = {
x = pos.x + dir.x * i,
y = pos.y + dir.y * i,
z = pos.z + dir.z * i
}
if nodecore.walkable(p) then return end
if dir.y == 0 and nodecore.walkable({x = p.x, y = p.y + 1, z = p.z}) then return end
local node = minetest.get_node(p)
local face = nodecore.facedirs[node.param2]
if node.name == glyph and vector.equals(face.k, dir) then
if dir.y == 1 then dir = vector.add(dir, face.b) end
return vector.add(p, dir)
end
end
end
local function zipcheck(pos)
local node = minetest.get_node(pos)
local face = nodecore.facedirs[node.param2]
local dir = face.k
local hit = zipscan(pos, dir)
if not hit then return end
if nodecore.walkable(hit) or
nodecore.walkable({x = hit.x, y = hit.y + 1, z = hit.z})
then return end
return hit
end
local cache = {}
local function zipdata_get(player)
local pname = player:get_player_name()
local found = cache[pname]
if found then return found end
local s = player:get_meta():get_string(modname) or ""
found = s and s ~= "" and minetest.deserialize(s) or {}
cache[pname] = found
return found
end
local function zipdata_set(player, data)
local pname = player:get_player_name()
cache[pname] = data
player:get_meta():set_string(modname, data and minetest.serialize(data) or "")
end
local function appendpos(oldpos, pos)
for _, p in pairs(oldpos) do
if vector.equals(pos, p) then return oldpos end
end
oldpos[#oldpos + 1] = pos
if #oldpos > 100 then table_remove(oldpos, 1) end
return oldpos
end
local enercache = {}
local function energized(pos)
local hash = minetest.hash_node_position(pos)
local found = enercache[hash]
if found and found > nodecore.gametime then return end
enercache[hash] = nodecore.gametime + 1
minetest.add_particlespawner({
amount = 10,
time = 1,
minpos = {x = pos.x - 0.5, y = pos.y - 0.5, z = pos.z - 0.5},
maxpos = {x = pos.x + 0.5, y = pos.y + 1.5, z = pos.z + 0.5},
minvel = {x = -0.5, y = -0.5, z = -0.5},
maxvel = {x = 0.5, y = 0.5, z = 0.5},
texture = "nc_lux_base.png^[mask:nc_lux_dot_mask.png^[opacity:128",
minexptime = 0.20,
maxexptime = 0.25,
glow = 4
})
end
local function playercheck(player)
local vel = player:get_player_velocity()
if vector.dot(vel, vel) > 0.1 then return end
local ctl = player:get_player_control()
if ctl.sneak or ctl.jump or ctl.up or ctl.down or ctl.left or ctl.right then return end
local data = zipdata_get(player)
if not data.pos then return end
local pos = vector.round(player:get_pos())
if vector.distance(pos, data.pos) > 2 then
for _, p in pairs(data.oldpos or {}) do
if vector.distance(pos, p) <= 2 then
nodecore.log("action", string_format(
"%s: %s pos correction from %s to %s",
modname, player:get_player_name(),
minetest.pos_to_string(pos),
minetest.pos_to_string(p)))
player:set_pos(data.pos)
return energized(pos)
end
end
nodecore.log("action", string_format("%s: %s exits ziprune at %s",
modname, player:get_player_name(), minetest.pos_to_string(pos)))
return zipdata_set(player)
end
local runes = nodecore.find_nodes_around(pos, glyph, 1)
table_sort(runes, function(a, b)
local da = vector.distance(pos, a)
local db = vector.distance(pos, b)
if da ~= db then return da < db end
if a.y ~= b.y then return a.y < b.y end
if a.x ~= b.x then return a.x < b.x end
return a.z < b.z
end)
for _, p in pairs(runes) do
local hit = zipcheck(p)
if hit then
nodecore.log("action", string_format("%s: %s zips from %s to %s",
modname, player:get_player_name(), minetest.pos_to_string(pos),
minetest.pos_to_string(hit)))
data.oldpos = appendpos(data.oldpos or {}, data.pos)
data.pos = hit
zipdata_set(player, data)
player:set_pos({x = hit.x, y = hit.y - 0.49, z = hit.z})
nodecore.sound_play(modname .. "_zip", {pos = hit})
return sparkly(p, hit)
end
end
energized(pos)
end
minetest.register_globalstep(function()
for _, player in pairs(minetest.get_connected_players()) do
playercheck(player)
end
end)
local powernodes = {}
for i = minpower, 8 do powernodes["nc_lux:cobble" .. i] = true end
2020-06-21 00:12:20 -04:00
nodecore.register_dnt({
name = modname,
time = 1,
action = function(pos, node)
2020-06-21 00:12:20 -04:00
if node.name ~= glyph then return end
local face = nodecore.facedirs[node.param2]
local unode = minetest.get_node(vector.add(pos, face.b))
if not powernodes[unode.name] then return end
if not zipscan(pos, face.k) then return end
2020-06-21 00:12:20 -04:00
nodecore.dnt_set(pos, modname)
minetest.add_particlespawner({
amount = 20,
time = 1,
minpos = {x = pos.x - 0.5, y = pos.y - 7/16, z = pos.z - 0.5},
maxpos = {x = pos.x + 0.5, y = pos.y - 7/16, z = pos.z + 0.5},
minvel = {x = 0, y = 0, z = 0},
maxvel = {x = 0, y = 0, z = 0},
minacc = {x = 0, y = 1, z = 0},
maxacc = {x = 0, y = 1, z = 0},
texture = "nc_lux_base.png^[mask:nc_lux_dot_mask.png^[opacity:128",
minexptime = 1,
maxexptime = 2,
glow = 4
})
for _, player in pairs(minetest.get_connected_players()) do
if vector.equals(vector.round(player:get_pos()), pos) then
zipdata_set(player, {pos = pos})
nodecore.sound_play(modname .. "_zip", {pos = pos})
end
end
end
})
2020-06-21 00:12:20 -04:00
nodecore.register_limited_abm({
label = "ZipRune Detection",
interval = 1,
chance = 1,
limited_max = 100,
limited_alert = 1000,
nodenames = {glyph},
neighbors = {"group:lux_cobble"},
action = function(pos) nodecore.dnt_set(pos, modname) end
})