diff --git a/API.md b/API.md index 17098dc..f9f057f 100644 --- a/API.md +++ b/API.md @@ -108,7 +108,7 @@ dripstone.register_droplet("mercury") dripstone.register_droplet("mineral_water") ``` -### dripstone.add_droplet_source(droplet, nodename) +### dripstone.register_source(droplet, nodename) In order to let droplets stream down dripstone nodes, you need to define which nodes a full dripstone block can absorb liquid from. Absorbing liquid does not @@ -120,10 +120,10 @@ However, you don't need to do this! You could use any node, as long as it has a name. ```lua -dripstone.add_droplet_source("water", "mymod:swamp_water_source") +dripstone.register_source("water", "mymod:swamp_water_source") ``` -### dripstone.add_droplet_catcher(droplet, oldnodename, newnodename) +### dripstone.register_catcher(droplet, oldnodename, newnodename) Similarly, on the other side of the dripstone, you can create a catcher that can catch any of the liquid drops dripping down. You can use this to create @@ -131,9 +131,9 @@ orchids, or to create other interactions with nodes that might change from a tiny bit of liquid. ```lua -dripstone.add_droplet_catcher("water", "mymod:cauldron_empty", "mymod:water_cauldron") -dripstone.add_droplet_catcher("water", "mymod:dirt", "mymod:farmland") -dripstone.add_droplet_catcher("lava", "mymod:precious_orchid", "mymod:dead_bush") +dripstone.register_catcher("water", "mymod:cauldron_empty", "mymod:water_cauldron") +dripstone.register_catcher("water", "mymod:dirt", "mymod:farmland") +dripstone.register_catcher("lava", "mymod:precious_orchid", "mymod:dead_bush") ``` ### dripstone.register_dripstone(flavor, def) diff --git a/api.lua b/api.lua index d6f1f29..8599077 100644 --- a/api.lua +++ b/api.lua @@ -1,150 +1,80 @@ -dripstone = {} +-- Nodes that function as cauldrons +local CAULDRONS = {} --- Internal values that cannot be changed by other mods (directly). -local internal = { - -- These values are not meant to be changed during runtime. - constant = { - -- How many nodes downwards a droplet is able to drop from a stalactite - -- before the droplet evaporates. - drop_down_reach = 50, +-- How many nodes downwards a droplet is able to drop from a stalactite +-- before the droplet evaporates. +local DROP_DOWN_REACH = 50 - -- The number of seconds it takes for a dripstone node to grow 1 unit - -- (NOTE: Not one node size! One unit, which quadratically increases - -- per node size.) - growth_factor = 3, +-- The number of seconds it takes for a dripstone node to grow 1 unit +-- (NOTE: Not one node size! One unit, which quadratically increases +-- per node size.) +local GROWTH_FACTOR = 3 - -- This mod's name. - modname = minetest.get_current_modname(), +-- This mod's name. +local MODNAME = minetest.get_current_modname() - -- The number of samples that each ABM should execute. - -- Make sure this is a whole number and less than speed_factor. - samples_per_interval = 30, +-- The number of samples that each ABM should execute. +-- Make sure this is a whole number and less than speed_factor. +local SAMPLES_PER_INTERVAL = 30 - -- Factor deciding this mod's relative speed. - -- Set this value to 1 if you wish to debug and let the dripstone - -- change rapidly. - -- Rule of thumb: with a setting of 60, it takes a lava farm about 30 - -- minutes to fill a cauldron with lava. - speed_factor = 60, +-- Nodes that provide droplets +local SOURCES = {} - -- Names of the various dripstone widths - width_names = { - "spike", "tiny", "small", "medium", - "great", "large", "huge", "block", - }, - }, +-- Factor deciding this mod's relative speed. +-- Set this value to 1 if you wish to debug and let the dripstone +-- change rapidly. +-- Rule of thumb: with a setting of 60, it takes a lava farm about 30 +-- minutes to fill a cauldron with lava. +local SPEED_FACTOR = 60 - -- Nodes that function as cauldrons - cauldrons = {}, - - -- Nodes that provide droplets - sources = {}, - - -- Nodes that allow a droplet to trickle down if it is directly below a +-- Nodes that allow a droplet to trickle down if it is directly below a -- node that passes down that droplet. - tricklers = {}, +local TRICKLERS = {} + +-- Names of the various dripstone widths +local WIDTH_NAMES = { + "spike", "tiny", "small", "medium", "great", "large", "huge", "block", } - ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ---------------------------- PUBLIC API -------------------------------- +------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- --- Register a node that can catch a droplet from a dripstone stalactite. -function dripstone.add_droplet_catcher(droplet, oldnodename, newnodename) - return internal.add_droplet_catcher(droplet, oldnodename, newnodename) -end - --- Register a new source node that can provide droplets to dripstone blocks. -function dripstone.add_droplet_source(droplet, nodename) - return internal.add_droplet_source(droplet, nodename) -end - --- Register a new dripstone type. --- --- { --- -- What item is dropped when the dripstone is broken. --- -- When left nil, the spike of the dripstone type is dropped. --- drop = "dry" --- --- -- What flavor to become when using liquid to grow. --- -- Leave to nil when unable to grow. --- grow_to = "dry" --- --- -- When receiving a droplet of a given type, transform into a different --- -- dripstone type. When a droplet is unspecified, the block cannot --- -- receive the droplet. --- on_droplet_receive = { --- water = "watered", --- lava = "molten", --- } --- --- -- Sounds that the dripstone makes --- sounds = --- --- -- Node tiles for layout --- tiles = --- --- -- Droplet type that the dripstone flavor can pass down. --- -- When the droplet is passed down, the dripstone converts to the --- -- "grow_to" type --- trickle_down = "water" --- --- -- Speed of how often a droplet trickles down. --- trickle_speed = 5 --- } -function dripstone.register_dripstone(flavor, def) - return internal.register_dripstone_flavor(flavor, def) -end - --- Register a new droplet type that can be absorbed and passed on by dripstone. -function dripstone.register_droplet(droplet) - if internal.cauldrons[droplet] == nil then - internal.cauldrons[droplet] = {} - end - if internal.sources[droplet] == nil then - internal.sources[droplet] = {} - end - if internal.tricklers[droplet] == nil then - internal.tricklers[droplet] = {} - end -end - --- Get a dripstone's node name based on its flavor and size. -function dripstone.size_to_name(flavor, size) - return internal.size_to_name(flavor, size) -end - -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- +-- Internal table that lets us define functions without directly exposing them. +local internal = {} -- Add a droplet catcher, which is a node that allows a stalactite spike to -- change the name using a droplet. -function internal.add_droplet_catcher(droplet, oldnodename, newnodename) - return internal.register_cauldron(droplet, oldnodename, newnodename) -end - -function internal.add_droplet_source(droplet, nodename) - if internal.sources[droplet] == nil then +function internal.register_droplet_catcher(droplet, oldnodename, newnodename) + if CAULDRONS[droplet] == nil then internal.uninitialized_droplet_error(droplet) end - table.insert(internal.sources[droplet], nodename) + + CAULDRONS[droplet][oldnodename] = newnodename +end + +function internal.register_droplet_source(droplet, nodename) + if SOURCES[droplet] == nil then + internal.uninitialized_droplet_error(droplet) + end + table.insert(SOURCES[droplet], nodename) + + -- If the node can emit an infinite number of droplets, + -- it can also absorb an infinite number of droplets. + internal.register_droplet_catcher(droplet, oldnodename, newnodename) end -- Add a droplet trickler, which is a dripstone node that allows a droplet to -- be trickled down from the node directly above it. -- Running this function overrides previous values. function internal.add_droplet_trickler(droplet, oldnodename, newnodename) - if internal.tricklers[droplet] == nil then + if TRICKLERS[droplet] == nil then internal.uninitialized_droplet_error(droplet) end - internal.tricklers[droplet][oldnodename] = newnodename + TRICKLERS[droplet][oldnodename] = newnodename end -- Capitalize a string @@ -161,7 +91,7 @@ function internal.drawtype_of_size(size) end function internal.hit_with_droplet(pos, node, droplet, spikename) - local m = internal.cauldrons[droplet] or {} + local m = CAULDRONS[droplet] or {} if m[node.name] == nil then -- Not a cauldron! Therefore we place a spike on top. @@ -202,14 +132,14 @@ end function internal.register_absorb_abm(droplet, oldnodename, newnodename) minetest.register_abm({ nodenames = { oldnodename }, - interval = internal.constant.speed_factor / internal.constant.samples_per_interval, - chance = internal.constant.samples_per_interval, + interval = SPEED_FACTOR / SAMPLES_PER_INTERVAL, + chance = SAMPLES_PER_INTERVAL, catch_up = true, - action = function (pos, node, aoc, aocw) + action = function(pos, node) local pos_above = vector.offset(pos, 0, 1, 0) local node_above = minetest.get_node(pos_above) - for _, source in pairs(internal.sources[droplet] or {}) do + for _, source in pairs(SOURCES[droplet] or {}) do if node_above.name == source then node.name = newnodename minetest.set_node(pos, node) @@ -220,14 +150,6 @@ function internal.register_absorb_abm(droplet, oldnodename, newnodename) }) end -function internal.register_cauldron(droplet, oldnodename, newnodename) - if internal.cauldrons[droplet] == nil then - internal.uninitialized_droplet_error(droplet) - end - - internal.cauldrons[droplet][oldnodename] = newnodename -end - function internal.register_dripstone_craft(newnodename, oldnodename, spikename) minetest.register_craft({ output = newnodename, @@ -276,9 +198,26 @@ function internal.register_dripstone_flavor(flavor, def) end end - -- Allow spike stalagmites to catch droplets + -- Makes dripstone stalagmite spikes delete droplets. + -- Without this, stalactites remain very thick and short while + -- stalagmites become absurdly long and thin. + -- A watered stalagmite can't accept a water droplet and the stalagmite + -- therefore grows one per droplet. To mitigate this, a watered spike + -- can still act as a water droplet cauldron without changing. + -- This way, no new droplets are passed on if the stalagmite is already + -- full, and the structure simply waits for a dripstone node to grow. + -- This behaviour is designed to be easy to override. (For example: if + -- you want a HEAVY watered dripstone type that holds 2 droplets.) + internal.register_droplet_catcher( + trickl, + internal.size_to_name(flavor, 1), + internal.size_to_name(flavor, 1) + ) + + -- Allow spike stalagmites to catch droplets. + -- This feature can override the former safeguard. for droplet, new_flavor in pairs(on_droplet_receive) do - internal.register_cauldron( + internal.register_droplet_catcher( droplet, internal.size_to_name(flavor, 1), internal.size_to_name(new_flavor, 1) @@ -327,22 +266,6 @@ function internal.register_dripstone_flavor(flavor, def) internal.size_to_name(dry_up, 1), trickle_speed ) - - -- Makes dripstone stalagmite spikes delete droplets. - -- Without this, stalactites remain very thick and short while - -- stalagmites become absurdly long and thin. - -- A watered stalagmite can't accept a water droplet and the stalagmite - -- therefore grows one per droplet. To mitigate this, a watered spike - -- can still act as a water droplet cauldron without changing. - -- This way, no new droplets are passed on if the stalagmite is already - -- full, and the structure simply waits for a dripstone node to grow. - -- This behaviour is designed to be easy to override. (For example: if - -- you want a HEAVY watered dripstone type that holds 2 droplets.) - internal.add_droplet_catcher( - trickl, - internal.size_to_name(flavor, 1), - internal.size_to_name(flavor, 1) - ) end end @@ -387,10 +310,10 @@ end function internal.register_drop_down_abm(droplet, spikename, dryspikename, trickle_speed) minetest.register_abm({ nodenames = { spikename }, - interval = trickle_speed * internal.constant.speed_factor / internal.constant.samples_per_interval, - chance = internal.constant.samples_per_interval, + interval = trickle_speed * SPEED_FACTOR / SAMPLES_PER_INTERVAL, + chance = SAMPLES_PER_INTERVAL, catch_up = true, - action = function (pos, node, aoc, aocw) + action = function(pos, node) local pos_below = vector.offset(pos, 0, -1, 0) local node_below = minetest.get_node(pos_below) @@ -399,7 +322,7 @@ function internal.register_drop_down_abm(droplet, spikename, dryspikename, trick return end - for dy = 2, internal.constant.drop_down_reach, 1 do + for dy = 2, DROP_DOWN_REACH, 1 do pos_below = vector.offset(pos, 0, -dy, 0) node_below = minetest.get_node(pos_below) @@ -421,14 +344,27 @@ function internal.register_drop_down_abm(droplet, spikename, dryspikename, trick }) end +-- Register a new droplet type that can be absorbed and passed on by dripstone. +function internal.register_droplet(droplet) + if CAULDRONS[droplet] == nil then + CAULDRONS[droplet] = {} + end + if SOURCES[droplet] == nil then + SOURCES[droplet] = {} + end + if TRICKLERS[droplet] == nil then + TRICKLERS[droplet] = {} + end +end + function internal.register_grow_abm(oldnodename, newnodename, width) minetest.register_abm({ nodenames = { oldnodename }, -- 2(w + 1) * 2(w + 1) - 2w * 2w = 8w + 4 - interval = (8 * width + 4) * internal.constant.speed_factor * internal.constant.growth_factor / internal.constant.samples_per_interval, - chance = internal.constant.samples_per_interval, + interval = (8 * width + 4) * SPEED_FACTOR * GROWTH_FACTOR / SAMPLES_PER_INTERVAL, + chance = SAMPLES_PER_INTERVAL, catch_up = true, - action = function (pos, node, aoc, aocw) + action = function(pos, node) node.name = newnodename minetest.set_node(pos, node) end @@ -438,14 +374,14 @@ end function internal.register_trickle_down_abm(droplet, width, old_source, new_source, dry_up, trickle_speed) minetest.register_abm({ nodenames = { old_source }, - interval = trickle_speed * internal.constant.speed_factor / internal.constant.samples_per_interval, - chance = internal.constant.samples_per_interval, + interval = trickle_speed * SPEED_FACTOR / SAMPLES_PER_INTERVAL, + chance = SAMPLES_PER_INTERVAL, catch_up = true, - action = function (pos, node, aoc, aocw) + action = function(pos, node) local pos_below = vector.offset(pos, 0, -1, 0) local node_below = minetest.get_node(pos_below) - local m = internal.tricklers[droplet] or {} + local m = TRICKLERS[droplet] or {} if m[node_below.name] ~= nil then -- Trickler found below! @@ -456,7 +392,7 @@ function internal.register_trickle_down_abm(droplet, width, old_source, new_sour else return -- Prevent droplet from leaking away end - + node.name = new_source minetest.set_node(pos_below, node_below) minetest.set_node(pos, node) @@ -465,7 +401,7 @@ function internal.register_trickle_down_abm(droplet, width, old_source, new_sour end function internal.size_to_description(flavor, size) - local width_name = internal.constant.width_names[size] + local width_name = WIDTH_NAMES[size] if size == 1 or size == 8 then return internal.capitalize(flavor) .. " dripstone " .. width_name @@ -475,8 +411,8 @@ function internal.size_to_description(flavor, size) end function internal.size_to_name(flavor, size) - local namespace = internal.constant.modname .. ":" - local width_name = internal.constant.width_names[size] + local namespace = MODNAME .. ":" + local width_name = WIDTH_NAMES[size] if size == 1 or size == 8 then return namespace .. flavor .. "_dripstone_" .. width_name @@ -490,3 +426,26 @@ function internal.uninitialized_droplet_error(droplet) "Droplet " .. droplet .. " has not been initialized yet!" ) end + +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- +--------------------------- PUBLIC API -------------------------------- +------------------------------------------------------------------------------- +------------------------------------------------------------------------------- + +dripstone = { + -- Register a node that can catch a droplet from a dripstone stalactite. + register_catcher = internal.register_droplet_catcher, + + -- Register a new dripstone type. + register_dripstone = internal.register_dripstone_flavor, + + -- Register a new droplet type that can be absorbed and passed on by dripstone. + register_droplet = internal.register_droplet, + + -- Register a source node that can provide droplets to dripstone blocks. + register_source = internal.register_droplet_source, + + -- Get a dripstone's node name based on its flavor and size. + size_to_name = internal.size_to_name, +} diff --git a/init.lua b/init.lua index 34b6ee6..7985f5a 100644 --- a/init.lua +++ b/init.lua @@ -72,16 +72,16 @@ dripstone.register_dripstone("hardened", { -- Register droplet sources above dripstone blocks if minetest.get_modpath("default") then - dripstone.add_droplet_source("water", "default:river_water_source") - dripstone.add_droplet_source("water", "default:water_source") - dripstone.add_droplet_source("lava", "default:lava_source") + dripstone.register_source("water", "default:river_water_source") + dripstone.register_source("water", "default:water_source") + dripstone.register_source("lava", "default:lava_source") end if minetest.get_modpath("mcl_core") then - dripstone.add_droplet_source("water", "mcl_core:water_source") - dripstone.add_droplet_source("lava", "mcl_core:lava_source") + dripstone.register_source("water", "mcl_core:water_source") + dripstone.register_source("lava", "mcl_core:lava_source") end if minetest.get_modpath("mclx_core") then - dripstone.add_droplet_source("water", "mclx_core:river_water_source") + dripstone.register_source("water", "mclx_core:river_water_source") end