Alternative tool with swapped mouse buttons, custom recipes, readme updated with also a youtube video

master
entuland 2019-02-02 17:06:06 +01:00
parent 9fee3779d9
commit d777debbe9
10 changed files with 156 additions and 57 deletions

View File

@ -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

2
default/README.txt Normal file
View File

@ -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

19
default/recipes.lua Normal file
View File

@ -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
View File

@ -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

BIN
textures/rhotator-alt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B