Add screwdriver callbacks, and code them for doors and beds
authorNovatux <nathanael.courant@laposte.net>
Wed, 13 May 2015 09:49:11 +0000 (11:49 +0200)
committerNovatux <nathanael.courant@laposte.net>
Thu, 14 May 2015 08:24:56 +0000 (10:24 +0200)
game_api.txt
mods/beds/api.lua
mods/doors/init.lua
mods/screwdriver/init.lua

index fa9d6fc560009a6dc3a4d044746d67d4fe44a924..a3edcfe8a750429b58fa78585f3052ce8b13ac6c 100644 (file)
@@ -144,6 +144,21 @@ farming.register_plant(name, Plant definition)
        maxlight = default.LIGHT_MAX -- Maximum light to grow
 }
 
+Screwdriver API
+---------------
+The screwdriver API allows you to control a node's behaviour when a screwdriver is used on it.
+To use it, add the on_screwdriver function to the node definition.
+on_rotate(pos, node, user, mode, new_param2)
+^ pos: position of the node that the screwdriver is being used on
+^ node: that node
+^ user: the player who used the screwdriver
+^ mode: screwdriver.ROTATE_FACE or screwdriver.ROTATE_AXIS
+^ new_param2: the new value of param2 that would have been set if on_rotate wasn't there
+^ return value: false to disallow rotation, nil to keep default behaviour, true to allow
+  it but to indicate that changed have already been made (so the screwdriver will wear out)
+^ use on_rotate = screwdriver.disallow to always disallow rotation
+^ use on_rotate = screwdriver.rotate_simple to allow only face rotation
+
 Stairs API
 ----------
 The stairs API lets you register stairs and slabs and ensures that they are registered the same way as those
index 320fcfb35dbaa6d4f4100414f7c51b82531edafc..8f924730897baee5ea949658873d9b646bc33078 100644 (file)
@@ -26,10 +26,10 @@ function beds.register_bed(name, def)
                                return true
                        end
                        local dir = minetest.facedir_to_dir(n.param2)
-                       local p = {x=pos.x+dir.x,y=pos.y,z=pos.z+dir.z}
+                       local p = vector.add(pos, dir)
                        local n2 = minetest.get_node_or_nil(p)
-                       local def = minetest.registered_items[n2.name] or nil
-                       if not n2 or not def or not def.buildable_to then
+                       local def = n2 and minetest.registered_items[n2.name]
+                       if not def or not def.buildable_to then
                                minetest.remove_node(pos)
                                return true
                        end
@@ -40,7 +40,7 @@ function beds.register_bed(name, def)
                        local n = minetest.get_node_or_nil(pos)
                        if not n then return end
                        local dir = minetest.facedir_to_dir(n.param2)
-                       local p = {x=pos.x+dir.x,y=pos.y,z=pos.z+dir.z}
+                       local p = vector.add(pos, dir)
                        local n2 = minetest.get_node(p)
                        if minetest.get_item_group(n2.name, "bed") == 2 and n.param2 == n2.param2 then
                                minetest.remove_node(p)
@@ -49,6 +49,37 @@ function beds.register_bed(name, def)
                on_rightclick = function(pos, node, clicker)
                        beds.on_rightclick(pos, clicker)
                end,
