commit 34fe189a3c016eb7d32d08425bf932c4baa2e589 Author: Evert Prants Date: Wed Jun 20 16:55:16 2018 +0300 Initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..175341e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2018 Evert "Diamond" Prants + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.html b/README.html new file mode 100644 index 0000000..a20e8f5 --- /dev/null +++ b/README.html @@ -0,0 +1,1143 @@ +README

Universal Fluid API

+

This API adds support for fluid_buffers inside nodes. This means that nodes can contain fluid.

+

How to Use

+
    +
  1. Add the node to the fluid_container group.
  2. +
  3. Add the following to the node defintion: +
    fluid_buffers = {
    +    buffer_name = {
    +        capacity = 2000,
    +        accepts = {"default:water_source", "group:water_source"}, -- you can also set this to true to accept any fluid!
    +        drainable = true,
    +    },
    +}
    +
  4. +
  5. Set the appropriate metadata.
  6. +
+
    +
  • buffer_name_fluid = string - Source node of the fluid.
  • +
  • buffer_name_fluid_storage = int - How much fluid there is in this buffer.
  • +
+

API

+

All numbers are in milli-buckets (1 bucket = 1000 mB).

+
    +
  • +

    fluid_lib.get_node_buffers(pos)

    +
      +
    • Returns all the fluid buffers present inside a node.
    • +
    +
  • +
  • +

    fluid_lib.get_buffer_data(pos, buffer)

    +
      +
    • Returns all the information about this buffer. +
      {
      +    fluid     = fluid source block,
      +    amount    = amount of fluid,
      +    accepts   = list of accepted fluids,
      +    capacity  = capacity of the buffer,
      +    drainable = is this buffer drainable,
      +}
      +
    • +
    +
  • +
  • +

    fluid_lib.buffer_accepts_fluid(pos, buffer, fluid)

    +
      +
    • Returns true if fluid can go inside the buffer at pos.
    • +
    +
  • +
  • +

    fluid_lib.can_insert_into_buffer(pos, buffer, fluid, count)

    +
      +
    • Returns the amount of fluid that can go inside the buffer at pos. If all of it fits, it returns count.
    • +
    +
  • +
  • +

    fluid_lib.insert_into_buffer(pos, buffer, fluid, count)

    +
      +
    • Actually does the inserting.
    • +
    +
  • +
  • +

    fluid_lib.can_take_from_buffer(pos, buffer, count)

    +
      +
    • Returns the amount of fluid that can be taken from the buffer at pos.
    • +
    +
  • +
  • +

    fluid_lib.take_from_buffer(pos, buffer, count)

    +
      +
    • Actually takes the fluid. On success, returns the source block that was taken and how much was actually taken.
    • +
    +
  • +
  • +

    bucket.register_liquid(source, flowing, itemname, inventory_image, name, groups, force_renew)

    +
      +
    • Works exactly the same as the default bucket mod, except it adds callbacks to insert/take fluid from nodes.
    • +
    • inventory_image can be a ColorString.
    • +
    +
  • +
+

License

+

bucket

+

See bucket/license.txt

+

fluid_lib

+

See LICENSE

