local function table_contains(t, v) for _,i in pairs(t) do if v == i then return true end end return false end local creative = minetest.settings:get_bool("creative_mode") local function throw_pearl(item, player) local playerpos = player:getpos() playerpos.y = playerpos.y+1.625 local obj = minetest.add_entity(playerpos, "hell:pearl_entity") local dir = player:get_look_dir() obj:setvelocity(vector.multiply(dir, 30)) obj:setacceleration({x=dir.x*-3, y=-dir.y^8*80-10, z=dir.z*-3}) obj:get_luaentity().player = player:get_player_name() if not creative then item:take_item() return item end end local function get_node(pos) local name = minetest.get_node(pos).name if name ~= "ignore" then return name end minetest.get_voxel_manip():read_from_map(pos, pos) name = minetest.get_node_or_nil(pos) if not name then return end return name.name end local softs = {} local function is_soft(pos) local name = get_node(pos) if not name then return false end local is_soft = softs[name] if is_soft ~= nil then return is_soft end if not minetest.registered_nodes[name] then softs[name] = false return false end is_soft = minetest.registered_nodes[name].walkable == false softs[name] = is_soft return is_soft end -- teleports the player there if there's free space local function teleport_player(pos, player) if not is_soft(pos) then return false end if not is_soft({x=pos.x, y=pos.y+1, z=pos.z}) and not is_soft({x=pos.x, y=pos.y-1, z=pos.z}) then return false end pos.y = pos.y+0.05 player:moveto(pos) return true end minetest.register_entity("hell:pearl_entity", { collisionbox = {0,0,0,0,0,0}, --not pointable visual_size = {x=0.1, y=0.1}, physical = false, -- Collides with things textures = {"hell_pearl.png"}, on_activate = function(self, staticdata) if not staticdata or staticdata == "" then return end local tmp = minetest.deserialize(staticdata) if not tmp then minetest.log("error", "[hell] pearl: invalid staticdata ") return end self.player = tmp.player end, get_staticdata = function(self) --forceload(vector.round(self.object:getpos())) return minetest.serialize({ player = self.player, }) end, timer = 0, on_step = function(self, dtime) self.timer = self.timer+dtime --[[ local delay = self.delay if delay < 0.1 then self.delay = delay+dtime return end self.delay = 0--]] if self.timer > 20 then self.object:remove() return end local pos = self.object:getpos() local rpos = vector.round(pos) local lastpos = self.lastpos if not lastpos then self.lastpos = vector.new(rpos) return end if lastpos.x and vector.equals(vector.round(lastpos), rpos) then return end local player = self.player if not player then minetest.log("error", "[hell] pearl: missing playername") self.object:remove() return end player = minetest.get_player_by_name(player) if not player then minetest.log("error", "[hell] pearl: missing player") self.object:remove() return end if not get_node(rpos) then minetest.log("error", "[hell] pearl: missing node") self.object:remove() return end self.lastpos = vector.new(pos) local free, p = minetest.line_of_sight(lastpos, pos) if free then return end if is_soft(p) then return end self.object:remove() minetest.after(0, function(p) --minetest.after is used that the sound is played after the teleportation minetest.sound_play("hell_pearl", {pos=p, max_hear_distance=10}) end, p) p.y = p.y+1 if teleport_player(vector.new(p), player) then return end p.y = p.y-2 for i = -1,1,2 do for _,j in pairs({{i, 0}, {0, i}}) do if teleport_player({x=p.x+j[1], y=p.y, z=p.z+j[2]}, player) then return end end end for i = -1,1,2 do for j = -1,1,2 do if teleport_player({x=p.x+j, y=p.y, z=p.z+i}, player) then return end end end end }) minetest.override_item("hell:pearl", {on_use = throw_pearl})