110 lines
3.2 KiB
Lua
110 lines
3.2 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local math, minetest, nodecore, pairs, string, type, vector
|
|
= math, minetest, nodecore, pairs, string, type, vector
|
|
local math_random, string_format
|
|
= math.random, string.format
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
local modname = minetest.get_current_modname()
|
|
local mintime = nodecore.setting_float(modname .. "_time", 2,
|
|
"Push out of solid time", [[The amount of time a player
|
|
needs to be trapped in a solid node before being pushed out.]])
|
|
local stepdist = nodecore.setting_float(modname .. "_stepdist", 5,
|
|
"Push out of solid distance", [[The maximum distance in
|
|
nodes that a player will be pushed out of solids.]])
|
|
|
|
local function normalbox(box)
|
|
if not box then return true end
|
|
if type(box) ~= "table" then return end
|
|
if box.fixed then return normalbox(box.fixed) end
|
|
if #box == 1 then return normalbox(box[1]) end
|
|
return box[1] == -0.5 and box[2] == -0.5 and box[3] == -0.5
|
|
and box[4] == 0.5 and box[5] == 0.5 and box[6] == 0.5
|
|
end
|
|
|
|
local function ispushout(def)
|
|
if not def.walkable then return end
|
|
if def.liquidtype ~= "none" then return end
|
|
if def.groups and def.groups.is_stack_only then return end
|
|
return normalbox(def.collision_box)
|
|
end
|
|
|
|
local solids = {}
|
|
minetest.after(0, function()
|
|
for k, v in pairs(minetest.registered_nodes) do
|
|
if ispushout(v) then
|
|
solids[k] = true
|
|
end
|
|
end
|
|
solids.ignore = nil
|
|
end)
|
|
|
|
local function isroom(pos)
|
|
return not (solids[minetest.get_node(pos).name]
|
|
or solids[minetest.get_node({
|
|
x = pos.x,
|
|
y = pos.y + 1,
|
|
z = pos.z
|
|
}).name])
|
|
end
|
|
|
|
nodecore.player_pushout_disable = nodecore.player_pushout_disable or function() end
|
|
|
|
nodecore.register_playerstep({
|
|
label = "push player out of solids",
|
|
action = function(player, data, dtime)
|
|
local function reset() data.pushout = nil end
|
|
|
|
if minetest.check_player_privs(player, "noclip")
|
|
or nodecore.player_pushout_disable(player, data)
|
|
then return reset() end
|
|
|
|
local pos = vector.round(player:get_pos())
|
|
if isroom(pos) then return reset() end
|
|
|
|
local podata = data.pushout or {}
|
|
local oldpos = podata.pos or pos
|
|
if not vector.equals(pos, oldpos) then return reset() end
|
|
|
|
local pt = (podata.time or 0) + dtime
|
|
if pt < mintime then
|
|
data.pushout = {time = pt, pos = pos}
|
|
return
|
|
end
|
|
|
|
local function pushto(newpos)
|
|
local dist = vector.distance(pos, newpos)
|
|
if dist > 1 then
|
|
nodecore.addphealth(player, -dist + 1, {
|
|
nc_type = "pushout"
|
|
})
|
|
end
|
|
newpos.y = newpos.y - 0.49
|
|
nodecore.log("action", string_format("player %q pushed out of"
|
|
.. " solid from %s to %s",
|
|
data.pname,
|
|
minetest.pos_to_string(pos),
|
|
minetest.pos_to_string(newpos)))
|
|
newpos.keepinv = true
|
|
player:set_pos(newpos)
|
|
return reset()
|
|
end
|
|
|
|
for rel in nodecore.settlescan() do
|
|
local p = vector.add(pos, rel)
|
|
if isroom(p) then
|
|
return pushto(p)
|
|
end
|
|
end
|
|
local function bias(n)
|
|
return n + ((n > 0) and math_random(-stepdist - 1, stepdist - 1)
|
|
or math_random(-stepdist + 1, stepdist + 1))
|
|
end
|
|
return pushto({
|
|
x = bias(pos.x),
|
|
y = bias(pos.y),
|
|
z = bias(pos.z)
|
|
})
|
|
end
|
|
})
|