3 Mods hinzugefügt, Fehlende Nahrungspunkteangaben im Inventar ergänzt, falsche Nahrungspunkte berichtigt

This commit is contained in:
N-Nachtigal 2025-05-13 18:57:15 +02:00
parent 763ba03e6c
commit 23dda4593a
151 changed files with 6445 additions and 109 deletions

19
mods/ropes/LICENSE.md Normal file
View file

@ -0,0 +1,19 @@
## MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

17
mods/ropes/README.md Normal file
View file

@ -0,0 +1,17 @@
# Ropes
This mod adds "rope boxes", blocks that when placed in world will automatically lower a rope at 1 meter per second until it reaches a fixed maximum length. The basic rope box produces 50m of rope by default and there are up to eight additional rope box types that produce multiples of that rope length - 100m, 150m, and so forth up to 450m. The number of rope boxes and the length of a standard rope length can be configured via the settings menu.
The rope stops lowering if it reaches an obstruction. Ropes can be cut using an axe or other choppy tool at any location and when they're cut the bottom half of the rope will disappear, dropping any climbers. The same happens to the entire rope if the rope box at the top of the rope is removed. Cutting the rope doesn't reduce the maximum length of rope the rope box will produce if it's removed and rebuilt again. Ropes are flammable. They respect protection settings - if the player that placed the rope box isn't permitted to build in an area then the rope descending from that box will treat it as an obstruction.
Also included is a rope ladder that behaves similarly, though it only comes in one standard maximum length - 50m by default, again changeable in settings.
This mod will also enhance default wood ladders and steel ladders to make them "extendable", capable of building upward independent of support to a setting-defined limit (defaulting to 5 nodes for wood and 15 nodes for steel ladders). This can be disabled if undesired.
This mod retains optional backward compatibility with the crafting items from the vines mod (anything with group "vines" can be used to make rope boxes and rope ladders). Ropes can also be made from cotton, available via an optional dependency on the farming mod.
In-game documentation is provided via an optional dependency on the doc mod.
## Interaction with other mods
By default ropes and rope ladders only extend downward into "air" nodes. Other mods can modify this behaviour to add other nodes as valid targets for ropes to extend into using either of two mechanisms: either add `ropes_can_extend_into = 1` to the node definition's groups list or add a dependency on the ropes mod to your mod and then set `ropes.can_extend_into_nodes[target_node_name] = true`. There is also a configuration setting, `ropes_can_extend_into_airlike`, that will allow ropes to extend into any node with `drawtype = "airlike"` in its definition. Note that in cases where ropes extend into non-air nodes the rope will still be replaced with an "air" node when it's eventually destroyed.

88
mods/ropes/bridge.lua Normal file
View file

@ -0,0 +1,88 @@
local S = ropes.S
if ropes.bridges_enabled then
local bridge_on_place = function(itemstack, placer, pointed_thing)
-- Shall place item and return the leftover itemstack.
-- The placer may be any ObjectRef or nil.
-- default: minetest.item_place
if placer == nil then
return minetest.item_place(itemstack, placer, pointed_thing)
end
local above = pointed_thing.above
local under = pointed_thing.under
if above.x == under.x and above.z == under.z and above.y > under.y then
-- we're aimed downward at a buildable node from above.
-- determine the direction the placer lies relative to this node.
local new_under = vector.new(under)
local placer_pos = placer:get_pos()
local diff_x = placer_pos.x - under.x
local diff_z = placer_pos.z - under.z
if math.abs(diff_x) > math.abs(diff_z) then
-- placer is displaced along the X axis relative to the target
if diff_x > 0 then
new_under.x = under.x - 1
else
new_under.x = under.x + 1
end
else
-- placer is displaced along the Z axis relative to the target
if diff_z > 0 then
new_under.z = under.z - 1
else
new_under.z = under.z + 1
end
end
if minetest.registered_nodes[minetest.get_node(new_under).name].buildable_to then
local new_pointed_thing = {type="node", under=new_under, above={x=new_under.x, y=new_under.y+1, z=new_under.z}}
return minetest.item_place(itemstack, placer, new_pointed_thing)
end
end
return minetest.item_place(itemstack, placer, pointed_thing)
end
minetest.register_node("ropes:wood_bridge", {
description = S("Wooden Bridge"),
_doc_items_longdesc = ropes.doc.wooden_bridge_longdesc,
_doc_items_usagehelp = ropes.doc.wooden_bridge_usagehelp,
tiles = {
"default_wood.png", "default_wood.png",
"default_wood.png^[transformR270", "default_wood.png^[transformR90",
"default_wood.png^[transformR270", "default_wood.png^[transformR90",
},
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
groups = {choppy = 2, flammable = 2, oddly_breakable_by_hand = 1, flow_through = 1, fence = 1, wall = 1},
sounds = default.node_sound_wood_defaults(),
node_box = {
type = "fixed",
fixed = {
{-0.5, 0.375, -0.5, 0.5, 0.5, 0.5}, -- Platform
{-0.375, -0.5, -0.5, 0.375, -0.375, -0.4375}, -- x beam4
{-0.375, -0.5, 0.4375, 0.375, -0.375, 0.5}, -- x beam3
{0.375, -0.5, -0.4375, 0.5, -0.375, 0.4375}, -- z beam2
{-0.5, -0.5, -0.4375, -0.375, -0.375, 0.4375}, -- z beam1
{0.375, -0.5, -0.5, 0.5, 0.375, -0.4375}, -- upright4
{0.375, -0.5, 0.4375, 0.5, 0.375, 0.5}, -- upright3
{-0.5, -0.5, -0.5, -0.375, 0.375, -0.4375}, -- upright2
{-0.5, -0.5, 0.4375, -0.375, 0.375, 0.5}, -- upright1
}
},
on_place = bridge_on_place,
})
minetest.register_craft({
output = "ropes:wood_bridge 5",
recipe = {
{"group:stick", "stairs:slab_wood", "group:stick"},
{"group:stick", "", "group:stick"},
{"group:stick", "group:stick", "group:stick"},
}
})
end

90
mods/ropes/crafts.lua Normal file
View file

@ -0,0 +1,90 @@
local S = ropes.S
if minetest.get_modpath("farming") then
-- this doesn't work reliably due to side effects of https://github.com/minetest/minetest/issues/5518
-- local old_def = minetest.registered_craftitems["farming:cotton"]
-- if old_def then
-- old_def.groups["thread"] = 1
-- minetest.override_item("farming:cotton", {
-- groups = old_def.groups
-- })
-- end
minetest.register_craft({
output = 'ropes:ropesegment',
recipe = {
{'farming:cotton','farming:cotton'},
{'farming:cotton','farming:cotton'},
{'farming:cotton','farming:cotton'},
}
})
if farming.mod == "redo" or farming.mod == "undo" then
minetest.register_craft({
output = 'ropes:ropesegment',
recipe = {
{'farming:hemp_rope'},
{'farming:hemp_rope'},
}
})
end
end
if minetest.get_modpath("hemp") then
minetest.register_craft({
output = 'ropes:ropesegment',
recipe = {
{'hemp:hemp_rope'},
{'hemp:hemp_rope'},
}
})
end
if minetest.get_modpath("cottages") then
minetest.register_craft({
output = 'ropes:ropesegment',
recipe = {
{'cottages:rope'},
{'cottages:rope'},
}
})
end
if minetest.get_modpath("moreblocks") then
minetest.register_craft({
output = 'ropes:ropesegment',
recipe = {
{'moreblocks:rope','moreblocks:rope'},
{'moreblocks:rope','moreblocks:rope'},
{'moreblocks:rope','moreblocks:rope'},
}
})
end
minetest.register_craft({
output = 'ropes:ropesegment',
recipe = {
{'group:thread','group:thread'},
{'group:thread','group:thread'},
{'group:thread','group:thread'},
}
})
minetest.register_craftitem("ropes:ropesegment", {
description = S("Rope Segment"),
_doc_items_longdesc = ropes.doc.ropesegment_longdesc,
_doc_items_usagehelp = ropes.doc.ropesegment_usage,
groups = {vines = 1},
inventory_image = "ropes_item.png",
})
local cotton_burn_time = 1
ropes.wood_burn_time = minetest.get_craft_result({method="fuel", width=1, items={ItemStack("default:wood")}}).time
ropes.rope_burn_time = cotton_burn_time * 6
local stick_burn_time = minetest.get_craft_result({method="fuel", width=1, items={ItemStack("default:stick")}}).time
ropes.ladder_burn_time = ropes.rope_burn_time * 2 + stick_burn_time * 3
minetest.register_craft({
type = "fuel",
recipe = "ropes:ropesegment",
burntime = ropes.rope_burn_time,
})

7
mods/ropes/depends.txt Normal file
View file

@ -0,0 +1,7 @@
default
farming?
vines?
doc?
loot?
hemp?
cottages?

View file

@ -0,0 +1 @@
Adds rope boxes of various lengths and also rope ladders.

53
mods/ropes/doc.lua Normal file
View file