+               on_rotate = function(pos, node, user, mode, new_param2)
+                       local dir = minetest.facedir_to_dir(node.param2)
+                       local p = vector.add(pos, dir)
+                       local node2 = minetest.get_node_or_nil(p)
+                       if not node2 or not minetest.get_item_group(node2.name, "bed") == 2 or
+                                       not node.param2 == node2.param2 then
+                               return false
+                       end
+                       if minetest.is_protected(p, user:get_player_name()) then
+                               minetest.record_protection_violation(p, user:get_player_name())
+                               return false
+                       end
+                       if mode ~= screwdriver.ROTATE_FACE then
+                               return false
+                       end
+                       local newp = vector.add(pos, minetest.facedir_to_dir(new_param2))
+                       local node3 = minetest.get_node_or_nil(newp)
+                       local def = node3 and minetest.registered_nodes[node3.name]
+                       if not def or not def.buildable_to then
+                               return false
+                       end
+                       if minetest.is_protected(newp, user:get_player_name()) then
+                               minetest.record_protection_violation(newp, user:get_player_name())
+                               return false
+                       end
+                       node.param2 = new_param2
+                       minetest.swap_node(pos, node)
+                       minetest.remove_node(p)
+                       minetest.set_node(newp, {name = node.name:gsub("%_bottom", "_top"), param2 = new_param2})
+                       return true
+               end,
        })
 
        minetest.register_node(name .. "_top", {
index f3dd16959c9a8a8b085278e9c4ad97ab463194ba..b762751affedb44a27b959648d02b64d891d3200 100644 (file)
@@ -163,6 +163,33 @@ function doors.register_door(name, def)
                return meta:get_string("doors_owner") == pn
        end
 
+       local function on_rotate(pos, node, dir, user, check_name, mode, new_param2)
+               if not check_player_priv(pos, user) then
+                       return false
+               end
+               if mode ~= screwdriver.ROTATE_FACE then
+                       return false
+               end
+
+               pos.y = pos.y + dir
+               if not minetest.get_node(pos).name == check_name then
+                       return false
+               end
+               if minetest.is_protected(pos, user:get_player_name()) then
+                       minetest.record_protection_violation(pos, user:get_player_name())
+                       return false
+               end
+
+               local node2 = minetest.get_node(pos)
+               node2.param2 = (node2.param2 + 1) % 4
+               minetest.swap_node(pos, node2)
+
+               pos.y = pos.y - dir
+               node.param2 = (node.param2 + 1) % 4
+               minetest.swap_node(pos, node)
+               return true
+       end
+
        minetest.register_node(name.."_b_1", {
                tiles = {tb[2], tb[2], tb[2], tb[2], tb[1], tb[1].."^[transformfx"},
                paramtype = "light",
@@ -190,6 +217,10 @@ function doors.register_door(name, def)
                        end
                end,
                
+               on_rotate = function(pos, node, user, mode, new_param2)
+                       return on_rotate(pos, node, 1, user, name.."_t_1", mode)
+               end,
+
                can_dig = check_player_priv,
                sounds = def.sounds,
                sunlight_propagates = def.sunlight,
@@ -223,6 +254,10 @@ function doors.register_door(name, def)
                        end
                end,
                
+               on_rotate = function(pos, node, user, mode, new_param2)
+                       return on_rotate(pos, node, -1, user, name.."_b_1", mode)
+               end,
+
                can_dig = check_player_priv,
                sounds = def.sounds,
                sunlight_propagates = def.sunlight,
@@ -256,6 +291,10 @@ function doors.register_door(name, def)
                        end
                end,
                
+               on_rotate = function(pos, node, user, mode, new_param2)
+                       return on_rotate(pos, node, 1, user, name.."_t_2", mode)
+               end,
+
                can_dig = check_player_priv,
                sounds = def.sounds,
                sunlight_propagates = def.sunlight,
@@ -289,6 +328,10 @@ function doors.register_door(name, def)
                        end
                end,
                
+               on_rotate = function(pos, node, user, mode, new_param2)
+                       return on_rotate(pos, node, -1, user, name.."_b_2", mode)
+               end,
+
                can_dig = check_player_priv,
                sounds = def.sounds,
                sunlight_propagates = def.sunlight,
@@ -392,6 +435,8 @@ function doors.register_trapdoor(name, def)
                minetest.set_node(pos, {name = newname, param1 = node.param1, param2 = node.param2})
        end
 
+       def.on_rotate = screwdriver.rotate_simple
+
        -- Common trapdoor configuration
        def.drawtype = "nodebox"
        def.paramtype = "light"
index 65e7f004e0d43de250336dad5909895638c94564..bccc9fbca2157703f07caffa406f64bf7e01b941 100644 (file)
@@ -1,3 +1,5 @@
+screwdriver = {}
+
 local function nextrange(x, max)
        x = x + 1
        if x > max then
@@ -6,8 +8,16 @@ local function nextrange(x, max)
        return x
 end
 
-local ROTATE_FACE = 1
-local ROTATE_AXIS = 2
+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
 local USES = 200
 
 -- Handles rotation
@@ -25,31 +35,47 @@ local function screwdriver_handler(itemstack, user, pointed_thing, mode)
 
        local node = minetest.get_node(pos)
        local ndef = minetest.registered_nodes[node.name]
-       if not ndef or not ndef.paramtype2 == "facedir" or
-                       (ndef.drawtype == "nodebox" and
-                       not ndef.node_box.type == "fixed") or
-                       node.param2 == nil then
-               return
-       end
-
-       if ndef.can_dig and not ndef.can_dig(pos, user) then
-               return
-       end
-
-       -- Set param2
+       -- Compute param2
        local rotationPart = node.param2 % 32 -- get first 4 bits
        local preservePart = node.param2 - rotationPart
-
        local axisdir = math.floor(rotationPart / 4)
        local rotation = rotationPart - axisdir * 4
-       if mode == ROTATE_FACE then
+       if mode == screwdriver.ROTATE_FACE then
                rotationPart = axisdir * 4 + nextrange(rotation, 3)
-       elseif mode == ROTATE_AXIS then
+       elseif mode == screwdriver.ROTATE_AXIS then
                rotationPart = nextrange(axisdir, 5) * 4
        end
 
-       node.param2 = preservePart + rotationPart
-       minetest.swap_node(pos, node)
+       local new_param2 = preservePart + rotationPart
+       local should_rotate = true
+
+       if ndef and ndef.on_rotate then -- Node provides a handler, so let the handler decide instead if the node can be rotated
+               -- 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)
+               if result == false then -- Disallow rotation
+                       return
+               elseif result == true then
+                       should_rotate = false
+               end
+       else
+               if not ndef or not ndef.paramtype2 == "facedir" or
+                               (ndef.drawtype == "nodebox" and
+                               not ndef.node_box.type == "fixed") or
+                               node.param2 == nil then
+                       return
+               end
+
+               if ndef.can_dig and not ndef.can_dig(pos, user) then
+                       return
+               end
+       end
+
+       if should_rotate then
+               node.param2 = new_param2
+               minetest.swap_node(pos, node)
+       end
 
        if not minetest.setting_getbool("creative_mode") then
                itemstack:add_wear(65535 / (USES - 1))
@@ -63,11 +89,11 @@ minetest.register_tool("screwdriver:screwdriver", {
        description = "Screwdriver (left-click rotates face, right-click rotates axis)",
        inventory_image = "screwdriver.png",
        on_use = function(itemstack, user, pointed_thing)
-               screwdriver_handler(itemstack, user, pointed_thing, ROTATE_FACE)
+               screwdriver_handler(itemstack, user, pointed_thing, screwdriver.ROTATE_FACE)
                return itemstack
        end,
        on_place = function(itemstack, user, pointed_thing)
-               screwdriver_handler(itemstack, user, pointed_thing, ROTATE_AXIS)
+               screwdriver_handler(itemstack, user, pointed_thing, screwdriver.ROTATE_AXIS)
                return itemstack
        end,
 })