Alternative tool with swapped mouse buttons, custom recipes, readme updated with also a youtube video
parent
9fee3779d9
commit
d777debbe9
38
README.md
38
README.md
|
@ -8,6 +8,8 @@ If you like my contributions you may consider reading http://entuland.com/en/sup
|
|||
|
||||
WIP MOD forum thread: https://forum.minetest.net/viewtopic.php?f=9&t=20321
|
||||
|
||||
A silly, incomplete and unscripted video presentation of this mod: https://youtu.be/ESTJ9FYGHh4
|
||||
|
||||
# Dependencies
|
||||
|
||||
A thin wrapper around a very useful library to deal with Matrices:
|
||||
|
@ -25,7 +27,7 @@ A thin wrapper around a very useful library to deal with Matrices:
|
|||
|
||||
# Why yet another screwdriver?
|
||||
|
||||
The default screwdriver included in minetest_game, as well as any other screwdriver mod I have found, operate differently depending on the node's direction and rotation. This means that any given click on a node may produce different results which you cannot predict at a glance.
|
||||
The default screwdriver included in minetest_game, as well as any other screwdriver mod I have found, operate differently depending on the node's direction and rotation. This means that any given click on a node may produce different results which you cannot predict at a glance, unless you're perfectly aware of where the node's main axis is pointing to.
|
||||
|
||||
The Rhotator Screwdriver uses a different approach: the direction and orientation of the node make absolutely no difference.
|
||||
|
||||
|
@ -38,11 +40,11 @@ These are the factors that affect the results of a click:
|
|||
|
||||
You will always be able to predict exactly the effect of the Rhotator Screwdriver.
|
||||
|
||||
Four consecutive clicks of the same button on the same position will always bring the node back to its original direction / orientation.
|
||||
Four consecutive clicks of the same button on the same position will always bring the node back to its original direction / orientation - or even less clicks, if you use the sneak key to invert the rotation direction.
|
||||
|
||||
### Why is it called "Rhotator" and not "Rotator"?
|
||||
|
||||
In mathematics, the greek letter *Rho* is used to indicate some stuff associated to certain types of matrices. Since I'm using matrices to compute the various rotations in the game I thought about including it in the mod's name to reduce the chance of naming conflicts.
|
||||
In mathematics the greek letter *Rho* is used to indicate some stuff associated to certain types of matrices. Since I'm using matrices to compute the various rotations in the game I thought about including it in the mod's name to reduce the chance of naming conflicts.
|
||||
|
||||
# Appearance
|
||||
|
||||
|
@ -58,7 +60,7 @@ The latter two types are handled exactly as the built-in screwdriver of `minetes
|
|||
|
||||
# Usage
|
||||
|
||||
Pretty simple:
|
||||
This is the behavior of the default `rhotator:screwdriver` tool:
|
||||
|
||||
- a right click will rotate the face you're pointing in clockwise direction
|
||||
- the arrow in the Testing Cube shows how the face will rotate when right-clicked
|
||||
|
@ -70,23 +72,31 @@ Pretty simple:
|
|||
- `PS` in the tool stands for `push`
|
||||
- hold the sneak key down while clicking to "pull" instead of "pushing"
|
||||
|
||||
The left-click interaction area is not limited to the edges you can see in the Testing Cube. In reality you can click anywhere in a triangle like this (highlighted here just for convenience, you won't see anything like this in the game):
|
||||
(an alternative `rhotator:screwdriver_alt` tool is available with a sligthly different recipe, the buttons swapped and a corresponding texture with `RT` and `PS` swapped as well)
|
||||
|
||||
The `push` interaction area is not limited to the edges you can see in the Testing Cube. In reality you can click anywhere in a triangle like this (highlighted here just for convenience, you won't see anything like this in the game):
|
||||
|
||||
![Interaction triangle](/screenshots/interaction-triangle.png)
|
||||
|
||||
# Non-full nodes
|
||||
|
||||
Nodes that don't occupy a full cube (such as slabs and stairs) can still be rotated properly, it's enough that you pay attention to the direction of the part you're pointing at - the "stomp" parts of the stairs will behave as the "top" face, the "rise" parts will behave as the "front" face. With the Rhotator Screwdriver there never really is a "top" or a "front" or whatever: the only thing that matters is the face you're pointing at.
|
||||
Nodes that don't occupy a full cube (such as slabs and stairs) can still be rotated properly, it's enough that you pay attention to the direction of the part you're pointing at - the "stomp" parts of the stairs, for example, will behave as the "top" face, the "rise" parts will behave as the "front" face. With the Rhotator Screwdriver there never really is a "top" or a "front" or whatever: the only thing that matters is the face you're pointing at.
|
||||
|
||||
# Crafting
|
||||
|
||||
Rhotator Screwdriver: a stick and a copper ingot;
|
||||
|
||||
![Screwdriver crafting](/screenshots/screwdriver-crafting.png)
|
||||
![Rhotator Screwdriver crafting](/screenshots/rhotator-recipe.png)
|
||||
|
||||
Rhotator Screwdriver Alt: two sticks and a copper ingot;
|
||||
|
||||
![Rhotator Screwdriver Alt crafting](/screenshots/rhotator-alt-recipe.png)
|
||||
|
||||
Rhotator Testing Cube: a Rhotator Screwdriver and any wool block
|
||||
|
||||
![Testing cube crafting](/screenshots/testcube-crafting.png)
|
||||
![Rhotator Testing Cube crafting](/screenshots/rhotator-cube-recipe.png)
|
||||
|
||||
Recipes can be customized by editing the `custom.recipes.lua` file that gets created in the mods' root folder upon first run.
|
||||
|
||||
# Chat commands
|
||||
|
||||
|
@ -95,16 +105,18 @@ Rhotator Testing Cube: a Rhotator Screwdriver and any wool block
|
|||
- `rhotator memory on` enable placement memory
|
||||
- `rhotator memory off` disable placement memory
|
||||
|
||||
Rotation memory starts off by default, it gets stored and recalled for each player between different sessions and between server restarts.
|
||||
|
||||
# Usage feedback
|
||||
|
||||
An HUD message will show usage feedback, in particular it will inform you about nodes that aren't currently supported.
|
||||
An HUD message will show usage feedback, in particular it will inform about nodes that aren't currently supported.
|
||||
|
||||
Here are possible messages you can receive:
|
||||
|
||||
- Rotated pointed face clockwise (right click)
|
||||
- Rotated pointed face counter-clockwise (sneak + right click)
|
||||
- Pushed closest edge (left click)
|
||||
- Pulled closest edge (sneak + left click)
|
||||
- Rotated pointed face clockwise
|
||||
- Rotated pointed face counter-clockwise
|
||||
- Pushed closest edge
|
||||
- Pulled closest edge
|
||||
- Cannot rotate node with paramtype2 == glasslikeliquidlevel
|
||||
- Unsupported node type: modname:nodename
|
||||
- Wallmounted node rotated with default screwdriver behavior
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
please do not edit any file in this folder,
|
||||
corresponding custom.* files get created in the main mod's folder for you to customize
|
|
@ -0,0 +1,19 @@
|
|||
-- only alter this file if it's named "custom.recipes.lua"
|
||||
-- alter the recipes as you please and delete / comment out
|
||||
-- the recipes you don't want to be available in the game
|
||||
-- the original versions are in "default/recipes.lua"
|
||||
|
||||
return {
|
||||
["rhotator:screwdriver"] = {
|
||||
{"default:copper_ingot"},
|
||||
{"group:stick"},
|
||||
},
|
||||
["rhotator:screwdriver_alt"] = {
|
||||
{"default:copper_ingot", "group:stick"},
|
||||
{"group:stick", ""},
|
||||
},
|
||||
["rhotator:cube"] = {
|
||||
{"group:wool"},
|
||||
{"rhotator:screwdriver"},
|
||||
},
|
||||
}
|
154
init.lua
154
init.lua
|
@ -185,41 +185,87 @@ local function vector_to_dir_index(vec)
|
|||
return (vec.y > 0) and POS.Y or NEG.Y
|
||||
end
|
||||
|
||||
|
||||
-- ========================================================================
|
||||
-- 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
|
||||
|
||||
-- ============================================================
|
||||
-- rhotator main
|
||||
|
||||
local function rotate_main(param2_rotation, player, pointed_thing, click, rot_index)
|
||||
local unit = extract_unit_vectors(player, pointed_thing)
|
||||
local current_pos = pointed_thing.under
|
||||
|
||||
|
||||
local message
|
||||
local transform = false
|
||||
local rotation = rot_matrices[rot_index]
|
||||
|
||||
|
||||
local controls = player:get_player_control()
|
||||
|
||||
|
||||
if click == PRIMARY_BTN then
|
||||
transform = dir_matrices[vector_to_dir_index(unit.thumb)]
|
||||
if controls.sneak then
|
||||
rotation = rot_matrices[(rot_index + 2) % 4]
|
||||
message = "Pulled closest edge (sneak + left click)"
|
||||
message = "Pulled closest edge"
|
||||
else
|
||||
message = "Pushed closest edge (left click)"
|
||||
message = "Pushed closest edge"
|
||||
end
|
||||
else
|
||||
transform = dir_matrices[vector_to_dir_index(unit.back)]
|
||||
if controls.sneak then
|
||||
rotation = rot_matrices[(rot_index + 2) % 4]
|
||||
message = "Rotated pointed face counter-clockwise (sneak + right click)"
|
||||
message = "Rotated pointed face counter-clockwise"
|
||||
else
|
||||
message = "Rotated pointed face clockwise (right click)"
|
||||
message = "Rotated pointed face clockwise"
|
||||
end
|
||||
end
|
||||
|
||||
local start = get_facedir_transform(param2_rotation)
|
||||
local stop = transform * rotation * transform:invert() * start
|
||||
return matrix_to_facedir(stop), message
|
||||
|
||||
|
||||
end
|
||||
|
||||
-- ============================================================
|
||||
|
@ -227,17 +273,17 @@ end
|
|||
|
||||
local handlers = {}
|
||||
|
||||
function handlers.facedir(node, player, pointed_thing, click)
|
||||
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)
|
||||
|
||||
|
||||
local playername = player:get_player_name()
|
||||
if storage:get_int("memory_" .. playername) == 1 then
|
||||
facedir_memory[playername] = rotation_result
|
||||
end
|
||||
|
||||
|
||||
return rotation_result + remaining, message
|
||||
end
|
||||
|
||||
|
@ -306,7 +352,7 @@ end
|
|||
|
||||
local function rhotator_on_placenode(pos, newnode, placer, oldnode, itemstack, pointed_thing)
|
||||
if not placer or not placer.get_player_name then return end
|
||||
|
||||
|
||||
local playername = placer:get_player_name()
|
||||
local key = "memory_" .. playername
|
||||
local memory = storage:get_int(key) == 1
|
||||
|
@ -314,21 +360,21 @@ local function rhotator_on_placenode(pos, newnode, placer, oldnode, itemstack, p
|
|||
|
||||
local new_rotation = facedir_memory[playername]
|
||||
if not new_rotation then return end
|
||||
|
||||
|
||||
local nodedef = minetest.registered_nodes[newnode.name]
|
||||
if not nodedef then return end
|
||||
|
||||
|
||||
local paramtype2 = nodedef.paramtype2
|
||||
|
||||
if paramtype2 ~= "facedir" and paramtype2 ~= "colorfacedir" then return end
|
||||
|
||||
local old_rotation = newnode.param2 % 32 -- get first 5 bits
|
||||
local remaining = newnode.param2 - old_rotation
|
||||
|
||||
|
||||
newnode.param2 = new_rotation + remaining
|
||||
minetest.swap_node(pos, newnode)
|
||||
minetest.check_for_falling(pos)
|
||||
|
||||
|
||||
notify(placer, "Placed node according to previous rotation")
|
||||
end
|
||||
|
||||
|
@ -344,7 +390,7 @@ function rhotator.command(playername, param)
|
|||
local params = param:split(" ")
|
||||
if params[1] == "memory" then
|
||||
command_memory(playername, params)
|
||||
return
|
||||
return
|
||||
end
|
||||
|
||||
minetest.chat_send_player(playername, "[rhotator] unsupported param: " .. param)
|
||||
|
@ -370,7 +416,7 @@ local function interact(player, pointed_thing, click)
|
|||
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
|
||||
|
@ -394,7 +440,7 @@ local function interact(player, pointed_thing, click)
|
|||
notify.warning(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)
|
||||
|
@ -403,29 +449,32 @@ local function interact(player, pointed_thing, click)
|
|||
if handler_message then
|
||||
notify(player, handler_message)
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
local function primary_callback(itemstack, player, pointed_thing)
|
||||
interact(player, pointed_thing, PRIMARY_BTN)
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local function secondary_callback(itemstack, player, pointed_thing)
|
||||
interact(player, pointed_thing, SECONDARY_BTN)
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_tool("rhotator:screwdriver", {
|
||||
description = "Rhotator Screwdriver (left-click pushes edge, right-click rotates face)",
|
||||
description = "Rhotator Screwdriver\nLeft-click pushes edge\nRight-click rotates face\nHold sneak to invert direction",
|
||||
inventory_image = "rhotator.png",
|
||||
on_use = function(itemstack, player, pointed_thing)
|
||||
interact(player, pointed_thing, PRIMARY_BTN)
|
||||
return itemstack
|
||||
end,
|
||||
on_place = function(itemstack, player, pointed_thing)
|
||||
interact(player, pointed_thing, SECONDARY_BTN)
|
||||
return itemstack
|
||||
end,
|
||||
on_use = primary_callback,
|
||||
on_place = secondary_callback,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "rhotator:screwdriver",
|
||||
recipe = {
|
||||
{"default:copper_ingot"},
|
||||
{"group:stick"}
|
||||
}
|
||||
minetest.register_tool("rhotator:screwdriver_alt", {
|
||||
description = "Rhotator Screwdriver Alt\nLeft-click rotates face\nRight-click pushes edge\nHold sneak to invert direction",
|
||||
inventory_image = "rhotator-alt.png",
|
||||
on_use = secondary_callback,
|
||||
on_place = primary_callback,
|
||||
})
|
||||
|
||||
minetest.register_node("rhotator:cube", {
|
||||
|
@ -438,14 +487,31 @@ minetest.register_node("rhotator:cube", {
|
|||
groups = { snappy = 2, choppy = 2, oddly_breakable_by_hand = 3 },
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "rhotator:cube",
|
||||
recipe = {
|
||||
{"group:wool"},
|
||||
{"rhotator:screwdriver"},
|
||||
}
|
||||
})
|
||||
local full_recipes_filename = custom_or_default("rhotator", mod_path, "recipes.lua")
|
||||
if not full_recipes_filename then
|
||||
error("[rhotator] unable to find " .. mod_path .. "/custom.recipes.lua")
|
||||
end
|
||||
|
||||
local recipes = dofile(full_recipes_filename);
|
||||
|
||||
if type(recipes) ~= "table" then
|
||||
error("[rhotator] malformed file " .. mod_path .. "/custom.recipes.lua")
|
||||
end
|
||||
|
||||
local expected_recipes = { "rhotator:screwdriver", "rhotator:screwdriver_alt", "rhotator:cube" }
|
||||
|
||||
for _, itemname in ipairs(expected_recipes) do
|
||||
if type(recipes[itemname]) == "table" then
|
||||
minetest.register_craft({
|
||||
output = itemname,
|
||||
recipe = recipes[itemname],
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_placenode(rhotator_on_placenode)
|
||||
|
||||
minetest.register_chatcommand("rhotator", {func = rhotator.command})
|
||||
minetest.register_chatcommand("rhotator", {
|
||||
description = "memory [on|off]: displays or sets rotation memory for newly placed blocks",
|
||||
func = rhotator.command
|
||||
})
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 73 KiB |
Binary file not shown.
Before Width: | Height: | Size: 72 KiB |
Binary file not shown.
After Width: | Height: | Size: 342 B |
Loading…
Reference in New Issue