Support protected areas checks and on_rotate() overrides

Cleanup and refactoring
master
entuland 2018-06-20 15:00:59 +02:00
parent 7f3ed66682
commit 6378cbf733
2 changed files with 105 additions and 39 deletions

View File

@ -19,6 +19,7 @@ These are the factors that affect the results of a click:
- the face you point at - the face you point at
- where on that face you point - where on that face you point
- what button you click - what button you click
- whether or not you hold down the sneak key
You will always be able to predict exactly the effect of the Rhotator Screwdriver. You will always be able to predict exactly the effect of the Rhotator Screwdriver.
@ -85,3 +86,4 @@ Here are possible messages you can receive:
- Cannot rotate node with paramtype2 == glasslikeliquidlevel - Cannot rotate node with paramtype2 == glasslikeliquidlevel
- Unsupported node type: modname:nodename - Unsupported node type: modname:nodename
plus some more messages warning about protected areas or rotations performed or prevented by custom on_rotate() handlers.

142
init.lua
View File

@ -1,8 +1,8 @@
rhotator = {} rhotator = {}
rhotator.mod_path = minetest.get_modpath(minetest.get_current_modname()) local mod_path = minetest.get_modpath(minetest.get_current_modname())
local matrix = dofile(rhotator.mod_path .. "/lib/matrix.lua") local matrix = dofile(mod_path .. "/lib/matrix.lua")
-- constants -- constants
@ -15,9 +15,13 @@ POS.X = 3
NEG.X = 4 NEG.X = 4
NEG.Y = 5 NEG.Y = 5
PRIMARY_BTN = 1 local PRIMARY_BTN = 1
SECONDARY_BTN = 2 local SECONDARY_BTN = 2
rhotator.PRIMARY_BTN = PRIMARY_BTN
rhotator.SECONDARY_BTN = SECONDARY_BTN
-- ============================================================
-- helper variables -- helper variables
local rot_matrices = {} local rot_matrices = {}
@ -26,6 +30,7 @@ local dir_matrices = {}
local huds = {} local huds = {}
local hud_timeout_seconds = 3 local hud_timeout_seconds = 3
-- ============================================================
-- init -- init
local function init_transforms() local function init_transforms()
@ -94,6 +99,7 @@ end
init_transforms() init_transforms()
-- ============================================================
-- helper functions -- helper functions
local function cross_product(a, b) local function cross_product(a, b)
@ -180,13 +186,14 @@ local function vector_to_dir_index(vec)
return (vec.y > 0) and POS.Y or NEG.Y return (vec.y > 0) and POS.Y or NEG.Y
end end
-- ============================================================
-- hud functions -- hud functions
local function hud_remove(player) local function hud_remove(player)
local playername = player:get_player_name() local playername = player:get_player_name()
local hud = huds[playername] local hud = huds[playername]
if not hud then return end if not hud then return end
if os.time() < hud_duration_time + hud.time then if os.time() < hud_timeout_seconds + hud.time then
return return
end end
player:hud_remove(hud.id) player:hud_remove(hud.id)
@ -225,71 +232,128 @@ local function notify(player, message)
end) end)
end end
-- ============================================================
-- rhotator main -- rhotator main
local function interact(itemstack, player, pointed_thing, click) local function rotate_main(param2_rotation, player, pointed_thing, click, rot_index)
if pointed_thing.type ~= "node" then
return
end
local node = minetest.get_node_or_nil(pointed_thing.under)
local def = minetest.registered_nodes[node.name]
if not node or not def then
notify(player, "Unsupported node type: " .. node.name)
return
end
if def.paramtype2 ~= "facedir" then
notify(player, "Cannot rotate node with paramtype2 == " .. def.paramtype2)
return
end
local unit = extract_unit_vectors(player, pointed_thing) local unit = extract_unit_vectors(player, pointed_thing)
local current_pos = pointed_thing.under
local message
local transform = false local transform = false
local rotation = rot_matrices[1] local rotation = rot_matrices[rot_index]
local controls = player:get_player_control() local controls = player:get_player_control()
if click == PRIMARY_BTN then if click == PRIMARY_BTN then
transform = dir_matrices[vector_to_dir_index(unit.thumb)] transform = dir_matrices[vector_to_dir_index(unit.thumb)]
if controls.sneak then if controls.sneak then
rotation = rot_matrices[3] rotation = rot_matrices[(rot_index + 2) % 4]
notify(player, "Pulled closest edge (sneak + left click)") message = "Pulled closest edge (sneak + left click)"
else else
notify(player, "Pushed closest edge (left click)") message = "Pushed closest edge (left click)"
end end
else else
transform = dir_matrices[vector_to_dir_index(unit.back)] transform = dir_matrices[vector_to_dir_index(unit.back)]
if controls.sneak then if controls.sneak then
rotation = rot_matrices[3] rotation = rot_matrices[(rot_index + 2) % 4]
notify(player, "Rotated pointed face counter-clockwise (sneak + right click)") message = "Rotated pointed face counter-clockwise (sneak + right click)"
else else
notify(player, "Rotated pointed face clockwise (right click)") message = "Rotated pointed face clockwise (right click)"
end end
end end
local start = get_facedir_transform(node.param2) local start = get_facedir_transform(param2_rotation)
local stop = transform * rotation * transform:invert() * start local stop = transform * rotation * transform:invert() * start
return matrix_to_facedir(stop), message
end
minetest.set_node(pointed_thing.under,{ -- ============================================================
name = node.name, -- param2 handlers
param1 = node.param1,
param2 = matrix_to_facedir(stop),
})
local handlers = {}
function handlers.facedir(node, player, pointed_thing, click)
local rotation = node.param2 % 32 -- get first 5 bits
local remaining = node.param2 - rotation
local rotate_90deg_clockwise = 1
local rotation_result, message = rotate_main(rotation, player, pointed_thing, click, rotate_90deg_clockwise)
return rotation_result + remaining, message
end
handlers.colorfacedir = handlers.facedir
-- ============================================================
-- interaction
local function interact(player, pointed_thing, click)
if pointed_thing.type ~= "node" then
return
end
local pos = pointed_thing.under
if minetest.is_protected(pos, player:get_player_name()) then
notify(player, "You're not authorized to alter nodes in this area")
minetest.record_protection_violation(pos, player:get_player_name())
return
end
local node = minetest.get_node(pointed_thing.under)
local nodedef = minetest.registered_nodes[node.name]
if not nodedef then
notify(player, "Unsupported node type: " .. node.name)
return
end
local handler = handlers[nodedef.paramtype2]
-- Node provides a handler, so let the handler decide instead if the node can be rotated
if nodedef.on_rotate then
-- Copy pos and node because callback can modify it
local pass_node = {name = node.name, param1 = node.param1, param2 = node.param2}
local pass_pos = vector.new(pos)
local result = nodedef.on_rotate(pass_pos, pass_node, player, click, node.param2)
if result == true then
notify(player, "Rotation reportedly performed by on_rotate()")
return
else
notify(player, "Rotation disallowed by on_rotate() return value")
return
end
elseif nodedef.on_rotate == false then
notify(player, "Rotation prevented by on_rotate == false")
return
elseif nodedef.can_dig and not nodedef.can_dig(pos, player) then
notify(player, "Rotation prevented by can_dig() checks")
return
elseif not handler then
notify(player, "Cannot rotate node with paramtype2 == " .. nodedef.paramtype2)
return
end
local new_param2, handler_message = handler(node, player, pointed_thing, click)
node.param2 = new_param2
minetest.swap_node(pos, node)
minetest.check_for_falling(pos)
if handler_message then
notify(player, handler_message)
end
return
end end
minetest.register_tool("rhotator:screwdriver", { minetest.register_tool("rhotator:screwdriver", {
description = "Rhotator Screwdriver (left-click pushes edge, right-click rotates face)", description = "Rhotator Screwdriver (left-click pushes edge, right-click rotates face)",
inventory_image = "rhotator.png", inventory_image = "rhotator.png",
on_use = function(itemstack, player, pointed_thing) on_use = function(itemstack, player, pointed_thing)
interact(itemstack, player, pointed_thing, PRIMARY_BTN) interact(player, pointed_thing, PRIMARY_BTN)
return itemstack return itemstack
end, end,
on_place = function(itemstack, player, pointed_thing) on_place = function(itemstack, player, pointed_thing)
interact(itemstack, player, pointed_thing, SECONDARY_BTN) interact(player, pointed_thing, SECONDARY_BTN)
return itemstack return itemstack
end, end,
}) })