Replace screwdriver mod with screwdriver2

master
Wuzzy 2021-12-27 17:29:55 +01:00
parent 29a8222d46
commit 78e166e14a
20 changed files with 368 additions and 268 deletions

View File

@ -6,7 +6,7 @@ External mods used:
* `show_wielded_item`
* `no_fall_damage`
* `player_api`, `xpanes`, `stairs`, `screwdriver`
* `player_api`, `xpanes`, `stairs`, `screwdriver2`
* `xdecor` (heavily modified, non-free textures removed)
---
@ -17,6 +17,7 @@ Textures:
- Crosshair, wieldhand, `smoke_puff.png`: trivial textures by Wuzzy (CC0)
- Emitter, detector textures: Derivate works of xdecor textures by jp (CC0)
- Mirror textures: by Wuzzy (CC0)
- Screwdriver texture: by 12Me21 (CC0)
Models:
- Player models comes from Minetest Game (see license of Minetest Game 5.4.1 for details)
@ -25,16 +26,10 @@ Sounds:
- All sounds come from Minetest Game (see license of Minetest Game 5.4.1 for details)
- Exception 1: Ocean waves come from https://freesound.org/people/inchadney/sounds/135805/
- by inchadney (CC BY 3.0)
- Excecption 2: Screwdriver sounds
- Created by InspectorJ <www.jshaw.co.uk> of Freesound.org (CC BY 3.0):
- `screwdriver_use.1.ogg`
- `screwdriver_use.2.ogg`
- `screwdriver_use.3.ogg`
- `screwdriver_use.4.ogg`
- (Original file name: Screwdriver, Ratchet, C.wav)
Code:
- `player_api`, `stairs`, `xpanes`, `screwdriver` come from Minetest Game 5.4.1 (mods modified for Lazarr!), by Minetest Game developers (see README.txt in those folders)
- `player_api`, `stairs`, `xpanes` come from Minetest Game 5.4.1 (mods modified for Lazarr!), by Minetest Game developers (see README.txt in those folders)
- `screwdriver2` by 12Me21, modified version (CC0)
- `xdecor` by jp (BSD-3 clause license; massively-simplified version from the original)
Other stuff:

View File

@ -105,7 +105,7 @@ local register_element = function(subname, def)
local def_core = table.copy(def)
def_core.after_place_node = full_update
def_core.after_dig_node = full_update
def_core.after_rotate = after_rotate
def_core._after_rotate = after_rotate
def_core.sounds = lzr_sounds.node_sound_wood_defaults()
def_core._lzr_active = "lzr_laser:"..subname.."_on"
def_core.tiles = def.tiles_off

View File

@ -75,7 +75,7 @@ end
local function reset_inventory(player)
clear_inventory(player)
local inv = player:get_inventory()
inv:add_item("main", "screwdriver:screwdriver")
inv:add_item("main", "screwdriver2:screwdriver")
end
local current_level = nil

View File

@ -1,2 +1,2 @@
name = lzr_levels
depends = lzr_core, lzr_mapgen, lzr_globals
depends = lzr_core, lzr_mapgen, lzr_globals, screwdriver2

View File

@ -1,21 +0,0 @@
Minetest Game mod: screwdriver
==============================
See license.txt for license information.
License of source code
----------------------
Originally by RealBadAngel, Maciej Kasatkin (LGPLv2.1+)
Various Minetest developers and contributors (LGPLv2.1+)
License of media (textures)
---------------------------
Created by Gambit (CC BY-SA 3.0):
screwdriver.png
Created by InspectorJ <www.jshaw.co.uk> of Freesound.org (CC BY 3.0):
screwdriver_use.1.ogg
screwdriver_use.2.ogg
screwdriver_use.3.ogg
screwdriver_use.4.ogg
(Original file name: Screwdriver, Ratchet, C.wav)

View File

