Compare commits

...

10 Commits

Author SHA1 Message Date
Treer
aed3ab8306 fix ancient portalstone upgrade
This update is one-way, reverting to older versions of cloudlands will not auto-revert basalt portals into ancient_portalstone portals.
2023-07-01 14:51:20 +10:00
Treer
ebd37839c5 Secret Squirrel 2023-06-29 01:02:42 +10:00
Treer
e52b9d0cca Add supported_games to mod.conf
Doesn't seem to be in lua_api.md, but is here https://content.minetest.net/help/game_support/
cloudlands tries to be game agnostic
2023-06-27 00:27:43 +10:00
Treer
eb566caff9 Fix crash in MTG 5.7 logging
Fixes the following error:

AsyncErr: Lua: Runtime error from mod 'cloudlands' in callback environment_OnGenerated(): ...sr/bin/../games/minetest_game/mods/default/functions.lua:731: attempt to call method 'is_player' (a nil value)
stack traceback:
    ...sr/bin/../games/minetest_game/mods/default/functions.lua:731: in function 'log_player_action'
    ...sr/bin/../games/minetest_game/mods/default/functions.lua:753: in function 'on_metadata_inventory_put'
    /home/username/.minetest/mods/cloudlands/cloudlands.lua:2514: in function 'addDetail_secrets'
    /home/username/.minetest/mods/cloudlands/cloudlands.lua:2800: in function 'renderCores'
    /home/username/.minetest/mods/cloudlands/cloudlands.lua:2941: in function </home/username/.minetest/mods/cloudlands/cloudlands.lua:2913>
    ....mount_MineteCbIaBa/usr/bin/../builtin/game/register.lua:446: in function <....mount_MineteCbIaBa/usr/bin/../builtin/game/register.lua:432>
2023-04-24 13:10:23 +10:00
Treer
6c9107062c Used chiselled Nether basalt for portalstones
- if chiselled Nether basalt is available.
Fix bug where a single island can end up with many ancient portals if plantlife_modpack is installed.
2021-01-26 00:24:48 +11:00
Treer
0009137327
Update README.md 2020-07-28 18:47:02 +10:00
Treer
d69a1090fb Check if an area is protected before creating a portal there
Use nether.volume_is_natural_and_unprotected() instead of nether.volume_is_natural()
2020-07-26 14:12:19 +10:00
Treer
523ec48d60
Update README.md
Add moonlight panorama to screenshots
2020-07-04 16:00:20 +10:00
Treer
4c27370c7a prevent crash when igniting portal
Happens when trying to ignite a portal to the cloudlands, but the cloudlands mod hasn't inited yet because it hasn't needed to generate any islands:

ServerError: AsyncErr: ServerThread::run Lua: Runtime error from mod 'default' in callback item_OnPlace(): ...\minetest-5 latest\bin\..\mods\cloudlands/cloudlands.lua:591: attempt to index upvalue 'noise_heightMap' (a nil value)
stack traceback:
	...\minetest-5 latest\bin\..\mods\cloudlands/cloudlands.lua:591: in function 'is_within_realm'
	...ble)\minetest-5 latest\bin\..\mods\nether/portal_api.lua:1356: in function 'ignite_portal'
	...ble)\minetest-5 latest\bin\..\mods\nether/portal_api.lua:2159: in function <...ble)\minetest-5 latest\bin\..\mods\nether/portal_api.lua:2156>
2020-06-23 00:07:25 +10:00
Treer
1e14d3ee83 Make ancient portalstone rarer in the Nether
Soon the nether mod will provide a portalstone, and cloudlands:ancient_portalstone will become an alias of that, so I'll reduce the amount of this that is being created in the Nether. Since 82d4283 ancient_portalstone is available from the islands anyway.
2020-06-22 22:37:47 +10:00
3 changed files with 269 additions and 178 deletions

View File

