init, chnaged wear time

This commit is contained in:
Rainer 2026-02-12 13:41:10 +01:00
commit 07333e9d5a
9 changed files with 479 additions and 0 deletions

336
init.lua Normal file
View file

@ -0,0 +1,336 @@
if not minetest.raycast then
minetest.log("error", "screwdriver2 requires minetest version 5.0 or newer")
return
end
local function disp(...)
for _, x in ipairs({...}) do
minetest.chat_send_all(dump(x))
end
end
screwdriver2 = {}
-- 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 = function(pos, node, player, mode, axis, amount, rotate_function)
if mode ~= 1 then return false end
end,
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}, {0, 1}},
z = {{0, 3, 1, 2}},
}
local function cycle_find(cycles, axis, param2, amount)
for _, cycle in ipairs(cycles[axis]) do
-- Find the current dir
for i, d in ipairs(cycle) do
if d == param2 then return cycle[1 + (i - 1 + amount) % #cycle] end
end
end
end
-- Functions to rotate a facedir/wallmounted value around an axis by a certain amount
local PARAM2TYPES = {
-- Facedir: lower 5 bits used for direction, 0 - 23
facedir = {
rotate = function(param2, axis, amount)
local facedir = param2 % 32
local new_facedir = cycle_find(facedir_cycles, axis, facedir, amount) or facedir
return param2 - facedir + new_facedir
end,
check_mode = function(old, new)
-- i can't remember if we're allowed to use bitwise operators yet
if old - old % 4 == new - new % 4 then return screwdriver.ROTATE_FACE end
return screwdriver.ROTATE_AXIS
end,
},
-- Wallmounted: lower 3 bits used, 0 - 5
wallmounted = {
rotate = function(param2, axis, amount)
local wallmounted = param2 % 8
local new_wallmounted = cycle_find(wallmounted_cycles, axis, wallmounted, amount) or wallmounted
return param2 - wallmounted + new_wallmounted
end,
check_mode = function(old, new)
if (old<=1) == (new<=1) then return screwdriver.ROTATE_FACE end
return screwdriver.ROTATE_AXIS
end,
},
-- 4dir: lower 2 bits used, 0 - 3
["4dir"] = {
rotate = function(param2, axis, amount)
if axis ~= "y" then return param2 end
local dir = param2 % 4
return param2 - dir + ((dir + amount) % 4)
end,
check_mode = function(old, new)
return screwdriver.ROTATE_FACE
end,
},
}
PARAM2TYPES.colorfacedir = PARAM2TYPES.facedir
PARAM2TYPES.colorwallmounted = PARAM2TYPES.wallmounted
PARAM2TYPES.color4dir = PARAM2TYPES["4dir"]
--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
-- Decide what sound to make when rotating a node
local sound_groups = {"cracky", "crumbly", "dig_immediate", "metal", "choppy", "oddly_breakable_by_hand", "snappy"}
local function get_dig_sound(def)
if def.sounds and def.sounds.dig then
return def.sounds.dig
elseif not def.sound_dig or def.sound_dig == "__group" then
local groups = def.groups
for i, name in ipairs(sound_groups) do
if groups[name] and groups[name] > 0 then
return "default_dig_"..name
end
end
else
return def.sound_dig
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
--disp(def.sound_dig)
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 param2type = PARAM2TYPES[def.paramtype2]
if not param2type 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 = param2type.rotate(node.param2, axis, amount)
if not new_param2 then return end
-- 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
local result = on_rotate(
vector.new(pos),
table.copy(node),
player,
param2type.check_mode(node.param2, new_param2),
new_param2,
-- New:
axis, -- "x", "y", or "z"
amount, -- 90 degrees = 1, etc.
param2type.rotate -- 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)
-- Sound
local sound = get_dig_sound(def)
if sound then
minetest.sound_play(sound,{
pos = pos,
gain = 0.25,
max_hear_distance = 32,
})
end
-- 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
-- Apply wear if not in creative mode
if not minetest.is_creative_enabled(player_name) then
itemstack:add_wear(65535 / 400)
return itemstack
end
end
minetest.register_tool("screwdriver2:screwdriver",{
description = "Better Screwdriver\nleft click = push edge, right click = rotate face",
_doc_items_longdesc = "A tool for rotating nodes. Designed to be easier to use than the standard screwdriver.",
_doc_items_usagehelp = [[
Left clicking a node will "push" its nearest edge away from you. (Hold sneak to reverse the direction.)
Right click rotates the node clockwise around the face you are pointing at.]],
_doc_items_hidden = false,
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,
})
-- Just in case someone needs the old screwdriver, define a recipe to craft it.
if minetest.get_modpath("screwdriver") then
minetest.register_craft({
output = "screwdriver:screwdriver",
type = "shapeless",
recipe = {"screwdriver2:screwdriver"},
})
minetest.register_craft({
output = "screwdriver2:screwdriver",
type = "shapeless",
recipe = {"screwdriver:screwdriver"},
})
minetest.clear_craft({
recipe = {
{"default:steel_ingot"},
{"group:stick"},
},
})
end
-- Override screwdriver:screwdriver recipe:
minetest.register_craft({
output = "screwdriver2:screwdriver",
recipe = {
{"default:steel_ingot"},
{"group:stick"},
},
})
if minetest.get_modpath("worldedit") then
dofile(minetest.get_modpath("screwdriver2").."/worldedit.lua")
end