\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..804431e --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# Universal Fluid API +This API adds support for `fluid_buffers` inside nodes. This means that nodes can contain fluid. + +## How to Use +1. Add the node to the `fluid_container` group. +2. Add the following to the node defintion: +``` +fluid_buffers = { + buffer_name = { + capacity = 2000, + accepts = {"default:water_source", "group:water_source"}, -- you can also set this to true to accept any fluid! + drainable = true, + }, +} +``` +3. Set the appropriate metadata. + +* **buffer_name_fluid** = `string` - Source node of the fluid. +* **buffer_name_fluid_storage** = `int` - How much fluid there is in this buffer. + +## API +All numbers are in **milli-buckets** (1 bucket = 1000 mB). + +* `fluid_lib.get_node_buffers(pos)` + * Returns all the fluid buffers present inside a node. + +* `fluid_lib.get_buffer_data(pos, buffer)` + * Returns all the information about this buffer. +``` +{ + fluid = fluid source block, + amount = amount of fluid, + accepts = list of accepted fluids, + capacity = capacity of the buffer, + drainable = is this buffer drainable, +} +``` + +* `fluid_lib.buffer_accepts_fluid(pos, buffer, fluid)` + * Returns `true` if `fluid` can go inside the `buffer` at `pos`. + +* `fluid_lib.can_insert_into_buffer(pos, buffer, fluid, count)` + * Returns the amount of `fluid` that can go inside the `buffer` at `pos`. If all of it fits, it returns `count`. + +* `fluid_lib.insert_into_buffer(pos, buffer, fluid, count)` + * Actually does the inserting. + +* `fluid_lib.can_take_from_buffer(pos, buffer, count)` + * Returns the amount of `fluid` that can be taken from the `buffer` at `pos`. + +* `fluid_lib.take_from_buffer(pos, buffer, count)` + * Actually takes the fluid. On success, returns the source block that was taken and how much was actually taken. + +* `bucket.register_liquid(source, flowing, itemname, inventory_image, name, groups, force_renew)` + * Works exactly the same as the default `bucket` mod, except it adds callbacks to insert/take fluid from nodes. + * `inventory_image` can be a **ColorString**. + +## License +### bucket +See [bucket/license.txt](bucket/license.txt) +### fluid_lib +See [LICENSE](LICENSE) diff --git a/bucket/README.md b/bucket/README.md new file mode 100644 index 0000000..37c45e5 --- /dev/null +++ b/bucket/README.md @@ -0,0 +1,18 @@ +bucket +========================= +This is an addition to the Minetest Game `bucket` mod that implements the `fluid_lib` API seamlessly. +See license.txt for original license information. + +Authors of source code +---------------------- +Kahrl (LGPL 2.1) + +celeron55, Perttu Ahola (LGPL 2.1) + +Various Minetest developers and contributors (LGPL 2.1) + +Modified by Evert "Diamond" Prants (MIT) + +Authors of media (textures) +--------------------------- +ElementW (CC BY-SA 3.0) diff --git a/bucket/depends.txt b/bucket/depends.txt new file mode 100644 index 0000000..a7c6667 --- /dev/null +++ b/bucket/depends.txt @@ -0,0 +1 @@ +default,fluid_lib diff --git a/bucket/init.lua b/bucket/init.lua new file mode 100644 index 0000000..6be8b8b --- /dev/null +++ b/bucket/init.lua @@ -0,0 +1,288 @@ +-- Minetest 0.4 mod: bucket +-- See README.md for licensing and other information. + +minetest.register_alias("bucket", "bucket:bucket_empty") +minetest.register_alias("bucket_water", "bucket:bucket_water") +minetest.register_alias("bucket_lava", "bucket:bucket_lava") + +minetest.register_craft({ + output = 'bucket:bucket_empty 1', + recipe = { + {'default:steel_ingot', '', 'default:steel_ingot'}, + {'', 'default:steel_ingot', ''}, + } +}) + +bucket = {} +bucket.liquids = {} + +local function check_protection(pos, name, text) + if minetest.is_protected(pos, name) then + minetest.log("action", (name ~= "" and name or "A mod") + .. " tried to " .. text + .. " at protected position " + .. minetest.pos_to_string(pos) + .. " with a bucket") + minetest.record_protection_violation(pos, name) + return true + end + return false +end + +-- Register a new liquid +-- source = name of the source node +-- flowing = name of the flowing node +-- itemname = name of the new bucket item (or nil if liquid is not takeable) +-- inventory_image = texture of the new bucket item (ignored if itemname == nil) +-- name = text description of the bucket item +-- groups = (optional) groups of the bucket item, for example {water_bucket = 1} +-- force_renew = (optional) bool. Force the liquid source to renew if it has a +-- source neighbour, even if defined as 'liquid_renewable = false'. +-- Needed to avoid creating holes in sloping rivers. +-- This function can be called from any mod (that depends on bucket). +function bucket.register_liquid(source, flowing, itemname, inventory_image, name, + groups, force_renew) + bucket.liquids[source] = { + source = source, + flowing = flowing, + itemname = itemname, + force_renew = force_renew, + } + bucket.liquids[flowing] = bucket.liquids[source] + + if itemname ~= nil then + -- Create an image using a color + if inventory_image:match("^#") then + inventory_image = "bucket.png^(bucket_mask.png^[multiply:".. inventory_image ..")" + end + + minetest.register_craftitem(itemname, { + description = name, + inventory_image = inventory_image, + stack_max = 1, + liquids_pointable = true, + groups = groups, + + on_place = function(itemstack, user, pointed_thing) + -- Must be pointing to node + if pointed_thing.type ~= "node" then + return + end + + local node = minetest.get_node_or_nil(pointed_thing.under) + local ndef = node and minetest.registered_nodes[node.name] + + -- Call on_rightclick if the pointed node defines it + if ndef and ndef.on_rightclick and + not (user and user:is_player() and + user:get_player_control().sneak) then + return ndef.on_rightclick( + pointed_thing.under, + node, user, + itemstack) + end + + local lpos + + -- Check if pointing to a buildable node + if ndef and ndef.buildable_to then + -- buildable; replace the node + lpos = pointed_thing.under + else + -- not buildable to; place the liquid above + -- check if the node above can be replaced + + lpos = pointed_thing.above + node = minetest.get_node_or_nil(lpos) + local above_ndef = node and minetest.registered_nodes[node.name] + + if not above_ndef or not above_ndef.buildable_to then + -- do not remove the bucket with the liquid + return itemstack + end + end + + if check_protection(lpos, user + and user:get_player_name() + or "", "place "..source) then + return + end + + -- Fill any fluid buffers if present + local place = true + if ndef.fluid_buffers then + local buffers = fluid_lib.get_node_buffers(lpos) + for buffer in pairs(buffers) do + if fluid_lib.can_insert_into_buffer(lpos, buffer, source, 1000) == 1000 then + fluid_lib.insert_into_buffer(lpos, buffer, source, 1000) + place = false + break + end + end + end + + if place then + minetest.set_node(lpos, {name = source}) + end + + return ItemStack("bucket:bucket_empty") + end + }) + end +end + +minetest.register_craftitem("bucket:bucket_empty", { + description = "Empty Bucket", + inventory_image = "bucket.png", + stack_max = 99, + liquids_pointable = true, + groups = {bucket_empty = 1}, + on_use = function(itemstack, user, pointed_thing) + if pointed_thing.type == "object" then + pointed_thing.ref:punch(user, 1.0, { full_punch_interval=1.0 }, nil) + return user:get_wielded_item() + elseif pointed_thing.type ~= "node" then + -- do nothing if it's neither object nor node + return + end + -- Check if pointing to a liquid source + local node = minetest.get_node(pointed_thing.under) + local liquiddef = bucket.liquids[node.name] + local item_count = user:get_wielded_item():get_count() + + if liquiddef ~= nil + and liquiddef.itemname ~= nil + and node.name == liquiddef.source then + if check_protection(pointed_thing.under, + user:get_player_name(), + "take ".. node.name) then + return + end + + -- default set to return filled bucket + local giving_back = liquiddef.itemname + + -- check if holding more than 1 empty bucket + if item_count > 1 then + + -- if space in inventory add filled bucked, otherwise drop as item + local inv = user:get_inventory() + if inv:room_for_item("main", {name=liquiddef.itemname}) then + inv:add_item("main", liquiddef.itemname) + else + local pos = user:getpos() + pos.y = math.floor(pos.y + 0.5) + minetest.add_item(pos, liquiddef.itemname) + end + + -- set to return empty buckets minus 1 + giving_back = "bucket:bucket_empty "..tostring(item_count-1) + + end + + -- force_renew requires a source neighbour + local source_neighbor = false + if liquiddef.force_renew then + source_neighbor = + minetest.find_node_near(pointed_thing.under, 1, liquiddef.source) + end + if not (source_neighbor and liquiddef.force_renew) then + minetest.add_node(pointed_thing.under, {name = "air"}) + end + + return ItemStack(giving_back) + else + -- non-liquid nodes will have their on_punch triggered + local node_def = minetest.registered_nodes[node.name] + if node_def then + node_def.on_punch(pointed_thing.under, node, user, pointed_thing) + end + return user:get_wielded_item() + end + end, + on_place = function(itemstack, user, pointed_thing) + -- Must be pointing to node + if pointed_thing.type ~= "node" then + return + end + + local lpos = pointed_thing.under + local node = minetest.get_node_or_nil(lpos) + local ndef = node and minetest.registered_nodes[node.name] + + -- Call on_rightclick if the pointed node defines it + if ndef and ndef.on_rightclick and + not (user and user:is_player() and + user:get_player_control().sneak) then + return ndef.on_rightclick( + lpos, + node, user, + itemstack) + end + + if check_protection(lpos, user + and user:get_player_name() + or "", "take "..source) then + return + end + + itemstack = ItemStack("bucket:bucket_empty") + + -- Remove fluid from buffers if present + if ndef.fluid_buffers then + local buffers = fluid_lib.get_node_buffers(lpos) + for buffer in pairs(buffers) do + if fluid_lib.can_take_from_buffer(lpos, buffer, 1000) == 1000 then + local fluid = fluid_lib.take_from_buffer(lpos, buffer, 1000) + if bucket.liquids[fluid] then + itemstack = ItemStack(bucket.liquids[fluid].itemname) + end + break + end + end + end + + return itemstack + end +}) + +bucket.register_liquid( + "default:water_source", + "default:water_flowing", + "bucket:bucket_water", + "bucket_water.png", + "Water Bucket", + {water_bucket = 1} +) + +-- River water source is 'liquid_renewable = false' to avoid horizontal spread +-- of water sources in sloping rivers that can cause water to overflow +-- riverbanks and cause floods. +-- River water source is instead made renewable by the 'force renew' option +-- used here. + +bucket.register_liquid( + "default:river_water_source", + "default:river_water_flowing", + "bucket:bucket_river_water", + "bucket_river_water.png", + "River Water Bucket", + {water_bucket = 1}, + true +) + +bucket.register_liquid( + "default:lava_source", + "default:lava_flowing", + "bucket:bucket_lava", + "bucket_lava.png", + "Lava Bucket" +) + +minetest.register_craft({ + type = "fuel", + recipe = "bucket:bucket_lava", + burntime = 60, + replacements = {{"bucket:bucket_lava", "bucket:bucket_empty"}}, +}) + diff --git a/bucket/license.txt b/bucket/license.txt new file mode 100644 index 0000000..a5156ae --- /dev/null +++ b/bucket/license.txt @@ -0,0 +1,51 @@ +License of source code +---------------------- + +GNU Lesser General Public License, version 2.1 +Copyright (C) 2011-2016 Kahrl +Copyright (C) 2011-2016 celeron55, Perttu Ahola +Copyright (C) 2011-2016 Various Minetest developers and contributors + +This program is free software; you can redistribute it and/or modify it under the terms +of the GNU Lesser General Public License as published by the Free Software Foundation; +either version 2.1 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +See the GNU Lesser General Public License for more details: +https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html + + +Licenses of media (textures) +---------------------------- + +Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0) +Copyright (C) 2015-2016 ElementW + +You are free to: +Share — copy and redistribute the material in any medium or format. +Adapt — remix, transform, and build upon the material for any purpose, even commercially. +The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and +indicate if changes were made. You may do so in any reasonable manner, but not in any way +that suggests the licensor endorses you or your use. + +ShareAlike — If you remix, transform, or build upon the material, you must distribute +your contributions under the same license as the original. + +No additional restrictions — You may not apply legal terms or technological measures that +legally restrict others from doing anything the license permits. + +Notices: + +You do not have to comply with the license for elements of the material in the public +domain or where your use is permitted by an applicable exception or limitation. +No warranties are given. The license may not give you all of the permissions necessary +for your intended use. For example, other rights such as publicity, privacy, or moral +rights may limit how you use the material. + +For more details: +http://creativecommons.org/licenses/by-sa/3.0/ diff --git a/bucket/mod.conf b/bucket/mod.conf new file mode 100644 index 0000000..1a24e1f --- /dev/null +++ b/bucket/mod.conf @@ -0,0 +1,2 @@ +name = bucket +depends = default,fluid_lib diff --git a/bucket/textures/bucket.png b/bucket/textures/bucket.png new file mode 100755 index 0000000..17b0c49 Binary files /dev/null and b/bucket/textures/bucket.png differ diff --git a/bucket/textures/bucket_lava.png b/bucket/textures/bucket_lava.png new file mode 100644 index 0000000..ac6108d Binary files /dev/null and b/bucket/textures/bucket_lava.png differ diff --git a/bucket/textures/bucket_mask.png b/bucket/textures/bucket_mask.png new file mode 100644 index 0000000..d913b21 Binary files /dev/null and b/bucket/textures/bucket_mask.png differ diff --git a/bucket/textures/bucket_river_water.png b/bucket/textures/bucket_river_water.png new file mode 100644 index 0000000..d4648bb Binary files /dev/null and b/bucket/textures/bucket_river_water.png differ diff --git a/bucket/textures/bucket_water.png b/bucket/textures/bucket_water.png new file mode 100644 index 0000000..5af836b Binary files /dev/null and b/bucket/textures/bucket_water.png differ diff --git a/fluid_lib/buffer.lua b/fluid_lib/buffer.lua new file mode 100644 index 0000000..92822f7 --- /dev/null +++ b/fluid_lib/buffer.lua @@ -0,0 +1,135 @@ +-- Fluid buffer support functions. + +local function node_data(pos) + local node = minetest.get_node(pos) + local nodedef = minetest.registered_nodes[node.name] + return node, nodedef +end + +function fluid_lib.get_node_buffers(pos) + local node, nodedef = node_data(pos) + if not nodedef['fluid_buffers'] then + return nil + end + + return nodedef['fluid_buffers'] +end + +function fluid_lib.get_buffer_data(pos, buffer) + local node, nodedef = node_data(pos) + local buffers = fluid_lib.get_node_buffers(pos) + + if not buffers[buffer] then + return nil + end + + local meta = minetest.get_meta(pos) + local fluid = meta:get_string(buffer .. "_fluid") + local amount = meta:get_int(buffer .. "_fluid_storage") + local capacity = buffers[buffer].capacity + local accepts = buffers[buffer].accepts + local drainable = buffers[buffer].drainable or true + + return { + fluid = fluid, + amount = amount, + accepts = accepts, + capacity = capacity, + drainable = drainable, + } +end + +function fluid_lib.buffer_accepts_fluid(pos, buffer, fluid) + local bfdata = fluid_lib.get_buffer_data(pos, buffer) + if not bfdata then return false end + + if bfdata.accepts == true or bfdata.accepts == fluid then + return true + end + + if bfdata.fluid ~= "" and bfdata.fluid ~= fluid then + return false + end + + if type(bfdata.accepts) ~= "table" then + bfdata.accepts = { bfdata.accepts } + end + + for _,pf in pairs(bfdata.accepts) do + if pf == fluid then + return true + elseif pf:match("^group") and ele.helpers.get_item_group(fluid, pf:gsub("group:", "")) then + return true + end + end + + return false +end + +function fluid_lib.can_insert_into_buffer(pos, buffer, fluid, count) + local bfdata = fluid_lib.get_buffer_data(pos, buffer) + if not bfdata then return 0 end + if bfdata.fluid ~= fluid and bfdata.fluid ~= "" then return 0 end + + local can_put = 0 + if bfdata.amount + count > bfdata.capacity then + can_put = bfdata.capacity - bfdata.amount + else + can_put = count + end + + return can_put +end + +function fluid_lib.insert_into_buffer(pos, buffer, fluid, count) + local bfdata = fluid_lib.get_buffer_data(pos, buffer) + if not bfdata then return nil end + if bfdata.fluid ~= fluid and bfdata.fluid ~= "" then return nil end + + local can_put = fluid_lib.can_insert_into_buffer(pos, buffer, fluid, count) + + if can_put == 0 then return count end + + local meta = minetest.get_meta(pos) + meta:set_int(buffer .. "_fluid_storage", bfdata.amount + can_put) + meta:set_string(buffer .. "_fluid", fluid) + + return 0 +end + +function fluid_lib.can_take_from_buffer(pos, buffer, count) + local bfdata = fluid_lib.get_buffer_data(pos, buffer) + if not bfdata or not bfdata.drainable then return 0 end + + local amount = bfdata.amount + local take_count = 0 + + if amount < count then + take_count = amount + else + take_count = count + end + + return take_count +end + +function fluid_lib.take_from_buffer(pos, buffer, count) + local bfdata = fluid_lib.get_buffer_data(pos, buffer) + if not bfdata then return nil end + + local fluid = bfdata.fluid + local amount = bfdata.amount + + local take_count = fluid_lib.can_take_from_buffer(pos, buffer, count) + + local new_storage = amount - take_count + if new_storage == 0 then + fluid = "" + end + + local meta = minetest.get_meta(pos) + meta:set_int(buffer .. "_fluid_storage", new_storage) + meta:set_string(buffer .. "_fluid", fluid) + + return bfdata.fluid, take_count +end diff --git a/fluid_lib/init.lua b/fluid_lib/init.lua new file mode 100644 index 0000000..3fb27bb --- /dev/null +++ b/fluid_lib/init.lua @@ -0,0 +1,12 @@ +-- Universal Fluid API implementation +-- Copyright (c) 2018 Evert "Diamond" Prants + +local modpath = minetest.get_modpath(minetest.get_current_modname()) + +fluid_lib = rawget(_G, "fluid_lib") or {} +fluid_lib.modpath = modpath + +fluid_lib.unit = "mB" +fluid_lib.unit_description = "milli-bucket" + +dofile(modpath.."/buffer.lua") diff --git a/fluid_lib/mod.conf b/fluid_lib/mod.conf new file mode 100644 index 0000000..a8af194 --- /dev/null +++ b/fluid_lib/mod.conf @@ -0,0 +1 @@ +name = fluid_lib diff --git a/modpack.txt b/modpack.txt new file mode 100644 index 0000000..e69de29