239 lines
7.8 KiB
Rust
239 lines
7.8 KiB
Rust
use piston::PistonBlock;
|
|
use pumpkin_data::Block;
|
|
use pumpkin_data::block_state::PistonBehavior;
|
|
use pumpkin_util::math::position::BlockPos;
|
|
use pumpkin_world::block::BlockDirection;
|
|
|
|
use crate::world::World;
|
|
|
|
#[expect(clippy::module_inception)]
|
|
pub(crate) mod piston;
|
|
pub(crate) mod piston_extension;
|
|
pub(crate) mod piston_head;
|
|
|
|
const MAX_MOVABLE_BLOCKS: usize = 12;
|
|
|
|
pub struct PistonHandler<'a> {
|
|
world: &'a World,
|
|
pos_from: BlockPos,
|
|
retracted: bool,
|
|
pos_to: BlockPos,
|
|
motion_direction: BlockDirection,
|
|
pub moved_blocks: Vec<BlockPos>,
|
|
pub broken_blocks: Vec<BlockPos>,
|
|
_piston_direction: BlockDirection,
|
|
}
|
|
|
|
impl<'a> PistonHandler<'a> {
|
|
pub fn new(world: &'a World, pos: BlockPos, dir: BlockDirection, retracted: bool) -> Self {
|
|
let motion_direction;
|
|
let pos_to = if retracted {
|
|
motion_direction = dir;
|
|
pos.offset_dir(dir.to_offset(), 1)
|
|
} else {
|
|
motion_direction = dir.opposite();
|
|
pos.offset_dir(dir.to_offset(), 2)
|
|
};
|
|
PistonHandler {
|
|
world,
|
|
pos_from: pos,
|
|
_piston_direction: dir,
|
|
retracted,
|
|
motion_direction,
|
|
pos_to,
|
|
moved_blocks: Vec::new(),
|
|
broken_blocks: Vec::new(),
|
|
}
|
|
}
|
|
|
|
pub async fn calculate_push(&mut self) -> bool {
|
|
self.moved_blocks.clear();
|
|
self.broken_blocks.clear();
|
|
let (block, block_state) = self
|
|
.world
|
|
.get_block_and_block_state(&self.pos_to)
|
|
.await
|
|
.unwrap();
|
|
if !PistonBlock::is_movable(
|
|
&block,
|
|
&block_state,
|
|
// self.world,
|
|
// self.pos_to,
|
|
// self.motion_direction,
|
|
// false,
|
|
// self.piston_direction,
|
|
) {
|
|
if self.retracted && block_state.piston_behavior == PistonBehavior::Destroy {
|
|
self.broken_blocks.push(self.pos_to);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
if !self.try_move(self.pos_to, self.motion_direction).await {
|
|
return false;
|
|
}
|
|
for block_pos in self.moved_blocks.clone() {
|
|
let block = self.world.get_block(&block_pos).await.unwrap();
|
|
if Self::is_block_sticky(&block)
|
|
&& !self.try_move_adjacent_block(&block, block_pos).await
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
fn is_block_sticky(block: &Block) -> bool {
|
|
block == &Block::SLIME_BLOCK || block == &Block::HONEY_BLOCK
|
|
}
|
|
|
|
fn is_adjacent_block_stuck(state: &Block, adjacent_state: &Block) -> bool {
|
|
if state == &Block::HONEY_BLOCK && adjacent_state == &Block::SLIME_BLOCK {
|
|
return false;
|
|
}
|
|
if state == &Block::SLIME_BLOCK && adjacent_state == &Block::HONEY_BLOCK {
|
|
return false;
|
|
}
|
|
Self::is_block_sticky(state) || Self::is_block_sticky(adjacent_state)
|
|
}
|
|
|
|
async fn try_move(&mut self, pos: BlockPos, _dir: BlockDirection) -> bool {
|
|
let (mut block, mut block_state) =
|
|
self.world.get_block_and_block_state(&pos).await.unwrap();
|
|
if block_state.is_air() {
|
|
return true;
|
|
}
|
|
if !PistonBlock::is_movable(
|
|
&block,
|
|
&block_state,
|
|
// self.world,
|
|
// pos,
|
|
// self.motion_direction,
|
|
// false,
|
|
// dir,
|
|
) {
|
|
return true;
|
|
}
|
|
if pos == self.pos_from {
|
|
return true;
|
|
}
|
|
if self.moved_blocks.contains(&pos) {
|
|
return true;
|
|
}
|
|
let mut i = 1;
|
|
if i + self.moved_blocks.len() > MAX_MOVABLE_BLOCKS {
|
|
return false;
|
|
}
|
|
while Self::is_block_sticky(&block) {
|
|
let block_pos = pos.offset_dir(self.motion_direction.opposite().to_offset(), i as i32);
|
|
let block2 = block;
|
|
(block, block_state) = self
|
|
.world
|
|
.get_block_and_block_state(&block_pos)
|
|
.await
|
|
.unwrap();
|
|
if block_state.is_air()
|
|
|| !Self::is_adjacent_block_stuck(&block2, &block)
|
|
|| !PistonBlock::is_movable(
|
|
&block,
|
|
&block_state,
|
|
// self.world,
|
|
// block_pos,
|
|
// self.motion_direction,
|
|
// false,
|
|
// self.motion_direction.opposite(),
|
|
)
|
|
|| block_pos == self.pos_from
|
|
{
|
|
break;
|
|
}
|
|
i += 1;
|
|
if i + self.moved_blocks.len() > MAX_MOVABLE_BLOCKS {
|
|
return false;
|
|
}
|
|
}
|
|
let mut j = 0;
|
|
for k in (0..i).rev() {
|
|
self.moved_blocks
|
|
.push(pos.offset_dir(self.motion_direction.opposite().to_offset(), k as i32));
|
|
j += 1;
|
|
}
|
|
let mut k = 1;
|
|
loop {
|
|
let block_pos2 = pos.offset_dir(self.motion_direction.to_offset(), k);
|
|
if let Some(l) = self.moved_blocks.iter().position(|&p| p == block_pos2) {
|
|
self.set_moved_blocks(j, l);
|
|
for m in 0..=(l + j) {
|
|
let block_pos3 = self.moved_blocks[m];
|
|
let block = self.world.get_block(&block_pos3).await.unwrap();
|
|
if Self::is_block_sticky(&block)
|
|
&& !Box::pin(self.try_move_adjacent_block(&block, block_pos3)).await
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
(block, block_state) = self
|
|
.world
|
|
.get_block_and_block_state(&block_pos2)
|
|
.await
|
|
.unwrap();
|
|
if block_state.is_air() {
|
|
return true;
|
|
}
|
|
if !PistonBlock::is_movable(
|
|
&block,
|
|
&block_state,
|
|
// self.world,
|
|
// block_pos2,
|
|
// self.motion_direction,
|
|
// true,
|
|
// self.motion_direction,
|
|
) || block_pos2 == self.pos_from
|
|
{
|
|
return false;
|
|
}
|
|
if block_state.piston_behavior == PistonBehavior::Destroy {
|
|
self.broken_blocks.push(block_pos2);
|
|
return true;
|
|
}
|
|
if self.moved_blocks.len() >= MAX_MOVABLE_BLOCKS {
|
|
return false;
|
|
}
|
|
self.moved_blocks.push(block_pos2);
|
|
j += 1;
|
|
k += 1;
|
|
}
|
|
}
|
|
|
|
fn set_moved_blocks(&mut self, from: usize, to: usize) {
|
|
let mut list = Vec::new();
|
|
let mut list2 = Vec::new();
|
|
let mut list3 = Vec::new();
|
|
list.extend_from_slice(&self.moved_blocks[0..to]);
|
|
list2.extend_from_slice(&self.moved_blocks[self.moved_blocks.len() - from..]);
|
|
list3.extend_from_slice(&self.moved_blocks[to..self.moved_blocks.len() - from]);
|
|
self.moved_blocks.clear();
|
|
self.moved_blocks.extend(list);
|
|
self.moved_blocks.extend(list2);
|
|
self.moved_blocks.extend(list3);
|
|
}
|
|
|
|
async fn try_move_adjacent_block(&mut self, block: &Block, pos: BlockPos) -> bool {
|
|
for direction in BlockDirection::all() {
|
|
if direction.to_axis() == self.motion_direction.to_axis() {
|
|
continue;
|
|
}
|
|
let block_pos = pos.offset(direction.to_offset());
|
|
let block_state2 = self.world.get_block(&block_pos).await.unwrap();
|
|
if Self::is_adjacent_block_stuck(&block_state2, block)
|
|
&& !self.try_move(block_pos, direction).await
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|