@ -1,183 +0,0 @@
-- screwdriver/init.lua
screwdriver = {}
-- Load support for MT game translation.
local S = minetest.get_translator("screwdriver")
local USES_DEFAULT = 200
screwdriver.ROTATE_FACE = 1
screwdriver.ROTATE_AXIS = 2
screwdriver.disallow = function(pos, node, user, mode, new_param2)
return false
end
screwdriver.rotate_simple = function(pos, node, user, mode, new_param2)
if mode ~= screwdriver.ROTATE_FACE then
return false
end
end
-- For attached wallmounted nodes: returns true if rotation is valid
-- simplified version of minetest:builtin/game/falling.lua#L148.
local function check_attached_node(pos, rotation)
local d = minetest.wallmounted_to_dir(rotation)
local p2 = vector.add(pos, d)
local n = minetest.get_node(p2).name
local def2 = minetest.registered_nodes[n]
if def2 and not def2.walkable then
return false
end
return true
end
screwdriver.rotate = {}
local facedir_tbl = {
[screwdriver.ROTATE_FACE] = {
[0] = 1, [1] = 2, [2] = 3, [3] = 0,
[4] = 5, [5] = 6, [6] = 7, [7] = 4,
[8] = 9, [9] = 10, [10] = 11, [11] = 8,
[12] = 13, [13] = 14, [14] = 15, [15] = 12,
[16] = 17, [17] = 18, [18] = 19, [19] = 16,
[20] = 21, [21] = 22, [22] = 23, [23] = 20,
},
[screwdriver.ROTATE_AXIS] = {
[0] = 4, [1] = 4, [2] = 4, [3] = 4,
[4] = 8, [5] = 8, [6] = 8, [7] = 8,
[8] = 12, [9] = 12, [10] = 12, [11] = 12,
[12] = 16, [13] = 16, [14] = 16, [15] = 16,
[16] = 20, [17] = 20, [18] = 20, [19] = 20,
[20] = 0, [21] = 0, [22] = 0, [23] = 0,
},
}
screwdriver.rotate.facedir = function(pos, node, mode)
local rotation = node.param2 % 32 -- get first 5 bits
local other = node.param2 - rotation
rotation = facedir_tbl[mode][rotation] or 0
return rotation + other
end
screwdriver.rotate.colorfacedir = screwdriver.rotate.facedir
local wallmounted_tbl = {
[screwdriver.ROTATE_FACE] = {[2] = 5, [3] = 4, [4] = 2, [5] = 3, [1] = 0, [0] = 1},
[screwdriver.ROTATE_AXIS] = {[2] = 1, [3] = 0, [4] = 1, [5] = 0, [1] = 3, [0] = 2}
}
screwdriver.rotate.wallmounted = function(pos, node, mode)
local rotation = node.param2 % 8 -- get first 3 bits
local other = node.param2 - rotation
rotation = wallmounted_tbl[mode][rotation] or 0
if minetest.get_item_group(node.name, "attached_node") ~= 0 then
-- find an acceptable orientation
for i = 1, 5 do
if not check_attached_node(pos, rotation) then
rotation = wallmounted_tbl[mode][rotation] or 0
else
break
end
end
end
return rotation + other
end
screwdriver.rotate.colorwallmounted = screwdriver.rotate.wallmounted
-- Handles rotation
screwdriver.handler = function(itemstack, user, pointed_thing, mode, uses)
if pointed_thing.type ~= "node" then
return
end
local pos = pointed_thing.under
local player_name = user and user:get_player_name() or ""
if minetest.is_protected(pos, player_name) and not minetest.check_player_privs(player_name, "protection_bypass") then
minetest.record_protection_violation(pos, player_name)
return
end
local node = minetest.get_node(pos)
local ndef = minetest.registered_nodes[node.name]
if not ndef then
return itemstack
end
-- Node MUST have 'rotatable' group
if not ndef.groups.rotatable then
return itemstack
end
if mode == screwdriver.ROTATE_AXIS and ndef and ndef.on_rightclick and
((not user) or (user and not user:get_player_control().sneak)) then
return ndef.on_rightclick(pos, node, user, itemstack,
pointed_thing) or itemstack
end
-- can we rotate this paramtype2?
local fn = screwdriver.rotate[ndef.paramtype2]
if not fn and not ndef.on_rotate then
return itemstack
end
local should_rotate = true
local new_param2
if fn then
new_param2 = fn(pos, node, mode)
else
new_param2 = node.param2
end
-- Node provides a handler, so let the handler decide instead if the node can be rotated
if ndef.on_rotate == false then
return
elseif ndef.on_rotate == "simple" then
if mode ~= screwdriver.ROTATE_FACE then
return
end
elseif ndef.on_rotate then
-- Copy pos and node because callback can modify it
local result = ndef.on_rotate(vector.new(pos),
{name = node.name, param1 = node.param1, param2 = node.param2},
user, mode, new_param2)
minetest.sound_play({name="screwdriver_use", gain=0.5}, {pos=pos, max_hear_distance=16}, true)
if result == false then -- Disallow rotation
return itemstack
elseif result == true then
should_rotate = false
end
elseif ndef.on_rotate == false then
return itemstack
elseif ndef.can_dig and not ndef.can_dig(pos, user) then
return itemstack
end
if should_rotate and new_param2 ~= node.param2 then
node.param2 = new_param2
minetest.swap_node(pos, node)
minetest.check_for_falling(pos)
minetest.sound_play({name="screwdriver_use", gain=0.5}, {pos=pos, max_hear_distance=16}, true)
if ndef.after_rotate then
ndef.after_rotate(vector.new(pos))
end
end
return itemstack
end
-- Screwdriver
minetest.register_tool("screwdriver:screwdriver", {
description = S("Mirror Rotator") .. "\n" .. S("Punch to rotate face, place to rotates axis"),
inventory_image = "screwdriver.png",
groups = {tool = 1},
on_use = function(itemstack, user, pointed_thing)
screwdriver.handler(itemstack, user, pointed_thing, screwdriver.ROTATE_FACE, USES_DEFAULT)
return itemstack
end,
on_place = function(itemstack, user, pointed_thing)
screwdriver.handler(itemstack, user, pointed_thing, screwdriver.ROTATE_AXIS, USES_DEFAULT)
return itemstack
end,
})

