Merge pull request #2 from EmptyStar/feature_20241226
Give nodes limited fill potential
This commit is contained in:
commit
eef06b4bc4
@ -5,7 +5,11 @@ Fill empty glass bottles with a variety of earthly materials including water, di
|
||||
|
||||
On their own, filled bottles are only good for collecting and decoration. However, other mods may wish to make use of filled bottles for other purposes, such as for ingredients in cooking or chemistry. It's also possible to define your own filled bottles; see the API section below for details.
|
||||
|
||||
Bottles function in a very similar fashion to buckets, but one key difference is that bottles *do not displace nor dispense any nodes*. Filling a bottle from a target node simply 'transforms' the empty glass bottle into a filled bottle, and emptying a filled bottle turns it back into an empty glass bottle. On a technical level, bottles don't 'carry' their contents the way buckets do.
|
||||
Bottles function in a very similar fashion to buckets with two key differences. First, many bottles may be filled from one single node before the node is exhausted (10 bottles by default), and an exhausted node may be fully consumed (e.g., sand or water) or replaced with a "stripped" version of itself (e.g., dirt with grass becomes only dirt). Secondly, emptying a filled bottle does not dispense its contents, it simply becomes an empty glass bottle when emptied. In this way, bottles are effectively "transformed" into other types of bottles but they do not actually carry their contents the way buckets do.
|
||||
|
||||
Also note that partially bottled nodes have a chance to yield no drops when dug. This chance scales with how many times the node is bottled such that bottling a node more times increases the chance that it will not yield drops when dug. This is a balance measure to prevent infinite bottling of a single node.
|
||||
|
||||
The chance to yield no drops does not apply to liquids by default as liquids can easily be exploited to bypass this restriction, but this can be changed via settings if needed for a special case.
|
||||
|
||||
Supported games/mods
|
||||
--------------------
|
||||
@ -29,6 +33,9 @@ Want to create your own filled bottles? Use the `bottles.register_filled_bottle`
|
||||
-- from; either a string for a single node name
|
||||
-- or an array that is a list of node names
|
||||
|
||||
replacement = <string>, -- the node to replace the target node(s) with when
|
||||
-- a target node is fully drained; default is "air"
|
||||
|
||||
name = <string>, -- a name for the filled bottle item; will be prefixed with
|
||||
-- `bottles:`, so don't include any such namespace; default
|
||||
-- value is "bottle_of_" + the name of the target node
|
||||
|
@ -7,8 +7,28 @@ local function do_spill(...)
|
||||
return bottles.spill(...)
|
||||
end
|
||||
|
||||
-- Local map for liquid category to int for simple comparison
|
||||
local liquid_map = {
|
||||
none = 0,
|
||||
source = 1,
|
||||
flowing = 2,
|
||||
}
|
||||
|
||||
-- Globals
|
||||
bottles = {
|
||||
-- Settings
|
||||
settings = {
|
||||
node_fill_limit = tonumber(core.settings:get("bottles.node_fill_limit",10) or 10),
|
||||
liquid_fill_unlimited = (function(value)
|
||||
if value == "all" then
|
||||
return 1
|
||||
elseif value == "flowing" then
|
||||
return 2
|
||||
else
|
||||
return 3
|
||||
end
|
||||
end)(core.settings:get("bottles.liquid_fill_unlimited","all") or "all"),
|
||||
},
|
||||
|
||||
-- Registry of filled bottles
|
||||
registered_filled_bottles = {},
|
||||
@ -16,6 +36,9 @@ bottles = {
|
||||
-- Index target nodes to their filled bottle types
|
||||
target_node_map = {},
|
||||
|
||||
-- Cache node liquid statuses for special liquid handling
|
||||
target_liquid_map = {},
|
||||
|
||||
-- Play a sound whenever a bottle is filled or spilled
|
||||
play_bottle_sound = function(pos,sound)
|
||||
if sound then
|
||||
@ -32,7 +55,8 @@ bottles = {
|
||||
fill = function(itemstack,placer,target)
|
||||
if target.type == "node" and placer:is_player() then
|
||||
-- Get targeted node
|
||||
local node = minetest.get_node(target.under)
|
||||
local pos = target.under
|
||||
local node = minetest.get_node(pos)
|
||||
local filled_bottle = bottles.target_node_map[node.name]
|
||||
if filled_bottle then
|
||||
-- Play contents sound
|
||||
@ -53,11 +77,24 @@ bottles = {
|
||||
end
|
||||
end
|
||||
|
||||
-- Set filled node metadata if enabled; special handling for liquids
|
||||
local liquid = bottles.target_liquid_map[node.name]
|
||||
if bottles.settings.node_fill_limit > 0 and liquid < bottles.settings.liquid_fill_unlimited then
|
||||
local meta = core.get_meta(pos)
|
||||
local limit = meta:get_int("bottles.node_fill_limit")
|
||||
limit = limit + 1
|
||||
if limit >= bottles.settings.node_fill_limit then
|
||||
core.set_node(pos,{ name = filled_bottle.replacement })
|
||||
else
|
||||
meta:set_int("bottles.node_fill_limit",limit)
|
||||
end
|
||||
end
|
||||
|
||||
-- Return value
|
||||
return retval
|
||||
return retval, filled_bottle.name
|
||||
end
|
||||
end
|
||||
return itemstack
|
||||
return itemstack, nil
|
||||
end,
|
||||
|
||||
-- Spill the contents out of a filled bottle
|
||||
@ -112,6 +149,10 @@ bottles = {
|
||||
return false
|
||||
end
|
||||
|
||||
spec.description = spec.description or ("Bottle of " .. contents_node.description:split("\n")[1])
|
||||
|
||||
spec.replacement = spec.replacement or "air"
|
||||
|
||||
if spec.image then
|
||||
-- do nothing
|
||||
elseif type(contents_node.tiles[1]) == "string" then
|
||||
@ -134,9 +175,9 @@ bottles = {
|
||||
spec.target = {spec.target}
|
||||
end
|
||||
|
||||
-- Ensure that target nodes are not already in use, fail registration if so
|
||||
-- Ensure that target nodes exist and are not already in use, fail registration if so
|
||||
for _,target in ipairs(spec.target) do
|
||||
if bottles.target_node_map[target] then
|
||||
if bottles.target_node_map[target] or not core.registered_nodes[target] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
@ -146,9 +187,10 @@ bottles = {
|
||||
spec.sound = contents_node.sounds.footstep.name
|
||||
end
|
||||
|
||||
-- Map target nodes to spec
|
||||
-- Map target nodes and liquid status to spec
|
||||
for _,target in ipairs(spec.target) do
|
||||
bottles.target_node_map[target] = spec
|
||||
bottles.target_liquid_map[target] = liquid_map[core.registered_nodes[target].liquidtype or "none"]
|
||||
end
|
||||
|
||||
-- Put bottle into map of registered filled bottles
|
||||
@ -156,7 +198,7 @@ bottles = {
|
||||
|
||||
-- Register new bottle node
|
||||
minetest.register_node(":" .. spec.name,{
|
||||
description = spec.description or ("Bottle of " .. contents_node.description:split("\n")[1]),
|
||||
description = spec.description,
|
||||
drawtype = "plantlike",
|
||||
tiles = {spec.image},
|
||||
inventory_image = spec.image,
|
||||
@ -204,3 +246,17 @@ minetest.override_item("vessels:glass_bottle",{
|
||||
liquids_pointable = true,
|
||||
on_use = do_fill,
|
||||
})
|
||||
|
||||
-- Nodes that have been partially filled have a chance to drop nothing when dug
|
||||
if bottles.settings.node_fill_limit > 0 then
|
||||
local oghnd = core.handle_node_drops
|
||||
core.handle_node_drops = function(pos, drops, digger)
|
||||
local meta = core.get_meta(pos)
|
||||
local limit = meta:get_int("bottles.node_fill_limit")
|
||||
if limit > 0 and math.random(1,bottles.settings.node_fill_limit) <= limit then
|
||||
-- do nothing, node will drop nothing
|
||||
else
|
||||
return oghnd(pos, drops, digger)
|
||||
end
|
||||
end
|
||||
end
|
@ -1 +1,4 @@
|
||||
bottles.register_filled_bottle({ target = "badland:badland_grass" })
|
||||
bottles.register_filled_bottle({
|
||||
target = "badland:badland_grass",
|
||||
replacement = "default:dirt",
|
||||
})
|
@ -1,14 +1,17 @@
|
||||
bottles.register_filled_bottle({
|
||||
target = "caverealms:stone_with_moss",
|
||||
description = "Bottle of Cave Moss",
|
||||
replacement = "default:cobble",
|
||||
})
|
||||
|
||||
bottles.register_filled_bottle({
|
||||
target = "caverealms:stone_with_lichen",
|
||||
description = "Bottle of Lichen",
|
||||
replacement = "default:cobble",
|
||||
})
|
||||
|
||||
bottles.register_filled_bottle({
|
||||
target = "caverealms:stone_with_algae",
|
||||
description = "Bottle of Algae",
|
||||
replacement = "default:cobble",
|
||||
})
|
@ -51,6 +51,7 @@ bottles.register_filled_bottle({
|
||||
sound = "default_grass_footstep",
|
||||
name = "bottle_of_grass",
|
||||
description = "Bottle of Grass",
|
||||
replacement = "default:dirt",
|
||||
})
|
||||
|
||||
bottles.register_filled_bottle({
|
||||
@ -58,6 +59,7 @@ bottles.register_filled_bottle({
|
||||
sound = "default_grass_footstep",
|
||||
name = "bottle_of_dry_grass",
|
||||
description = "Bottle of Dry Grass",
|
||||
replacement = "default:dry_dirt",
|
||||
})
|
||||
|
||||
bottles.register_filled_bottle({
|
||||
@ -65,6 +67,7 @@ bottles.register_filled_bottle({
|
||||
sound = "default_grass_footstep",
|
||||
name = "bottle_of_coniferous_litter",
|
||||
description = "Bottle of Coniferous Litter",
|
||||
replacement = "default:dirt",
|
||||
})
|
||||
|
||||
bottles.register_filled_bottle({
|
||||
@ -72,6 +75,7 @@ bottles.register_filled_bottle({
|
||||
sound = "default_grass_footstep",
|
||||
name = "bottle_of_rainforest_litter",
|
||||
description = "Bottle of Rainforest Litter",
|
||||
replacement = "default:dirt",
|
||||
})
|
||||
|
||||
bottles.register_filled_bottle({
|
||||
@ -79,6 +83,7 @@ bottles.register_filled_bottle({
|
||||
sound = "default_grass_footstep",
|
||||
name = "bottle_of_moss",
|
||||
description = "Bottle of Tundra Moss",
|
||||
replacement = "default:permafrost_with_stones",
|
||||
})
|
||||
|
||||
bottles.register_filled_bottle({
|
||||
|
@ -1 +1,4 @@
|
||||
bottles.register_filled_bottle({ target = "dorwinion:dorwinion_grass" })
|
||||
bottles.register_filled_bottle({
|
||||
target = "dorwinion:dorwinion_grass",
|
||||
replacement = "dorwinion:dorwinion",
|
||||
})
|
@ -12,5 +12,6 @@ for _,dirt in ipairs({
|
||||
bottles.register_filled_bottle({
|
||||
target = "ethereal:" .. dirt:lower() .. "_dirt",
|
||||
description = "Bottle of " .. dirt .. " Dirt",
|
||||
replacement = "default:dirt",
|
||||
})
|
||||
end
|
@ -1,35 +1,38 @@
|
||||
for node,description in pairs({
|
||||
["everness:coral_desert_stone_with_moss"] = "Coral Cave Moss",
|
||||
["everness:mold_stone_with_moss"] = "Moldy Moss",
|
||||
["everness:coral_dirt"] = false,
|
||||
["everness:cursed_dirt"] = false,
|
||||
["everness:crystal_dirt"] = false,
|
||||
["everness:forsaken_tundra_dirt"] = false,
|
||||
["everness:forsaken_tundra_dirt_with_grass"] = "Forsaken Tundra Grass",
|
||||
["everness:dirt_with_coral_grass"] = "Coral Grass",
|
||||
["everness:dirt_with_cursed_grass"] = "Cursed Grass",
|
||||
["everness:dirt_with_crystal_grass"] = "Crystal Grass",
|
||||
["everness:dry_ocean_dirt"] = false,
|
||||
["everness:crystal_cave_dirt"] = false,
|
||||
["everness:crystal_cave_dirt_with_moss"] = "Crystal Cave Moss",
|
||||
["everness:moss_block"] = "Moss",
|
||||
["everness:crystal_moss_block"] = "Crystal Moss",
|
||||
["everness:coral_sand"] = false,
|
||||
["everness:coral_white_sand"] = false,
|
||||
["everness:cursed_sand"] = false,
|
||||
["everness:crystal_sand"] = false,
|
||||
["everness:forsaken_tundra_beach_sand"] = false,
|
||||
["everness:forsaken_desert_sand"] = false,
|
||||
["everness:coral_forest_deep_ocean_sand"] = false,
|
||||
["everness:cursed_lands_deep_ocean_sand"] = false,
|
||||
["everness:crystal_forest_deep_ocean_sand"] = false,
|
||||
["everness:mineral_sand"] = false,
|
||||
["everness:frosted_snowblock"] = "Frosted Snow",
|
||||
["everness:cursed_mud"] = false,
|
||||
for node,data in pairs({
|
||||
["everness:coral_desert_stone_with_moss"] = { "Coral Cave Moss", "everness:coral_desert_stone" },
|
||||
["everness:mold_stone_with_moss"] = { "Moldy Moss" },
|
||||
["everness:coral_dirt"] = {},
|
||||
["everness:cursed_dirt"] = {},
|
||||
["everness:crystal_dirt"] = { "Crystal Dirt", "air", "everness_crystal_dirt" },
|
||||
["everness:forsaken_tundra_dirt"] = {},
|
||||
["everness:forsaken_tundra_dirt_with_grass"] = { "Forsaken Tundra Grass", "everness:forsaken_tundra_dirt" },
|
||||
["everness:dirt_with_coral_grass"] = { "Coral Grass", "everness:coral_dirt" },
|
||||
["everness:dirt_with_cursed_grass"] = { "Cursed Grass", "everness:cursed_dirt" },
|
||||
["everness:dirt_with_crystal_grass"] = { "Crystal Grass", "everness:crystal_dirt" },
|
||||
["everness:dry_ocean_dirt"] = {},
|
||||
["everness:crystal_cave_dirt"] = {},
|
||||
["everness:crystal_cave_dirt_with_moss"] = { "Crystal Cave Moss" },
|
||||
["everness:moss_block"] = { "Moss" },
|
||||
["everness:crystal_moss_block"] = { "Crystal Moss" },
|
||||
["everness:coral_sand"] = {},
|
||||
["everness:coral_white_sand"] = {},
|
||||
["everness:cursed_sand"] = {},
|
||||
["everness:crystal_sand"] = {},
|
||||
["everness:forsaken_tundra_beach_sand"] = {},
|
||||
["everness:forsaken_desert_sand"] = {},
|
||||
["everness:coral_forest_deep_ocean_sand"] = {},
|
||||
["everness:cursed_lands_deep_ocean_sand"] = {},
|
||||
["everness:crystal_forest_deep_ocean_sand"] = {},
|
||||
["everness:mineral_sand"] = {},
|
||||
["everness:frosted_snowblock"] = { "Frosted Snow" },
|
||||
["everness:cursed_mud"] = {},
|
||||
["everness:mineral_lava_stone_with_moss"] = { "Mineral Cave Moss", "everness:mineral_cave_stone" }
|
||||
}) do
|
||||
bottles.register_filled_bottle({
|
||||
name = data[3],
|
||||
target = node,
|
||||
description = description and ("Bottle of " .. description) or nil,
|
||||
description = data[1] and ("Bottle of " .. data[1]) or nil,
|
||||
replacement = data[2]
|
||||
})
|
||||
end
|
||||
|
||||
|
@ -1 +1,4 @@
|
||||
bottles.register_filled_bottle({ target = "frost_land:frost_land_grass" })
|
||||
bottles.register_filled_bottle({
|
||||
target = "frost_land:frost_land_grass",
|
||||
replacement = "default:dirt",
|
||||
})
|
@ -1,4 +1,5 @@
|
||||
bottles.register_filled_bottle({
|
||||
target = "japaneseforest:japanese_dirt_with_grass",
|
||||
description = "Bottle of Japanese Grass",
|
||||
replacement = "default:dirt",
|
||||
})
|
@ -2,5 +2,8 @@ for _,node in ipairs({
|
||||
"livingjungle:jungleground",
|
||||
"livingjungle:leafyjungleground",
|
||||
}) do
|
||||
bottles.register_filled_bottle({ target = node })
|
||||
bottles.register_filled_bottle({
|
||||
target = node,
|
||||
replacement = "default:dirt",
|
||||
})
|
||||
end
|
@ -1,20 +1,22 @@
|
||||
for node,description in pairs({
|
||||
["naturalbiomes:alderswamp_litter"] = "Alder Swamp Grass",
|
||||
["naturalbiomes:alpine_litter"] = "Alpine Grass",
|
||||
["naturalbiomes:bambooforest_litter"] = "Bamboo Litter",
|
||||
["naturalbiomes:bushland_bushlandlitter"] = false,
|
||||
["naturalbiomes:bushland_bushlandlitter2"] = false,
|
||||
["naturalbiomes:bushland_bushlandlitter3"] = false,
|
||||
["naturalbiomes:heath_litter"] = "Heath Litter",
|
||||
["naturalbiomes:heath_litter2"] = "Heath Litter",
|
||||
["naturalbiomes:heath_litter3"] = "Heath Litter",
|
||||
["naturalbiomes:mediterran_litter"] = "Mediterranean Grass",
|
||||
["naturalbiomes:outback_litter"] = "Outback Grass",
|
||||
["naturalbiomes:palmbeach_sand"] = "Beach Sand",
|
||||
["naturalbiomes:savannalitter"] = "Savanna Litter",
|
||||
for node,data in pairs({
|
||||
["naturalbiomes:alderswamp_litter"] = { "Alder Swamp Grass", "naturalbiomes:alderswamp_dirt" },
|
||||
["naturalbiomes:alderswamp_dirt"] = {},
|
||||
["naturalbiomes:alpine_litter"] = { "Alpine Grass", "default:dirt" },
|
||||
["naturalbiomes:bambooforest_litter"] = { "Bamboo Litter" },
|
||||
["naturalbiomes:bushland_bushlandlitter"] = { false, "default:dirt" },
|
||||
["naturalbiomes:bushland_bushlandlitter2"] = {},
|
||||
["naturalbiomes:bushland_bushlandlitter3"] = {},
|
||||
["naturalbiomes:heath_litter"] = { "Heath Litter", "default:sand" },
|
||||
["naturalbiomes:heath_litter2"] = { "Heath Litter", "default:sand" },
|
||||
["naturalbiomes:heath_litter3"] = { "Heath Litter", "default:sand" },
|
||||
["naturalbiomes:mediterran_litter"] = { "Mediterranean Grass", "default:dirt" },
|
||||
["naturalbiomes:outback_litter"] = { "Outback Grass", "naturalbiomes:outback_ground" },
|
||||
["naturalbiomes:palmbeach_sand"] = { "Beach Sand" },
|
||||
["naturalbiomes:savannalitter"] = { "Savanna Litter", "default:dirt" },
|
||||
}) do
|
||||
bottles.register_filled_bottle({
|
||||
target = node,
|
||||
description = description and ("Bottle of " .. description) or nil,
|
||||
description = data[1] and ("Bottle of " .. data[1]) or nil,
|
||||
replacement = data[2],
|
||||
})
|
||||
end
|
@ -1,4 +1,5 @@
|
||||
bottles.register_filled_bottle({
|
||||
target = "nightshade:nightshade_dirt_with_grass",
|
||||
description = "Bottle of Nightshade Grass",
|
||||
replacement = "default:dirt",
|
||||
})
|
@ -1,4 +1,5 @@
|
||||
bottles.register_filled_bottle({
|
||||
target = "prairie:prairie_dirt_with_grass",
|
||||
description = "Bottle of Prairie Grass",
|
||||
replacement = "default:dirt",
|
||||
})
|
9
settingtypes.txt
Normal file
9
settingtypes.txt
Normal file
@ -0,0 +1,9 @@
|
||||
# How many times a node can be collected via bottle before the target node is used up. Used up nodes will be replaced with a registered replacement node and have a chance to drop nothing when mined. Set to 0 for infinite bottle fills per node.
|
||||
bottles.node_fill_limit (Node fill limit) int 10 0 99
|
||||
|
||||
# Allow infinite bottling for liquids:
|
||||
# "all" = all liquids allow infinite bottling
|
||||
# "flowing" = only flowing liquids allow infinite bottling
|
||||
# "none" = no liquids allow infinite bottling
|
||||
# NOTE: This setting can easily be circumvented for most typical liquids
|
||||
bottles.liquid_fill_unlimited (Liquid fill unlimited) enum all all,flowing,none
|
Loading…
x
Reference in New Issue
Block a user