@ -0,0 +1,53 @@
ropes.doc = {}
if not minetest.get_modpath("doc") then
return
end
local S = ropes.S
ropes.doc.ropesegment_longdesc = S("Rope segments are bundles of fibre twisted into robust cables.")
ropes.doc.ropesegment_usage = S("This craft item is useful for creating rope ladders, or for spooling on wooden spindles to hang and climb upon.")
ropes.doc.ropeladder_longdesc = S("A hanging rope ladder that automatically extends downward.")
ropes.doc.ropeladder_usage = S("After a rope ladder is placed on a vertical wall it will begin extending downward until it reaches its maximum length (@1 meters). If the rope ladder is removed all of the ladder below the point of removal will disappear. A rope ladder can be severed partway down using an axe or similar tool, and the ladder below the point where it is cut will collapse. No rope is actually lost in the process, though, and if the uppermost section of the ladder is removed and replaced the ladder will re-extend to the same maximum length as before.", ropes.ropeLadderLength)
local rope_length_doc = S("Rope boxes have a certain amount of rope contained within them specified in the name of the node, and have a limit to how much rope they can support that depends on the material they're made of. The different lengths can be crafted by combining and splitting up rope boxes in the crafting grid. For example, you can craft a @1m rope box by putting a @2m rope box and a rope segment in the crafting grid, or a @3m rope box and two rope segments in the crafting grid. Two rope segments can be recovered by putting the @4m rope box in the crafting grid by itself.", ropes.ropeLength*3, ropes.ropeLength*2, ropes.ropeLength, ropes.ropeLength*3) .. "\n"
if ropes.woodRopeBoxMaxMultiple == 1 then
rope_length_doc = rope_length_doc .. "\n" .. S("Wood") .. " " .. S("rope boxes can hold @1m of rope.", ropes.ropeLength)
elseif ropes.woodRopeBoxMaxMultiple > 1 then
rope_length_doc = rope_length_doc .. "\n" .. S("Wood") .. " " .. S("rope boxes can hold rope lengths from @1m to @2m.", ropes.ropeLength, ropes.ropeLength*ropes.woodRopeBoxMaxMultiple)
end
if ropes.copperRopeBoxMaxMultiple == 1 then
rope_length_doc = rope_length_doc .. "\n" .. S("Copper") .. " " .. S("rope boxes can hold @1m of rope.", ropes.ropeLength)
elseif ropes.copperRopeBoxMaxMultiple > 1 then
rope_length_doc = rope_length_doc .. "\n" .. S("Copper") .. " " .. S("rope boxes can hold rope lengths from @1m to @2m.", ropes.ropeLength, ropes.ropeLength*ropes.copperRopeBoxMaxMultiple)
end
if ropes.steelRopeBoxMaxMultiple == 1 then
rope_length_doc = rope_length_doc .. "\n" .. S("Steel") .. " " .. S("rope boxes can hold @1m of rope.", ropes.ropeLength)
elseif ropes.steelRopeBoxMaxMultiple > 1 then
rope_length_doc = rope_length_doc .. "\n" .. S("Steel") .. " " .. S("rope boxes can hold rope lengths from @1m to @2m.", ropes.ropeLength, ropes.ropeLength*ropes.steelRopeBoxMaxMultiple)
end
ropes.doc.ropebox_longdesc = S("Ropes are hung by placing rope boxes, which automatically lower a rope of fixed length below them. They can be climbed and cut.")
ropes.doc.ropebox_usage = rope_length_doc .. "\n\n" ..
S("When a rope box is placed the rope will immediately begin lowering from it at one meter per second. The rope will only descend when its end is in the vicinity of an active player, suspending its journey when no players are nearby, so a long descent may require a player to climb down the rope as it goes. If you are near the bottom end of a rope that's extending you'll be automatically carried down with it. The rope will stop when it encounters and obstruction, but will resume lowering if the obstruction is removed.") .. "\n\n" ..
S("A rope can be severed midway using an axe or other similar tool. The section of rope below the cut will collapse and disappear, potentially causing players who were hanging on to it to fall. The remaining rope will not resume descent on its own, but the rope box at the top of the rope \"remembers\" how long the rope was and if it is deconstructed and replaced it will still have the same maximum length of rope as before - no rope is permanently lost when a rope is severed like this.")
if ropes.extending_ladder_enabled then
ropes.doc.ladder_longdesc = S("A ladder for climbing. It can reach greater heights when placed against a supporting block.")
ropes.doc.ladder_usagehelp = S("Right-clicking on a ladder with a stack of identical ladder items will automatically add new ladder segments to the top, provided it hasn't extended too far up beyond the last block behind it providing support.")
end
ropes.doc.wooden_bridge_longdesc = S("A wooden platform with support struts useful for bridging gaps.")
ropes.doc.wooden_bridge_usagehelp = S("This behaves like most structural blocks except in one circumstance: when placed on top of a block with buildable space on the side facing away from you, this block will not be built on top but instead will extend out from that far side of the target block. This allows a platform to be easily built that juts out away from the location you're standing on.")
doc.add_entry_alias("nodes", "ropes:ropeladder_top", "nodes", "ropes:ropeladder")
doc.add_entry_alias("nodes", "ropes:ropeladder_top", "nodes", "ropes:ropeladder_bottom")
doc.add_entry_alias("nodes", "ropes:ropeladder_top", "nodes", "ropes:ropeladder_falling")
doc.add_entry_alias("nodes", "ropes:rope", "nodes", "ropes:rope_bottom")
doc.add_entry_alias("nodes", "ropes:rope", "nodes", "ropes:rope_top")

View file

