From 2c4f592b226ebedd6452513264a0586279be6a1b Mon Sep 17 00:00:00 2001 From: DonBatman Date: Sun, 4 Jan 2026 11:32:22 -0800 Subject: [PATCH] Complete rewrite --- Readme.md | 60 +++++- description.txt | 1 - init.lua | 373 ++++++++++++++++++++----------------- mod.conf | 4 +- textures/mysponge_drop.png | Bin 0 -> 216 bytes 5 files changed, 261 insertions(+), 177 deletions(-) delete mode 100644 description.txt create mode 100644 textures/mysponge_drop.png diff --git a/Readme.md b/Readme.md index 29e917e..2afd2c0 100644 --- a/Readme.md +++ b/Readme.md @@ -1,13 +1,59 @@ -Mysponge +Advanced Sponges for Luanti -Soak up water with this sponge. +A focused liquid management mod that introduces a high-utility sponge for clearing water, featuring immersive visual effects and unique environmental interactions. -Put the wet sponge in the furnace and get water. +Features -Leave the sponge sit out and it will dry. +Core Utility: A single, reliable sponge type designed specifically for efficient water management. +Dynamic States: Transitions between Dry, Moist, and Saturated states based on usage. -Code Licence - MIT -Media Licence - CC0 +Atmospheric Effects: -Forum - https://forum.minetest.net/viewtopic.php?f=9&t=11539 +Dripping: Saturated sponges placed on ceilings or walls will drip water particles. + +Steam: Smelting sponges in a furnace produces steam particles. + +Advanced Mechanics: + +Leaf Drying: Punching leaf nodes with a dry sponge instantly dries them out, perfect for clearing dense foliage. + +Manual Squeezing: Shift-click a saturated sponge with an empty bucket to reclaim water manually. + +Liquid Blocking: Temporarily places "air" nodes to prevent immediate liquid backflow while clearing. + +The Sponge + +The primary tool for clearing flood zones and managing water levels in your builds. + +Absorption Radius: 4 nodes. + +Crafting: Uses standard wool and basic materials for accessibility. + +Usage Guide + +Clearing Water + +Place a Dry Sponge near or inside water. It will instantly absorb all liquid within its 4-node radius and transition to a Saturated state. + +Drying the Sponge + +Saturated sponges must be dried before they can be used again: + +Smelting: Place a Saturated Sponge in a furnace. This produces a Dry Sponge. + +Squeezing: Hold an Empty Bucket and Shift + Right-Click a placed Saturated Sponge to empty it instantly and reclaim the water. + +Environmental Interaction + +Forestry: Use a Dry Sponge to "punch" leaf nodes. The leaves will turn into "Dry Leaves," allowing for quick clearing of jungles or forests. + +Decor: Features a unique nodebox design that allows it to look like an industrial filter or a heavy-duty cleaning tool when placed. + +Technical Details + +Dependencies: default, wool + +Engine Version: Compatible with Luanti 5.0.0+ + +Performance: Optimized node manipulation logic to ensure server stability during water clearing. diff --git a/description.txt b/description.txt deleted file mode 100644 index d087aab..0000000 --- a/description.txt +++ /dev/null @@ -1 +0,0 @@ -Soak up water with a sponge. diff --git a/init.lua b/init.lua index bfaf7a2..24aab9d 100644 --- a/init.lua +++ b/init.lua @@ -1,199 +1,238 @@ -local mysponge_table = { -- item, ,desc, image, scale, nodegroup, bright -{"sponge_dry", "Dry Sponge", "mysponge_sponge.png", "0.5", {dig_immediate=1, oddly_breakable_by_hand=1, flammable=1}}, -{"sponge_moist", "Moist Sponge", "mysponge_sponge2.png", "0.7", {dig_immediate=1, oddly_breakable_by_hand=1, not_in_creative_inventory=1}}, -{"sponge_wet", "Wet Sponge", "mysponge_sponge3.png", "1", {dig_immediate=1, oddly_breakable_by_hand=1, not_in_creative_inventory=1 }}, -{"sponge_soaked","Soaked Sponge", "mysponge_sponge3.png", "1", {dig_immediate=1, oddly_breakable_by_hand=1, not_in_creative_inventory=1}}, +local modname = minetest.get_current_modname() + +local mysponge_table = { + {"sponge_dry", "Dry Sponge", "mysponge_sponge.png", 0.5, {dig_immediate=2, flammable=1}, 3}, + {"sponge_moist", "Moist Sponge", "mysponge_sponge2.png", 0.7, {dig_immediate=2, not_in_creative_inventory=1}, 2}, + {"sponge_wet", "Wet Sponge", "mysponge_sponge3.png", 0.85, {dig_immediate=2, not_in_creative_inventory=1}, 1}, + {"sponge_soaked", "Soaked Sponge", "mysponge_sponge3.png", 1.0, {dig_immediate=2, not_in_creative_inventory=1}, 0}, } -for i in ipairs(mysponge_table) do - local item = mysponge_table[i][1] - local desc = mysponge_table[i][2] - local image = mysponge_table[i][3] - local scale = mysponge_table[i][4] - local nodegroup = mysponge_table[i][5] +local function absorb_water(pos, current_item) + local radius = 0 + for _, data in ipairs(mysponge_table) do + if data[1] == current_item then + radius = data[6] + break + end + end -minetest.register_node("mysponge:"..item,{ - description = desc, - tiles = {image}, - drawtype = "nodebox", - visual_scale = scale, - paramtype = "light", - groups = nodegroup, - node_box = { - type = "fixed", - fixed = { - {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, - } + if radius <= 0 and current_item ~= "sponge_soaked" then radius = 0 end + if current_item == "sponge_soaked" then return false end + + local adjacent_min = {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1} + local adjacent_max = {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1} + local trigger_nodes = minetest.find_nodes_in_area(adjacent_min, adjacent_max, {"group:water"}) + + if #trigger_nodes > 0 then + local soak_min = {x = pos.x - radius, y = pos.y - radius, z = pos.z - radius} + local soak_max = {x = pos.x + radius, y = pos.y + radius, z = pos.z + radius} + local all_water = minetest.find_nodes_in_area(soak_min, soak_max, {"group:water"}) + + for _, wpos in ipairs(all_water) do + minetest.set_node(wpos, {name = "mysponge:air1"}) + end + + minetest.set_node(pos, {name = "mysponge:sponge_soaked"}) + minetest.sound_play("default_water_footstep", {pos = pos, gain = 0.5}) + return true + end + return false +end + +for _, data in ipairs(mysponge_table) do + local item, desc, image, scale, nodegroup = unpack(data) + local half_width = scale / 2 + local height_top = -0.5 + scale + + minetest.register_node(modname .. ":" .. item, { + description = desc, + tiles = {image}, + drawtype = "nodebox", + paramtype = "light", + groups = nodegroup, + node_box = { + type = "fixed", + fixed = {-half_width, -0.5, -half_width, half_width, height_top, half_width}, }, - selection_box = { - type = "fixed", - fixed = { - {-0.25, -0.25, -0.25, 0.25, 0.25, 0.25}, - } - } -}) + selection_box = { + type = "fixed", + fixed = {-half_width, -0.5, -half_width, half_width, height_top, half_width}, + }, + after_place_node = function(pos, placer, itemstack, pointed_thing) + absorb_water(pos, item) + end, + on_rightclick = function(pos, node, clicker, itemstack, pointed_thing) + if itemstack:get_name() == "bucket:bucket_empty" and node.name ~= modname .. ":sponge_dry" then + local inv = clicker:get_inventory() + local leftover = inv:add_item("main", "bucket:bucket_water") + + if not leftover:is_empty() then + minetest.add_item(clicker:get_pos(), leftover) + end + + itemstack:take_item() + + minetest.add_particlespawner({ + amount = 20, + time = 0.1, + minpos = {x=pos.x-0.2, y=pos.y, z=pos.z-0.2}, + maxpos = {x=pos.x+0.2, y=pos.y+0.2, z=pos.z+0.2}, + minvel = {x=-3, y=1, z=-3}, maxvel = {x=3, y=4, z=3}, + minacc = {x=0, y=-9.8, z=0}, maxacc = {x=0, y=-9.8, z=0}, + minexptime = 0.4, maxexptime = 0.8, + minsize = 1, maxsize = 3, + texture = "mysponge_drop.png", + }) + minetest.set_node(pos, {name = modname .. ":sponge_dry"}) + minetest.sound_play("default_water_footstep", {pos = pos, gain = 0.8}) + return itemstack + end + end, + }) end -local air_nodes = { - {"air1"}, - {"air2"}, - {"air3"}, - } -for i in ipairs(air_nodes) do - local airnode = air_nodes[i][1] -minetest.register_node("mysponge:"..airnode,{ - description = "Air", - inventory_image = "unknown_node.png", - tiles = {"mysponge_air.png"}, - drawtype = "allfaces", - walkable = false, - pointable = false, - paramtype = "light", - drop = "", - groups = {not_in_creative_inventory=1} +minetest.register_abm({ + label = "Sponge Particles", + nodenames = {modname .. ":sponge_soaked", modname .. ":sponge_wet", modname .. ":sponge_moist"}, + interval = 1.0, + chance = 1, + action = function(pos, node) + if node.name == modname .. ":sponge_soaked" or node.name == modname .. ":sponge_wet" then + local below = {x=pos.x, y=pos.y-1, z=pos.z} + local b_node = minetest.get_node(below) + local def = minetest.registered_nodes[b_node.name] + + if def and (not def.walkable or b_node.name == "air") then + minetest.add_particlespawner({ + amount = 3, + time = 1, + minpos = {x = pos.x - 0.3, y = pos.y - 0.45, z = pos.z - 0.3}, + maxpos = {x = pos.x + 0.3, y = pos.y - 0.5, z = pos.z + 0.3}, + minvel = {x = 0, y = -1, z = 0}, + maxvel = {x = 0, y = -2, z = 0}, + minacc = {x = 0, y = -9.8, z = 0}, + maxacc = {x = 0, y = -9.8, z = 0}, + minexptime = 1, maxexptime = 2, + minsize = 1.5, maxsize = 2.5, + texture = "mysponge_drop.png", + vertical = true, + }) + end + elseif node.name == modname .. ":sponge_moist" then + minetest.add_particlespawner({ + amount = 2, + time = 1, + minpos = {x = pos.x - 0.2, y = pos.y + 0.1, z = pos.z - 0.2}, + maxpos = {x = pos.x + 0.2, y = pos.y + 0.4, z = pos.z + 0.2}, + minvel = {x = -0.1, y = 0.1, z = -0.1}, + maxvel = {x = 0.1, y = 0.3, z = 0.1}, + minexptime = 1.5, maxexptime = 2.5, + minsize = 1, maxsize = 2, + texture = "mysponge_drop.png^[opacity:80", + }) + end + end, }) + +local air_nodes = {"air1", "air2", "air3"} +for i, airnode in ipairs(air_nodes) do + local next_node = air_nodes[i+1] and (modname .. ":" .. air_nodes[i+1]) or "air" + minetest.register_node(modname .. ":" .. airnode, { + description = "Air", + drawtype = "airlike", + walkable = false, + pointable = false, + buildable_to = true, + paramtype = "light", + sunlight_propagates = true, + groups = {not_in_creative_inventory = 1}, + on_construct = function(pos) + minetest.get_node_timer(pos):start(0.5) + end, + on_timer = function(pos) + minetest.set_node(pos, {name = next_node}) + end, + }) end - -minetest.register_node("mysponge:dry_leaves",{ +minetest.register_node(modname .. ":dry_leaves", { description = "Dry Leaves", tiles = {"mysponge_dry_leaves.png"}, drawtype = "allfaces_optional", paramtype = "light", - groups = {dig_immediate=1, oddly_breakable_by_hand=1} + groups = {dig_immediate = 2, oddly_breakable_by_hand = 1, leaves = 1}, }) - -------------------------------------------------------------------------- ---ABMs -------------------------------------------------------------------------- ---Set Air - ---Air 1 minetest.register_abm({ - nodenames = {"group:water"}, - neighbors = {"mysponge:sponge_dry"}, - interval = 0.2, - chance = 1, - action = function(pos) - minetest.set_node(pos, {name="mysponge:air1"}) - end + label = "Sponge Leaf Drying", + nodenames = {"group:leaves"}, + neighbors = {modname .. ":sponge_dry"}, + interval = 5.0, + chance = 2, + action = function(pos, node) + local below = {x=pos.x, y=pos.y-1, z=pos.z} + local b_node = minetest.get_node(below) + + if b_node.name == modname .. ":sponge_dry" and node.name ~= modname .. ":dry_leaves" then + minetest.set_node(pos, {name = modname .. ":dry_leaves"}) + minetest.set_node(below, {name = modname .. ":sponge_moist"}) + minetest.sound_play("default_dry_grass_footstep", {pos = pos, gain = 0.5}) + end + end, }) ---Air 2 minetest.register_abm({ - nodenames = {"group:water"}, - neighbors = {"mysponge:air1"}, - interval = 0.2, - chance = 1, - action = function(pos) - minetest.set_node(pos, {name="mysponge:air2"}) - end -}) - ---Air 3 -minetest.register_abm({ - nodenames = {"group:water"}, - neighbors = {"mysponge:air2"}, - interval = 0.2, - chance = 1, - action = function(pos) - minetest.set_node(pos, {name="mysponge:air3"}) - end -}) - -------------------------------------------------------------------------- ---Set default Air -------------------------------------------------------------------------- - -minetest.register_abm({ - nodenames = {"mysponge:air1","mysponge:air2","mysponge:air3"}, - interval = 4, - chance = 1, - action = function(pos) - minetest.set_node(pos, {name="air"}) - end -}) - -------------------------------------------------------------------------- ---Set Sponge -------------------------------------------------------------------------- - -minetest.register_abm({ - nodenames = {"mysponge:sponge_dry"}, + label = "Sponge Water Absorption", + nodenames = {modname .. ":sponge_dry", modname .. ":sponge_moist", modname .. ":sponge_wet"}, neighbors = {"group:water"}, - interval = 1, + interval = 1.0, chance = 1, - action = function(pos) - minetest.set_node(pos, {name="mysponge:sponge_soaked"}) - end - -}) ------------------------------------------------------------------------------------------------- ---Shrink Sponge in Air ------------------------------------------------------------------------------------------------- -minetest.register_abm({ - nodenames = {"mysponge:sponge_soaked"}, - neighbors = {"air"}, - interval = 60, - chance = 1, - action = function(pos) - minetest.set_node(pos, {name="mysponge:sponge_wet"}) - end, -}) -minetest.register_abm({ - nodenames = {"mysponge:sponge_wet"}, - neighbors = {"air"}, - interval = 60, - chance = 1, - action = function(pos) - minetest.set_node(pos, {name="mysponge:sponge_moist"}) - end, -}) -minetest.register_abm({ - nodenames = {"mysponge:sponge_moist"}, - neighbors = {"air"}, - interval = 60, - chance = 1, - action = function(pos) - minetest.set_node(pos, {name="mysponge:sponge_dry"}) + action = function(pos, node) + local item_name = node.name:split(":")[2] + absorb_water(pos, item_name) end, }) +local drying_chain = { + [modname .. ":sponge_soaked"] = modname .. ":sponge_wet", + [modname .. ":sponge_wet"] = modname .. ":sponge_moist", + [modname .. ":sponge_moist"] = modname .. ":sponge_dry", +} -------------------------------------------------------------------------------------------------- ---Craft +minetest.register_abm({ + label = "Sponge Drying", + nodenames = {modname .. ":sponge_soaked", modname .. ":sponge_wet", modname .. ":sponge_moist"}, + neighbors = {"air"}, + interval = 45, + chance = 1, + action = function(pos, node) + local next_stage = drying_chain[node.name] + if next_stage then + minetest.set_node(pos, {name = next_stage}) + end + end, +}) -minetest.register_craft({ +local function register_sponge_drying(input_item, water_yield) + minetest.register_craft({ type = "cooking", - output = "default:water_source 3", - recipe = "mysponge:sponge_soaked", + output = "default:water_source " .. tostring(water_yield), + recipe = input_item, cooktime = 5, - replacements = {{"mysponge:sponge_soaked", "mysponge:sponge_dry"}}, - }) -minetest.register_craft({ - type = "cooking", - output = "default:water_source 2", - recipe = "mysponge:sponge_wet", - cooktime = 5, - replacements = {{"mysponge:sponge_wet", "mysponge:sponge_dry"}}, - }) -minetest.register_craft({ - type = "cooking", - output = "mysponge:dry_leaves", - recipe = "group:leaves", - cooktime = 3, - }) -minetest.register_craft({ - output = "mysponge:sponge_dry 1", - recipe = { - {"default:mese_crystal_fragment","mysponge:dry_leaves","default:mese_crystal_fragment"}, - {"mysponge:dry_leaves","default:mese_crystal_fragment","mysponge:dry_leaves"}, - {"default:mese_crystal_fragment","mysponge:dry_leaves","default:mese_crystal_fragment"}, - }, - cooktime = 3, + replacements = {{input_item, modname .. ":sponge_dry"}}, }) +end +register_sponge_drying(modname .. ":sponge_soaked", 3) +register_sponge_drying(modname .. ":sponge_wet", 2) +register_sponge_drying(modname .. ":sponge_moist", 1) - +minetest.register_craft({ + output = modname .. ":sponge_dry 1", + recipe = { + {"farming:string", "wool:yellow", "farming:string"}, + {"wool:yellow", "default:mese_crystal_fragment", "wool:yellow"}, + {"farming:string", "wool:yellow", "farming:string"}, + }, +}) diff --git a/mod.conf b/mod.conf index 5a0a8a6..ba9c036 100644 --- a/mod.conf +++ b/mod.conf @@ -1,5 +1,5 @@ name = mysponge description = A sponge that soaks up water -depends = default -author = Don Michaud +depends = default, wool +author = Don title = My Sponge diff --git a/textures/mysponge_drop.png b/textures/mysponge_drop.png new file mode 100644 index 0000000000000000000000000000000000000000..7e9d0ee84759f0f60f133493ee58f4acfff1a0b4 GIT binary patch literal 216 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqEa{HEjtmSN`?>!lvI6;x#X;^) zj5kl})B`yR$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@pr;JNj1^1m%YcIHC7!;n?5`MEgw!MpKUL;22WQ%mvv4FO#o23 BH~#