From 9518086b6b43891dc2059e0a36efc0b252a1d4ff Mon Sep 17 00:00:00 2001 From: iliekprogrammar Date: Tue, 30 Mar 2021 13:28:44 +0800 Subject: [PATCH] Kelp store age metadata by bulk on a queue, improve API, etc. Highlights: - Implement storing age metadata by bulk on a queue, with maximum interval and queue length. - "Flexible" initial growth probability. Will provide helper APIs. - Fix various bugs with unsubmerged kelp detection, submerged tip detection, age metadata. - Various optimizations. --- mods/ITEMS/mcl_ocean/kelp.lua | 456 ++++++++++++++++++++++------------ 1 file changed, 301 insertions(+), 155 deletions(-) diff --git a/mods/ITEMS/mcl_ocean/kelp.lua b/mods/ITEMS/mcl_ocean/kelp.lua index 741d9e59a..7a39bc850 100644 --- a/mods/ITEMS/mcl_ocean/kelp.lua +++ b/mods/ITEMS/mcl_ocean/kelp.lua @@ -1,8 +1,9 @@ -- TODO: whenever it becomes possible to fully implement kelp without the --- plantlike_rooted limitation, please adapt the code accordingly. +-- plantlike_rooted limitation, please update accordingly. -- -- TODO: In MC, you can't actually destroy kelp by bucket'ing water in the middle. -- However, because of the plantlike_rooted hack, we'll just allow it for now. +-- TODO: Integrate ABMs with the new kelp code, disabled. OR remove them entirely local S = minetest.get_translator("mcl_ocean") local mod_doc = minetest.get_modpath("doc") ~= nil @@ -15,6 +16,7 @@ local mt_registered_items = minetest.registered_items local mt_registered_nodes = minetest.registered_nodes -- functions +local mt_log = minetest.log local mt_add_item = minetest.add_item local mt_get_item_group = minetest.get_item_group local mt_get_node = minetest.get_node @@ -22,21 +24,31 @@ local mt_get_node_level = minetest.get_node_level local mt_get_node_max_level = minetest.get_node_max_level local mt_get_node_or_nil = minetest.get_node_or_nil local mt_get_node_timer = minetest.get_node_timer -local mt_hash_node_position = minetest.hash_node_position -local mt_is_protected = minetest.is_protected -local mt_set_node = minetest.set_node local mt_get_meta = minetest.get_meta +local mt_hash_node_position = minetest.hash_node_position +local mt_pos_to_string = minetest.pos_to_string +local mt_is_protected = minetest.is_protected +local mt_record_protection_violation = minetest.record_protection_violation +local mt_set_node = minetest.set_node local mt_is_creative_enabled = minetest.is_creative_enabled local mt_sound_play = minetest.sound_play +local math_min = math.min +local math_max = math.max +local math_ceil = math.ceil local math_floor = math.floor local math_random = math.random local string_format = string.format +local table_copy = table.copy +local table_insert = table.insert -- DEBUG: functions local log = minetest.log local chatlog = minetest.chat_send_all +il_chatlog = chatlog +il_get_meta = mt_get_meta +il_hash_pos = mt_hash_node_position -------------------------------------------------------------------------------- -- Kelp API @@ -45,30 +57,51 @@ local chatlog = minetest.chat_send_all local kelp = {} mcl_ocean.kelp = kelp +-- Kelp minimum and maximum age. Once reached the maximum, kelp no longer grows. +kelp.MIN_AGE = 0 kelp.MAX_AGE = 25 -kelp.TIMER_INTERVAL = 0.2 + +-- Tick interval (in seconds) for updating kelp. +kelp.TICK = 0.2 + +-- Tick interval (in seconds) to store kelp meta. +kelp.META_TICK = 2 + +-- Max age queue length +kelp.MAX_AGE_QUEUE = 20 -- The average amount of growth for kelp in a day is 2.16 (https://youtu.be/5Bp4lAjAk3I) --- Normally, a day lasts 20 minutes, meaning this nodetimer is executed 24000 times. --- Calculate probability via 2.16/24000 and we get the probability 9/100'000 or 9.0e-5 --- NOTE: currently, we can't exactly use the same type of randomness MC does, --- because it has multiple complicated sets of PRNGs. --- kelp.RANDOM_NUMERATOR = 9 -kelp.RANDOM_NUMERATOR = 100 -kelp.RANDOM_DENOMINATOR = 100000 +-- Normally, a day lasts 20 minutes, meaning kelp.next_grow() is executed +-- 1200 / TICK times. Per tick probability = (216/100) / (1200/TICK) +-- NOTE: currently, we can't exactly use the same type of randomness MC does, because +-- it has multiple complicated sets of PRNGs. +-- NOTE: Small loss of precision, should be 10 to preserve it. +-- kelp.ROLL_GROWTH_PRECISION = 10 +-- kelp.ROLL_GROWTH_NUMERATOR = 216 * kelp.TICK * kelp.ROLL_GROWTH_PRECISION +-- kelp.ROLL_GROWTH_DENOMINATOR = 100 * 1200 * kelp.ROLL_GROWTH_PRECISION +kelp.ROLL_GROWTH_PRECISION = 1 +kelp.ROLL_GROWTH_NUMERATOR = 216 * kelp.TICK +kelp.ROLL_GROWTH_DENOMINATOR = 100 * 1200 +-- Sounds used to dig and place kelp. kelp.leaf_sounds = mcl_sounds.node_sound_leaves_defaults() -- TODO: is this really necessary -- Lock drops to avoid duplicate drops, set after dropping detached kelp. kelp.lock_drop = 0 --- Registrar of nodetimers, indexed by pos_hash. -kelp.registered_nodetimers = {} +-- Pool storing nodetimers +kelp.timers_pool = {} -- Pool storing age, indexed by pos_hash. kelp.age_pool = {} +-- Queue(List) of hashed positions to save their ages. +-- Invalid ones may still persist in this queue. +kelp.age_queue = {} +-- Stores only valid positions of each hashed postiions. +kelp.age_queue_pos = {} + -- is age in the growable range? function kelp.is_age_growable(age) @@ -78,9 +111,10 @@ end -- Is this water? -- Returns the liquidtype, if indeed water. -function kelp.is_submerged(node, def) +function kelp.is_submerged(node) if mt_get_item_group(node.name, "water") ~= 0 then - return def.liquidtype -- Expected only "source" and "flowing" from water liquids + -- Expected only "source" and "flowing" from water liquids + return mt_registered_nodes[node.name].liquidtype end return false end @@ -88,16 +122,19 @@ end -- Is the water downward flowing? -- (kelp can grow/be placed inside downward flowing water) -function kelp.is_downward_flowing(pos, node, def, is_above) +function kelp.is_downward_flowing(pos, node, pos_above, node_above, __is_above__) + -- Function params: (pos[, node]) or (node, pos_above) or (node, node_above) + local node = node or mt_get_node(pos) + local result = (math_floor(node.param2 / 8) % 2) == 1 - if not (result or is_above) then + if not (result or __is_above__) then -- If not, also check node above. -- (this is needed due a weird quirk in the definition of "downwards flowing" -- liquids in Minetest) - local node_above = mt_get_node({x=pos.x,y=pos.y+1,z=pos.z}) - local def_above = mt_registered_nodes[node_above.name] - result = kelp.is_submerged(node_above, def_above) - or kelp.is_downward_flowing(pos, node_above, def_above, true) + local pos_above = pos_above or {x=pos.x,y=pos.y+1,z=pos.z} + local node_above = node_above or mt_get_node(pos_above) + result = kelp.is_submerged(node_above) + or kelp.is_downward_flowing(nil, node_above, nil, nil, true) end return result end @@ -105,20 +142,23 @@ end -- Will node fall at that position? -- This only checks if a node would fall, meaning that node need not be at pos. -function kelp.is_falling(pos, node) +function kelp.is_falling(pos, node, is_falling, pos_bottom, node_bottom, def_bottom) + -- Optional params: is_falling, pos_bottom, node_bottom, def_bottom + -- NOTE: Modified from check_single_for_falling in builtin. - -- Please update as necessary. + -- Please update accordingly. local nodename = node.name - if mt_get_item_group(nodename, "falling_node") == 0 then + if is_falling == false or + is_falling == nil and mt_get_item_group(nodename, "falling_node") == 0 then return false end - local pos_bottom = {x = pos.x, y = pos.y - 1, z = pos.z} + local pos_bottom = pos_bottom or {x = pos.x, y = pos.y - 1, z = pos.z} -- get_node_or_nil: Only fall if node below is loaded - local node_bottom = mt_get_node_or_nil(pos_bottom) + local node_bottom = node_bottom or mt_get_node_or_nil(pos_bottom) local nodename_bottom = node_bottom.name - local def_bottom = node_bottom and mt_registered_nodes[nodename_bottom] + local def_bottom = def_bottom or node_bottom and mt_registered_nodes[nodename_bottom] if not def_bottom then return false end @@ -143,6 +183,20 @@ function kelp.is_falling(pos, node) end +-- Roll whether to grow kelp or not. +function kelp.roll_growth(numerator, denominator) + -- Optional params: numerator, denominator + return math_random(denominator or kelp.ROLL_GROWTH_DENOMINATOR) <= (numerator or kelp.ROLL_GROWTH_NUMERATOR) +end + + +-- Roll initial age for kelp. +function kelp.roll_init_age(min, max) + -- Optional params + return math_random(min or kelp.MIN_AGE, (max or kelp.MAX_AGE)-1) +end + + -- Converts param2 to kelp height. function kelp.get_height(param2) return math_floor(param2 / 16) @@ -150,28 +204,31 @@ end -- Obtain pos and node of the tip of kelp. -function kelp.get_tip(pos, node) - local size = math.ceil(node.param2 / 16) - local pos_tip = table.copy(pos) - pos_tip.y = pos_tip.y + size - return pos_tip, mt_get_node(pos_tip) +function kelp.get_tip(pos, param2) + -- Optional params: param2 + local height = kelp.get_height(param2 or mt_get_node(pos).param2) + local pos_tip = {x=pos.x, y=pos.y, z=pos.z} + pos_tip.y = pos_tip.y + height + 1 + return pos_tip, mt_get_node(pos_tip), height end -- Obtain position of the first kelp unsubmerged. -function kelp.find_unsubmerged(pos, node) +function kelp.find_unsubmerged(pos, node, height) + -- Optional params: node, height + local node = node or mt_get_node(pos) + local height = height or kelp.get_height(node.param2) - local x,y,z = pos.x, pos.y, pos.z - local height = kelp.get_height(node.param2) - local walk_pos = {x=x, z=z} + local walk_pos = {x=pos.x, z=pos.z} + local y = pos.y for i=1,height do walk_pos.y = y + i local walk_node = mt_get_node(walk_pos) - if mt_get_item_group(walk_node.name, "water") == 0 then - return walk_pos, walk_node + if not kelp.is_submerged(walk_node) then + return walk_pos, walk_node, height, i end end - return nil + return nil, nil, height, height end @@ -181,53 +238,145 @@ function kelp.next_param2(param2) end --- Grow next kelp. -function kelp.next_grow(pos, node, pos_tip, def_tip, downward_flowing) - -- Optional parameters - local pos_tip, def_tip = pos_tip, def_tip - local downward_flowing = downward_flowing - if pos_tip == nil and def_tip == nil then - local node_tip - pos_tip, node_tip = kelp.get_tip(pos, node) - def_tip = mt_registered_nodes[node_tip.name] - downward_flowing = kelp.is_submerged(pos_tip, node_tip) - and kelp.is_downward_flowing(pos_tip, node_tip, def_tip) +-- Stores age from kelp.age_queue* into their respective meta +function kelp.store_meta() + local count = 0 + for _ in pairs(kelp.age_queue_pos) do + count = count + 1 + end + -- chatlog(string_format("Storing age metadata: %d in queue", #kelp.age_queue)) + -- chatlog(string_format("Storing age metadata: %d valid in queue", count)) + for i=1,#kelp.age_queue do + local pos_hash = kelp.age_queue[i] + local pos = kelp.age_queue_pos[pos_hash] + -- queued hashes may no longer point to a valid pos, e.g. kelp is destroyed. + if pos then + mt_get_meta(pos):set_int("mcl_ocean:kelp_age", kelp.age_pool[pos_hash]) + end + end + kelp.age_queue = {} + kelp.age_queue_pos = {} +end + + +-- Store and queue a kelp's age to be saved into meta later. +function kelp.store_age(age, pos, pos_hash) + -- Watched params: pos + -- Optional params: pos_hash + local pos_hash = pos_hash or mt_hash_node_position(pos) + + kelp.age_pool[pos_hash] = age + if not kelp.age_queue_pos[pos_hash] then + table_insert(kelp.age_queue, pos_hash) + kelp.age_queue_pos[pos_hash] = pos + return true, pos_hash end + return false, pos_hash +end + + +-- Initialise a kelp's age. +function kelp.init_age(pos, age, pos_hash, meta) + -- Watched params: pos + -- Optional params: age, pos_hash, meta + local pos_hash = pos_hash or mt_hash_node_position(pos) + local meta = meta or mt_get_meta(pos) + + local age = age + if not meta:contains("mcl_ocean:kelp_age") then + -- NOTE: age param can only replace initial age roll. + age = age or kelp.roll_init_age() + kelp.store_age(age, pos, pos_hash) + else + age = meta:get_int("mcl_ocean:kelp_age") + if not kelp.age_pool[pos_hash] then + kelp.age_pool[pos_hash] = age + end + end + + return age, pos_hash, meta +end + + +-- Initialise kelp nodetimer. +function kelp.init_timer(pos, pos_hash) + -- Optional params: pos_hash + local pos_hash = pos_hash or mt_hash_node_position(pos) + + local timer = kelp.timers_pool[pos_hash] + if not timer then + timer = mt_get_node_timer(pos) + kelp.timers_pool[pos_hash] = timer + end + if not timer:is_started() then + timer:start(kelp.TICK) + end + + return pos_hash +end + + +-- Apply next kelp height. +function kelp.next_height(pos, node, pos_tip, node_tip, submerged, downward_flowing) + -- Modified params: node + -- Optional params: node, pos_tip, node_tip, submerged, downward_flowing + local node = node or mt_get_node(pos) + local pos_tip = pos_tip + local node_tip = node_tip or (pos_tip and mt_get_node(pos_tip)) + if not pos_tip then + pos_tip,node_tip = kelp.get_tip(pos) + end + local downward_flowing = downward_flowing or + (submerged or kelp.is_submerged(node_tip) + and kelp.is_downward_flowing(pos_tip, node_tip)) + -- Liquid source: Grow normally. - local node = table.copy(node) node.param2 = kelp.next_param2(node.param2) mt_set_node(pos, node) -- Flowing liquid: Grow 1 step, but also turn the tip node into a liquid source. if downward_flowing then - local alt_liq = def_tip.liquid_alternative_source + local alt_liq = mt_registered_nodes[node_tip.name].liquid_alternative_source if alt_liq then mt_set_node(pos_tip, {name=alt_liq}) end end + + return node, pos_tip, node_tip, submerged, downward_flowing end --- Naturally grow next kelp. -function kelp.natural_grow(pos, node, age, pos_hash, meta) - -- Must grow first, then get the new meta - kelp.next_grow(pos, node) - - -- Optional params - local meta = meta or mt_get_meta(pos) +-- Grow next kelp. +function kelp.next_grow(age, pos, node, pos_hash, pos_tip, node_tip, submerged, downward_flowing) + -- Watched params: pos + -- Modified params: node + -- Optional params: node, pos_hash, pos_tip, node_tip, submerged, downward_flowing + local node = node or mt_get_node(pos) local pos_hash = pos_hash or mt_hash_node_position(pos) + local pos_tip = pos_tip + local node_tip = node_tip or (pos_tip and mt_get_node(pos_tip)) + if not pos_tip then + pos_tip,node_tip = kelp.get_tip(pos) + end - local age = age + 1 - kelp.age_pool[pos_hash] = age - meta:set_int("mcl_ocean:kelp_age", age) + -- New kelp must also be submerged in water. + local downward_flowing = downward_flowing or kelp.is_downward_flowing(pos_tip, node_tip) + if not (submerged or kelp.is_submerged(node_tip)) then + return + end + kelp.next_height(pos, node, pos_tip, node_tip, submerged, downward_flowing) + + return kelp.store_age(age, pos, pos_hash), node, pos_hash, pos_tip, node_tip, submerged, downward_flowing end -- Drops the items for detached kelps. -function kelp.detach_drop(pos, height, pos_hash) - local pos_hash = pos_hash or mt_hash_node_position(pos) -- Optional params +function kelp.detach_drop(pos, param2, pos_hash) + -- Optional params: param2, pos_hash + local height = kelp.get_height(param2 or mt_get_node(pos).param2) + local pos_hash = pos_hash or mt_hash_node_position(pos) if kelp.lock_drop > 0 then minetest.log("error", @@ -236,9 +385,11 @@ function kelp.detach_drop(pos, height, pos_hash) return end - local x,y,z = pos.x,pos.y,pos.z + local y = pos.y + local walk_pos = {x=pos.x, z=pos.z} for i=1,height do - mt_add_item({x=x, y=y+i, z=z}, "mcl_ocean:kelp") + walk_pos.y = y+i + mt_add_item(walk_pos, "mcl_ocean:kelp") end -- Locks drop. @@ -251,7 +402,8 @@ end -- Synonymous to digging the kelp. -- NOTE: this is intended for whenever kelp truly becomes segmented plants -- instead of rooted to the floor. Don't try to remove dig_pos. -function kelp.detach_dig(dig_pos, pos, node, is_drop, pos_hash) +function kelp.detach_dig(dig_pos, pos, node, drop, pos_hash) + -- Optional params: drop, pos_hash local pos_hash = pos_hash or mt_hash_node_position(pos) local param2 = node.param2 @@ -260,17 +412,18 @@ function kelp.detach_dig(dig_pos, pos, node, is_drop, pos_hash) -- Digs the entire kelp: invoke after_dig_node to mt_set_node. if new_height <= 0 then - if is_drop then - kelp.detach_drop(dig_pos, kelp.get_height(param2), pos_hash) + if drop then + kelp.detach_drop(dig_pos, param2, pos_hash) end mt_set_node(pos, { name=mt_registered_nodes[node.name].node_dig_prediction, - param=node.param, param2=0 }) + param=node.param, + param2=0 }) -- Digs the kelp beginning at a height else - if is_drop then - kelp.detach_drop(dig_pos, kelp.get_height(param2) - new_height, pos_hash) + if drop then + kelp.detach_drop(dig_pos, param2 - new_height, pos_hash) end mt_set_node(pos, {name=node.name, param=node.param, param2=16*new_height}) end @@ -281,7 +434,6 @@ end -- Kelp callback functions -------------------------------------------------------------------------------- --- Set this to drop kelps when function kelp.surface_on_dig(pos, node, digger) kelp.detach_dig(pos, pos, node, true) end @@ -292,34 +444,47 @@ function kelp.surface_after_dig_node(pos, node) end -function kelp.surface_on_timer(pos, elapsed) +function kelp.surface_on_timer(pos) local node = mt_get_node(pos) - local dig_pos = kelp.find_unsubmerged(pos, node) local pos_hash = mt_hash_node_position(pos) + + -- Update detahed kelps + local dig_pos = kelp.find_unsubmerged(pos, node) if dig_pos then + -- chatlog("detach_dig") kelp.detach_dig(dig_pos, pos, node, true, pos_hash) end -- Grow kelp on chance - if math_random(kelp.RANDOM_DENOMINATOR) - kelp.RANDOM_NUMERATOR < 0 then + if kelp.roll_growth() then local age = kelp.age_pool[pos_hash] if kelp.is_age_growable(age) then - kelp.natural_grow(pos, node, age, pos_hash) + kelp.next_grow(age, pos, node, pos_hash) end end return true end +function kelp.surface_on_construct(pos) + local pos_hash = mt_hash_node_position(pos) + kelp.init_age(pos, nil, pos_hash) + kelp.init_timer(pos, pos_hash) +end + + function kelp.surface_on_destruct(pos) local node = mt_get_node(pos) local pos_hash = mt_hash_node_position(pos) -- on_falling callback. Activated by pistons for falling nodes too. if kelp.is_falling(pos, node) then - kelp.detach_drop(pos, kelp.get_height(node.param2), pos_hash) + kelp.detach_drop(pos, node.param2, pos_hash) end + -- Removes position from queue + kelp.age_queue_pos[pos_hash] = nil + -- Unlocks drops. if kelp.lock_drop > 0 then kelp.lock_drop = kelp.lock_drop - 1 @@ -327,13 +492,14 @@ function kelp.surface_on_destruct(pos) end + function kelp.surface_on_mvps_move(pos, node, oldpos, nodemeta) -- Pistons moving falling nodes will have already activated on_falling callback. kelp.detach_dig(pos, pos, node, mt_get_item_group(node.name, "falling_node") ~= 1) end --- NOTE: Uncomment this to use ABMs +-- NOTE: Uncomment this to use ABMs. -- local function surface_unsubmerged_abm(pos, node) -- local dig_pos = find_unsubmerged(pos, node) -- if dig_pos then @@ -343,52 +509,6 @@ end -- end --- NOTE: Uncomment this to use nodetimers -function kelp.surface_register_nodetimer(pos, node, pos_hash, meta, age) - -- Optional params - local pos_hash,meta,age = pos_hash,meta,age - local timer = kelp.registered_nodetimers[pos_hash] - - if not timer then - pos_hash = pos_hash or mt_hash_node_position(pos) - - timer = mt_get_node_timer(pos) - kelp.registered_nodetimers[pos_hash] = timer - - -- Pool age to avoid meta:get* operations - meta = meta or mt_get_meta(pos) - if not age then - if not meta:contains("mcl_ocean:kelp_age") then - age = math_random(0, kelp.MAX_AGE-1) - else - age = meta:get_int("mcl_ocean:kelp_age") - end - end - kelp.age_pool[pos_hash] = age - meta:set_int("mcl_ocean:kelp_age", age) - end - - if not timer:is_started() then - timer:start(kelp.TIMER_INTERVAL) - end - - return pos_hash, meta, age -end - - --- NOTE: Uncomment this to use ABMs --- function kelp.grow_abm(pos, node) --- -- Grow kelp by 1 node length if it would grow inside water --- node.param2 = next_param2(node.param2) --- local pos_tip, node_tip = get_tip(pos, node) --- local def_tip = mt_registered_nodes[node_tip.name] --- if is_submerged(node_tip, def_tip) then --- kelp.next_grow(pos, node, pos_tip, def_tip, --- is_downward_flowing(pos_tip, node_tip, def_tip)) --- end --- end - - function kelp.kelp_on_place(itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" or not placer then return itemstack @@ -410,17 +530,18 @@ function kelp.kelp_on_place(itemstack, placer, pointed_thing) -- Protection if mt_is_protected(pos_under, player_name) or mt_is_protected(pos_above, player_name) then - minetest.log("action", player_name + mt_log("action", player_name .. " tried to place " .. itemstack:get_name() .. " at protected position " - .. minetest.pos_to_string(pos_under)) - minetest.record_protection_violation(pos_under, player_name) + .. mt_pos_to_string(pos_under)) + mt_record_protection_violation(pos_under, player_name) return itemstack end local pos_tip, node_tip, def_tip - local pos_hash, meta + local pos_hash = mt_hash_node_position(pos_under) + local age = kelp.roll_init_age() -- Kelp must also be placed on the top/tip side of the surface/kelp @@ -430,12 +551,9 @@ function kelp.kelp_on_place(itemstack, placer, pointed_thing) -- When placed on kelp. if mt_get_item_group(nu_name, "kelp") == 1 then - pos_tip, node_tip = kelp.get_tip(pos_under, node_under) + pos_tip,node_tip = kelp.get_tip(pos_under, node_under.param2) def_tip = mt_registered_nodes[node_tip.name] - pos_hash = mt_hash_node_position(pos_under) - meta = mt_get_meta(pos_under) - -- When placed on surface. else local new_kelp = false @@ -452,18 +570,15 @@ function kelp.kelp_on_place(itemstack, placer, pointed_thing) return itemstack end - -- NOTE: Uncomment this to use nodetimers - -- Register nodetimer - pos_hash, meta = kelp.surface_register_nodetimer(pos_under) - pos_tip = pos_above node_tip = mt_get_node(pos_above) def_tip = mt_registered_nodes[node_tip.name] end -- New kelp must also be submerged in water. - local downward_flowing = kelp.is_downward_flowing(pos_tip, node_tip, def_tip) - if not (kelp.is_submerged(node_tip, def_tip) or downward_flowing) then + local downward_flowing = kelp.is_downward_flowing(pos_tip, node_tip) + local submerged = kelp.is_submerged(node_tip) + if not submerged then return itemstack end @@ -472,19 +587,46 @@ function kelp.kelp_on_place(itemstack, placer, pointed_thing) if def_node.sounds then mt_sound_play(def_node.sounds.place, { gain = 0.5, pos = pos_under }, true) end - kelp.next_grow(pos_under, node_under, pos_tip, def_tip, downward_flowing) + kelp.next_height(pos_under, node_under, pos_tip, node_tip, def_tip, submerged, downward_flowing) if not mt_is_creative_enabled(player_name) then itemstack:take_item() end - -- Reroll age - local age = math_random(0, kelp.MAX_AGE-1) - meta:set_int("mcl_ocean:kelp_age", age) - kelp.age_pool[pos_hash] = age - return itemstack end + +function kelp.lbm_register_nodetimer(pos, node) + local pos_hash = mt_hash_node_position(pos) + kelp.init_age(pos, nil, pos_hash) + kelp.init_timer(pos, pos_hash) +end + + +local gstep_time = 0 +function kelp.globalstep(dtime) + if #kelp.age_queue > kelp.MAX_AGE_QUEUE then + kelp.store_meta() + end + + gstep_time = gstep_time + dtime + if gstep_time < kelp.META_TICK then + return + end + gstep_time = 0 + + if #kelp.age_queue > 0 then + kelp.store_meta() + end +end + + +function kelp.on_shutdown() + if #kelp.age_queue > 0 then + kelp.store_meta() + end +end + -------------------------------------------------------------------------------- -- Kelp registration API -------------------------------------------------------------------------------- @@ -526,9 +668,10 @@ kelp.surface_deftemplate = { groups = { dig_immediate = 3, deco_block = 1, plant = 1, kelp = 1, }, --sounds = sounds, --node_dig_prediction = nodename, - after_dig_node = kelp.surface_after_dig_node, + on_construct = kelp.surface_on_construct, on_destruct = kelp.surface_on_destruct, on_dig = kelp.surface_on_dig, + after_dig_node = kelp.surface_after_dig_node, on_timer = kelp.surface_on_timer, mesecon = { on_mvps_move = kelp.surface_on_mvps_move, }, drop = "", -- drops are handled in on_dig @@ -573,7 +716,7 @@ function kelp.register_kelp_surface(surface, surface_deftemplate, surface_docs) doc.add_entry_alias("nodes", surface_docs.entry_id_orig, "nodes", surfacename) end - local sounds = table.copy(def.sounds) + local sounds = table_copy(def.sounds) sounds.dig = kelp.leaf_sounds.dig sounds.dug = kelp.leaf_sounds.dug sounds.place = kelp.leaf_sounds.place @@ -592,9 +735,9 @@ end -- Kelp surfaces nodes --------------------------------------------------------- -- Dirt must be registered first, for the docs -kelp.register_kelp_surface(kelp.surfaces[1], table.copy(kelp.surface_deftemplate), kelp.surface_docs) +kelp.register_kelp_surface(kelp.surfaces[1], table_copy(kelp.surface_deftemplate), kelp.surface_docs) for i=2, #kelp.surfaces do - kelp.register_kelp_surface(kelp.surfaces[i], table.copy(kelp.surface_deftemplate), kelp.surface_docs) + kelp.register_kelp_surface(kelp.surfaces[i], table_copy(kelp.surface_deftemplate), kelp.surface_docs) end -- Kelp item ------------------------------------------------------------------- @@ -676,15 +819,18 @@ minetest.register_craft({ -- ABMs ------------------------------------------------------------------------ --- NOTE: Uncomment this to use nodetimers minetest.register_lbm({ - label = "Kelp timer registration", - name = "mcl_ocean:kelp_timer_registration", + label = "Kelp initialise", + name = "mcl_ocean:kelp_init", nodenames = { "group:kelp" }, - run_at_every_load = true, -- so old kelps are also registered - action = kelp.surface_register_nodetimer, + run_at_every_load = true, -- so old kelps are also initialised + action = kelp.lbm_register_nodetimer, }) + +minetest.register_globalstep(kelp.globalstep) +minetest.register_on_shutdown(kelp.on_shutdown) + -- NOTE: Uncomment this to use ABMs -- minetest.register_abm({ -- label = "Kelp drops",