Pumpkin/pumpkin/src/block/blocks/redstone/abstruct_redstone_gate.rs
2025-07-29 12:19:05 +02:00

262 lines
7.8 KiB
Rust

use std::sync::Arc;
use async_trait::async_trait;
use pumpkin_data::{
Block, BlockDirection, BlockState, HorizontalFacingExt,
block_properties::{
BlockProperties, ComparatorLikeProperties, EnumVariants, HorizontalFacing,
RedstoneWireLikeProperties, RepeaterLikeProperties,
},
};
use pumpkin_util::math::position::BlockPos;
use pumpkin_world::{
BlockStateId,
tick::TickPriority,
world::{BlockAccessor, BlockFlags},
};
use crate::{
block::{GetRedstonePowerArgs, OnNeighborUpdateArgs, OnStateReplacedArgs, PlayerPlacedArgs},
entity::player::Player,
world::World,
};
use super::{get_redstone_power, is_diode};
pub trait RedstoneGateBlockProperties {
fn is_powered(&self) -> bool;
fn get_facing(&self) -> HorizontalFacing;
fn set_facing(&mut self, facing: HorizontalFacing);
}
#[async_trait]
pub trait RedstoneGateBlock<T: Send + BlockProperties + RedstoneGateBlockProperties> {
async fn can_place_at(&self, world: &dyn BlockAccessor, pos: BlockPos) -> bool {
let under_pos = pos.down();
let under_state = world.get_block_state(&under_pos).await;
self.can_place_above(world, under_pos, under_state).await
}
async fn can_place_above(
&self,
_world: &dyn BlockAccessor,
_pos: BlockPos,
state: &BlockState,
) -> bool {
state.is_side_solid(BlockDirection::Up)
}
async fn get_weak_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 {
let props = T::from_state_id(args.state.id, args.block);
if props.is_powered() && props.get_facing().to_block_direction() == args.direction {
self.get_output_level(args.world, *args.position).await
} else {
0
}
}
async fn get_strong_redstone_power(&self, args: GetRedstonePowerArgs<'_>) -> u8 {
self.get_weak_redstone_power(args).await
}
async fn get_output_level(&self, world: &World, pos: BlockPos) -> u8;
async fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) {
let state = args.world.get_block_state(args.position).await;
if RedstoneGateBlock::can_place_at(self, args.world.as_ref(), *args.position).await {
self.update_powered(args.world, *args.position, state, args.block)
.await;
return;
}
args.world
.set_block_state(
args.position,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
for dir in BlockDirection::all() {
args.world
.update_neighbor(&args.position.offset(dir.to_offset()), args.source_block)
.await;
}
}
async fn update_powered(&self, world: &World, pos: BlockPos, state: &BlockState, block: &Block);
async fn has_power(
&self,
world: &World,
pos: BlockPos,
state: &BlockState,
block: &Block,
) -> bool {
self.get_power(world, pos, state, block).await > 0
}
async fn get_power(
&self,
world: &World,
pos: BlockPos,
state: &BlockState,
block: &Block,
) -> u8 {
get_power::<T>(world, pos, state.id, block).await
}
async fn get_max_input_level_sides(
&self,
world: &World,
pos: BlockPos,
state_id: BlockStateId,
block: &Block,
only_gate: bool,
) -> u8 {
let props = T::from_state_id(state_id, block);
let facing = props.get_facing();
let power_left = get_power_on_side(world, &pos, facing.rotate_clockwise(), only_gate).await;
let power_right =
get_power_on_side(world, &pos, facing.rotate_counter_clockwise(), only_gate).await;
std::cmp::max(power_left, power_right)
}
async fn update_target(
&self,
world: &Arc<World>,
pos: BlockPos,
state_id: BlockStateId,
block: &Block,
) {
let props = T::from_state_id(state_id, block);
let facing = props.get_facing();
let front_pos = pos.offset(facing.opposite().to_offset());
world.update_neighbor(&front_pos, block).await;
world
.update_neighbors(&front_pos, Some(facing.to_block_direction()))
.await;
}
async fn on_place(&self, player: &Player, block: &Block) -> BlockStateId {
let mut props = T::default(block);
let dir = player
.living_entity
.entity
.get_horizontal_facing()
.opposite();
props.set_facing(dir);
props.to_state_id(block)
}
async fn player_placed(&self, args: PlayerPlacedArgs<'_>) {
if RedstoneGateBlock::has_power(
self,
args.world,
*args.position,
BlockState::from_id(args.state_id),
args.block,
)
.await
{
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
}
async fn on_state_replaced(&self, args: OnStateReplacedArgs<'_>) {
if args.moved || Block::from_state_id(args.old_state_id) == args.block {
return;
}
RedstoneGateBlock::update_target(
self,
args.world,
*args.position,
BlockState::from_id(args.old_state_id).id,
args.block,
)
.await;
}
async fn is_target_not_aligned(
&self,
world: &dyn BlockAccessor,
pos: BlockPos,
state: &BlockState,
block: &Block,
) -> bool {
let props = T::from_state_id(state.id, block);
let facing = props.get_facing().opposite();
let (target_block, target_state) = world
.get_block_and_state(&pos.offset(facing.to_offset()))
.await;
if target_block == &Block::COMPARATOR {
let props = ComparatorLikeProperties::from_state_id(target_state.id, target_block);
props.facing != facing
} else if target_block == &Block::REPEATER {
let props = RepeaterLikeProperties::from_state_id(target_state.id, target_block);
props.facing != facing
} else {
false
}
}
fn get_update_delay_internal(&self, state_id: BlockStateId, block: &Block) -> u8;
}
pub async fn get_power<T: BlockProperties + RedstoneGateBlockProperties + Send>(
world: &World,
pos: BlockPos,
state_id: BlockStateId,
block: &Block,
) -> u8 {
let props = T::from_state_id(state_id, block);
let facing = props.get_facing();
let source_pos = pos.offset(facing.to_offset());
let (source_block, source_state) = world.get_block_and_state(&source_pos).await;
let source_level = get_redstone_power(
source_block,
source_state,
world,
&source_pos,
facing.to_block_direction(),
)
.await;
if source_level >= 15 {
source_level
} else {
source_level.max(if source_block == &Block::REDSTONE_WIRE {
let props = RedstoneWireLikeProperties::from_state_id(source_state.id, source_block);
props.power.to_index() as u8
} else {
0
})
}
}
async fn get_power_on_side(
world: &World,
pos: &BlockPos,
side: HorizontalFacing,
only_gate: bool,
) -> u8 {
let side_pos = pos.offset(side.to_block_direction().to_offset());
let (side_block, side_state) = world.get_block_and_state(&side_pos).await;
if !only_gate || is_diode(side_block) {
world
.block_registry
.get_weak_redstone_power(
side_block,
world,
&side_pos,
side_state,
side.to_block_direction(),
)
.await
} else {
0
}
}