@ -0,0 +1,251 @@
local S = ropes.S
if ropes.extending_ladder_enabled then
local wood_recipe = {
{"group:stick", "", "group:stick"},
{"group:stick", "", "group:stick"},
{"group:stick", "group:stick", "group:stick"},
}
local wood_name = S("Wooden Extendable Ladder")
local steel_recipe = {
{"default:steel_ingot", "", "default:steel_ingot"},
{"default:steel_ingot", "", "default:steel_ingot"},
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
}
local steel_name = S("Steel Extendable Ladder")
-- Overlay texture: by 1F616EMO, CC0 <https://creativecommons.org/publicdomain/zero/1.0/>
local texture_overlay = "^ropes_ropeladder_overlay.png"
if ropes.replace_default_ladders then
minetest.unregister_item("default:ladder_wood")
minetest.unregister_item("default:ladder_steel")
minetest.clear_craft({output = "default:ladder_wood"})
minetest.clear_craft({output = "default:ladder_steel"})
local wallmounted_to_facedir =
{[0] = 15, -- ceiling
[1] = 13, -- floor
[2] = 1, -- +X
[3] = 3, -- -X
[4] = 0, -- +Z
[5] = 2, -- -Z
}
minetest.register_lbm({
label = "Switch from wallmounted default ladders to rope mod extending ladders",
name = "ropes:wallmounted_ladder_to_facedir_ladder",
nodenames = {"default:ladder_wood", "default:ladder_steel"},
run_at_every_load = false,
action = function(pos, node)
local new_node = {param2 = wallmounted_to_facedir[node.param2]}
if (node.name == "default:ladder_wood") then
new_node.name = "ropes:ladder_wood"
else
new_node.name = "ropes:ladder_steel"
end
minetest.set_node(pos, new_node)
end,
})
wood_recipe = {
{"group:stick", "", "group:stick"},
{"group:stick", "group:stick", "group:stick"},
{"group:stick", "", "group:stick"},
}
wood_name = S("Wooden Ladder")
steel_recipe = {
{'default:steel_ingot', '', 'default:steel_ingot'},
{'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
{'default:steel_ingot', '', 'default:steel_ingot'},
}
steel_name = S("Steel Ladder")
texture_overlay = ""
else
-- Swap between normal and extendable ladders
minetest.register_craft({
type = "shapeless",
output = "ropes:ladder_wood",
recipe = {"default:ladder_wood"},
})
minetest.register_craft({
type = "shapeless",
output = "ropes:ladder_steel",
recipe = {"default:ladder_steel"},
})
end
minetest.register_craft({
output = "ropes:ladder_wood 5",
recipe = wood_recipe,
})
minetest.register_craft({
output = 'ropes:ladder_steel 15',
recipe = steel_recipe,
})
local ladder_extender = function(pos, node, clicker, itemstack, pointed_thing, ladder_node, standing_limit)
-- on_rightclick can be called by other mods, make sure we have all the parameters we need
if pointed_thing == nil or itemstack == nil then
return itemstack
end
local clicked_stack = ItemStack(itemstack)
-- true if we're pointing up at the ladder from below and there's a buildable space below it
-- this check allows us to extend ladders downward
local pointing_directly_below =
pointed_thing.above.x == pos.x and
pointed_thing.above.z == pos.z and
pointed_thing.above.y == pos.y - 1 and
minetest.registered_nodes[minetest.get_node(pointed_thing.above).name].buildable_to
if clicked_stack:get_name() == ladder_node and not pointing_directly_below then
local param2 = minetest.get_node(pos).param2
local dir = minetest.facedir_to_dir(param2)
local scan_limit = pos.y + 6 -- Only add ladder segments up to five nodes above the one clicked on
pos.y = pos.y + 1
while pos.y < scan_limit and minetest.get_node(pos).name == ladder_node do
param2 = minetest.get_node(pos).param2
pos.y = pos.y + 1
end
if pos.y < scan_limit and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to then
-- scan downward behind the ladder to find support
local behind_pos = vector.add(pos, minetest.facedir_to_dir(param2))
local target_height = pos.y - standing_limit - 1
while behind_pos.y > target_height and minetest.registered_nodes[minetest.get_node(behind_pos).name].buildable_to do
behind_pos.y = behind_pos.y - 1
end
-- If there's enough support, build a new ladder segment
if behind_pos.y > target_height then
if minetest.is_protected(pos, clicker:get_player_name()) then
minetest.record_protection_violation(pos, clicker:get_player_name())
else
minetest.set_node(pos, {name=ladder_node, param2=param2})
if not minetest.settings:get_bool("creative_mode") then
clicked_stack:take_item(1)
end
end
end
end
elseif clicked_stack:get_definition().type == "node" then
return minetest.item_place_node(itemstack, clicker, pointed_thing)
end
return clicked_stack
end
minetest.register_node("ropes:ladder_wood", {
description = wood_name,
_doc_items_longdesc = ropes.doc.ladder_longdesc,
_doc_items_usagehelp = ropes.doc.ladder_usagehelp,
tiles = {"default_wood.png","default_wood.png","default_wood.png^[transformR270","default_wood.png^[transformR270","default_ladder_wood.png"},
use_texture_alpha = "clip",
inventory_image = "default_ladder_wood.png" .. texture_overlay,
wield_image = "default_ladder_wood.png",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
walkable = false,
climbable = true,
is_ground_content = false,
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-0.375, -0.5, 0.375, -0.25, 0.5, 0.5}, -- Upright1
{0.25, -0.5, 0.375, 0.375, 0.5, 0.5}, -- Upright2
{-0.4375, 0.3125, 0.4375, 0.4375, 0.4375, 0.5}, -- Rung_4
{-0.4375, -0.1875, 0.4375, 0.4375, -0.0625, 0.5}, -- Rung_2
{-0.4375, -0.4375, 0.4375, 0.4375, -0.3125, 0.5}, -- Rung_1
{-0.4375, 0.0625, 0.4375, 0.4375, 0.1875, 0.5}, -- Rung_3
}
},
groups = {choppy = 2, oddly_breakable_by_hand = 3, flammable = 2, flow_through = 1},
sounds = default.node_sound_wood_defaults(),
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
return ladder_extender(pos, node, clicker, itemstack, pointed_thing, "ropes:ladder_wood", ropes.extending_wood_ladder_limit)
end,
})
minetest.register_node("ropes:ladder_steel", {
description = steel_name,
_doc_items_longdesc = ropes.doc.ladder_longdesc,
_doc_items_usagehelp = ropes.doc.ladder_usagehelp,
tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png","default_ladder_steel.png"},
use_texture_alpha = "clip",
inventory_image = "default_ladder_steel.png" .. texture_overlay,
wield_image = "default_ladder_steel.png",
paramtype = "light",
paramtype2 = "facedir",
sunlight_propagates = true,
walkable = false,
climbable = true,
is_ground_content = false,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-0.4375, -0.5, 0.3125, -0.25, 0.5, 0.5}, -- Upright1
{0.25, -0.5, 0.3125, 0.4375, 0.5, 0.5}, -- Upright2
{-0.25, 0.3125, 0.375, 0.25, 0.4375, 0.5}, -- Rung_4
{-0.25, -0.1875, 0.375, 0.25, -0.0625, 0.5}, -- Rung_2
{-0.25, -0.4375, 0.375, 0.25, -0.3125, 0.5}, -- Rung_1
{-0.25, 0.0625, 0.375, 0.25, 0.1875, 0.5}, -- Rung_3
}
},
groups = {cracky = 2, flow_through = 1},
sounds = default.node_sound_metal_defaults(),
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
return ladder_extender(pos, node, clicker, itemstack, pointed_thing, "ropes:ladder_steel", ropes.extending_steel_ladder_limit)
end,
})
else
-- Table of possible wallmounted values
local facedir_to_wallmounted = {
4, -- +Z
2, -- +X
5, -- -Z
3, -- -X
1, -- -Y
0, -- +Y
}
-- Mapping from facedir value to index in facedir_to_dir.
local facedir_to_wallmounted_map = {
[0]=1, 2, 3, 4,
5, 2, 6, 4,
6, 2, 5, 4,
1, 5, 3, 6,
1, 6, 3, 5,
1, 4, 3, 2,
}
minetest.register_lbm({
label = "Switch from ropes ladders to wallmounted default ladders",
name = "ropes:facedir_ladder_to_wallmounted_ladder",
nodenames = {"ropes:ladder_wood", "ropes:ladder_steel"},
run_at_every_load = false,
action = function(pos, node)
local new_node = {param2 = facedir_to_wallmounted[facedir_to_wallmounted_map[node.param2 % 32]]}
if (node.name == "ropes:ladder_wood") then
new_node.name = "default:ladder_wood"
else
new_node.name = "default:ladder_steel"
end
minetest.set_node(pos, new_node)
end,
})
end

115
mods/ropes/functions.lua Normal file
View file

@ -0,0 +1,115 @@
ropes.can_place_rope_in_node = function(target_node_name)
if ropes.can_extend_into_nodes[target_node_name] == true then
return true
end
local target_def = minetest.registered_nodes[target_node_name]
if target_def then
if target_def.drawtype == "airlike" and ropes.can_extend_into_airlike then
return true
end
if target_def.groups and target_def.groups.ropes_can_extend_into then
return true
end
end
return false
end
ropes.make_rope_on_timer = function(rope_node_name)
return function(pos, elapsed)
local currentend = minetest.get_node(pos)
local currentmeta = minetest.get_meta(pos)
local currentlength = currentmeta:get_int("length_remaining")
local placer_name = currentmeta:get_string("placer")
local newpos = {x=pos.x, y=pos.y-1, z=pos.z}
local newnode = minetest.get_node(newpos)
local oldnode = minetest.get_node(pos)
if currentlength > 1 and (not minetest.is_protected(newpos, placer_name)
or minetest.check_player_privs(placer_name, "protection_bypass")) then
if ropes.can_place_rope_in_node(newnode.name) then
minetest.add_node(newpos, {name=currentend.name, param2=oldnode.param2})
local newmeta = minetest.get_meta(newpos)
newmeta:set_int("length_remaining", currentlength-1)
newmeta:set_string("placer", placer_name)
minetest.set_node(pos, {name=rope_node_name, param2=oldnode.param2})
ropes.move_players_down(pos, 1)
else
local timer = minetest.get_node_timer( pos )
timer:start( 1 )
end
end
end
end
local data = {}
local c_air = minetest.get_content_id("air")
ropes.destroy_rope = function(pos, nodes)
local top = pos.y
local bottom = pos.y-15
local voxel_manip = minetest.get_voxel_manip()
local finished = false
local ids_to_destroy = {}
for _, node in pairs(nodes) do
if minetest.registered_nodes[node] then
ids_to_destroy[minetest.get_content_id(node)] = true
end
end
while not finished do
local emin, emax = voxel_manip:read_from_map({x=pos.x, y=bottom, z=pos.z}, {x=pos.x, y=top, z=pos.z})
voxel_manip:get_data(data)
local voxel_area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
bottom = emin.y
for y = top, bottom, -1 do
local index = voxel_area:index(pos.x, y, pos.z)
if ids_to_destroy[data[index]] then
data[index] = c_air
else
finished = true
break
end
end
voxel_manip:set_data(data)
voxel_manip:write_to_map()
voxel_manip:update_map()
top = bottom - 1
bottom = bottom - 15
end
end
ropes.hanging_after_destruct = function(pos, top_node, middle_node, bottom_node)
local node = minetest.get_node(pos)
if node.name == top_node or node.name == middle_node or node.name == bottom_node then
return -- this was done by another ladder or rope node changing this one, don't react
end
pos.y = pos.y + 1 -- one up
local node_above = minetest.get_node(pos)
if node_above.name == middle_node then
minetest.swap_node(pos, {name=bottom_node, param2=node_above.param2})
end
pos.y = pos.y - 2 -- one down
local node_below = minetest.get_node(pos)
if node_below.name == middle_node then
ropes.destroy_rope(pos, {middle_node, bottom_node})
elseif node_below.name == bottom_node then
minetest.swap_node(pos, {name="air"})
end
end
ropes.move_players_down = function(pos, radius)
local all_objects = minetest.get_objects_inside_radius({x=pos.x, y=pos.y+radius, z=pos.z}, radius)
local players = {}
local _,obj
for _,obj in pairs(all_objects) do
if obj:is_player() then
local obj_pos = obj:get_pos()
if math.abs(obj_pos.x-pos.x) < 0.5 and math.abs(obj_pos.z-pos.z) < 0.5 then
obj:set_pos({x=obj_pos.x, y=obj_pos.y-1, z=obj_pos.z}, true)
end
end
end
end

418
mods/ropes/i18n.py Normal file
View file

