Pumpkin/crates/pumpkin/src/block/blocks/sponge.rs
2026-08-26 14:18:00 +02:00

145 lines
4.7 KiB
Rust

use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::vector3::Vector3;
use std::collections::{HashSet, VecDeque};
use std::sync::Arc;
use crate::block::{BlockBehaviour, OnNeighborUpdateArgs, PlacedArgs};
use pumpkin_data::dimension::Dimension;
use pumpkin_data::particle::Particle;
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{Block, BlockStateId};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::world::BlockFlags;
#[pumpkin_block("minecraft:sponge")]
pub struct SpongeBlock;
impl SpongeBlock {
pub fn absorb_water(world: &Arc<crate::world::World>, position: &BlockPos) -> bool {
let mut water_blocks = Vec::new();
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
// Start from the sponge position
queue.push_back(*position);
visited.insert(*position);
while let Some(current_pos) = queue.pop_front() {
for direction in &[
(1, 0, 0),
(-1, 0, 0),
(0, 1, 0),
(0, -1, 0),
(0, 0, 1),
(0, 0, -1),
] {
let next_pos = BlockPos::new(
current_pos.0.x + direction.0,
current_pos.0.y + direction.1,
current_pos.0.z + direction.2,
);
if visited.contains(&next_pos) {
continue;
}
let taxicab_dist = (next_pos.0.x - position.0.x).abs()
+ (next_pos.0.y - position.0.y).abs()
+ (next_pos.0.z - position.0.z).abs();
// Wiki standard: distance 7, max 118 blocks
if taxicab_dist > 7 || water_blocks.len() >= 118 {
continue;
}
visited.insert(next_pos);
let (block, _state) = world.get_block_and_state(&next_pos);
// Only add to queue if it's water.
// This prevents "jumping" through air or solid blocks.
if block.id == Block::WATER.id {
water_blocks.push(next_pos);
queue.push_back(next_pos);
}
}
}
if water_blocks.is_empty() {
false
} else {
let mut event =
crate::plugin::api::events::block::sponge_absorb::SpongeAbsorbEvent::new(*position);
if let Some(server) = world.server.upgrade() {
server.plugin_manager.fire_blocking(&server, &mut event);
}
if event.cancelled {
return false;
}
for water_pos in &water_blocks {
world.set_block_state(water_pos, BlockStateId::AIR, BlockFlags::NOTIFY_ALL);
}
world.set_block_state(
position,
Block::WET_SPONGE.default_state.id,
BlockFlags::NOTIFY_ALL,
);
world.play_block_sound(Sound::BlockSpongeAbsorb, SoundCategory::Blocks, *position);
true
}
}
}
impl BlockBehaviour for SpongeBlock {
fn placed(&self, args: PlacedArgs<'_>) {
// Attempt to absorb water on placement
Self::absorb_water(args.world, args.position);
}
fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
// If a neighboring block changed and it's water, attempt to absorb.
if args.source_block.id == Block::WATER.id {
Self::absorb_water(args.world, args.position);
}
}
}
#[pumpkin_block("minecraft:wet_sponge")]
pub struct WetSpongeBlock;
impl BlockBehaviour for WetSpongeBlock {
fn placed(&self, args: PlacedArgs<'_>) {
{
// Check if placed in Nether, if so, dry out
if args.world.dimension == Dimension::THE_NETHER {
args.world.set_block_state(
args.position,
Block::SPONGE.default_state.id,
BlockFlags::NOTIFY_ALL,
);
// Play dry sound and spawn smoke particles
args.world.play_block_sound(
Sound::BlockWetSpongeDries,
SoundCategory::Blocks,
*args.position,
);
args.world.spawn_particle(
Vector3::new(
args.position.0.x as f64 + 0.5,
args.position.0.y as f64 + 1.0,
args.position.0.z as f64 + 0.5,
),
Vector3::new(0.25, 0.0, 0.25),
0.01,
16,
Particle::Cloud,
);
}
}
}
}