@ -24,7 +24,7 @@ Hallelujah Mountains [maps](https://i.imgur.com/2SkoAyB.png) can be generated by
**Recommended mods:**
* [Vines](https://forum.minetest.net/viewtopic.php?t=2344), or something that contains vines, such as plantlife_modpack or MineClone2. These will grow off the sides of the islands, climate permitting, allowing you to climb, and looking picturesque!
* [Nether](https://forum.minetest.net/viewtopic.php?t=5790) enables players to build portals to Hallelujah Mountains, allowing secret entrances into floating kingdoms, or the island altitude to be configured way up high while still having a means to get there. Stones to build these portals can only be found in the Nether (but not until [PR13](https://github.com/minetest-mods/nether/pull/13) is merged).
* [Nether](https://forum.minetest.net/viewtopic.php?t=5790) enables players to build portals to Hallelujah Mountains, allowing secret entrances into floating kingdoms, or the island altitude to be configured way up high while still having a means to get there. Stones to build these portals can only be found in the Nether.
* Extra ways to explore
* [Bridger](https://forum.minetest.net/viewtopic.php?t=18243) or [Bridges](https://forum.minetest.net/viewtopic.php?t=3488) if you want to build nice wooden bridges between islands
* [Airboat](https://github.com/paramat/airboat) to sail the high clouds on a small airship
@ -40,6 +40,9 @@ Hallelujah Mountains [maps](https://i.imgur.com/2SkoAyB.png) can be generated by
**Forum:** [link](https://forum.minetest.net/viewtopic.php?t=20257)
**Screenshots:**
[![default](https://i.imgur.com/jwHN6Sd.jpg)](http://panoramas.minetest.land/IslandMoonlight.html)
![default](https://i.imgur.com/PUjjaIK.jpg)
![default](https://i.imgur.com/xn9UING.jpg)
@ -49,3 +52,5 @@ Hallelujah Mountains [maps](https://i.imgur.com/2SkoAyB.png) can be generated by
![default](https://i.imgur.com/wBLEFWn.jpg)
![default](https://i.imgur.com/A6vDDGc.jpg)

View File

@ -16,6 +16,7 @@ local BIOLUMINESCENCE = false or -- Allow giant trees variants which have
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 USE_NETHER_BASALT = true -- Whether to use "nether:basalt_chiselled" as the portalstone instead of adding "cloudlands:ancient_portalstone" to the Nether.
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
@ -114,6 +115,7 @@ LOWLAND_BIOMES = fromSettings(MODNAME .. "_use_lowland_biomes", LOWLAND_BI
TREE_RARITY = fromSettings(MODNAME .. "_giant_tree_rarety", TREE_RARITY * 100) / 100
BIOLUMINESCENCE = fromSettings(MODNAME .. "_bioluminescence", BIOLUMINESCENCE)
ENABLE_PORTALS = fromSettings(MODNAME .. "_enable_portals", ENABLE_PORTALS)
USE_NETHER_BASALT = fromSettings(MODNAME .. "_use_nether_basalt", USE_NETHER_BASALT)
local noiseparams_eddyField = {
offset = -1,
@ -186,6 +188,7 @@ local nodeId_gravel
local nodeId_vine
local nodeName_vine
local nodeName_ignore = minetest.get_name_from_content_id(nodeId_ignore)
local nodeName_portalStone -- not set until all mods are loaded
local REQUIRED_DENSITY = 0.4
@ -318,7 +321,7 @@ interop.split_nodename = function(nodeName)
result_nodename = nodeName:sub(pos + 1)
end
return result_modname, result_nodename
end;
end
-- returns a unique id for the biome, normally this is numeric but with mapgen v6 it can be a string name.
interop.get_biome_key = function(pos)
@ -381,15 +384,27 @@ end
Portals
==============================]]--
local addDetail_ancientPortal = nil;
local addDetail_ancientPortal = nil
local bookOfPortalsText_AncientPortalstone =
S("Construction requires 14 blocks of ancient portalstone. We have no knowledge of how portalstones were created, the means to craft them are likely lost to time, so our only source has been to scavenge the Nether for the remnants of ancient broken portals. A finished frame is four blocks wide, five blocks high, and stands vertically, like a doorway.") .. "\n\n" ..
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.")
local bookOfPortalsText_NetherChiselledBasalt =
S("Construction requires 14 blocks of chiselled Nether basalt, which can be crafted from hewn Nether basalt. The only source we are aware of for Nether basalt are the islands of basalt columns found in the magma ocean deep within the Nether, but to find the magma ocean Mantle requires finding a passageway through the netherrack. A finished frame is four blocks wide, five blocks high, and stands vertically, like a doorway.") .. "\n\n" ..
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.")
if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_exists("nether") and nether.register_portal ~= nil then
-- The Portals API is available
-- Register a player-buildable portal to Hallelujah Mountains.
-- returns a position on the island which is suitable for a portal to be placed, or nil if none can be found
local function find_potential_portal_location_on_island(island_info)
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
local function find_potential_portal_location_on_island(island_info, player_name)
local result = nil
@ -398,7 +413,7 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
local coreList = cloudlands.get_island_details(
{x = island_info.x - searchRadius, z = island_info.z - searchRadius},
{x = island_info.x + searchRadius, z = island_info.z + searchRadius}
);
)
-- Deterministically sample the island for a low location that isn't water.
-- Seed the prng so this function always returns the same coords for the island
@ -428,15 +443,18 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
end
table.sort(positions, compareFn)
-- nether.volume_is_natural() was deprecated in favor of nether.volume_is_natural_and_unprotected()
local volume_is_natural_and_unprotected = nether.volume_is_natural_and_unprotected or nether.volume_is_natural
-- Now the locations are sorted by how good they are, find the first/best that doesn't
-- grief a player build.
-- Ancient Portalstone has is_ground_content set to true, so we won't have to worry about
-- old/broken portal frames interfering with the results of nether.volume_is_natural()
-- Ancient Portalstone has is_ground_content set to true, so we won't have to worry about old/broken
-- portal frames interfering with the results of nether.volume_is_natural_and_unprotected()
for _, position in ipairs(positions) do
-- Unfortunately, at this point we don't know the orientation of the portal, so use worst case
local minp = {x = position.x - 2, y = position.y, z = position.z - 2}
local maxp = {x = position.x + 3, y = position.y + 4, z = position.z + 3}
if nether.volume_is_natural(minp, maxp) then
if volume_is_natural_and_unprotected(minp, maxp, player_name) then
result = position
break
end
@ -448,7 +466,8 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
-- returns nil if no suitable location could be found, otherwise returns (portal_pos, island_info)
local function find_nearest_island_location_for_portal(surface_x, surface_z)
-- player_name is optional, allowing a player to spawn a remote portal in their own protected areas.
local function find_nearest_island_location_for_portal(surface_x, surface_z, player_name)
local result = nil
@ -457,19 +476,21 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
if island == nil then island = cloudlands.find_nearest_island(surface_x, surface_z, 400) end
if island ~= nil then
result = find_potential_portal_location_on_island(island)
result = find_potential_portal_location_on_island(island, player_name)
end
return result, island
end
-- 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".
-- "Ancient Portalstone" was a tempory placeholder "portalstone" until the Nether mod started
-- providing a block obtainable by exploring the Nether and earmarked for mods like this one
-- to use for portals.
-- The Nether now provides various basalts as portalstones, but "Ancient Portalstone" will
-- still be registered for backwards compatibility.
-- 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", {
minetest.register_node(MODNAME .. ":ancient_portalstone", {
description = S("Ancient Portalstone"),
tiles = {portalstone_end, portalstone_end, portalstone_side, portalstone_side, portalstone_side, portalstone_side},
paramtype2 = "facedir",
@ -478,48 +499,13 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
on_blast = function() --[[blast proof]] end
})
minetest.register_ore({
ore_type = "scatter",
ore = "cloudlands:ancient_portalstone",
wherein = "nether:rack",
clust_scarcity = 28 * 28 * 28,
clust_num_ores = 6,
clust_size = 3,
y_max = nether.DEPTH,
y_min = nether.DEPTH_FLOOR or -32000,
})
local _ = {name = "air", prob = 0}
local A = {name = "air", prob = 255, force_place = true}
local PU = {name = "cloudlands:ancient_portalstone", param2 = 0, prob = 255, force_place = true}
local PW = {name = "cloudlands:ancient_portalstone", param2 = 12, prob = 255, force_place = true}
local PN = {name = "cloudlands:ancient_portalstone", param2 = 4, prob = 255, force_place = true}
minetest.register_decoration({
name = "Ancient broken portal",
deco_type = "schematic",
place_on = "nether:rack",
sidelen = 80,
fill_ratio = 0.0003,
biomes = {"nether_caverns"},
y_max = nether.DEPTH,
y_min = nether.DEPTH_FLOOR or -32000,
schematic = {
size = {x = 4, y = 4, z = 1},
data = {
PN, A, PW, PN,
PU, A, A, PU,
A, _, _, PU,
_, _, _, PU
},
yslice_prob = {
{ypos = 3, prob = 92},
{ypos = 1, prob = 30},
}
},
place_offset_y = 1,
flags = "force_placement,all_floors",
rotation = "random"
})
local PU = {name = "portalstone", param2 = 0, prob = 255, force_place = true}
local PW = {name = "portalstone", param2 = 12, prob = 255, force_place = true}
local PN = {name = "portalstone", param2 = 4, prob = 255, force_place = true}
local islandsWithPortals = {}
-- this uses place_schematic() without minetest.after(), so should be called after vm:write_to_map()
addDetail_ancientPortal = function(core)
@ -535,7 +521,17 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
fastHash = (37 * fastHash) + math_floor(core.depth)
if (PORTAL_RARITY * 10000) < math_floor((math_abs(fastHash)) % 10000) then return false end
local portalPos = find_potential_portal_location_on_island(core)
if islandsWithPortals[fastHash] then
-- This is a hack to stop multiple portals appearing on the island.
-- It will fail if neightboring chunks generate the same core in different server sessions.
-- I think what happens is mods like plantlife_modpack prevent nether.volume_is_natural_and_unprotected() from
-- returning a deterministic result during chunk generation, thus find_potential_portal_location_on_island()
-- doesn't always return the same spot on the island?
return
end
islandsWithPortals[fastHash] = true
local portalPos = find_potential_portal_location_on_island(core, nil)
if portalPos ~= nil then
local orientation = (fastHash % 2) * 90
@ -555,126 +551,204 @@ if ENABLE_PORTALS and minetest.get_modpath("nether") ~= nil and minetest.global_
},
orientation,
{ -- node replacements
["default:obsidian"] = "cloudlands:ancient_portalstone",
["portalstone"] = nodeName_portalStone,
},
true
)
end
end
-- A wrapper for nether.register_portal() where only the frameNodeName and corresponding bookOfPortalsText needs to be specified
local function register_portal(frameNodeName, bookOfPortalsText)
nether.register_portal("cloudlands_portal", {
shape = nether.PortalShape_Traditional,
frame_node_name = "cloudlands:ancient_portalstone",
wormhole_node_color = 2, -- 2 is blue
particle_color = "#77F",
particle_texture = {
name = "nether_particle_anim1.png",
animation = {
type = "vertical_frames",
aspect_w = 7,
aspect_h = 7,
length = 1,
return nether.register_portal("cloudlands_portal", {
shape = nether.PortalShape_Traditional,
frame_node_name = frameNodeName,
wormhole_node_color = 2, -- 2 is blue
particle_color = "#77F",
particle_texture = {
name = "nether_particle_anim1.png",
animation = {
type = "vertical_frames",
aspect_w = 7,
aspect_h = 7,
length = 1,
},
scale = 1.5
},
scale = 1.5
},
title = S("Hallelujah Mountains Portal"),
book_of_portals_pagetext =
S("Construction requires 14 blocks of ancient portalstone. We have no knowledge of how portalstones were created, the means to craft them are likely lost to time, so our only source has been to scavenge the Nether for the remnants of ancient broken portals. A finished frame is four blocks wide, five blocks high, and stands vertically, like a doorway.") .. "\n\n" ..
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."),
title = S("Hallelujah Mountains Portal"),
book_of_portals_pagetext = bookOfPortalsText,
is_within_realm = function(pos)
-- 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}))
return pos.y > math_max(40, island_bottom)
end,
is_within_realm = function(pos)
-- 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.
if noise_heightMap == nil then cloudlands.init() end
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}))
find_realm_anchorPos = function(surface_anchorPos)
-- Find the nearest island and obtain a suitable surface position on it
local destination_pos, island = find_nearest_island_location_for_portal(surface_anchorPos.x, surface_anchorPos.z)
return pos.y > math_max(40, island_bottom)
end,
find_realm_anchorPos = function(surface_anchorPos, player_name)
-- Find the nearest island and obtain a suitable surface position on it
local destination_pos, island = find_nearest_island_location_for_portal(surface_anchorPos.x, surface_anchorPos.z, player_name)
if island ~= nil then
-- Allow any existing or player-positioned portal on the island to be linked to
-- first before resorting to the island's default portal position
local existing_portal_location, existing_portal_orientation = nether.find_nearest_working_portal(
"cloudlands_portal",
{x = island.x, y = 100000, z = island.z}, -- Using 100000 for y to ensure the position is in the cloudlands realm and so find_nearest_working_portal() will only returns island portals.
island.radius * 0.9, -- Islands normally don't reach their full radius. Ensure this distance limit encompasses any location find_nearest_island_location_for_portal() can return.
0 -- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are in the Cloudlands realm)
)
if existing_portal_location ~= nil then
return existing_portal_location, existing_portal_orientation
end
end
return destination_pos
end,
find_surface_anchorPos = function(realm_anchorPos)
-- This function isn't needed since find_surface_target_y() will be used by default,
-- but by implementing it I can look for any existing nearby portals before falling
-- 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 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 =
nether.find_nearest_working_portal("cloudlands_portal", {x = realm_anchorPos.x, y = -100000, z = realm_anchorPos.z}, 150, 0)
if island ~= nil then
-- Allow any existing or player-positioned portal on the island to be linked to
-- first before resorting to the island's default portal position
local existing_portal_location, existing_portal_orientation = nether.find_nearest_working_portal(
"cloudlands_portal",
{x = island.x, y = 100000, z = island.z}, -- Using 100000 for y to ensure the position is in the cloudlands realm and so find_nearest_working_portal() will only returns island portals.
island.radius * 0.9, -- Islands normally don't reach their full radius. Ensure this distance limit encompasses any location find_nearest_island_location_for_portal() can return.
0 -- a y_factor of 0 makes the search ignore the altitude of the portals (as long as they are in the Cloudlands realm)
)
if existing_portal_location ~= nil then
return existing_portal_location, existing_portal_orientation
else
local y = nether.find_surface_target_y(realm_anchorPos.x, realm_anchorPos.z, "cloudlands_portal")
return {x = realm_anchorPos.x, y = y, z = realm_anchorPos.z}
end
end,
on_ignite = function(portalDef, anchorPos, orientation)
-- make some sparks fly on ignition
local p1, p2 = portalDef.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
local pos = vector.divide(vector.add(p1, p2), 2)
local textureName = portalDef.particle_texture
if type(textureName) == "table" then textureName = textureName.name end
local velocity
if orientation == 0 then
velocity = {x = 0, y = 0, z = 7}
else
velocity = {x = 7, y = 0, z = 0}
end
local particleSpawnerDef = {
amount = 180,
time = 0.15,
minpos = {x = pos.x - 1, y = pos.y - 1.5, z = pos.z - 1},
maxpos = {x = pos.x + 1, y = pos.y + 1.5, z = pos.z + 1},
minvel = velocity,
maxvel = velocity,
minacc = {x = 0, y = 0, z = 0},
maxacc = {x = 0, y = 0, z = 0},
minexptime = 0.1,
maxexptime = 0.5,
minsize = 0.3 * portalDef.particle_texture_scale,
maxsize = 0.8 * portalDef.particle_texture_scale,
collisiondetection = false,
texture = textureName .. "^[colorize:#99F:alpha",
animation = portalDef.particle_texture_animation,
glow = 8
}
minetest.add_particlespawner(particleSpawnerDef)
velocity = vector.multiply(velocity, -1)
particleSpawnerDef.minvel, particleSpawnerDef.maxvel = velocity, velocity
minetest.add_particlespawner(particleSpawnerDef)
end
return destination_pos
end,
}) -- end of nether.register_portal() invocation
end -- end of local function register_portal()
find_surface_anchorPos = function(realm_anchorPos)
-- This function isn't needed since find_surface_target_y() will be used by default,
-- but by implementing it I can look for any existing nearby portals before falling
-- 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 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 =
nether.find_nearest_working_portal("cloudlands_portal", {x = realm_anchorPos.x, y = -100000, z = realm_anchorPos.z}, 150, 0)
minetest.register_on_mods_loaded(function()
-- wait until all the other mods are loaded to see if "nether:basalt_chiselled" is still available to use as a portalstone
-- (another mod might be using it for portals)
if existing_portal_location ~= nil then
return existing_portal_location, existing_portal_orientation
else
local y = nether.find_surface_target_y(realm_anchorPos.x, realm_anchorPos.z, "cloudlands_portal")
return {x = realm_anchorPos.x, y = y, z = realm_anchorPos.z}
end
end,
on_ignite = function(portalDef, anchorPos, orientation)
-- make some sparks fly on ignition
local p1, p2 = portalDef.shape:get_p1_and_p2_from_anchorPos(anchorPos, orientation)
local pos = vector.divide(vector.add(p1, p2), 2)
local textureName = portalDef.particle_texture
if type(textureName) == "table" then textureName = textureName.name end
local velocity
if orientation == 0 then
velocity = {x = 0, y = 0, z = 7}
else
velocity = {x = 7, y = 0, z = 0}
end
local particleSpawnerDef = {
amount = 180,
time = 0.15,
minpos = {x = pos.x - 1, y = pos.y - 1.5, z = pos.z - 1},
maxpos = {x = pos.x + 1, y = pos.y + 1.5, z = pos.z + 1},
minvel = velocity,
maxvel = velocity,
minacc = {x = 0, y = 0, z = 0},
maxacc = {x = 0, y = 0, z = 0},
minexptime = 0.1,
maxexptime = 0.5,
minsize = 0.3 * portalDef.particle_texture_scale,
maxsize = 0.8 * portalDef.particle_texture_scale,
collisiondetection = false,
texture = textureName .. "^[colorize:#99F:alpha",
animation = portalDef.particle_texture_animation,
glow = 8
}
minetest.add_particlespawner(particleSpawnerDef)
velocity = vector.multiply(velocity, -1);
particleSpawnerDef.minvel, particleSpawnerDef.maxvel = velocity, velocity
minetest.add_particlespawner(particleSpawnerDef)
local useBasalt = false
if USE_NETHER_BASALT and
minetest.registered_nodes["nether:basalt_chiselled"] ~= nil and
register_portal("nether:basalt_chiselled", bookOfPortalsText_NetherChiselledBasalt) then
-- Chiselled basalt is available for cloudlands to use as portal stone,
-- so there's no need to add any ancient portalstone to the Nether.
useBasalt = true
end
})
if useBasalt then
nodeName_portalStone = "nether:basalt_chiselled"
-- We want to alias any legacy ancient_portalstone to be nether:basalt_chiselled.
-- Players may have already built portals out of "cloudlands:ancient_portalstone" and we
-- want those portals to continue to work.
-- use _force() to Unregister ancient_portalstone and make it an alias of nether:basalt_chiselled
minetest.register_alias_force(MODNAME .. ":ancient_portalstone", nodeName_portalStone)
else
nodeName_portalStone = MODNAME .. ":ancient_portalstone"
register_portal(nodeName_portalStone, bookOfPortalsText_AncientPortalstone)
-- Ensure Ancient Portalstone can be obtained from the Nether
minetest.register_ore({
ore_type = "scatter",
ore = MODNAME .. ":ancient_portalstone",
wherein = "nether:rack",
clust_scarcity = 32 * 32 * 32,
clust_num_ores = 6,
clust_size = 3,
y_max = nether.DEPTH_CEILING or nether.DEPTH,
y_min = nether.DEPTH_FLOOR or -32000,
})
minetest.register_decoration({
name = "Ancient broken portal",
deco_type = "schematic",
place_on = "nether:rack",
sidelen = 80,
fill_ratio = 0.00018,
biomes = {"nether_caverns"},
y_max = nether.DEPTH_CEILING or nether.DEPTH,
y_min = nether.DEPTH_FLOOR or -32000,
schematic = {
size = {x = 4, y = 4, z = 1},
data = {
PN, A, PW, PN,
PU, A, A, PU,
A, _, _, PU,
_, _, _, PU
},
yslice_prob = {
{ypos = 3, prob = 92},
{ypos = 1, prob = 30},
}
},
place_offset_y = 1,
{ -- node replacements
["portalstone"] = nodeName_portalStone,
},
flags = "force_placement,all_floors",
rotation = "random"
})
end
end)
end
--[[==============================
@ -1145,7 +1219,7 @@ if not minetest.global_exists("SkyTrees") then -- If SkyTrees added into other m
-- returns a new position vector, rotated around (0, 0) to match the schematic rotation (provided the schematic_size is correct!)
local function rotatePositon(position, schematic_size, rotation)
local result = vector.new(position);
local result = vector.new(position)
if rotation == 90 then
result.x = position.z
result.z = schematic_size.x - position.x - 1
@ -1159,7 +1233,7 @@ if not minetest.global_exists("SkyTrees") then -- If SkyTrees added into other m
return result
end
local rotatedCenter = rotatePositon(schematicInfo.center, schematicInfo.size, rotation);
local rotatedCenter = rotatePositon(schematicInfo.center, schematicInfo.size, rotation)
local treePos = vector.subtract(position, rotatedCenter)
if themeName == nil then themeName = SkyTrees.selectTheme(position, schematicInfo) end
@ -1211,7 +1285,7 @@ if not minetest.global_exists("SkyTrees") then -- If SkyTrees added into other m
plan_obj:read_from_schem_file(filename, replacements)
plan_obj.data.ground_y = -1 -- prevent read_from_schem_file() from automatically adjusting the height when it encounters dirt in the schematic (SkyTrees sometimes have dirt up in their nooks)
plan_obj.data.facedir = round(rotation / 90)
rotatedCenter = plan_obj:get_world_pos(vector.add(vector.multiply(schematicInfo.center, -1), -1), position); -- this function performs the rotation I require, even if it's named/intended for something else.
rotatedCenter = plan_obj:get_world_pos(vector.add(vector.multiply(schematicInfo.center, -1), -1), position) -- this function performs the rotation I require, even if it's named/intended for something else.
plan_obj.data.anchor_pos = rotatedCenter
if DEBUG_SKYTREES then minetest.log("info", "building tree at " .. dump(position) .. "rotated to " .. dump(treePos) .. "rotatedCenter " .. dump(rotatedCenter) .. ", " .. schematicInfo.filename) end
@ -1253,7 +1327,7 @@ if not minetest.global_exists("SkyTrees") then -- If SkyTrees added into other m
end
SkyTrees.init();
SkyTrees.init()
--[[==============================
@ -1280,7 +1354,7 @@ local function init_mapgen()
biomes["Taiga"] = {node_top="mapgen_dirt_with_snow", node_filler="mapgen_dirt", node_stone="mapgen_stone"}
else
for k,v in pairs(minetest.registered_biomes) do
biomes[minetest.get_biome_id(k)] = v;
biomes[minetest.get_biome_id(k)] = v
end
end
if DEBUG then minetest.log("info", "registered biomes: " .. dump(biomes)) end
@ -1328,7 +1402,7 @@ local function addCores(coreList, coreType, x1, z1, x2, z2)
-- this function is used by the API functions, so may be invoked without our on_generated
-- being called
cloudlands.init();
cloudlands.init()
for z = math_floor(z1 / coreType.territorySize), math_floor(z2 / coreType.territorySize) do
for x = math_floor(x1 / coreType.territorySize), math_floor(x2 / coreType.territorySize) do
@ -1401,12 +1475,12 @@ local function addCores(coreList, coreType, x1, z1, x2, z2)
break
end
end
end;
end
end
if spaceConditionMet then
-- all conditions met, we've located a new island core
--minetest.log("Adding core "..x..","..y..","..z..","..radius);
--minetest.log("Adding core "..x..","..y..","..z..","..radius)
local y = round(noise_heightMap:get2d({x = coreX, y = coreZ}))
local newCore = {
x = coreX,
@ -1493,7 +1567,7 @@ cloudlands.get_island_details = function(minp, maxp)
-- settings from rearranging islands.
if region_restrictions then removeUnwantedIslands(result) end
return result;
return result
end
@ -1527,7 +1601,7 @@ cloudlands.find_nearest_island = function(x, z, search_radius)
if result == nil or core.distance < result.distance then result = core end
end
return result;
return result
end
@ -1536,7 +1610,7 @@ end
-- having to recalculate it during each call to get_height_at().
cloudlands.get_height_at = function(x, z, coreList)
local result, isWater = nil, false;
local result, isWater = nil, false
if coreList == nil then
local pos = {x = x, z = z}
@ -1927,7 +2001,7 @@ local function addDetail_skyTree(decoration_list, core, minp, maxp)
end
end
local maxOffsetFromCenter = core.radius - (tree.requiredIslandRadius - 4); -- 4 is an arbitrary number, to allow trees to get closer to the edge
local maxOffsetFromCenter = core.radius - (tree.requiredIslandRadius - 4) -- 4 is an arbitrary number, to allow trees to get closer to the edge
-- Use a known PRNG implementation
local prng = PcgRandom(
@ -1973,7 +2047,7 @@ local function addDetail_skyTree(decoration_list, core, minp, maxp)
if DEBUG_SKYTREES then minetest.log("info", "core x: "..coreX.." y: ".. coreZ .. " treePos: " .. treePos.x .. ", y: " .. treePos.y) end
SkyTrees.placeTree(treePos, treeAngle, tree, nil, core.biome.node_top)
return true;
return true
end
@ -2010,7 +2084,7 @@ minetest.register_node(
local nodeName_egg = "secret:fossilized_egg"
local eggTextureBaseName = interop.find_node_texture({"default:jungleleaves", "mcl_core:jungleleaves", "ethereal:frost_leaves", "main:leaves"})
local eggTextureBaseName = interop.find_node_texture({"default:jungleleaves", "mcl_core:jungleleaves", "ethereal:frost_leaves", "main:leaves", "nc_tree: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
@ -2062,6 +2136,8 @@ 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)"
--frameTexture = "([combine:16x16:0,0="..woodTexture.."\\^[resize\\:16x16\\^[colorize\\:black\\:170:1,1="..woodTexture.."\\^[colorize\\:#0F0\\:255\\^[resize\\:14x14^[makealpha:0,255,0)"
--frameTexture = "(("..woodTexture.."^[colorize:black:170)^([combine:16x16:1,1="..woodTexture.."\\^[resize\\:14x14\\^[colorize\\:#0F0\\:255))"
end
-- Since "secret:fossilized_egg_display" doesn't use this mod's name as the prefix, we shouldn't
@ -2133,7 +2209,8 @@ if frameTexture ~= nil and nodeName_frameGlass ~= nodeName_ignore and minetest.r
end
end
local nodeId_egg = minetest.get_content_id(nodeName_egg)
local nodeId_egg = interop.find_node_id({"draconis:egg_fire_green", nodeName_egg})
local nodeId_eggNest = interop.find_node_id(table.insert_all({"draconis:bone_pile_scorched"}, NODENAMES_GRAVEL))
local nodeId_airStandIn = minetest.get_content_id(interop.register_clone("air"))
-- defer assigning the following until all mods are loaded
@ -2383,7 +2460,7 @@ local function addDetail_secrets(decoration_list, core, data, area, minp, maxp)
local eggX = core.x
local eggZ = core.z - directionOffsetZ * 0.75 -- move the egg away from where the burrow entrance is carved
placeNode(eggX, burrowFloor, eggZ, nodeId_egg)
if nodeId_gravel ~= nodeId_ignore then placeNode(eggX, burrowFloor - 1, eggZ, nodeId_gravel) end
if nodeId_eggNest ~= nodeId_ignore then placeNode(eggX, burrowFloor - 1, eggZ, nodeId_eggNest) end
if nodeId_cobweb ~= nodeId_ignore then
placeNode(core.x - 6, burrowFloor + 3, core.z - 1, nodeId_cobweb)
placeNode(core.x + 4, burrowFloor + 4, core.z + 3, nodeId_cobweb)
@ -2502,6 +2579,9 @@ look for us here. McNish is attempting to strengthen the gliders.
invBookshelf:add_item("books", book_itemstack)
local dummyPlayer = {}
dummyPlayer.get_player_name = function() return "server" end
dummyPlayer.is_fake_player = true
dummyPlayer.is_player = function() return false end -- it's unclear whether this should return false for fake players
minetest.registered_nodes[nodeName_bookshelf].on_metadata_inventory_put(bookshelf_pos, "books", 1, book_itemstack, dummyPlayer)
end
end
@ -2523,6 +2603,7 @@ look for us here. McNish is attempting to strengthen the gliders.
addIfFound({"binoculars:binoculars"}, 1)
addIfFound(NODENAMES_WOOD, 10)
addIfFound({"mcl_torches:torch", "default:torch", "torch:torch"}, 3)
addIfFound({"hangglider:hangglider"}, 1)
end
end
@ -2674,7 +2755,7 @@ local function renderCores(cores, minp, maxp, blockseed)
if DEBUG_GEOMETRIC then surfaceNoise = SURFACEMAP_OFFSET end
local surface = round(surfaceNoise * 3 * (core.thickness + 1) * horz_easing) -- if you change this formular then update maxSufaceRise in on_generated()
local coreBottom = math_floor(coreTop - (core.thickness + core.depth))
local noisyDepthOfFiller = depth_filler;
local noisyDepthOfFiller = depth_filler
if noisyDepthOfFiller >= 3 then noisyDepthOfFiller = noisyDepthOfFiller + math_floor(randomNumbers[(x + z) % 256] * 3) - 1 end
local yBottom = math_max(minp.y, coreBottom - 4) -- the -4 is for rare instances when density noise pushes the bottom of the island deeper
@ -2711,7 +2792,7 @@ local function renderCores(cores, minp, maxp, blockseed)
-- ensure nodeId_top blocks also cover the rounded sides of islands (which may be lower
-- than the flat top), then dust the top surface.
if topBlockIndex >= 0 then
voxelsWereManipulated = true;
voxelsWereManipulated = true
-- we either have the highest block, or overdrawTop - but we don't want to set overdrawTop nodes to nodeId_top
-- (we will err on the side of caution when we can't distinguish the top of a island's side from overdrawTop)
@ -2773,10 +2854,10 @@ local function renderCores(cores, minp, maxp, blockseed)
else
data[vi] = nodeId_filler
end
end;
end
end
end
end;
end
end
end
@ -2895,6 +2976,10 @@ cloudlands.init = function()
init_mapgen()
init_secrets()
end
if noise_eddyField == nil then
-- See comment in init_mapgen() about when this can be called
minetest.log("warning", "cloudlands.init() unable to init - was probably invoked before the the environment was created")
end
end
local function on_generated(minp, maxp, blockseed)
@ -2914,13 +2999,12 @@ local function on_generated(minp, maxp, blockseed)
return
end
cloudlands.init();
local cores = cloudlands.get_island_details(minp, maxp)
if DEBUG then
minetest.log("info", "Cores for on_generated(): " .. #cores)
for _,core in pairs(cores) do
minetest.log("core ("..core.x..","..core.y..","..core.z..") r"..core.radius);
minetest.log("core ("..core.x..","..core.y..","..core.z..") r"..core.radius)
end
end
@ -2949,4 +3033,4 @@ minetest.register_on_mapgen_init(
worldSeed = mgparams.seed
--if DEBUG then minetest.set_mapgen_params({mgname = "singlenode"--[[, flags = "nolight"]]}) end
end
)
)

View File

@ -1,5 +1,7 @@
name = cloudlands
optional_depends = nether, vines, biomeinfo, schemlib, default, mcl_core, xpanes, ethereal, main
optional_depends = nether, vines, biomeinfo, schemlib, default, draconis, mcl_core, xpanes, ethereal, main, nc_tree
min_minetest_version = 5.0
supported_games = *
description = """
Hallelujah Mountains for Minetest