@ -0,0 +1,418 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": []
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods'],
"verbose": ['--verbose', '-v']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name):
lOut = [f"# textdomain: {mod_name}\n"]
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
def import_tr_file(tr_file):
dOut = {}
text = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name)
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print("Unable to find modname in folder " + folder)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()

66
mods/ropes/init.lua Normal file
View file

@ -0,0 +1,66 @@
ropes = {
name = 'ropes',
}
-- internationalization boilerplate
local modname = minetest.get_current_modname()
local MP = minetest.get_modpath(modname)
ropes.S = minetest.get_translator(modname)
ropes.ropeLength = tonumber(minetest.settings:get("ropes_rope_length")) or 50
ropes.woodRopeBoxMaxMultiple = tonumber(minetest.settings:get("ropes_wood_rope_box_max_multiple")) or 2
ropes.copperRopeBoxMaxMultiple = tonumber(minetest.settings:get("ropes_copper_rope_box_max_multiple")) or 5
ropes.steelRopeBoxMaxMultiple = tonumber(minetest.settings:get("ropes_steel_rope_box_max_multiple")) or 9
ropes.create_all_definitions = minetest.settings:get_bool("ropes_create_all_definitions")
ropes.ropeLadderLength = tonumber(minetest.settings:get("ropes_rope_ladder_length")) or 50
ropes.extending_ladder_enabled = minetest.settings:get_bool("ropes_extending_ladder_enabled")
if ropes.extending_ladder_enabled == nil then
ropes.extending_ladder_enabled = true
end
ropes.replace_default_ladders = minetest.settings:get_bool("ropes_replace_default_ladders")
ropes.extending_wood_ladder_limit = tonumber(minetest.settings:get("ropes_extending_wood_ladder_limit")) or 5
ropes.extending_steel_ladder_limit = tonumber(minetest.settings:get("ropes_extending_steel_ladder_limit")) or 15
ropes.bridges_enabled = minetest.settings:get_bool("ropes_bridges_enabled")
if ropes.bridges_enabled == nil then
ropes.bridges_enabled = true
end
ropes.can_extend_into_airlike = minetest.settings:get_bool("ropes_can_extend_into_airlike")
ropes.can_extend_into_nodes = {["air"] = true}
if minetest.get_modpath("nether") then
ropes.can_extend_into_nodes["nether:fumes"] = true
end
dofile( MP .. "/doc.lua" )
dofile( MP .. "/functions.lua" )
dofile( MP .. "/crafts.lua" )
dofile( MP .. "/ropeboxes.lua" )
dofile( MP .. "/ropeladder.lua" )
dofile( MP .. "/extendingladder.lua" )
dofile( MP .. "/bridge.lua" )
dofile( MP .. "/loot.lua" )
for i=1,5 do
minetest.register_alias(string.format("vines:%irope_block", i), string.format("ropes:%irope_block", i))
end
minetest.register_alias("vines:rope", "ropes:rope")
minetest.register_alias("vines:rope_bottom", "ropes:rope_bottom")
minetest.register_alias("vines:rope_end", "ropes:rope_bottom")
minetest.register_alias("vines:rope_top", "ropes:rope_top")
minetest.register_alias("vines:ropeladder_top", "ropes:ropeladder_top")
minetest.register_alias("vines:ropeladder", "ropes:ropeladder")
minetest.register_alias("vines:ropeladder_bottom", "ropes:ropeladder_bottom")
minetest.register_alias("vines:ropeladder_falling", "ropes:ropeladder_falling")
minetest.register_alias("vines:rope_block", "ropes:steel5rope_block")
for i=1,9 do
minetest.register_alias(string.format("ropes:%irope_block", i), string.format("ropes:steel%irope_block", i))
end
minetest.register_alias("castle:ropes", "ropes:rope")
minetest.register_alias("castle:ropebox", "ropes:steel1rope_block")
minetest.register_alias("castle:box_rope", "ropes:rope")
print("[Ropes] Loaded!")

View file

@ -0,0 +1,71 @@
# textdomain: ropes
### bridge.lua ###
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Wooden Bridge=Puente de madera
### crafts.lua ###
Rope Segment=Segmento de cuerda
### doc.lua ###
A hanging rope ladder that automatically extends downward.=Una escalera de cuerda colgante que se extiende automáticamente hacia abajo.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
A ladder for climbing. It can reach greater heights when placed against a supporting block.=Una escalera para subir. Puede alcanzar mayores alturas cuando se coloca contra un bloque de soporte.
A rope can be severed midway using an axe or other similar tool. The section of rope below the cut will collapse and disappear, potentially causing players who were hanging on to it to fall. The remaining rope will not resume descent on its own, but the rope box at the top of the rope "remembers" how long the rope was and if it is deconstructed and replaced it will still have the same maximum length of rope as before - no rope is permanently lost when a rope is severed like this.=Una cuerda puede ser cortada a mitad de camino usando un hacha u otra herramienta similar. La sección de la cuerda debajo del corte se colapsará y desaparecerá, lo que puede causar que los jugadores que estaban colgados de ella se caigan. El resto de la cuerda no volverá a descender por sí sola, pero la caja de la cuerda en la parte superior de la cuerda "recuerda" la longitud de la cuerda y si es deconstruida y reemplazada tendrá la misma longitud máxima de cuerda que antes - ninguna cuerda se pierde permanentemente cuando una cuerda es cortada de esta manera.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
A wooden platform with support struts useful for bridging gaps.=Una plataforma de madera con puntales de soporte útil para salvar huecos.
After a rope ladder is placed on a vertical wall it will begin extending downward until it reaches its maximum length (@1 meters). If the rope ladder is removed all of the ladder below the point of removal will disappear. A rope ladder can be severed partway down using an axe or similar tool, and the ladder below the point where it is cut will collapse. No rope is actually lost in the process, though, and if the uppermost section of the ladder is removed and replaced the ladder will re-extend to the same maximum length as before.=Después de colocar una escalera de cuerda en una pared vertical, comenzará a extenderse hacia abajo hasta que alcance su longitud máxima (@1 metro). Si se retira la escalera de cuerda, desaparecerá toda la escalera por debajo del punto de extracción. Una escalera de cuerda puede ser cortada hasta la mitad usando un hacha o una herramienta similar, y la escalera por debajo del punto donde es cortada colapsará. Sin embargo, no se pierde ninguna cuerda en el proceso, y si la sección superior de la escalera se retira y se reemplaza, la escalera se volverá a extender a la misma longitud máxima que antes.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Right-clicking on a ladder with a stack of identical ladder items will automatically add new ladder segments to the top, provided it hasn't extended too far up beyond the last block behind it providing support.=Al hacer clic con el botón derecho en una escalera con una pila de elementos de escalera idénticos, se agregarán automáticamente nuevos segmentos de escalera a la parte superior, siempre que no se haya extendido demasiado más allá del último bloque detrás de ella que brinda soporte.
Rope boxes have a certain amount of rope contained within them specified in the name of the node, and have a limit to how much rope they can support that depends on the material they're made of. The different lengths can be crafted by combining and splitting up rope boxes in the crafting grid. For example, you can craft a @1m rope box by putting a @2m rope box and a rope segment in the crafting grid, or a @3m rope box and two rope segments in the crafting grid. Two rope segments can be recovered by putting the @4m rope box in the crafting grid by itself.=Las cajas de cuerdas tienen una cierta cantidad de cuerda contenida dentro de ellas especificada en el nombre del nodo, y tienen un límite en la cantidad de cuerda que pueden soportar que depende del material del que están hechas. Las diferentes longitudes se pueden realizar combinando y dividiendo las cajas de cuerda en la rejilla de elaboración. Por ejemplo, puedes crear una caja de cuerda de @1m poniendo una caja de cuerda de @2m y un segmento de cuerda en la rejilla de artesanía, o una caja de cuerda de @3m y dos segmentos de cuerda en la rejilla de artesanía. Se pueden recuperar dos segmentos de cable colocando solo la caja de cable de @4m en la rejilla de fabricación.
Rope segments are bundles of fibre twisted into robust cables.=Los segmentos de cable son haces de fibras trenzadas en cables robustos.
Ropes are hung by placing rope boxes, which automatically lower a rope of fixed length below them. They can be climbed and cut.=Las cuerdas se cuelgan colocando cajas de cuerda, que bajan automáticamente una cuerda de longitud fija por debajo de ellas. Se pueden escalar y cortar.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
This behaves like most structural blocks except in one circumstance: when placed on top of a block with buildable space on the side facing away from you, this block will not be built on top but instead will extend out from that far side of the target block. This allows a platform to be easily built that juts out away from the location you're standing on.=Esto se comporta como la mayoría de los bloques estructurales, excepto en una circunstancia: cuando se coloca encima de un bloque con un espacio edificable en el lado que mira hacia afuera, este bloque no se construirá en la parte superior, sino que se extenderá desde ese lado más alejado del bloque objetivo. . Esto permite construir fácilmente una plataforma que sobresale del lugar en el que se encuentra.
This craft item is useful for creating rope ladders, or for spooling on wooden spindles to hang and climb upon.=Esta objeto es útil para crear escaleras de cuerda, o para enrollar en husillos de madera para colgar y trepar.
When a rope box is placed the rope will immediately begin lowering from it at one meter per second. The rope will only descend when its end is in the vicinity of an active player, suspending its journey when no players are nearby, so a long descent may require a player to climb down the rope as it goes. If you are near the bottom end of a rope that's extending you'll be automatically carried down with it. The rope will stop when it encounters and obstruction, but will resume lowering if the obstruction is removed.=Cuando se coloca una caja de cuerda, la cuerda comienza a descender inmediatamente a un metro por segundo. La cuerda sólo descenderá cuando su final esté cerca de un jugador activo, suspendiendo su viaje cuando no haya jugadores cerca, por lo que un largo descenso puede requerir que el jugador baje por la cuerda a medida que avanza. Si estás cerca del extremo inferior de una cuerda que se está extendiendo, serás arrastrado automáticamente hacia abajo con ella. La cuerda se detendrá cuando se encuentre con una obstrucción, pero volverá a bajar si se retira la obstrucción.
rope boxes can hold @1m of rope.=Las cajas de cuerdas pueden mantener @1m de cuerda.
rope boxes can hold rope lengths from @1m to @2m.=Las cajas de cuerda pueden contener longitudes de cuerda de @1m a @2m.
### doc.lua ###
### ropeboxes.lua ###
Copper=cobre
Steel=acero
Wood=madera
### extendingladder.lua ###
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Steel Extendable Ladder=Escalera extensible de acero
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Steel Ladder=Escalera de acero
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Wooden Extendable Ladder=Escalera extensible de madera
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Wooden Ladder=Escalera de madera
### ropeboxes.lua ###
@1 Ropebox @2m=Caja de cuerda de @1 de @2m
Rope=Cuerda
### ropeladder.lua ###
Rope Ladder=Escalera de cuerda

