From d8d667600896307c0e9de24ed43a41009e7552e7 Mon Sep 17 00:00:00 2001 From: teknostom <42341753+teknostom@users.noreply.github.com> Date: Wed, 30 Jul 2025 14:10:15 +0200 Subject: [PATCH] add gourds (#1090) * add gourds * fix * Update stem.rs --- .../blocks/plant/gourds/attached_stem.rs | 85 +++++++++++ pumpkin/src/block/blocks/plant/gourds/mod.rs | 2 + pumpkin/src/block/blocks/plant/gourds/stem.rs | 134 ++++++++++++++++++ pumpkin/src/block/blocks/plant/mod.rs | 1 + pumpkin/src/block/fluid/flowing.rs | 1 + pumpkin/src/block/registry.rs | 4 + 6 files changed, 227 insertions(+) create mode 100644 pumpkin/src/block/blocks/plant/gourds/attached_stem.rs create mode 100644 pumpkin/src/block/blocks/plant/gourds/mod.rs create mode 100644 pumpkin/src/block/blocks/plant/gourds/stem.rs diff --git a/pumpkin/src/block/blocks/plant/gourds/attached_stem.rs b/pumpkin/src/block/blocks/plant/gourds/attached_stem.rs new file mode 100644 index 000000000..c39929c6d --- /dev/null +++ b/pumpkin/src/block/blocks/plant/gourds/attached_stem.rs @@ -0,0 +1,85 @@ +use async_trait::async_trait; +use pumpkin_data::{ + Block, + block_properties::{ + BlockProperties, EnumVariants, Integer0To7, WallTorchLikeProperties, WheatLikeProperties, + }, +}; +use pumpkin_util::math::position::BlockPos; +use pumpkin_world::{BlockStateId, world::BlockAccessor}; + +use crate::block::{ + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, + blocks::plant::PlantBlockBase, +}; + +type AttachedStemProperties = WallTorchLikeProperties; + +type StemProperties = WheatLikeProperties; +pub struct AttachedStemBlock; + +impl BlockMetadata for AttachedStemBlock { + fn namespace(&self) -> &'static str { + "minecraft" + } + + fn ids(&self) -> &'static [&'static str] { + &[ + Block::ATTACHED_PUMPKIN_STEM.name, + Block::ATTACHED_MELON_STEM.name, + ] + } +} + +impl AttachedStemBlock { + fn get_stem(block: &Block) -> &Block { + match block.id { + id if id == Block::ATTACHED_PUMPKIN_STEM.id => &Block::PUMPKIN_STEM, + id if id == Block::ATTACHED_MELON_STEM.id => &Block::MELON_STEM, + _ => &Block::MELON_STEM, // Should never happen + } + } + + fn get_gourd(block: &Block) -> &Block { + match block.id { + id if id == Block::ATTACHED_PUMPKIN_STEM.id => &Block::PUMPKIN, + id if id == Block::ATTACHED_MELON_STEM.id => &Block::MELON, + _ => &Block::MELON, // Should never happen + } + } +} + +#[async_trait] +impl BlockBehaviour for AttachedStemBlock { + async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { + ::can_place_at(self, args.block_accessor, args.position).await + } + + async fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + let props = AttachedStemProperties::from_state_id(args.state_id, args.block); + if args.direction.to_horizontal_facing() == Some(props.facing) + && args.neighbor_state_id != Self::get_gourd(args.block).default_state.id + { + let mut props = StemProperties::default(Self::get_stem(args.block)); + props.age = Integer0To7::from_index(7); + return props.to_state_id(Self::get_stem(args.block)); + } + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + } +} + +impl PlantBlockBase for AttachedStemBlock { + async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool { + let block = block_accessor.get_block(pos).await; + block == &Block::FARMLAND + } +} diff --git a/pumpkin/src/block/blocks/plant/gourds/mod.rs b/pumpkin/src/block/blocks/plant/gourds/mod.rs new file mode 100644 index 000000000..0a9a22a0f --- /dev/null +++ b/pumpkin/src/block/blocks/plant/gourds/mod.rs @@ -0,0 +1,2 @@ +pub mod attached_stem; +pub mod stem; diff --git a/pumpkin/src/block/blocks/plant/gourds/stem.rs b/pumpkin/src/block/blocks/plant/gourds/stem.rs new file mode 100644 index 000000000..40e0a1625 --- /dev/null +++ b/pumpkin/src/block/blocks/plant/gourds/stem.rs @@ -0,0 +1,134 @@ +use crate::block::{ + BlockBehaviour, BlockMetadata, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, RandomTickArgs, + blocks::plant::PlantBlockBase, +}; +use async_trait::async_trait; +use pumpkin_data::{ + Block, BlockDirection, + block_properties::{ + BlockProperties, EnumVariants, Integer0To7, WallTorchLikeProperties, WheatLikeProperties, + }, + tag, + tag::Taggable, +}; +use pumpkin_util::{ + math::position::BlockPos, + random::{RandomGenerator, xoroshiro128::Xoroshiro}, +}; +use pumpkin_world::{ + BlockStateId, + world::{BlockAccessor, BlockFlags}, +}; +use rand::Rng; + +type StemProperties = WheatLikeProperties; +type AttachedStemProperties = WallTorchLikeProperties; + +pub struct StemBlock; + +impl BlockMetadata for StemBlock { + fn namespace(&self) -> &'static str { + "minecraft" + } + + fn ids(&self) -> &'static [&'static str] { + &[Block::PUMPKIN_STEM.name, Block::MELON_STEM.name] + } +} + +impl StemBlock { + fn state_with_age(block: &Block, state: u16, age: i32) -> BlockStateId { + let mut props = StemProperties::from_state_id(state, block); + props.age = Integer0To7::from_index(age as u16); + props.to_state_id(block) + } + + fn get_attached_stem(dir: BlockDirection, block: &Block) -> BlockStateId { + let attached_block = match block.id { + id if id == Block::PUMPKIN_STEM.id => &Block::ATTACHED_PUMPKIN_STEM, + id if id == Block::MELON_STEM.id => &Block::ATTACHED_MELON_STEM, + _ => &Block::ATTACHED_MELON_STEM, // Should never happen + }; + let mut props = AttachedStemProperties::default(attached_block); + props.facing = dir.to_horizontal_facing().unwrap(); + props.to_state_id(attached_block) + } + + fn get_gourd(block: &Block) -> &Block { + match block.id { + id if id == Block::PUMPKIN_STEM.id => &Block::PUMPKIN, + id if id == Block::MELON_STEM.id => &Block::MELON, + _ => &Block::MELON, // Should never happen + } + } +} + +#[async_trait] +impl BlockBehaviour for StemBlock { + async fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { + ::can_place_at(self, args.block_accessor, args.position).await + } + + async fn get_state_for_neighbor_update( + &self, + args: GetStateForNeighborUpdateArgs<'_>, + ) -> BlockStateId { + ::get_state_for_neighbor_update( + self, + args.world, + args.position, + args.state_id, + ) + .await + } + + async fn random_tick(&self, args: RandomTickArgs<'_>) { + // TODO add light level and moisture check + let f = 5; + if rand::rng().random_range(0..=(25 / f)) == 0 { + let (block, state) = args.world.get_block_and_state_id(args.position).await; + let props = StemProperties::from_state_id(state, block); + let age = i32::from(props.age.to_index()); + if age < 7 { + args.world + .set_block_state( + args.position, + Self::state_with_age(block, state, age + 1), + BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + } else { + let dir = BlockDirection::random_horizontal(&mut RandomGenerator::Xoroshiro( + Xoroshiro::from_seed(rand::rng().random()), + )); + let plant_block_pos = args.position.offset(dir.to_offset()); + let plant_block_state = args.world.get_block_state(&plant_block_pos).await; + let under_block: &Block = args.world.get_block(&plant_block_pos.down()).await; + if plant_block_state.is_air() + && (under_block == &Block::FARMLAND + || under_block.is_tagged_with_by_tag(&tag::Block::MINECRAFT_DIRT)) + { + let attached_stem = Self::get_attached_stem(dir, block); + let gourd = Self::get_gourd(block); + args.world + .set_block_state( + &plant_block_pos, + gourd.default_state.id, + BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + args.world + .set_block_state(args.position, attached_stem, BlockFlags::NOTIFY_NEIGHBORS) + .await; + } + } + } + } +} + +impl PlantBlockBase for StemBlock { + async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool { + let block = block_accessor.get_block(pos).await; + block == &Block::FARMLAND + } +} diff --git a/pumpkin/src/block/blocks/plant/mod.rs b/pumpkin/src/block/blocks/plant/mod.rs index 4b7fa34c7..6815db728 100644 --- a/pumpkin/src/block/blocks/plant/mod.rs +++ b/pumpkin/src/block/blocks/plant/mod.rs @@ -7,6 +7,7 @@ pub mod crop; pub mod dry_vegetation; pub mod flower; pub mod flowerbed; +pub mod gourds; pub mod leaf_litter; pub mod lily_pad; pub mod mushroom_plant; diff --git a/pumpkin/src/block/fluid/flowing.rs b/pumpkin/src/block/fluid/flowing.rs index 996957ec6..df137444b 100644 --- a/pumpkin/src/block/fluid/flowing.rs +++ b/pumpkin/src/block/fluid/flowing.rs @@ -267,6 +267,7 @@ pub trait FlowingFluid { return true; } + //TODO check if source if self.is_same_fluid(fluid, state_id) { let props = FlowingFluidProperties::from_state_id(state_id, fluid); return props.level == Level::L8 && props.falling != Falling::True; diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index 9b25b3699..3adf197b1 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -107,6 +107,8 @@ use crate::block::blocks::redstone::dispenser::DispenserBlock; use crate::block::blocks::redstone::dropper::DropperBlock; use super::BlockIsReplacing; +use super::blocks::plant::gourds::attached_stem::AttachedStemBlock; +use super::blocks::plant::gourds::stem::StemBlock; use super::fluid::FluidBehaviour; use super::{ BrokenArgs, CanPlaceAtArgs, CanUpdateAtArgs, EmitsRedstonePowerArgs, ExplodeArgs, @@ -201,6 +203,8 @@ pub fn default_registry() -> Arc { manager.register(SkullBlock); manager.register(ChiseledBookshelfBlock); manager.register(LecternBlock); + manager.register(StemBlock); + manager.register(AttachedStemBlock); // Fire manager.register(SoulFireBlock);