2018-06-19 15:59:31 -07:00
rhotator = { }
2018-06-20 06:00:59 -07:00
local mod_path = minetest.get_modpath ( minetest.get_current_modname ( ) )
2018-06-22 08:55:43 -07:00
local storage = minetest.get_mod_storage ( )
2018-06-25 16:32:20 -07:00
local notify = dofile ( mod_path .. " /notify.lua " )
2018-06-19 15:59:31 -07:00
-- constants
local POS = { }
local NEG = { }
POS.Y = 0
POS.Z = 1
NEG.Z = 2
POS.X = 3
NEG.X = 4
NEG.Y = 5
2018-06-20 06:00:59 -07:00
local PRIMARY_BTN = 1
local SECONDARY_BTN = 2
2019-02-04 14:09:03 -08:00
local OFF = 0
local ON = 1
local AUTO = 2
2018-06-20 06:00:59 -07:00
-- ============================================================
2019-02-04 14:09:03 -08:00
-- helpers
local function get_multi_action ( playername , primary , sneak )
local invert_buttons = storage : get_int ( " multi_invert_buttons_ " .. playername ) == 1
local invert_sneak = storage : get_int ( " multi_invert_sneak_ " .. playername ) == 1
local logic_primary = primary ~= invert_buttons
local logic_sneak = sneak ~= invert_sneak
if logic_primary then
if not logic_sneak then
return { " rotate " , " Rotates pointed-to face clockwise " }
else
return { " push " , " Pushes closest edge " }
end
else
if not logic_sneak then
return { " memory " , " Cycles through memory modes " }
else
return { " copy " , " Copies rotation from pointed-to node " }
end
end
end
local rhotator_command_description = table.concat ( {
" displays this description " ,
" /rhotator memory [on|off|auto]: displays or sets rotation memory for newly placed blocks (auto means 'auto copy from pointed-to node if possible, no rotation otherwise') " ,
2021-06-23 11:20:24 -07:00
" /rhotator require_tool [on|off|default]: displays or sets requirement of tool in inventory for newly placed nodes, default means 'server default' " ,
" /rhotator server_require_tool [on|off]: displays or sets server default requirement of tool in inventory, player choice takes precedence, requires priv 'rhotator_admin' to alter this value " ,
2019-02-04 14:09:03 -08:00
" /rhotator multi: lists the configuration of the multitool " ,
" /rhotator multi invert_buttons [on|off]: displays or sets mouse button inversion in the multitool " ,
" /rhotator multi invert_sneak [on|off]: displays or sets sneak effect inversion in the multitool " ,
} , " \n " )
2018-06-19 15:59:31 -07:00
local rot_matrices = { }
local dir_matrices = { }
2018-06-22 08:55:43 -07:00
local facedir_memory = { }
2018-06-20 06:00:59 -07:00
-- ============================================================
2018-06-19 15:59:31 -07:00
-- init
local function init_transforms ( )
local rot = { }
local dir = { }
-- no rotation
rot [ 0 ] = matrix { { 1 , 0 , 0 } ,
{ 0 , 1 , 0 } ,
{ 0 , 0 , 1 } }
-- 90 degrees clockwise
rot [ 1 ] = matrix { { 0 , 0 , 1 } ,
{ 0 , 1 , 0 } ,
{ - 1 , 0 , 0 } }
-- 180 degrees
rot [ 2 ] = matrix { { - 1 , 0 , 0 } ,
{ 0 , 1 , 0 } ,
{ 0 , 0 , - 1 } }
-- 270 degrees clockwise
rot [ 3 ] = matrix { { 0 , 0 , - 1 } ,
{ 0 , 1 , 0 } ,
{ 1 , 0 , 0 } }
rot_matrices = rot
-- directions
-- Y+
dir [ 0 ] = matrix { { 1 , 0 , 0 } ,
{ 0 , 1 , 0 } ,
{ 0 , 0 , 1 } }
-- Z+
dir [ 1 ] = matrix { { 1 , 0 , 0 } ,
{ 0 , 0 , - 1 } ,
{ 0 , 1 , 0 } }
-- Z-
dir [ 2 ] = matrix { { 1 , 0 , 0 } ,
{ 0 , 0 , 1 } ,
{ 0 , - 1 , 0 } }
-- X+
dir [ 3 ] = matrix { { 0 , 1 , 0 } ,
{ - 1 , 0 , 0 } ,
{ 0 , 0 , 1 } }
-- X-
dir [ 4 ] = matrix { { 0 , - 1 , 0 } ,
{ 1 , 0 , 0 } ,
{ 0 , 0 , 1 } }
-- Y-
dir [ 5 ] = matrix { { - 1 , 0 , 0 } ,
{ 0 , - 1 , 0 } ,
{ 0 , 0 , 1 } }
dir_matrices = dir
rhotator._facedir_transform = { }
for facedir = 0 , 23 do
local direction = math.floor ( facedir / 4 )
local rotation = facedir % 4
local transform = dir [ direction ] * rot [ rotation ]
rhotator._facedir_transform [ facedir ] = transform
end
end
init_transforms ( )
2018-06-20 06:00:59 -07:00
-- ============================================================
2018-06-19 15:59:31 -07:00
-- helper functions
local function cross_product ( a , b )
return vector.new (
a.y * b.z - a.z * b.y ,
a.z * b.x - a.x * b.z ,
a.x * b.y - a.y * b.x
)
end
local function extract_main_axis ( dir )
local axes = { " x " , " y " , " z " }
local axis = 1
local max = 0
for i = 1 , 3 do
local abs = math.abs ( dir [ axes [ i ] ] )
if abs > max then
axis = i
max = abs
end
end
return axes [ axis ]
end
local function sign ( num )
return ( num < 0 ) and - 1 or 1
end
local function extract_unit_vectors ( player , pointed_thing )
assert ( pointed_thing.type == " node " )
local abs_face_pos = minetest.pointed_thing_to_face_pos ( player , pointed_thing )
local pos = pointed_thing.under
local f = vector.subtract ( abs_face_pos , pos )
local facedir = 0
local primary = 0
local m1 , m2
local unit_direction = vector.new ( )
local unit_rotation = vector.new ( )
local rotation = vector.new ( )
if math.abs ( f.y ) == 0.5 then
unit_direction.y = sign ( f.y )
rotation.x = f.x
rotation.z = f.z
elseif math.abs ( f.z ) == 0.5 then
unit_direction.z = sign ( f.z )
rotation.x = f.x
rotation.y = f.y
else
unit_direction.x = sign ( f.x )
rotation.y = f.y
rotation.z = f.z
end
local main_axis = extract_main_axis ( rotation )
unit_rotation [ main_axis ] = sign ( rotation [ main_axis ] )
return {
back = unit_direction ,
wrap = unit_rotation ,
thumb = cross_product ( unit_direction , unit_rotation ) ,
}
end
local function get_facedir_transform ( facedir )
return rhotator._facedir_transform [ facedir ] or rhotator._facedir_transform [ 0 ]
end
2019-02-09 04:05:41 -08:00
-- =================================================================================
-- 'facedirs' table and 'matrix_to_facedir' function kindly provided by Pedro Gimeno
-- https://notabug.org/pgimeno/Gists/src/matrix-to-facedir/matrix_to_facedir.lua
-- (slightly adapted here for the different matrix format)
local facedirs = {
[ - 246 ] = 19 ,
[ - 244 ] = 10 ,
[ - 242 ] = 4 ,
[ - 240 ] = 13 ,
[ - 90 ] = 21 ,
[ - 82 ] = 20 ,
[ - 80 ] = 22 ,
[ - 72 ] = 23 ,
[ - 36 ] = 7 ,
[ - 30 ] = 18 ,
[ - 24 ] = 12 ,
[ - 18 ] = 9 ,
[ 18 ] = 11 ,
[ 24 ] = 16 ,
[ 30 ] = 14 ,
[ 36 ] = 5 ,
[ 72 ] = 3 ,
[ 80 ] = 2 ,
[ 82 ] = 0 ,
[ 90 ] = 1 ,
[ 240 ] = 17 ,
[ 242 ] = 6 ,
[ 244 ] = 8 ,
[ 246 ] = 15 ,
}
local function matrix_to_facedir ( matrix )
local index = matrix [ 1 ] [ 1 ] + 3 * matrix [ 1 ] [ 2 ] + 9 * matrix [ 1 ] [ 3 ]
+ 27 * matrix [ 2 ] [ 1 ] + 81 * matrix [ 2 ] [ 2 ] + 243 * matrix [ 2 ] [ 3 ]
return facedirs [ index ] or 0
2018-06-19 15:59:31 -07:00
end
local function vector_to_dir_index ( vec )
local main_axis = extract_main_axis ( vec )
if main_axis == " x " then return ( vec.x > 0 ) and POS.X or NEG.X end
if main_axis == " z " then return ( vec.z > 0 ) and POS.Z or NEG.Z end
return ( vec.y > 0 ) and POS.Y or NEG.Y
end
2019-02-02 08:06:06 -08:00
-- ========================================================================
-- customization helpers
local function copy_file ( source , dest )
local src_file = io.open ( source , " rb " )
if not src_file then
return false , " copy_file() unable to open source for reading "
end
local src_data = src_file : read ( " *all " )
src_file : close ( )
local dest_file = io.open ( dest , " wb " )
if not dest_file then
return false , " copy_file() unable to open dest for writing "
end
dest_file : write ( src_data )
dest_file : close ( )
return true , " files copied successfully "
end
local function custom_or_default ( modname , path , filename )
local default_filename = " default/ " .. filename
local full_filename = path .. " /custom. " .. filename
local full_default_filename = path .. " / " .. default_filename
os.rename ( path .. " / " .. filename , full_filename )
local file = io.open ( full_filename , " rb " )
if not file then
minetest.debug ( " [ " .. modname .. " ] Copying " .. default_filename .. " to " .. filename .. " (path: " .. path .. " ) " )
local success , err = copy_file ( full_default_filename , full_filename )
if not success then
minetest.debug ( " [ " .. modname .. " ] " .. err )
return false
end
file = io.open ( full_filename , " rb " )
if not file then
minetest.debug ( " [ " .. modname .. " ] Unable to load " .. filename .. " file from path " .. path )
return false
end
end
file : close ( )
return full_filename
end
2018-06-20 06:00:59 -07:00
-- ============================================================
2018-06-19 15:59:31 -07:00
-- rhotator main
2019-02-04 14:09:03 -08:00
local function rotate_main ( param2_rotation , player , pointed_thing , click , rot_index , sneak )
2018-06-19 15:59:31 -07:00
local unit = extract_unit_vectors ( player , pointed_thing )
2018-06-20 06:00:59 -07:00
local current_pos = pointed_thing.under
2019-02-02 08:06:06 -08:00
2018-06-20 06:00:59 -07:00
local message
2018-06-19 15:59:31 -07:00
local transform = false
2018-06-20 06:00:59 -07:00
local rotation = rot_matrices [ rot_index ]
2019-02-02 08:06:06 -08:00
2018-06-19 15:59:31 -07:00
if click == PRIMARY_BTN then
transform = dir_matrices [ vector_to_dir_index ( unit.thumb ) ]
2019-02-04 14:09:03 -08:00
if sneak then
2018-06-20 06:00:59 -07:00
rotation = rot_matrices [ ( rot_index + 2 ) % 4 ]
2019-02-02 08:06:06 -08:00
message = " Pulled closest edge "
2018-06-20 01:18:52 -07:00
else
2019-02-02 08:06:06 -08:00
message = " Pushed closest edge "
2018-06-20 01:18:52 -07:00
end
2018-06-19 15:59:31 -07:00
else
transform = dir_matrices [ vector_to_dir_index ( unit.back ) ]
2019-02-04 14:09:03 -08:00
if sneak then
2018-06-20 06:00:59 -07:00
rotation = rot_matrices [ ( rot_index + 2 ) % 4 ]
2019-02-02 08:06:06 -08:00
message = " Rotated pointed face counter-clockwise "
2018-06-20 01:18:52 -07:00
else
2019-02-02 08:06:06 -08:00
message = " Rotated pointed face clockwise "
2018-06-20 01:18:52 -07:00
end
2018-06-19 15:59:31 -07:00
end
2018-06-20 06:00:59 -07:00
local start = get_facedir_transform ( param2_rotation )
2018-06-20 01:18:52 -07:00
local stop = transform * rotation * transform : invert ( ) * start
2018-06-20 06:00:59 -07:00
return matrix_to_facedir ( stop ) , message
2019-02-02 08:06:06 -08:00
2018-06-20 06:00:59 -07:00
end
2018-06-19 15:59:31 -07:00
2018-06-20 06:00:59 -07:00
-- ============================================================
-- param2 handlers
local handlers = { }
2019-02-04 14:09:03 -08:00
function handlers . facedir ( node , player , pointed_thing , click , sneak )
2018-06-20 06:00:59 -07:00
local rotation = node.param2 % 32 -- get first 5 bits
local remaining = node.param2 - rotation
local rotate_90deg_clockwise = 1
2019-02-04 14:09:03 -08:00
local rotation_result , message = rotate_main ( rotation , player , pointed_thing , click , rotate_90deg_clockwise , sneak )
2019-02-02 08:06:06 -08:00
2019-02-04 14:09:03 -08:00
local playername = player and player : get_player_name ( ) or " "
2018-06-22 08:55:43 -07:00
if storage : get_int ( " memory_ " .. playername ) == 1 then
facedir_memory [ playername ] = rotation_result
end
2019-02-02 08:06:06 -08:00
2018-06-20 06:00:59 -07:00
return rotation_result + remaining , message
end
handlers.colorfacedir = handlers.facedir
2018-06-20 06:02:14 -07:00
-- ============================================================
-- Replicate default screwdriver behavior for wallmounted nodes
-- 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
local wallmounted_tbl = {
[ PRIMARY_BTN ] = { [ 2 ] = 5 , [ 3 ] = 4 , [ 4 ] = 2 , [ 5 ] = 3 , [ 1 ] = 0 , [ 0 ] = 1 } ,
[ SECONDARY_BTN ] = { [ 2 ] = 5 , [ 3 ] = 4 , [ 4 ] = 2 , [ 5 ] = 1 , [ 1 ] = 0 , [ 0 ] = 3 }
}
function handlers . wallmounted ( node , player , pointed_thing , click )
local pos = pointed_thing.under
local rotation = node.param2 % 8 -- get first 3 bits
local other = node.param2 - rotation
rotation = wallmounted_tbl [ click ] [ 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 [ click ] [ rotation ] or 0
else
break
end
end
end
return rotation + other , " Wallmounted node rotated with default screwdriver behavior "
end
handlers.colorwallmounted = handlers.wallmounted
2018-06-22 08:55:43 -07:00
-- ============================================================
2019-02-04 14:09:03 -08:00
-- rotation memory, flags and placement
2018-06-22 08:55:43 -07:00
2019-02-04 14:09:03 -08:00
local function flag_helper ( playername , key_prefix , flag , readable , use_hud )
2018-06-22 08:55:43 -07:00
local player = minetest.get_player_by_name ( playername )
2019-02-04 14:09:03 -08:00
if not player then return end
local key = key_prefix .. " _ " .. playername
2021-06-23 11:20:24 -07:00
if key_prefix == " server_require_tool " then
key = " server_require_tool "
end
local curval = storage : get ( key )
if curval ~= nil then
curval = storage : get_int ( key )
end
local newval = nil
local statuses = { " off " , " on " , " auto " }
2019-02-04 14:09:03 -08:00
if flag == " off " then
newval = 0
storage : set_int ( key , newval )
elseif flag == " on " then
newval = 1
storage : set_int ( key , newval )
elseif key_prefix == " memory " and flag == " auto " then
newval = 2
storage : set_int ( key , newval )
2021-06-23 11:20:24 -07:00
elseif key_prefix == " require_tool " and ( flag == " default " or curval == nil ) then
newval = 2
statuses [ 3 ] = " server default "
storage : set_string ( key , " " )
2019-02-04 14:09:03 -08:00
elseif flag then
2018-06-22 08:55:43 -07:00
rhotator.command ( playername , " " )
return
end
2021-06-23 11:20:24 -07:00
if curval == nil then
curval = 0
end
if newval ~= nil then
curval = newval
end
local status = statuses [ curval + 1 ]
2019-02-04 14:09:03 -08:00
if use_hud then
2021-06-23 11:20:24 -07:00
notify ( playername , readable .. " is " .. status )
2019-02-04 14:09:03 -08:00
else
2021-06-23 11:20:24 -07:00
minetest.chat_send_player ( playername , " [rhotator] " .. readable .. " is " .. status )
2019-02-04 14:09:03 -08:00
end
2018-06-22 08:55:43 -07:00
end
2019-02-04 14:09:03 -08:00
local copy_rotation_callback
2019-02-02 08:06:06 -08:00
2021-06-21 07:53:27 -07:00
local rhotator_tools = {
" rhotator:screwdriver " ,
" rhotator:screwdriver_alt " ,
" rhotator:memory " ,
" rhotator:screwdriver_multi " ,
}
local function rhotator_inventory_contains_tool ( player )
local player_inv = player : get_inventory ( )
local holding_tool = false
for _ , tool in pairs ( rhotator_tools ) do
if player_inv : contains_item ( " main " , tool ) then
holding_tool = true
break
end
end
return holding_tool
end
2019-02-04 14:09:03 -08:00
local function rhotator_on_placenode ( pos , newnode , player , oldnode , itemstack , pointed_thing )
local playername = player and player : get_player_name ( ) or " "
2021-06-21 07:53:27 -07:00
2021-06-23 11:20:24 -07:00
local require_tool = false
if storage : get ( " require_tool_ " .. playername ) == nil then
require_tool = storage : get_int ( " server_require_tool " ) == 1
else
require_tool = storage : get_int ( " require_tool_ " .. playername ) == 1
end
2021-06-21 07:53:27 -07:00
if require_tool and not rhotator_inventory_contains_tool ( player ) then
-- notify(player, "Required tool not found in inventory")
return
end
2018-06-22 08:55:43 -07:00
local key = " memory_ " .. playername
2019-02-04 14:09:03 -08:00
local memory = storage : get_int ( key )
if memory == OFF then
-- notify(player, "Default placement (memory placement is off)")
return
end
2018-06-22 08:55:43 -07:00
2019-02-04 14:09:03 -08:00
if memory == AUTO then
if not copy_rotation_callback ( true , player , pointed_thing ) then return end
memory = ON
end
2018-06-22 08:55:43 -07:00
local new_rotation = facedir_memory [ playername ]
2019-02-04 14:09:03 -08:00
if memory == ON and not new_rotation then
notify ( player , " Default placement (no stored rotation) " )
return
end
2019-02-02 08:06:06 -08:00
2018-06-22 08:55:43 -07:00
local nodedef = minetest.registered_nodes [ newnode.name ]
2019-02-04 14:09:03 -08:00
if not nodedef then
notify.warning ( player , " Unregistered node placed " )
return
end
2018-06-22 08:55:43 -07:00
local paramtype2 = nodedef.paramtype2
2019-02-04 14:09:03 -08:00
if paramtype2 ~= " facedir " and paramtype2 ~= " colorfacedir " then
notify.warning ( player , " Default placement (can't rotate nodes of this type) " )
return
end
2018-06-22 08:55:43 -07:00
local old_rotation = newnode.param2 % 32 -- get first 5 bits
local remaining = newnode.param2 - old_rotation
2019-02-04 14:09:03 -08:00
local new_param2 = new_rotation + remaining
local click = SECONDARY_BTN
if not rhotator.check_on_rotate_handler ( pos , newnode , nodedef , player , click , new_param2 ) then return end
newnode.param2 = new_param2
2018-06-22 08:55:43 -07:00
minetest.swap_node ( pos , newnode )
minetest.check_for_falling ( pos )
2019-02-02 08:06:06 -08:00
2019-02-04 14:09:03 -08:00
notify ( player , " Placed node according to previous rotation " )
2018-06-22 08:55:43 -07:00
end
2018-06-20 06:00:59 -07:00
-- ============================================================
-- interaction
2018-06-19 15:59:31 -07:00
2018-06-22 08:55:43 -07:00
function rhotator . command ( playername , param )
if param == " " then
2019-02-04 14:09:03 -08:00
minetest.chat_send_player ( playername , " /rhotator: " .. rhotator_command_description )
2018-06-22 08:55:43 -07:00
return
end
local params = param : split ( " " )
2019-02-04 14:09:03 -08:00
local command = params [ 1 ]
table.remove ( params , 1 )
if command == " memory " then
flag_helper ( playername , " memory " , params [ 1 ] , " Rotation memory " )
2019-02-02 08:06:06 -08:00
return
2021-06-21 07:53:27 -07:00
elseif command == " require_tool " then
flag_helper ( playername , " require_tool " , params [ 1 ] , " Tool in inventory requirement " )
return
2019-02-04 14:09:03 -08:00
elseif command == " multi " then
command = params [ 1 ]
table.remove ( params , 1 )
if command == " invert_buttons " then
flag_helper ( playername , " multi_invert_buttons " , params [ 1 ] , " Multitool button inversion " )
return
elseif command == " invert_sneak " then
flag_helper ( playername , " multi_invert_sneak " , params [ 1 ] , " Multitool sneak inversion " )
return
elseif command == nil then
rhotator.command_describe_multi ( playername )
return
end
2021-06-23 11:20:24 -07:00
elseif command == " server_require_tool " then
if minetest.get_player_privs ( playername ) . rhotator_admin or not params [ 1 ] then
flag_helper ( playername , " server_require_tool " , params [ 1 ] , " Server default for tool in inventory requirement " )
else
minetest.chat_send_player ( playername , " [rhotator] Missing 'rhotator_admin' privilege " )
end
return
2018-06-22 08:55:43 -07:00
end
minetest.chat_send_player ( playername , " [rhotator] unsupported param: " .. param )
end
2019-02-04 14:09:03 -08:00
rhotator.command_describe_multi = function ( playername )
rhotator.command ( playername , " memory " )
2021-06-21 07:53:27 -07:00
rhotator.command ( playername , " require_tool " )
2021-06-23 11:20:24 -07:00
rhotator.command ( playername , " server_require_tool " )
2019-02-04 14:09:03 -08:00
rhotator.command ( playername , " multi invert_buttons " )
rhotator.command ( playername , " multi invert_sneak " )
minetest.chat_send_player ( playername , table.concat ( {
" [rhotator] Current Multitool configuration: " ,
" Left-click: " .. get_multi_action ( playername , true , false ) [ 2 ] ,
" Sneak-left-click: " .. get_multi_action ( playername , true , true ) [ 2 ] ,
" Right-click: " .. get_multi_action ( playername , false , false ) [ 2 ] ,
" Sneak-right-click: " .. get_multi_action ( playername , false , true ) [ 2 ] ,
} , " \n " ) )
end
local function interact ( player , pointed_thing , click , sneak )
2018-06-20 06:00:59 -07:00
if pointed_thing.type ~= " node " then
return
end
local pos = pointed_thing.under
2019-02-04 14:09:03 -08:00
local playername = player and player : get_player_name ( ) or " "
if minetest.is_protected ( pos , playername ) then
2018-06-25 16:32:20 -07:00
notify.error ( player , " You're not authorized to alter nodes in this area " )
2019-02-04 14:09:03 -08:00
minetest.record_protection_violation ( pos , playername )
2018-06-20 06:00:59 -07:00
return
end
local node = minetest.get_node ( pointed_thing.under )
local nodedef = minetest.registered_nodes [ node.name ]
if not nodedef then
2018-06-25 16:32:20 -07:00
notify.error ( player , " Unsupported node type: " .. node.name )
2018-06-20 06:00:59 -07:00
return
end
local handler = handlers [ nodedef.paramtype2 ]
2019-02-02 08:06:06 -08:00
2019-02-04 14:09:03 -08:00
if nodedef.can_dig and not nodedef.can_dig ( pos , player ) then
2018-06-25 16:32:20 -07:00
notify.warning ( player , " Rotation prevented by can_dig() checks " )
2018-06-20 06:00:59 -07:00
return
elseif not handler then
2018-06-25 16:32:20 -07:00
notify.warning ( player , " Cannot rotate node with paramtype2 == " .. nodedef.paramtype2 )
2018-06-20 06:00:59 -07:00
return
end
2019-02-02 08:06:06 -08:00
2019-02-04 14:09:03 -08:00
local new_param2 , handler_message = handler ( node , player , pointed_thing , click , sneak )
2019-02-08 18:15:09 -08:00
-- Node provides a handler, so let the handler decide instead if the node can be rotated
local pass = rhotator.check_on_rotate_handler ( pos , node , nodedef , player , click , new_param2 )
if not pass then return end
2019-02-04 14:09:03 -08:00
2018-06-20 06:00:59 -07:00
node.param2 = new_param2
minetest.swap_node ( pos , node )
minetest.check_for_falling ( pos )
if handler_message then
notify ( player , handler_message )
end
2019-02-04 14:09:03 -08:00
end
2019-02-02 08:06:06 -08:00
2019-02-04 14:09:03 -08:00
rhotator.check_on_rotate_handler = function ( pos , node , nodedef , player , click , new_param2 )
if nodedef.on_rotate == false then
notify.warning ( player , " Rotation prevented by on_rotate == false " )
return false
elseif nodedef.on_rotate then
-- Copy pos and node because callback can modify it
2019-05-13 06:54:14 -07:00
local pass_node = table.copy ( node )
2019-02-04 14:09:03 -08:00
local pass_pos = vector.new ( pos )
local result = nodedef.on_rotate ( pass_pos , pass_node , player , click , new_param2 )
if result == true then
notify ( player , " Rotation reportedly performed by on_rotate() " )
return false
end
notify.warning ( player , " Rotation disallowed by on_rotate() return value " )
return false
end
return true
2019-02-02 08:06:06 -08:00
end
local function primary_callback ( itemstack , player , pointed_thing )
2019-02-04 14:09:03 -08:00
local sneak = player and player : get_player_control ( ) . sneak or false
interact ( player , pointed_thing , PRIMARY_BTN , sneak )
2019-02-02 13:10:32 -08:00
return itemstack
2019-02-02 08:06:06 -08:00
end
local function secondary_callback ( itemstack , player , pointed_thing )
2019-02-04 14:09:03 -08:00
local sneak = player and player : get_player_control ( ) . sneak or false
interact ( player , pointed_thing , SECONDARY_BTN , sneak )
2019-02-02 13:10:32 -08:00
return itemstack
end
local function toggle_memory_callback ( itemstack , player , pointed_thing )
2019-02-04 14:09:03 -08:00
local playername = player and player : get_player_name ( ) or " "
2019-02-02 13:10:32 -08:00
local key = " memory_ " .. playername
2019-02-04 14:09:03 -08:00
local flag = storage : get_int ( key ) or 0
2019-02-09 04:17:01 -08:00
flag = flag == 2 and 0 or flag + 1
2019-02-04 14:09:03 -08:00
flag = ( { " off " , " on " , " auto " } ) [ flag + 1 ]
local use_hud = true
flag_helper ( playername , " memory " , flag , " Rotation memory " , use_hud )
2019-02-02 13:10:32 -08:00
return itemstack
end
2019-02-04 14:09:03 -08:00
copy_rotation_callback = function ( itemstack , player , pointed_thing )
local playername = player and player : get_player_name ( ) or " "
2019-02-02 13:10:32 -08:00
if pointed_thing.type ~= " node " then
return
end
local pos = pointed_thing.under
local node = minetest.get_node ( pointed_thing.under )
local nodedef = minetest.registered_nodes [ node.name ]
if not nodedef then
notify.error ( player , " Unsupported node type: " .. node.name )
return
end
local paramtype2 = nodedef.paramtype2
if paramtype2 ~= " facedir " and paramtype2 ~= " colorfacedir " then
notify.warning ( player , " Unsupported node type: " .. node.name .. " - cannot copy rotation " )
return
end
local rotation = node.param2 % 32 -- get first 5 bits
facedir_memory [ playername ] = rotation
notify ( player , " Copied rotation from node: " .. node.name )
return itemstack
2018-06-19 15:59:31 -07:00
end
minetest.register_tool ( " rhotator:screwdriver " , {
2019-02-02 08:06:06 -08:00
description = " Rhotator Screwdriver \n Left-click pushes edge \n Right-click rotates face \n Hold sneak to invert direction " ,
2018-06-19 15:59:31 -07:00
inventory_image = " rhotator.png " ,
2019-02-02 08:06:06 -08:00
on_use = primary_callback ,
on_place = secondary_callback ,
2018-06-19 15:59:31 -07:00
} )
2019-02-02 08:06:06 -08:00
minetest.register_tool ( " rhotator:screwdriver_alt " , {
description = " Rhotator Screwdriver Alt \n Left-click rotates face \n Right-click pushes edge \n Hold sneak to invert direction " ,
inventory_image = " rhotator-alt.png " ,
on_use = secondary_callback ,
on_place = primary_callback ,
2018-06-19 15:59:31 -07:00
} )
2019-02-02 13:10:32 -08:00
minetest.register_tool ( " rhotator:memory " , {
description = " Rhotator Memory Tool \n Left-click toggles rotation memory \n Right-click copies rotation from pointed node " ,
inventory_image = " rhotator-memory.png " ,
on_use = toggle_memory_callback ,
on_place = copy_rotation_callback ,
} )
2019-02-04 14:09:03 -08:00
local function multi_callback ( itemstack , player , pointed_thing , button )
local playername = player and player : get_player_name ( ) or " "
local primary = button == PRIMARY_BTN
local sneak = player and player : get_player_control ( ) . sneak
local action = get_multi_action ( playername , primary , sneak ) [ 1 ]
if action == " memory " then
toggle_memory_callback ( itemstack , player , pointed_thing )
elseif action == " copy " then
copy_rotation_callback ( itemstack , player , pointed_thing )
elseif action == " rotate " then
interact ( player , pointed_thing , SECONDARY_BTN , false )
elseif action == " push " then
interact ( player , pointed_thing , PRIMARY_BTN , false )
else
notify.error ( playername , " Get a better developer " )
end
end
local function multi_primary_callback ( itemstack , player , pointed_thing )
multi_callback ( itemstack , player , pointed_thing , PRIMARY_BTN )
return itemstack
end
local function multi_secondary_callback ( itemstack , player , pointed_thing )
multi_callback ( itemstack , player , pointed_thing , SECONDARY_BTN )
return itemstack
end
minetest.register_tool ( " rhotator:screwdriver_multi " , {
description = " Rhotator Screwdriver Multitool \n Combines rotate, push, memory and copy in a single tool \n Run '/rhotator' in the chat for help " ,
inventory_image = " rhotator-multi.png " ,
on_use = multi_primary_callback ,
on_place = multi_secondary_callback ,
on_secondary_use = multi_secondary_callback ,
} )
2018-06-19 15:59:31 -07:00
minetest.register_node ( " rhotator:cube " , {
drawtype = " mesh " ,
mesh = " rhotocube.obj " ,
tiles = { " rhotocube.png " } ,
paramtype2 = " facedir " ,
description = " Rhotator Testing Cube " ,
walkable = true ,
groups = { snappy = 2 , choppy = 2 , oddly_breakable_by_hand = 3 } ,
} )
2019-02-02 08:06:06 -08:00
local full_recipes_filename = custom_or_default ( " rhotator " , mod_path , " recipes.lua " )
if not full_recipes_filename then
2019-02-02 13:10:32 -08:00
error ( " [rhotator] unable to find " .. mod_path .. " /custom.recipes.lua " )
2019-02-02 08:06:06 -08:00
end
local recipes = dofile ( full_recipes_filename ) ;
if type ( recipes ) ~= " table " then
2019-02-02 13:10:32 -08:00
error ( " [rhotator] malformed file " .. mod_path .. " /custom.recipes.lua " )
2019-02-02 08:06:06 -08:00
end
2019-02-02 13:10:32 -08:00
for _ , item in ipairs ( recipes ) do
if type ( item ) == " table " and type ( item.output ) == " string " and type ( item.recipe ) == " table " then
minetest.register_craft ( item )
end
2019-02-02 08:06:06 -08:00
end
2018-06-22 08:55:43 -07:00
minetest.register_on_placenode ( rhotator_on_placenode )
2019-02-02 08:06:06 -08:00
minetest.register_chatcommand ( " rhotator " , {
2019-02-04 14:09:03 -08:00
description = rhotator_command_description ,
2019-02-02 13:10:32 -08:00
func = rhotator.command
2019-02-02 08:06:06 -08:00
} )
2021-06-23 11:20:24 -07:00
minetest.register_privilege ( " rhotator_admin " , {
description = " Can alter the 'server_require_tool' flag via the `/rhotator` command " ,
give_to_singleplayer = true ,
} )