View file

@ -0,0 +1,70 @@
# textdomain: ropes
### bridge.lua ###
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Wooden Bridge=Деревянный мост
### crafts.lua ###
Rope Segment=Сегмент каната
### doc.lua ###
A hanging rope ladder that automatically extends downward.=Подвесная веревочная лестница, которая автоматически выдвигается вниз.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
A ladder for climbing. It can reach greater heights when placed against a supporting block.=Лестница для подъема. Она может достигать большей высоты, если ее приставить к опорному блоку.
A rope can be severed midway using an axe or other similar tool. The section of rope below the cut will collapse and disappear, potentially causing players who were hanging on to it to fall. The remaining rope will not resume descent on its own, but the rope box at the top of the rope "remembers" how long the rope was and if it is deconstructed and replaced it will still have the same maximum length of rope as before - no rope is permanently lost when a rope is severed like this.=Веревку можно перерезать на полпути с помощью топора или другого похожего инструмента. Часть веревки в месте разреза разрушится и исчезнет, ​​что может привести к падению игроков, которые на ней висели. Оставшаяся веревка не возобновит спуск сама по себе, но коробка для веревок наверху «помнит», какой длины была веревка, и если ее разобрать и заменить, она все равно будет иметь ту же максимальную длину веревки, что и раньше — ни одна веревка не теряется навсегда, когда веревка перерезана таким образом.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
A wooden platform with support struts useful for bridging gaps.=Деревянная платформа с опорными стойками, используемая для перекрытия щелей.
After a rope ladder is placed on a vertical wall it will begin extending downward until it reaches its maximum length (@1 meters). If the rope ladder is removed all of the ladder below the point of removal will disappear. A rope ladder can be severed partway down using an axe or similar tool, and the ladder below the point where it is cut will collapse. No rope is actually lost in the process, though, and if the uppermost section of the ladder is removed and replaced the ladder will re-extend to the same maximum length as before.=После того, как веревочная лестница будет размещена на вертикальной стене, она начнет удлиняться вниз, пока не достигнет своей максимальной длины (@1 метр). Если веревочную лестницу убрать, вся лестница ниже точки удаления исчезнет. Веревочную лестницу можно перерезать наполовину с помощью топора или подобного инструмента, и лестница ниже точки, где она перерезана, рухнет. Однако в этом процессе веревка фактически не теряется, и если снять и заменить самую верхнюю часть лестницы, лестница снова удлинится до той же максимальной длины, что и раньше.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Right-clicking on a ladder with a stack of identical ladder items will automatically add new ladder segments to the top, provided it hasn't extended too far up beyond the last block behind it providing support.=Щелчок правой кнопкой мыши по лестнице со стопкой идентичных элементов лестницы автоматически добавит новые сегменты лестницы наверх, при условии, что она не выступает слишком далеко за пределы последнего блока позади нее, обеспечивающего поддержку.
Rope boxes have a certain amount of rope contained within them specified in the name of the node, and have a limit to how much rope they can support that depends on the material they're made of. The different lengths can be crafted by combining and splitting up rope boxes in the crafting grid. For example, you can craft a @1m rope box by putting a @2m rope box and a rope segment in the crafting grid, or a @3m rope box and two rope segments in the crafting grid. Two rope segments can be recovered by putting the @4m rope box in the crafting grid by itself.=Ящики для веревок содержат определенное количество веревки, указанное в названии узла, и имеют ограничение на то, сколько веревки они могут поддерживать, в зависимости от материала, из которого они сделаны. Различные длины могут быть созданы путем объединения и разделения ящиков для веревок в сетке крафта. Например, вы можете создать ящик для веревки @1m, поместив ящик для веревки @2m и сегмент веревки в сетку крафта, или ящик для веревки @3m и два сегмента веревки в сетку крафта. Два сегмента веревки можно получить, поместив ящик для веревки @4m в сетку крафта отдельно.
Rope segments are bundles of fibre twisted into robust cables.=Сегменты каната представляют собой пучки волокон, скрученные в прочные тросы.
Ropes are hung by placing rope boxes, which automatically lower a rope of fixed length below them. They can be climbed and cut.=Канаты подвешиваются путем размещения ящиков для канатов, которые автоматически опускают под ними канат фиксированной длины. По ним можно лазить и резать.
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
This behaves like most structural blocks except in one circumstance: when placed on top of a block with buildable space on the side facing away from you, this block will not be built on top but instead will extend out from that far side of the target block. This allows a platform to be easily built that juts out away from the location you're standing on.=Он ведет себя как большинство структурных блоков, за исключением одного обстоятельства: при размещении на блоке с пространством для строительства на стороне, обращенной от вас, этот блок не будет построен сверху, а вместо этого будет выступать из дальней стороны целевого блока. Это позволяет легко построить платформу, которая выступает из того места, где вы стоите.
This craft item is useful for creating rope ladders, or for spooling on wooden spindles to hang and climb upon.=Этот предмет ручной работы пригодится для создания веревочных лестниц или для наматывания на деревянные стержни, чтобы можно было повесить веревку или залезть на нее.
When a rope box is placed the rope will immediately begin lowering from it at one meter per second. The rope will only descend when its end is in the vicinity of an active player, suspending its journey when no players are nearby, so a long descent may require a player to climb down the rope as it goes. If you are near the bottom end of a rope that's extending you'll be automatically carried down with it. The rope will stop when it encounters and obstruction, but will resume lowering if the obstruction is removed.=Когда вы размещаете ящик с веревкой, веревка немедленно начнет опускаться с него со скоростью один метр в секунду. Веревка будет опускаться только тогда, когда ее конец находится в непосредственной близости от активного игрока, приостанавливая свое движение, когда поблизости нет игроков, поэтому для длительного спуска игроку может потребоваться спускаться по веревке по мере ее движения. Если вы находитесь около нижнего конца удлиняющейся веревки, вы автоматически будете унесены ею вниз. Веревка остановится, когда столкнется с препятствием, но возобновит спуск, если препятствие будет устранено.
rope boxes can hold @1m of rope.=Ящики для веревок вмещают до 1 м веревки.
rope boxes can hold rope lengths from @1m to @2m.=Ящики для веревок могут вмещать веревки длиной от @1 до @2 метров.
### doc.lua ###
### ropeboxes.lua ###
Copper=Медный
Steel=Стальной
Wood=Древесный
### extendingladder.lua ###
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Steel Extendable Ladder=Стальная раздвижная лестница
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Steel Ladder=Стальная лестница
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Wooden Extendable Ladder=Деревянная раздвижная лестница
#WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
Wooden Ladder=Деревянная лестница
### ropeboxes.lua ###
@1 Ropebox @2m=@1 веревочный ящик @2м
Rope=Веревка
### ropeladder.lua ###
Rope Ladder=Веревочная лестница

View file

