1
0
Fork 0

Compare commits

...

2 Commits

3 changed files with 165 additions and 206 deletions

12
API.md
View File

@ -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)

347
api.lua
View File

@ -1,151 +1,49 @@
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 = <standard sound definition for a node>
--
-- -- Node tiles for layout
-- tiles = <node tile layout>
--
-- -- 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
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- 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
internal.uninitialized_droplet_error(droplet)
end
table.insert(internal.sources[droplet], nodename)
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
internal.uninitialized_droplet_error(droplet)
end
internal.tricklers[droplet][oldnodename] = newnodename
end
-- Internal table that lets us define functions without directly exposing them.
local internal = {}
-- Capitalize a string
function internal.capitalize(str)
@ -161,7 +59,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.
@ -177,11 +75,7 @@ end
-- Determine whether this mod considers a node an air node.
function internal.is_air(nodename)
if nodename == "air" then
return true
else
return minetest.get_item_group(nodename, "air") ~= 0
end
return (nodename == "air") or (minetest.get_item_group(nodename, "air") ~= 0)
end
-- Create a node box for any given dripstone size.
@ -202,14 +96,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 +114,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,
@ -268,7 +154,7 @@ function internal.register_dripstone_flavor(flavor, def)
-- Allow dripstone nodes to trickle down droplets
for droplet, new_flavor in pairs(on_droplet_receive) do
for width = 1, 8, 1 do
internal.add_droplet_trickler(
internal.register_trickler(
droplet,
internal.size_to_name(flavor, width),
internal.size_to_name(new_flavor, width)
@ -276,9 +162,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 +230,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
@ -351,25 +238,29 @@ function internal.register_dripstone_node(flavor, size, tiles, sounds, drop)
description = internal.size_to_description(flavor, size),
tiles = tiles,
groups = {
pickaxey=2,
material_stone=1,
pickaxey = 2,
material_stone = 1,
fall_damage_add_percent = math.max(4 - size, 0) / 4 * 100
},
is_ground_content = true,
drop = {
max_items = math.floor((size + 1) / 2),
items = {
{ rarity = 1
, items = { drop }
{
rarity = 1,
items = { drop },
},
{ rarity = 2
, items = { drop }
{
rarity = 2,
items = { drop },
},
{ rarity = 4
, items = { drop }
{
rarity = 4,
items = { drop },
},
{ rarity = 4
, items = { drop }
{
rarity = 4,
items = { drop },
},
}
},
@ -387,10 +278,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 +290,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 +312,48 @@ 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
-- Add a droplet catcher, which is a node that allows a stalactite spike to
-- change the name using a droplet.
function internal.register_droplet_catcher(droplet, oldnodename, newnodename)
if CAULDRONS[droplet] == nil then
internal.uninitialized_droplet_error(droplet)
end
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, nodename, nodename)
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 +363,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 +381,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)
@ -464,8 +389,19 @@ function internal.register_trickle_down_abm(droplet, width, old_source, new_sour
})
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.register_trickler(droplet, oldnodename, newnodename)
if TRICKLERS[droplet] == nil then
internal.uninitialized_droplet_error(droplet)
end
TRICKLERS[droplet][oldnodename] = newnodename
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,
}

View File

@ -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