-- 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.get_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("%s 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 })