├── LICENSE.md
├── README.md
├── bridge.lua
├── crafts.lua
├── depends.txt
├── description.txt
├── doc.lua
├── extendingladder.lua
├── functions.lua
├── i18n.py
├── init.lua
├── locale
├── ropes.es.tr
├── ropes.ru.tr
└── template.txt
├── loot.lua
├── mod.conf
├── ropeboxes.lua
├── ropeladder.lua
├── screenshot.png
├── settingtypes.txt
├── sounds
├── license.txt
├── ropes_creak.1.ogg
├── ropes_creak.2.ogg
└── ropes_creak.3.ogg
└── textures
├── ropes_1.png
├── ropes_2.png
├── ropes_3.png
├── ropes_4.png
├── ropes_5.png
├── ropes_item.png
├── ropes_ropebox_front_1.png
├── ropes_ropebox_front_2.png
├── ropes_ropebox_front_3.png
├── ropes_ropebox_front_4.png
├── ropes_ropebox_front_5.png
├── ropes_ropebox_side.png
├── ropes_ropeladder.png
├── ropes_ropeladder_bottom.png
├── ropes_ropeladder_overlay.png
└── ropes_ropeladder_top.png
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ## MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Ropes
2 |
3 | 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.
4 |
5 | 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.
6 |
7 | 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.
8 |
9 | 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.
10 |
11 | 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.
12 |
13 | In-game documentation is provided via an optional dependency on the doc mod.
14 |
15 | ## Interaction with other mods
16 |
17 | 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.
--------------------------------------------------------------------------------
/bridge.lua:
--------------------------------------------------------------------------------
1 | local S = ropes.S
2 |
3 | if ropes.bridges_enabled then
4 |
5 | local bridge_on_place = function(itemstack, placer, pointed_thing)
6 | -- Shall place item and return the leftover itemstack.
7 | -- The placer may be any ObjectRef or nil.
8 | -- default: minetest.item_place
9 | if placer == nil then
10 | return minetest.item_place(itemstack, placer, pointed_thing)
11 | end
12 |
13 | local above = pointed_thing.above
14 | local under = pointed_thing.under
15 |
16 | if above.x == under.x and above.z == under.z and above.y > under.y then
17 | -- we're aimed downward at a buildable node from above.
18 | -- determine the direction the placer lies relative to this node.
19 | local new_under = vector.new(under)
20 | local placer_pos = placer:get_pos()
21 | local diff_x = placer_pos.x - under.x
22 | local diff_z = placer_pos.z - under.z
23 | if math.abs(diff_x) > math.abs(diff_z) then
24 | -- placer is displaced along the X axis relative to the target
25 | if diff_x > 0 then
26 | new_under.x = under.x - 1
27 | else
28 | new_under.x = under.x + 1
29 | end
30 | else
31 | -- placer is displaced along the Z axis relative to the target
32 | if diff_z > 0 then
33 | new_under.z = under.z - 1
34 | else
35 | new_under.z = under.z + 1
36 | end
37 | end
38 | if minetest.registered_nodes[minetest.get_node(new_under).name].buildable_to then
39 | local new_pointed_thing = {type="node", under=new_under, above={x=new_under.x, y=new_under.y+1, z=new_under.z}}
40 | return minetest.item_place(itemstack, placer, new_pointed_thing)
41 | end
42 | end
43 |
44 | return minetest.item_place(itemstack, placer, pointed_thing)
45 | end
46 |
47 | minetest.register_node("ropes:wood_bridge", {
48 | description = S("Wooden Bridge"),
49 | _doc_items_longdesc = ropes.doc.wooden_bridge_longdesc,
50 | _doc_items_usagehelp = ropes.doc.wooden_bridge_usagehelp,
51 | tiles = {
52 | "default_wood.png", "default_wood.png",
53 | "default_wood.png^[transformR270", "default_wood.png^[transformR90",
54 | "default_wood.png^[transformR270", "default_wood.png^[transformR90",
55 | },
56 | drawtype = "nodebox",
57 | paramtype = "light",
58 | paramtype2 = "facedir",
59 | is_ground_content = false,
60 | groups = {choppy = 2, flammable = 2, oddly_breakable_by_hand = 1, flow_through = 1, fence = 1, wall = 1},
61 | sounds = default.node_sound_wood_defaults(),
62 | node_box = {
63 | type = "fixed",
64 | fixed = {
65 | {-0.5, 0.375, -0.5, 0.5, 0.5, 0.5}, -- Platform
66 | {-0.375, -0.5, -0.5, 0.375, -0.375, -0.4375}, -- x beam4
67 | {-0.375, -0.5, 0.4375, 0.375, -0.375, 0.5}, -- x beam3
68 | {0.375, -0.5, -0.4375, 0.5, -0.375, 0.4375}, -- z beam2
69 | {-0.5, -0.5, -0.4375, -0.375, -0.375, 0.4375}, -- z beam1
70 | {0.375, -0.5, -0.5, 0.5, 0.375, -0.4375}, -- upright4
71 | {0.375, -0.5, 0.4375, 0.5, 0.375, 0.5}, -- upright3
72 | {-0.5, -0.5, -0.5, -0.375, 0.375, -0.4375}, -- upright2
73 | {-0.5, -0.5, 0.4375, -0.375, 0.375, 0.5}, -- upright1
74 | }
75 | },
76 | on_place = bridge_on_place,
77 | })
78 |
79 | minetest.register_craft({
80 | output = "ropes:wood_bridge 5",
81 | recipe = {
82 | {"group:stick", "stairs:slab_wood", "group:stick"},
83 | {"group:stick", "", "group:stick"},
84 | {"group:stick", "group:stick", "group:stick"},
85 | }
86 | })
87 |
88 | end
89 |
--------------------------------------------------------------------------------
/crafts.lua:
--------------------------------------------------------------------------------
1 | local S = ropes.S
2 |
3 | if minetest.get_modpath("farming") then
4 | -- this doesn't work reliably due to side effects of https://github.com/minetest/minetest/issues/5518
5 | -- local old_def = minetest.registered_craftitems["farming:cotton"]
6 | -- if old_def then
7 | -- old_def.groups["thread"] = 1
8 | -- minetest.override_item("farming:cotton", {
9 | -- groups = old_def.groups
10 | -- })
11 | -- end
12 | minetest.register_craft({
13 | output = 'ropes:ropesegment',
14 | recipe = {
15 | {'farming:cotton','farming:cotton'},
16 | {'farming:cotton','farming:cotton'},
17 | {'farming:cotton','farming:cotton'},
18 | }
19 | })
20 |
21 | if farming.mod == "redo" or farming.mod == "undo" then
22 | minetest.register_craft({
23 | output = 'ropes:ropesegment',
24 | recipe = {
25 | {'farming:hemp_rope'},
26 | {'farming:hemp_rope'},
27 | }
28 | })
29 | end
30 | end
31 |
32 | if minetest.get_modpath("hemp") then
33 | minetest.register_craft({
34 | output = 'ropes:ropesegment',
35 | recipe = {
36 | {'hemp:hemp_rope'},
37 | {'hemp:hemp_rope'},
38 | }
39 | })
40 | end
41 |
42 | if minetest.get_modpath("cottages") then
43 | minetest.register_craft({
44 | output = 'ropes:ropesegment',
45 | recipe = {
46 | {'cottages:rope'},
47 | {'cottages:rope'},
48 | }
49 | })
50 | end
51 |
52 | if minetest.get_modpath("moreblocks") then
53 | minetest.register_craft({
54 | output = 'ropes:ropesegment',
55 | recipe = {
56 | {'moreblocks:rope','moreblocks:rope'},
57 | {'moreblocks:rope','moreblocks:rope'},
58 | {'moreblocks:rope','moreblocks:rope'},
59 | }
60 | })
61 | end
62 |
63 | minetest.register_craft({
64 | output = 'ropes:ropesegment',
65 | recipe = {
66 | {'group:thread','group:thread'},
67 | {'group:thread','group:thread'},
68 | {'group:thread','group:thread'},
69 | }
70 | })
71 |
72 | minetest.register_craftitem("ropes:ropesegment", {
73 | description = S("Rope Segment"),
74 | _doc_items_longdesc = ropes.doc.ropesegment_longdesc,
75 | _doc_items_usagehelp = ropes.doc.ropesegment_usage,
76 | groups = {vines = 1},
77 | inventory_image = "ropes_item.png",
78 | })
79 |
80 | local cotton_burn_time = 1
81 | ropes.wood_burn_time = minetest.get_craft_result({method="fuel", width=1, items={ItemStack("default:wood")}}).time
82 | ropes.rope_burn_time = cotton_burn_time * 6
83 | local stick_burn_time = minetest.get_craft_result({method="fuel", width=1, items={ItemStack("default:stick")}}).time
84 | ropes.ladder_burn_time = ropes.rope_burn_time * 2 + stick_burn_time * 3
85 |
86 | minetest.register_craft({
87 | type = "fuel",
88 | recipe = "ropes:ropesegment",
89 | burntime = ropes.rope_burn_time,
90 | })
--------------------------------------------------------------------------------
/depends.txt:
--------------------------------------------------------------------------------
1 | default
2 | farming?
3 | vines?
4 | doc?
5 | loot?
6 | hemp?
7 | cottages?
8 |
--------------------------------------------------------------------------------
/description.txt:
--------------------------------------------------------------------------------
1 | Adds rope boxes of various lengths and also rope ladders.
--------------------------------------------------------------------------------
/doc.lua:
--------------------------------------------------------------------------------
1 | ropes.doc = {}
2 |
3 | if not minetest.get_modpath("doc") then
4 | return
5 | end
6 |
7 | local S = ropes.S
8 |
9 | ropes.doc.ropesegment_longdesc = S("Rope segments are bundles of fibre twisted into robust cables.")
10 | 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.")
11 |
12 | ropes.doc.ropeladder_longdesc = S("A hanging rope ladder that automatically extends downward.")
13 | 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)
14 |
15 | 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"
16 |
17 | if ropes.woodRopeBoxMaxMultiple == 1 then
18 | rope_length_doc = rope_length_doc .. "\n" .. S("Wood") .. " " .. S("rope boxes can hold @1m of rope.", ropes.ropeLength)
19 | elseif ropes.woodRopeBoxMaxMultiple > 1 then
20 | 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)
21 | end
22 |
23 | if ropes.copperRopeBoxMaxMultiple == 1 then
24 | rope_length_doc = rope_length_doc .. "\n" .. S("Copper") .. " " .. S("rope boxes can hold @1m of rope.", ropes.ropeLength)
25 | elseif ropes.copperRopeBoxMaxMultiple > 1 then
26 | 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)
27 | end
28 |
29 | if ropes.steelRopeBoxMaxMultiple == 1 then
30 | rope_length_doc = rope_length_doc .. "\n" .. S("Steel") .. " " .. S("rope boxes can hold @1m of rope.", ropes.ropeLength)
31 | elseif ropes.steelRopeBoxMaxMultiple > 1 then
32 | 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)
33 | end
34 |
35 | 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.")
36 | ropes.doc.ropebox_usage = rope_length_doc .. "\n\n" ..
37 | 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" ..
38 | 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.")
39 |
40 | if ropes.extending_ladder_enabled then
41 | ropes.doc.ladder_longdesc = S("A ladder for climbing. It can reach greater heights when placed against a supporting block.")
42 | 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.")
43 | end
44 |
45 | ropes.doc.wooden_bridge_longdesc = S("A wooden platform with support struts useful for bridging gaps.")
46 | 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.")
47 |
48 | doc.add_entry_alias("nodes", "ropes:ropeladder_top", "nodes", "ropes:ropeladder")
49 | doc.add_entry_alias("nodes", "ropes:ropeladder_top", "nodes", "ropes:ropeladder_bottom")
50 | doc.add_entry_alias("nodes", "ropes:ropeladder_top", "nodes", "ropes:ropeladder_falling")
51 |
52 | doc.add_entry_alias("nodes", "ropes:rope", "nodes", "ropes:rope_bottom")
53 | doc.add_entry_alias("nodes", "ropes:rope", "nodes", "ropes:rope_top")
54 |
--------------------------------------------------------------------------------
/extendingladder.lua:
--------------------------------------------------------------------------------
1 | local S = ropes.S
2 |
3 | if ropes.extending_ladder_enabled then
4 |
5 | local wood_recipe = {
6 | {"group:stick", "", "group:stick"},
7 | {"group:stick", "", "group:stick"},
8 | {"group:stick", "group:stick", "group:stick"},
9 | }
10 | local wood_name = S("Wooden Extendable Ladder")
11 |
12 | local steel_recipe = {
13 | {"default:steel_ingot", "", "default:steel_ingot"},
14 | {"default:steel_ingot", "", "default:steel_ingot"},
15 | {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
16 | }
17 | local steel_name = S("Steel Extendable Ladder")
18 |
19 | -- Overlay texture: by 1F616EMO, CC0
20 | local texture_overlay = "^ropes_ropeladder_overlay.png"
21 |
22 | if ropes.replace_default_ladders then
23 |
24 | minetest.unregister_item("default:ladder_wood")
25 | minetest.unregister_item("default:ladder_steel")
26 | minetest.clear_craft({output = "default:ladder_wood"})
27 | minetest.clear_craft({output = "default:ladder_steel"})
28 |
29 | local wallmounted_to_facedir =
30 | {[0] = 15, -- ceiling
31 | [1] = 13, -- floor
32 | [2] = 1, -- +X
33 | [3] = 3, -- -X
34 | [4] = 0, -- +Z
35 | [5] = 2, -- -Z
36 | }
37 |
38 | minetest.register_lbm({
39 | label = "Switch from wallmounted default ladders to rope mod extending ladders",
40 | name = "ropes:wallmounted_ladder_to_facedir_ladder",
41 | nodenames = {"default:ladder_wood", "default:ladder_steel"},
42 | run_at_every_load = false,
43 | action = function(pos, node)
44 | local new_node = {param2 = wallmounted_to_facedir[node.param2]}
45 | if (node.name == "default:ladder_wood") then
46 | new_node.name = "ropes:ladder_wood"
47 | else
48 | new_node.name = "ropes:ladder_steel"
49 | end
50 | minetest.set_node(pos, new_node)
51 | end,
52 | })
53 |
54 | wood_recipe = {
55 | {"group:stick", "", "group:stick"},
56 | {"group:stick", "group:stick", "group:stick"},
57 | {"group:stick", "", "group:stick"},
58 | }
59 | wood_name = S("Wooden Ladder")
60 |
61 | steel_recipe = {
62 | {'default:steel_ingot', '', 'default:steel_ingot'},
63 | {'default:steel_ingot', 'default:steel_ingot', 'default:steel_ingot'},
64 | {'default:steel_ingot', '', 'default:steel_ingot'},
65 | }
66 | steel_name = S("Steel Ladder")
67 |
68 | texture_overlay = ""
69 |
70 | else
71 | -- Swap between normal and extendable ladders
72 | minetest.register_craft({
73 | type = "shapeless",
74 | output = "ropes:ladder_wood",
75 | recipe = {"default:ladder_wood"},
76 | })
77 |
78 | minetest.register_craft({
79 | type = "shapeless",
80 | output = "ropes:ladder_steel",
81 | recipe = {"default:ladder_steel"},
82 | })
83 | end
84 |
85 | minetest.register_craft({
86 | output = "ropes:ladder_wood 5",
87 | recipe = wood_recipe,
88 | })
89 |
90 | minetest.register_craft({
91 | output = 'ropes:ladder_steel 15',
92 | recipe = steel_recipe,
93 | })
94 |
95 | local ladder_extender = function(pos, node, clicker, itemstack, pointed_thing, ladder_node, standing_limit)
96 | -- on_rightclick can be called by other mods, make sure we have all the parameters we need
97 | if pointed_thing == nil or itemstack == nil then
98 | return itemstack
99 | end
100 |
101 | local clicked_stack = ItemStack(itemstack)
102 |
103 | -- true if we're pointing up at the ladder from below and there's a buildable space below it
104 | -- this check allows us to extend ladders downward
105 | local pointing_directly_below =
106 | pointed_thing.above.x == pos.x and
107 | pointed_thing.above.z == pos.z and
108 | pointed_thing.above.y == pos.y - 1 and
109 | minetest.registered_nodes[minetest.get_node(pointed_thing.above).name].buildable_to
110 |
111 | if clicked_stack:get_name() == ladder_node and not pointing_directly_below then
112 | local param2 = minetest.get_node(pos).param2
113 | local dir = minetest.facedir_to_dir(param2)
114 | local scan_limit = pos.y + 6 -- Only add ladder segments up to five nodes above the one clicked on
115 | pos.y = pos.y + 1
116 | while pos.y < scan_limit and minetest.get_node(pos).name == ladder_node do
117 | param2 = minetest.get_node(pos).param2
118 | pos.y = pos.y + 1
119 | end
120 | if pos.y < scan_limit and minetest.registered_nodes[minetest.get_node(pos).name].buildable_to then
121 |
122 | -- scan downward behind the ladder to find support
123 | local behind_pos = vector.add(pos, minetest.facedir_to_dir(param2))
124 | local target_height = pos.y - standing_limit - 1
125 | while behind_pos.y > target_height and minetest.registered_nodes[minetest.get_node(behind_pos).name].buildable_to do
126 | behind_pos.y = behind_pos.y - 1
127 | end
128 |
129 | -- If there's enough support, build a new ladder segment
130 | if behind_pos.y > target_height then
131 | if minetest.is_protected(pos, clicker:get_player_name()) then
132 | minetest.record_protection_violation(pos, clicker:get_player_name())
133 | else
134 | minetest.set_node(pos, {name=ladder_node, param2=param2})
135 | if not minetest.settings:get_bool("creative_mode") then
136 | clicked_stack:take_item(1)
137 | end
138 | end
139 | end
140 | end
141 | elseif clicked_stack:get_definition().type == "node" then
142 | return minetest.item_place_node(itemstack, clicker, pointed_thing)
143 | end
144 | return clicked_stack
145 | end
146 |
147 | minetest.register_node("ropes:ladder_wood", {
148 | description = wood_name,
149 | _doc_items_longdesc = ropes.doc.ladder_longdesc,
150 | _doc_items_usagehelp = ropes.doc.ladder_usagehelp,
151 | tiles = {"default_wood.png","default_wood.png","default_wood.png^[transformR270","default_wood.png^[transformR270","default_ladder_wood.png"},
152 | use_texture_alpha = "clip",
153 | inventory_image = "default_ladder_wood.png" .. texture_overlay,
154 | wield_image = "default_ladder_wood.png",
155 | paramtype = "light",
156 | paramtype2 = "facedir",
157 | sunlight_propagates = true,
158 | walkable = false,
159 | climbable = true,
160 | is_ground_content = false,
161 | drawtype = "nodebox",
162 | paramtype = "light",
163 | node_box = {
164 | type = "fixed",
165 | fixed = {
166 | {-0.375, -0.5, 0.375, -0.25, 0.5, 0.5}, -- Upright1
167 | {0.25, -0.5, 0.375, 0.375, 0.5, 0.5}, -- Upright2
168 | {-0.4375, 0.3125, 0.4375, 0.4375, 0.4375, 0.5}, -- Rung_4
169 | {-0.4375, -0.1875, 0.4375, 0.4375, -0.0625, 0.5}, -- Rung_2
170 | {-0.4375, -0.4375, 0.4375, 0.4375, -0.3125, 0.5}, -- Rung_1
171 | {-0.4375, 0.0625, 0.4375, 0.4375, 0.1875, 0.5}, -- Rung_3
172 | }
173 | },
174 | groups = {choppy = 2, oddly_breakable_by_hand = 3, flammable = 2, flow_through = 1},
175 | sounds = default.node_sound_wood_defaults(),
176 | on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
177 | return ladder_extender(pos, node, clicker, itemstack, pointed_thing, "ropes:ladder_wood", ropes.extending_wood_ladder_limit)
178 | end,
179 | })
180 |
181 | minetest.register_node("ropes:ladder_steel", {
182 | description = steel_name,
183 | _doc_items_longdesc = ropes.doc.ladder_longdesc,
184 | _doc_items_usagehelp = ropes.doc.ladder_usagehelp,
185 | tiles = {"default_steel_block.png","default_steel_block.png","default_steel_block.png","default_steel_block.png","default_ladder_steel.png"},
186 | use_texture_alpha = "clip",
187 | inventory_image = "default_ladder_steel.png" .. texture_overlay,
188 | wield_image = "default_ladder_steel.png",
189 | paramtype = "light",
190 | paramtype2 = "facedir",
191 | sunlight_propagates = true,
192 | walkable = false,
193 | climbable = true,
194 | is_ground_content = false,
195 | drawtype = "nodebox",
196 | node_box = {
197 | type = "fixed",
198 | fixed = {
199 | {-0.4375, -0.5, 0.3125, -0.25, 0.5, 0.5}, -- Upright1
200 | {0.25, -0.5, 0.3125, 0.4375, 0.5, 0.5}, -- Upright2
201 | {-0.25, 0.3125, 0.375, 0.25, 0.4375, 0.5}, -- Rung_4
202 | {-0.25, -0.1875, 0.375, 0.25, -0.0625, 0.5}, -- Rung_2
203 | {-0.25, -0.4375, 0.375, 0.25, -0.3125, 0.5}, -- Rung_1
204 | {-0.25, 0.0625, 0.375, 0.25, 0.1875, 0.5}, -- Rung_3
205 | }
206 | },
207 | groups = {cracky = 2, flow_through = 1},
208 | sounds = default.node_sound_metal_defaults(),
209 | on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
210 | return ladder_extender(pos, node, clicker, itemstack, pointed_thing, "ropes:ladder_steel", ropes.extending_steel_ladder_limit)
211 | end,
212 | })
213 |
214 | else
215 |
216 | -- Table of possible wallmounted values
217 | local facedir_to_wallmounted = {
218 | 4, -- +Z
219 | 2, -- +X
220 | 5, -- -Z
221 | 3, -- -X
222 | 1, -- -Y
223 | 0, -- +Y
224 | }
225 | -- Mapping from facedir value to index in facedir_to_dir.
226 | local facedir_to_wallmounted_map = {
227 | [0]=1, 2, 3, 4,
228 | 5, 2, 6, 4,
229 | 6, 2, 5, 4,
230 | 1, 5, 3, 6,
231 | 1, 6, 3, 5,
232 | 1, 4, 3, 2,
233 | }
234 |
235 | minetest.register_lbm({
236 | label = "Switch from ropes ladders to wallmounted default ladders",
237 | name = "ropes:facedir_ladder_to_wallmounted_ladder",
238 | nodenames = {"ropes:ladder_wood", "ropes:ladder_steel"},
239 | run_at_every_load = false,
240 | action = function(pos, node)
241 | local new_node = {param2 = facedir_to_wallmounted[facedir_to_wallmounted_map[node.param2 % 32]]}
242 | if (node.name == "ropes:ladder_wood") then
243 | new_node.name = "default:ladder_wood"
244 | else
245 | new_node.name = "default:ladder_steel"
246 | end
247 | minetest.set_node(pos, new_node)
248 | end,
249 | })
250 |
251 | end
252 |
--------------------------------------------------------------------------------
/functions.lua:
--------------------------------------------------------------------------------
1 | ropes.can_place_rope_in_node = function(target_node_name)
2 | if ropes.can_extend_into_nodes[target_node_name] == true then
3 | return true
4 | end
5 | local target_def = minetest.registered_nodes[target_node_name]
6 | if target_def then
7 | if target_def.drawtype == "airlike" and ropes.can_extend_into_airlike then
8 | return true
9 | end
10 | if target_def.groups and target_def.groups.ropes_can_extend_into then
11 | return true
12 | end
13 | end
14 | return false
15 | end
16 |
17 | ropes.make_rope_on_timer = function(rope_node_name)
18 | return function(pos, elapsed)
19 | local currentend = minetest.get_node(pos)
20 | local currentmeta = minetest.get_meta(pos)
21 | local currentlength = currentmeta:get_int("length_remaining")
22 | local placer_name = currentmeta:get_string("placer")
23 | local newpos = {x=pos.x, y=pos.y-1, z=pos.z}
24 | local newnode = minetest.get_node(newpos)
25 | local oldnode = minetest.get_node(pos)
26 | if currentlength > 1 and (not minetest.is_protected(newpos, placer_name)
27 | or minetest.check_player_privs(placer_name, "protection_bypass")) then
28 | if ropes.can_place_rope_in_node(newnode.name) then
29 | minetest.add_node(newpos, {name=currentend.name, param2=oldnode.param2})
30 | local newmeta = minetest.get_meta(newpos)
31 | newmeta:set_int("length_remaining", currentlength-1)
32 | newmeta:set_string("placer", placer_name)
33 | minetest.set_node(pos, {name=rope_node_name, param2=oldnode.param2})
34 | ropes.move_players_down(pos, 1)
35 | else
36 | local timer = minetest.get_node_timer( pos )
37 | timer:start( 1 )
38 | end
39 | end
40 | end
41 | end
42 |
43 | local data = {}
44 | local c_air = minetest.get_content_id("air")
45 |
46 | ropes.destroy_rope = function(pos, nodes)
47 | local top = pos.y
48 | local bottom = pos.y-15
49 | local voxel_manip = minetest.get_voxel_manip()
50 |
51 | local finished = false
52 | local ids_to_destroy = {}
53 | for _, node in pairs(nodes) do
54 | if minetest.registered_nodes[node] then
55 | ids_to_destroy[minetest.get_content_id(node)] = true
56 | end
57 | end
58 |
59 | while not finished do
60 | local emin, emax = voxel_manip:read_from_map({x=pos.x, y=bottom, z=pos.z}, {x=pos.x, y=top, z=pos.z})
61 | voxel_manip:get_data(data)
62 | local voxel_area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
63 | bottom = emin.y
64 | for y = top, bottom, -1 do
65 | local index = voxel_area:index(pos.x, y, pos.z)
66 | if ids_to_destroy[data[index]] then
67 | data[index] = c_air
68 | else
69 | finished = true
70 | break
71 | end
72 | end
73 | voxel_manip:set_data(data)
74 | voxel_manip:write_to_map()
75 | voxel_manip:update_map()
76 | top = bottom - 1
77 | bottom = bottom - 15
78 | end
79 | end
80 |
81 |
82 | ropes.hanging_after_destruct = function(pos, top_node, middle_node, bottom_node)
83 | local node = minetest.get_node(pos)
84 | if node.name == top_node or node.name == middle_node or node.name == bottom_node then
85 | return -- this was done by another ladder or rope node changing this one, don't react
86 | end
87 |
88 | pos.y = pos.y + 1 -- one up
89 | local node_above = minetest.get_node(pos)
90 | if node_above.name == middle_node then
91 | minetest.swap_node(pos, {name=bottom_node, param2=node_above.param2})
92 | end
93 |
94 | pos.y = pos.y - 2 -- one down
95 | local node_below = minetest.get_node(pos)
96 | if node_below.name == middle_node then
97 | ropes.destroy_rope(pos, {middle_node, bottom_node})
98 | elseif node_below.name == bottom_node then
99 | minetest.swap_node(pos, {name="air"})
100 | end
101 | end
102 |
103 | ropes.move_players_down = function(pos, radius)
104 | local all_objects = minetest.get_objects_inside_radius({x=pos.x, y=pos.y+radius, z=pos.z}, radius)
105 | local players = {}
106 | local _,obj
107 | for _,obj in pairs(all_objects) do
108 | if obj:is_player() then
109 | local obj_pos = obj:get_pos()
110 | if math.abs(obj_pos.x-pos.x) < 0.5 and math.abs(obj_pos.z-pos.z) < 0.5 then
111 | obj:set_pos({x=obj_pos.x, y=obj_pos.y-1, z=obj_pos.z}, true)
112 | end
113 | end
114 | end
115 | end
116 |
--------------------------------------------------------------------------------
/i18n.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | #
4 | # Script to generate the template file and update the translation files.
5 | # Copy the script into the mod or modpack root folder and run it there.
6 | #
7 | # Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
8 | # LGPLv2.1+
9 |
10 | from __future__ import print_function
11 | import os, fnmatch, re, shutil, errno
12 | from sys import argv as _argv
13 |
14 | # Running params
15 | params = {"recursive": False,
16 | "help": False,
17 | "mods": False,
18 | "verbose": False,
19 | "folders": []
20 | }
21 | # Available CLI options
22 | options = {"recursive": ['--recursive', '-r'],
23 | "help": ['--help', '-h'],
24 | "mods": ['--installed-mods'],
25 | "verbose": ['--verbose', '-v']
26 | }
27 |
28 | # Strings longer than this will have extra space added between
29 | # them in the translation files to make it easier to distinguish their
30 | # beginnings and endings at a glance
31 | doublespace_threshold = 60
32 |
33 | def set_params_folders(tab: list):
34 | '''Initialize params["folders"] from CLI arguments.'''
35 | # Discarding argument 0 (tool name)
36 | for param in tab[1:]:
37 | stop_param = False
38 | for option in options:
39 | if param in options[option]:
40 | stop_param = True
41 | break
42 | if not stop_param:
43 | params["folders"].append(os.path.abspath(param))
44 |
45 | def set_params(tab: list):
46 | '''Initialize params from CLI arguments.'''
47 | for option in options:
48 | for option_name in options[option]:
49 | if option_name in tab:
50 | params[option] = True
51 | break
52 |
53 | def print_help(name):
54 | '''Prints some help message.'''
55 | print(f'''SYNOPSIS
56 | {name} [OPTIONS] [PATHS...]
57 | DESCRIPTION
58 | {', '.join(options["help"])}
59 | prints this help message
60 | {', '.join(options["recursive"])}
61 | run on all subfolders of paths given
62 | {', '.join(options["mods"])}
63 | run on locally installed modules
64 | {', '.join(options["verbose"])}
65 | add output information
66 | ''')
67 |
68 |
69 | def main():
70 | '''Main function'''
71 | set_params(_argv)
72 | set_params_folders(_argv)
73 | if params["help"]:
74 | print_help(_argv[0])
75 | elif params["recursive"] and params["mods"]:
76 | print("Option --installed-mods is incompatible with --recursive")
77 | else:
78 | # Add recursivity message
79 | print("Running ", end='')
80 | if params["recursive"]:
81 | print("recursively ", end='')
82 | # Running
83 | if params["mods"]:
84 | print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
85 | run_all_subfolders("~/.minetest/mods")
86 | elif len(params["folders"]) >= 2:
87 | print("on folder list:", params["folders"])
88 | for f in params["folders"]:
89 | if params["recursive"]:
90 | run_all_subfolders(f)
91 | else:
92 | update_folder(f)
93 | elif len(params["folders"]) == 1:
94 | print("on folder", params["folders"][0])
95 | if params["recursive"]:
96 | run_all_subfolders(params["folders"][0])
97 | else:
98 | update_folder(params["folders"][0])
99 | else:
100 | print("on folder", os.path.abspath("./"))
101 | if params["recursive"]:
102 | run_all_subfolders(os.path.abspath("./"))
103 | else:
104 | update_folder(os.path.abspath("./"))
105 |
106 | #group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
107 | #See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
108 | pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
109 | pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
110 |
111 | # Handles "concatenation" .. " of strings"
112 | pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
113 |
114 | pattern_tr = re.compile(r'(.+?[^@])=(.*)')
115 | pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
116 | pattern_tr_filename = re.compile(r'\.tr$')
117 | pattern_po_language_code = re.compile(r'(.*)\.po$')
118 |
119 | #attempt to read the mod's name from the mod.conf file. Returns None on failure
120 | def get_modname(folder):
121 | try:
122 | with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
123 | for line in mod_conf:
124 | match = pattern_name.match(line)
125 | if match:
126 | return match.group(1)
127 | except FileNotFoundError:
128 | pass
129 | return None
130 |
131 | #If there are already .tr files in /locale, returns a list of their names
132 | def get_existing_tr_files(folder):
133 | out = []
134 | for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
135 | for name in files:
136 | if pattern_tr_filename.search(name):
137 | out.append(name)
138 | return out
139 |
140 | # A series of search and replaces that massage a .po file's contents into
141 | # a .tr file's equivalent
142 | def process_po_file(text):
143 | # The first three items are for unused matches
144 | text = re.sub(r'#~ msgid "', "", text)
145 | text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
146 | text = re.sub(r'"\n#~ msgstr "', "=", text)
147 | # comment lines
148 | text = re.sub(r'#.*\n', "", text)
149 | # converting msg pairs into "=" pairs
150 | text = re.sub(r'msgid "', "", text)
151 | text = re.sub(r'"\nmsgstr ""\n"', "=", text)
152 | text = re.sub(r'"\nmsgstr "', "=", text)
153 | # various line breaks and escape codes
154 | text = re.sub(r'"\n"', "", text)
155 | text = re.sub(r'"\n', "\n", text)
156 | text = re.sub(r'\\"', '"', text)
157 | text = re.sub(r'\\n', '@n', text)
158 | # remove header text
159 | text = re.sub(r'=Project-Id-Version:.*\n', "", text)
160 | # remove double-spaced lines
161 | text = re.sub(r'\n\n', '\n', text)
162 | return text
163 |
164 | # Go through existing .po files and, if a .tr file for that language
165 | # *doesn't* exist, convert it and create it.
166 | # The .tr file that results will subsequently be reprocessed so
167 | # any "no longer used" strings will be preserved.
168 | # Note that "fuzzy" tags will be lost in this process.
169 | def process_po_files(folder, modname):
170 | for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
171 | for name in files:
172 | code_match = pattern_po_language_code.match(name)
173 | if code_match == None:
174 | continue
175 | language_code = code_match.group(1)
176 | tr_name = modname + "." + language_code + ".tr"
177 | tr_file = os.path.join(root, tr_name)
178 | if os.path.exists(tr_file):
179 | if params["verbose"]:
180 | print(f"{tr_name} already exists, ignoring {name}")
181 | continue
182 | fname = os.path.join(root, name)
183 | with open(fname, "r", encoding='utf-8') as po_file:
184 | if params["verbose"]:
185 | print(f"Importing translations from {name}")
186 | text = process_po_file(po_file.read())
187 | with open(tr_file, "wt", encoding='utf-8') as tr_out:
188 | tr_out.write(text)
189 |
190 | # from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
191 | # Creates a directory if it doesn't exist, silently does
192 | # nothing if it already exists
193 | def mkdir_p(path):
194 | try:
195 | os.makedirs(path)
196 | except OSError as exc: # Python >2.5
197 | if exc.errno == errno.EEXIST and os.path.isdir(path):
198 | pass
199 | else: raise
200 |
201 | # Converts the template dictionary to a text to be written as a file
202 | # dKeyStrings is a dictionary of localized string to source file sets
203 | # dOld is a dictionary of existing translations and comments from
204 | # the previous version of this text
205 | def strings_to_text(dkeyStrings, dOld, mod_name):
206 | lOut = [f"# textdomain: {mod_name}\n"]
207 |
208 | dGroupedBySource = {}
209 |
210 | for key in dkeyStrings:
211 | sourceList = list(dkeyStrings[key])
212 | sourceList.sort()
213 | sourceString = "\n".join(sourceList)
214 | listForSource = dGroupedBySource.get(sourceString, [])
215 | listForSource.append(key)
216 | dGroupedBySource[sourceString] = listForSource
217 |
218 | lSourceKeys = list(dGroupedBySource.keys())
219 | lSourceKeys.sort()
220 | for source in lSourceKeys:
221 | localizedStrings = dGroupedBySource[source]
222 | localizedStrings.sort()
223 | lOut.append("")
224 | lOut.append(source)
225 | lOut.append("")
226 | for localizedString in localizedStrings:
227 | val = dOld.get(localizedString, {})
228 | translation = val.get("translation", "")
229 | comment = val.get("comment")
230 | if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
231 | lOut.append("")
232 | if comment != None:
233 | lOut.append(comment)
234 | lOut.append(f"{localizedString}={translation}")
235 | if len(localizedString) > doublespace_threshold:
236 | lOut.append("")
237 |
238 |
239 | unusedExist = False
240 | for key in dOld:
241 | if key not in dkeyStrings:
242 | val = dOld[key]
243 | translation = val.get("translation")
244 | comment = val.get("comment")
245 | # only keep an unused translation if there was translated
246 | # text or a comment associated with it
247 | if translation != None and (translation != "" or comment):
248 | if not unusedExist:
249 | unusedExist = True
250 | lOut.append("\n\n##### not used anymore #####\n")
251 | if len(key) > doublespace_threshold and not lOut[-1] == "":
252 | lOut.append("")
253 | if comment != None:
254 | lOut.append(comment)
255 | lOut.append(f"{key}={translation}")
256 | if len(key) > doublespace_threshold:
257 | lOut.append("")
258 | return "\n".join(lOut) + '\n'
259 |
260 | # Writes a template.txt file
261 | # dkeyStrings is the dictionary returned by generate_template
262 | def write_template(templ_file, dkeyStrings, mod_name):
263 | # read existing template file to preserve comments
264 | existing_template = import_tr_file(templ_file)
265 |
266 | text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
267 | mkdir_p(os.path.dirname(templ_file))
268 | with open(templ_file, "wt", encoding='utf-8') as template_file:
269 | template_file.write(text)
270 |
271 |
272 | # Gets all translatable strings from a lua file
273 | def read_lua_file_strings(lua_file):
274 | lOut = []
275 | with open(lua_file, encoding='utf-8') as text_file:
276 | text = text_file.read()
277 | #TODO remove comments here
278 |
279 | text = re.sub(pattern_concat, "", text)
280 |
281 | strings = []
282 | for s in pattern_lua.findall(text):
283 | strings.append(s[1])
284 | for s in pattern_lua_bracketed.findall(text):
285 | strings.append(s)
286 |
287 | for s in strings:
288 | s = re.sub(r'"\.\.\s+"', "", s)
289 | s = re.sub("@[^@=0-9]", "@@", s)
290 | s = s.replace('\\"', '"')
291 | s = s.replace("\\'", "'")
292 | s = s.replace("\n", "@n")
293 | s = s.replace("\\n", "@n")
294 | s = s.replace("=", "@=")
295 | lOut.append(s)
296 | return lOut
297 |
298 | # Gets strings from an existing translation file
299 | # returns both a dictionary of translations
300 | # and the full original source text so that the new text
301 | # can be compared to it for changes.
302 | def import_tr_file(tr_file):
303 | dOut = {}
304 | text = None
305 | if os.path.exists(tr_file):
306 | with open(tr_file, "r", encoding='utf-8') as existing_file :
307 | # save the full text to allow for comparison
308 | # of the old version with the new output
309 | text = existing_file.read()
310 | existing_file.seek(0)
311 | # a running record of the current comment block
312 | # we're inside, to allow preceeding multi-line comments
313 | # to be retained for a translation line
314 | latest_comment_block = None
315 | for line in existing_file.readlines():
316 | line = line.rstrip('\n')
317 | if line[:3] == "###":
318 | # Reset comment block if we hit a header
319 | latest_comment_block = None
320 | continue
321 | if line[:1] == "#":
322 | # Save the comment we're inside
323 | if not latest_comment_block:
324 | latest_comment_block = line
325 | else:
326 | latest_comment_block = latest_comment_block + "\n" + line
327 | continue
328 | match = pattern_tr.match(line)
329 | if match:
330 | # this line is a translated line
331 | outval = {}
332 | outval["translation"] = match.group(2)
333 | if latest_comment_block:
334 | # if there was a comment, record that.
335 | outval["comment"] = latest_comment_block
336 | latest_comment_block = None
337 | dOut[match.group(1)] = outval
338 | return (dOut, text)
339 |
340 | # Walks all lua files in the mod folder, collects translatable strings,
341 | # and writes it to a template.txt file
342 | # Returns a dictionary of localized strings to source file sets
343 | # that can be used with the strings_to_text function.
344 | def generate_template(folder, mod_name):
345 | dOut = {}
346 | for root, dirs, files in os.walk(folder):
347 | for name in files:
348 | if fnmatch.fnmatch(name, "*.lua"):
349 | fname = os.path.join(root, name)
350 | found = read_lua_file_strings(fname)
351 | if params["verbose"]:
352 | print(f"{fname}: {str(len(found))} translatable strings")
353 |
354 | for s in found:
355 | sources = dOut.get(s, set())
356 | sources.add(f"### {os.path.basename(fname)} ###")
357 | dOut[s] = sources
358 |
359 | if len(dOut) == 0:
360 | return None
361 | templ_file = os.path.join(folder, "locale/template.txt")
362 | write_template(templ_file, dOut, mod_name)
363 | return dOut
364 |
365 | # Updates an existing .tr file, copying the old one to a ".old" file
366 | # if any changes have happened
367 | # dNew is the data used to generate the template, it has all the
368 | # currently-existing localized strings
369 | def update_tr_file(dNew, mod_name, tr_file):
370 | if params["verbose"]:
371 | print(f"updating {tr_file}")
372 |
373 | tr_import = import_tr_file(tr_file)
374 | dOld = tr_import[0]
375 | textOld = tr_import[1]
376 |
377 | textNew = strings_to_text(dNew, dOld, mod_name)
378 |
379 | if textOld and textOld != textNew:
380 | print(f"{tr_file} has changed.")
381 | shutil.copyfile(tr_file, f"{tr_file}.old")
382 |
383 | with open(tr_file, "w", encoding='utf-8') as new_tr_file:
384 | new_tr_file.write(textNew)
385 |
386 | # Updates translation files for the mod in the given folder
387 | def update_mod(folder):
388 | modname = get_modname(folder)
389 | if modname is not None:
390 | process_po_files(folder, modname)
391 | print(f"Updating translations for {modname}")
392 | data = generate_template(folder, modname)
393 | if data == None:
394 | print(f"No translatable strings found in {modname}")
395 | else:
396 | for tr_file in get_existing_tr_files(folder):
397 | update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
398 | else:
399 | print("Unable to find modname in folder " + folder)
400 |
401 | # Determines if the folder being pointed to is a mod or a mod pack
402 | # and then runs update_mod accordingly
403 | def update_folder(folder):
404 | is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
405 | if is_modpack:
406 | subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
407 | for subfolder in subfolders:
408 | update_mod(subfolder + "/")
409 | else:
410 | update_mod(folder)
411 | print("Done.")
412 |
413 | def run_all_subfolders(folder):
414 | for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
415 | update_folder(modfolder + "/")
416 |
417 |
418 | main()
419 |
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | ropes = {
2 | name = 'ropes',
3 | }
4 |
5 | -- internationalization boilerplate
6 | local modname = minetest.get_current_modname()
7 | local MP = minetest.get_modpath(modname)
8 | ropes.S = minetest.get_translator(modname)
9 |
10 | ropes.ropeLength = tonumber(minetest.settings:get("ropes_rope_length")) or 50
11 | ropes.woodRopeBoxMaxMultiple = tonumber(minetest.settings:get("ropes_wood_rope_box_max_multiple")) or 2
12 | ropes.copperRopeBoxMaxMultiple = tonumber(minetest.settings:get("ropes_copper_rope_box_max_multiple")) or 5
13 | ropes.steelRopeBoxMaxMultiple = tonumber(minetest.settings:get("ropes_steel_rope_box_max_multiple")) or 9
14 | ropes.create_all_definitions = minetest.settings:get_bool("ropes_create_all_definitions")
15 |
16 | ropes.ropeLadderLength = tonumber(minetest.settings:get("ropes_rope_ladder_length")) or 50
17 |
18 | ropes.extending_ladder_enabled = minetest.settings:get_bool("ropes_extending_ladder_enabled")
19 | if ropes.extending_ladder_enabled == nil then
20 | ropes.extending_ladder_enabled = true
21 | end
22 | ropes.replace_default_ladders = minetest.settings:get_bool("ropes_replace_default_ladders")
23 |
24 | ropes.extending_wood_ladder_limit = tonumber(minetest.settings:get("ropes_extending_wood_ladder_limit")) or 5
25 | ropes.extending_steel_ladder_limit = tonumber(minetest.settings:get("ropes_extending_steel_ladder_limit")) or 15
26 |
27 | ropes.bridges_enabled = minetest.settings:get_bool("ropes_bridges_enabled")
28 | if ropes.bridges_enabled == nil then
29 | ropes.bridges_enabled = true
30 | end
31 |
32 | ropes.can_extend_into_airlike = minetest.settings:get_bool("ropes_can_extend_into_airlike")
33 | ropes.can_extend_into_nodes = {["air"] = true}
34 | if minetest.get_modpath("nether") then
35 | ropes.can_extend_into_nodes["nether:fumes"] = true
36 | end
37 |
38 | dofile( MP .. "/doc.lua" )
39 | dofile( MP .. "/functions.lua" )
40 | dofile( MP .. "/crafts.lua" )
41 | dofile( MP .. "/ropeboxes.lua" )
42 | dofile( MP .. "/ropeladder.lua" )
43 | dofile( MP .. "/extendingladder.lua" )
44 | dofile( MP .. "/bridge.lua" )
45 | dofile( MP .. "/loot.lua" )
46 |
47 | for i=1,5 do
48 | minetest.register_alias(string.format("vines:%irope_block", i), string.format("ropes:%irope_block", i))
49 | end
50 | minetest.register_alias("vines:rope", "ropes:rope")
51 | minetest.register_alias("vines:rope_bottom", "ropes:rope_bottom")
52 | minetest.register_alias("vines:rope_end", "ropes:rope_bottom")
53 | minetest.register_alias("vines:rope_top", "ropes:rope_top")
54 | minetest.register_alias("vines:ropeladder_top", "ropes:ropeladder_top")
55 | minetest.register_alias("vines:ropeladder", "ropes:ropeladder")
56 | minetest.register_alias("vines:ropeladder_bottom", "ropes:ropeladder_bottom")
57 | minetest.register_alias("vines:ropeladder_falling", "ropes:ropeladder_falling")
58 | minetest.register_alias("vines:rope_block", "ropes:steel5rope_block")
59 | for i=1,9 do
60 | minetest.register_alias(string.format("ropes:%irope_block", i), string.format("ropes:steel%irope_block", i))
61 | end
62 | minetest.register_alias("castle:ropes", "ropes:rope")
63 | minetest.register_alias("castle:ropebox", "ropes:steel1rope_block")
64 | minetest.register_alias("castle:box_rope", "ropes:rope")
65 |
66 | print("[Ropes] Loaded!")
67 |
--------------------------------------------------------------------------------
/locale/ropes.es.tr:
--------------------------------------------------------------------------------
1 | # textdomain: ropes
2 |
3 |
4 | ### bridge.lua ###
5 |
6 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
7 | Wooden Bridge=Puente de madera
8 |
9 | ### crafts.lua ###
10 |
11 | Rope Segment=Segmento de cuerda
12 |
13 | ### doc.lua ###
14 |
15 | A hanging rope ladder that automatically extends downward.=Una escalera de cuerda colgante que se extiende automáticamente hacia abajo.
16 |
17 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
18 | 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.
19 |
20 | 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.
21 |
22 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
23 | A wooden platform with support struts useful for bridging gaps.=Una plataforma de madera con puntales de soporte útil para salvar huecos.
24 |
25 | 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.
26 |
27 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
28 | 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.
29 |
30 | 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.
31 |
32 | Rope segments are bundles of fibre twisted into robust cables.=Los segmentos de cable son haces de fibras trenzadas en cables robustos.
33 |
34 | 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.
35 |
36 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
37 | 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.
38 |
39 | 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.
40 |
41 | 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.
42 |
43 | rope boxes can hold @1m of rope.=Las cajas de cuerdas pueden mantener @1m de cuerda.
44 | rope boxes can hold rope lengths from @1m to @2m.=Las cajas de cuerda pueden contener longitudes de cuerda de @1m a @2m.
45 |
46 | ### doc.lua ###
47 | ### ropeboxes.lua ###
48 |
49 | Copper=cobre
50 | Steel=acero
51 | Wood=madera
52 |
53 | ### extendingladder.lua ###
54 |
55 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
56 | Steel Extendable Ladder=Escalera extensible de acero
57 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
58 | Steel Ladder=Escalera de acero
59 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
60 | Wooden Extendable Ladder=Escalera extensible de madera
61 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
62 | Wooden Ladder=Escalera de madera
63 |
64 | ### ropeboxes.lua ###
65 |
66 | @1 Ropebox @2m=Caja de cuerda de @1 de @2m
67 | Rope=Cuerda
68 |
69 | ### ropeladder.lua ###
70 |
71 | Rope Ladder=Escalera de cuerda
72 |
--------------------------------------------------------------------------------
/locale/ropes.ru.tr:
--------------------------------------------------------------------------------
1 | # textdomain: ropes
2 |
3 | ### bridge.lua ###
4 |
5 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
6 | Wooden Bridge=Деревянный мост
7 |
8 | ### crafts.lua ###
9 |
10 | Rope Segment=Сегмент каната
11 |
12 | ### doc.lua ###
13 |
14 | A hanging rope ladder that automatically extends downward.=Подвесная веревочная лестница, которая автоматически выдвигается вниз.
15 |
16 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
17 | A ladder for climbing. It can reach greater heights when placed against a supporting block.=Лестница для подъема. Она может достигать большей высоты, если ее приставить к опорному блоку.
18 |
19 | 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.=Веревку можно перерезать на полпути с помощью топора или другого похожего инструмента. Часть веревки в месте разреза разрушится и исчезнет, что может привести к падению игроков, которые на ней висели. Оставшаяся веревка не возобновит спуск сама по себе, но коробка для веревок наверху «помнит», какой длины была веревка, и если ее разобрать и заменить, она все равно будет иметь ту же максимальную длину веревки, что и раньше — ни одна веревка не теряется навсегда, когда веревка перерезана таким образом.
20 |
21 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
22 | A wooden platform with support struts useful for bridging gaps.=Деревянная платформа с опорными стойками, используемая для перекрытия щелей.
23 |
24 | 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 метр). Если веревочную лестницу убрать, вся лестница ниже точки удаления исчезнет. Веревочную лестницу можно перерезать наполовину с помощью топора или подобного инструмента, и лестница ниже точки, где она перерезана, рухнет. Однако в этом процессе веревка фактически не теряется, и если снять и заменить самую верхнюю часть лестницы, лестница снова удлинится до той же максимальной длины, что и раньше.
25 |
26 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
27 | 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.=Щелчок правой кнопкой мыши по лестнице со стопкой идентичных элементов лестницы автоматически добавит новые сегменты лестницы наверх, при условии, что она не выступает слишком далеко за пределы последнего блока позади нее, обеспечивающего поддержку.
28 |
29 | 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 в сетку крафта отдельно.
30 |
31 | Rope segments are bundles of fibre twisted into robust cables.=Сегменты каната представляют собой пучки волокон, скрученные в прочные тросы.
32 |
33 | Ropes are hung by placing rope boxes, which automatically lower a rope of fixed length below them. They can be climbed and cut.=Канаты подвешиваются путем размещения ящиков для канатов, которые автоматически опускают под ними канат фиксированной длины. По ним можно лазить и резать.
34 |
35 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
36 | 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.=Он ведет себя как большинство структурных блоков, за исключением одного обстоятельства: при размещении на блоке с пространством для строительства на стороне, обращенной от вас, этот блок не будет построен сверху, а вместо этого будет выступать из дальней стороны целевого блока. Это позволяет легко построить платформу, которая выступает из того места, где вы стоите.
37 |
38 | This craft item is useful for creating rope ladders, or for spooling on wooden spindles to hang and climb upon.=Этот предмет ручной работы пригодится для создания веревочных лестниц или для наматывания на деревянные стержни, чтобы можно было повесить веревку или залезть на нее.
39 |
40 | 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.=Когда вы размещаете ящик с веревкой, веревка немедленно начнет опускаться с него со скоростью один метр в секунду. Веревка будет опускаться только тогда, когда ее конец находится в непосредственной близости от активного игрока, приостанавливая свое движение, когда поблизости нет игроков, поэтому для длительного спуска игроку может потребоваться спускаться по веревке по мере ее движения. Если вы находитесь около нижнего конца удлиняющейся веревки, вы автоматически будете унесены ею вниз. Веревка остановится, когда столкнется с препятствием, но возобновит спуск, если препятствие будет устранено.
41 |
42 | rope boxes can hold @1m of rope.=Ящики для веревок вмещают до 1 м веревки.
43 | rope boxes can hold rope lengths from @1m to @2m.=Ящики для веревок могут вмещать веревки длиной от @1 до @2 метров.
44 |
45 | ### doc.lua ###
46 | ### ropeboxes.lua ###
47 |
48 | Copper=Медный
49 | Steel=Стальной
50 | Wood=Древесный
51 |
52 | ### extendingladder.lua ###
53 |
54 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
55 | Steel Extendable Ladder=Стальная раздвижная лестница
56 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
57 | Steel Ladder=Стальная лестница
58 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
59 | Wooden Extendable Ladder=Деревянная раздвижная лестница
60 | #WARNING: AUTOTRANSLATED BY GOOGLE TRANSLATE
61 | Wooden Ladder=Деревянная лестница
62 |
63 | ### ropeboxes.lua ###
64 |
65 | @1 Ropebox @2m=@1 веревочный ящик @2м
66 | Rope=Веревка
67 |
68 | ### ropeladder.lua ###
69 |
70 | Rope Ladder=Веревочная лестница
71 |
--------------------------------------------------------------------------------
/locale/template.txt:
--------------------------------------------------------------------------------
1 | # textdomain: ropes
2 |
3 |
4 | ### bridge.lua ###
5 |
6 | Wooden Bridge=
7 |
8 | ### crafts.lua ###
9 |
10 | Rope Segment=
11 |
12 | ### doc.lua ###
13 |
14 | A hanging rope ladder that automatically extends downward.=
15 |
16 | A ladder for climbing. It can reach greater heights when placed against a supporting block.=
17 |
18 | 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.=
19 |
20 | A wooden platform with support struts useful for bridging gaps.=
21 |
22 | 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.=
23 |
24 | 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.=
25 |
26 | 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.=
27 |
28 | Rope segments are bundles of fibre twisted into robust cables.=
29 |
30 | Ropes are hung by placing rope boxes, which automatically lower a rope of fixed length below them. They can be climbed and cut.=
31 |
32 | 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.=
33 |
34 | This craft item is useful for creating rope ladders, or for spooling on wooden spindles to hang and climb upon.=
35 |
36 | 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.=
37 |
38 | rope boxes can hold @1m of rope.=
39 | rope boxes can hold rope lengths from @1m to @2m.=
40 |
41 | ### doc.lua ###
42 | ### ropeboxes.lua ###
43 |
44 | Copper=
45 | Steel=
46 | Wood=
47 |
48 | ### extendingladder.lua ###
49 |
50 | Steel Extendable Ladder=
51 | Steel Ladder=
52 | Wooden Extendable Ladder=
53 | Wooden Ladder=
54 |
55 | ### ropeboxes.lua ###
56 |
57 | @1 Ropebox @2m=
58 | Rope=
59 |
60 | ### ropeladder.lua ###
61 |
62 | Rope Ladder=
63 |
--------------------------------------------------------------------------------
/loot.lua:
--------------------------------------------------------------------------------
1 | if not minetest.get_modpath("loot") then
2 | return
3 | end
4 |
5 | loot.register_loot({
6 | weights = { generic = 300 },
7 | payload = {
8 | stack = ItemStack("ropes:ropesegment"),
9 | min_size = 1,
10 | max_size = 50,
11 | },
12 | })
13 |
14 | if ropes.ropeLadderLength > 0 then
15 | loot.register_loot({
16 | weights = { generic = 150 },
17 | payload = {
18 | stack = ItemStack("ropes:ropeladder_top"),
19 | min_size = 1,
20 | max_size = 20,
21 | },
22 | })
23 | end
24 |
25 | if ropes.woodRopeBoxMaxMultiple > 0 then
26 | loot.register_loot({
27 | weights = { generic = 100 },
28 | payload = {
29 | stack = ItemStack("ropes:wood1rope_block"),
30 | min_size = 1,
31 | max_size = 20,
32 | },
33 | })
34 | end
35 |
36 | if ropes.copperRopeBoxMaxMultiple > 0 then
37 | loot.register_loot({
38 | weights = { generic = 75 },
39 | payload = {
40 | stack = ItemStack("ropes:copper1rope_block"),
41 | min_size = 1,
42 | max_size = 15,
43 | },
44 | })
45 | end
46 |
47 | if ropes.steelRopeBoxMaxMultiple > 0 then
48 | loot.register_loot({
49 | weights = { generic = 50 },
50 | payload = {
51 | stack = ItemStack("ropes:steel1rope_block"),
52 | min_size = 1,
53 | max_size = 10,
54 | },
55 | })
56 | end
--------------------------------------------------------------------------------
/mod.conf:
--------------------------------------------------------------------------------
1 | name = ropes
2 | description = Adds rope boxes and ladders
3 | depends = default
4 | optional_depends = cottages, doc, farming, hemp, loot, vines
5 |
--------------------------------------------------------------------------------
/ropeboxes.lua:
--------------------------------------------------------------------------------
1 | -- internationalization boilerplate
2 | local S = ropes.S
3 |
4 | local function rope_box_tiles(count, tint)
5 | return {
6 | string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
7 | string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
8 | string.format("ropes_ropebox_side.png^[colorize:%s^ropes_ropebox_side.png", tint),
9 | string.format("ropes_ropebox_side.png^[colorize:%s^ropes_ropebox_side.png", tint),
10 | string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
11 | string.format("ropes_ropebox_front_%i.png^[colorize:%s^ropes_ropebox_front_%i.png^ropes_%i.png", count, tint, count, count),
12 | }
13 | end
14 |
15 | local rope_box_data = {
16 | {
17 | node={
18 | {-0.125, -0.125, -0.25, 0.125, 0.125, 0.25}, -- pulley
19 | {-0.125, -0.25, -0.125, 0.125, 0.25, 0.125}, -- pulley
20 | {-0.125, -0.1875, -0.1875, 0.125, 0.1875, 0.1875}, -- pulley_core
21 | {-0.1875, -0.5, -0.125, -0.125, 0.125, 0.125}, -- support
22 | {0.125, -0.5, -0.125, 0.1875, 0.125, 0.125}, -- support
23 | },
24 | --selection = {-0.1875, -0.5, -0.25, 0.1875, 0.25, 0.25}, -- selection
25 | tiles = 1,
26 | },
27 | {
28 | node={
29 | {-0.1875, -0.125, -0.25, 0.1875, 0.125, 0.25}, -- pulley
30 | {-0.1875, -0.25, -0.125, 0.1875, 0.25, 0.125}, -- pulley
31 | {-0.1875, -0.1875, -0.1875, 0.1875, 0.1875, 0.1875}, -- pulley_core
32 | {-0.25, -0.5, -0.125, -0.1875, 0.125, 0.125}, -- support
33 | {0.1875, -0.5, -0.125, 0.25, 0.125, 0.125}, -- support
34 | },
35 | --selection = {-0.1875, -0.5, -0.25, 0.1875, 0.25, 0.25}, -- selection
36 | tiles = 2,
37 | },
38 | {
39 | node={
40 | {-0.25, -0.125, -0.25, 0.25, 0.125, 0.25}, -- pulley
41 | {-0.25, -0.25, -0.125, 0.25, 0.25, 0.125}, -- pulley
42 | {-0.25, -0.1875, -0.1875, 0.25, 0.1875, 0.1875}, -- pulley_core
43 | {-0.3125, -0.5, -0.125, -0.25, 0.125, 0.125}, -- support
44 | {0.25, -0.5, -0.125, 0.3125, 0.125, 0.125}, -- support
45 | },
46 | --selection = {-0.3125, -0.5, -0.25, 0.3125, 0.25, 0.25}, -- selection
47 | tiles = 3,
48 | },
49 | {
50 | node={
51 | {-0.3125, -0.125, -0.25, 0.3125, 0.125, 0.25}, -- pulley
52 | {-0.3125, -0.25, -0.125, 0.3125, 0.25, 0.125}, -- pulley
53 | {-0.3125, -0.1875, -0.1875, 0.3125, 0.1875, 0.1875}, -- pulley_core
54 | {-0.375, -0.5, -0.125, -0.3125, 0.125, 0.125}, -- support
55 | {0.3125, -0.5, -0.125, 0.375, 0.125, 0.125}, -- support
56 | },
57 | --selection = {-0.375, -0.5, -0.25, 0.375, 0.25, 0.25}, -- selection
58 | tiles = 4,
59 | },
60 | {
61 | node={
62 | {-0.375, -0.125, -0.25, 0.375, 0.125, 0.25}, -- pulley
63 | {-0.375, -0.25, -0.125, 0.375, 0.25, 0.125}, -- pulley
64 | {-0.375, -0.1875, -0.1875, 0.375, 0.1875, 0.1875}, -- pulley_core
65 | {-0.4375, -0.5, -0.125, -0.375, 0.125, 0.125}, -- support
66 | {0.375, -0.5, -0.125, 0.4375, 0.125, 0.125}, -- support
67 | },
68 | --selection = {-0.4375, -0.5, -0.25, 0.4375, 0.25, 0.25}, -- selection
69 | tiles = 5,
70 | },
71 | {
72 | node={
73 | {-0.1875, -0.1875, -0.3125, 0.1875, 0.1875, 0.3125}, -- pulley
74 | {-0.1875, -0.3125, -0.1875, 0.1875, 0.3125, 0.1875}, -- pulley
75 | {-0.1875, -0.25, -0.25, 0.1875, 0.25, 0.25}, -- pulley_core
76 | {-0.25, -0.5, -0.125, -0.1875, 0.125, 0.125}, -- support
77 | {0.1875, -0.5, -0.125, 0.25, 0.125, 0.125}, -- support
78 | },
79 | --selection = {-0.1875, -0.5, -0.3125, 0.1875, 0.3125, 0.3125}, -- selection
80 | tiles = 2,
81 | },
82 | {
83 | node={
84 | {-0.25, -0.1875, -0.3125, 0.25, 0.1875, 0.3125}, -- pulley
85 | {-0.25, -0.3125, -0.1875, 0.25, 0.3125, 0.1875}, -- pulley
86 | {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, -- pulley_core
87 | {-0.3125, -0.5, -0.125, -0.25, 0.125, 0.125}, -- support
88 | {0.25, -0.5, -0.125, 0.3125, 0.125, 0.125}, -- support
89 | },
90 | --selection = {-0.3125, -0.5, -0.3125, 0.3125, 0.3125, 0.3125}, -- selection
91 | tiles = 3,
92 | },
93 | {
94 | node={
95 | {-0.3125, -0.1875, -0.3125, 0.3125, 0.1875, 0.3125}, -- pulley
96 | {-0.3125, -0.3125, -0.1875, 0.3125, 0.3125, 0.1875}, -- pulley
97 | {-0.3125, -0.25, -0.25, 0.3125, 0.25, 0.25}, -- pulley_core
98 | {-0.375, -0.5, -0.125, -0.3125, 0.125, 0.125}, -- support
99 | {0.3125, -0.5, -0.125, 0.375, 0.125, 0.125}, -- support
100 | },
101 | --selection = {-0.375, -0.5, -0.3125, 0.375, 0.3125, 0.3125}, -- selection
102 | tiles = 4,
103 | },
104 | {
105 | node={
106 | {-0.375, -0.1875, -0.3125, 0.375, 0.1875, 0.3125}, -- pulley
107 | {-0.375, -0.3125, -0.1875, 0.375, 0.3125, 0.1875}, -- pulley
108 | {-0.375, -0.25, -0.25, 0.375, 0.25, 0.25}, -- pulley_core
109 | {-0.4375, -0.5, -0.125, -0.375, 0.125, 0.125}, -- support
110 | {0.375, -0.5, -0.125, 0.4375, 0.125, 0.125}, -- support
111 | },
112 | --selection_bottom = {-0.4375, -0.5, -0.3125, 0.4375, 0.3125, 0.3125}, -- selection
113 | tiles = 5,
114 | }
115 | }
116 |
117 | local function register_rope_block(multiple, max_multiple, name_prefix, node_prefix, tint, base_material, flammable)
118 | local node_name = string.format("ropes:%s%irope_block", node_prefix, multiple)
119 | local rope_block_def = {
120 | description = S("@1 Ropebox @2m", name_prefix, ropes.ropeLength*multiple),
121 | _doc_items_create_entry = false,
122 | drawtype="nodebox",
123 | sunlight_propagates = true,
124 | paramtype = "light",
125 | paramtype2 = "wallmounted",
126 | walkable = false,
127 | climbable = true,
128 | tiles = rope_box_tiles(rope_box_data[multiple].tiles, tint),
129 | use_texture_alpha = "clip",
130 | is_ground_content = false,
131 | node_box = {
132 | type = "fixed",
133 | fixed = rope_box_data[multiple].node
134 | },
135 | selection_box = {type="regular"},
136 | collision_box = {type="regular"},
137 | groups = {attached_node = 1, choppy=2, oddly_breakable_by_hand=1, rope_block = 1},
138 |
139 | on_place = function(itemstack, placer, pointed_thing)
140 | if pointed_thing.type == "node" then
141 | local target_node = minetest.get_node(pointed_thing.under)
142 | local target_def = minetest.registered_nodes[target_node.name]
143 | if target_def.walkable == false then
144 | return itemstack
145 | end
146 | end
147 | return minetest.item_place(itemstack, placer, pointed_thing)
148 | end,
149 |
150 | after_place_node = function(pos, placer)
151 | local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
152 | local placer_name = placer:get_player_name()
153 |
154 | if minetest.is_protected(pos_below, placer_name) and not minetest.check_player_privs(placer, "protection_bypass") then
155 | return
156 | end
157 |
158 | local node_below = minetest.get_node(pos_below)
159 | if ropes.can_place_rope_in_node(node_below.name) then
160 | minetest.add_node(pos_below, {name="ropes:rope_bottom"})
161 | local meta = minetest.get_meta(pos_below)
162 | meta:set_int("length_remaining", ropes.ropeLength*multiple)
163 | meta:set_string("placer", placer:get_player_name())
164 | end
165 | end,
166 |
167 | after_destruct = function(pos)
168 | local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
169 | ropes.destroy_rope(pos_below, {'ropes:rope', 'ropes:rope_bottom'})
170 | end
171 | }
172 |
173 | -- If this number is higher than permitted, we still want to register the block (in case
174 | -- some were already placed in-world) but we want to hide it from creative inventory
175 | -- and if someone digs it we want to disintegrate it into its component parts to prevent
176 | -- reuse.
177 | if multiple > max_multiple then
178 | rope_block_def.groups.not_in_creative_inventory = 1
179 | rope_block_def.drop = string.format("ropes:%s1rope_block %i", node_prefix, multiple)
180 | end
181 |
182 | if flammable then
183 | rope_block_def.groups.flammable = flammable
184 |
185 | minetest.register_craft({
186 | type = "fuel",
187 | recipe = node_name,
188 | burntime = ropes.rope_burn_time * multiple + ropes.wood_burn_time,
189 | })
190 | else
191 | core.register_craft({
192 | type = "fuel",
193 | recipe = node_name,
194 | burntime = ropes.rope_burn_time * multiple,
195 | replacements = {{node_name, base_material}}
196 | })
197 | end
198 |
199 | minetest.register_node(node_name, rope_block_def)
200 |
201 | if (multiple ~= 1) then
202 | -- Only register a recipe to craft this if it's within the permitted multiple range
203 | if multiple <= max_multiple then
204 | for i = 1, multiple-1 do
205 | local rec = {string.format("ropes:%s%irope_block", node_prefix, i)}
206 | for n = 1, multiple-i do
207 | table.insert(rec, "ropes:ropesegment")
208 | end
209 | minetest.register_craft({
210 | output = node_name,
211 | type = "shapeless",
212 | recipe = rec
213 | })
214 | end
215 | end
216 |
217 | -- Always allow players to disintegrate this into component parts, in case
218 | -- there were some in inventory and the setting was changed.
219 | minetest.register_craft({
220 | output = "ropes:ropesegment",
221 | type = "shapeless",
222 | recipe = {
223 | node_name
224 | },
225 | replacements = {
226 | {node_name, string.format('ropes:%s%irope_block', node_prefix, multiple-1)},
227 | },
228 | })
229 | end
230 |
231 | if minetest.get_modpath("doc") then
232 | doc.add_entry_alias("nodes", "ropes:rope", "nodes", node_name)
233 | end
234 | end
235 |
236 | local rope_def = {
237 | description = S("Rope"),
238 | _doc_items_longdesc = ropes.doc.ropebox_longdesc,
239 | _doc_items_usagehelp = ropes.doc.ropebox_usage,
240 | walkable = false,
241 | climbable = true,
242 | sunlight_propagates = true,
243 | paramtype = "light",
244 | drop = "",
245 | tiles = { "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_5.png", "ropes_5.png" },
246 | use_texture_alpha = "clip",
247 | is_ground_content = false,
248 | groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
249 | sounds = {
250 | footstep = {name = "ropes_creak", gain = 0.8, max_hear_distance = 6},
251 | dig = "__group",
252 | dug = "__group",
253 | },
254 | drawtype = "nodebox",
255 | node_box = {
256 | type = "connected",
257 | fixed = {-1/16, -1/2, -1/16, 1/16, 1/2, 1/16},
258 | connect_top = {-1/16, 1/2, -1/16, 1/16, 3/4, 1/16}
259 | },
260 | connects_to = {"group:rope_block"},
261 | connect_sides = {"top"},
262 | selection_box = {
263 | type = "fixed",
264 | fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
265 | },
266 | after_destruct = function(pos)
267 | ropes.hanging_after_destruct(pos, "ropes:rope_top", "ropes:rope", "ropes:rope_bottom")
268 | end,
269 | }
270 |
271 | local rope_extension_timer = ropes.make_rope_on_timer("ropes:rope")
272 |
273 | local rope_bottom_def = {
274 | description = S("Rope"),
275 | _doc_items_create_entry = false,
276 | walkable = false,
277 | climbable = true,
278 | sunlight_propagates = true,
279 | paramtype = "light",
280 | drop = "",
281 | tiles = { "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_3.png", "ropes_5.png", "ropes_5.png" },
282 | use_texture_alpha = "clip",
283 | is_ground_content = false,
284 | drawtype = "nodebox",
285 | groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
286 | sounds = {
287 | footstep = {name = "ropes_creak", gain = 0.8, max_hear_distance = 6},
288 | dig = "__group",
289 | dug = "__group",
290 | },
291 | node_box = {
292 | type = "connected",
293 | fixed = {
294 | {-1/16, -3/8, -1/16, 1/16, 1/2, 1/16},
295 | {-2/16, -5/16, -2/16, 2/16, -1/16, 2/16},
296 | },
297 | connect_top = {-1/16, 1/2, -1/16, 1/16, 3/4, 1/16}
298 | },
299 | connects_to = {"group:rope_block"},
300 | connect_sides = {"top"},
301 | selection_box = {
302 | type = "fixed",
303 | fixed = {-1/8, -1/2, -1/8, 1/8, 1/2, 1/8},
304 | },
305 |
306 | on_construct = function( pos )
307 | local timer = minetest.get_node_timer( pos )
308 | timer:start( 1 )
309 | end,
310 |
311 | on_timer = rope_extension_timer,
312 |
313 | after_destruct = function(pos)
314 | ropes.hanging_after_destruct(pos, "ropes:rope_top", "ropes:rope", "ropes:rope_bottom")
315 | end,
316 | }
317 |
318 | minetest.register_node("ropes:rope", rope_def)
319 | minetest.register_node("ropes:rope_bottom", rope_bottom_def)
320 |
321 | if ropes.woodRopeBoxMaxMultiple > 0 or ropes.create_all_definitions then
322 | if ropes.woodRopeBoxMaxMultiple > 0 then
323 | minetest.register_craft({
324 | output = "ropes:wood1rope_block",
325 | recipe = {
326 | {'group:wood'},
327 | {'group:vines'}
328 | }
329 | })
330 | end
331 | for i = 1,9 do
332 | if ropes.woodRopeBoxMaxMultiple >= i or ropes.create_all_definitions then
333 | register_rope_block(i, ropes.woodRopeBoxMaxMultiple, S("Wood"), "wood", "#86683a", "group:wood", 2)
334 | end
335 | end
336 | end
337 |
338 | if ropes.copperRopeBoxMaxMultiple > 0 or ropes.create_all_definitions then
339 | if ropes.copperRopeBoxMaxMultiple > 0 then
340 | minetest.register_craft({
341 | output = "ropes:copper1rope_block",
342 | recipe = {
343 | {'default:copper_ingot'},
344 | {'group:vines'}
345 | }
346 | })
347 | end
348 | for i = 1,9 do
349 | if ropes.copperRopeBoxMaxMultiple >= i or ropes.create_all_definitions then
350 | register_rope_block(i, ropes.copperRopeBoxMaxMultiple, S("Copper"), "copper", "#c88648", "default:copper_ingot")
351 | end
352 | end
353 | end
354 |
355 | if ropes.steelRopeBoxMaxMultiple > 0 or ropes.create_all_definitions then
356 | if ropes.steelRopeBoxMaxMultiple > 0 then
357 | minetest.register_craft({
358 | output = "ropes:steel1rope_block",
359 | recipe = {
360 | {'default:steel_ingot'},
361 | {'group:vines'}
362 | }
363 | })
364 | end
365 | for i = 1,9 do
366 | if ropes.steelRopeBoxMaxMultiple >= i or ropes.create_all_definitions then
367 | register_rope_block(i, ropes.steelRopeBoxMaxMultiple, S("Steel"), "steel", "#ffffff", "default:steel_ingot")
368 | end
369 | end
370 | end
371 |
--------------------------------------------------------------------------------
/ropeladder.lua:
--------------------------------------------------------------------------------
1 | if ropes.ropeLadderLength == 0 and not ropes.create_all_definitions then
2 | return
3 | end
4 |
5 | local S = ropes.S
6 |
7 | if ropes.ropeLadderLength > 0 then
8 | minetest.register_craft({
9 | output = "ropes:ropeladder_top",
10 | recipe = {
11 | {'','group:stick',''},
12 | {'group:vines','group:stick','group:vines'},
13 | {'','group:stick',''},
14 | }
15 | })
16 | end
17 |
18 | minetest.register_craft({
19 | type = "fuel",
20 | recipe = "ropes:ropeladder_top",
21 | burntime = ropes.ladder_burn_time,
22 | })
23 |
24 | local rope_ladder_top_def = {
25 | description = S("Rope Ladder"),
26 | _doc_items_longdesc = ropes.doc.ropeladder_longdesc,
27 | _doc_items_usagehelp = ropes.doc.ropeladder_usage,
28 | drawtype = "signlike",
29 | tiles = {"default_ladder_wood.png^ropes_ropeladder_top.png"},
30 | use_texture_alpha = "clip",
31 | is_ground_content = false,
32 | inventory_image = "default_ladder_wood.png^ropes_ropeladder_top.png",
33 | wield_image = "default_ladder_wood.png^ropes_ropeladder_top.png",
34 | paramtype = "light",
35 | paramtype2 = "wallmounted",
36 | walkable = false,
37 | climbable = true,
38 | sunlight_propagates = true,
39 | selection_box = {
40 | type = "wallmounted",
41 | --wall_top = =
42 | --wall_bottom = =
43 | --wall_side = =
44 |
45 | },
46 | groups = { choppy=2, oddly_breakable_by_hand=1,flammable=2},
47 | sounds = default.node_sound_wood_defaults(),
48 |
49 | on_place = function(itemstack, placer, pointed_thing)
50 | if pointed_thing.type == "node" then
51 | local target_node = minetest.get_node(pointed_thing.under)
52 | local target_def = minetest.registered_nodes[target_node.name]
53 | if target_def.walkable == false then
54 | return itemstack
55 | end
56 | end
57 | return minetest.item_place(itemstack, placer, pointed_thing)
58 | end,
59 |
60 | after_place_node = function(pos, placer)
61 | local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
62 | local node_below = minetest.get_node(pos_below)
63 | local this_node = minetest.get_node(pos)
64 | local placer_name = placer:get_player_name()
65 | -- 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.
66 | if ropes.can_place_rope_in_node(node_below.name) and this_node.param2 > 1
67 | and (not minetest.is_protected(pos_below, placer_name)
68 | or minetest.check_player_privs(placer_name, "protection_bypass")) then
69 | minetest.add_node(pos_below, {name="ropes:ropeladder_bottom", param2=this_node.param2})
70 | local meta = minetest.get_meta(pos_below)
71 | meta:set_int("length_remaining", ropes.ropeLadderLength)
72 | meta:set_string("placer", placer_name)
73 | end
74 | end,
75 | after_destruct = function(pos)
76 | local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
77 | ropes.destroy_rope(pos_below, {"ropes:ropeladder", "ropes:ropeladder_bottom", "ropes:ropeladder_falling"})
78 | end,
79 | }
80 |
81 | if ropes.ropeLadderLength == 0 then
82 | rope_ladder_top_def.groups.not_in_creative_inventory = 1
83 | end
84 |
85 | minetest.register_node("ropes:ropeladder_top", rope_ladder_top_def)
86 |
87 | minetest.register_node("ropes:ropeladder", {
88 | description = S("Rope Ladder"),
89 | _doc_items_create_entry = false,
90 | drop = "",
91 | drawtype = "signlike",
92 | tiles = {"default_ladder_wood.png^ropes_ropeladder.png"},
93 | use_texture_alpha = "clip",
94 | is_ground_content = false,
95 | inventory_image = "default_ladder_wood.png^ropes_ropeladder.png",
96 | wield_image = "default_ladder_wood.png^ropes_ropeladder.png",
97 | paramtype = "light",
98 | paramtype2 = "wallmounted",
99 | walkable = false,
100 | climbable = true,
101 | sunlight_propagates = true,
102 | selection_box = {
103 | type = "wallmounted",
104 | --wall_top = =
105 | --wall_bottom = =
106 | --wall_side = =
107 | },
108 | groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
109 | sounds = default.node_sound_wood_defaults(),
110 |
111 | after_destruct = function(pos)
112 | ropes.hanging_after_destruct(pos, "ropes:ropeladder_falling", "ropes:ropeladder", "ropes:ropeladder_bottom")
113 | end,
114 | })
115 |
116 | local ladder_extender = ropes.make_rope_on_timer("ropes:ropeladder")
117 |
118 | minetest.register_node("ropes:ropeladder_bottom", {
119 | description = S("Rope Ladder"),
120 | _doc_items_create_entry = false,
121 | drop = "",
122 | drawtype = "signlike",
123 | tiles = {"default_ladder_wood.png^ropes_ropeladder_bottom.png"},
124 | use_texture_alpha = "clip",
125 | is_ground_content = false,
126 | inventory_image = "default_ladder_wood.png^ropes_ropeladder_bottom.png",
127 | wield_image = "default_ladder_wood.png^ropes_ropeladder_bottom.png",
128 | paramtype = "light",
129 | paramtype2 = "wallmounted",
130 | walkable = false,
131 | climbable = true,
132 | sunlight_propagates = true,
133 | selection_box = {
134 | type = "wallmounted",
135 | --wall_top = =
136 | --wall_bottom = =
137 | --wall_side = =
138 |
139 | },
140 | groups = {choppy=2, flammable=2, not_in_creative_inventory=1},
141 | sounds = default.node_sound_wood_defaults(),
142 | on_construct = function( pos )
143 | local timer = minetest.get_node_timer( pos )
144 | timer:start( 1 )
145 | end,
146 | on_timer = ladder_extender,
147 |
148 | after_destruct = function(pos)
149 | ropes.hanging_after_destruct(pos, "ropes:ropeladder_falling", "ropes:ropeladder", "ropes:ropeladder_bottom")
150 | end,
151 | })
152 |
153 | minetest.register_node("ropes:ropeladder_falling", {
154 | description = S("Rope Ladder"),
155 | _doc_items_create_entry = false,
156 | drop = "",
157 | drawtype = "signlike",
158 | tiles = {"default_ladder_wood.png^ropes_ropeladder.png"},
159 | use_texture_alpha = "clip",
160 | is_ground_content = false,
161 | inventory_image = "default_ladder_wood.png^ropes_ropeladder.png",
162 | wield_image = "default_ladder_wood.png^ropes_ropeladder.png",
163 | paramtype = "light",
164 | paramtype2 = "wallmounted",
165 | walkable = false,
166 | climbable = true,
167 | sunlight_propagates = true,
168 | selection_box = {
169 | type = "wallmounted",
170 | --wall_top = =
171 | --wall_bottom = =
172 | --wall_side = =
173 |
174 | },
175 | groups = {flammable=2, not_in_creative_inventory=1},
176 | sounds = default.node_sound_wood_defaults(),
177 | on_construct = function( pos )
178 | local timer = minetest.get_node_timer( pos )
179 | timer:start( 1 )
180 | end,
181 | on_timer = function( pos, elapsed )
182 | local pos_below = {x=pos.x, y=pos.y-1, z=pos.z}
183 | local node_below = minetest.get_node(pos_below)
184 |
185 | if (node_below.name ~= "ignore") then
186 | ropes.destroy_rope(pos_below, {'ropes:ropeladder', 'ropes:ropeladder_bottom', 'ropes:ropeladder_falling'})
187 | minetest.swap_node(pos, {name="air"})
188 | else
189 | local timer = minetest.get_node_timer( pos )
190 | timer:start( 1 )
191 | end
192 | end
193 | })
194 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/screenshot.png
--------------------------------------------------------------------------------
/settingtypes.txt:
--------------------------------------------------------------------------------
1 | #The shortest rope will extend for this many meters. Longer ropes come in
2 | #multiples of this length.
3 | #Changing this value will not affect ropes that already exist in-world.
4 | ropes_rope_length (Rope length) int 50 1 30000
5 |
6 | #Rope ladders will extend this far at maximum.
7 | #Changing this value will not affect rope ladders that already exist in-world.
8 | #Setting this to 0 disables rope ladders.
9 | ropes_rope_ladder_length (Rope ladder length) int 50 0 30000
10 |
11 | #Sets the maximum length multiple wooden rope box permitted to be crafted.
12 | #So for example if the rope length is set to 50 and this is set to 2,
13 | #the longest possible wooden rope box a player can craft has 100 meters of rope.
14 | #Allowed values run from 0 to 9. 0 disables wood rope boxes.
15 | ropes_wood_rope_box_max_multiple (Maximum wood_rope box multiple) int 2 0 9
16 |
17 | #Sets the maximum length multiple copper rope box permitted to be crafted.
18 | #So for example if the rope length is set to 50 and this is set to 5,
19 | #the longest possible copper rope box a player can craft has 250 meters of rope.
20 | #Allowed values run from 0 to 9. 0 disables copper rope boxes.
21 | ropes_copper_rope_box_max_multiple (Maximum copper rope box multiple) int 5 0 9
22 |
23 | #Sets the maximum length multiple steel rope box permitted to be crafted.
24 | #So for example if the rope length is set to 50 and this is set to 9,
25 | #the longest possible steel rope box a player can craft has 450 meters of rope.
26 | #Allowed values run from 0 to 9. 0 disables steel rope boxes.
27 | ropes_steel_rope_box_max_multiple (Maximum steel rope box multiple) int 9 0 9
28 |
29 | #If this is set to true, then the mod will generate definitions for the rope boxes
30 | #that are otherwise not permitted by the settings above. These rope boxes
31 | #will not be craftable and will not be available in the creative inventory,
32 | #but they will continue to exist if they were placed "in world" and a player
33 | #can deconstruct them to retrieve their rope components. This setting is
34 | #intended for the situation where you have an established world and you want
35 | #to reduce the number of rope boxes available to players without turning
36 | #existing rope boxes into "unknown node"s.
37 | ropes_create_all_definitions (Create all rope box definitions) bool false
38 |
39 | #Extending ladders are capable of standing on their own, to a defined limit.
40 | #A ladder can extend to its unsupported limit before needing another node
41 | #behind it to provide a new point of support. Right-clicking on an existing
42 | #ladder with a stack of ladders will add new ladder segments to its top.
43 | ropes_extending_ladder_enabled (Enable extendable ladders) bool true
44 |
45 | #If extending ladders are enabled, this setting will cause them to replace
46 | #the default ladders entirely.
47 | ropes_replace_default_ladders (Replace default ladders with extendable ladders) bool false
48 |
49 | ropes_extending_wood_ladder_limit (Unsupported limit of wooden ladders) int 5
50 | ropes_extending_steel_ladder_limit (Unsupported limit of steel ladders) int 15
51 |
52 | #These nodes make it easier to build bridges by extending out away
53 | #from the player as they're placed
54 | ropes_bridges_enabled (Enable bridges) bool true
55 |
56 | #Allows ropes and rope ladders to extend into all "airlike" nodes.
57 | #Note that ropes will leave "air" nodes behind when destroyed.
58 | ropes_can_extend_into_airlike (Ropes can extend into all nodes with drawtype airlike) bool false
--------------------------------------------------------------------------------
/sounds/license.txt:
--------------------------------------------------------------------------------
1 | ropes_creak.ogg - by jergonda from https://www.freesound.org/people/jergonda/sounds/254735/ under public domain via CC 0
--------------------------------------------------------------------------------
/sounds/ropes_creak.1.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/sounds/ropes_creak.1.ogg
--------------------------------------------------------------------------------
/sounds/ropes_creak.2.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/sounds/ropes_creak.2.ogg
--------------------------------------------------------------------------------
/sounds/ropes_creak.3.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/sounds/ropes_creak.3.ogg
--------------------------------------------------------------------------------
/textures/ropes_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_1.png
--------------------------------------------------------------------------------
/textures/ropes_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_2.png
--------------------------------------------------------------------------------
/textures/ropes_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_3.png
--------------------------------------------------------------------------------
/textures/ropes_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_4.png
--------------------------------------------------------------------------------
/textures/ropes_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_5.png
--------------------------------------------------------------------------------
/textures/ropes_item.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_item.png
--------------------------------------------------------------------------------
/textures/ropes_ropebox_front_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropebox_front_1.png
--------------------------------------------------------------------------------
/textures/ropes_ropebox_front_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropebox_front_2.png
--------------------------------------------------------------------------------
/textures/ropes_ropebox_front_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropebox_front_3.png
--------------------------------------------------------------------------------
/textures/ropes_ropebox_front_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropebox_front_4.png
--------------------------------------------------------------------------------
/textures/ropes_ropebox_front_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropebox_front_5.png
--------------------------------------------------------------------------------
/textures/ropes_ropebox_side.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropebox_side.png
--------------------------------------------------------------------------------
/textures/ropes_ropeladder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropeladder.png
--------------------------------------------------------------------------------
/textures/ropes_ropeladder_bottom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropeladder_bottom.png
--------------------------------------------------------------------------------
/textures/ropes_ropeladder_overlay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropeladder_overlay.png
--------------------------------------------------------------------------------
/textures/ropes_ropeladder_top.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/minetest-mods/ropes/a06e94e4e22795a3fc33c1a1b5435be579942338/textures/ropes_ropeladder_top.png
--------------------------------------------------------------------------------