Updated Cloudlands, and made compost bins work with hoppers.

master
NathanSalapat 2020-06-22 07:43:02 -05:00
parent 11a5200170
commit 51fbd54112
13 changed files with 982 additions and 154 deletions

View File

@ -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.
@ -171,7 +174,7 @@ local noise_surfaceMap
local noise_skyReef
local worldSeed
local nodeId_ignore = minetest.CONTENT_IGNORE
local nodeId_ignore = minetest.CONTENT_IGNORE
local nodeId_air
local nodeId_stone
local nodeId_grass
@ -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)
@ -500,7 +563,7 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
end
nether.register_portal("cloudlands_portal", {
nether.register_portal("cloudlands_portal", {
shape = nether.PortalShape_Traditional,
frame_node_name = "cloudlands:ancient_portalstone",
wormhole_node_color = 2, -- 2 is blue
@ -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,48 +1987,60 @@ 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^[verticalframe:5:4^[brighten"
},
description = S("Cobweb"),
groups = {snappy = 3, liquid = 3, flammable = 3, not_in_creative_inventory = 1},
drawtype = "plantlike",
walkable = false,
liquid_viscosity = 8,
liquidtype = "source",
liquid_alternative_flowing = nodeName_standinCobweb,
liquid_alternative_source = nodeName_standinCobweb,
liquid_renewable = false,
liquid_range = 0,
sunlight_propagates = true,
paramtype = "light"
}
)
end
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"),
groups = {snappy = 3, liquid = 3, flammable = 3, not_in_creative_inventory = 1},
drawtype = "plantlike",
walkable = false,
liquid_viscosity = 8,
liquidtype = "source",
liquid_alternative_flowing = nodeName_standinCobweb,
liquid_alternative_source = nodeName_standinCobweb,
liquid_renewable = false,
liquid_range = 0,
sunlight_propagates = true,
paramtype = "light"
}
)
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,29 +2490,23 @@ 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)
---====---]], groundDesc),
S("Diary of Bert Shackleton") -- description
)
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})
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??)
if invChest ~= nil then invChest:add_item("main", book_itemstack) end
else
-- add the book to the bookshelf and manually trigger update_bookshelf() so its
-- name will reflect the new contents.
invBookshelf:add_item("books", book_itemstack)
local dummyPlayer = {}
dummyPlayer.get_player_name = function() return "server" end
minetest.registered_nodes[nodeName_bookshelf].on_metadata_inventory_put(bookshelf_pos, "books", 1, book_itemstack, dummyPlayer)
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??)
if invChest ~= nil then invChest:add_item("main", book_itemstack) end
else
-- add the book to the bookshelf and manually trigger update_bookshelf() so its
-- name will reflect the new contents.
invBookshelf:add_item("books", book_itemstack)
local dummyPlayer = {}
dummyPlayer.get_player_name = function() return "server" end
minetest.registered_nodes[nodeName_bookshelf].on_metadata_inventory_put(bookshelf_pos, "books", 1, book_itemstack, dummyPlayer)
end
end
end
@ -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

426
mods/cloudlands/i18n.py Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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(),
})

View File

@ -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},
@ -141,7 +141,7 @@ for i in ipairs(dye_table) do
})
minetest.register_node('furniture:curtain_short_center_'..name..'_0', {
description = 'Short Middle'..desc..' Curtain Closed',
description = 'Short Middle '..desc..' Curtain Closed',
drawtype = 'mesh',
mesh = 'furniture_curtain_short.obj',
tiles = {'furniture_curtain_short_0.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},
@ -321,7 +321,7 @@ for i in ipairs(dye_table) do
})
minetest.register_node('furniture:curtain_tall_center_'..name..'_0', {
description = 'Tall Middle'..desc..' Curtain Closed',
description = 'Tall Middle '..desc..' Curtain Closed',
drawtype = 'mesh',
mesh = 'furniture_curtain_tall.obj',
tiles = {'furniture_curtain_tall_0.png^[multiply:'..hex},
@ -343,7 +343,7 @@ for i in ipairs(dye_table) do
})
minetest.register_node('furniture:curtain_tall_center_'..name..'_1', {
description = 'Tall Middle'..desc..' Curtain Open',
description = 'Tall Middle '..desc..' Curtain Open',
drawtype = 'mesh',
mesh = 'furniture_curtain_tall.obj',
tiles = {'furniture_curtain_tall_center_1.png^[multiply:'..hex},

View File

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

View File

@ -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.',

View File

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