View File

@ -1,50 +0,0 @@
License of source code
----------------------
GNU Lesser General Public License, version 2.1
Copyright (C) 2013-2016 RealBadAngel, Maciej Kasatkin
Copyright (C) 2013-2016 Various Minetest developers and contributors
This program is free software; you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software Foundation;
either version 2.1 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details:
https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
Licenses of media (textures)
----------------------------
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
Copyright (C) 2013-2016 Gambit
You are free to:
Share — copy and redistribute the material in any medium or format.
Adapt — remix, transform, and build upon the material for any purpose, even commercially.
The licensor cannot revoke these freedoms as long as you follow the license terms.
Under the following terms:
Attribution — You must give appropriate credit, provide a link to the license, and
indicate if changes were made. You may do so in any reasonable manner, but not in any way
that suggests the licensor endorses you or your use.
ShareAlike — If you remix, transform, or build upon the material, you must distribute
your contributions under the same license as the original.
No additional restrictions — You may not apply legal terms or technological measures that
legally restrict others from doing anything the license permits.
Notices:
You do not have to comply with the license for elements of the material in the public
domain or where your use is permitted by an applicable exception or limitation.
No warranties are given. The license may not give you all of the permissions necessary
for your intended use. For example, other rights such as publicity, privacy, or moral
rights may limit how you use the material.
For more details:
http://creativecommons.org/licenses/by-sa/3.0/

View File

@ -1,2 +0,0 @@
name = screwdriver
description = A screwdriver allows rotating blocks

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 B

