203 lines
7.0 KiB
Rust
203 lines
7.0 KiB
Rust
use crate::Arc;
|
|
use crate::block::blocks::abstract_wall_mounting::WallMountedBlock;
|
|
use crate::block::blocks::redstone::block_receives_redstone_power;
|
|
use crate::block::entities::bell::BellBlockEntity;
|
|
use crate::block::registry::BlockActionResult;
|
|
use crate::block::{
|
|
BlockBehaviour, BlockFuture, BlockHitResult, BrokenArgs, CanPlaceAtArgs, NormalUseArgs,
|
|
OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs,
|
|
};
|
|
use crate::world::World;
|
|
use pumpkin_data::block_properties::BlockProperties;
|
|
use pumpkin_data::block_properties::HorizontalFacing;
|
|
use pumpkin_data::block_properties::{AttachFace, BellAttachment, BellLikeProperties};
|
|
use pumpkin_data::sound::Sound;
|
|
use pumpkin_data::sound::SoundCategory;
|
|
use pumpkin_data::tag::Taggable;
|
|
use pumpkin_data::{Block, BlockDirection};
|
|
use pumpkin_data::{HorizontalFacingExt, tag};
|
|
use pumpkin_macros::pumpkin_block;
|
|
use pumpkin_util::math::position::BlockPos;
|
|
use pumpkin_world::BlockStateId;
|
|
use pumpkin_world::world::BlockFlags;
|
|
|
|
fn ring_bell(position: BlockPos, world: &Arc<World>, hit_direction: Option<HorizontalFacing>) {
|
|
let (block, state_id) = world.get_block_and_state_id(&position);
|
|
|
|
let props = BellLikeProperties::from_state_id(state_id, block);
|
|
let direction = hit_direction.map_or(props.facing, |direction3| direction3);
|
|
|
|
if let Some(block_entity) = world.get_block_entity(&position)
|
|
&& let Some(be) = block_entity.as_any().downcast_ref::<BellBlockEntity>()
|
|
{
|
|
be.activate(direction);
|
|
}
|
|
|
|
world.play_sound_fine(
|
|
Sound::BlockBellUse,
|
|
SoundCategory::Blocks,
|
|
&position.to_centered_f64(),
|
|
1.0,
|
|
2.0,
|
|
);
|
|
|
|
//TODO Emit game event: BLOCK_CHANGE -> Send block update Packet
|
|
}
|
|
|
|
fn is_point_on_bell(
|
|
hit: &BlockHitResult,
|
|
attachment: BellAttachment,
|
|
block_face: HorizontalFacing,
|
|
) -> bool {
|
|
if hit.face == &BlockDirection::Up || hit.face == &BlockDirection::Down {
|
|
return false;
|
|
}
|
|
if hit.cursor_pos.y <= 0.8124f32 {
|
|
match attachment {
|
|
BellAttachment::Floor => {
|
|
hit.face.to_axis() == block_face.to_block_direction().to_axis()
|
|
}
|
|
BellAttachment::SingleWall | BellAttachment::DoubleWall => {
|
|
hit.face.to_axis() != block_face.to_block_direction().to_axis()
|
|
}
|
|
BellAttachment::Ceiling => true,
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
fn is_single_wall(position: BlockPos, facing: HorizontalFacing, world: &World) -> bool {
|
|
!world
|
|
.get_block(&position.offset(facing.to_offset()))
|
|
.is_solid()
|
|
}
|
|
|
|
#[pumpkin_block("minecraft:bell")]
|
|
pub struct BellBlock;
|
|
|
|
impl WallMountedBlock for BellBlock {
|
|
fn get_direction(&self, state_id: BlockStateId, block: &Block) -> BlockDirection {
|
|
let props = BellLikeProperties::from_state_id(state_id, block);
|
|
match props.attachment {
|
|
BellAttachment::Ceiling => BlockDirection::Down,
|
|
BellAttachment::Floor => BlockDirection::Up,
|
|
BellAttachment::SingleWall | BellAttachment::DoubleWall => {
|
|
props.facing.opposite().to_block_direction()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl BlockBehaviour for BellBlock {
|
|
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
|
|
if let Some(direction) = args.direction
|
|
&& let Some(world) = args.world
|
|
{
|
|
if direction == BlockDirection::Up {
|
|
let block: &Block = world.get_block(args.position);
|
|
|
|
if block.has_tag(&tag::Block::MINECRAFT_UNSTABLE_BOTTOM_CENTER) {
|
|
false
|
|
} else {
|
|
let block_pos = args.position.offset(direction.to_offset());
|
|
let block_state = world.get_block_state(&block_pos);
|
|
block_state.is_center_solid(direction)
|
|
}
|
|
} else {
|
|
WallMountedBlock::can_place_at(self, world, args.position, direction)
|
|
}
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
|
|
Box::pin(async move {
|
|
let world: &World = args.world;
|
|
world.remove_block_entity(args.position);
|
|
})
|
|
}
|
|
|
|
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
|
Box::pin(async move {
|
|
args.world
|
|
.add_block_entity(Arc::new(BellBlockEntity::new(*args.position)));
|
|
})
|
|
}
|
|
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
|
Box::pin(async move {
|
|
let state = args.world.get_block_state(args.position);
|
|
|
|
let props = BellLikeProperties::from_state_id(state.id, args.block);
|
|
|
|
if !is_point_on_bell(args.hit, props.attachment, props.facing) {
|
|
return BlockActionResult::Pass; // Pass if Crosshair wasn't correctly positioned
|
|
}
|
|
ring_bell(
|
|
*args.position,
|
|
args.world,
|
|
args.hit.face.to_horizontal_facing(),
|
|
);
|
|
|
|
BlockActionResult::Success
|
|
})
|
|
}
|
|
|
|
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
|
|
Box::pin(async move {
|
|
let mut props = BellLikeProperties::default(args.block);
|
|
|
|
let block_face;
|
|
let facing;
|
|
(block_face, facing) =
|
|
WallMountedBlock::get_placement_face(self, args.player, args.direction);
|
|
|
|
props.facing = match block_face {
|
|
AttachFace::Floor | AttachFace::Ceiling => facing,
|
|
AttachFace::Wall => facing.opposite(),
|
|
};
|
|
|
|
props.attachment = match block_face {
|
|
AttachFace::Wall => {
|
|
if is_single_wall(*args.position, props.facing.opposite(), args.world) {
|
|
BellAttachment::SingleWall
|
|
} else {
|
|
BellAttachment::DoubleWall
|
|
}
|
|
}
|
|
AttachFace::Floor => BellAttachment::Floor,
|
|
AttachFace::Ceiling => BellAttachment::Ceiling,
|
|
};
|
|
|
|
props.to_state_id(args.block)
|
|
})
|
|
}
|
|
|
|
fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> {
|
|
Box::pin(async move {
|
|
let world: &World = args.world;
|
|
|
|
let is_receiving_power = block_receives_redstone_power(world, args.position).await;
|
|
let state = args.world.get_block_state(args.position);
|
|
|
|
let mut props = BellLikeProperties::from_state_id(state.id, args.block);
|
|
|
|
if props.powered != is_receiving_power {
|
|
props.powered = is_receiving_power;
|
|
|
|
args.world
|
|
.set_block_state(
|
|
args.position,
|
|
props.to_state_id(args.block),
|
|
BlockFlags::NOTIFY_ALL,
|
|
)
|
|
.await;
|
|
|
|
if is_receiving_power {
|
|
ring_bell(*args.position, args.world, None);
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|