@ -0,0 +1,62 @@
# textdomain: ropes
### bridge.lua ###
Wooden Bridge=
### crafts.lua ###
Rope Segment=
### doc.lua ###
A hanging rope ladder that automatically extends downward.=
A ladder for climbing. It can reach greater heights when placed against a supporting block.=
A rope can be severed midway using an axe or other similar tool. The section of rope below the cut will collapse and disappear, potentially causing players who were hanging on to it to fall. The remaining rope will not resume descent on its own, but the rope box at the top of the rope "remembers" how long the rope was and if it is deconstructed and replaced it will still have the same maximum length of rope as before - no rope is permanently lost when a rope is severed like this.=
A wooden platform with support struts useful for bridging gaps.=
After a rope ladder is placed on a vertical wall it will begin extending downward until it reaches its maximum length (@1 meters). If the rope ladder is removed all of the ladder below the point of removal will disappear. A rope ladder can be severed partway down using an axe or similar tool, and the ladder below the point where it is cut will collapse. No rope is actually lost in the process, though, and if the uppermost section of the ladder is removed and replaced the ladder will re-extend to the same maximum length as before.=
Right-clicking on a ladder with a stack of identical ladder items will automatically add new ladder segments to the top, provided it hasn't extended too far up beyond the last block behind it providing support.=
Rope boxes have a certain amount of rope contained within them specified in the name of the node, and have a limit to how much rope they can support that depends on the material they're made of. The different lengths can be crafted by combining and splitting up rope boxes in the crafting grid. For example, you can craft a @1m rope box by putting a @2m rope box and a rope segment in the crafting grid, or a @3m rope box and two rope segments in the crafting grid. Two rope segments can be recovered by putting the @4m rope box in the crafting grid by itself.=
Rope segments are bundles of fibre twisted into robust cables.=
Ropes are hung by placing rope boxes, which automatically lower a rope of fixed length below them. They can be climbed and cut.=
This behaves like most structural blocks except in one circumstance: when placed on top of a block with buildable space on the side facing away from you, this block will not be built on top but instead will extend out from that far side of the target block. This allows a platform to be easily built that juts out away from the location you're standing on.=
This craft item is useful for creating rope ladders, or for spooling on wooden spindles to hang and climb upon.=
When a rope box is placed the rope will immediately begin lowering from it at one meter per second. The rope will only descend when its end is in the vicinity of an active player, suspending its journey when no players are nearby, so a long descent may require a player to climb down the rope as it goes. If you are near the bottom end of a rope that's extending you'll be automatically carried down with it. The rope will stop when it encounters and obstruction, but will resume lowering if the obstruction is removed.=
rope boxes can hold @1m of rope.=
rope boxes can hold rope lengths from @1m to @2m.=
### doc.lua ###
### ropeboxes.lua ###
Copper=
Steel=
Wood=
### extendingladder.lua ###
Steel Extendable Ladder=
Steel Ladder=
Wooden Extendable Ladder=
Wooden Ladder=
### ropeboxes.lua ###
@1 Ropebox @2m=
Rope=
### ropeladder.lua ###
Rope Ladder=

56
mods/ropes/loot.lua Normal file
View file

@ -0,0 +1,56 @@
if not minetest.get_modpath("loot") then
return
end
loot.register_loot({
weights = { generic = 300 },
payload = {
stack = ItemStack("ropes:ropesegment"),
min_size = 1,
max_size = 50,
},
})
if ropes.ropeLadderLength > 0 then
loot.register_loot({
weights = { generic = 150 },
payload = {
stack = ItemStack("ropes:ropeladder_top"),
min_size = 1,
max_size = 20,
},
})
end
if ropes.woodRopeBoxMaxMultiple > 0 then
loot.register_loot({
weights = { generic = 100 },
payload = {
stack = ItemStack("ropes:wood1rope_block"),
min_size = 1,
max_size = 20,
},
})
end
if ropes.copperRopeBoxMaxMultiple > 0 then
loot.register_loot({
weights = { generic = 75 },
payload = {
stack = ItemStack("ropes:copper1rope_block"),
min_size = 1,
max_size = 15,
},
})
end
if ropes.steelRopeBoxMaxMultiple > 0 then
loot.register_loot({
weights = { generic = 50 },
payload = {
stack = ItemStack("ropes:steel1rope_block"),
min_size = 1,
max_size = 10,
},
})
end

7
mods/ropes/mod.conf Normal file
View file

@ -0,0 +1,7 @@
name = ropes
description = Adds rope boxes and ladders
depends = default
optional_depends = cottages, doc, farming, hemp, loot, vines
release = 29152
author = FaceDeer
title = Ropes

363
mods/ropes/ropeboxes.lua Normal file
View file

