diff --git a/README.md b/README.md index 2b73de9..587e98a 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Some of the other cave decorations provide dim bioluminescent lighting in some c The "[doc](https://forum.minetest.net/viewtopic.php?f=9&t=15912&p=240152)" mod is supported to provide in-game documentation for all of the new items and nodes this mod adds. -"[ropes](https://github.com/minetest-mods/ropes)" are very useful for navigating some of the large open spaces this mod provides. +"[ropes](https://github.com/minetest-mods/ropes)" are very useful for navigating some of the large open spaces this mod provides. Some are large enough that a [glider](https://github.com/CBugDCoder/glider) may be well suited. "[radiant damage](https://github.com/FaceDeer/radiant_damage)" greatly increases the danger of the Magma Sea if heat radiance is enabled, as well as several of the rare crystals in the deeper layers that emit Mese radiation if that damage type is enabled. diff --git a/big_webs/LICENSE.txt b/big_webs/LICENSE.txt new file mode 100644 index 0000000..6bf46b1 --- /dev/null +++ b/big_webs/LICENSE.txt @@ -0,0 +1,24 @@ +Sounds and textures are under various licenses, see the license.txt file in the /sounds and /textures directories for details. + +License for Code +---------------- + +Copyright (C) 2021 FaceDeer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/big_webs/init.lua b/big_webs/init.lua new file mode 100644 index 0000000..986e2df --- /dev/null +++ b/big_webs/init.lua @@ -0,0 +1,198 @@ +local modname = minetest.get_current_modname() +local S = minetest.get_translator(modname) + +local default_path = minetest.get_modpath("default") + +local get_node_box = function(connector_thickness) + return { + type = "connected", + --fixed = {-hub_thickness,-hub_thickness,-hub_thickness,hub_thickness,hub_thickness,hub_thickness}, + connect_top = {-connector_thickness, 0, -connector_thickness, connector_thickness, 0.5, connector_thickness}, + connect_bottom = {-connector_thickness, -0.5, -connector_thickness, connector_thickness, 0, connector_thickness}, + connect_back = {-connector_thickness, -connector_thickness, 0, connector_thickness, connector_thickness, 0.5}, + connect_right = {0, -connector_thickness, -connector_thickness, 0.5, connector_thickness, connector_thickness}, + connect_front = {-connector_thickness, -connector_thickness, -0.5, connector_thickness, connector_thickness, 0}, + connect_left = {-0.5, -connector_thickness, -connector_thickness, 0, connector_thickness, connector_thickness}, + disconnected = {-connector_thickness, -connector_thickness, -connector_thickness, connector_thickness, connector_thickness, connector_thickness}, + } +end + +local in_anchor_group = function(name) + return + minetest.get_item_group(name, "soil") > 0 or + minetest.get_item_group(name, "stone") > 0 or + minetest.get_item_group(name, "tree") > 0 or + minetest.get_item_group(name, "leaves") > 0 or + minetest.get_item_group(name, "sand") > 0 or + minetest.get_item_group(name, "wood") > 0 or + name == "ignore" +end + +local cardinal_directions = { + {x=1,y=0,z=0}, + {x=-1,y=0,z=0}, + {x=0,y=1,z=0}, + {x=0,y=-1,z=0}, + {x=0,y=0,z=1}, + {x=0,y=0,z=-1} +} + +local cardinal_planes = { + {3,5}, + {3,5}, + {1,5}, + {1,5}, + {1,3}, + {1,3}, +} + +local insert_if_not_in_hashtable = function(pos, insert_into, if_not_in) + local hash = minetest.hash_node_position(pos) + if if_not_in[hash] then + return + end + table.insert(insert_into, pos) +end + +-- flood fill through the web to get all web and anchor locations +local get_web_nodes = function(pos, webs, anchors) + local to_check = {} + table.insert(to_check, pos) + while next(to_check) ~= nil do + local check_pos = table.remove(to_check) + local check_node = minetest.get_node(check_pos) + if minetest.get_item_group(check_node.name, "webbing") > 0 then + webs[minetest.hash_node_position(check_pos)] = true + for _, dir in pairs(cardinal_directions) do + insert_if_not_in_hashtable(vector.add(check_pos, dir), to_check, webs) + end + elseif in_anchor_group(check_node.name) then + anchors[minetest.hash_node_position(check_pos)] = true + end + end +end + +local sound +if default_path then + sound = default.node_sound_leaves_defaults() +end + + +local web_line = function(pos, dir, distance) + local web_spine = {} + for i = 0, distance do + local web_pos = vector.add(pos, vector.multiply(dir,i)) + local node_name = minetest.get_node(web_pos).name + if node_name == "air" or node_name == "big_webs:webbing" then + table.insert(web_spine, web_pos) + elseif in_anchor_group(node_name) then + anchored=true + break + else + anchored=false + break + end + end + + if anchored then + for _, web_pos in pairs(web_spine) do + if math.random() < 0.9 then + minetest.set_node(web_pos, {name="big_webs:webbing"}) + end + end + return web_spine + end + return nil +end + +local generate_web = function(pos) + local dir_choice = math.random(1, 6) + local dir = cardinal_directions[dir_choice] + local web_spine = web_line(pos, dir, 30) + if web_spine then + local dir2 = cardinal_directions[cardinal_planes[dir_choice][math.random(1, 2)]] + local dir2_opposite = vector.multiply(dir2, -1) + for _, web_pos in pairs(web_spine) do + web_line(web_pos, dir2, 15) + web_line(web_pos, dir2_opposite, 15) + end + end +end + +minetest.register_node("big_webs:webbing", { + description = S("Giant Cave Spider Webbing"), + _doc_items_longdesc = S("Thick ropes of sticky, springy silk, strung between cavern walls in hopes of catching bats and even larger beasts."), + _doc_items_usagehelp = S("Webbing can be collected and re-strung elsewhere to aid in climbing. It absorbs all falling damage when you land on it."), + tiles = { + {name="big_webs.png"}, + }, + use_texture_alpha = "blend", + connects_to = {"group:soil", "group:stone", "group:tree", "group:leaves", "group:sand", "group:wood", "group:webbing"}, + connect_sides = { "top", "bottom", "front", "left", "back", "right" }, + drawtype = "nodebox", + node_box = get_node_box(0.0625), + collision_box = get_node_box(0.0625), + inventory_image = "big_webs_item.png", + wield_image = "big_webs_item.png", + paramtype = "light", + is_ground_content = false, + climbable = true, + floodable = true, + groups = {snappy = 2, choppy = 2, webbing = 1, flammable=1, fall_damage_add_percent=-100, bouncy=20}, + sounds = sound, + on_construct = function(pos) + minetest.get_node_timer(pos):start(30) + end, + on_destruct = function(pos) + for _, dir in pairs(cardinal_directions) do + local neighbor_pos = vector.add(pos, dir) + if minetest.get_item_group(minetest.get_node(neighbor_pos).name, "webbing") > 0 then + minetest.get_node_timer(neighbor_pos):start(30) + end + end + minetest.get_node_timer(pos):stop() + end, + on_timer = function(pos, elapsed) + local webs = {} + local anchors = {} + get_web_nodes(pos, webs, anchors) + local first_anchor = next(anchors) + for hash, _ in pairs(webs) do + local web_pos = minetest.get_position_from_hash(hash) + if first_anchor == nil then + -- unsupported web + minetest.set_node(web_pos, {name="air"}) + minetest.item_drop(ItemStack("big_webs:webbing"), nil, web_pos) + end + minetest.get_node_timer(web_pos):stop() -- no need to recheck + end + end, +}) + +minetest.register_node("big_webs:web_egg", { + description = S("Giant Cave Spider Web Generator"), + tiles = { + {name="big_webs.png"}, + }, + use_texture_alpha = "blend", + connects_to = {"group:soil", "group:stone", "group:tree", "group:leaves", "group:sand", "group:wood", "group:webbing"}, + connect_sides = { "top", "bottom", "front", "left", "back", "right" }, + drawtype = "nodebox", + node_box = get_node_box(0.0625), + collision_box = get_node_box(0.0625), + inventory_image = "big_webs_item.png", + wield_image = "big_webs_item.png", + paramtype = "light", + is_ground_content = false, + climbable = true, + floodable = true, + groups = {snappy = 2, choppy = 2, webbing = 1, flammable=1, fall_damage_add_percent=-100, bouncy=20}, + sounds = sound, + on_construct = function(pos) + minetest.get_node_timer(pos):start(1) + end, + on_timer = function(pos, elapsed) + minetest.set_node(pos, {name="air"}) + generate_web(pos) + end, +}) \ No newline at end of file diff --git a/big_webs/locale/template.txt b/big_webs/locale/template.txt new file mode 100644 index 0000000..22bfe9d --- /dev/null +++ b/big_webs/locale/template.txt @@ -0,0 +1,12 @@ +# textdomain: big_webs + + +### init.lua ### + +Giant Cave Spider Web Generator= +Giant Cave Spider Webbing= + +Thick ropes of sticky, springy silk, strung between cavern walls in hopes of catching bats and even larger beasts.= + +Webbing can be collected and re-strung elsewhere to aid in climbing. It absorbs all falling damage when you land on it.= + diff --git a/big_webs/mod.conf b/big_webs/mod.conf new file mode 100644 index 0000000..bd7de62 --- /dev/null +++ b/big_webs/mod.conf @@ -0,0 +1,2 @@ +name=big_webs +optional_depends=default \ No newline at end of file diff --git a/big_webs/textures/big_webs.png b/big_webs/textures/big_webs.png new file mode 100644 index 0000000..5e82942 Binary files /dev/null and b/big_webs/textures/big_webs.png differ diff --git a/big_webs/textures/big_webs_item.png b/big_webs/textures/big_webs_item.png new file mode 100644 index 0000000..8dc021d Binary files /dev/null and b/big_webs/textures/big_webs_item.png differ diff --git a/chasms/LICENSE.txt b/chasms/LICENSE.txt new file mode 100644 index 0000000..646c3d5 --- /dev/null +++ b/chasms/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 FaceDeer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/chasms/init.lua b/chasms/init.lua new file mode 100644 index 0000000..2964257 --- /dev/null +++ b/chasms/init.lua @@ -0,0 +1,169 @@ +local data = {} + +chasms = {} + +local maxy = tonumber(minetest.settings:get("chasms_maxy")) or -50 +local miny = tonumber(minetest.settings:get("chasms_miny")) or -2500 +local falloff = tonumber(minetest.settings:get("chasms_falloff")) or 100 + +local web_probability = 0.15 -- the chance that a given mapblock will have webbing criss-crossing the chasm + +local chasms_threshold = tonumber(minetest.settings:get("chasms_threshold")) or 0.9 +local np_chasms_default = { + offset = 0, + scale = 1, + spread = {x = 50, y = 1000, z = 3000}, + seed = 94586, + octaves = 2, + persist = 0.63, + lacunarity = 2.0, +} +local np_chasms = minetest.settings:get_np_group("chasms_params") or np_chasms_default +-- For some reason, these numbers are returned as strings by get_np_group. +local tonumberize_params = function(params) + params.scale = tonumber(params.scale) + params.lacunarity = tonumber(params.lacunarity) + params.spread.x = tonumber(params.spread.x) + params.spread.y = tonumber(params.spread.y) + params.spread.z = tonumber(params.spread.z) + params.offset = tonumber(params.offset) + params.persistence = tonumber(params.persistence) +end +tonumberize_params(np_chasms) +local nobj_chasm +local chasm_data = {} + +local waver_strength = 8 +local waver_vector = {x=waver_strength, y=0, z=0} +local np_waver = { + offset = 0, + scale = waver_strength, + spread = {x = 50, y = 50, z = 50}, + seed = 49585, + octaves = 2, + persist = 0.63, + lacunarity = 2.0, +} +local nobj_waver +local waver_data = {} + +local minfalloff = miny + falloff +local maxfalloff = maxy - falloff +local get_intensity = function(y) + if y < miny or y > maxy then + return 0 + end + if y <= maxfalloff and y >= minfalloff then + return 1 + end + if y < minfalloff then + return (y-miny)/falloff + end +-- if y > maxfalloff then + return (maxy-y)/falloff +-- end +end + +local c_air = minetest.get_content_id("air") +local c_web + +local big_webs_path = minetest.get_modpath("big_webs") +if big_webs_path then + c_web = minetest.get_content_id("big_webs:webbing") +end + +local z_displace = 10000 + + +local calculate_web_array = function(minp, maxp) + local seed = math.random()*10000000 + math.randomseed(minp.y + z_displace*minp.z) -- use consistent seeds across the x axis + local webs = {} + for count = 1, math.random(5,20) do + local width = math.random(5, 25) + local direction_vertical = math.random() > 0.5 + local web_y = math.random(minp.y+8, maxp.y-8) + local web_z = math.random(minp.z+8, maxp.z-8) + for i = -math.floor(width/2), math.ceil(width/2) do + if direction_vertical then + webs[(web_y+i) + web_z*z_displace] = true + else + webs[web_y + (web_z+i)*z_displace] = true + end + end + end + math.randomseed(seed) + return webs +end + +minetest.register_on_generated(function(minp, maxp, seed) + if minp.y >= maxy or maxp.y <= miny then + return + end + + -- check if webs are present + local webs + local webs_present = false + if big_webs_path then + local seed = math.random()*10000000 + math.randomseed(minp.y + z_displace*minp.z) -- use consistent seeds across the x axis + if math.random() < web_probability then + webs_present = true + end + math.randomseed(seed) + end + + local vm, emin, emax = minetest.get_mapgen_object("voxelmanip") + vm:get_data(data) + + nobj_chasm = nobj_chasm or minetest.get_perlin_map(np_chasms, {x = emax.x - emin.x + 1 + waver_strength*2, y = emax.y - emin.y + 1, z = emax.z - emin.z + 1}) + nobj_chasm:get_3d_map_flat(vector.subtract(emin, waver_vector), chasm_data) + + nobj_waver = nobj_waver or minetest.get_perlin_map(np_waver, {x = emax.x - emin.x + 1, y = emax.y - emin.y + 1, z = emax.z - emin.z + 1}) + nobj_waver:get_3d_map_flat(emin, waver_data) + + local chasm_area = VoxelArea:new{MinEdge = vector.subtract(emin, waver_vector), MaxEdge = vector.add(emax, waver_vector)} + local data_area = VoxelArea:new{MinEdge = emin, MaxEdge = emax} + + for i, x, y, z in data_area:iterp_xyz(emin, emax) do + local waver = math.min(math.max(math.floor(waver_data[i]+0.5), -waver_strength), waver_strength) + local intensity = get_intensity(y) + if chasm_data[chasm_area:index(x+waver, y, z)]*intensity > chasms_threshold then + if webs_present then + webs = webs or calculate_web_array(minp, maxp) -- only calculate webs when we know we're in a chasm + if webs[y + z*z_displace] and math.random() < 0.85 then -- random holes in the web + data[i] = c_web + minetest.get_node_timer({x=x,y=y,z=z}):start(1) -- this timer will check for unsupported webs + else + data[i] = c_air + end + else + data[i] = c_air + end + end + end + + vm:set_data(data) + vm:calc_lighting() + vm:write_to_map() +end) + +local nobj_local_chasm = minetest.get_perlin(np_chasms) +local nobj_local_waver = minetest.get_perlin(np_waver) + +chasms.is_in_chasm = function(pos) + nobj_local_chasm = nobj_local_chasm or minetest.get_perlin(np_chasms) + nobj_local_waver = nobj_local_waver or minetest.get_perlin(np_waver) + local waver = math.min(math.max(math.floor(nobj_local_waver:get_3d(pos)+0.5), -waver_strength), waver_strength) + local chasm_value = nobj_local_chasm:get_3d({x=pos.x+waver, y=pos.y, z=pos.z}) + return chasm_value*get_intensity(pos.y) > chasms_threshold +end + +-- A little cheaper to run, for mapgens that know they don't have to worry about the tops and bottoms of chasms +chasms.is_in_chasm_without_taper = function(pos) + nobj_local_chasm = nobj_local_chasm or minetest.get_perlin(np_chasms) + nobj_local_waver = nobj_local_waver or minetest.get_perlin(np_waver) + local waver = math.min(math.max(math.floor(nobj_local_waver:get_3d(pos)+0.5), -waver_strength), waver_strength) + local chasm_value = nobj_local_chasm:get_3d({x=pos.x+waver, y=pos.y, z=pos.z}) + return chasm_value > chasms_threshold +end \ No newline at end of file diff --git a/chasms/mod.conf b/chasms/mod.conf new file mode 100644 index 0000000..f0464dc --- /dev/null +++ b/chasms/mod.conf @@ -0,0 +1,3 @@ +name=chasms +depends=mapgen_helper +optional_depends=big_webs \ No newline at end of file diff --git a/chasms/settingtypes.txt b/chasms/settingtypes.txt new file mode 100644 index 0000000..f3995a8 --- /dev/null +++ b/chasms/settingtypes.txt @@ -0,0 +1,5 @@ +chasms_params (Noise params for chasms) noise_params_3d 0, 1, (50, 1000, 3000), 94586, 2, 0.63, 2.0 +chasms_threshold (Noise threshold for chasms) float 0.9 +chasms_maxy (Maximum Y) int -50 +chasms_miny (Minimum Y) int -2500 +chasms_falloff (Taper range when approaching max or min) int 100 \ No newline at end of file diff --git a/df_caverns/level1.lua b/df_caverns/level1.lua index 801501c..774079f 100644 --- a/df_caverns/level1.lua +++ b/df_caverns/level1.lua @@ -10,6 +10,8 @@ local c_spindlestem_white = df_caverns.node_id.spindlestem_white local tower_cap_shrublist local fungiwood_shrublist +local chasms_path = minetest.get_modpath("chasms") + if minetest.get_modpath("df_farming") then tower_cap_shrublist = { df_farming.spawn_plump_helmet_vm, @@ -273,6 +275,18 @@ local decorate_level_1 = function(minp, maxp, seed, vm, node_arrays, area, data) if dry and data[vi] == c_wet_flowstone then data[vi] = c_dry_flowstone end + + if chasms_path then + local pos = area:position(vi) + if chasms.is_in_chasm_without_taper(pos) then + local flooded_caverns = nvals_cave[vi] < 0 -- this indicates if we're in the "flooded" set of caves or not. + if flooded_caverns and pos.y < subsea_level then + data[vi] = c_water + else + data[vi] = c_air + end + end + end end vm:set_param2_data(data_param2) diff --git a/df_caverns/level2.lua b/df_caverns/level2.lua index c503e72..732a214 100644 --- a/df_caverns/level2.lua +++ b/df_caverns/level2.lua @@ -14,6 +14,9 @@ local c_dry_flowstone = df_caverns.node_id.dry_flowstone local c_veinstone = df_caverns.node_id.veinstone local c_pearls = df_caverns.node_id.pearls +local chasms_path = minetest.get_modpath("chasms") + + local wall_vein_perlin_params = { offset = 0, scale = 1, @@ -354,6 +357,19 @@ local decorate_level_2 = function(minp, maxp, seed, vm, node_arrays, area, data) if dry and data[vi] == c_wet_flowstone then data[vi] = c_dry_flowstone end + + if chasms_path then + local pos = area:position(vi) + if chasms.is_in_chasm_without_taper(pos) then + local flooded_caverns = nvals_cave[vi] < 0 -- this indicates if we're in the "flooded" set of caves or not. + if flooded_caverns and pos.y < subsea_level then + data[vi] = c_water + else + data[vi] = c_air + end + end + end + end vm:set_param2_data(data_param2) diff --git a/df_caverns/level3.lua b/df_caverns/level3.lua index 5997a21..f7766e0 100644 --- a/df_caverns/level3.lua +++ b/df_caverns/level3.lua @@ -17,6 +17,9 @@ local c_glow_ore = df_caverns.node_id.glow_ore local c_salty_cobble = df_caverns.node_id.salty_cobble local c_salt_crystal = df_caverns.node_id.salt_crystal local c_sprite = df_caverns.node_id.sprite +local c_webs_egg = df_caverns.node_id.big_webs_egg + +local chasms_path = minetest.get_modpath("chasms") local subsea_level = math.floor(df_caverns.config.level3_min - (df_caverns.config.level3_min - df_caverns.config.level2_min) * 0.33) local flooding_threshold = math.min(df_caverns.config.tunnel_flooding_threshold, df_caverns.config.cavern_threshold) @@ -355,6 +358,7 @@ local decorate_level_3 = function(minp, maxp, seed, vm, node_arrays, area, data) local index2d = mapgen_helper.index2di(minp, maxp, area, vi) local biome_name = get_biome(heatmap[index2d], humiditymap[index2d]) local flooded_caverns = nvals_cave[vi] < 0 -- this indicates if we're in the "flooded" set of caves or not. + local ystride = area.ystride if not (flooded_caverns and minp.y < subsea_level and area:get_y(vi) < subsea_level) then if flooded_caverns or biome_name == "blackcap" then @@ -363,9 +367,15 @@ local decorate_level_3 = function(minp, maxp, seed, vm, node_arrays, area, data) else df_caverns.tunnel_ceiling(minp, maxp, area, vi, nvals_cracks, data, data_param2, false) end + if c_webs_egg and (biome_name == "barren" or biome_name == "blackcap") and nvals_cracks[index2d] > 0.5 and math.random() < 0.1 then + local index = vi-ystride + if data[index] == c_air then + data[index] = c_webs_egg + minetest.get_node_timer(area:position(index)):start(1) + end + end else -- air pockets - local ystride = area.ystride local cracks = nvals_cracks[index2d] if cracks > 0.5 and data[vi-ystride] == c_water then data[vi-ystride] = c_air @@ -459,7 +469,7 @@ local decorate_level_3 = function(minp, maxp, seed, vm, node_arrays, area, data) end ---------------------------------------------- - -- Column material override for dry biome + -- Column material override for dry and icy biomes for _, vi in ipairs(node_arrays.column_nodes) do local index2d = mapgen_helper.index2di(minp, maxp, area, vi) local biome_name = get_biome(heatmap[index2d], humiditymap[index2d]) @@ -476,7 +486,7 @@ local decorate_level_3 = function(minp, maxp, seed, vm, node_arrays, area, data) -- with the full blown generated array rigamarole. hoar_moss_generator = hoar_moss_generator or minetest.get_perlin(hoar_moss_perlin_params) local pos = area:position(vi) - if hoar_moss_generator.get_3d and hoar_moss_generator:get_3d({x=pos.z, y=pos.y, z=pos.x}) > 0.5 then -- TODO: version 0.4.16 gets no hoar moss + if hoar_moss_generator:get_3d({x=pos.z, y=pos.y, z=pos.x}) > 0.5 then data[vi] = c_hoar_moss else data[vi] = c_ice @@ -491,6 +501,19 @@ local decorate_level_3 = function(minp, maxp, seed, vm, node_arrays, area, data) elseif biome_name == "barren" and not flooded_caverns and data[vi] == c_wet_flowstone then data[vi] = c_dry_flowstone end + + if chasms_path then + local pos = area:position(vi) + if chasms.is_in_chasm_without_taper(pos) then + if flooded_caverns and pos.y < subsea_level then + data[vi] = c_water -- this puts a crack in the ice of icy biomes, but why not? A crack in the ice is interesting. + else + data[vi] = c_air + end + end + end + + end vm:set_param2_data(data_param2) diff --git a/df_caverns/mod.conf b/df_caverns/mod.conf index 6ff8691..0c3b855 100644 --- a/df_caverns/mod.conf +++ b/df_caverns/mod.conf @@ -1,4 +1,4 @@ name = df_caverns description = Adds vast underground caverns in the style of Dwarf Fortress, complete with underground flora in diverse biomes. Also adds stalactite/stalagmite decorations in the smaller tunnels. depends = default, subterrane, df_trees, df_mapitems -optional_depends = df_farming, ice_sprites, oil, df_underworld_items, magma_conduits, bones_loot, named_waypoints, name_generator, fireflies \ No newline at end of file +optional_depends = df_farming, ice_sprites, oil, df_underworld_items, magma_conduits, bones_loot, named_waypoints, name_generator, fireflies, chasms, big_webs \ No newline at end of file diff --git a/df_caverns/node_ids.lua b/df_caverns/node_ids.lua index 1cd8de6..f71d708 100644 --- a/df_caverns/node_ids.lua +++ b/df_caverns/node_ids.lua @@ -14,6 +14,11 @@ if minetest.get_modpath("df_farming") then df_caverns.node_id.dead_fungus = minetest.get_content_id("df_farming:dead_fungus") end +if minetest.get_modpath("big_webs") then + df_caverns.node_id.big_webs = minetest.get_content_id("big_webs:webbing") + df_caverns.node_id.big_webs_egg = minetest.get_content_id("big_webs:web_egg") +end + df_caverns.node_id.air = minetest.get_content_id("air") df_caverns.node_id.cobble = minetest.get_content_id("default:cobble") diff --git a/df_caverns/screenshots/chasm.jpg b/df_caverns/screenshots/chasm.jpg new file mode 100644 index 0000000..c349f93 Binary files /dev/null and b/df_caverns/screenshots/chasm.jpg differ diff --git a/df_caverns/shared.lua b/df_caverns/shared.lua index cb93210..733e257 100644 --- a/df_caverns/shared.lua +++ b/df_caverns/shared.lua @@ -18,12 +18,30 @@ local c_spongestone = df_caverns.node_id.spongestone local c_rock_rot = df_caverns.node_id.rock_rot local c_water = df_caverns.node_id.water local c_wet_flowstone = df_caverns.node_id.wet_flowstone +local c_webs = df_caverns.node_id.big_webs +local c_webs_egg = df_caverns.node_id.big_webs_egg df_caverns.data_param2 = {} +-- prevent mapgen from using these nodes as a base for stalactites or stalagmites +local dont_build_speleothems_on = {} +for _, content_id in pairs(df_mapitems.wet_stalagmite_ids) do + dont_build_speleothems_on[content_id] = true +end +for _, content_id in pairs(df_mapitems.dry_stalagmite_ids) do + dont_build_speleothems_on[content_id] = true +end +if minetest.get_modpath("big_webs") then + dont_build_speleothems_on[c_webs] = true + dont_build_speleothems_on[c_webs_egg] = true +end + -------------------------------------------------- df_caverns.stalagmites = function(abs_cracks, vert_rand, vi, area, data, data_param2, wet, reverse_sign) + if dont_build_speleothems_on[data[vi]] then + return + end local flowstone local stalagmite_ids if wet then @@ -118,13 +136,6 @@ df_caverns.glow_worm_cavern_ceiling = function(abs_cracks, vert_rand, vi, area, end end -local content_in_list=function(content, list) - for i, v in ipairs(list) do - if content == v then return true end - end - return false -end - df_caverns.tunnel_floor = function(minp, maxp, area, vi, nvals_cracks, data, data_param2, wet, dirt_node) if maxp.y > -30 then wet = false @@ -135,7 +146,7 @@ df_caverns.tunnel_floor = function(minp, maxp, area, vi, nvals_cracks, data, dat local abs_cracks = math.abs(cracks) if wet then - if abs_cracks < 0.05 and data[vi+ystride] == c_air and not content_in_list(data[vi], df_mapitems.wet_stalagmite_ids) then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites + if abs_cracks < 0.05 and data[vi+ystride] == c_air and not dont_build_speleothems_on[data[vi]] then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites local param2 = abs_cracks*1000000 - math.floor(abs_cracks*1000000/4)*4 local height = math.floor(abs_cracks * 100) subterrane.stalagmite(vi+ystride, area, data, data_param2, param2, height, df_mapitems.wet_stalagmite_ids) @@ -144,7 +155,7 @@ df_caverns.tunnel_floor = function(minp, maxp, area, vi, nvals_cracks, data, dat data[vi] = dirt_node end else - if abs_cracks < 0.025 and data[vi+ystride] == c_air and not content_in_list(data[vi], df_mapitems.dry_stalagmite_ids) then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites + if abs_cracks < 0.025 and data[vi+ystride] == c_air and not dont_build_speleothems_on[data[vi]] then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites local param2 = abs_cracks*1000000 - math.floor(abs_cracks*1000000/4)*4 local height = math.floor(abs_cracks * 100) subterrane.stalagmite(vi+ystride, area, data, data_param2, param2, height, df_mapitems.dry_stalagmite_ids) @@ -165,14 +176,14 @@ df_caverns.tunnel_ceiling = function(minp, maxp, area, vi, nvals_cracks, data, d local abs_cracks = math.abs(cracks) if wet then - if abs_cracks < 0.05 and data[vi-ystride] == c_air and not content_in_list(data[vi], df_mapitems.wet_stalagmite_ids) then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites + if abs_cracks < 0.05 and data[vi-ystride] == c_air and not dont_build_speleothems_on[data[vi]] then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites local param2 = abs_cracks*1000000 - math.floor(abs_cracks*1000000/4)*4 local height = math.floor(abs_cracks * 100) subterrane.stalactite(vi-ystride, area, data, data_param2, param2, height, df_mapitems.wet_stalagmite_ids) data[vi] = c_wet_flowstone end else - if abs_cracks < 0.025 and data[vi-ystride] == c_air and not content_in_list(data[vi], df_mapitems.dry_stalagmite_ids) then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites + if abs_cracks < 0.025 and data[vi-ystride] == c_air and not dont_build_speleothems_on[data[vi]] then -- make sure data[vi] is not already flowstone. Stalagmites from lower levels are acting as base for further stalagmites local param2 = abs_cracks*1000000 - math.floor(abs_cracks*1000000/4)*4 local height = math.floor(abs_cracks * 100) subterrane.stalactite(vi-ystride, area, data, data_param2, param2, height, df_mapitems.dry_stalagmite_ids) diff --git a/df_caverns/sunless_sea.lua b/df_caverns/sunless_sea.lua index 8a8e0b6..4625ccc 100644 --- a/df_caverns/sunless_sea.lua +++ b/df_caverns/sunless_sea.lua @@ -9,6 +9,8 @@ local c_dry_flowstone = df_caverns.node_id.dry_flowstone local c_lava = df_caverns.node_id.lava local c_obsidian = df_caverns.node_id.obsidian +local chasms_path = minetest.get_modpath("chasms") + local c_coral_table = {} for node_name, node_def in pairs(minetest.registered_nodes) do if minetest.get_item_group(node_name, "dfcaverns_cave_coral") > 0 then @@ -383,6 +385,17 @@ local decorate_sunless_sea = function(minp, maxp, seed, vm, node_arrays, area, d data_param2[vi] = math.random(1,4)-1 minetest.get_node_timer(area:position(vi)):start(math.random(10, 60)) end + + if chasms_path then + local pos = area:position(vi) + if chasms.is_in_chasm(pos) then + if pos.y <= sea_level then + data[vi] = c_water + else + data[vi] = c_air + end + end + end end end diff --git a/df_mapitems/veinstone.lua b/df_mapitems/veinstone.lua index db95d4c..291dd0e 100644 --- a/df_mapitems/veinstone.lua +++ b/df_mapitems/veinstone.lua @@ -9,6 +9,58 @@ minetest.register_node("df_mapitems:veinstone", { _magma_conduits_heats_to = df_mapitems.node_name.cobble, is_ground_content = false, light_source = 2, - drop = df_mapitems.node_name.cobble, + drop = "df_mapitems:veinstone", sounds = df_mapitems.sounds.stone, + on_punch = function(pos, node, puncher, pointed_thing) + minetest.node_punch(pos, node, puncher, pointed_thing) + minetest.swap_node(pos, {name="df_mapitems:veinstone_pulse"}) + minetest.get_node_timer(pos):start(2) + end, +}) + +minetest.register_node("df_mapitems:veinstone_pulse", { + description = S("Veinstone"), + _doc_items_longdesc = df_mapitems.doc.veinstone_desc, + _doc_items_usagehelp = df_mapitems.doc.veinstone_usage, + tiles = {df_mapitems.texture.stone .. "^dfcaverns_veins.png"}, + groups = {cracky = 3, stone = 1, lava_heatable = 1, not_in_creative_inventory = 1}, + _magma_conduits_heats_to = df_mapitems.node_name.cobble, + is_ground_content = false, + light_source = 8, + drop = "df_mapitems:veinstone", + sounds = df_mapitems.sounds.stone, + on_timer = function(pos, elapsed) + local positions, count = minetest.find_nodes_in_area(vector.add(pos,1), vector.subtract(pos,1), "df_mapitems:veinstone") + if count["df_mapitems:veinstone"] == 0 then + positions, count = minetest.find_nodes_in_area(vector.add(pos,2), vector.subtract(pos,2), "df_mapitems:veinstone") + end + if count["df_mapitems:veinstone"] == 0 then + positions = {[1] = minetest.find_node_near(pos, 3, "df_mapitems:veinstone")} + end + if positions[1] == nil then + positions = {[1] = minetest.find_node_near(pos, 4, "df_mapitems:veinstone")} + end + for _, neighbor_pos in pairs(positions) do + minetest.swap_node(neighbor_pos, {name="df_mapitems:veinstone_pulse"}) + minetest.get_node_timer(neighbor_pos):start(2) + end + minetest.swap_node(pos, {name="df_mapitems:veinstone_refractory"}) + minetest.get_node_timer(pos):start(12) + end, +}) + +minetest.register_node("df_mapitems:veinstone_refractory", { + description = S("Veinstone"), + _doc_items_longdesc = df_mapitems.doc.veinstone_desc, + _doc_items_usagehelp = df_mapitems.doc.veinstone_usage, + tiles = {df_mapitems.texture.stone .. "^dfcaverns_veins.png"}, + groups = {cracky = 3, stone = 1, lava_heatable = 1, not_in_creative_inventory = 1}, + _magma_conduits_heats_to = df_mapitems.node_name.cobble, + is_ground_content = false, + light_source = 1, + drop = "df_mapitems:veinstone", + sounds = df_mapitems.sounds.stone, + on_timer = function(pos, elapsed) + minetest.swap_node(pos, {name="df_mapitems:veinstone"}) + end, }) \ No newline at end of file diff --git a/guide.md b/guide.md index 6eda6dc..4cb6df4 100644 --- a/guide.md +++ b/guide.md @@ -160,6 +160,14 @@ Quarry bushes can be found here. On the third cavern layer the dry barren caverns will sprout clusters of enormous hexagonal red crystals from their floors and ceilings, providing light. These crystals have no particular use but are rare and beautiful to look at. +# Chasms + +![A chasm](./df_caverns/screenshots/chasm.jpg) + +Not all vast open spaces underground are the result of aeons of erosion by water or magma. The foundations of the world shift from time to time, causing deep faults to split open in the rock. Yawning chasms can be found oriented along the north-south axis, some of them stretching for kilometers both in length and depth. They cross through multiple cavern layers and are an environment in their own right. Chasms can be a convenient way of traveling long distances and also a convenient way of falling to your death. + +The great extent of chasms makes them hospitable to small flying creatures, and their narrowness makes the hospitable to creatures that feed on them - giant cave spider webs can be found strung across them here and there. A dubious salvation for anyone falling from above. + # Sunless Sea ![Sunless river](./df_caverns/screenshots/sunless_river.jpg) @@ -174,7 +182,7 @@ The distinguishing feature of the Sunless Sea is the vast expanse of water to be Below the surface of the water Snareweed can be found, a dangerous kelp-like growth that uses bioluminescence and reflective patches to lure cave fish close enough to snag with their barbs. Swimming through Snareweed is a painful experience. Deeper down are craggy towers of softly glowing Cave Coral. -This is the last stop for the living world, however. Caverns continue to stretch below but nothing grows down there. +This is the last stop for the living world. Caverns continue to stretch below but nothing grows below. # Lakes of Oil @@ -200,9 +208,9 @@ The Magma Sea is not without its treasures, however. In the most infernal region ![There are older and fouler things than orcs in the deep places of the world](./df_caverns/screenshots/underworld.jpg) -The foundations of the world lie at -3100 meters, under a default configuration. The diggable rock of the world ends at a rippling layer of invulnerable Slade, a material of unparalleled density. There is a cavern layer at the seam between rock and Slade, however, as if the earth itself was reluctant to touch the strange matter below. The crevices of the ceiling have strange glowing rocks in them, producing a surprisingly bright ambiance. Don't attempt to disturb those rocks. +The foundations of the world lie at -3100 meters. The diggable rock of the world ends at a rippling layer of invulnerable Slade, a material of unparalleled density. There is a cavern layer at the seam between rock and Slade, however, as if the earth itself was reluctant to touch the strange matter below. The crevices of the ceiling have strange glowing rocks in them, producing a surprisingly bright ambiance. Don't attempt to disturb those rocks. -There is no native life in the Underworld. It appears there once _was_, however. Occasional clusters of vacant buildings can be found, impossibly crafted from bricks of Slade and empty of any furnishings. Around those clusters of vacant buildings are fields filled with sealed pits lined with flawless Slade blocks. It is unclear whether these pits are as vacant as the buildings, though. The seals capping them are engraved with ancient words in lost tongues, but one phrase can be translated from the oldest known languages: "This Place is Not a Place of Honor." +There is no native life in the Underworld. It appears there once _was_, however. Occasional clusters of vacant buildings can be found, impossibly crafted from bricks of Slade and empty of any furnishings. Around those clusters of vacant buildings are fields filled with sealed pits that are lined with flawless Slade blocks. It is unclear whether these pits are as vacant as the buildings, though. The seals capping them are engraved with ancient words in lost tongues, but one phrase can be translated from the oldest known languages: "This Place is Not a Place of Honor." Just as ominously, the ancient bones of long-dead warriors are scattered amidst the buildings. They contain loot for those willing to risk disturbing them. There are no traces of who - or what - they died fighting, but their ancient sentinels still lurk nearby to guard them. diff --git a/hunter_statue/init.lua b/hunter_statue/init.lua index d3642bd..92f733a 100644 --- a/hunter_statue/init.lua +++ b/hunter_statue/init.lua @@ -114,7 +114,7 @@ hunter_statue.register_hunter_statue = function(node_name, statue_def) if fleshy_armour then armour_multiplier = fleshy_armour/100 end - nearest_player:add_player_velocity(vector.multiply(vector.direction(pos, nearest_pos), knockback)) + nearest_player:add_velocity(vector.multiply(vector.direction(pos, nearest_pos), knockback)) nearest_player:set_hp(math.max(nearest_player:get_hp() - damage*armour_multiplier, 0)) minetest.sound_play({name="hunter_statue_thud"}, {pos = nearest_pos}) return diff --git a/subterrane b/subterrane index 6439ca5..6cc2fe8 160000 --- a/subterrane +++ b/subterrane @@ -1 +1 @@ -Subproject commit 6439ca59bfe8e3e4c5ed0826f41d2c49b862fb75 +Subproject commit 6cc2fe8faea4026101dc9f462a7594edfc26ca7f