use std::sync::Arc; use crate::{ block::{ BlockBehaviour, GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs, UseWithItemArgs, registry::BlockActionResult, }, entity::{Entity, item::ItemEntity}, world::World, }; use pumpkin_data::{ Block, BlockStateId, block_properties::{BlockProperties, ComposterLikeProperties}, composter_increase_chance::get_composter_increase_chance_from_item_id, entity::EntityType, item::Item, item_stack::ItemStack, world::WorldEvent, }; use pumpkin_inventory::screen_handler::InventoryPlayer; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{tick::TickPriority, world::BlockFlags}; use rand::RngExt; #[pumpkin_block("minecraft:composter")] pub struct ComposterBlock; impl BlockBehaviour for ComposterBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { { let state_id = args.world.get_block_state_id(args.position); let props = ComposterLikeProperties::from_state_id(state_id, args.block); if props.level == 8 { self.clear_composter(args.world, args.position, state_id, args.block); } BlockActionResult::Pass } } fn use_with_item(&self, args: UseWithItemArgs<'_>) -> BlockActionResult { { let state_id = args.world.get_block_state_id(args.position); let props = ComposterLikeProperties::from_state_id(state_id, args.block); let level = props.level; // Check if the composter is full if level == 8 { self.clear_composter(args.world, args.position, state_id, args.block); return BlockActionResult::Consume; } let item_stack = &mut *args.item_stack; let item_id = item_stack.item.id; // Check if the item is consumable by the composter let Some(chance) = get_composter_increase_chance_from_item_id(item_id) else { return BlockActionResult::Pass; }; // Consume one item from the stack (if in survival mode) if !args.player.has_infinite_materials() { item_stack.decrement(1); } // Determine if the composter level should increase if level < 7 && (level == 0 || rand::rng().random_bool(f64::from(chance))) { self.update_level_composter( args.world, args.position, state_id, args.block, level + 1, ); args.world .sync_world_event(WorldEvent::ComposterFill, *args.position, 1); } // Consume the item BlockActionResult::Consume } } fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { let state_id = args.world.get_block_state_id(args.position); let props = ComposterLikeProperties::from_state_id(state_id, args.block); let level = props.level; if level == 7 { self.update_level_composter(args.world, args.position, state_id, args.block, level + 1); } } fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option { { let props = ComposterLikeProperties::from_state_id(args.state.id, args.block); Some(props.level) } } } impl ComposterBlock { pub fn update_level_composter( &self, world: &Arc, location: &BlockPos, state_id: BlockStateId, block: &Block, level: u8, ) { let mut props = ComposterLikeProperties::from_state_id(state_id, block); props.level = level; world.set_block_state(location, props.to_state_id(block), BlockFlags::NOTIFY_ALL); if level == 7 { world.schedule_block_tick(block, *location, 20, TickPriority::Normal); } } pub fn clear_composter( &self, world: &Arc, location: &BlockPos, state_id: BlockStateId, block: &Block, ) { self.update_level_composter(world, location, state_id, block, 0); let item_position = { let mut rng = rand::rng(); location.to_centered_f64().add_raw( rng.random_range(-0.35..=0.35), rng.random_range(-0.35..=0.35) + 0.51, rng.random_range(-0.35..=0.35), ) }; let item_entity = ItemEntity::new( Entity::new(world.clone(), item_position, &EntityType::ITEM), ItemStack::new(1, &Item::BONE_MEAL), ); world.spawn_entity(Arc::new(item_entity)); } }