Initial commit
128
games/devtest/mods/testtools/README.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# Test Tools readme
|
||||
|
||||
Test Tools is a mod for developers that adds a bunch of tools to directly manipulate nodes and entities. This is great for quickly testing out stuff.
|
||||
|
||||
Here's the list of tools:
|
||||
|
||||
## Remover
|
||||
Removes nodes and non-player entities that you punch.
|
||||
|
||||
## Node Setter
|
||||
Replace a node with another one.
|
||||
|
||||
First, punch a node you want to remember.
|
||||
Then rightclick any other node to replace it with the node you remembered.
|
||||
|
||||
If you rightclick while pointing nothing, you can manually enter the node and param2.
|
||||
|
||||
## Param2 Tool
|
||||
Change the value param2 of nodes.
|
||||
|
||||
* Punch: Add 1 to param2
|
||||
* Sneak+Punch: Add 8 to param2
|
||||
* Place: Subtract 1 from param2
|
||||
* Sneak+Place: Subtract 8 from param2
|
||||
|
||||
Note: Use the debug screen (F5) to see the param2 of the pointed node.
|
||||
|
||||
## Falling Node Tool
|
||||
Turns nodes into falling nodes.
|
||||
|
||||
Usage:
|
||||
|
||||
* Punch node: Make it fall
|
||||
* Place: Try to teleport up to 2 units upwards, then make it fall
|
||||
|
||||
## Node Meta Editor
|
||||
Edit and view metadata of nodes.
|
||||
|
||||
Usage:
|
||||
|
||||
* Punch: Open node metadata editor
|
||||
|
||||
## Item Meta Editor
|
||||
Edit and view metadata of items.
|
||||
|
||||
Usage:
|
||||
|
||||
* Place/Punch: Opens item metadata editor of the item in the next
|
||||
inventory slot from the wielded item
|
||||
|
||||
## Entity Rotator
|
||||
Changes the entity rotation (with `set_rotation`).
|
||||
|
||||
Usage:
|
||||
|
||||
* Punch entity: Rotate yaw
|
||||
* Punch entity while holding down “Sneak” key: Rotate pitch
|
||||
* Punch entity while holding down “Special” key (aka “Aux”): Rotate roll
|
||||
|
||||
Each usage rotates the entity by 22.5°.
|
||||
|
||||
## Entity Spawner
|
||||
Spawns entities.
|
||||
|
||||
Usage:
|
||||
|
||||
* Punch to select entity or spawn one directly
|
||||
* Place to place selected entity
|
||||
|
||||
## Object Property Editor
|
||||
Edits properties of objects.
|
||||
|
||||
Usage:
|
||||
|
||||
* Punch object to open a formspec that allows you to view and edit properties
|
||||
* Punch air to edit properties of your own player object
|
||||
|
||||
To edit a property, select it in the list, enter a new value (in Lua syntax)
|
||||
and hit “Submit”.
|
||||
|
||||
## Object Attacher
|
||||
Allows you to attach an object to another one.
|
||||
|
||||
Basic usage:
|
||||
* First select the parent object, then the child object that should be attached
|
||||
* Selecting an object is done by punching it
|
||||
* Sneak+punch to detach selected object
|
||||
* If you punch air, you select yourself
|
||||
|
||||
Configuration:
|
||||
* Place: Increase attachment Y position
|
||||
* Sneak+place: decrease attachment Y position
|
||||
* Aux+place: Increase attachment X rotation
|
||||
* Aux+Sneak+Rightclick: Decrease attachment X rotation
|
||||
|
||||
Hint: To detach all objects nearby you (including on yourself), use the
|
||||
`/detach` server command.
|
||||
|
||||
## Object Mover
|
||||
Move an object by a given distance.
|
||||
|
||||
Usage:
|
||||
* Punch object into the direction you want to move it
|
||||
* Sneak+punch: Move object towards you
|
||||
* Place: Increase move distance
|
||||
* Sneak+place: Decrease move distance
|
||||
|
||||
## Children Getter
|
||||
Shows list of objects that are attached to an object (aka "children") in chat.
|
||||
|
||||
Usage:
|
||||
* Punch object: Show children of punched object
|
||||
* Punch air: Show your own children
|
||||
|
||||
## Entity Visual Scaler
|
||||
Change visual size of entities
|
||||
|
||||
Usage:
|
||||
|
||||
* Punch entity to increase visual size
|
||||
* Sneak+punch entity to decrease visual size
|
||||
|
||||
## Light Tool
|
||||
Show light level of node.
|
||||
|
||||
Usage:
|
||||
* Punch: Show light info of node in front of the punched node's side
|
||||
* Place: Show light info of the node that you touched
|
||||
951
games/devtest/mods/testtools/init.lua
Normal file
@@ -0,0 +1,951 @@
|
||||
local S = minetest.get_translator("testtools")
|
||||
local F = minetest.formspec_escape
|
||||
|
||||
dofile(minetest.get_modpath("testtools") .. "/light.lua")
|
||||
|
||||
minetest.register_tool("testtools:param2tool", {
|
||||
description = S("Param2 Tool") .."\n"..
|
||||
S("Modify param2 value of nodes") .."\n"..
|
||||
S("Punch: +1") .."\n"..
|
||||
S("Sneak+Punch: +8") .."\n"..
|
||||
S("Place: -1") .."\n"..
|
||||
S("Sneak+Place: -8"),
|
||||
inventory_image = "testtools_param2tool.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type ~= "node" or (not pos) then
|
||||
return
|
||||
end
|
||||
local add = 1
|
||||
if user then
|
||||
local ctrl = user:get_player_control()
|
||||
if ctrl.sneak then
|
||||
add = 8
|
||||
end
|
||||
end
|
||||
local node = minetest.get_node(pos)
|
||||
node.param2 = node.param2 + add
|
||||
minetest.swap_node(pos, node)
|
||||
end,
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type ~= "node" or (not pos) then
|
||||
return
|
||||
end
|
||||
local add = -1
|
||||
if user then
|
||||
local ctrl = user:get_player_control()
|
||||
if ctrl.sneak then
|
||||
add = -8
|
||||
end
|
||||
end
|
||||
local node = minetest.get_node(pos)
|
||||
node.param2 = node.param2 + add
|
||||
minetest.swap_node(pos, node)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("testtools:node_setter", {
|
||||
description = S("Node Setter") .."\n"..
|
||||
S("Replace pointed node with something else") .."\n"..
|
||||
S("Punch: Select pointed node") .."\n"..
|
||||
S("Place on node: Replace node with selected node") .."\n"..
|
||||
S("Place in air: Manually select a node"),
|
||||
inventory_image = "testtools_node_setter.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type == "nothing" then
|
||||
local meta = itemstack:get_meta()
|
||||
meta:set_string("node", "air")
|
||||
meta:set_int("node_param2", 0)
|
||||
if user and user:is_player() then
|
||||
minetest.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", "air", 0))
|
||||
end
|
||||
return itemstack
|
||||
elseif pointed_thing.type ~= "node" or (not pos) then
|
||||
return
|
||||
end
|
||||
local node = minetest.get_node(pos)
|
||||
local meta = itemstack:get_meta()
|
||||
meta:set_string("node", node.name)
|
||||
meta:set_int("node_param2", node.param2)
|
||||
if user and user:is_player() then
|
||||
minetest.chat_send_player(user:get_player_name(), S("Now placing: @1 (param2=@2)", node.name, node.param2))
|
||||
end
|
||||
return itemstack
|
||||
end,
|
||||
on_secondary_use = function(itemstack, user, pointed_thing)
|
||||
local meta = itemstack:get_meta()
|
||||
local nodename = meta:get_string("node") or ""
|
||||
local param2 = meta:get_int("node_param2") or 0
|
||||
|
||||
minetest.show_formspec(user:get_player_name(), "testtools:node_setter",
|
||||
"size[4,4]"..
|
||||
"field[0.5,1;3,1;nodename;"..F(S("Node name (itemstring):"))..";"..F(nodename).."]"..
|
||||
"field[0.5,2;3,1;param2;"..F(S("param2:"))..";"..F(tostring(param2)).."]"..
|
||||
"button_exit[0.5,3;3,1;submit;"..F(S("Submit")).."]"
|
||||
)
|
||||
end,
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
local meta = itemstack:get_meta()
|
||||
local nodename = meta:get_string("node")
|
||||
if nodename == "" and user and user:is_player() then
|
||||
minetest.chat_send_player(user:get_player_name(), S("Punch a node first!"))
|
||||
return
|
||||
end
|
||||
local param2 = meta:get_int("node_param2")
|
||||
if not param2 then
|
||||
param2 = 0
|
||||
end
|
||||
local node = { name = nodename, param2 = param2 }
|
||||
if not minetest.registered_nodes[nodename] then
|
||||
minetest.chat_send_player(user:get_player_name(), S("Cannot set unknown node: @1", nodename))
|
||||
return
|
||||
end
|
||||
minetest.set_node(pos, node)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("testtools:remover", {
|
||||
description = S("Remover") .."\n"..
|
||||
S("Punch: Remove pointed node or object"),
|
||||
inventory_image = "testtools_remover.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type == "node" and pos ~= nil then
|
||||
minetest.remove_node(pos)
|
||||
elseif pointed_thing.type == "object" then
|
||||
local obj = pointed_thing.ref
|
||||
if not obj:is_player() then
|
||||
obj:remove()
|
||||
else
|
||||
minetest.chat_send_player(user:get_player_name(), S("Can't remove players!"))
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("testtools:falling_node_tool", {
|
||||
description = S("Falling Node Tool") .."\n"..
|
||||
S("Punch: Make pointed node fall") .."\n"..
|
||||
S("Place: Move pointed node 2 units upwards, then make it fall"),
|
||||
inventory_image = "testtools_falling_node_tool.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
-- Teleport node 1-2 units upwards (if possible) and make it fall
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type ~= "node" or (not pos) then
|
||||
return
|
||||
end
|
||||
local ok = false
|
||||
local highest
|
||||
for i=1,2 do
|
||||
local above = {x=pos.x,y=pos.y+i,z=pos.z}
|
||||
local n2 = minetest.get_node(above)
|
||||
local def2 = minetest.registered_nodes[n2.name]
|
||||
if def2 and (not def2.walkable) then
|
||||
highest = above
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
if highest then
|
||||
local node = minetest.get_node(pos)
|
||||
local metatable = minetest.get_meta(pos):to_table()
|
||||
minetest.remove_node(pos)
|
||||
minetest.set_node(highest, node)
|
||||
local meta_highest = minetest.get_meta(highest)
|
||||
meta_highest:from_table(metatable)
|
||||
ok = minetest.spawn_falling_node(highest)
|
||||
else
|
||||
ok = minetest.spawn_falling_node(pos)
|
||||
end
|
||||
if not ok and user and user:is_player() then
|
||||
minetest.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!"))
|
||||
end
|
||||
end,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type ~= "node" or (not pos) then
|
||||
return
|
||||
end
|
||||
local ok = minetest.spawn_falling_node(pos)
|
||||
if not ok and user and user:is_player() then
|
||||
minetest.chat_send_player(user:get_player_name(), S("Falling node could not be spawned!"))
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_tool("testtools:rotator", {
|
||||
description = S("Entity Rotator") .. "\n" ..
|
||||
S("Rotate pointed entity") .."\n"..
|
||||
S("Punch: Yaw") .."\n"..
|
||||
S("Sneak+Punch: Pitch") .."\n"..
|
||||
S("Aux1+Punch: Roll"),
|
||||
inventory_image = "testtools_entity_rotator.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "object" then
|
||||
return
|
||||
end
|
||||
local obj = pointed_thing.ref
|
||||
if obj:is_player() then
|
||||
-- No player rotation
|
||||
return
|
||||
else
|
||||
local axis = "y"
|
||||
if user and user:is_player() then
|
||||
local ctrl = user:get_player_control()
|
||||
if ctrl.sneak then
|
||||
axis = "x"
|
||||
elseif ctrl.aux1 then
|
||||
axis = "z"
|
||||
end
|
||||
end
|
||||
local rot = obj:get_rotation()
|
||||
rot[axis] = rot[axis] + math.pi/8
|
||||
if rot[axis] > math.pi*2 then
|
||||
rot[axis] = rot[axis] - math.pi*2
|
||||
end
|
||||
obj:set_rotation(rot)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local mover_config = function(itemstack, user, pointed_thing)
|
||||
if not (user and user:is_player()) then
|
||||
return
|
||||
end
|
||||
local name = user:get_player_name()
|
||||
local ctrl = user:get_player_control()
|
||||
local meta = itemstack:get_meta()
|
||||
local dist = 1.0
|
||||
if meta:contains("distance") then
|
||||
dist = meta:get_int("distance")
|
||||
end
|
||||
if ctrl.sneak then
|
||||
dist = dist - 1
|
||||
else
|
||||
dist = dist + 1
|
||||
end
|
||||
meta:set_int("distance", dist)
|
||||
minetest.chat_send_player(user:get_player_name(), S("distance=@1/10", dist*2))
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:object_mover", {
|
||||
description = S("Object Mover") .."\n"..
|
||||
S("Move pointed object towards or away from you") .."\n"..
|
||||
S("Punch: Move by distance").."\n"..
|
||||
S("Sneak+Punch: Move by negative distance").."\n"..
|
||||
S("Place: Increase distance").."\n"..
|
||||
S("Sneak+Place: Decrease distance"),
|
||||
inventory_image = "testtools_object_mover.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = mover_config,
|
||||
on_secondary_use = mover_config,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "object" then
|
||||
return
|
||||
end
|
||||
local obj = pointed_thing.ref
|
||||
if not (user and user:is_player()) then
|
||||
return
|
||||
end
|
||||
local yaw = user:get_look_horizontal()
|
||||
local dir = minetest.yaw_to_dir(yaw)
|
||||
local pos = obj:get_pos()
|
||||
local pitch = user:get_look_vertical()
|
||||
if pitch > 0.25 * math.pi then
|
||||
dir.y = -1
|
||||
dir.x = 0
|
||||
dir.z = 0
|
||||
elseif pitch < -0.25 * math.pi then
|
||||
dir.y = 1
|
||||
dir.x = 0
|
||||
dir.z = 0
|
||||
end
|
||||
local ctrl = user:get_player_control()
|
||||
if ctrl.sneak then
|
||||
dir = vector.multiply(dir, -1)
|
||||
end
|
||||
local meta = itemstack:get_meta()
|
||||
if meta:contains("distance") then
|
||||
local dist = meta:get_int("distance")
|
||||
dir = vector.multiply(dir, dist*0.2)
|
||||
end
|
||||
pos = vector.add(pos, dir)
|
||||
obj:set_pos(pos)
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
|
||||
minetest.register_tool("testtools:entity_scaler", {
|
||||
description = S("Entity Visual Scaler") .."\n"..
|
||||
S("Scale visual size of entities") .."\n"..
|
||||
S("Punch: Increase size") .."\n"..
|
||||
S("Sneak+Punch: Decrease scale"),
|
||||
inventory_image = "testtools_entity_scaler.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "object" then
|
||||
return
|
||||
end
|
||||
local obj = pointed_thing.ref
|
||||
if obj:is_player() then
|
||||
-- No player scaling
|
||||
return
|
||||
else
|
||||
local diff = 0.1
|
||||
if user and user:is_player() then
|
||||
local ctrl = user:get_player_control()
|
||||
if ctrl.sneak then
|
||||
diff = -0.1
|
||||
end
|
||||
end
|
||||
local prop = obj:get_properties()
|
||||
if not prop.visual_size then
|
||||
prop.visual_size = { x=1, y=1, z=1 }
|
||||
else
|
||||
prop.visual_size = { x=prop.visual_size.x+diff, y=prop.visual_size.y+diff, z=prop.visual_size.z+diff }
|
||||
if prop.visual_size.x <= 0.1 then
|
||||
prop.visual_size.x = 0.1
|
||||
end
|
||||
if prop.visual_size.y <= 0.1 then
|
||||
prop.visual_size.y = 0.1
|
||||
end
|
||||
if prop.visual_size.z <= 0.1 then
|
||||
prop.visual_size.z = 0.1
|
||||
end
|
||||
end
|
||||
obj:set_properties(prop)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local selections = {}
|
||||
local entity_list
|
||||
local function get_entity_list()
|
||||
if entity_list then
|
||||
return entity_list
|
||||
end
|
||||
local ents = minetest.registered_entities
|
||||
local list = {}
|
||||
for k,_ in pairs(ents) do
|
||||
table.insert(list, k)
|
||||
end
|
||||
table.sort(list)
|
||||
entity_list = list
|
||||
return entity_list
|
||||
end
|
||||
minetest.register_tool("testtools:entity_spawner", {
|
||||
description = S("Entity Spawner") .."\n"..
|
||||
S("Spawns entities") .."\n"..
|
||||
S("Punch: Select entity to spawn") .."\n"..
|
||||
S("Place: Spawn selected entity"),
|
||||
inventory_image = "testtools_entity_spawner.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
local name = user:get_player_name()
|
||||
if pointed_thing.type == "node" then
|
||||
if selections[name] then
|
||||
local pos = pointed_thing.above
|
||||
minetest.add_entity(pos, get_entity_list()[selections[name]])
|
||||
else
|
||||
minetest.chat_send_player(name, S("Select an entity first (with punch key)!"))
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type == "object" then
|
||||
return
|
||||
end
|
||||
if user and user:is_player() then
|
||||
local list = table.concat(get_entity_list(), ",")
|
||||
local name = user:get_player_name()
|
||||
local sel = selections[name] or ""
|
||||
minetest.show_formspec(name, "testtools:entity_list",
|
||||
"size[9,9]"..
|
||||
"textlist[0,0;9,8;entity_list;"..list..";"..sel..";false]"..
|
||||
"button[0,8;4,1;spawn;Spawn entity]"
|
||||
)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local function prop_to_string(property)
|
||||
if type(property) == "string" then
|
||||
return "\"" .. property .. "\""
|
||||
elseif type(property) == "table" then
|
||||
return tostring(dump(property)):gsub("\n", "")
|
||||
else
|
||||
return tostring(property)
|
||||
end
|
||||
end
|
||||
|
||||
local property_formspec_data = {}
|
||||
local property_formspec_index = {}
|
||||
local selected_objects = {}
|
||||
local function get_object_properties_form(obj, playername)
|
||||
if not playername then return "" end
|
||||
local props = obj:get_properties()
|
||||
local str = ""
|
||||
property_formspec_data[playername] = {}
|
||||
local proplist = {}
|
||||
for k,_ in pairs(props) do
|
||||
table.insert(proplist, k)
|
||||
end
|
||||
table.sort(proplist)
|
||||
for p=1, #proplist do
|
||||
local k = proplist[p]
|
||||
local v = props[k]
|
||||
local newline = ""
|
||||
newline = k .. " = "
|
||||
newline = newline .. prop_to_string(v)
|
||||
str = str .. F(newline)
|
||||
if p < #proplist then
|
||||
str = str .. ","
|
||||
end
|
||||
table.insert(property_formspec_data[playername], k)
|
||||
end
|
||||
return str
|
||||
end
|
||||
|
||||
local editor_formspec_selindex = {}
|
||||
|
||||
local editor_formspec = function(playername, obj, value, sel)
|
||||
if not value then
|
||||
value = ""
|
||||
end
|
||||
if not sel then
|
||||
sel = ""
|
||||
end
|
||||
local list = get_object_properties_form(obj, playername)
|
||||
local title
|
||||
if obj:is_player() then
|
||||
title = S("Object properties of player “@1”", obj:get_player_name())
|
||||
else
|
||||
local ent = obj:get_luaentity()
|
||||
title = S("Object properties of @1", ent.name)
|
||||
end
|
||||
minetest.show_formspec(playername, "testtools:object_editor",
|
||||
"size[9,9]"..
|
||||
"label[0,0;"..F(title).."]"..
|
||||
"textlist[0,0.5;9,7.5;object_props;"..list..";"..sel..";false]"..
|
||||
"field[0.2,8.75;8,1;value;"..F(S("Value"))..";"..F(value).."]"..
|
||||
"field_close_on_enter[value;false]"..
|
||||
"button[8,8.5;1,1;submit;"..F(S("Submit")).."]"
|
||||
)
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:object_editor", {
|
||||
description = S("Object Property Editor") .."\n"..
|
||||
S("Edit properties of objects") .."\n"..
|
||||
S("Punch object: Edit object") .."\n"..
|
||||
S("Punch air: Edit yourself"),
|
||||
inventory_image = "testtools_object_editor.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if user and user:is_player() then
|
||||
local name = user:get_player_name()
|
||||
|
||||
if pointed_thing.type == "object" then
|
||||
selected_objects[name] = pointed_thing.ref
|
||||
elseif pointed_thing.type == "nothing" then
|
||||
-- Use on yourself if pointing nothing
|
||||
selected_objects[name] = user
|
||||
else
|
||||
-- Unsupported pointed thing
|
||||
return
|
||||
end
|
||||
|
||||
local sel = editor_formspec_selindex[name]
|
||||
local val
|
||||
if selected_objects[name] and selected_objects[name]:get_properties() then
|
||||
local props = selected_objects[name]:get_properties()
|
||||
local keys = property_formspec_data[name]
|
||||
if property_formspec_index[name] and props then
|
||||
local key = keys[property_formspec_index[name]]
|
||||
val = prop_to_string(props[key])
|
||||
end
|
||||
end
|
||||
|
||||
editor_formspec(name, selected_objects[name], val, sel)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local ent_parent = {}
|
||||
local ent_child = {}
|
||||
local DEFAULT_ATTACH_OFFSET_Y = 11
|
||||
|
||||
local attacher_config = function(itemstack, user, pointed_thing)
|
||||
if not (user and user:is_player()) then
|
||||
return
|
||||
end
|
||||
if pointed_thing.type == "object" then
|
||||
return
|
||||
end
|
||||
local name = user:get_player_name()
|
||||
local ctrl = user:get_player_control()
|
||||
local meta = itemstack:get_meta()
|
||||
if ctrl.aux1 then
|
||||
local rot_x = meta:get_float("rot_x")
|
||||
if ctrl.sneak then
|
||||
rot_x = rot_x - math.pi/8
|
||||
else
|
||||
rot_x = rot_x + math.pi/8
|
||||
end
|
||||
if rot_x > 6.2 then
|
||||
rot_x = 0
|
||||
elseif rot_x < 0 then
|
||||
rot_x = math.pi * (15/8)
|
||||
end
|
||||
minetest.chat_send_player(name, S("rotation=@1", minetest.pos_to_string({x=rot_x,y=0,z=0})))
|
||||
meta:set_float("rot_x", rot_x)
|
||||
else
|
||||
local pos_y
|
||||
if meta:contains("pos_y") then
|
||||
pos_y = meta:get_int("pos_y")
|
||||
else
|
||||
pos_y = DEFAULT_ATTACH_OFFSET_Y
|
||||
end
|
||||
if ctrl.sneak then
|
||||
pos_y = pos_y - 1
|
||||
else
|
||||
pos_y = pos_y + 1
|
||||
end
|
||||
minetest.chat_send_player(name, S("position=@1", minetest.pos_to_string({x=0,y=pos_y,z=0})))
|
||||
meta:set_int("pos_y", pos_y)
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:object_attacher", {
|
||||
description = S("Object Attacher") .."\n"..
|
||||
S("Attach object to another") .."\n"..
|
||||
S("Punch objects to first select parent object, then the child object to attach") .."\n"..
|
||||
S("Punch air to select yourself") .."\n"..
|
||||
S("Place: Incease attachment Y offset") .."\n"..
|
||||
S("Sneak+Place: Decease attachment Y offset") .."\n"..
|
||||
S("Aux1+Place: Incease attachment rotation") .."\n"..
|
||||
S("Aux1+Sneak+Place: Decrease attachment rotation"),
|
||||
inventory_image = "testtools_object_attacher.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = attacher_config,
|
||||
on_secondary_use = attacher_config,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if user and user:is_player() then
|
||||
local name = user:get_player_name()
|
||||
local selected_object
|
||||
if pointed_thing.type == "object" then
|
||||
selected_object = pointed_thing.ref
|
||||
elseif pointed_thing.type == "nothing" then
|
||||
selected_object = user
|
||||
else
|
||||
return
|
||||
end
|
||||
local ctrl = user:get_player_control()
|
||||
if ctrl.sneak then
|
||||
if selected_object:get_attach() then
|
||||
selected_object:set_detach()
|
||||
minetest.chat_send_player(name, S("Object detached!"))
|
||||
else
|
||||
minetest.chat_send_player(name, S("Object is not attached!"))
|
||||
end
|
||||
return
|
||||
end
|
||||
local parent = ent_parent[name]
|
||||
local child = ent_child[name]
|
||||
local ename = S("<unknown>")
|
||||
if not parent then
|
||||
parent = selected_object
|
||||
ent_parent[name] = parent
|
||||
elseif not child then
|
||||
child = selected_object
|
||||
ent_child[name] = child
|
||||
end
|
||||
local entity = selected_object:get_luaentity()
|
||||
if entity then
|
||||
ename = entity.name
|
||||
elseif selected_object:is_player() then
|
||||
ename = selected_object:get_player_name()
|
||||
end
|
||||
if selected_object == parent then
|
||||
minetest.chat_send_player(name, S("Parent object selected: @1", ename))
|
||||
elseif selected_object == child then
|
||||
minetest.chat_send_player(name, S("Child object selected: @1", ename))
|
||||
end
|
||||
if parent and child then
|
||||
if parent == child then
|
||||
minetest.chat_send_player(name, S("Can't attach an object to itself!"))
|
||||
ent_parent[name] = nil
|
||||
ent_child[name] = nil
|
||||
return
|
||||
end
|
||||
local meta = itemstack:get_meta()
|
||||
local y
|
||||
if meta:contains("pos_y") then
|
||||
y = meta:get_int("pos_y")
|
||||
else
|
||||
y = DEFAULT_ATTACH_OFFSET_Y
|
||||
end
|
||||
local rx = meta:get_float("rot_x") or 0
|
||||
local offset = {x=0,y=y,z=0}
|
||||
local angle = {x=rx,y=0,z=0}
|
||||
child:set_attach(parent, "", offset, angle)
|
||||
local check_parent = child:get_attach()
|
||||
if check_parent then
|
||||
minetest.chat_send_player(name, S("Object attached! position=@1, rotation=@2",
|
||||
minetest.pos_to_string(offset), minetest.pos_to_string(angle)))
|
||||
else
|
||||
minetest.chat_send_player(name, S("Attachment failed!"))
|
||||
end
|
||||
ent_parent[name] = nil
|
||||
ent_child[name] = nil
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local function print_object(obj)
|
||||
if obj:is_player() then
|
||||
return "player '"..obj:get_player_name().."'"
|
||||
elseif obj:get_luaentity() then
|
||||
return "LuaEntity '"..obj:get_luaentity().name.."'"
|
||||
else
|
||||
return "object"
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:children_getter", {
|
||||
description = S("Children Getter") .."\n"..
|
||||
S("Shows list of objects attached to object") .."\n"..
|
||||
S("Punch object to show its 'children'") .."\n"..
|
||||
S("Punch air to show your own 'children'"),
|
||||
inventory_image = "testtools_children_getter.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if user and user:is_player() then
|
||||
local name = user:get_player_name()
|
||||
local selected_object
|
||||
local self_name
|
||||
if pointed_thing.type == "object" then
|
||||
selected_object = pointed_thing.ref
|
||||
elseif pointed_thing.type == "nothing" then
|
||||
selected_object = user
|
||||
else
|
||||
return
|
||||
end
|
||||
self_name = print_object(selected_object)
|
||||
local children = selected_object:get_children()
|
||||
local ret = ""
|
||||
for c=1, #children do
|
||||
ret = ret .. "* " .. print_object(children[c])
|
||||
if c < #children then
|
||||
ret = ret .. "\n"
|
||||
end
|
||||
end
|
||||
if ret == "" then
|
||||
ret = S("No children attached to @1.", self_name)
|
||||
else
|
||||
ret = S("Children of @1:", self_name) .. "\n" .. ret
|
||||
end
|
||||
minetest.chat_send_player(user:get_player_name(), ret)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Use loadstring to parse param as a Lua value
|
||||
local function use_loadstring(param, player)
|
||||
-- For security reasons, require 'server' priv, just in case
|
||||
-- someone is actually crazy enough to run this on a public server.
|
||||
local privs = minetest.get_player_privs(player:get_player_name())
|
||||
if not privs.server then
|
||||
return false, "You need 'server' privilege to change object properties!"
|
||||
end
|
||||
if not param then
|
||||
return false, "Failed: parameter is nil"
|
||||
end
|
||||
--[[ DANGER ZONE ]]
|
||||
-- Interpret string as Lua value
|
||||
local func, errormsg = loadstring("return (" .. param .. ")")
|
||||
if not func then
|
||||
return false, "Failed: " .. errormsg
|
||||
end
|
||||
|
||||
-- Apply sandbox here using setfenv
|
||||
setfenv(func, {})
|
||||
|
||||
-- Run it
|
||||
local good, errOrResult = pcall(func)
|
||||
if not good then
|
||||
-- A Lua error was thrown
|
||||
return false, "Failed: " .. errOrResult
|
||||
end
|
||||
|
||||
-- errOrResult will be the value
|
||||
return true, errOrResult
|
||||
end
|
||||
|
||||
-- Item Meta Editor + Node Meta Editor
|
||||
local node_meta_posses = {}
|
||||
local meta_latest_keylist = {}
|
||||
|
||||
local function show_meta_formspec(user, metatype, pos_or_item, key, value, keylist)
|
||||
local textlist
|
||||
if keylist then
|
||||
textlist = "textlist[0,0.5;2.5,6.5;keylist;"..keylist.."]"
|
||||
else
|
||||
textlist = ""
|
||||
end
|
||||
|
||||
local form = "size[15,9]"..
|
||||
"label[0,0;"..F(S("Current keys:")).."]"..
|
||||
textlist..
|
||||
"field[3,0.5;12,1;key;"..F(S("Key"))..";"..F(key).."]"..
|
||||
"textarea[3,1.5;12,6;value;"..F(S("Value (use empty value to delete key)"))..";"..F(value).."]"..
|
||||
"button[4,8;3,1;set;"..F(S("Set value")).."]"
|
||||
|
||||
local extra_label
|
||||
local formname
|
||||
if metatype == "node" then
|
||||
formname = "testtools:node_meta_editor"
|
||||
extra_label = S("pos = @1", minetest.pos_to_string(pos_or_item))
|
||||
else
|
||||
formname = "testtools:item_meta_editor"
|
||||
extra_label = S("item = @1", pos_or_item:get_name())
|
||||
end
|
||||
form = form .. "label[0,7.2;"..F(extra_label).."]"
|
||||
|
||||
minetest.show_formspec(user:get_player_name(), formname, form)
|
||||
end
|
||||
|
||||
local function get_meta_keylist(meta, playername, escaped)
|
||||
local keys = {}
|
||||
local ekeys = {}
|
||||
local mtable = meta:to_table()
|
||||
for k,_ in pairs(mtable.fields) do
|
||||
table.insert(keys, k)
|
||||
if escaped then
|
||||
table.insert(ekeys, F(k))
|
||||
else
|
||||
table.insert(ekeys, k)
|
||||
end
|
||||
end
|
||||
if playername then
|
||||
meta_latest_keylist[playername] = keys
|
||||
end
|
||||
return table.concat(ekeys, ",")
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:node_meta_editor", {
|
||||
description = S("Node Meta Editor") .. "\n" ..
|
||||
S("Place: Edit node metadata"),
|
||||
inventory_image = "testtools_node_meta_editor.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
if not user:is_player() then
|
||||
return itemstack
|
||||
end
|
||||
local pos = pointed_thing.under
|
||||
node_meta_posses[user:get_player_name()] = pos
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
show_meta_formspec(user, "node", pos, "", "", get_meta_keylist(meta, user:get_player_name(), true))
|
||||
return itemstack
|
||||
end,
|
||||
})
|
||||
|
||||
local function get_item_next_to_wielded_item(player)
|
||||
local inv = player:get_inventory()
|
||||
local wield = player:get_wield_index()
|
||||
local itemstack = inv:get_stack("main", wield+1)
|
||||
return itemstack
|
||||
end
|
||||
local function set_item_next_to_wielded_item(player, itemstack)
|
||||
local inv = player:get_inventory()
|
||||
local wield = player:get_wield_index()
|
||||
inv:set_stack("main", wield+1, itemstack)
|
||||
end
|
||||
|
||||
local function use_item_meta_editor(itemstack, user, pointed_thing)
|
||||
if not user:is_player() then
|
||||
return itemstack
|
||||
end
|
||||
local item_to_edit = get_item_next_to_wielded_item(user)
|
||||
if item_to_edit:is_empty() then
|
||||
minetest.chat_send_player(user:get_player_name(), S("Place an item next to the Item Meta Editor in your inventory first!"))
|
||||
return itemstack
|
||||
end
|
||||
local meta = item_to_edit:get_meta()
|
||||
show_meta_formspec(user, "item", item_to_edit, "", "", get_meta_keylist(meta, user:get_player_name(), true))
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:item_meta_editor", {
|
||||
description = S("Item Meta Editor") .. "\n" ..
|
||||
S("Punch/Place: Edit item metadata of item in the next inventory slot"),
|
||||
inventory_image = "testtools_item_meta_editor.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = use_item_meta_editor,
|
||||
on_secondary_use = use_item_meta_editor,
|
||||
on_place = use_item_meta_editor,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if not (player and player:is_player()) then
|
||||
return
|
||||
end
|
||||
if formname == "testtools:entity_list" then
|
||||
local name = player:get_player_name()
|
||||
if fields.entity_list then
|
||||
local expl = minetest.explode_textlist_event(fields.entity_list)
|
||||
if expl.type == "DCL" then
|
||||
local pos = vector.add(player:get_pos(), {x=0,y=1,z=0})
|
||||
selections[name] = expl.index
|
||||
minetest.add_entity(pos, get_entity_list()[expl.index])
|
||||
return
|
||||
elseif expl.type == "CHG" then
|
||||
selections[name] = expl.index
|
||||
return
|
||||
end
|
||||
elseif fields.spawn and selections[name] then
|
||||
local pos = vector.add(player:get_pos(), {x=0,y=1,z=0})
|
||||
minetest.add_entity(pos, get_entity_list()[selections[name]])
|
||||
return
|
||||
end
|
||||
elseif formname == "testtools:object_editor" then
|
||||
local name = player:get_player_name()
|
||||
if fields.object_props then
|
||||
local expl = minetest.explode_textlist_event(fields.object_props)
|
||||
if expl.type == "DCL" or expl.type == "CHG" then
|
||||
property_formspec_index[name] = expl.index
|
||||
|
||||
local props = selected_objects[name]:get_properties()
|
||||
local keys = property_formspec_data[name]
|
||||
if (not property_formspec_index[name]) or (not props) then
|
||||
return
|
||||
end
|
||||
local key = keys[property_formspec_index[name]]
|
||||
editor_formspec_selindex[name] = expl.index
|
||||
editor_formspec(name, selected_objects[name], prop_to_string(props[key]), expl.index)
|
||||
return
|
||||
end
|
||||
end
|
||||
if fields.key_enter_field == "value" or fields.submit then
|
||||
local props = selected_objects[name]:get_properties()
|
||||
local keys = property_formspec_data[name]
|
||||
if (not property_formspec_index[name]) or (not props) then
|
||||
return
|
||||
end
|
||||
local key = keys[property_formspec_index[name]]
|
||||
if not key then
|
||||
return
|
||||
end
|
||||
local success, str = use_loadstring(fields.value, player)
|
||||
if success then
|
||||
props[key] = str
|
||||
else
|
||||
minetest.chat_send_player(name, str)
|
||||
return
|
||||
end
|
||||
selected_objects[name]:set_properties(props)
|
||||
local sel = editor_formspec_selindex[name]
|
||||
editor_formspec(name, selected_objects[name], prop_to_string(props[key]), sel)
|
||||
return
|
||||
end
|
||||
elseif formname == "testtools:node_setter" then
|
||||
local playername = player:get_player_name()
|
||||
local witem = player:get_wielded_item()
|
||||
if witem:get_name() == "testtools:node_setter" then
|
||||
if fields.nodename and fields.param2 then
|
||||
local param2 = tonumber(fields.param2)
|
||||
if not param2 then
|
||||
return
|
||||
end
|
||||
local meta = witem:get_meta()
|
||||
meta:set_string("node", fields.nodename)
|
||||
meta:set_int("node_param2", param2)
|
||||
player:set_wielded_item(witem)
|
||||
end
|
||||
end
|
||||
elseif formname == "testtools:node_meta_editor" or formname == "testtools:item_meta_editor" then
|
||||
local name = player:get_player_name()
|
||||
local metatype
|
||||
local pos_or_item
|
||||
if formname == "testtools:node_meta_editor" then
|
||||
metatype = "node"
|
||||
pos_or_item = node_meta_posses[name]
|
||||
else
|
||||
metatype = "item"
|
||||
pos_or_item = get_item_next_to_wielded_item(player)
|
||||
end
|
||||
if fields.keylist then
|
||||
local evnt = minetest.explode_textlist_event(fields.keylist)
|
||||
if evnt.type == "DCL" or evnt.type == "CHG" then
|
||||
local keylist_table = meta_latest_keylist[name]
|
||||
if metatype == "node" and not pos_or_item then
|
||||
return
|
||||
end
|
||||
local meta
|
||||
if metatype == "node" then
|
||||
meta = minetest.get_meta(pos_or_item)
|
||||
else
|
||||
meta = pos_or_item:get_meta()
|
||||
end
|
||||
if not keylist_table then
|
||||
return
|
||||
end
|
||||
if #keylist_table == 0 then
|
||||
return
|
||||
end
|
||||
local key = keylist_table[evnt.index]
|
||||
local value = meta:get_string(key)
|
||||
local keylist_escaped = {}
|
||||
for k,v in pairs(keylist_table) do
|
||||
keylist_escaped[k] = F(v)
|
||||
end
|
||||
local keylist = table.concat(keylist_escaped, ",")
|
||||
show_meta_formspec(player, metatype, pos_or_item, key, value, keylist)
|
||||
return
|
||||
end
|
||||
elseif fields.key and fields.key ~= "" and fields.value then
|
||||
if metatype == "node" and not pos_or_item then
|
||||
return
|
||||
end
|
||||
local meta
|
||||
if metatype == "node" then
|
||||
meta = minetest.get_meta(pos_or_item)
|
||||
elseif metatype == "item" then
|
||||
if pos_or_item:is_empty() then
|
||||
return
|
||||
end
|
||||
meta = pos_or_item:get_meta()
|
||||
end
|
||||
if fields.set then
|
||||
meta:set_string(fields.key, fields.value)
|
||||
if metatype == "item" then
|
||||
set_item_next_to_wielded_item(player, pos_or_item)
|
||||
end
|
||||
show_meta_formspec(player, metatype, pos_or_item, fields.key, fields.value,
|
||||
get_meta_keylist(meta, name, true))
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
meta_latest_keylist[name] = nil
|
||||
node_meta_posses[name] = nil
|
||||
end)
|
||||
37
games/devtest/mods/testtools/light.lua
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
local S = minetest.get_translator("testtools")
|
||||
|
||||
local function get_func(is_place)
|
||||
return function(itemstack, user, pointed_thing)
|
||||
local pos
|
||||
if is_place then
|
||||
pos = pointed_thing.under
|
||||
else
|
||||
pos = pointed_thing.above
|
||||
end
|
||||
if pointed_thing.type ~= "node" or not pos then
|
||||
return
|
||||
end
|
||||
|
||||
local node = minetest.get_node(pos)
|
||||
local pstr = minetest.pos_to_string(pos)
|
||||
local time = minetest.get_timeofday()
|
||||
local sunlight = minetest.get_natural_light(pos)
|
||||
local artificial = minetest.get_artificial_light(node.param1)
|
||||
local message = ("pos=%s | param1=0x%02x | " ..
|
||||
"sunlight=%d | artificial=%d | timeofday=%.5f" )
|
||||
:format(pstr, node.param1, sunlight, artificial, time)
|
||||
minetest.chat_send_player(user:get_player_name(), message)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_tool("testtools:lighttool", {
|
||||
description = S("Light Tool") .. "\n" ..
|
||||
S("Show light values of node") .. "\n" ..
|
||||
S("Punch: Light of node above touched node") .. "\n" ..
|
||||
S("Place: Light of touched node itself"),
|
||||
inventory_image = "testtools_lighttool.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
on_use = get_func(false),
|
||||
on_place = get_func(true),
|
||||
})
|
||||
2
games/devtest/mods/testtools/mod.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
name = testtools
|
||||
description = Some tools to directly manipulate nodes and entities. Great for development and testing
|
||||
|
After Width: | Height: | Size: 281 B |
|
After Width: | Height: | Size: 151 B |
|
After Width: | Height: | Size: 182 B |
|
After Width: | Height: | Size: 189 B |
|
After Width: | Height: | Size: 140 B |
|
After Width: | Height: | Size: 114 B |
BIN
games/devtest/mods/testtools/textures/testtools_lighttool.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 135 B |
BIN
games/devtest/mods/testtools/textures/testtools_node_setter.png
Normal file
|
After Width: | Height: | Size: 146 B |
|
After Width: | Height: | Size: 173 B |
|
After Width: | Height: | Size: 180 B |
BIN
games/devtest/mods/testtools/textures/testtools_object_mover.png
Normal file
|
After Width: | Height: | Size: 175 B |
BIN
games/devtest/mods/testtools/textures/testtools_param2tool.png
Normal file
|
After Width: | Height: | Size: 127 B |
BIN
games/devtest/mods/testtools/textures/testtools_remover.png
Normal file
|
After Width: | Height: | Size: 129 B |