Updated Cloudlands, and made compost bins work with hoppers.
parent
11a5200170
commit
51fbd54112
|
@ -6,31 +6,34 @@ local LOWLAND_BIOMES = false or -- If true then determine an island's bi
|
|||
local LOWLAND_BIOME_ALTITUDE = 10 -- Higher than beaches, lower than mountains (See LOWLAND_BIOMES)
|
||||
local VINE_COVERAGE = 0.3 -- set to 0 to turn off vines
|
||||
local REEF_RARITY = 0.015 -- Chance of a viable island having a reef or atoll
|
||||
local TREE_RARITY = 0.06 -- Chance of a viable island having a giant tree growing out of it
|
||||
local TREE_RARITY = 0.08 -- Chance of a viable island having a giant tree growing out of it
|
||||
local PORTAL_RARITY = 0.04 -- Chance of a viable island having some ancient portalstone on it (If portals API available and ENABLE_PORTALS is true)
|
||||
local BIOLUMINESCENCE = false or -- Allow giant trees variants which have glowing parts
|
||||
minetest.get_modpath("glowtest") ~= nil or
|
||||
minetest.get_modpath("ethereal") ~= nil or
|
||||
minetest.get_modpath("glow") ~= nil or
|
||||
minetest.get_modpath("nsspf") ~= nil or
|
||||
minetest.get_modpath("nightscape") ~= nil or
|
||||
minetest.get_modpath("moonflower") ~= nil -- a world using any of these mods is OK with bioluminescence
|
||||
local ENABLE_PORTALS = true -- Whether to allow players to build portals to islands. Portals require the Nether mod.
|
||||
local EDDYFIELD_SIZE = 1 -- size of the "eddy field-lines" that smaller islands follow
|
||||
local ISLANDS_SEED = 1000 -- You only need to change this if you want to try different island layouts without changing the map seed
|
||||
|
||||
-- Some lists of known node aliases (any nodes which can't be found won't be used).
|
||||
local NODENAMES_STONE = {"epic:stone", "mcl_core:stone", "default:stone"}
|
||||
local NODENAMES_WATER = {"mapgen_water_source", "mcl_core:water_source", "default:water_source"}
|
||||
local NODENAMES_ICE = {"mapgen_ice", "mcl_core:ice", "pedology:ice_white", "default:ice"}
|
||||
local NODENAMES_GRAVEL = {"mapgen_gravel", "mcl_core:gravel", "default:gravel"}
|
||||
local NODENAMES_GRASS = {"mapgen_dirt_with_grass", "mcl_core:dirt_with_grass", "default:dirt_with_grass"} -- currently only used with games that don't register biomes, e.g. Hades Revisted
|
||||
local NODENAMES_DIRT = {"mapgen_dirt", "mcl_core:dirt", "default:dirt"} -- currently only used with games that don't register biomes, e.g. Hades Revisted
|
||||
local NODENAMES_SILT = {"mapgen_silt", "default:silt", "aotearoa:silt", "darkage:silt", "mapgen_sand", "mcl_core:sand", "default:sand"} -- silt isn't a thing yet, but perhaps one day it will be. Use sand for the bottom of ponds in the meantime.
|
||||
local NODENAMES_VINES = {"mcl_core:vine", "vines:side_end", "ethereal:vine"} -- ethereal vines don't grow, so only select that if there's nothing else.
|
||||
local NODENAMES_STONE = {"epic:stone", "mapgen_stone", "mcl_core:stone", "default:stone", "main:stone"}
|
||||
local NODENAMES_WATER = {"mapgen_water_source", "mcl_core:water_source", "default:water_source", "main:water"}
|
||||
local NODENAMES_ICE = {"mapgen_ice", "mcl_core:ice", "pedology:ice_white", "default:ice", "main:ice"}
|
||||
local NODENAMES_GRAVEL = {"mapgen_gravel", "mcl_core:gravel", "default:gravel", "main:gravel"}
|
||||
local NODENAMES_GRASS = {"mapgen_dirt_with_grass", "mcl_core:dirt_with_grass", "default:dirt_with_grass", "main:grass"} -- currently only used with games that don't register biomes, e.g. Hades Revisted
|
||||
local NODENAMES_DIRT = {"mapgen_dirt", "mcl_core:dirt", "default:dirt", "main:dirt"} -- currently only used with games that don't register biomes, e.g. Hades Revisted
|
||||
local NODENAMES_SILT = {"mapgen_silt", "default:silt", "aotearoa:silt", "darkage:silt", "mapgen_sand", "mcl_core:sand", "default:sand", "main:sand"} -- silt isn't a thing yet, but perhaps one day it will be. Use sand for the bottom of ponds in the meantime.
|
||||
local NODENAMES_VINES = {"mcl_core:vine", "vines:side_end", "ethereal:vine", "main:vine"} -- ethereal vines don't grow, so only select that if there's nothing else.
|
||||
local NODENAMES_HANGINGVINE = {"vines:vine_end"}
|
||||
local NODENAMES_HANGINGROOT = {"vines:root_end"}
|
||||
local NODENAMES_TREEWOOD = {"mcl_core:tree", "default:tree", "mapgen_tree"}
|
||||
local NODENAMES_TREELEAVES = {"mcl_core:leaves", "default:leaves", "mapgen_leaves"}
|
||||
local NODENAMES_TREEWOOD = {"mcl_core:tree", "default:tree", "mapgen_tree", "main:tree"}
|
||||
local NODENAMES_TREELEAVES = {"mcl_core:leaves", "default:leaves", "mapgen_leaves", "main:leaves"}
|
||||
local NODENAMES_FRAMEGLASS = {"xpanes:obsidian_pane_flat", "xpanes:pane_flat", "default:glass", "xpanes:pane_natural_flat", "mcl_core:glass", "walls:window"}
|
||||
local NODENAMES_WOOD = {"default:wood", "mcl_core:wood", "main:wood"}
|
||||
|
||||
local MODNAME = minetest.get_current_modname()
|
||||
local VINES_REQUIRED_HUMIDITY = 49
|
||||
|
@ -48,7 +51,7 @@ local DEBUG_SKYTREES = false -- dev logging
|
|||
-- notice problems requiring it.
|
||||
local OVERDRAW = 0
|
||||
|
||||
local S = minetest.get_translator(minetest.get_current_modname())
|
||||
local S = minetest.get_translator(MODNAME)
|
||||
|
||||
cloudlands = {} -- API functions can be accessed via this global:
|
||||
-- cloudlands.get_island_details(minp, maxp) -- returns an array of island-information-tables, y is ignored.
|
||||
|
@ -182,6 +185,7 @@ local nodeId_silt
|
|||
local nodeId_gravel
|
||||
local nodeId_vine
|
||||
local nodeName_vine
|
||||
local nodeName_ignore = minetest.get_name_from_content_id(nodeId_ignore)
|
||||
|
||||
local REQUIRED_DENSITY = 0.4
|
||||
|
||||
|
@ -241,7 +245,7 @@ if isMapgenV6 then
|
|||
end
|
||||
|
||||
local interop = {}
|
||||
-- returns the id of the first name in the list that resolves to a node id, or nodeId_ignore if not found
|
||||
-- returns the id of the first nodename in the list that resolves to a node id, or nodeId_ignore if not found
|
||||
interop.find_node_id = function (node_contender_names)
|
||||
local result = nodeId_ignore
|
||||
for _,contenderName in ipairs(node_contender_names) do
|
||||
|
@ -251,17 +255,37 @@ interop.find_node_id = function (node_contender_names)
|
|||
result = minetest.get_content_id(nonAliasName)
|
||||
end
|
||||
|
||||
--if DEBUG then minetest.log("info", contenderName .. " returned " .. result) end
|
||||
--if DEBUG then minetest.log("info", contenderName .. " returned " .. result .. " (" .. minetest.get_name_from_content_id(result) .. ")") end
|
||||
if result ~= nodeId_ignore then return result end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- returns the name of the first name in the list that resolves to a node id, or 'ignore' if not found
|
||||
-- returns the name of the first nodename in the list that resolves to a node id, or 'ignore' if not found
|
||||
interop.find_node_name = function (node_contender_names)
|
||||
return minetest.get_name_from_content_id(interop.find_node_id(node_contender_names))
|
||||
end
|
||||
|
||||
interop.get_first_element_in_table = function(tbl)
|
||||
for k,v in pairs(tbl) do return v end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- returns the top-texture name of the first nodename in the list that's a registered node, or nil if not found
|
||||
interop.find_node_texture = function (node_contender_names)
|
||||
local result = nil
|
||||
local nodeName = minetest.get_name_from_content_id(interop.find_node_id(node_contender_names))
|
||||
if nodeName ~= nil then
|
||||
local node = minetest.registered_nodes[nodeName]
|
||||
if node ~= nil then
|
||||
result = node.tiles
|
||||
if type(result) == "table" then result = result["name"] or interop.get_first_element_in_table(result) end -- incase it's not a string
|
||||
if type(result) == "table" then result = result["name"] or interop.get_first_element_in_table(result) end -- incase multiple tile definitions
|
||||
end
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- returns the node name of the clone node.
|
||||
interop.register_clone = function(node_name, clone_name)
|
||||
local node = minetest.registered_nodes[node_name]
|
||||
|
@ -316,6 +340,43 @@ interop.file_exists = function(filename)
|
|||
end
|
||||
end
|
||||
|
||||
-- returns a written book item (technically an item stack), or nil if no books mod available
|
||||
interop.write_book = function(title, author, text, description)
|
||||
|
||||
local stackName_writtenBook
|
||||
if minetest.get_modpath("mcl_books") then
|
||||
stackName_writtenBook = "mcl_books:written_book"
|
||||
text = title .. "\n\n" .. text -- MineClone2 books doen't show a title (or author)
|
||||
|
||||
elseif minetest.get_modpath("book") ~= nil then
|
||||
stackName_writtenBook = "book:book_written"
|
||||
text = "\n\n" .. text -- Crafter books put the text immediately under the title
|
||||
|
||||
elseif minetest.get_modpath("default") ~= nil then
|
||||
stackName_writtenBook = "default:book_written"
|
||||
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
local book_itemstack = ItemStack(stackName_writtenBook)
|
||||
local book_data = {}
|
||||
book_data.title = title
|
||||
book_data.text = text
|
||||
book_data.owner = author
|
||||
book_data.author = author
|
||||
book_data.description = description
|
||||
book_data.page = 1
|
||||
book_data.page_max = 1
|
||||
book_data.generation = 0
|
||||
book_data["book.book_title"] = title -- Crafter book title
|
||||
book_data["book.book_text"] = text -- Crafter book text
|
||||
|
||||
book_itemstack:get_meta():from_table({fields = book_data})
|
||||
|
||||
return book_itemstack
|
||||
end
|
||||
|
||||
--[[==============================
|
||||
Portals
|
||||
==============================]]--
|
||||
|
@ -405,6 +466,7 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
|
|||
-- Ideally the Nether mod will provide a block obtainable by exploring the Nether which is
|
||||
-- earmarked for mods like this one to use for portals, but until this happens I'll create
|
||||
-- our own tempory placeholder "portalstone".
|
||||
-- The Portals API is currently provided by nether, which depends on default, so we can assume default textures are available
|
||||
local portalstone_end = "default_furnace_top.png^(default_ice.png^[opacity:120)^[multiply:#668" -- this gonna look bad with non-default texturepacks, hopefully Nether mod will provide a real block
|
||||
local portalstone_side = "[combine:16x16:0,0=default_furnace_top.png:4,0=default_furnace_top.png:8,0=default_furnace_top.png:12,0=default_furnace_top.png:^(default_ice.png^[opacity:120)^[multiply:#668"
|
||||
minetest.register_node("cloudlands:ancient_portalstone", {
|
||||
|
@ -459,17 +521,18 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
|
|||
rotation = "random"
|
||||
})
|
||||
|
||||
-- this uses place_schematic() without minetest.after(), so should be called after vm:write_to_map()
|
||||
addDetail_ancientPortal = function(core)
|
||||
|
||||
if (core.radius < 8 or PORTAL_RARITY == 0) then return false end -- avoid portals hanging off the side of small islands
|
||||
|
||||
local fastHash = 3
|
||||
fastHash = (37 * fastHash) + 9354 -- to keep this probability distinct from reefs and atols
|
||||
fastHash = (37 * fastHash) + ISLANDS_SEED
|
||||
fastHash = (37 * fastHash) + core.x
|
||||
fastHash = (37 * fastHash) + core.z
|
||||
fastHash = (37 * fastHash) + math_floor(core.radius)
|
||||
fastHash = (37 * fastHash) + math_floor(core.depth)
|
||||
fastHash = (37 * fastHash) + ISLANDS_SEED
|
||||
fastHash = (37 * fastHash) + 9354 -- to keep this probability distinct from reefs and atols
|
||||
if (PORTAL_RARITY * 10000) < math_floor((math_abs(fastHash)) % 10000) then return false end
|
||||
|
||||
local portalPos = find_potential_portal_location_on_island(core)
|
||||
|
@ -521,10 +584,12 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
|
|||
S("The only portal we managed to scavenge enough portalstone to build took us to a land of floating islands. There were hills and forests and even water up there, but the edges are a perilous drop — a depth of which we cannot even begin to plumb."),
|
||||
|
||||
is_within_realm = function(pos)
|
||||
cloudlands.init()
|
||||
-- return true if pos is in the cloudlands
|
||||
-- I'm doing this based off height for speed, so it sometimes gets it wrong when the
|
||||
-- Hallelujah mountains start reaching the ground.
|
||||
local largestCoreType = cloudlands.coreTypes[1] -- the first island type is the biggest/thickest
|
||||
--local island_bottom = ALTITUDE - (largestCoreType.depthMax * 0.66) + round(noise_heightMap:get2d({x = pos.x, y = pos.z}))
|
||||
local island_bottom = ALTITUDE - (largestCoreType.depthMax * 0.66) + round(noise_heightMap:get2d({x = pos.x, y = pos.z}))
|
||||
return pos.y > math_max(40, island_bottom)
|
||||
end,
|
||||
|
@ -556,7 +621,7 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
|
|||
-- back to find_surface_target_y.
|
||||
|
||||
-- Using -100000 for y to ensure the position is outside the cloudlands realm and so
|
||||
-- find_nearest_working_portal() will only returns surface portals.
|
||||
-- find_nearest_working_portal() will only returns ground portals.
|
||||
-- a y_factor of 0 makes the search ignore the -100000 altitude of the portals (as
|
||||
-- long as they are outside the cloudlands realm)
|
||||
local existing_portal_location, existing_portal_orientation =
|
||||
|
@ -612,6 +677,7 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
|
|||
end
|
||||
|
||||
})
|
||||
|
||||
end
|
||||
|
||||
--[[==============================
|
||||
|
@ -693,6 +759,11 @@ if not minetest.global_exists("SkyTrees") then -- If SkyTrees added into other m
|
|||
for key, value in pairs(trunkNode) do newTrunkNode[key] = value end
|
||||
newTrunkNode.name = SkyTrees.MODNAME .. ":" .. nodesuffix
|
||||
newTrunkNode.description = description
|
||||
if newTrunkNode.paramtype2 == nil then newTrunkNode.paramtype2 = "facedir" end
|
||||
if newTrunkNode.on_dig ~= nil and minetest.get_modpath("main") then
|
||||
newTrunkNode.on_dig = nil -- Crafter has special trunk auto-digging logic that doesn't make sense for giant trees
|
||||
end
|
||||
|
||||
if dropsTemplateWood then
|
||||
newTrunkNode.drop = nodeName_templateWood
|
||||
if newTrunkNode.groups == nil then newTrunkNode.groups = {} end
|
||||
|
@ -931,7 +1002,8 @@ if not minetest.global_exists("SkyTrees") then -- If SkyTrees added into other m
|
|||
{
|
||||
tiles = { heartwoodTexture },
|
||||
description = S("Heart of the Tree"),
|
||||
groups = {oddly_breakable_by_hand = 3},
|
||||
groups = {oddly_breakable_by_hand = 3, handy = 1},
|
||||
_mcl_hardness = 0.4,
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
light_source = heartwoodGlow, -- plants can grow under the heart of the Tree
|
||||
|
@ -1167,7 +1239,7 @@ if not minetest.global_exists("SkyTrees") then -- If SkyTrees added into other m
|
|||
if rotation ~= 0 then schematicCoords = rotatePositon(schematicCoords, schematicInfo.size, rotation) end
|
||||
local nodePos = vector.add(treePos, schematicCoords)
|
||||
local nodeToConstruct = minetest.get_node(nodePos)
|
||||
if nodeToConstruct.name == "air" or nodeToConstruct.name == "ignore" then
|
||||
if nodeToConstruct.name == "air" or nodeToConstruct.name == nodeName_ignore then
|
||||
--this is now normal - e.g. if vines are set to 'ignore' then the nodeToConstruct won't be there.
|
||||
--minetest.log("error", "nodesWithConstructor["..i.."] does not match schematic " .. schematicInfo.filename .. " at " .. nodePos.x..","..nodePos.y..","..nodePos.z.." rotation "..rotation)
|
||||
else
|
||||
|
@ -1701,7 +1773,7 @@ local function addDetail_skyReef(decoration_list, core, data, area, minp, maxp)
|
|||
fastHash = (37 * fastHash) + math_floor(core.depth)
|
||||
if ISLANDS_SEED ~= 1000 then fastHash = (37 * fastHash) + ISLANDS_SEED end
|
||||
local rarityAdj = 1
|
||||
if core.type.requiresNexus and isAtoll then rarityAdj = 4 end -- humongous islands are very rare, and look good as a atoll
|
||||
if core.type.requiresNexus and isAtoll then rarityAdj = 4 end -- humongous islands are very rare, and look good as an atoll
|
||||
if (REEF_RARITY * rarityAdj * 1000) < math_floor((math_abs(fastHash)) % 1000) then return false end
|
||||
|
||||
local coreX = core.x --save doing a table lookup in the loop
|
||||
|
@ -1915,14 +1987,13 @@ end
|
|||
-- We might not need this stand-in cobweb, but unless we go overboard on listing many
|
||||
-- optional dependencies we won't know whether there's a proper cobweb available to
|
||||
-- use until after it's too late to register this one.
|
||||
if minetest.get_modpath("default") then
|
||||
-- the crack texture is probably available
|
||||
local nodeName_standinCobweb = MODNAME .. ":cobweb"
|
||||
minetest.register_node(
|
||||
nodeName_standinCobweb,
|
||||
{
|
||||
tiles = {
|
||||
-- [Ab]Use the crack texture to avoid needing to include a cobweb texture
|
||||
-- crack_anylength.png is required by the engine, so all games will have it.
|
||||
"crack_anylength.png^[verticalframe:5:4^[brighten"
|
||||
},
|
||||
description = S("Cobweb"),
|
||||
|
@ -1939,24 +2010,37 @@ if minetest.get_modpath("default") then
|
|||
paramtype = "light"
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
local nodeName_egg = "secret:fossilized_egg"
|
||||
local eggTextureName = "default_jungleleaves.png" -- called this in default/Voxelgarden/MineClone2
|
||||
if minetest.get_modpath("ethereal") ~= nil then eggTextureName = "ethereal_frost_leaves.png" end -- called "ethereal_frost_leaves.png" in ethereal
|
||||
local eggTextureBaseName = interop.find_node_texture({"default:jungleleaves", "mcl_core:jungleleaves", "ethereal:frost_leaves", "main:leaves"})
|
||||
|
||||
-- [Ab]Use a leaf texture. Originally this was to avoid needing to include an egg texture (extra files) and
|
||||
-- exposing that the mod contains secrets, however both those reasons are obsolete and the mod could have textures
|
||||
-- added in future
|
||||
local eggTextureName = eggTextureBaseName.."^[colorize:#280040E0^[noalpha"
|
||||
|
||||
-- Since "secret:fossilized_egg" doesn't use this mod's name for the prefix, we can't assume
|
||||
-- another mod isn't also using/providing it
|
||||
if minetest.registered_nodes[nodeName_egg] == nil then
|
||||
|
||||
local fossilSounds = nil
|
||||
local nodeName_stone = interop.find_node_name(NODENAMES_STONE)
|
||||
if nodeName_stone ~= nodeName_ignore then fossilSounds = minetest.registered_nodes[nodeName_stone].sounds end
|
||||
|
||||
minetest.register_node(
|
||||
":"..nodeName_egg,
|
||||
{
|
||||
tiles = {
|
||||
-- [Ab]Use a leaf texture to avoid needing to include an egg texture and exposing that the mod contains secrets
|
||||
eggTextureName.."^[colorize:#280040E0^[noalpha"
|
||||
},
|
||||
tiles = { eggTextureName },
|
||||
description = S("Fossilized Egg"),
|
||||
groups = {oddly_breakable_by_hand = 3, not_in_creative_inventory = 1},
|
||||
groups = {
|
||||
oddly_breakable_by_hand = 3, -- MTG
|
||||
handy = 1, -- MCL
|
||||
stone = 1, -- Crafter needs to know the material in order to be breakable by hand
|
||||
not_in_creative_inventory = 1
|
||||
},
|
||||
_mcl_hardness = 0.4,
|
||||
sounds = fossilSounds,
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
|
@ -1972,6 +2056,86 @@ if minetest.registered_nodes[nodeName_egg] == nil then
|
|||
}
|
||||
)
|
||||
end
|
||||
|
||||
-- Allow the player to craft their egg into an egg in a display case
|
||||
local nodeName_eggDisplay = nodeName_egg .. "_display"
|
||||
local nodeName_frameGlass = interop.find_node_name(NODENAMES_FRAMEGLASS)
|
||||
local woodTexture = interop.find_node_texture(NODENAMES_WOOD)
|
||||
local frameTexture = nil
|
||||
if woodTexture ~= nil then
|
||||
-- perhaps it's time for cloudlands to contain textures.
|
||||
frameTexture = "([combine:16x16:0,0="..woodTexture.."\\^[colorize\\:black\\:170:1,1="..woodTexture.."\\^[colorize\\:#0F0\\:255\\^[resize\\:14x14^[makealpha:0,255,0)"
|
||||
end
|
||||
|
||||
-- Since "secret:fossilized_egg_display" doesn't use this mod's name as the prefix, we shouldn't
|
||||
-- assume another mod isn't also using/providing it.
|
||||
if frameTexture ~= nil and nodeName_frameGlass ~= nodeName_ignore and minetest.registered_nodes[nodeName_eggDisplay] == nil then
|
||||
minetest.register_node(
|
||||
":"..nodeName_eggDisplay,
|
||||
{
|
||||
tiles = { eggTextureName .. "^" .. frameTexture },
|
||||
description = S("Fossil Display"),
|
||||
groups = {
|
||||
oddly_breakable_by_hand = 3,
|
||||
glass = 1, -- Crafter needs to know the material in order to be breakable by hand
|
||||
not_in_creative_inventory = 1},
|
||||
_mcl_hardness = 0.2,
|
||||
drop = "",
|
||||
sounds = minetest.registered_nodes[nodeName_frameGlass].sounds,
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.066666, -0.5, -0.066666, 0.066666, 0.4375, 0.066666}, -- column1
|
||||
{-0.133333, -0.5, -0.133333, 0.133333, 0.375, 0.133333}, -- column2
|
||||
{-0.2, -0.4375, -0.2, 0.2, 0.285, 0.2 }, -- column3
|
||||
{-0.2, -0.36, -0.28, 0.2, 0.14, 0.28 }, -- side1
|
||||
{-0.28, -0.36, -0.2, 0.28, 0.14, 0.2 }, -- side2
|
||||
|
||||
-- corner frame (courtesy of NodeBox Editor Abuse mod)
|
||||
{-0.4375, 0.4375, 0.4375, 0.4375, 0.5, 0.5},
|
||||
{-0.4375, -0.5, 0.4375, 0.4375, -0.4375, 0.5},
|
||||
{-0.5, -0.5, 0.4375, -0.4375, 0.5, 0.5},
|
||||
{0.4375, -0.5, 0.4375, 0.5, 0.5, 0.5},
|
||||
{-0.5, 0.4375, -0.4375, -0.4375, 0.5, 0.4375},
|
||||
{-0.5, -0.5, -0.4375, -0.4375, -0.4375, 0.4375},
|
||||
{0.4375, 0.4375, -0.4375, 0.5, 0.5, 0.4375},
|
||||
{0.4375, -0.5, -0.4375, 0.5, -0.4375, 0.4375},
|
||||
{-0.5, 0.4375, -0.5, 0.5, 0.5, -0.4375},
|
||||
{-0.5, -0.5, -0.5, 0.5, -0.4375, -0.4375},
|
||||
{0.4375, -0.4375, -0.5, 0.5, 0.4375, -0.4375},
|
||||
{-0.5, -0.4375, -0.5, -0.4375, 0.4375, -0.4375}
|
||||
}
|
||||
},
|
||||
after_destruct = function(pos,node)
|
||||
minetest.set_node(pos, {name = nodeName_egg, param2 = node.param2})
|
||||
end,
|
||||
}
|
||||
)
|
||||
|
||||
if minetest.get_modpath("xpanes") ~= nil then
|
||||
minetest.register_craft({
|
||||
output = nodeName_eggDisplay,
|
||||
recipe = {
|
||||
{"group:stick", "group:pane", "group:stick"},
|
||||
{"group:pane", nodeName_egg, "group:pane"},
|
||||
{"group:stick", "group:pane", "group:stick"}
|
||||
}
|
||||
})
|
||||
else
|
||||
-- Game doesn't have glass panes, so just use glass
|
||||
minetest.register_craft({
|
||||
output = nodeName_eggDisplay,
|
||||
recipe = {
|
||||
{"group:stick", nodeName_frameGlass, "group:stick"},
|
||||
{nodeName_frameGlass, nodeName_egg, nodeName_frameGlass},
|
||||
{"group:stick", nodeName_frameGlass, "group:stick"}
|
||||
}
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local nodeId_egg = minetest.get_content_id(nodeName_egg)
|
||||
local nodeId_airStandIn = minetest.get_content_id(interop.register_clone("air"))
|
||||
|
||||
|
@ -2308,15 +2472,10 @@ local function addDetail_secrets(decoration_list, core, data, area, minp, maxp)
|
|||
end
|
||||
end
|
||||
|
||||
local stackName_writtenBook = "default:book_written"
|
||||
if isMineCloneBookshelf then stackName_writtenBook = "mcl_books:written_book" end
|
||||
|
||||
local book_itemstack = ItemStack(stackName_writtenBook)
|
||||
local book_data = {}
|
||||
book_data.title = S("Weddell Outpost")
|
||||
-- Instead of being a stand-alone line, the McNish line is tacked on the end of the
|
||||
-- journey sentence because otherwise it gets truncated off by default:book_written
|
||||
book_data.text = S([[The aerostat is lost.
|
||||
local book_itemstack = interop.write_book(
|
||||
S("Weddell Outpost, November 21"), -- title
|
||||
S("Bert Shackleton"), -- owner/author
|
||||
S([[The aerostat is lost.
|
||||
|
||||
However, salvage attempts throughout the night managed to
|
||||
save most provisions before it finally broke apart and fell.
|
||||
|
@ -2331,18 +2490,11 @@ is becoming cause for concern.
|
|||
Quite a journey is now required, we cannot stay - nobody will
|
||||
look for us here. McNish is attempting to strengthen the gliders.
|
||||
|
||||
---====---
|
||||
]], groundDesc)
|
||||
|
||||
if isMineCloneBookshelf then book_data.text = book_data.title .. "\n\n" .. book_data.text end -- MineClone2 doesn't show the title
|
||||
book_data.owner = S("Bert Shackleton")
|
||||
book_data.author = book_data.owner
|
||||
book_data.description = S("Diary of Bert Shackleton")
|
||||
book_data.page = 1
|
||||
book_data.page_max = 1
|
||||
book_data.generation = 0
|
||||
book_itemstack:get_meta():from_table({fields = book_data})
|
||||
---====---]], groundDesc),
|
||||
S("Diary of Bert Shackleton") -- description
|
||||
)
|
||||
|
||||
if book_itemstack ~= nil then
|
||||
if invBookshelf == nil then
|
||||
-- mineclone bookshelves are decorational like Minecraft, put the book in the chest instead
|
||||
-- (also testing for nil invBookshelf because it can happen. Weird race condition??)
|
||||
|
@ -2356,6 +2508,7 @@ look for us here. McNish is attempting to strengthen the gliders.
|
|||
minetest.registered_nodes[nodeName_bookshelf].on_metadata_inventory_put(bookshelf_pos, "books", 1, book_itemstack, dummyPlayer)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if invChest ~= nil then
|
||||
-- leave some junk from the expedition in the chest
|
||||
|
@ -2369,10 +2522,10 @@ look for us here. McNish is attempting to strengthen the gliders.
|
|||
end
|
||||
end
|
||||
end
|
||||
addIfFound({"mcl_tools:pick_iron", "default:pick_steel"}, 1)
|
||||
addIfFound({"mcl_tools:pick_iron", "default:pick_steel", "main:ironpick"}, 1)
|
||||
addIfFound({"binoculars:binoculars"}, 1)
|
||||
addIfFound({"mcl_core:wood", "default:wood"}, 10)
|
||||
addIfFound({"mcl_torches:torch", "default:torch"}, 3)
|
||||
addIfFound(NODENAMES_WOOD, 10)
|
||||
addIfFound({"mcl_torches:torch", "default:torch", "torch:torch"}, 3)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -2382,26 +2535,25 @@ look for us here. McNish is attempting to strengthen the gliders.
|
|||
end
|
||||
|
||||
local function init_secrets()
|
||||
nodeId_bed_top = interop.find_node_id({"beds:bed_top"})
|
||||
nodeId_bed_bottom = interop.find_node_id({"beds:bed_bottom"})
|
||||
nodeId_torch = interop.find_node_id({"mcl_torches:torch_wall", "default:torch_wall"})
|
||||
nodeId_chest = interop.find_node_id({"chest", "mcl_chests:chest", "default:chest"})
|
||||
nodeId_bed_top = interop.find_node_id({"beds:bed_top", "bed:bed_front"})
|
||||
nodeId_bed_bottom = interop.find_node_id({"beds:bed_bottom", "bed:bed_back"})
|
||||
nodeId_torch = interop.find_node_id({"mcl_torches:torch_wall", "default:torch_wall", "torch:wall"})
|
||||
nodeId_chest = interop.find_node_id({"chest", "mcl_chests:chest", "default:chest", "utility:chest"})
|
||||
nodeId_junk = interop.find_node_id({"xdecor:barrel", "cottages:barrel", "homedecor:copper_pans", "vessels:steel_bottle", "mcl_flowerpots:flower_pot"})
|
||||
nodeId_anvil = interop.find_node_id({"castle:anvil", "cottages:anvil", "mcl_anvils:anvil", "default:anvil" }) -- "default:anvil" isn't a thing, but perhaps one day.
|
||||
nodeId_workbench = interop.find_node_id({"homedecor:table", "xdecor:workbench", "mcl_crafting_table:crafting_table", "default:table", "random_buildings:bench"}) -- "default:table" isn't a thing, but perhaps one day.
|
||||
nodeId_cobweb = interop.find_node_id({"mcl_core:cobweb", "xdecor:cobweb", "homedecor:cobweb_plantlike", "default:cobweb"})
|
||||
nodeId_anvil = interop.find_node_id({"castle:anvil", "cottages:anvil", "mcl_anvils:anvil", "default:anvil", "main:anvil" }) -- "default:anvil" and "main:anvil" aren't a thing, but perhaps one day.
|
||||
nodeId_workbench = interop.find_node_id({"homedecor:table", "xdecor:workbench", "mcl_crafting_table:crafting_table", "default:table", "random_buildings:bench", "craftingtable:craftingtable"}) -- "default:table" isn't a thing, but perhaps one day.
|
||||
nodeId_cobweb = interop.find_node_id({"mcl_core:cobweb", "xdecor:cobweb", "homedecor:cobweb_plantlike", "default:cobweb", "main:cobweb"})
|
||||
|
||||
local mineCloneBookshelfName = "mcl_books:bookshelf"
|
||||
nodeId_bookshelf = interop.find_node_id({mineCloneBookshelfName, "default:bookshelf"})
|
||||
nodeName_bookshelf = minetest.get_name_from_content_id(nodeId_bookshelf)
|
||||
isMineCloneBookshelf = nodeName_bookshelf == mineCloneBookshelfName
|
||||
|
||||
local nodeName_standinCobweb = MODNAME .. ":cobweb"
|
||||
if nodeId_cobweb ~= nodeId_ignore then
|
||||
-- This game has proper cobwebs, replace any cobwebs this mod may have generated
|
||||
-- previously (when a cobweb mod wasn't included) with the proper cobwebs.
|
||||
minetest.register_alias(nodeName_standinCobweb, minetest.get_name_from_content_id(nodeId_cobweb))
|
||||
else
|
||||
elseif minetest.registered_nodes[nodeName_standinCobweb] ~= nil then
|
||||
-- use a stand-in cobweb created by this mod
|
||||
nodeId_cobweb = minetest.get_content_id(nodeName_standinCobweb)
|
||||
end
|
||||
|
@ -2592,7 +2744,7 @@ local function renderCores(cores, minp, maxp, blockseed)
|
|||
local pondWallBuffer = core.type.pondWallBuffer
|
||||
local pondBottom = nodeId_filler
|
||||
local pondWater = nodeId_water
|
||||
if radius > 18 and core.depth > 15 and nodeId_silt ~= nodeId_ignore then
|
||||
if radius > 18 and core.depth > 15 and nodeId_pondBottom ~= nodeId_ignore then
|
||||
-- only give ponds a sandbed when islands are large enough for it not to stick out the side or bottom
|
||||
pondBottom = nodeId_pondBottom
|
||||
end
|
||||
|
@ -2650,11 +2802,10 @@ local function renderCores(cores, minp, maxp, blockseed)
|
|||
|
||||
for _,core in ipairs(cores) do
|
||||
addDetail_skyTree(decorations, core, minp, maxp)
|
||||
if addDetail_ancientPortal ~= nil then addDetail_ancientPortal(core) end
|
||||
end
|
||||
for _,decoration in ipairs(decorations) do
|
||||
local nodeAtPos = minetest.get_node(decoration.pos)
|
||||
if nodeAtPos.name == "air" or nodeAtPos.name == "ignore" then minetest.set_node(decoration.pos, decoration.node) end
|
||||
if nodeAtPos.name == "air" or nodeAtPos.name == nodeName_ignore then minetest.set_node(decoration.pos, decoration.node) end
|
||||
end
|
||||
|
||||
local dustingInProgress = false
|
||||
|
@ -2733,6 +2884,11 @@ local function renderCores(cores, minp, maxp, blockseed)
|
|||
vm:set_lighting({day=15, night=0}, brightMin, brightMax)
|
||||
|
||||
vm:write_to_map() -- seems to be unnecessary when other mods that use vm are running
|
||||
|
||||
for _,core in ipairs(cores) do
|
||||
-- place any schematics which should be placed after the landscape
|
||||
if addDetail_ancientPortal ~= nil then addDetail_ancientPortal(core) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,426 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Script to generate the template file and update the translation files.
|
||||
# Copy the script into the mod or modpack root folder and run it there.
|
||||
#
|
||||
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
|
||||
# LGPLv2.1+
|
||||
#
|
||||
# See https://github.com/minetest-tools/update_translations for
|
||||
# potential future updates to this script.
|
||||
|
||||
from __future__ import print_function
|
||||
import os, fnmatch, re, shutil, errno
|
||||
from sys import argv as _argv
|
||||
|
||||
# Running params
|
||||
params = {"recursive": False,
|
||||
"help": False,
|
||||
"mods": False,
|
||||
"verbose": False,
|
||||
"folders": [],
|
||||
"no-old-file": False
|
||||
}
|
||||
# Available CLI options
|
||||
options = {"recursive": ['--recursive', '-r'],
|
||||
"help": ['--help', '-h'],
|
||||
"mods": ['--installed-mods'],
|
||||
"verbose": ['--verbose', '-v'],
|
||||
"no-old-file": ['--no-old-file']
|
||||
}
|
||||
|
||||
# Strings longer than this will have extra space added between
|
||||
# them in the translation files to make it easier to distinguish their
|
||||
# beginnings and endings at a glance
|
||||
doublespace_threshold = 60
|
||||
|
||||
def set_params_folders(tab: list):
|
||||
'''Initialize params["folders"] from CLI arguments.'''
|
||||
# Discarding argument 0 (tool name)
|
||||
for param in tab[1:]:
|
||||
stop_param = False
|
||||
for option in options:
|
||||
if param in options[option]:
|
||||
stop_param = True
|
||||
break
|
||||
if not stop_param:
|
||||
params["folders"].append(os.path.abspath(param))
|
||||
|
||||
def set_params(tab: list):
|
||||
'''Initialize params from CLI arguments.'''
|
||||
for option in options:
|
||||
for option_name in options[option]:
|
||||
if option_name in tab:
|
||||
params[option] = True
|
||||
break
|
||||
|
||||
def print_help(name):
|
||||
'''Prints some help message.'''
|
||||
print(f'''SYNOPSIS
|
||||
{name} [OPTIONS] [PATHS...]
|
||||
DESCRIPTION
|
||||
{', '.join(options["help"])}
|
||||
prints this help message
|
||||
{', '.join(options["recursive"])}
|
||||
run on all subfolders of paths given
|
||||
{', '.join(options["mods"])}
|
||||
run on locally installed modules
|
||||
{', '.join(options["no-old-file"])}
|
||||
do not create *.old files
|
||||
{', '.join(options["verbose"])}
|
||||
add output information
|
||||
''')
|
||||
|
||||
|
||||
def main():
|
||||
'''Main function'''
|
||||
set_params(_argv)
|
||||
set_params_folders(_argv)
|
||||
if params["help"]:
|
||||
print_help(_argv[0])
|
||||
elif params["recursive"] and params["mods"]:
|
||||
print("Option --installed-mods is incompatible with --recursive")
|
||||
else:
|
||||
# Add recursivity message
|
||||
print("Running ", end='')
|
||||
if params["recursive"]:
|
||||
print("recursively ", end='')
|
||||
# Running
|
||||
if params["mods"]:
|
||||
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
|
||||
run_all_subfolders("~/.minetest/mods")
|
||||
elif len(params["folders"]) >= 2:
|
||||
print("on folder list:", params["folders"])
|
||||
for f in params["folders"]:
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(f)
|
||||
else:
|
||||
update_folder(f)
|
||||
elif len(params["folders"]) == 1:
|
||||
print("on folder", params["folders"][0])
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(params["folders"][0])
|
||||
else:
|
||||
update_folder(params["folders"][0])
|
||||
else:
|
||||
print("on folder", os.path.abspath("./"))
|
||||
if params["recursive"]:
|
||||
run_all_subfolders(os.path.abspath("./"))
|
||||
else:
|
||||
update_folder(os.path.abspath("./"))
|
||||
|
||||
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
|
||||
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
|
||||
pattern_lua = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
|
||||
pattern_lua_bracketed = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
|
||||
|
||||
# Handles "concatenation" .. " of strings"
|
||||
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
|
||||
|
||||
pattern_tr = re.compile(r'(.+?[^@])=(.*)')
|
||||
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
|
||||
pattern_tr_filename = re.compile(r'\.tr$')
|
||||
pattern_po_language_code = re.compile(r'(.*)\.po$')
|
||||
|
||||
#attempt to read the mod's name from the mod.conf file. Returns None on failure
|
||||
def get_modname(folder):
|
||||
try:
|
||||
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
|
||||
for line in mod_conf:
|
||||
match = pattern_name.match(line)
|
||||
if match:
|
||||
return match.group(1)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return None
|
||||
|
||||
#If there are already .tr files in /locale, returns a list of their names
|
||||
def get_existing_tr_files(folder):
|
||||
out = []
|
||||
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
|
||||
for name in files:
|
||||
if pattern_tr_filename.search(name):
|
||||
out.append(name)
|
||||
return out
|
||||
|
||||
# A series of search and replaces that massage a .po file's contents into
|
||||
# a .tr file's equivalent
|
||||
def process_po_file(text):
|
||||
# The first three items are for unused matches
|
||||
text = re.sub(r'#~ msgid "', "", text)
|
||||
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
|
||||
text = re.sub(r'"\n#~ msgstr "', "=", text)
|
||||
# comment lines
|
||||
text = re.sub(r'#.*\n', "", text)
|
||||
# converting msg pairs into "=" pairs
|
||||
text = re.sub(r'msgid "', "", text)
|
||||
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
|
||||
text = re.sub(r'"\nmsgstr "', "=", text)
|
||||
# various line breaks and escape codes
|
||||
text = re.sub(r'"\n"', "", text)
|
||||
text = re.sub(r'"\n', "\n", text)
|
||||
text = re.sub(r'\\"', '"', text)
|
||||
text = re.sub(r'\\n', '@n', text)
|
||||
# remove header text
|
||||
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
|
||||
# remove double-spaced lines
|
||||
text = re.sub(r'\n\n', '\n', text)
|
||||
return text
|
||||
|
||||
# Go through existing .po files and, if a .tr file for that language
|
||||
# *doesn't* exist, convert it and create it.
|
||||
# The .tr file that results will subsequently be reprocessed so
|
||||
# any "no longer used" strings will be preserved.
|
||||
# Note that "fuzzy" tags will be lost in this process.
|
||||
def process_po_files(folder, modname):
|
||||
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
|
||||
for name in files:
|
||||
code_match = pattern_po_language_code.match(name)
|
||||
if code_match == None:
|
||||
continue
|
||||
language_code = code_match.group(1)
|
||||
tr_name = modname + "." + language_code + ".tr"
|
||||
tr_file = os.path.join(root, tr_name)
|
||||
if os.path.exists(tr_file):
|
||||
if params["verbose"]:
|
||||
print(f"{tr_name} already exists, ignoring {name}")
|
||||
continue
|
||||
fname = os.path.join(root, name)
|
||||
with open(fname, "r", encoding='utf-8') as po_file:
|
||||
if params["verbose"]:
|
||||
print(f"Importing translations from {name}")
|
||||
text = process_po_file(po_file.read())
|
||||
with open(tr_file, "wt", encoding='utf-8') as tr_out:
|
||||
tr_out.write(text)
|
||||
|
||||
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
|
||||
# Creates a directory if it doesn't exist, silently does
|
||||
# nothing if it already exists
|
||||
def mkdir_p(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError as exc: # Python >2.5
|
||||
if exc.errno == errno.EEXIST and os.path.isdir(path):
|
||||
pass
|
||||
else: raise
|
||||
|
||||
# Converts the template dictionary to a text to be written as a file
|
||||
# dKeyStrings is a dictionary of localized string to source file sets
|
||||
# dOld is a dictionary of existing translations and comments from
|
||||
# the previous version of this text
|
||||
def strings_to_text(dkeyStrings, dOld, mod_name):
|
||||
lOut = [f"# textdomain: {mod_name}\n"]
|
||||
|
||||
dGroupedBySource = {}
|
||||
|
||||
for key in dkeyStrings:
|
||||
sourceList = list(dkeyStrings[key])
|
||||
sourceList.sort()
|
||||
sourceString = "\n".join(sourceList)
|
||||
listForSource = dGroupedBySource.get(sourceString, [])
|
||||
listForSource.append(key)
|
||||
dGroupedBySource[sourceString] = listForSource
|
||||
|
||||
lSourceKeys = list(dGroupedBySource.keys())
|
||||
lSourceKeys.sort()
|
||||
for source in lSourceKeys:
|
||||
localizedStrings = dGroupedBySource[source]
|
||||
localizedStrings.sort()
|
||||
lOut.append("")
|
||||
lOut.append(source)
|
||||
lOut.append("")
|
||||
for localizedString in localizedStrings:
|
||||
val = dOld.get(localizedString, {})
|
||||
translation = val.get("translation", "")
|
||||
comment = val.get("comment")
|
||||
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None:
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{localizedString}={translation}")
|
||||
if len(localizedString) > doublespace_threshold:
|
||||
lOut.append("")
|
||||
|
||||
|
||||
unusedExist = False
|
||||
for key in dOld:
|
||||
if key not in dkeyStrings:
|
||||
val = dOld[key]
|
||||
translation = val.get("translation")
|
||||
comment = val.get("comment")
|
||||
# only keep an unused translation if there was translated
|
||||
# text or a comment associated with it
|
||||
if translation != None and (translation != "" or comment):
|
||||
if not unusedExist:
|
||||
unusedExist = True
|
||||
lOut.append("\n\n##### not used anymore #####\n")
|
||||
if len(key) > doublespace_threshold and not lOut[-1] == "":
|
||||
lOut.append("")
|
||||
if comment != None:
|
||||
lOut.append(comment)
|
||||
lOut.append(f"{key}={translation}")
|
||||
if len(key) > doublespace_threshold:
|
||||
lOut.append("")
|
||||
return "\n".join(lOut) + '\n'
|
||||
|
||||
# Writes a template.txt file
|
||||
# dkeyStrings is the dictionary returned by generate_template
|
||||
def write_template(templ_file, dkeyStrings, mod_name):
|
||||
# read existing template file to preserve comments
|
||||
existing_template = import_tr_file(templ_file)
|
||||
|
||||
text = strings_to_text(dkeyStrings, existing_template[0], mod_name)
|
||||
mkdir_p(os.path.dirname(templ_file))
|
||||
with open(templ_file, "wt", encoding='utf-8') as template_file:
|
||||
template_file.write(text)
|
||||
|
||||
|
||||
# Gets all translatable strings from a lua file
|
||||
def read_lua_file_strings(lua_file):
|
||||
lOut = []
|
||||
with open(lua_file, encoding='utf-8') as text_file:
|
||||
text = text_file.read()
|
||||
#TODO remove comments here
|
||||
|
||||
text = re.sub(pattern_concat, "", text)
|
||||
|
||||
strings = []
|
||||
for s in pattern_lua.findall(text):
|
||||
strings.append(s[1])
|
||||
for s in pattern_lua_bracketed.findall(text):
|
||||
strings.append(s)
|
||||
|
||||
for s in strings:
|
||||
s = re.sub(r'"\.\.\s+"', "", s)
|
||||
s = re.sub("@[^@=0-9]", "@@", s)
|
||||
s = s.replace('\\"', '"')
|
||||
s = s.replace("\\'", "'")
|
||||
s = s.replace("\n", "@n")
|
||||
s = s.replace("\\n", "@n")
|
||||
s = s.replace("=", "@=")
|
||||
lOut.append(s)
|
||||
return lOut
|
||||
|
||||
# Gets strings from an existing translation file
|
||||
# returns both a dictionary of translations
|
||||
# and the full original source text so that the new text
|
||||
# can be compared to it for changes.
|
||||
def import_tr_file(tr_file):
|
||||
dOut = {}
|
||||
text = None
|
||||
if os.path.exists(tr_file):
|
||||
with open(tr_file, "r", encoding='utf-8') as existing_file :
|
||||
# save the full text to allow for comparison
|
||||
# of the old version with the new output
|
||||
text = existing_file.read()
|
||||
existing_file.seek(0)
|
||||
# a running record of the current comment block
|
||||
# we're inside, to allow preceeding multi-line comments
|
||||
# to be retained for a translation line
|
||||
latest_comment_block = None
|
||||
for line in existing_file.readlines():
|
||||
line = line.rstrip('\n')
|
||||
if line[:3] == "###":
|
||||
# Reset comment block if we hit a header
|
||||
latest_comment_block = None
|
||||
continue
|
||||
if line[:1] == "#":
|
||||
# Save the comment we're inside
|
||||
if not latest_comment_block:
|
||||
latest_comment_block = line
|
||||
else:
|
||||
latest_comment_block = latest_comment_block + "\n" + line
|
||||
continue
|
||||
match = pattern_tr.match(line)
|
||||
if match:
|
||||
# this line is a translated line
|
||||
outval = {}
|
||||
outval["translation"] = match.group(2)
|
||||
if latest_comment_block:
|
||||
# if there was a comment, record that.
|
||||
outval["comment"] = latest_comment_block
|
||||
latest_comment_block = None
|
||||
dOut[match.group(1)] = outval
|
||||
return (dOut, text)
|
||||
|
||||
# Walks all lua files in the mod folder, collects translatable strings,
|
||||
# and writes it to a template.txt file
|
||||
# Returns a dictionary of localized strings to source file sets
|
||||
# that can be used with the strings_to_text function.
|
||||
def generate_template(folder, mod_name):
|
||||
dOut = {}
|
||||
for root, dirs, files in os.walk(folder):
|
||||
for name in files:
|
||||
if fnmatch.fnmatch(name, "*.lua"):
|
||||
fname = os.path.join(root, name)
|
||||
found = read_lua_file_strings(fname)
|
||||
if params["verbose"]:
|
||||
print(f"{fname}: {str(len(found))} translatable strings")
|
||||
|
||||
for s in found:
|
||||
sources = dOut.get(s, set())
|
||||
sources.add(f"### {os.path.basename(fname)} ###")
|
||||
dOut[s] = sources
|
||||
|
||||
if len(dOut) == 0:
|
||||
return None
|
||||
templ_file = os.path.join(folder, "locale/template.txt")
|
||||
write_template(templ_file, dOut, mod_name)
|
||||
return dOut
|
||||
|
||||
# Updates an existing .tr file, copying the old one to a ".old" file
|
||||
# if any changes have happened
|
||||
# dNew is the data used to generate the template, it has all the
|
||||
# currently-existing localized strings
|
||||
def update_tr_file(dNew, mod_name, tr_file):
|
||||
if params["verbose"]:
|
||||
print(f"updating {tr_file}")
|
||||
|
||||
tr_import = import_tr_file(tr_file)
|
||||
dOld = tr_import[0]
|
||||
textOld = tr_import[1]
|
||||
|
||||
textNew = strings_to_text(dNew, dOld, mod_name)
|
||||
|
||||
if textOld and textOld != textNew:
|
||||
print(f"{tr_file} has changed.")
|
||||
if not params["no-old-file"]:
|
||||
shutil.copyfile(tr_file, f"{tr_file}.old")
|
||||
|
||||
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
|
||||
new_tr_file.write(textNew)
|
||||
|
||||
# Updates translation files for the mod in the given folder
|
||||
def update_mod(folder):
|
||||
modname = get_modname(folder)
|
||||
if modname is not None:
|
||||
process_po_files(folder, modname)
|
||||
print(f"Updating translations for {modname}")
|
||||
data = generate_template(folder, modname)
|
||||
if data == None:
|
||||
print(f"No translatable strings found in {modname}")
|
||||
else:
|
||||
for tr_file in get_existing_tr_files(folder):
|
||||
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
|
||||
else:
|
||||
print("Unable to find modname in folder " + folder)
|
||||
|
||||
# Determines if the folder being pointed to is a mod or a mod pack
|
||||
# and then runs update_mod accordingly
|
||||
def update_folder(folder):
|
||||
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
|
||||
if is_modpack:
|
||||
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
|
||||
for subfolder in subfolders:
|
||||
update_mod(subfolder + "/")
|
||||
else:
|
||||
update_mod(folder)
|
||||
print("Done.")
|
||||
|
||||
def run_all_subfolders(folder):
|
||||
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
|
||||
update_folder(modfolder + "/")
|
||||
|
||||
|
||||
main()
|
|
@ -13,6 +13,7 @@ Construction requires 14 blocks of ancient portalstone. We have no knowledge of
|
|||
|
||||
Dead bleached wood=
|
||||
Diary of Bert Shackleton=
|
||||
Fossil Display=
|
||||
Fossilized Egg=
|
||||
Giant Ziricote=
|
||||
Giant tree=
|
||||
|
@ -22,11 +23,11 @@ Heart of the Tree=
|
|||
Leaves of a giant tree=
|
||||
Sakura blossom=
|
||||
|
||||
The aerostat is lost.@n@nHowever, salvage attempts throughout the night managed to@nsave most provisions before it finally broke apart and fell.@n@n ---@=@=@=@=---@n@nThis island is highly exposed and the weather did not treat@nthe tents well. We have enlarged a sheltered crag in the @1,@nbut it is laborous work and the condition of some of the party@nis becoming cause for concern.@n@nQuite a journey is now required, we cannot stay - nobody will@nlook for us here. McNish is attempting to strengthen the gliders.@n@n ---@=@=@=@=---@n=
|
||||
The aerostat is lost.@n@nHowever, salvage attempts throughout the night managed to@nsave most provisions before it finally broke apart and fell.@n@n ---@=@=@=@=---@n@nThis island is highly exposed and the weather did not treat@nthe tents well. We have enlarged a sheltered crag in the @1,@nbut it is laborous work and the condition of some of the party@nis becoming cause for concern.@n@nQuite a journey is now required, we cannot stay - nobody will@nlook for us here. McNish is attempting to strengthen the gliders.@n@n ---@=@=@=@=---=
|
||||
|
||||
The only portal we managed to scavenge enough portalstone to build took us to a land of floating islands. There were hills and forests and even water up there, but the edges are a perilous drop — a depth of which we cannot even begin to plumb.=
|
||||
|
||||
Weddell Outpost=
|
||||
Weddell Outpost, November 21=
|
||||
Wisteria blossom=
|
||||
ice=
|
||||
rock=
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = cloudlands
|
||||
optional_depends = vines, schemlib, default, mcl_core, ethereal, biomeinfo, nether
|
||||
optional_depends = nether, vines, biomeinfo, schemlib, default, mcl_core, xpanes, ethereal, main
|
||||
description = """
|
||||
Hallelujah Mountains for Minetest
|
||||
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 370 KiB |
|
@ -1,3 +1,17 @@
|
|||
|
||||
-- modified by dcc to work with hoppers
|
||||
--
|
||||
-- NOTE: input hopper goes on SIDE not top... to allow punching of barrel - placing hopper to point correctly
|
||||
-- may require screwdriver
|
||||
--
|
||||
-- output hopper goes below barrel
|
||||
--
|
||||
-- If testing - please note the abm's only fire every 30 seconds so it can take some time
|
||||
--
|
||||
|
||||
|
||||
|
||||
|
||||
compost = {}
|
||||
compost.items = {}
|
||||
compost.groups = {}
|
||||
|
@ -55,6 +69,154 @@ compost.register_item("farming:wheat")
|
|||
compost.register_group("plant")
|
||||
compost.register_group("flower")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- dcc
|
||||
|
||||
local wood_barrel_src_on_construct = function(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
inv:set_size("src", 1)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local wood_barrel_dst_on_construct = function(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
inv:set_size("dst", 1)
|
||||
|
||||
-- this is for wood barrel #3 which has 1 compost ready for taking
|
||||
--
|
||||
inv:set_stack('dst', 1, 'compost:compost')
|
||||
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("formspec",
|
||||
"size[8,5.3]" ..
|
||||
default.gui_bg ..
|
||||
default.gui_bg_img ..
|
||||
default.gui_slots ..
|
||||
"list[current_name;dst;3.5,0;1,1;]" ..
|
||||
"list[current_player;main;0,1.15;8,1;]" ..
|
||||
"list[current_player;main;0,2.38;8,3;8]" ..
|
||||
"listring[current_name;main]" ..
|
||||
"listring[current_player;main]" ..
|
||||
default.get_hotbar_bg(0,1.15)
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
-- Only allow compostable items to be placed in input
|
||||
-- allow_metadata_inventory_put is a pre-check prior to actual
|
||||
-- putting...
|
||||
--
|
||||
local wood_barrel_allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
--print ("[compost] wood_barrel_allow_metadata_inventory_put ", listname)
|
||||
|
||||
if listname == "src" then
|
||||
|
||||
local stackname = stack:get_name()
|
||||
--print ("[compost] wood_barrel_allow_metadata_inventory_put ", stackname )
|
||||
if compost.can_compost(stackname) then
|
||||
--print ("[compost] succeed wood_barrel_allow_metadata_inventory_put ", stackname)
|
||||
return 1 -- take only 1 item
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- actual put
|
||||
--
|
||||
local wood_barrel_on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
--print ("[compost] wood_barrel_on_metadata_inventory_put ")
|
||||
|
||||
if listname == "src" then
|
||||
|
||||
-- check again just in case
|
||||
--
|
||||
local stackname = stack:get_name()
|
||||
|
||||
if compost.can_compost(stackname) then
|
||||
|
||||
-- we received a compostable item - change to new node type
|
||||
--
|
||||
minetest.set_node(pos, {name = "compost:wood_barrel_1"})
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Versions of callbacks for the inaccessible versions of the barrel
|
||||
--
|
||||
|
||||
-- nope
|
||||
local wood_barrelNope_allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
--print ("[compost] wood_barrelNope_allow_metadata_inventory_put ")
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
-- nope
|
||||
local wood_barrelNope_on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
--print ("[compost] wood_barrelNope_on_metadata_inventory_put ")
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- nope
|
||||
local wood_barrelNope_allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
|
||||
-- nope you can't take out here
|
||||
return 0
|
||||
end
|
||||
|
||||
-- nope
|
||||
local wood_barrelNope_on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- Versions of callbacks for when compost is ready....
|
||||
|
||||
-- nope
|
||||
local wood_barrel_allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
|
||||
-- YES you can take out here - we know it is 1 because we put it there with the constructor
|
||||
return 1
|
||||
end
|
||||
|
||||
-- nope
|
||||
local wood_barrel_on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
|
||||
-- when emptied we revert to a plain wood barrel
|
||||
|
||||
local node = minetest.get_node (pos)
|
||||
|
||||
if node.name == "compost:wood_barrel_3" then
|
||||
minetest.set_node(pos, {name = "compost:wood_barrel"})
|
||||
return ItemStack("compost:compost")
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
minetest.register_node("compost:wood_barrel", {
|
||||
description = "Wood Barrel",
|
||||
tiles = {"default_wood.png"},
|
||||
|
@ -71,6 +233,13 @@ minetest.register_node("compost:wood_barrel", {
|
|||
is_ground_content = false,
|
||||
groups = {choppy = 3},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
|
||||
on_construct = wood_barrel_src_on_construct, -- dcc
|
||||
allow_metadata_inventory_put = wood_barrel_allow_metadata_inventory_put, -- dcc
|
||||
on_metadata_inventory_put = wood_barrel_on_metadata_inventory_put, -- dcc
|
||||
allow_metadata_inventory_take = wood_barrelNope_allow_metadata_inventory_take, -- dcc
|
||||
on_metadata_inventory_take = wood_barrelNope_on_metadata_inventory_take, -- dcc
|
||||
|
||||
on_punch = function(pos, node, puncher, pointed_thing)
|
||||
local wielded_item = puncher:get_wielded_item():get_name()
|
||||
if compost.can_compost(wielded_item) then
|
||||
|
@ -84,6 +253,11 @@ minetest.register_node("compost:wood_barrel", {
|
|||
end
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
minetest.register_node("compost:wood_barrel_1", {
|
||||
description = "Wood Barrel with compost",
|
||||
tiles = {"default_wood.png^compost_compost_1.png", "default_wood.png"},
|
||||
|
@ -99,6 +273,13 @@ minetest.register_node("compost:wood_barrel_1", {
|
|||
},
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
|
||||
allow_metadata_inventory_put = wood_barrelNope_allow_metadata_inventory_put, -- dcc
|
||||
on_metadata_inventory_put = wood_barrelNope_on_metadata_inventory_put, -- dcc
|
||||
allow_metadata_inventory_take = wood_barrelNope_allow_metadata_inventory_take, -- dcc
|
||||
on_metadata_inventory_take = wood_barrelNope_on_metadata_inventory_take, -- dcc
|
||||
|
||||
|
||||
groups = {choppy = 3},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
@ -118,6 +299,13 @@ minetest.register_node("compost:wood_barrel_2", {
|
|||
},
|
||||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
|
||||
allow_metadata_inventory_put = wood_barrelNope_allow_metadata_inventory_put, -- dcc
|
||||
on_metadata_inventory_put = wood_barrelNope_on_metadata_inventory_put, -- dcc
|
||||
allow_metadata_inventory_take = wood_barrelNope_allow_metadata_inventory_take, -- dcc
|
||||
on_metadata_inventory_take = wood_barrelNope_on_metadata_inventory_take, -- dcc
|
||||
|
||||
|
||||
groups = {choppy = 3},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
@ -138,6 +326,14 @@ minetest.register_node("compost:wood_barrel_3", {
|
|||
paramtype = "light",
|
||||
is_ground_content = false,
|
||||
groups = {choppy = 3},
|
||||
|
||||
|
||||
on_construct = wood_barrel_dst_on_construct, -- dcc
|
||||
allow_metadata_inventory_put = wood_barrelNope_allow_metadata_inventory_put, -- dcc
|
||||
on_metadata_inventory_put = wood_barrelNope_on_metadata_inventory_put, -- dcc
|
||||
allow_metadata_inventory_take = wood_barrel_allow_metadata_inventory_take, -- dcc
|
||||
on_metadata_inventory_take = wood_barrel_on_metadata_inventory_take, -- dcc
|
||||
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_punch = function(pos, node, player, pointed_thing)
|
||||
local p = {x = pos.x + math.random(0, 5)/5 - 0.5, y = pos.y+1, z = pos.z + math.random(0, 5)/5 - 0.5}
|
||||
|
@ -195,7 +391,7 @@ minetest.register_craft({
|
|||
})
|
||||
|
||||
if minetest.get_modpath ("tubelib") and tubelib then
|
||||
print ("[compost] found tubelib")
|
||||
--print ("[compost] found tubelib")
|
||||
|
||||
-- Thanks to @joe7575
|
||||
tubelib.register_node ("compost:wood_barrel", {
|
||||
|
@ -231,3 +427,23 @@ if minetest.get_modpath ("tubelib") and tubelib then
|
|||
end,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- dcc
|
||||
if minetest.get_modpath ("hopper") and hopper then
|
||||
--print ("[compost] found hopper")
|
||||
hopper:add_container({
|
||||
{"top", "compost:wood_barrel_3", "dst"}, -- take compost from above into hopper below
|
||||
{"side", "compost:wood_barrel", "src"}, -- insert compostable items below to be composted from hopper at side
|
||||
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name = compost
|
||||
depends = default
|
||||
optional_depends = tubelib
|
||||
optional_depends = tubelib, hopper
|
||||
description = This mod allows you to compost grass, leaves, flowers.
|
||||
author = cdqwertz
|
||||
|
|
|
@ -77,8 +77,6 @@ minetest.register_node('epic:stone_with_titanium', {
|
|||
tiles = {'nether_rack.png^epic_titanium_ore.png'},
|
||||
groups = {cracky = 1, stone = 1},
|
||||
drop = 'epic:titanium_lump',
|
||||
light_source = 14,
|
||||
paramtype = 'light',
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ for i in ipairs(dye_table) do
|
|||
})
|
||||
|
||||
minetest.register_node('furniture:curtain_short_right_'..name..'_1', {
|
||||
description = 'Short '..desc..' Curtain Open',
|
||||
description = 'Short Right '..desc..' Curtain Open',
|
||||
drawtype = 'mesh',
|
||||
mesh = 'furniture_curtain_short.obj',
|
||||
tiles = {'furniture_curtain_short_right_1.png^[multiply:'..hex},
|
||||
|
@ -298,7 +298,7 @@ for i in ipairs(dye_table) do
|
|||
})
|
||||
|
||||
minetest.register_node('furniture:curtain_tall_right_'..name..'_1', {
|
||||
description = 'Tall '..desc..' Curtain Open',
|
||||
description = 'Tall Right '..desc..' Curtain Open',
|
||||
drawtype = 'mesh',
|
||||
mesh = 'furniture_curtain_tall.obj',
|
||||
tiles = {'furniture_curtain_tall_right_1.png^[multiply:'..hex},
|
||||
|
|
|
@ -1668,7 +1668,7 @@ local function create_book(item_name, inventory_description, inventory_image, ti
|
|||
minetest.register_craftitem(item_name, {
|
||||
description = inventory_description,
|
||||
inventory_image = inventory_image,
|
||||
groups = {book = 1},
|
||||
groups = {book = 1, not_in_creative_inventory=1},
|
||||
on_use = display_book,
|
||||
_doc_items_hidden = true,
|
||||
_doc_items_longdesc =
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
local news = {
|
||||
'6/22/20',
|
||||
'Cloudland portals are working now.',
|
||||
'',
|
||||
'6/21/20',
|
||||
'Small tweaks to hoppers. Updated Nether.',
|
||||
'Added wide curtains. Hoppers work with compost bins thanks to Daniel1.',
|
||||
'',
|
||||
'6/20/20',
|
||||
'Added hoppers. Small bugfixes.',
|
||||
|
|
|
@ -189,4 +189,31 @@ stations.dual_register_recipe('sewing', {
|
|||
output = 'furniture:curtain_tall_'..colors[i]..'_1',
|
||||
})
|
||||
|
||||
stations.dual_register_recipe('sewing', {
|
||||
input = {
|
||||
[('furniture:fabric_'..colors[i])] = 2,
|
||||
[('furniture:thread_'..colors[i])] = 2,
|
||||
['ropes:ropesegment'] = 1,
|
||||
},
|
||||
output = 'furniture:curtain_tall_left_'..colors[i]..'_1',
|
||||
})
|
||||
|
||||
stations.dual_register_recipe('sewing', {
|
||||
input = {
|
||||
[('furniture:fabric_'..colors[i])] = 2,
|
||||
[('furniture:thread_'..colors[i])] = 2,
|
||||
['ropes:ropesegment'] = 1,
|
||||
},
|
||||
output = 'furniture:curtain_tall_right_'..colors[i]..'_1',
|
||||
})
|
||||
|
||||
stations.dual_register_recipe('sewing', {
|
||||
input = {
|
||||
[('furniture:fabric_'..colors[i])] = 2,
|
||||
[('furniture:thread_'..colors[i])] = 2,
|
||||
['ropes:ropesegment'] = 1,
|
||||
},
|
||||
output = 'furniture:curtain_tall_center_'..colors[i]..'_1',
|
||||
})
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue