Pumpkin/pumpkin/src/block/blocks/redstone/observer.rs
2026-05-14 20:34:55 +02:00

142 lines
4.7 KiB
Rust

use std::sync::Arc;
use crate::block::{
BlockFuture, EmitsRedstonePowerArgs, GetRedstonePowerArgs, GetStateForNeighborUpdateArgs,
OnPlaceArgs, OnScheduledTickArgs, OnStateReplacedArgs,
};
use pumpkin_data::{
Block, FacingExt,
block_properties::{BlockProperties, ObserverLikeProperties},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{BlockStateId, tick::TickPriority, world::BlockFlags};
use crate::{block::BlockBehaviour, world::World};
#[pumpkin_block("minecraft:observer")]
pub struct ObserverBlock;
impl BlockBehaviour for ObserverBlock {
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let mut props = ObserverLikeProperties::default(args.block);
props.facing = args.player.living_entity.entity.get_facing();
props.to_state_id(args.block)
})
}
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);
let mut props = ObserverLikeProperties::from_state_id(state.id, args.block);
if props.powered {
props.powered = false;
args.world
.set_block_state(
args.position,
props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS,
)
.await;
} else {
props.powered = true;
args.world
.set_block_state(
args.position,
props.to_state_id(args.block),
BlockFlags::NOTIFY_LISTENERS,
)
.await;
args.world
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal);
}
Self::update_neighbors(args.world, args.block, args.position, &props).await;
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let props = ObserverLikeProperties::from_state_id(args.state_id, args.block);
if props.facing.to_block_direction() == args.direction && !props.powered {
Self::schedule_tick(args.world, args.position);
}
args.state_id
})
}
fn emits_redstone_power<'a>(
&'a self,
args: EmitsRedstonePowerArgs<'a>,
) -> BlockFuture<'a, bool> {
Box::pin(async move {
let props = ObserverLikeProperties::from_state_id(args.state.id, args.block);
props.facing.to_block_direction() == args.direction
})
}
fn get_weak_redstone_power<'a>(
&'a self,
args: GetRedstonePowerArgs<'a>,
) -> BlockFuture<'a, u8> {
Box::pin(async move {
let props = ObserverLikeProperties::from_state_id(args.state.id, args.block);
if props.facing.to_block_direction() == args.direction && props.powered {
15
} else {
0
}
})
}
fn get_strong_redstone_power<'a>(
&'a self,
args: GetRedstonePowerArgs<'a>,
) -> BlockFuture<'a, u8> {
Box::pin(async move { self.get_weak_redstone_power(args).await })
}
fn on_state_replaced<'a>(&'a self, args: OnStateReplacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if !args.moved {
let props = ObserverLikeProperties::from_state_id(args.old_state_id, args.block);
if props.powered
&& args
.world
.is_block_tick_scheduled(args.position, &Block::OBSERVER)
{
Self::update_neighbors(args.world, args.block, args.position, &props).await;
}
}
})
}
}
impl ObserverBlock {
async fn update_neighbors(
world: &Arc<World>,
block: &Block,
block_pos: &BlockPos,
props: &ObserverLikeProperties,
) {
let facing = props.facing;
let opposite_facing_pos =
block_pos.offset(facing.to_block_direction().opposite().to_offset());
world.update_neighbor(&opposite_facing_pos, block).await;
world
.update_neighbors(&opposite_facing_pos, Some(facing.to_block_direction()))
.await;
}
fn schedule_tick(world: &World, block_pos: &BlockPos) {
world.schedule_block_tick(&Block::OBSERVER, *block_pos, 2, TickPriority::Normal);
}
}