From 3fcbf9ba3d613f5976b449f899e0533c3a91af9b Mon Sep 17 00:00:00 2001 From: Alexander Medvedev Date: Wed, 12 Aug 2026 20:32:22 +0200 Subject: [PATCH] feat(block): implement calibrated sculk sensor resonance logic --- .../pumpkin/src/block/blocks/decorated_pot.rs | 27 ++++--- .../src/block/blocks/redstone/sculk_sensor.rs | 80 +++++++++++++++++-- 2 files changed, 90 insertions(+), 17 deletions(-) diff --git a/crates/pumpkin/src/block/blocks/decorated_pot.rs b/crates/pumpkin/src/block/blocks/decorated_pot.rs index 696e7cb53..ead347a55 100644 --- a/crates/pumpkin/src/block/blocks/decorated_pot.rs +++ b/crates/pumpkin/src/block/blocks/decorated_pot.rs @@ -1,10 +1,10 @@ use std::sync::Arc; +use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{BlockProperties, DecoratedPotLikeProperties}; use pumpkin_data::item::Item; use pumpkin_data::item_stack::ItemStack; use pumpkin_data::sound::{Sound, SoundCategory}; -use pumpkin_data::BlockStateId; use pumpkin_macros::pumpkin_block; use crate::block::entities::decorated_pot::DecoratedPotBlockEntity; @@ -20,10 +20,8 @@ pub struct DecoratedPotBlock; impl BlockBehaviour for DecoratedPotBlock { fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { Box::pin(async move { - let mut props = DecoratedPotLikeProperties::from_state_id( - args.block.default_state.id, - args.block, - ); + let mut props = + DecoratedPotLikeProperties::from_state_id(args.block.default_state.id, args.block); props.facing = args .player .living_entity @@ -60,8 +58,9 @@ impl BlockBehaviour for DecoratedPotBlock { } if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(pot_entity) = - block_entity.as_any().downcast_ref::() + && let Some(pot_entity) = block_entity + .as_any() + .downcast_ref::() { if pot_entity.try_insert_item(args.item_stack, 1).await { args.world.play_sound( @@ -97,8 +96,9 @@ impl BlockBehaviour for DecoratedPotBlock { fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(pot_entity) = - block_entity.as_any().downcast_ref::() + && let Some(pot_entity) = block_entity + .as_any() + .downcast_ref::() && let Some(contained) = pot_entity.take_item().await { args.world.drop_stack(args.position, contained).await; @@ -109,7 +109,9 @@ impl BlockBehaviour for DecoratedPotBlock { SoundCategory::Blocks, &args.position.to_f64(), ); - args.world.drop_stack(args.position, ItemStack::new(4, &Item::BRICK)).await; + args.world + .drop_stack(args.position, ItemStack::new(4, &Item::BRICK)) + .await; }) } @@ -119,8 +121,9 @@ impl BlockBehaviour for DecoratedPotBlock { ) -> BlockFuture<'a, Option> { Box::pin(async move { if let Some(block_entity) = args.world.get_block_entity(args.position) - && let Some(pot_entity) = - block_entity.as_any().downcast_ref::() + && let Some(pot_entity) = block_entity + .as_any() + .downcast_ref::() { Some(pot_entity.get_comparator_output().await) } else { diff --git a/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs b/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs index a554e2312..0a70c724c 100644 --- a/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs +++ b/crates/pumpkin/src/block/blocks/redstone/sculk_sensor.rs @@ -1,15 +1,17 @@ use std::sync::Arc; +use crate::block::entities::calibrated_sculk_sensor::CalibratedSculkSensorBlockEntity; +use crate::block::entities::sculk_sensor::SculkSensorBlockEntity; use crate::block::{ - BlockBehaviour, BlockFuture, BlockMetadata, EmitsRedstonePowerArgs, GetRedstonePowerArgs, - OnPlaceArgs, OnScheduledTickArgs, + BlockBehaviour, BlockFuture, BlockMetadata, EmitsRedstonePowerArgs, GetComparatorOutputArgs, + GetRedstonePowerArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs, }; use crate::world::World; use pumpkin_data::block_properties::{ - BlockProperties, CalibratedSculkSensorLikeProperties, SculkSensorLikeProperties, - SculkSensorPhase, + BlockProperties, CalibratedSculkSensorLikeProperties, HorizontalFacing, + SculkSensorLikeProperties, SculkSensorPhase, }; -use pumpkin_data::{Block, BlockId, BlockStateId}; +use pumpkin_data::{Block, BlockDirection, BlockId, BlockStateId}; use pumpkin_util::math::position::BlockPos; use pumpkin_world::tick::TickPriority; use pumpkin_world::world::BlockFlags; @@ -22,12 +24,27 @@ impl BlockMetadata for SculkSensorBlock { } } +const fn horizontal_facing_to_dir(facing: HorizontalFacing) -> BlockDirection { + match facing { + HorizontalFacing::North => BlockDirection::North, + HorizontalFacing::South => BlockDirection::South, + HorizontalFacing::West => BlockDirection::West, + HorizontalFacing::East => BlockDirection::East, + } +} + impl SculkSensorBlock { pub async fn trigger(world: &Arc, pos: &BlockPos, block: &Block, power: u8) { if block.id == BlockId::SCULK_SENSOR { let state = world.get_block_state(pos); let mut props = SculkSensorLikeProperties::from_state_id(state.id, block); if props.sculk_sensor_phase == SculkSensorPhase::Inactive { + if let Some(be) = world.get_block_entity(pos) + && let Some(sensor_be) = be.as_any().downcast_ref::() + { + *sensor_be.last_vibration_frequency.lock().await = power as i32; + } + props.sculk_sensor_phase = SculkSensorPhase::Active; props.power = power; world @@ -40,6 +57,28 @@ impl SculkSensorBlock { let state = world.get_block_state(pos); let mut props = CalibratedSculkSensorLikeProperties::from_state_id(state.id, block); if props.sculk_sensor_phase == SculkSensorPhase::Inactive { + let back_dir = horizontal_facing_to_dir(props.facing).opposite(); + let back_pos = pos.offset(back_dir.to_offset()); + let back_state = world.get_block_state(&back_pos); + let back_block = Block::from_state_id(back_state.id); + + let calibrated_freq = world + .block_registry + .get_weak_redstone_power(back_block, world, &back_pos, back_state, back_dir) + .await; + + if calibrated_freq > 0 && calibrated_freq != power { + return; + } + + if let Some(be) = world.get_block_entity(pos) + && let Some(cal_be) = be + .as_any() + .downcast_ref::() + { + *cal_be.last_vibration_frequency.lock().await = power as i32; + } + props.sculk_sensor_phase = SculkSensorPhase::Active; props.power = power; world @@ -66,6 +105,18 @@ impl BlockBehaviour for SculkSensorBlock { }) } + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + if args.block.id == BlockId::CALIBRATED_SCULK_SENSOR { + let entity = CalibratedSculkSensorBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); + } else if args.block.id == BlockId::SCULK_SENSOR { + let entity = SculkSensorBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); + } + }) + } + fn emits_redstone_power<'a>( &'a self, _args: EmitsRedstonePowerArgs<'a>, @@ -99,6 +150,25 @@ impl BlockBehaviour for SculkSensorBlock { }) } + fn get_comparator_output<'a>( + &'a self, + args: GetComparatorOutputArgs<'a>, + ) -> BlockFuture<'a, Option> { + Box::pin(async move { + let be = args.world.get_block_entity(args.position)?; + if let Some(sensor_be) = be.as_any().downcast_ref::() { + return Some(*sensor_be.last_vibration_frequency.lock().await as u8); + } + if let Some(cal_be) = be + .as_any() + .downcast_ref::() + { + return Some(*cal_be.last_vibration_frequency.lock().await as u8); + } + None + }) + } + fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { let state = args.world.get_block_state(args.position);