From ed7e26fb8aa58edbc38708ac44c68a19f0df48d6 Mon Sep 17 00:00:00 2001 From: snoutie Date: Sat, 11 Jan 2025 22:57:44 +0100 Subject: [PATCH] Add Bucket Support --- api.lua | 19 ++- bucket.lua | 281 +++++++++++++++++++++++++++++++++++++++++++++ game/default.lua | 15 +++ game/voxelibre.lua | 15 +++ init.lua | 20 +--- internal.lua | 6 + mod.conf | 2 +- physics.lua | 2 +- 8 files changed, 340 insertions(+), 20 deletions(-) create mode 100644 bucket.lua create mode 100644 game/default.lua create mode 100644 game/voxelibre.lua diff --git a/api.lua b/api.lua index b1ed4a7..3b7bb82 100644 --- a/api.lua +++ b/api.lua @@ -15,6 +15,7 @@ local modpath = core.get_modpath(core.get_current_modname()) local internal = dofile(modpath .. "/internal.lua") +local internal_bucket = dofile(modpath .. "/bucket.lua") -- Returns liquid_id of the node or -- nil when the node is not a liquid @@ -64,10 +65,18 @@ function liquid_physics.set_liquid_at(pos, liquid_id, liquid_level) end local lpn = internal.new_lpn(core.hash_node_position(pos), pos) internal.set_lpn(liquid_id, lpn, liquid_level) + internal.set_node(lpn) internal.add_node_to_check(pos) return true end +-- This function will override the bucket provided to add the capapability +-- of transporting liquids of factional size +-- @param name string Name of the bucket, e.g. "bucket:bucket:water" +function liquid_physics.register_bucket(name) + internal_bucket.register_filled_bucket(name) +end + -- Executing this function will override the nodes specified. -- New liquid sources will be "liquid_physics:namespace_name_level_1" through 7 -- @param namespace string Name of mod e.g. "default" @@ -97,6 +106,7 @@ function liquid_physics.register_liquid(namespace, source_name, flowing_name) --Overwrite source source_liquid_def.liquid_range = 0 + source_liquid_def.groups.liquid_physics = 1 core.register_node(":" .. source_liquid_name, source_liquid_def) liquid_physics._liquid_ids[source_liquid_name] = id @@ -148,14 +158,17 @@ function liquid_physics.register_liquid(namespace, source_name, flowing_name) liquid_physics._liquid_ids[node_name] = id table.insert(liquids, node_name) - table.insert(liquid_physics._liquids, node_name) end table.insert(liquids, source_liquid_name) - table.insert(liquid_physics._liquids, source_liquid_name) -- Finally, stop flowing - core.override_item(flowing_liquid_name, { liquid_range = 0, liquid_renewable = false }, nil) + flowing_liquid_def.groups.liquid_physics = 1 + core.override_item(flowing_liquid_name, { + liquid_range = 0, + liquid_renewable = false, + groups = flowing_liquid_def.groups + }, nil) liquid_physics._liquid_ids[flowing_liquid_name] = id liquid_physics._registered_liquids[id] = liquids diff --git a/bucket.lua b/bucket.lua new file mode 100644 index 0000000..c6c4dfa --- /dev/null +++ b/bucket.lua @@ -0,0 +1,281 @@ +local internal_bucket = {} + +local wear_levels = {} +wear_levels[1] = 65535 +wear_levels[2] = 57337 +wear_levels[3] = 49146 +wear_levels[4] = 40955 +wear_levels[5] = 32764 +wear_levels[6] = 24573 +wear_levels[7] = 16382 +wear_levels[8] = 8191 +wear_levels[9] = 0 + +local base_mcl_buckets = core.get_modpath("mcl_buckets") +local base_default = core.get_modpath("default") + +local function check_protection(pos, name, text) + if core.is_protected(pos, name) then + core.log("action", (name ~= "" and name or "A mod") + .. " tried to " .. text + .. " at protected position " + .. core.pos_to_string(pos) + .. " with a bucket") + core.record_protection_violation(pos, name) + return true + end + return false +end + +function internal_bucket.get_wear(liquid_level) + return wear_levels[liquid_level + 1] +end + +function internal_bucket.get_liquid_level(wear) + for i = 1, 9 do + if wear_levels[i] == wear then + return i - 1 + end + end +end + +local function set_bucket_item_liquid_level(item, liquid_level) + local meta = item:get_meta() + + meta:set_string("description", + ItemStack(item:get_name()):get_description() .. " " .. liquid_level .. "/8") + + item:set_wear(internal_bucket.get_wear(liquid_level)) +end + +local function mcl_get_pointed_thing(usr) + local start = usr:get_pos() + start.y = start.y + usr:get_properties().eye_height + local look_dir = usr:get_look_dir() + local _end = vector.add(start, vector.multiply(look_dir, 5)) + + local ray = core.raycast(start, _end, false, true) + for pointed_thing in ray do + local name = core.get_node(pointed_thing.under).name + local def = core.registered_nodes[name] + if not def or def.drawtype ~= "flowingliquid" then + return pointed_thing + end + end +end + +local function get_bucket_name_empty() + if base_default then + return "bucket:bucket_empty" + elseif base_mcl_buckets then + return "mcl_buckets:bucket_empty" + end + return nil +end + +local function get_bucket_name_filled(source_name) + if base_default then + return bucket.liquids[source_name].itemname + elseif base_mcl_buckets then + return mcl_buckets.liquids[source_name].bucketname + end + return nil +end + +local function get_liquid_name(bucket_name) + if base_default then + for source, b in pairs(bucket.liquids) do + if b.itemname and b.itemname == bucket_name then + return b.source + end + end + elseif base_mcl_buckets then + return mcl_buckets.buckets[bucket_name].source_place + end + return nil +end + +local function bucket_on_use(on_use_fallback, 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 + return + end + + local liquid = liquid_physics.get_liquid_at(pointed_thing.under) + + if liquid == nil then + return on_use_fallback(itemstack, user, pointed_thing) + end + + local node = core.get_node(pointed_thing.under) + + if check_protection(pointed_thing.under, + user:get_player_name(), + "take " .. node.name) then + return + end + + local liquid_source = liquid_physics.get_liquid_node_names(liquid.liquid_id)[8] + local bucket_name = get_bucket_name_filled(liquid_source) + + if bucket_name == nil then + return on_use_fallback(itemstack, user, pointed_thing) + end + + -- Filled Bucket + local bucket_item = ItemStack(bucket_name) + set_bucket_item_liquid_level(bucket_item, liquid.liquid_level) + + local return_item = bucket_item + + local item_count = user:get_wielded_item():get_count() + + if item_count > 1 then + local inv = user:get_inventory() + if inv:room_for_item("main", { name = bucket_name }) then + inv:add_item("main", bucket_item) + else + local pos = user:getpos() + pos.y = math.floor(pos.y + 0.5) + core.add_item(pos, bucket_item) + end + + -- set to return empty buckets minus 1 + return_item = ItemStack(get_bucket_name_empty() .. tostring(item_count - 1)) + end + + liquid_physics.set_liquid_at(pointed_thing.under, 0, 0) + + return return_item +end + +local function bucket_on_place(on_place_fallback, bucket_liquid_id, source_name, itemstack, user, pointed_thing) + -- Must be pointing to node + if pointed_thing.type ~= "node" then + return + end + + local node = core.get_node(pointed_thing.under) + local node_def = core.registered_nodes[node.name] + + if not node_def then + return itemstack + end + + -- Call on_rightclick if the pointed node defines it + if node_def.on_rightclick and + not (user and user:is_player() and + user:get_player_control().sneak) then + return node_def.on_rightclick( + pointed_thing.under, + node, user, + itemstack) + end + + -- Where to place the liquid at + local place_at_pos + + if node_def.buildable_to then + place_at_pos = pointed_thing.under + else + place_at_pos = pointed_thing.above + + node = core.get_node(place_at_pos) + + local node_above_def = core.registered_nodes[node.name] + + if not node_above_def or not node_above_def.buildable_to then + return itemstack + end + end + + if check_protection(place_at_pos, user + and user:get_player_name() + or "", "place " .. source_name) then + return + end + + local liquid_level = internal_bucket.get_liquid_level(itemstack:get_wear()) + local liquid = liquid_physics.get_liquid_at(place_at_pos) + + if liquid == nil then + if liquid_physics.set_liquid_at(place_at_pos, bucket_liquid_id, liquid_level) then + return ItemStack(get_bucket_name_empty()) + end + return itemstack + end + + if liquid.liquid_id == bucket_liquid_id then + local give_amount = math.min(8 - liquid.liquid_level, liquid_level) + if liquid_physics.set_liquid_at(place_at_pos, bucket_liquid_id, liquid.liquid_level + give_amount) then + if give_amount == liquid_level then + return ItemStack(get_bucket_name_empty()) + end + set_bucket_item_liquid_level(itemstack, liquid_level - give_amount) + return itemstack + end + return itemstack + end +end + +function internal_bucket.register_empty_bucket(bucket_name) + local bucket_tool = core.registered_items[bucket_name] + + if base_default then + local on_use_fallback = bucket_tool.on_use + local on_use_wrapper = function(itemstack, user, pointed_thing) + return bucket_on_use(on_use_fallback, itemstack, user, pointed_thing) + end + core.override_item(bucket_name, { on_use = on_use_wrapper }, nil) + elseif base_mcl_buckets then + local on_use_fallback = bucket_tool.on_place + local on_use_wrapper = function(itemstack, user, pointed_thing) + local use_select_box = core.settings:get_bool("mcl_buckets_use_select_box", false) + if use_select_box == false then + -- TODO: Understand why this is nil + if user.get_pos == nil then + return itemstack + end + pointed_thing = mcl_get_pointed_thing(user) + end + return bucket_on_use(on_use_fallback, itemstack, user, pointed_thing) + end + core.override_item(bucket_name, { on_place = on_use_wrapper }, nil) + end +end + +function internal_bucket.register_filled_bucket(name) + local bucket_tool = core.registered_items[name] + + local source_name = get_liquid_name(name) + if source_name == nil then + error("Liquid Physics: Could not register bucket. Liquid for bucket " .. name .. " was not found") + end + local bucket_liquid_id = liquid_physics.get_liquid_id(source_name) + if bucket_liquid_id == nil then + error("Liquid Physics: Could not register bucket. Liquid " .. + source_name .. " was not registered with liquid physics.") + end + + if base_default then + local on_place_fallback = bucket_tool.on_place + local on_place_wrapper = function(itemstack, user, pointed_thing) + return bucket_on_place(on_place_fallback, bucket_liquid_id, source_name, itemstack, user, pointed_thing) + end + core.override_item(name, { + on_place = on_place_wrapper + }, nil) + elseif base_mcl_buckets then + local on_place_fallback = bucket_tool.on_place + local on_place_wrapper = function(itemstack, user, pointed_thing) + return bucket_on_place(on_place_fallback, bucket_liquid_id, source_name, itemstack, user, pointed_thing) + end + core.override_item(name, { + on_place = on_place_wrapper + }, nil) + end +end + +return internal_bucket diff --git a/game/default.lua b/game/default.lua new file mode 100644 index 0000000..1cdb1ff --- /dev/null +++ b/game/default.lua @@ -0,0 +1,15 @@ +local modpath = core.get_modpath(core.get_current_modname()) +local internal_bucket = dofile(modpath .. "/bucket.lua") + +internal_bucket.register_empty_bucket("bucket:bucket_empty") + +-- Settings + +if core.settings:get_bool("liquid_physics_enable_water_physics", false) then + liquid_physics.register_liquid("default", "water_source", "water_flowing") + internal_bucket.register_filled_bucket("bucket:bucket_water") +end +if core.settings:get_bool("liquid_physics_enable_lava_physics", false) then + liquid_physics.register_liquid("default", "lava_source", "lava_flowing") + internal_bucket.register_filled_bucket("bucket:bucket_lava") +end diff --git a/game/voxelibre.lua b/game/voxelibre.lua new file mode 100644 index 0000000..9f3963a --- /dev/null +++ b/game/voxelibre.lua @@ -0,0 +1,15 @@ +local modpath = core.get_modpath(core.get_current_modname()) +local internal_bucket = dofile(modpath .. "/bucket.lua") + +internal_bucket.register_empty_bucket("mcl_buckets:bucket_empty") + +-- Settings + +if core.settings:get_bool("liquid_physics_enable_water_physics", false) then + liquid_physics.register_liquid("mcl_core", "water_source", "water_flowing") + internal_bucket.register_filled_bucket("mcl_buckets:bucket_water") +end +if core.settings:get_bool("liquid_physics_enable_lava_physics", false) then + liquid_physics.register_liquid("mcl_core", "lava_source", "lava_flowing") + internal_bucket.register_filled_bucket("mcl_buckets:bucket_lava") +end diff --git a/init.lua b/init.lua index de19499..d5e2136 100644 --- a/init.lua +++ b/init.lua @@ -23,29 +23,19 @@ liquid_physics._nodes_to_check = {} liquid_physics._registered_liquids = {} -- INTERNAL USE ONLY - Stores all liquid node names and their corresponding liquid_id liquid_physics._liquid_ids = {} --- INTERNAL USE ONLY - All liquid nodes for ABM and LBM -liquid_physics._liquids = {} local internal = dofile(modpath .. "/internal.lua") + dofile(modpath .. "/api.lua") -local namespace = "" - if core.get_modpath("default") then - namespace = "default" + dofile(modpath .. "/game/default.lua") elseif core.get_modpath("mcl_core") then - namespace = "mcl_core" + dofile(modpath .. "/game/voxelibre.lua") else error("Liquid Physics only supports VoxeLibre or Minetest") end -if core.settings:get_bool("liquid_physics_enable_water_physics") then - liquid_physics.register_liquid(namespace, "water_source", "water_flowing") -end -if minetest.settings:get_bool("liquid_physics_enable_lava_physics") then - liquid_physics.register_liquid(namespace, "lava_source", "lava_flowing") -end - core.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing) if internal.get_liquid_id(core.get_node(pos).name) ~= nil then internal.add_node_to_check(pos) @@ -54,7 +44,7 @@ end) core.register_lbm { name = "liquid_physics:update", - nodenames = liquid_physics._liquids, + nodenames = "group:liquid_physics", run_at_every_load = true, action = function(pos, node, dtime_s) internal.add_node_to_check(pos) @@ -62,7 +52,7 @@ core.register_lbm { } core.register_abm({ - nodenames = liquid_physics._liquids, + nodenames = "group:liquid_physics", neighbors = { "air" }, interval = 0.2, chance = 0, diff --git a/internal.lua b/internal.lua index 42b1f4a..013ef17 100644 --- a/internal.lua +++ b/internal.lua @@ -96,6 +96,12 @@ function internal.set_lpn(liquid_id, lpn, liquid_level) lpn.liquid_id = liquid_id end +-- Sets liquid node from LPN +-- @param lpn table Liquid Physics Node +function internal.set_node(lpn) + core.set_node(lpn.pos, { name = internal.get_liquid_node_name(lpn.liquid_id, lpn.liquid_level) }) +end + -- This node will be checked in the next cycle -- @param pos table Position of the node function internal.add_node_to_check(pos) diff --git a/mod.conf b/mod.conf index 969caad..cc3a38b 100644 --- a/mod.conf +++ b/mod.conf @@ -1,6 +1,6 @@ name = liquid_physics depends = -optional_depends = default, mcl_core +optional_depends = default, mcl_core, bucket, mcl_buckets author = snoutie description = Adds physics to liquids title = Liquid Physics diff --git a/physics.lua b/physics.lua index 62d5aa3..93d70e7 100644 --- a/physics.lua +++ b/physics.lua @@ -225,7 +225,7 @@ core.register_on_mapblocks_changed(function(modified_blocks, modified_blocks_cou -- Update only changed nodes from buffer for _, lpn in pairs(b) do if lpn.init_liquid_level ~= lpn.liquid_level then - core.set_node(lpn.pos, { name = internal.get_liquid_node_name(lpn.liquid_id, lpn.liquid_level) }) + internal.set_node(lpn) end end end)