feat: Falling Blocks
This commit is contained in:
parent
73c2c0115e
commit
306a007e3c
@ -1043,6 +1043,16 @@ pub(crate) fn build() -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc = r" Get a raw ID from an State ID."]
|
||||||
|
#[inline]
|
||||||
|
pub const fn get_raw_id_from_state_id(state_id: u16) -> u16 {
|
||||||
|
if state_id as usize >= Self::RAW_ID_FROM_STATE_ID.len() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
Self::RAW_ID_FROM_STATE_ID[state_id as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[doc = r" Get a block from a state id."]
|
#[doc = r" Get a block from a state id."]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn from_state_id(id: u16) -> &'static Self {
|
pub const fn from_state_id(id: u16) -> &'static Self {
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
use crate::block::{BlockBehaviour, OnPlaceArgs};
|
use crate::block::blocks::falling::FallingBlock;
|
||||||
|
use crate::block::{
|
||||||
|
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PlacedArgs,
|
||||||
|
};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
use pumpkin_data::block_properties::{BlockProperties, WallTorchLikeProperties};
|
||||||
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
use pumpkin_data::tag::{RegistryKey, get_tag_values};
|
||||||
@ -23,4 +26,18 @@ impl BlockBehaviour for AnvilBlock {
|
|||||||
props.facing = dir;
|
props.facing = dir;
|
||||||
props.to_state_id(args.block)
|
props.to_state_id(args.block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn placed(&self, args: PlacedArgs<'_>) {
|
||||||
|
FallingBlock::placed(&FallingBlock, args).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_state_for_neighbor_update(
|
||||||
|
&self,
|
||||||
|
args: GetStateForNeighborUpdateArgs<'_>,
|
||||||
|
) -> BlockStateId {
|
||||||
|
FallingBlock::get_state_for_neighbor_update(&FallingBlock, args).await
|
||||||
|
}
|
||||||
|
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
||||||
|
FallingBlock::on_scheduled_tick(&FallingBlock, args).await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
62
pumpkin/src/block/blocks/falling.rs
Normal file
62
pumpkin/src/block/blocks/falling.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use crate::{
|
||||||
|
block::{
|
||||||
|
BlockBehaviour, BlockMetadata, GetStateForNeighborUpdateArgs, OnScheduledTickArgs,
|
||||||
|
PlacedArgs,
|
||||||
|
},
|
||||||
|
entity::falling::FallingEntity,
|
||||||
|
};
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use pumpkin_data::{
|
||||||
|
Block, BlockState,
|
||||||
|
tag::{self, Taggable},
|
||||||
|
};
|
||||||
|
use pumpkin_world::{BlockStateId, tick::TickPriority};
|
||||||
|
pub struct FallingBlock;
|
||||||
|
|
||||||
|
impl FallingBlock {
|
||||||
|
#[must_use]
|
||||||
|
pub fn can_fall_through(state: &BlockState, block: &Block) -> bool {
|
||||||
|
state.is_air()
|
||||||
|
|| block.is_tagged_with_by_tag(&tag::Block::MINECRAFT_FIRE)
|
||||||
|
|| state.is_liquid()
|
||||||
|
|| state.replaceable()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockMetadata for FallingBlock {
|
||||||
|
fn namespace(&self) -> &'static str {
|
||||||
|
"minecraft"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ids(&self) -> &'static [&'static str] {
|
||||||
|
&[Block::GRAVEL.name, Block::SAND.name, Block::RED_SAND.name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl BlockBehaviour for FallingBlock {
|
||||||
|
async fn placed(&self, args: PlacedArgs<'_>) {
|
||||||
|
// TODO: make delay configurable
|
||||||
|
args.world
|
||||||
|
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
async fn get_state_for_neighbor_update(
|
||||||
|
&self,
|
||||||
|
args: GetStateForNeighborUpdateArgs<'_>,
|
||||||
|
) -> BlockStateId {
|
||||||
|
// TODO: make delay configurable
|
||||||
|
args.world
|
||||||
|
.schedule_block_tick(args.block, *args.position, 2, TickPriority::Normal)
|
||||||
|
.await;
|
||||||
|
args.state_id
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) {
|
||||||
|
let (block, state) = args.world.get_block_and_state(&args.position.down()).await;
|
||||||
|
if !Self::can_fall_through(state, block) || args.position.0.y < args.world.min_y {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FallingEntity::replace_spawn(args.world, *args.position, args.block.default_state.id).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -17,6 +17,7 @@ pub mod dirt_path;
|
|||||||
pub mod doors;
|
pub mod doors;
|
||||||
pub mod end_portal;
|
pub mod end_portal;
|
||||||
pub mod end_portal_frame;
|
pub mod end_portal_frame;
|
||||||
|
pub mod falling;
|
||||||
pub mod farmland;
|
pub mod farmland;
|
||||||
pub mod fence_gates;
|
pub mod fence_gates;
|
||||||
pub mod fences;
|
pub mod fences;
|
||||||
|
|||||||
@ -11,6 +11,7 @@ use crate::block::blocks::dirt_path::DirtPathBlock;
|
|||||||
use crate::block::blocks::doors::DoorBlock;
|
use crate::block::blocks::doors::DoorBlock;
|
||||||
use crate::block::blocks::end_portal::EndPortalBlock;
|
use crate::block::blocks::end_portal::EndPortalBlock;
|
||||||
use crate::block::blocks::end_portal_frame::EndPortalFrameBlock;
|
use crate::block::blocks::end_portal_frame::EndPortalFrameBlock;
|
||||||
|
use crate::block::blocks::falling::FallingBlock;
|
||||||
use crate::block::blocks::farmland::FarmlandBlock;
|
use crate::block::blocks::farmland::FarmlandBlock;
|
||||||
use crate::block::blocks::fence_gates::FenceGateBlock;
|
use crate::block::blocks::fence_gates::FenceGateBlock;
|
||||||
use crate::block::blocks::fences::FenceBlock;
|
use crate::block::blocks::fences::FenceBlock;
|
||||||
@ -205,6 +206,7 @@ pub fn default_registry() -> Arc<BlockRegistry> {
|
|||||||
manager.register(LecternBlock);
|
manager.register(LecternBlock);
|
||||||
manager.register(StemBlock);
|
manager.register(StemBlock);
|
||||||
manager.register(AttachedStemBlock);
|
manager.register(AttachedStemBlock);
|
||||||
|
manager.register(FallingBlock);
|
||||||
|
|
||||||
// Fire
|
// Fire
|
||||||
manager.register(SoulFireBlock);
|
manager.register(SoulFireBlock);
|
||||||
|
|||||||
128
pumpkin/src/entity/falling.rs
Normal file
128
pumpkin/src/entity/falling.rs
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_data::{damage::DamageType, entity::EntityType};
|
||||||
|
use pumpkin_protocol::java::client::play::{MetaDataType, Metadata};
|
||||||
|
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||||
|
use pumpkin_world::{BlockStateId, world::BlockFlags};
|
||||||
|
use std::sync::{Arc, atomic::Ordering};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
entity::{Entity, EntityBase, NBTStorage, living::LivingEntity},
|
||||||
|
server::Server,
|
||||||
|
world::World,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct FallingEntity {
|
||||||
|
entity: Entity,
|
||||||
|
block_state_id: BlockStateId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FallingEntity {
|
||||||
|
pub fn new(entity: Entity, block_state_id: BlockStateId) -> Self {
|
||||||
|
Self {
|
||||||
|
entity,
|
||||||
|
block_state_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaced the current Block and Spawns a new Falling one
|
||||||
|
pub async fn replace_spawn(world: &Arc<World>, position: BlockPos, block_state: BlockStateId) {
|
||||||
|
// Replace the original block, TODO: use fluid state
|
||||||
|
world
|
||||||
|
.set_block_state(
|
||||||
|
&position,
|
||||||
|
Block::AIR.default_state.id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let position = position.0.to_f64().add_raw(0.5, 0.0, 0.5);
|
||||||
|
let entity = Entity::new(
|
||||||
|
Uuid::new_v4(),
|
||||||
|
world.clone(),
|
||||||
|
position,
|
||||||
|
&EntityType::FALLING_BLOCK,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
entity.data.store(i32::from(block_state), Ordering::Relaxed);
|
||||||
|
let entity = Arc::new(Self::new(entity, block_state));
|
||||||
|
world.spawn_entity(entity).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NBTStorage for FallingEntity {}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl EntityBase for FallingEntity {
|
||||||
|
async fn tick(&self, caller: Arc<dyn EntityBase>, server: &Server) {
|
||||||
|
let entity = &self.entity;
|
||||||
|
entity.tick(caller.clone(), server).await;
|
||||||
|
|
||||||
|
let original_velo = entity.velocity.load();
|
||||||
|
let mut velo = original_velo;
|
||||||
|
velo.y -= self.get_gravity();
|
||||||
|
|
||||||
|
entity.velocity.store(velo);
|
||||||
|
|
||||||
|
entity.move_entity(caller.clone(), velo).await;
|
||||||
|
entity.tick_block_collisions(&caller, server).await;
|
||||||
|
if entity.on_ground.load(Ordering::Relaxed) {
|
||||||
|
entity.velocity.store(velo.multiply(0.7, -0.5, 0.7));
|
||||||
|
entity
|
||||||
|
.world
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.set_block_state(
|
||||||
|
&self.entity.block_pos.load(),
|
||||||
|
self.block_state_id,
|
||||||
|
BlockFlags::NOTIFY_ALL,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
entity.remove().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.velocity.store(velo.multiply(0.98, 0.98, 0.98));
|
||||||
|
|
||||||
|
entity.send_pos_rot().await;
|
||||||
|
|
||||||
|
entity.send_velocity().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn init_data_tracker(&self) {
|
||||||
|
self.entity
|
||||||
|
.send_meta_data(&[Metadata::new(
|
||||||
|
8,
|
||||||
|
MetaDataType::BlockPos,
|
||||||
|
self.entity.block_pos.load(),
|
||||||
|
)])
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn damage_with_context(
|
||||||
|
&self,
|
||||||
|
_amount: f32,
|
||||||
|
_damage_type: DamageType,
|
||||||
|
_position: Option<Vector3<f64>>,
|
||||||
|
_source: Option<&dyn EntityBase>,
|
||||||
|
_cause: Option<&dyn EntityBase>,
|
||||||
|
) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_entity(&self) -> &Entity {
|
||||||
|
&self.entity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_living_entity(&self) -> Option<&LivingEntity> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_nbt_storage(&self) -> &dyn NBTStorage {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_gravity(&self) -> f64 {
|
||||||
|
0.04
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -53,6 +53,7 @@ pub mod ai;
|
|||||||
pub mod decoration;
|
pub mod decoration;
|
||||||
pub mod effect;
|
pub mod effect;
|
||||||
pub mod experience_orb;
|
pub mod experience_orb;
|
||||||
|
pub mod falling;
|
||||||
pub mod hunger;
|
pub mod hunger;
|
||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod living;
|
pub mod living;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user