Pumpkin/crates/pumpkin/src/block/blocks/redstone/repeater.rs
Alexander Medvedev fdb6da2b0d fix: anvil
2026-08-31 16:47:23 +02:00

235 lines
7.8 KiB
Rust

use std::sync::Arc;
use pumpkin_data::{
Block, BlockDirection, BlockState, BlockStateId, HorizontalFacingExt,
block_properties::{BlockProperties, HorizontalFacing},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::tick::TickPriority;
use pumpkin_world::world::BlockFlags;
use crate::{
block::{
BlockBehaviour, CanPlaceAtArgs, EmitsRedstonePowerArgs, GetRedstonePowerArgs,
GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs,
OnScheduledTickArgs, OnStateReplacedArgs, PlacedArgs, PlayerPlacedArgs,
registry::BlockActionResult,
},
world::World,
};
use super::abstract_redstone_gate::{RedstoneGateBlock, RedstoneGateBlockProperties};
type RepeaterProperties = pumpkin_data::block_properties::RepeaterLikeProperties;
#[pumpkin_block("minecraft:repeater")]
pub struct RepeaterBlock;
impl BlockBehaviour for RepeaterBlock {
fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let state_id = RedstoneGateBlock::on_place(self, args.player, args.block);
let mut props = RepeaterProperties::from_state_id(state_id, args.block);
props.locked = self.is_locked(args.world, *args.position, state_id, args.block);
props.to_state_id(args.block)
}
fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
RedstoneGateBlock::on_neighbor_update(self, args);
}
fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
let (block, state) = args.world.get_block_and_state(args.position);
if self.is_locked(args.world, *args.position, state.id, block) {
return;
}
let mut props = RepeaterProperties::from_state_id(state.id, block);
let now_powered = props.powered;
let should_be_powered = self.has_power(args.world, *args.position, state, block);
if now_powered && !should_be_powered {
props.powered = false;
args.world.set_block_state(
args.position,
props.to_state_id(block),
BlockFlags::NOTIFY_LISTENERS,
);
RedstoneGateBlock::update_target(
self,
args.world,
*args.position,
props.to_state_id(block),
block,
);
} else if !now_powered {
props.powered = true;
args.world.set_block_state(
args.position,
props.to_state_id(block),
BlockFlags::NOTIFY_LISTENERS,
);
if !should_be_powered {
args.world.schedule_block_tick(
block,
*args.position,
RedstoneGateBlock::get_update_delay_internal(
self,
props.to_state_id(block),
block,
),
TickPriority::VeryHigh,
);
}
RedstoneGateBlock::update_target(
self,
args.world,
*args.position,
props.to_state_id(block),
block,
);
}
}
fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
let state = args.world.get_block_state(args.position);
let props = RepeaterProperties::from_state_id(state.id, args.block);
Self::on_use(props, args.world, *args.position, args.block);
BlockActionResult::SuccessServer
}
fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 {
RedstoneGateBlock::get_weak_redstone_power(self, args)
}
fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 {
RedstoneGateBlock::get_strong_redstone_power(self, args)
}
fn emits_redstone_power(&self, args: EmitsRedstonePowerArgs<'_>) -> bool {
let repeater_props = RepeaterProperties::from_state_id(args.state.id, args.block);
repeater_props.facing.to_block_direction() == args.direction
|| repeater_props.facing.to_block_direction() == args.direction.opposite()
}
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
RedstoneGateBlock::can_place_at(self, args.block_accessor, *args.position)
}
fn placed(&self, args: PlacedArgs<'_>) {
RedstoneGateBlock::update_target(
self,
args.world,
*args.position,
args.state_id,
args.block,
);
}
fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
if args.direction == BlockDirection::Down
&& !RedstoneGateBlock::can_place_above(
self,
args.world,
*args.neighbor_position,
BlockState::from_id(args.neighbor_state_id),
)
{
return Block::AIR.default_state.id;
}
let mut props = RepeaterProperties::from_state_id(args.state_id, args.block);
if args.direction.to_axis() != props.facing.to_block_direction().to_axis() {
props.locked = self.is_locked(args.world, *args.position, args.state_id, args.block);
return props.to_state_id(args.block);
}
args.state_id
}
fn player_placed(&self, args: PlayerPlacedArgs<'_>) {
RedstoneGateBlock::player_placed(self, args);
}
fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) {
RedstoneGateBlock::on_state_replaced(self, args);
}
}
impl RedstoneGateBlockProperties for RepeaterProperties {
fn is_powered(&self) -> bool {
self.powered
}
fn get_facing(&self) -> HorizontalFacing {
self.facing
}
fn set_facing(&mut self, facing: HorizontalFacing) {
self.facing = facing;
}
}
impl RedstoneGateBlock<RepeaterProperties> for RepeaterBlock {
fn get_output_level(&self, _world: &World, _pos: BlockPos) -> u8 {
15
}
fn update_powered(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block) {
if self.is_locked(world, pos, state.id, block) {
return;
}
let props = RepeaterProperties::from_state_id(state.id, block);
let powered = props.powered;
// Note: The signature for has_power must be called without self, as it's a trait method.
let has_power = RedstoneGateBlock::has_power(self, world, pos, state, block);
if powered != has_power && !world.is_block_tick_scheduled(&pos, block) {
let priority =
if RedstoneGateBlock::is_target_not_aligned(self, world, pos, state, block) {
TickPriority::ExtremelyHigh
} else if powered {
TickPriority::VeryHigh
} else {
TickPriority::High
};
world.schedule_block_tick(
block,
pos,
RedstoneGateBlock::get_update_delay_internal(self, state.id, block),
priority,
);
}
}
fn get_update_delay_internal(&self, state_id: BlockStateId, block: &Block) -> u8 {
let props = RepeaterProperties::from_state_id(state_id, block);
props.delay * 2
}
}
impl RepeaterBlock {
fn on_use(props: RepeaterProperties, world: &Arc<World>, block_pos: BlockPos, block: &Block) {
let mut props = props;
props.delay = if props.delay == 4 { 1 } else { props.delay + 1 };
let state = props.to_state_id(block);
world.set_block_state(&block_pos, state, BlockFlags::NOTIFY_ALL);
}
fn is_locked(
&self,
world: &World,
pos: BlockPos,
state_id: BlockStateId,
block: &Block,
) -> bool {
Self::get_max_input_level_sides(self, world, pos, state_id, block, true) > 0
}
}