View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -0,0 +1,5 @@
Improved node rotation tool.
![bbcode>markdown](http://kland.smilebasicsource.com/i/kwdeu.png)
https://forum.minetest.net/viewtopic.php?f=9&t=20856

249
mods/screwdriver2/init.lua Normal file
View File

@ -0,0 +1,249 @@
local S = minetest.get_translator("screwdriver2")
if not minetest.raycast then
minetest.log("error", "screwdriver2 requires minetest version 5.0 or newer")
return
end
screwdriver2 = {}
local function rotate_simple(_, _, _, _, new_param2)
if new_param2 % 32 > 3 then return false end
end
-- If the screwdriver mod is not installed, create a fake screwdriver variable.
-- (This mod has an optional dependancy on screwdriver, so minetest loads screwdriver first if it exists.)
-- - Some mods will only set on_rotate when `screwdriver` exists.
-- - Mods may expect `screwdiver` to exist if `on_rotate` is called.
if not minetest.global_exists("screwdriver") then
screwdriver = {
ROTATE_FACE = 1,
ROTATE_AXIS = 2,
rotate_simple = rotate_simple,
disallow = false, -- I doubt anyone actually used screwdriver.disallow but whatever.
}
end
local get_pointed = dofile(minetest.get_modpath("screwdriver2").."/pointed.lua")
-- Functions to choose rotation based on pointed location
local insanity_2 = {xy = 1, yz = 1, zx = 1; zy = -1, yx = -1, xz = -1} -- Don't worry about this
local function push_edge(normal, point)
local biggest = 0
local biggest_axis
local normal_axis
-- Find the normal axis, and the axis of the with the
-- greatest magnitude (other than the normal axis)
for axis in pairs(point) do
if normal[axis] ~= 0 then
normal_axis = axis
elseif math.abs(point[axis])>biggest then
biggest = math.abs(point[axis])
biggest_axis = axis
end
end
-- Find the third axis, which is the one to rotate around
if normal_axis and biggest_axis then
for axis in pairs(point) do
if axis ~= normal_axis and axis ~= biggest_axis then
-- Decide which direction to rotate (+ or -)
return axis, insanity_2[normal_axis..biggest_axis] * math.sign(normal[normal_axis] * point[biggest_axis])
end
end
end
return "y", 0
end
local function rotate_face(normal, _)
-- Find the normal axis
for axis, value in pairs(normal) do
if value ~= 0 then
return axis, math.sign(value)
end
end
return "y", 0
end
-- Numbers taken from https://forum.minetest.net/viewtopic.php?p=73195&sid=1d2d2e4e76ce2ef9c84646481a4b84bc#p73195
-- "How to rotate (clockwise) by axis from any facedir:"
-- "(this will be made into a lua function)"
-- 5 years later...
local facedir_cycles = {
x = {{12,13,14,15},{16,19,18,17},{ 0, 4,22, 8},{ 1, 5,23, 9},{ 2, 6,20,10},{ 3, 7,21,11}},
y = {{ 0, 1, 2, 3},{20,23,22,21},{ 4,13,10,19},{ 8,17, 6,15},{12, 9,18, 7},{16, 5,14,11}},
z = {{ 4, 5, 6, 7},{ 8,11,10, 9},{ 0,16,20,12},{ 1,17,21,13},{ 2,18,22,14},{ 3,19,23,15}},
}
local wallmounted_cycles = {
x = {0, 4, 1, 5},
y = {4, 2, 5, 3},
z = {0, 3, 1, 2},
}
-- Functions to rotate a facedir/wallmounted value around an axis by a certain amount
local rotate = {
-- Facedir: lower 5 bits used for direction, 0 - 23
facedir = function(param2, axis, amount)
local facedir = param2 % 32
for _, cycle in ipairs(facedir_cycles[axis]) do
-- Find the current facedir
-- Minetest adds table.indexof, but I refuse to use it because it returns -1 rather than nil
for i, fd in ipairs(cycle) do
if fd == facedir then
return param2 - facedir + cycle[1+(i-1 + amount) % 4] -- If only Lua didn't use 1 indexing...
end
end
end
return param2
end,
-- Wallmounted: lower 3 bits used, 0 - 5
wallmounted = function(param2, axis, amount)
local wallmounted = param2 % 8
for i, wm in ipairs(wallmounted_cycles[axis]) do
if wm == wallmounted then
return param2 - wallmounted + wallmounted_cycles[axis][1+(i-1 + amount) % 4]
end
end
return param2
end
}
rotate.colorfacedir = rotate.facedir
rotate.colorwallmounted = rotate.wallmounted
--Todo: maybe support degrotate?
local function rect(angle, radius)
return math.cos(2*math.pi * angle) * radius, math.sin(2*math.pi * angle) * radius
end
-- Generate the screwdriver particle effects
local other_axes = {x = {"y","z"}, y = {"z","x"}, z = {"x","y"}}
local function particle_ring(pos, axis, direction)
local axis2, axis3 = unpack(other_axes[axis])
local particle_pos = vector.new()
local particle_vel = vector.new()
for i = 0, 0.999, 1/6 do
particle_pos[axis3], particle_pos[axis2] = rect(i, 0.5^0.5)
particle_vel[axis3], particle_vel[axis2] = rect(i - 1/4 * direction, 2)
minetest.add_particle({
pos = vector.add(pos, particle_pos),
velocity = particle_vel,
acceleration = vector.multiply(particle_pos, -7),
expirationtime = 0.25,
size = 2,
texture = "screwdriver2.png",
})
-- Smaller particles that last slightly longer, to give the illusion of
-- the particles disappearing smoothly
-- ?
-- minetest.add_particle({
-- pos = vector.add(pos, particle_pos),
-- velocity = particle_vel,
-- acceleration = vector.multiply(particle_pos, -7),
-- expirationtime = 0.3,
-- size = 1,
-- texture = "screwdriver2.png",
-- })
end
end
-- Main
-- Idea: split this into 2 functions
-- 1: on_use parameters -> axis/amount/etc.
-- 2: param2/axis/amount/etc. -> new param2
function screwdriver.use(itemstack, player, pointed_thing, is_right_click)
if pointed_thing.type ~= "node" then return end
local pos = pointed_thing.under
-- Check protection
local player_name = player:get_player_name()
if minetest.is_protected(pos, player_name) then
minetest.record_protection_violation(pos, player_name)
return
end
-- Get node info
local node = minetest.get_node_or_nil(pos)
if not node then return end
local def = minetest.registered_nodes[node.name]
if not def then return end -- probably unnessesary
-- Node MUST have 'rotatable' group
if not def.groups.rotatable then
return itemstack
end
local on_rotate = def.on_rotate
if on_rotate == false then return end
--if on_rotate == nil and def.can_dig and not def.can_dig(vector.new(pos), player) then return end
-- Choose rotation function based on paramtype2 (facedir/wallmounted)
local rotate_function = rotate[def.paramtype2]
if not rotate_function then return end
-- Choose rotation axis/direction and param2 based on click type and pointed location
local axis, amount
local normal, point = get_pointed(player, pointed_thing)
if not normal or vector.length(normal) == 0 then return end -- Raycast failed or player is inside selection box
local control = player:get_player_control()
if is_right_click then
axis, amount = rotate_face(normal, point)
-- This line intentionally left blank.
else
axis, amount = push_edge(normal, point)
if control.sneak then amount = -amount end
end
local new_param2 = rotate_function(node.param2, axis, amount)
-- Calculate particle position
local particle_offset = vector.new()
particle_offset[axis] = point[axis]--math.sign(normal[axis]) * 0.5
-- Handle node's on_rotate function
local handled
if type(on_rotate) == "function" then
-- If a mod is loaded after screwdriver but before screwdriver2,
-- it will still end up using the old `rotate_simple` function.
-- So, we'll check that here, and override it in that case.
if on_rotate == screwdriver.rotate_simple then on_rotate = rotate_simple end
local result = on_rotate(
vector.new(pos),
table.copy(node),
player,
is_right_click and 2 or 1, -- Deprecated
new_param2,
-- New:
axis, -- "x", "y", or "z"
amount, -- 90 degrees = 1, etc.
rotate_function -- function(node.param2, axis, amount) -> new_param2
)
if result == false then
return
elseif result == true then
handled = true
end
end
-- Draw particles (TODO: check if rotation was actually done)
particle_ring(vector.add(pos, particle_offset), axis, amount)
-- TODO: Play sound
-- Replace node
if not handled then
if new_param2 == node.param2 then return end -- no rotation was done
node.param2 = new_param2
minetest.swap_node(pos, node)
end
minetest.check_for_falling(pos)
if def._after_rotate then def._after_rotate(pos) end
end
minetest.register_tool("screwdriver2:screwdriver",{
description = S("Screwdriver").."\n"..S("Punch to push edge, place to rotate face"),
inventory_image = "screwdriver2.png",
on_use = function(itemstack, player, pointed_thing)
return screwdriver.use(itemstack, player, pointed_thing, false)
end,
on_place = function(itemstack, player, pointed_thing)
return screwdriver.use(itemstack, player, pointed_thing, true)
end,
})

View File

@ -0,0 +1,3 @@
name = screwdriver2
description = A more intuitive node rotation tool.
optional_depends = screwdriver, worldedit_commands, default

View File

@ -0,0 +1,91 @@
-- returns function:
-- --------------------------------------------------------------------------
-- normal, point, box_id = function(player, pointed_thing)
-- ==========================================================================
-- normal - unit vector pointing out from the face which is pointed at
-- point - point (relative to the node position) which is being pointed at
-- box_id - index of the selection box which is being pointed at
-- ==========================================================================
-- Try to get the exact point the player is looking at.
-- There is some inaccuracy due to the client-side view bobbing animation.
-- To prevent the wrong node face from being found, it checks to make sure
-- the position returned by the raycaster matches pointed_thing.
-- If it doesn't match, the raycast is done again with slight offsets.
-- This will never return the WRONG node face, but may not be able to find the correct one in rare situations.
local function disp(...)
for _, x in ipairs({...}) do
minetest.chat_send_all(dump(x))
end
end
local bob_amount = (minetest.settings:get("view_bobbing_amount") or 1)
-- Calculate offsets for one cycle of the view bobbing animation
-- https://github.com/minetest/minetest/blob/b298b0339c79db7f5b3873e73ff9ea0130f05a8a/src/camera.cpp#L344
local check_points = {}
for i = 0, 0.99999, 1/20 do
local bobfrac = i * 2 % 1
local bobdir = i < 0.5 and 1 or -1
local bobtmp = math.sin(bobfrac ^ 1.2 * math.pi)
local x = (0.3 * bobdir * math.sin(bobfrac * math.pi)) * bob_amount/10 -- Why is this divided by 10?
local y = (-0.28 * bobtmp ^ 2) * bob_amount/10
-- I'm not exactly sure how the roll actually works, and it has a very small effect.
--local roll = -0.03 * bobdir * bobtmp * math.pi * bob_amount/10
table.insert(check_points, {
x = x,-- * math.cos(roll) - y * math.sin(roll),
y = y,-- * math.cos(roll) - x * math.sin(roll),
-- no Z offset
})
end
-- Get the start and end points for the raycaster
local function get_look_dir(player)
local placer_pos = player:get_pos()
placer_pos.y = placer_pos.y + player:get_properties().eye_height
return placer_pos, vector.multiply(player:get_look_dir(), 20)
end
local function try_raycast(pos, look_dir, pointed_thing, offset)
if offset then
--disp(offset.x .. " " .. offset.z)
pos = vector.add(pos, offset)
end
local raycast = minetest.raycast(pos, vector.add(pos, look_dir), false)
local pointed = raycast:next()
if pointed and pointed.type == "node" then
-- minetest.add_particle({
-- pos = pointed.intersection_point,
-- expirationtime = 5,
-- size = 0.1,
-- texture = "heart.png",
-- glow = 14,
-- })
if vector.equals(pointed.under, pointed_thing.under) and vector.equals(pointed.above, pointed_thing.above) then
return
pointed.intersection_normal,
vector.subtract(pointed.intersection_point, pointed.under),
pointed.box_id
end
end
end
-- Get the point the player is looking at
return function(player, pointed_thing)
local pos, look_dir = get_look_dir(player)
local pitch = player:get_look_vertical()
local yaw = player:get_look_horizontal()
--disp(angle)
for i, offset in ipairs(check_points) do
local a, b, c = try_raycast(pos, look_dir, pointed_thing, i > 1 and { -- (don't apply offset for the first check)
x = math.sin(-yaw) * math.sin(pitch) * offset.y + math.cos(yaw) * offset.x,
y = math.cos(pitch) * offset.y,
z = math.cos(-yaw) * math.sin(pitch) * offset.y + math.sin(yaw) * offset.x,
})
if a then return a, b, c end
end
minetest.log("warning", "Could not get pointed position")
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 B