diff options
author | Auke Kok <auke-jan.h.kok@intel.com> | 2015-01-14 22:39:10 -0800 |
---|---|---|
committer | Auke Kok <auke-jan.h.kok@intel.com> | 2015-01-14 22:39:10 -0800 |
commit | 132989c80bbce7624bdc634c0b9d2b72b6195a76 (patch) | |
tree | 6521c3798dc4e871f331063a17217f9e75026916 /init.lua |
Initial checkin.
This mostly already works - It's at the stage where I'm debugging
the tricky bits and most of the error and edge case handling, and
some of the lua tricks.
Diffstat (limited to 'init.lua')
-rw-r--r-- | init.lua | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..e1c1892 --- /dev/null +++ b/init.lua @@ -0,0 +1,457 @@ +-- +-- Minetest Sedimentology Mod +-- +local interval = 1.0 +local count = 20 +local radius = 100 + +local stat_considered = 0 +local stat_displaced = 0 +local stat_degraded = 0 + +local function round(f) + if f >= 0 then + return math.floor(f + 0.5) + else + return math.ceil(f - 0.5) + end +end + +local walker = { + [0] = {x = 0, z = 1}, {x = 1, z = 0}, {x = 0, z = -1}, {x = -1, z = 0}, + {x = 1, z = 1}, {x = 1, z = -1}, {x = -1, z = 1}, {x = -1, z = -1}, + {x = 2, z = 0}, {x = -2, z = 0}, {x = 0, z = 2}, {x = 0, z = -2}, + {x = 2, z = 1}, {x = 2, z = -1}, {x = 1, z = 2}, {x = 1, z = -2}, + {x = -1, z = 2}, {x = -1, z = -2}, {x = -2, z = 1}, {x = -2, z = -1}, + {x = 2, z = 2}, {x = -2, z = 2}, {x = 2, z = -2}, {x = -2, z = -2} +} +local walker_step = 0 +local walker_start = 0 +local walker_phase = 4 + +local function walker_f(x, y) + if x == 0 and y == 0 then + walker_step = 0 + end + + if walker_step == 0 or walker_step == 4 or walker_step == 8 or walker_step == 24 then + walker_start = math.floor(math.random() * 4.0) + walker_phase = 4 + elseif walker_step == 12 then + walker_start = math.floor(math.random() * 8.0) + walker_phase = 8 + end + + local section_start = walker_step - (walker_step % walker_phase) + local section_part = ((walker_step - section_start) + walker_start) % walker_phase + + walker_step = walker_step + 1 + return walker[section_start + section_part] +end + +local function roll(chance) + return (math.random() >= chance) +end + +local function node_above(node) + local pos = minetest.get_pos(node) + return {x = pos.x, y = pos.y + 1, z = pos.z} +end + +local function node_below(node) + local pos = minetest.get_pos(node) + return {x = pos.x, y = pos.y - 1, z = pos.z} +end + +local function pos_is_node(pos) + return minetest.get_node_or_nil(pos) +end + +local function node_is_air(node) + return node.name == "air" +end + +local function node_is_plant(node) + if not node then + return false + end + + local name = node.name + local drawtype = minetest.registered_nodes[name].drawtype + if drawtype == "plantlike" then + return true + end + + if minetest.registered_nodes[node.name].groups.flora == 1 then + return true + end + + return ((name == "default:leaves") or + (name == "default:jungleleaves") or + (name == "default:pine_needles") or + (name == "default:cactus")) +end + +local function node_is_water(node) + if not node then + return false + end + + print(dump(node)) + + return ((node.name == "default:water_source") or + (node.name == "default:water_flowing")) +end + +local function node_is_lava(node) + if not node then + return false + end + + return ((node.name == "default:lava_source") or + (node.name == "default:lava_flowing")) +end + +local function node_is_liquid(node) + if not node then + return false + end + + local name = node.name + local drawtype = minetest.registered_nodes[name].drawtype + if drawtype then + if (drawtype == "liquid") or (drawtype == "flowingliquid") then + return true + end + end + + return false +end + +local function scan_for_water(pos, waterfactor) + local w = waterfactor + for xx = pos.x - 2,pos.x + 2,1 do + for yy = pos.y - 2,pos.y + 2,1 do + for zz = pos.z - 2,pos.z + 2,1 do + local nn = minetest.get_node({xx, yy, zz}) + if nn.name == "default:water_flowing" then + return 0.25 + elseif nn.name == "default:water_source" then + w = 0.125 + break + end + end + end + end + return w +end + +local function scan_for_vegetation(pos) + local v = 1.0 + for xx = pos.x - 3,pos.x + 3,1 do + for yy = pos.y - 3,pos.y + 3,1 do + for zz = pos.z - 3,pos.z + 3,1 do + local nn = minetest.get_node({xx, yy, zz}) + if node_is_plant(nn) then + -- factor distance to plant + local d = (math.abs(xx - pos.x) + math.abs(yy - pos.y) + math.abs(zz - pos.z)) / 3.0 + -- scale it + local vv = 0.5 / (4.0 - d) + -- only take the lowest value + if (vv < v) then + v = vv + end + end + end + end + end + return v +end + +local function node_is_valid_target_for_displacement(pos) + local node = minetest.get_node(pos) + + if node_is_liquid(node) then + return true + elseif node_is_air(node) then + return true + elseif node_is_plant(node) then + return true + end + return false +end + +local function node_is_locked_in(pos) + if + node_is_valid_target_for_displacement({x = pos.x - 1, y = pos.y, z = pos.z}) or + node_is_valid_target_for_displacement({x = pos.x + 1, y = pos.y, z = pos.z}) or + node_is_valid_target_for_displacement({x = pos.x, y = pos.y, z = pos.z - 1}) or + node_is_valid_target_for_displacement({x = pos.x, y = pos.y, z = pos.z + 1}) + then + return false + end + return true +end + +local function find_deposit_location(x, y, z) + local yy = y + while true do + if node_is_valid_target_for_displacement({x = x, y = yy, z = z}) then + yy = yy - 1 + if yy < -32768 then + return y + end + else + return yy + 1 + end + end +end + +local function sed() + local underliquid = 0 + + -- pick a random block in (radius) around (random online player) + local playerlist = minetest.get_connected_players() + local playercount = table.getn(playerlist) + if playercount == 0 then + return + end + local r = math.random(playercount) + local randomplayer = playerlist[r] + local playerpos = randomplayer:getpos() + local pos = { + x = math.random(playerpos.x - radius, playerpos.x + radius), + y = 0, + z = math.random(playerpos.z - radius, playerpos.z + radius) + } + local node = minetest.get_pos(pos) + + stat_considered = stat_considered + 1 + + -- now go find the topmost non-air block + repeat + node = node_above(node) + until node_is_air(node) + + repeat + node = node_below(node) + until not node_is_air(node) + + -- then search under water/lava and any see-through plant stuff + while (node_is_liquid(node)) do + underliquid = underliquid + 1 + node = node_below(node) + end + + -- check if we're material that we can do something with + local hardness = 1.0 + local resistance = 1.0 + + if node.name == "default:dirt" or + node.name == "default:dirt_with_grass" or + node.name == "default:dirt_with_grass_footsteps" or + node.name == "default:dirt_with_snow" then + -- default hardness (very soft) here + elseif node.name == "default:sand" or node.name == "default:desert_sand" then + -- sand is "hard" to break into clay, but moves easily + hardness = 0.01 + elseif node.name == "default:gravel" then + hardness = 0.15 + resistance = 0.70 + elseif node.name == "default:clay" then + resistance = 0.3 + elseif node.name == "default:sandstone" or + node.name == "default:cobble" or + node.name == "default:mossycobble" or + node.name == "default:desert_cobble" then + hardness = 0.05 + resistance = 0.05 + elseif node.name == "default:desert_stone" or + node.name == "default:stone" then + hardness = 0.01 + resistance = 0.01 + elseif node.name == "default:stone_with_coal" or + node.name == "default:stone_with_iron" or + node.name == "default:stone_with_copper" or + node.name == "default:stone_with_gold" or + node.name == "default:stone_with_mese" or + node.name == "default:stone_with_diamond" then + hardness = 0.0001 + resistance = 0.01 + else + -- we don't do anything with this node type + return + end + + -- determine nearby water scaling + local waterfactor = 0.01 + if underliquid > 0 then + waterfactor = 0.5 + else + waterfactor = scan_for_water(pos, waterfactor) + end + + if roll(waterfactor) then + return + end + + -- slow down deeper under sea level (wave action reduced energy) + if underliquid and pos.y < 0.0 then + if roll(2.0 * math.pow(0.5, 0.0 - pos.y)) then + return + end + end + + -- factor in vegetation that slows erosion down + if roll(scan_for_vegetation(pos)) then + return + end + + + -- displacement - before we erode this material, we check to see if + -- it's not easier to move the material first. If that fails, we'll + -- end up degrading the material as calculated + + if not node_is_locked_in(pos) then + local steps = 8 + + if node.name == "default:sand" or + node.name == "default:desert_sand" or + (underliquid > 0) then + steps = 24 + else + steps = 8 + end + + -- walker algorithm here + local lowest = pos.y + local lowesto = {x = pos.x, z = pos.z} + local o = {x = 0, z = 0} + + for step = 1, steps, 1 do + o = walker_f(o.x, o.z) + local h = find_deposit_location(pos.x + o.x, lowest, pos.z + o.z) +print("walking step " .. step .. " to " .. pos.x + o.x .. ", " .. pos.z + o.z .. " -> lowest = " .. h) + + if h < lowest then + lowest = h + lowesto = o + end + end + + if lowest < pos.y then + local tpos = {x = pos.x + o.x, y = lowest, z = pos.z + o.z} + + if not roll(resistance) then + local tnode = minetest.get_node(tpos) + + if node_is_air(tnode) or node_is_plant(tnode) or node_is_liquid(tnode) then + -- time to displace the node from pos to tpos + minetest.place_node(tpos, node) + minetest.get_meta(tpos):from_table(minetest.get_meta(pos):to_table()) + minetest.remove_node(pos) + + -- FIXME + -- fix water at source location + -- fix water at target location + + print("Moved:", node.name, pos.x, pos.y, pos.z, "to:", tnode.name, tpos.x, tpos.y, tpos.z) + stat_displaced = stat_displaced + 1 + + -- done - don't degrade this block further + return + else + --debug + print("displacement failed: target has something:", tpos.x, tpos.y, tpos.z) + end + end + end + end + + -- degrade + + -- compensate speed for grass/dirt cycle + + -- sand only becomes clay under sealevel + if ((node.name == "default:sand" or node.name == "default:desert_sand") and (underliquid > 0) and pos.y >= 0.0) then + return + end + + -- prevent dirt-to-sand outside deserts + -- FIXME should account for Biome here too + if (underliquid < 1) and (node.name == "default:sand" or node.name == "default:desert_sand") then + return + end + + if roll(hardness) then + return + end + + -- finally, determine new material type + local newmat = "air" + + if node.name == "default:dirt" then + newmat = "default:sand" + elseif node.name == "default:dirt_with_grass" or + node.name == "default:dirt_with_grass_footsteps" or + node.name == "default:dirt_with_snow" then + newmat = "default:dirt" + elseif node.name == "default:sand" or node.name == "default:desert_sand" then + newmat = "default:clay" + elseif node.name == "default:gravel" then + newmat = "default:dirt" + elseif node.name == "default:clay" then + return + elseif node.name == "default:sandstone" or + node.name == "default:cobble" or + node.name == "default:mossycobble" or + node.name == "default:desert_cobble" then + newmat = "default:gravel" + elseif node.name == "default:desert_stone" or + node.name == "default:stone" then + newmat = "default:cobble" + elseif node.name == "default:stone_with_coal" or + node.name == "default:stone_with_iron" or + node.name == "default:stone_with_copper" or + node.name == "default:stone_with_gold" or + node.name == "default:stone_with_mese" or + node.name == "default:stone_with_diamond" then + newmat = "default:stone" + else + print("wut", node.name) + return + end + + minetest.set_node(pos, {name = newmat}) + + stat_degraded = stat_degraded + 1 +end + +local function sedimentology() + -- select a random point that is loaded in the game + for c=1,count,1 do + sed() + end + -- requeue a timer to call again + minetest.after(interval, sedimentology) +end + +local function sedcmd(name, param) + if param == "stats" then + local output = "Sedimentology mod statistics:" .. + "\nconsidered: " .. stat_considered .. + "\ndisplaced: " .. stat_displaced .. + "\ndegraded: " .. stat_degraded + return true, output + end + return true, "Command completed succesfully" +end + +minetest.register_chatcommand("sed", { + params = "stats|...", + description = "Various action commands for the sedimentology mod", + func = sedcmd +}) + +minetest.after(interval, sedimentology) +print("Initialized Sedimentology") |