@ -0,0 +1,363 @@
-- internationalization boilerplate
local S = ropes.S
local function rope_box_tiles(count, tint)
return {
string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
string.format("ropes_ropebox_side.png^[colorize:%s^ropes_ropebox_side.png", tint),
string.format("ropes_ropebox_side.png^[colorize:%s^ropes_ropebox_side.png", tint),
string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
}
end
local rope_box_data = {
{
node={
{-0.125, -0.125, -0.25, 0.125, 0.125, 0.25}, -- pulley
{-0.125, -0.25, -0.125, 0.125, 0.25, 0.125}, -- pulley
{-0.125, -0.1875, -0.1875, 0.125, 0.1875, 0.1875}, -- pulley_core
{-0.1875, -0.5, -0.125, -0.125, 0.125, 0.125}, -- support
{0.125, -0.5, -0.125, 0.1875, 0.125, 0.125}, -- support
},
--selection = {-0.1875, -0.5, -0.25, 0.1875, 0.25, 0.25}, -- selection
tiles = 1,
},
{
node={
{-0.1875, -0.125, -0.25, 0.1875, 0.125, 0.25}, -- pulley
{-0.1875, -0.25, -0.125, 0.1875, 0.25, 0.125}, -- pulley
{-0.1875, -0.1875, -0.1875, 0.1875, 0.1875, 0.1875}, -- pulley_core
{-0.25, -0.5, -0.125, -0.1875, 0.125, 0.125}, -- support
{0.1875, -0.5, -0.125, 0.25, 0.125, 0.125}, -- support
},
--selection = {-0.1875, -0.5, -0.25, 0.1875, 0.25, 0.25}, -- selection
tiles = 2,
},
{
node={
{-0.25, -0.125, -0.25, 0.25, 0.125, 0.25}, -- pulley
{-0.25, -0.25, -0.125, 0.25, 0.25, 0.125}, -- pulley
{-0.25, -0.1875, -0.1875, 0.25, 0.1875, 0.1875}, -- pulley_core
{-0.3125, -0.5, -0.125, -0.25, 0.125, 0.125}, -- support
{0.25, -0.5, -0.125, 0.3125, 0.125, 0.125}, -- support
},
--selection = {-0.3125, -0.5, -0.25, 0.3125, 0.25, 0.25}, -- selection
tiles = 3,
},
{
node={
{-0.3125, -0.125, -0.25, 0.3125, 0.125, 0.25}, -- pulley
{-0.3125, -0.25, -0.125, 0.3125, 0.25, 0.125}, -- pulley
{-0.3125, -0.1875, -0.1875, 0.3125, 0.1875, 0.1875}, -- pulley_core
{-0.375, -0.5, -0.125, -0.3125, 0.125, 0.125}, -- support
{0.3125, -0.5, -0.125, 0.375, 0.125, 0.125}, -- support
},
--selection = {-0.375, -0.5, -0.25, 0.375, 0.25, 0.25}, -- selection
tiles = 4,
},
{
node={
{-0.375, -0.125, -0.25, 0.375, 0.125, 0.25}, -- pulley
{-0.375, -0.25, -0.125, 0.375, 0.25, 0.125}, -- pulley
{-0.375, -0.1875, -0.1875, 0.375, 0.1875, 0.1875}, -- pulley_core
{-0.4375, -0.5, -0.125, -0.375, 0.125, 0.125}, -- support
{0.375, -0.5, -0.125, 0.4375, 0.125, 0.125}, -- support
},
--selection = {-0.4375, -0.5, -0.25, 0.4375, 0.25, 0.25}, -- selection
tiles = 5,
},
{
node={
{-0.1875, -0.1875, -0.3125, 0.1875, 0.1875, 0.3125}, -- pulley
{-0.1875, -0.3125, -0.1875, 0.1875, 0.3125, 0.1875}, -- pulley
{-0.1875, -0.25, -0.25, 0.1875, 0.25, 0.25}, -- pulley_core
{-0.25, -0.5, -0.125, -0.1875, 0.125, 0.125}, -- support
{0.1875, -0.5, -0.125, 0.25, 0.125, 0.125}, -- support
},
--selection = {-0.1875, -0.5, -0.3125, 0.1875, 0.3125, 0.3125}, -- selection
tiles = 2,
},
{
node={
{-0.25, -0.1875, -0.3125, 0.25, 0.1875, 0.3125}, -- pulley
{-0.25, -0.3125, -0.1875, 0.25, 0.3125, 0.1875}, -- pulley
{-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, -- pulley_core
{-0.3125, -0.5, -0.125, -0.25, 0.125, 0.125}, -- support
{0.25, -0.5, -0.125, 0.3125, 0.125, 0.125}, -- support
},
--selection = {-0.3125, -0.5, -0.3125, 0.3125, 0.3125, 0.3125}, -- selection
tiles = 3,
},
{
node={
{-0.3125, -0.1875, -0.3125, 0.3125, 0.1875, 0.3125}, -- pulley
{-0.3125, -0.3125, -0.1875, 0.3125, 0.3125, 0.1875}, -- pulley
{-0.3125, -0.25, -0.25, 0.3125, 0.25, 0.25}, -- pulley_core
{-0.375, -0.5, -0.125, -0.3125, 0.125, 0.125}, -- support
{0.3125, -0.5, -0.125, 0.375, 0.125, 0.125}, -- support
},
--selection = {-0.375, -0.5, -0.3125, 0.375, 0.3125, 0.3125}, -- selection
tiles = 4,
},
{
node={
{-0.375, -0.1875, -0.3125, 0.375, 0.1875, 0.3125}, -- pulley
{-0.375, -0.3125, -0.1875, 0.375, 0.3125, 0.1875}, -- pulley
{-0.375, -0.25, -0.25, 0.375, 0.25, 0.25}, -- pulley_core
{-0.4375, -0.5, -0.125, -0.375, 0.125, 0.125}, -- support
{0.375, -0.5, -0.125, 0.4375, 0.125, 0.125}, -- support
},
--selection_bottom = {-0.4375, -0.5, -0.3125, 0.4375, 0.3125, 0.3125}, -- selection
tiles = 5,
}
}
local function register_rope_block(multiple, max_multiple, name_prefix, node_prefix, tint, flammable)
local node_name = string.format("ropes:%s%irope_block", node_prefix, multiple)
local rope_block_def = {
description = S("@1 Ropebox @2m", name_prefix, ropes.ropeLength*multiple),
_doc_items_create_entry = false,
drawtype="nodebox",
sunlight_propagates = true,
paramtype = "light",
paramtype2 = "wallmounted",
walkable = false,
climbable = true,
tiles = rope_box_tiles(rope_box_data[multiple].tiles, tint),
use_texture_alpha = "clip",
is_ground_content = false,
node_box = {
type = "fixed",
fixed = rope_box_data[multiple].node
},
selection_box = {type="regular"},
collision_box = {type="regular"},
groups = {attached_node = 1, choppy=2, oddly_breakable_by_hand=1, rope_block = 1},
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local target_node = minetest.get_node(pointed_thing.under)
local target_def = minetest.registered_nodes[target_node.name]
if target_def.walkable == false then
return itemstack
end
end
return minetest.item_place(itemstack, placer, pointed_thing)
end,
after_place_node = function(pos, placer)
local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
local placer_name = placer:get_player_name()
if minetest.is_protected(pos_below, placer_name) and not minetest.check_player_privs(placer, "protection_bypass") then
return
end
local node_below = minetest.get_node(pos_below)
if ropes.can_place_rope_in_node(node_below.name) then
minetest.add_node(pos_below, {name="ropes:rope_bottom"})
local meta = minetest.get_meta(pos_below)
meta:set_int("length_remaining", ropes.ropeLength*multiple)
meta:set_string("placer", placer:get_player_name())
end
end,
after_destruct = function(pos)
local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
ropes.destroy_rope(pos_below, {'ropes:rope', 'ropes:rope_bottom'})
end
}
-- If this number is higher than permitted, we still want to register the block (in case
-- some were already placed in-world) but we want to hide it from creative inventory
-- and if someone digs it we want to disintegrate it into its component parts to prevent
-- reuse.
if multiple > max_multiple then
rope_block_def.groups.not_in_creative_inventory = 1
rope_block_def.drop = string.format("ropes:%s1rope_block %i", node_prefix, multiple)
end
if flammable then
rope_block_def.groups.flammable = flammable
minetest.register_craft({
type = "fuel",
recipe = node_name,
burntime = ropes.rope_burn_time * multiple + ropes.wood_burn_time,
})
end
minetest.register_node(node_name, rope_block_def)
if (multiple ~= 1) then
-- Only register a recipe to craft this if it's within the permitted multiple range
if multiple <= max_multiple then
for i = 1, multiple-1 do
local rec = {string.format("ropes:%s%irope_block", node_prefix, i)}
for n = 1, multiple-i do
table.insert(rec, "ropes:ropesegment")
end
minetest.register_craft({
output = node_name,
type = "shapeless",
recipe = rec
})
end
end
-- Always allow players to disintegrate this into component parts, in case
-- there were some in inventory and the setting was changed.
minetest.register_craft({
output = "ropes:ropesegment",
type = "shapeless",
recipe = {
node_name
},
replacements = {
{node_name, string.format('ropes:%s%irope_block', node_prefix, multiple-1)},
},
})
end
if minetest.get_modpath("doc") then
doc.add_entry_alias("nodes", "ropes:rope", "nodes", node_name)
end
end
local rope_def = {
description = S("Rope"),
_doc_items_longdesc = ropes.doc.ropebox_longdesc,
_doc_items_usagehelp = ropes.doc.ropebox_usage,
walkable = false,
climbable = true,
sunlight_propagates = true,
paramtype = "light",
drop = "",
tiles = { "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_5.png", "ropes_5.png" },
use_texture_alpha = "clip",
is_ground_content = false,
groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
sounds = {
footstep = {name = "ropes_creak", gain = 0.8, max_hear_distance = 6},
dig = "__group",
dug = "__group",
},
drawtype = "nodebox",
node_box = {
type = "connected",
fixed = {-1/16, -1/2, -1/16, 1/16, 1/2, 1/16},
connect_top = {-1/16, 1/2, -1/16, 1/16, 3/4, 1/16}
},
connects_to = {"group:rope_block"},
connect_sides = {"top"},
selection_box = {
type = "fixed",
fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
},
after_destruct = function(pos)
ropes.hanging_after_destruct(pos, "ropes:rope_top", "ropes:rope", "ropes:rope_bottom")
end,
}
local rope_extension_timer = ropes.make_rope_on_timer("ropes:rope")
local rope_bottom_def = {
description = S("Rope"),
_doc_items_create_entry = false,
walkable = false,
climbable = true,
sunlight_propagates = true,
paramtype = "light",
drop = "",
tiles = { "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_5.png", "ropes_5.png" },
use_texture_alpha = "clip",
is_ground_content = false,
drawtype = "nodebox",
groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
sounds = {
footstep = {name = "ropes_creak", gain = 0.8, max_hear_distance = 6},
dig = "__group",
dug = "__group",
},
node_box = {
type = "connected",
fixed = {
{-1/16, -3/8, -1/16, 1/16, 1/2, 1/16},
{-2/16, -5/16, -2/16, 2/16, -1/16, 2/16},
},
connect_top = {-1/16, 1/2, -1/16, 1/16, 3/4, 1/16}
},
connects_to = {"group:rope_block"},
connect_sides = {"top"},
selection_box = {
type = "fixed",
fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
},
on_construct = function( pos )
local timer = minetest.get_node_timer( pos )
timer:start( 1 )
end,
on_timer = rope_extension_timer,
after_destruct = function(pos)
ropes.hanging_after_destruct(pos, "ropes:rope_top", "ropes:rope", "ropes:rope_bottom")
end,
}
minetest.register_node("ropes:rope", rope_def)
minetest.register_node("ropes:rope_bottom", rope_bottom_def)
if ropes.woodRopeBoxMaxMultiple > 0 or ropes.create_all_definitions then
if ropes.woodRopeBoxMaxMultiple > 0 then
minetest.register_craft({
output = "ropes:wood1rope_block",
recipe = {
{'group:wood'},
{'group:vines'}
}
})
end
for i = 1,9 do
if ropes.woodRopeBoxMaxMultiple >= i or ropes.create_all_definitions then
register_rope_block(i, ropes.woodRopeBoxMaxMultiple, S("Wood"), "wood", "#86683a", 2)
end
end
end
if ropes.copperRopeBoxMaxMultiple > 0 or ropes.create_all_definitions then
if ropes.copperRopeBoxMaxMultiple > 0 then
minetest.register_craft({
output = "ropes:copper1rope_block",
recipe = {
{'default:copper_ingot'},
{'group:vines'}
}
})
end
for i = 1,9 do
if ropes.copperRopeBoxMaxMultiple >= i or ropes.create_all_definitions then
register_rope_block(i, ropes.copperRopeBoxMaxMultiple, S("Copper"), "copper", "#c88648")
end
end
end
if ropes.steelRopeBoxMaxMultiple > 0 or ropes.create_all_definitions then
if ropes.steelRopeBoxMaxMultiple > 0 then
minetest.register_craft({
output = "ropes:steel1rope_block",
recipe = {
{'default:steel_ingot'},
{'group:vines'}
}
})
end
for i = 1,9 do
if ropes.steelRopeBoxMaxMultiple >= i or ropes.create_all_definitions then
register_rope_block(i, ropes.steelRopeBoxMaxMultiple, S("Steel"), "steel", "#ffffff")
end
end
end

193
mods/ropes/ropeladder.lua Normal file
View file

@ -0,0 +1,193 @@
if ropes.ropeLadderLength == 0 and not ropes.create_all_definitions then
return
end
local S = ropes.S
if ropes.ropeLadderLength > 0 then
minetest.register_craft({
output = "ropes:ropeladder_top",
recipe = {
{'','group:stick',''},
{'group:vines','group:stick','group:vines'},
{'','group:stick',''},
}
})
end
minetest.register_craft({
type = "fuel",
recipe = "ropes:ropeladder_top",
burntime = ropes.ladder_burn_time,
})
local rope_ladder_top_def = {
description = S("Rope Ladder"),
_doc_items_longdesc = ropes.doc.ropeladder_longdesc,
_doc_items_usagehelp = ropes.doc.ropeladder_usage,
drawtype = "signlike",
tiles = {"default_ladder_wood.png^ropes_ropeladder_top.png"},
use_texture_alpha = "clip",
is_ground_content = false,
inventory_image = "default_ladder_wood.png^ropes_ropeladder_top.png",
wield_image = "default_ladder_wood.png^ropes_ropeladder_top.png",
paramtype = "light",
paramtype2 = "wallmounted",
walkable = false,
climbable = true,
sunlight_propagates = true,
selection_box = {
type = "wallmounted",
--wall_top = = <default>
--wall_bottom = = <default>
--wall_side = = <default>
},
groups = { choppy=2, oddly_breakable_by_hand=1,flammable=2},
sounds = default.node_sound_wood_defaults(),
on_place = function(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local target_node = minetest.get_node(pointed_thing.under)
local target_def = minetest.registered_nodes[target_node.name]
if target_def.walkable == false then
return itemstack
end
end
return minetest.item_place(itemstack, placer, pointed_thing)
end,
after_place_node = function(pos, placer)
local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
local node_below = minetest.get_node(pos_below)
local this_node = minetest.get_node(pos)
local placer_name = placer:get_player_name()
-- param2 holds the facing direction of this node. If it's 0 or 1 the node is "flat" and we don't want the ladder to extend.
if ropes.can_place_rope_in_node(node_below.name) and this_node.param2 > 1
and (not minetest.is_protected(pos_below, placer_name)
or minetest.check_player_privs(placer_name, "protection_bypass")) then
minetest.add_node(pos_below, {name="ropes:ropeladder_bottom", param2=this_node.param2})
local meta = minetest.get_meta(pos_below)
meta:set_int("length_remaining", ropes.ropeLadderLength)
meta:set_string("placer", placer_name)
end
end,
after_destruct = function(pos)
local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
ropes.destroy_rope(pos_below, {"ropes:ropeladder", "ropes:ropeladder_bottom", "ropes:ropeladder_falling"})
end,
}
if ropes.ropeLadderLength == 0 then
rope_ladder_top_def.groups.not_in_creative_inventory = 1
end
minetest.register_node("ropes:ropeladder_top", rope_ladder_top_def)
minetest.register_node("ropes:ropeladder", {
description = S("Rope Ladder"),
_doc_items_create_entry = false,
drop = "",
drawtype = "signlike",
tiles = {"default_ladder_wood.png^ropes_ropeladder.png"},
use_texture_alpha = "clip",
is_ground_content = false,
inventory_image = "default_ladder_wood.png^ropes_ropeladder.png",
wield_image = "default_ladder_wood.png^ropes_ropeladder.png",
paramtype = "light",
paramtype2 = "wallmounted",
walkable = false,
climbable = true,
sunlight_propagates = true,
selection_box = {
type = "wallmounted",
--wall_top = = <default>
--wall_bottom = = <default>
--wall_side = = <default>
},
groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
sounds = default.node_sound_wood_defaults(),
after_destruct = function(pos)
ropes.hanging_after_destruct(pos, "ropes:ropeladder_falling", "ropes:ropeladder", "ropes:ropeladder_bottom")
end,
})
local ladder_extender = ropes.make_rope_on_timer("ropes:ropeladder")
minetest.register_node("ropes:ropeladder_bottom", {
description = S("Rope Ladder"),
_doc_items_create_entry = false,
drop = "",
drawtype = "signlike",
tiles = {"default_ladder_wood.png^ropes_ropeladder_bottom.png"},
use_texture_alpha = "clip",
is_ground_content = false,
inventory_image = "default_ladder_wood.png^ropes_ropeladder_bottom.png",
wield_image = "default_ladder_wood.png^ropes_ropeladder_bottom.png",
paramtype = "light",
paramtype2 = "wallmounted",
walkable = false,
climbable = true,
sunlight_propagates = true,
selection_box = {
type = "wallmounted",
--wall_top = = <default>
--wall_bottom = = <default>
--wall_side = = <default>
},
groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
sounds = default.node_sound_wood_defaults(),
on_construct = function( pos )
local timer = minetest.get_node_timer( pos )
timer:start( 1 )
end,
on_timer = ladder_extender,
after_destruct = function(pos)
ropes.hanging_after_destruct(pos, "ropes:ropeladder_falling", "ropes:ropeladder", "ropes:ropeladder_bottom")
end,
})
minetest.register_node("ropes:ropeladder_falling", {
description = S("Rope Ladder"),
_doc_items_create_entry = false,
drop = "",
drawtype = "signlike",
tiles = {"default_ladder_wood.png^ropes_ropeladder.png"},
use_texture_alpha = "clip",
is_ground_content = false,
inventory_image = "default_ladder_wood.png^ropes_ropeladder.png",
wield_image = "default_ladder_wood.png^ropes_ropeladder.png",
paramtype = "light",
paramtype2 = "wallmounted",
walkable = false,
climbable = true,
sunlight_propagates = true,
selection_box = {
type = "wallmounted",
--wall_top = = <default>
--wall_bottom = = <default>
--wall_side = = <default>
},
groups = {flammable=2, not_in_creative_inventory=1},
sounds = default.node_sound_wood_defaults(),
on_construct = function( pos )
local timer = minetest.get_node_timer( pos )
timer:start( 1 )
end,
on_timer = function( pos, elapsed )
local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
local node_below = minetest.get_node(pos_below)
if (node_below.name ~= "ignore") then
ropes.destroy_rope(pos_below, {'ropes:ropeladder', 'ropes:ropeladder_bottom', 'ropes:ropeladder_falling'})
minetest.swap_node(pos, {name="air"})
else
local timer = minetest.get_node_timer( pos )
timer:start( 1 )
end
end
})

BIN
mods/ropes/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

View file

@ -0,0 +1,58 @@
#The shortest rope will extend for this many meters. Longer ropes come in
#multiples of this length.
#Changing this value will not affect ropes that already exist in-world.
ropes_rope_length (Rope length) int 50 1 30000
#Rope ladders will extend this far at maximum.
#Changing this value will not affect rope ladders that already exist in-world.
#Setting this to 0 disables rope ladders.
ropes_rope_ladder_length (Rope ladder length) int 50 0 30000
#Sets the maximum length multiple wooden rope box permitted to be crafted.
#So for example if the rope length is set to 50 and this is set to 2,
#the longest possible wooden rope box a player can craft has 100 meters of rope.
#Allowed values run from 0 to 9. 0 disables wood rope boxes.
ropes_wood_rope_box_max_multiple (Maximum wood_rope box multiple) int 2 0 9
#Sets the maximum length multiple copper rope box permitted to be crafted.
#So for example if the rope length is set to 50 and this is set to 5,
#the longest possible copper rope box a player can craft has 250 meters of rope.
#Allowed values run from 0 to 9. 0 disables copper rope boxes.
ropes_copper_rope_box_max_multiple (Maximum copper rope box multiple) int 5 0 9
#Sets the maximum length multiple steel rope box permitted to be crafted.
#So for example if the rope length is set to 50 and this is set to 9,
#the longest possible steel rope box a player can craft has 450 meters of rope.
#Allowed values run from 0 to 9. 0 disables steel rope boxes.
ropes_steel_rope_box_max_multiple (Maximum steel rope box multiple) int 9 0 9
#If this is set to true, then the mod will generate definitions for the rope boxes
#that are otherwise not permitted by the settings above. These rope boxes
#will not be craftable and will not be available in the creative inventory,
#but they will continue to exist if they were placed "in world" and a player
#can deconstruct them to retrieve their rope components. This setting is
#intended for the situation where you have an established world and you want
#to reduce the number of rope boxes available to players without turning
#existing rope boxes into "unknown node"s.
ropes_create_all_definitions (Create all rope box definitions) bool false
#Extending ladders are capable of standing on their own, to a defined limit.
#A ladder can extend to its unsupported limit before needing another node
#behind it to provide a new point of support. Right-clicking on an existing
#ladder with a stack of ladders will add new ladder segments to its top.
ropes_extending_ladder_enabled (Enable extendable ladders) bool true
#If extending ladders are enabled, this setting will cause them to replace
#the default ladders entirely.
ropes_replace_default_ladders (Replace default ladders with extendable ladders) bool false
ropes_extending_wood_ladder_limit (Unsupported limit of wooden ladders) int 5
ropes_extending_steel_ladder_limit (Unsupported limit of steel ladders) int 15
#These nodes make it easier to build bridges by extending out away
#from the player as they're placed
ropes_bridges_enabled (Enable bridges) bool true
#Allows ropes and rope ladders to extend into all "airlike" nodes.
#Note that ropes will leave "air" nodes behind when destroyed.
ropes_can_extend_into_airlike (Ropes can extend into all nodes with drawtype airlike) bool false

View file

@ -0,0 +1 @@
ropes_creak.ogg - by jergonda from https://www.freesound.org/people/jergonda/sounds/254735/ under public domain via CC 0

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 426 B