write something there
This commit is contained in:
commit
b4b6c08f4f
8546 changed files with 309825 additions and 0 deletions
122
mods/worldgate/src/functions.lua
Normal file
122
mods/worldgate/src/functions.lua
Normal file
|
@ -0,0 +1,122 @@
|
|||
--
|
||||
-- Worldgate functions
|
||||
--
|
||||
|
||||
-- Function for selecting a random base schematic
|
||||
local base_schematics = (function()
|
||||
local schems = {}
|
||||
for _,schematic in ipairs(minetest.get_dir_list(worldgate.modpath .. "/schematics/base/",false)) do
|
||||
table.insert(schems,worldgate.modpath .. "/schematics/base/" .. schematic)
|
||||
end
|
||||
return schems
|
||||
end)()
|
||||
local base_count = #base_schematics
|
||||
worldgate.schematics = {}
|
||||
worldgate.schematics.base = base_schematics
|
||||
|
||||
function worldgate.get_random_base(pcgr)
|
||||
return base_schematics[pcgr and pcgr:next(1,base_count) or math.random(1,base_count)]
|
||||
end
|
||||
|
||||
-- Function for selecting a random decor schematic
|
||||
local decor_schematics = (function()
|
||||
local schems = {}
|
||||
for _,schematic in ipairs(minetest.get_dir_list(worldgate.modpath .. "/schematics/decor/",false)) do
|
||||
table.insert(schems,worldgate.modpath .. "/schematics/decor/" .. schematic)
|
||||
end
|
||||
return schems
|
||||
end)()
|
||||
local decor_count = #decor_schematics
|
||||
worldgate.schematics.decor = decor_schematics
|
||||
|
||||
function worldgate.get_random_decor(pcgr)
|
||||
return decor_schematics[pcgr and pcgr:next(1,decor_count) or math.random(1,decor_count)]
|
||||
end
|
||||
|
||||
-- Function for selecting a random quality value
|
||||
function worldgate.get_random_quality(pcgr)
|
||||
return pcgr and pcgr:next(-1,1) or math.random(-1,1)
|
||||
end
|
||||
|
||||
-- Function for adding new worldgates without data checks
|
||||
function worldgate.add_gate_unsafe(def)
|
||||
-- Add gate to list of gates
|
||||
local ngates = #worldgate.gates
|
||||
worldgate.gates[ngates + 1] = def
|
||||
|
||||
-- Index the gate via mapblock hashing
|
||||
local hash = minetest.hash_node_position(def.position:divide(16):floor())
|
||||
local gates = worldgate.hash_index[hash] or {}
|
||||
gates[#gates + 1] = ngates + 1
|
||||
worldgate.hash_index[hash] = gates
|
||||
end
|
||||
|
||||
-- Function for adding new worldgates
|
||||
local ymin = math.max(-29900,worldgate.settings.ymin)
|
||||
local ymax = math.min(29900,worldgate.settings.ymax)
|
||||
function worldgate.add_gate(def)
|
||||
-- Position must be a valid vector
|
||||
if not def.position then
|
||||
error("Attempted to add a worldgate without a position")
|
||||
elseif not vector.check(def.position) then
|
||||
error("Worldgate position must be a vector created with vector.new")
|
||||
elseif def.position.y > ymax or def.position.y < ymin then
|
||||
error("Worldgate position " .. minetest.pos_to_string(def.position) .. " is beyond ymin/ymax values")
|
||||
end
|
||||
|
||||
if not def.base then
|
||||
def.base = worldgate.get_random_base()
|
||||
elseif type(def.base) ~= "string" then
|
||||
error("Worldgate base must be a string that identifies a schematic")
|
||||
end
|
||||
|
||||
if not def.decor then
|
||||
def.decor = worldgate.get_random_decor()
|
||||
elseif type(def.decor) ~= "string" then
|
||||
error("Worldgate decor must be a string that identifies a schematic")
|
||||
end
|
||||
|
||||
if not def.quality then
|
||||
def.quality = worldgate.get_random_quality()
|
||||
elseif not (def.quality == -1 or def.quality == 0 or def.quality == 1) then
|
||||
error("Worldgate quality must be an integer between -1 and 1 inclusive")
|
||||
end
|
||||
|
||||
if def.exact == nil then
|
||||
def.exact = false
|
||||
else
|
||||
def.exact = not not def.exact -- boolean cast
|
||||
end
|
||||
|
||||
-- Add gate via unsafe function
|
||||
worldgate.add_gate_unsafe(def)
|
||||
end
|
||||
|
||||
-- Function for checking a mapblock against the gate hash index
|
||||
function worldgate.get_gates_for_mapblock(pos)
|
||||
local gates = worldgate.hash_index[minetest.hash_node_position(pos:divide(16):floor())] or {}
|
||||
for i = 0, #gates do
|
||||
gates[i] = worldgate.gates[gates[i]]
|
||||
end
|
||||
return gates
|
||||
end
|
||||
|
||||
-- Function for finding a suitable placement for a worldgate within a given area
|
||||
function worldgate.find_worldgate_location_in_area(minp,maxp)
|
||||
end
|
||||
|
||||
-- Function for spawning a worldgate
|
||||
function worldgate.generate_gate(pos)
|
||||
end
|
||||
|
||||
-- Function for gate generation callbacks
|
||||
worldgate.worldgate_generated_callbacks = {}
|
||||
function worldgate.reigster_on_worldgate_generated(fn)
|
||||
table.insert(worldgate.worldgate_generated_callbacks,fn)
|
||||
end
|
||||
|
||||
-- Function for failed gate generation callbacks
|
||||
worldgate.worldgate_failed_callbacks = {}
|
||||
function worldgate.reigster_on_worldgate_failed(fn)
|
||||
table.insert(worldgate.worldgate_failed_callbacks,fn)
|
||||
end
|
123
mods/worldgate/src/gates.lua
Normal file
123
mods/worldgate/src/gates.lua
Normal file
|
@ -0,0 +1,123 @@
|
|||
--
|
||||
-- Native worldgate generation
|
||||
--
|
||||
|
||||
-- Generate gates if the mod is configured to do so
|
||||
if worldgate.settings.native then
|
||||
-- Define rng based on the world seed
|
||||
local pcgr = PcgRandom(minetest.get_mapgen_setting("seed"))
|
||||
|
||||
-- Get distance between gates (spread)
|
||||
local spread = worldgate.settings.native_spread
|
||||
|
||||
-- Do not generate gates beyond totalmax to prevent any wierdness with world
|
||||
-- boundaries
|
||||
local totalmax = 29900
|
||||
|
||||
-- Get minimum and maximum y values
|
||||
local ymin = math.max(-totalmax,worldgate.settings.ymin)
|
||||
local ymax = math.min(totalmax,worldgate.settings.ymax)
|
||||
|
||||
-- Cache frequently used global functions for better performance
|
||||
local add_gate = worldgate.add_gate_unsafe -- native gates are made with respect to checks
|
||||
local get_random_base = worldgate.get_random_base
|
||||
local get_random_decor = worldgate.get_random_decor
|
||||
local get_random_quality = worldgate.get_random_quality
|
||||
local vn = vector.new
|
||||
|
||||
-- Align coordinate values with 0 for more centered/grounded generation
|
||||
local alignment_offset = totalmax - spread * math.floor(totalmax / spread)
|
||||
local ymin_offset = -ymin - spread * math.floor(-ymin / spread)
|
||||
local ymax_offset = ymax - spread * math.floor(ymax / spread)
|
||||
local min = -totalmax + alignment_offset
|
||||
local max = totalmax - alignment_offset
|
||||
ymin = ymin + ymin_offset
|
||||
ymax = ymax - ymax_offset
|
||||
|
||||
-- Generate x/z values with jitter so that worldgate locations are less
|
||||
-- predictable
|
||||
local xzjitterp = math.floor(spread * worldgate.settings.native_xzjitter / 100)
|
||||
local xzjittern = -xzjitterp
|
||||
|
||||
-- Function to get a gate based on an x/y/z coordinate
|
||||
local function generate_gate(x,y,z)
|
||||
return {
|
||||
position = vn(x + pcgr:next(xzjittern,xzjitterp),y + pcgr:next(0,24),z + pcgr:next(xzjittern,xzjitterp)),
|
||||
base = get_random_base(pcgr),
|
||||
decor = get_random_decor(pcgr),
|
||||
quality = get_random_quality(pcgr),
|
||||
exact = false,
|
||||
}
|
||||
end
|
||||
|
||||
-- Probability table for gate quality bias in favor of average and above
|
||||
-- average quality values (20% poor, 50% average, 30% above average)
|
||||
local quality_bias = {-1,-1,0,0,0,0,0,1,1,1}
|
||||
|
||||
-- Link gates if configured, else generate gates with no destinations
|
||||
if worldgate.settings.native_link then
|
||||
-- Generate x/z gate definitions for y = 0 since these are the destination
|
||||
-- gates of all native gates
|
||||
local surface_gates = {}
|
||||
for x = min, max, spread do
|
||||
for z = min, max, spread do
|
||||
local pos = vn(x,0,z)
|
||||
local hpos = minetest.hash_node_position(pos)
|
||||
surface_gates[hpos] = {
|
||||
position = vn(x + pcgr:next(xzjittern,xzjitterp),pcgr:next(0,24),z + pcgr:next(xzjittern,xzjitterp)),
|
||||
base = get_random_base(pcgr),
|
||||
decor = get_random_decor(pcgr),
|
||||
quality = quality_bias[pcgr:next(1,10)],
|
||||
exact = false,
|
||||
destination = (function() -- placeholder to be converted to an actual gate
|
||||
local nhashes = {
|
||||
minetest.hash_node_position(vn(x == min and x + spread or x - spread,0,z == min and z + spread or z - spread)),
|
||||
minetest.hash_node_position(vn(x == min and x + spread or x - spread,0,z == max and z - spread or z + spread)),
|
||||
minetest.hash_node_position(vn(x == max and x - spread or x + spread,0,z == min and z + spread or z - spread)),
|
||||
minetest.hash_node_position(vn(x == max and x - spread or x + spread,0,z == max and z - spread or z + spread)),
|
||||
}
|
||||
local neighbors = {}
|
||||
for n = 1, 4 do
|
||||
n = nhashes[n]
|
||||
local g = surface_gates[n]
|
||||
if not g or g.destination ~= hpos then
|
||||
neighbors[#neighbors + 1] = n
|
||||
end
|
||||
end
|
||||
return neighbors[1] and neighbors[pcgr:next(1,#neighbors)] or nhashes[pcgr:next(1,4)]
|
||||
end)(),
|
||||
}
|
||||
end
|
||||
|
||||
-- Override gate generation function to generate destinations
|
||||
generate_gate = function(x,y,z)
|
||||
local pos = vn(x,y,z)
|
||||
local gate
|
||||
if y == 0 then
|
||||
gate = surface_gates[minetest.hash_node_position(pos)]
|
||||
gate.destination = surface_gates[gate.destination].position
|
||||
else
|
||||
gate = {
|
||||
position = vn(x + pcgr:next(xzjittern,xzjitterp),y + pcgr:next(0,24),z + pcgr:next(xzjittern,xzjitterp)),
|
||||
base = get_random_base(pcgr),
|
||||
decor = get_random_decor(pcgr),
|
||||
quality = get_random_quality(pcgr),
|
||||
exact = false,
|
||||
}
|
||||
pos.y = 0
|
||||
gate.destination = surface_gates[minetest.hash_node_position(pos)].position
|
||||
end
|
||||
return gate
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Generate gates
|
||||
for x = min, max, spread do
|
||||
for y = ymin, ymax, spread do
|
||||
for z = min, max, spread do
|
||||
add_gate(generate_gate(x,y,z))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
188
mods/worldgate/src/link.lua
Normal file
188
mods/worldgate/src/link.lua
Normal file
|
@ -0,0 +1,188 @@
|
|||
--
|
||||
-- Worldgate linking functions
|
||||
--
|
||||
|
||||
-- Telemosaic location hashing and unhashing functions
|
||||
local function hash_pos(pos)
|
||||
return math.floor(pos.x + 0.5)..':'..
|
||||
math.floor(pos.y + 0.5)..':'..
|
||||
math.floor(pos.z + 0.5)
|
||||
end
|
||||
|
||||
local function unhash_pos(hash)
|
||||
local list = string.split(hash, ':')
|
||||
local p = {
|
||||
x = tonumber(list[1]),
|
||||
y = tonumber(list[2]),
|
||||
z = tonumber(list[3])
|
||||
}
|
||||
if p.x and p.y and p.z then
|
||||
return p
|
||||
end
|
||||
end
|
||||
|
||||
-- Polyfill for vector.in_area
|
||||
if not vector.in_area then
|
||||
function vector.in_area(pos, min, max)
|
||||
return (pos.x >= min.x) and (pos.x <= max.x) and
|
||||
(pos.y >= min.y) and (pos.y <= max.y) and
|
||||
(pos.z >= min.z) and (pos.z <= max.z)
|
||||
end
|
||||
end
|
||||
|
||||
-- Garbage collection index to manage force-loaded chunks
|
||||
local forceloader = {
|
||||
index = {},
|
||||
load = function(self,blockpos)
|
||||
-- Get the reference counter for the mapblock
|
||||
local hash = minetest.hash_node_position(blockpos)
|
||||
local refcounter = self.index[hash] or 0
|
||||
|
||||
-- Force-load mapblock if it is not yet force-loaded
|
||||
if refcounter == 0 then
|
||||
minetest.forceload_block(blockpos,true)
|
||||
end
|
||||
|
||||
-- Store updated reference counter
|
||||
refcounter = refcounter + 1
|
||||
self.index[hash] = refcounter
|
||||
end,
|
||||
unload = function(self,blockpos)
|
||||
-- Get the reference counter for the mapblock
|
||||
local hash = minetest.hash_node_position(blockpos)
|
||||
local refcounter = self.index[hash] or 1
|
||||
|
||||
-- Decrement reference counter and unload mapblock if there are no more
|
||||
-- references to it
|
||||
refcounter = refcounter - 1
|
||||
if refcounter == 0 then
|
||||
minetest.forceload_free_block(blockpos,true)
|
||||
end
|
||||
|
||||
-- Store updated reference counter
|
||||
self.index[hash] = refcounter
|
||||
end,
|
||||
}
|
||||
|
||||
-- Function for linking and force-loading gate destinations
|
||||
function worldgate.link(pos)
|
||||
-- Get meta/destination
|
||||
local nodemeta = minetest.get_meta(pos)
|
||||
local destination = nodemeta:get("telemosaic:dest")
|
||||
local do_worldgate_link = false
|
||||
|
||||
-- Check for worldgate destination and convert to vector
|
||||
local dpos = nil
|
||||
if not destination then
|
||||
dpos = nodemeta:get("worldgate:destination")
|
||||
if not dpos then
|
||||
return true
|
||||
end
|
||||
destination = minetest.string_to_pos(dpos)
|
||||
do_worldgate_link = true
|
||||
else
|
||||
destination = vector.new(unhash_pos(destination))
|
||||
end
|
||||
|
||||
-- Look for players near this gate
|
||||
local players = minetest.get_connected_players()
|
||||
for i = 1, #players do
|
||||
-- If a player is found, then force-load the destination
|
||||
local player = players[i]
|
||||
if player and player:get_pos():in_area(pos:add(-32),pos:add(32)) then
|
||||
if not nodemeta:get("worldgate:player_nearby") then
|
||||
-- Define destination area to emerge
|
||||
local mapblock = destination:divide(16):floor():multiply(16)
|
||||
local emin = mapblock:add(vector.new(0,-32,0))
|
||||
local emax = mapblock:add(vector.new(0,47,0))
|
||||
|
||||
-- Emerge the destination mapblocks
|
||||
minetest.emerge_area(emin,emax,function(blockpos,action,calls_remaining)
|
||||
-- Do worldgate link to Telemosaic if defined for this gate
|
||||
if calls_remaining == 0 then
|
||||
if do_worldgate_link then
|
||||
destination = nil
|
||||
local beacons = minetest.find_nodes_in_area(emin:add(vector.new(-1,-1,-1)),emax:add(vector.new(1,1,1)),"group:telemosaic")
|
||||
for beacon = 1, #beacons do
|
||||
beacon = beacons[beacon]
|
||||
local spos = minetest.get_meta(beacon):get("worldgate:source")
|
||||
if dpos == spos then
|
||||
destination = beacon
|
||||
nodemeta:set_string("telemosaic:dest",hash_pos(beacon))
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if destination then
|
||||
-- Force-load destination mapblocks and own mapblock
|
||||
forceloader:load(pos:divide(16):floor())
|
||||
for p = -32, 32, 16 do
|
||||
forceloader:load(destination:add(vector.new(0,p,0)):divide(16):floor())
|
||||
end
|
||||
else
|
||||
-- Beacon cannot be linked
|
||||
minetest.swap_node(pos,{ name = "telemosaic:beacon_off", param2 = 0 })
|
||||
return false
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Flag this beacon as having a player nearby
|
||||
nodemeta:set_string("worldgate:player_nearby","1")
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- No players found, unload all force-loaded blocks and unflag the beacon
|
||||
if nodemeta:get("worldgate:player_nearby") then
|
||||
forceloader:unload(pos:divide(16):floor())
|
||||
for p = -32, 32, 16 do
|
||||
forceloader:unload(destination:add(vector.new(0,p,0)):divide(16):floor())
|
||||
end
|
||||
nodemeta:set_string("worldgate:player_nearby","")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- Run linking function on node timer
|
||||
for _,beacon in ipairs({
|
||||
"telemosaic:beacon",
|
||||
"telemosaic:beacon_protected",
|
||||
}) do
|
||||
minetest.override_item(beacon,{
|
||||
on_timer = worldgate.link,
|
||||
})
|
||||
end
|
||||
|
||||
-- Queue for linking function calls
|
||||
local linkq = {}
|
||||
|
||||
-- Run linking function when a player teleports
|
||||
local tt = telemosaic.teleport
|
||||
telemosaic.teleport = function(player,src,dest)
|
||||
tt(player,src,dest)
|
||||
dest.y = dest.y - 1
|
||||
worldgate.link(vector.new(src))
|
||||
worldgate.link(vector.new(dest))
|
||||
end
|
||||
|
||||
-- Run linking function when a beacon changes its state
|
||||
local tss = telemosaic.set_state
|
||||
telemosaic.set_state = function(pos,state)
|
||||
tss(pos,state)
|
||||
worldgate.link(vector.new(pos))
|
||||
end
|
||||
|
||||
-- Register LBM to jump-start force-load timers
|
||||
minetest.register_abm({
|
||||
label = "Run worldgate linking function",
|
||||
nodenames = "group:telemosaic",
|
||||
interval = 3,
|
||||
chance = 1,
|
||||
catch_up = false,
|
||||
action = function(pos)
|
||||
worldgate.link(pos)
|
||||
end,
|
||||
})
|
9
mods/worldgate/src/logging.lua
Normal file
9
mods/worldgate/src/logging.lua
Normal file
|
@ -0,0 +1,9 @@
|
|||
-- Logging for successfully generated worldgates
|
||||
worldgate.reigster_on_worldgate_generated(function(pos,gate,strategy)
|
||||
minetest.log("action","Worldgate generated at " .. minetest.pos_to_string(pos) .. " using the " .. strategy .. " strategy")
|
||||
end)
|
||||
|
||||
-- Logging for worldgates that failed to generate
|
||||
worldgate.reigster_on_worldgate_failed(function(gate)
|
||||
minetest.log("warning","Worldgate failed to generate" .. (gate.exact and " at " or " near ") .. minetest.pos_to_string(gate.position))
|
||||
end)
|
301
mods/worldgate/src/mapgen.lua
Normal file
301
mods/worldgate/src/mapgen.lua
Normal file
|
@ -0,0 +1,301 @@
|
|||
--
|
||||
-- Worldgate mapgen
|
||||
--
|
||||
|
||||
-- Do not register mapgen if worldgate mapgen is disabled
|
||||
if not worldgate.settings.mapgen then
|
||||
return
|
||||
end
|
||||
|
||||
-- Spawning flags
|
||||
local underwaterspawn = worldgate.settings.underwaterspawn
|
||||
local midairspawn = worldgate.settings.midairspawn
|
||||
|
||||
-- Telemosaic location hashing function
|
||||
local function hash_pos(pos)
|
||||
return math.floor(pos.x + 0.5)..':'..
|
||||
math.floor(pos.y + 0.5)..':'..
|
||||
math.floor(pos.z + 0.5)
|
||||
end
|
||||
|
||||
-- Common cached variables and functions
|
||||
local vmcache = {} -- VoxelManip data cache, increases performance
|
||||
|
||||
local schematic_airspace = worldgate.modpath .. "/schematics/worldgate_airspace.mts"
|
||||
local schematic_platform = worldgate.modpath .. "/schematics/worldgate_platform.mts"
|
||||
|
||||
local extender_break_chance = worldgate.settings.breakage
|
||||
|
||||
local quality_selector = {
|
||||
[0] = function(pcgr) -- 25% chance for tier 1 extender to be cobblestone
|
||||
return { name = (pcgr:next(1,4) == 1 and "default:cobble" or "telemosaic:extender_one"), param2 = 0 }
|
||||
end,
|
||||
function() return { name = "telemosaic:extender_one", param2 = 0 } end,
|
||||
function() return { name = "telemosaic:extender_two", param2 = 0 } end,
|
||||
function() return { name = "telemosaic:extender_three", param2 = 0 } end,
|
||||
function() return { name = "telemosaic:extender_three", param2 = 0 } end,
|
||||
}
|
||||
|
||||
local water = {
|
||||
[minetest.get_content_id("mapgen_water_source")] = true,
|
||||
[minetest.get_content_id("mapgen_river_water_source")] = true,
|
||||
}
|
||||
|
||||
local vn = vector.new
|
||||
|
||||
-- Disallowed nodes to spawn on; no prefix means group
|
||||
local disallowed_nodes = {
|
||||
"leaves",
|
||||
"tree",
|
||||
}
|
||||
local disallowed_nodes_length = #disallowed_nodes
|
||||
local disallowed_cids = {[minetest.CONTENT_AIR] = true}
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
for node,def in pairs(minetest.registered_nodes) do
|
||||
for i = 1, disallowed_nodes_length do
|
||||
i = disallowed_nodes[i]
|
||||
if node == i or minetest.get_item_group(node,i) > 0 then
|
||||
disallowed_cids[minetest.get_content_id(node)] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Bricks to cobblestone map for quality degradation
|
||||
local bricks_list = {
|
||||
"default:stonebrick",
|
||||
"stairs:stair_stonebrick",
|
||||
"stairs:stair_inner_stonebrick",
|
||||
"stairs:stair_outer_stonebrick",
|
||||
"stairs:slab_stonebrick",
|
||||
}
|
||||
|
||||
local bricks_map = {
|
||||
["default:stonebrick"] = "default:cobble",
|
||||
["stairs:stair_stonebrick"] = "stairs:stair_cobble",
|
||||
["stairs:stair_inner_stonebrick"] = "stairs:stair_inner_cobble",
|
||||
["stairs:stair_outer_stonebrick"] = "stairs:stair_outer_cobble",
|
||||
["stairs:slab_stonebrick"] = "stairs:slab_cobble",
|
||||
}
|
||||
|
||||
local bricks_degrade_chance = {
|
||||
[-1] = 9,
|
||||
[0] = 42,
|
||||
[1] = 200,
|
||||
}
|
||||
|
||||
-- Worldgate mapgen function
|
||||
minetest.register_on_generated(function(minp,maxp,blockseed)
|
||||
-- Find all gates within the emerged area of the current mapchunk
|
||||
local gates = {}
|
||||
for x = 0, 4 do
|
||||
for y = 0, 4 do
|
||||
for z = 0, 4 do
|
||||
local hashed = worldgate.get_gates_for_mapblock(vn(minp.x + x * 16,minp.y + y * 16,minp.z + z * 16))
|
||||
for h = 1, #hashed do
|
||||
gates[#gates + 1] = hashed[h]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #gates == 0 then
|
||||
return -- no gates to generate
|
||||
end
|
||||
|
||||
-- Get LVM values
|
||||
local vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
|
||||
local va = VoxelArea(emin,emax)
|
||||
local ystride = va.ystride
|
||||
local zstride = va.zstride
|
||||
|
||||
-- Generate the gates in this mapchunk, if any
|
||||
for gate = 1, #gates do
|
||||
gate = gates[gate]
|
||||
|
||||
-- Random number generator
|
||||
local pcgr = PcgRandom(minetest.hash_node_position(gate.position))
|
||||
|
||||
-- Define location variable
|
||||
local location = nil
|
||||
|
||||
-- Variable for tracking location selection strategy
|
||||
local strategy = nil
|
||||
|
||||
-- Set the exact position if gate is exact, else find a suitable location
|
||||
-- in the current mapchunk
|
||||
if gate.exact then
|
||||
location = gate.position
|
||||
strategy = "exact"
|
||||
else
|
||||
-- Load LVM data
|
||||
local vdata = vm:get_data(vmcache)
|
||||
|
||||
-- Constrain the area to +/- 2 vertical mapblocks for more consistent and
|
||||
-- deterministic mapgen
|
||||
local mapblock = gate.position:divide(16):floor():multiply(16)
|
||||
emin = mapblock:add(vn(0,-32,0))
|
||||
emax = mapblock:add(vn(0,47,0))
|
||||
|
||||
-- Function for indexing 2D heightmap array
|
||||
local function index2d(x,z) -- (portions of this function © FaceDeer 2018, licensed MIT, copied from https://github.com/minetest-mods/mapgen_helper/blob/2521562a42472271d9d761f2b1e84ead59250a14/noise_manager.lua)
|
||||
return x - minp.x +
|
||||
(maxp.x - minp.x + 1)
|
||||
*(z - minp.z)
|
||||
+ 1
|
||||
end
|
||||
|
||||
-- Probe heightmap for suitable location
|
||||
local heightmap = minetest.get_mapgen_object("heightmap") or {}
|
||||
for i = 1, 8 do
|
||||
local randomx = pcgr:next(emin.x,emax.x)
|
||||
local randomz = pcgr:next(emin.z,emax.z)
|
||||
local heightmapy = heightmap[index2d(randomx,randomz)]
|
||||
|
||||
if not heightmapy then
|
||||
goto retry_probe
|
||||
end
|
||||
|
||||
local pos = va:index(randomx,heightmapy,randomz)
|
||||
local cid = vdata[pos]
|
||||
local above = vdata[pos + ystride]
|
||||
|
||||
if cid and cid ~= minetest.CONTENT_AIR and cid ~= minetest.CONTENT_IGNORE and above and above ~= cid then
|
||||
-- Only spawn underwater if allowed
|
||||
if not underwaterspawn and (water[cid] or water[above]) then
|
||||
goto retry_probe
|
||||
end
|
||||
|
||||
-- Check for valid space above
|
||||
for ypos = pos + ystride * 2, pos + ystride * 10, ystride do
|
||||
local ydata = vdata[ypos]
|
||||
if not ydata or ydata == minetest.CONTENT_IGNORE or (not underwaterspawn and water[ydata]) then
|
||||
goto retry_probe
|
||||
end
|
||||
end
|
||||
|
||||
-- A valid location was found on the heightmap
|
||||
location = vn(randomx,heightmapy,randomz)
|
||||
strategy = "heightmap"
|
||||
break
|
||||
end
|
||||
::retry_probe::
|
||||
end
|
||||
|
||||
-- If no heightmap location found, then generate on a random node under air
|
||||
if not location then
|
||||
local air, nodecount = minetest.find_nodes_in_area(emin,emax,"air")
|
||||
local nair = nodecount.air
|
||||
for i = 1, math.min(nair,8) do
|
||||
local pos = va:indexp(air[pcgr:next(1,nair)])
|
||||
while disallowed_cids[vdata[pos]] do
|
||||
pos = pos - ystride -- probe downwards until we find something that isn't air
|
||||
end
|
||||
if vdata[pos] and vdata[pos] ~= minetest.CONTENT_IGNORE then
|
||||
location = va:position(pos)
|
||||
strategy = "grounded"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If mapchunk is completely solid or empty, then generate in a random location
|
||||
if not location then
|
||||
for i = 1, 8 do
|
||||
local randomx = pcgr:next(emin.x,emax.x)
|
||||
local randomy = pcgr:next(emin.y,emax.y)
|
||||
local randomz = pcgr:next(emin.z,emax.z)
|
||||
|
||||
-- Only spawn in midair or underwater if allowed
|
||||
local pos = va:indexp(vn(randomx,randomy,randomz))
|
||||
for ypos = pos, pos + ystride * 11, ystride do
|
||||
local ydata = vdata[ypos]
|
||||
if ydata and ydata ~= minetest.CONTENT_IGNORE and (underwaterspawn or not water[ydata]) and (ypos == pos and (midairspawn or ydata ~= minetest.CONTENT_AIR) or true) then
|
||||
location = va:position(pos)
|
||||
strategy = "random"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fail if no suitable location found
|
||||
if not location then
|
||||
-- Trigger failure callbacks
|
||||
for c = 1, #worldgate.worldgate_failed_callbacks do
|
||||
worldgate.worldgate_failed_callbacks[c](gate)
|
||||
end
|
||||
return -- cannot generate this worldgate
|
||||
end
|
||||
|
||||
-- Adjust location by y + 1
|
||||
location = location:add(vn(0,1,0))
|
||||
end
|
||||
|
||||
-- Place airspace
|
||||
minetest.place_schematic_on_vmanip(vm,location,schematic_airspace,"90",nil,true,"place_center_x,place_center_z")
|
||||
|
||||
-- Place platform
|
||||
minetest.place_schematic_on_vmanip(vm,location:add(vn(0,-8,0)),schematic_platform,"random",nil,true,"place_center_x,place_center_z")
|
||||
|
||||
-- Place base
|
||||
minetest.place_schematic_on_vmanip(vm,location,gate.base,"random",nil,false,"place_center_x,place_center_z")
|
||||
|
||||
-- Place decor
|
||||
minetest.place_schematic_on_vmanip(vm,location:add(vn(0,3,0)),gate.decor,"random",nil,false,"place_center_x,place_center_z")
|
||||
|
||||
-- Update liquids
|
||||
vm:update_liquids()
|
||||
|
||||
-- Write back to LVM
|
||||
vm:write_to_map()
|
||||
|
||||
-- Process extenders based on gate quality
|
||||
for _,epos in ipairs(minetest.find_nodes_in_area(location:add(vn(-6,0,-6)),location:add(vn(6,3,6)),"group:worldgate_extender")) do
|
||||
if pcgr:next(1,100) <= extender_break_chance then -- chance for any extender to be broken
|
||||
minetest.swap_node(epos,{ name = "default:cobble", param2 = 0 })
|
||||
else
|
||||
minetest.swap_node(epos,quality_selector[minetest.get_item_group(minetest.get_node(epos).name,"worldgate_extender") + gate.quality](pcgr))
|
||||
end
|
||||
end
|
||||
|
||||
-- Replace bricks with cobblestone based on gate quality
|
||||
local bricks = minetest.find_nodes_in_area(location:add(vn(-6,0,-6)),location:add(vn(6,13,6)),bricks_list)
|
||||
local brick_degrade_chance = bricks_degrade_chance[gate.quality]
|
||||
for _,brick in ipairs(bricks) do
|
||||
if pcgr:next(1,brick_degrade_chance) == 1 then
|
||||
local brick_node = minetest.get_node(brick)
|
||||
local cobble_node = bricks_map[brick_node.name]
|
||||
if cobble_node then
|
||||
minetest.swap_node(brick,{ name = cobble_node, param2 = brick_node.param2 })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Write the gate's position to the beacon's node meta for linking purposes
|
||||
local beacon = (function()
|
||||
for dy = 2, 0, -1 do
|
||||
local beacon_location = location:add(vn(0,dy,0))
|
||||
local beacon_node = minetest.get_node(beacon_location)
|
||||
if beacon_node and beacon_node.name:find("^telemosaic:beacon") then
|
||||
return beacon_location
|
||||
end
|
||||
end
|
||||
end)()
|
||||
local nodemeta = minetest.get_meta(beacon)
|
||||
nodemeta:set_string("worldgate:source",minetest.pos_to_string(gate.position))
|
||||
if gate.destination then
|
||||
nodemeta:set_string("worldgate:destination",minetest.pos_to_string(gate.destination))
|
||||
minetest.swap_node(beacon,{ name = "telemosaic:beacon", param2 = 0 })
|
||||
end
|
||||
|
||||
-- Fix lighting
|
||||
minetest.fix_light(location:add(vn(-6,-8,-6)),location:add(vn(6,11,6)))
|
||||
|
||||
-- Trigger callbacks
|
||||
for c = 1, #worldgate.worldgate_generated_callbacks do
|
||||
worldgate.worldgate_generated_callbacks[c](location,gate,strategy)
|
||||
end
|
||||
end
|
||||
end)
|
31
mods/worldgate/src/nodes.lua
Normal file
31
mods/worldgate/src/nodes.lua
Normal file
|
@ -0,0 +1,31 @@
|
|||
--
|
||||
-- Worldgate structure nodes
|
||||
-- These get replaced with "real" nodes defined by games/mods/mapgen
|
||||
|
||||
for name,def in pairs({
|
||||
Extender1 = {
|
||||
color = "#00FF00",
|
||||
group = "worldgate_extender",
|
||||
group_value = 1,
|
||||
},
|
||||
Extender2 = {
|
||||
color = "#0000FF",
|
||||
group = "worldgate_extender",
|
||||
group_value = 2,
|
||||
},
|
||||
Extender3 = {
|
||||
color = "#FF00FF",
|
||||
group = "worldgate_extender",
|
||||
group_value = 3,
|
||||
},
|
||||
}) do
|
||||
minetest.register_node("worldgate:structure_" .. name:lower(),{
|
||||
description = "Worldgate Structure Node: " .. name,
|
||||
groups = {
|
||||
not_in_creative_inventory = 1,
|
||||
oddly_breakable_by_hand = 1,
|
||||
[def.group] = def.group_value,
|
||||
},
|
||||
color = def.color,
|
||||
})
|
||||
end
|
78
mods/worldgate/src/settings_overrides.lua
Normal file
78
mods/worldgate/src/settings_overrides.lua
Normal file
|
@ -0,0 +1,78 @@
|
|||
--
|
||||
-- Overrides for Telemosaic settings
|
||||
--
|
||||
|
||||
-- Give extenders a lot more range
|
||||
if worldgate.settings.superextenders then
|
||||
-- Global extender values
|
||||
telemosaic.extender_ranges = { 250, 750, 1500 }
|
||||
|
||||
-- Tier 1 extenders
|
||||
local tier1range = telemosaic.extender_ranges[1]
|
||||
local tier1groups = minetest.registered_nodes["telemosaic:extender_one"].groups
|
||||
tier1groups.telemosaic_extender = tier1range
|
||||
minetest.override_item("telemosaic:extender_one",{ groups = tier1groups })
|
||||
|
||||
-- Tier 2 extenders
|
||||
local tier2range = telemosaic.extender_ranges[2]
|
||||
local tier2groups = minetest.registered_nodes["telemosaic:extender_two"].groups
|
||||
tier2groups.telemosaic_extender = tier2range
|
||||
minetest.override_item("telemosaic:extender_two",{ groups = tier2groups })
|
||||
|
||||
-- Tier 3 extenders
|
||||
local tier3range = telemosaic.extender_ranges[3]
|
||||
local tier3groups = minetest.registered_nodes["telemosaic:extender_three"].groups
|
||||
tier3groups.telemosaic_extender = tier3range
|
||||
minetest.override_item("telemosaic:extender_three",{ groups = tier3groups })
|
||||
end
|
||||
|
||||
-- Implement a longer minimum delay for teleportation to prevent spamming and
|
||||
-- weirdness with loading/unloading mapblocks
|
||||
local min_delay = 5
|
||||
if telemosaic.teleport_delay < min_delay then
|
||||
telemosaic.teleport_delay = min_delay
|
||||
end
|
||||
|
||||
-- Override beacons to give off light if configured
|
||||
if worldgate.settings.beaconglow then
|
||||
for _,beacon in ipairs({
|
||||
"telemosaic:beacon",
|
||||
"telemosaic:beacon_protected",
|
||||
"telemosaic:beacon_err",
|
||||
"telemosaic:beacon_err_protected",
|
||||
}) do
|
||||
minetest.override_item(beacon,{
|
||||
light_source = 15,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Override right-click function to consume mese crystal shards after use as a
|
||||
-- Telemosaic key, if configured
|
||||
if worldgate.settings.destroykeys then
|
||||
local trc = telemosaic.rightclick
|
||||
telemosaic.rightclick = function(pos, node, player, itemstack, pointed_thing)
|
||||
local item = itemstack:get_name()
|
||||
local returned_item = trc(pos, node, player, itemstack, pointed_thing)
|
||||
if item == "telemosaic:key" and returned_item:get_name() == "default:mese_crystal_fragment" then
|
||||
return ItemStack()
|
||||
else
|
||||
return returned_item
|
||||
end
|
||||
end
|
||||
|
||||
for _,beacon in ipairs({
|
||||
"telemosaic:beacon",
|
||||
"telemosaic:beacon_err",
|
||||
"telemosaic:beacon_disabled",
|
||||
"telemosaic:beacon_off",
|
||||
"telemosaic:beacon_protected",
|
||||
"telemosaic:beacon_err_protected",
|
||||
"telemosaic:beacon_disabled_protected",
|
||||
"telemosaic:beacon_off_protected",
|
||||
}) do
|
||||
minetest.override_item(beacon,{
|
||||
on_rightclick = telemosaic.rightclick
|
||||
})
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue