Pumpkin/crates/pumpkin/src/block/blocks/cake.rs
2026-08-08 13:44:51 +02:00

218 lines
7.3 KiB
Rust

use std::sync::Arc;
use crate::{
block::{
BlockBehaviour, BlockFuture, CanPlaceAtArgs, GetComparatorOutputArgs,
GetStateForNeighborUpdateArgs, NormalUseArgs, OnPlaceArgs, OnScheduledTickArgs,
UseWithItemArgs, blocks::candle_cakes::cake_from_candle, registry::BlockActionResult,
},
entity::player::Player,
world::World,
};
use pumpkin_data::item::Item;
use pumpkin_data::{
Block, BlockStateId,
block_properties::{BlockProperties, CakeLikeProperties},
sound::{Sound, SoundCategory},
};
use pumpkin_macros::pumpkin_block;
use pumpkin_util::{GameMode, math::position::BlockPos};
use pumpkin_world::{
tick::TickPriority,
world::{BlockAccessor, BlockFlags},
};
use rand::{RngExt, rng};
#[pumpkin_block("minecraft:cake")]
pub struct CakeBlock;
impl CakeBlock {
pub async fn consume_if_hungry(
world: &Arc<World>,
player: &Player,
block: &Block,
location: &BlockPos,
state_id: BlockStateId,
) -> BlockActionResult {
match player.gamemode.load() {
GameMode::Survival | GameMode::Adventure => {
let hunger_level = player.hunger_manager.level.load();
if hunger_level >= 20 {
return BlockActionResult::Pass;
}
player.hunger_manager.level.store(20.min(hunger_level + 2));
player
.hunger_manager
.saturation
.store(player.hunger_manager.saturation.load() + 0.4);
player.send_health().await;
}
GameMode::Creative | GameMode::Spectator => {}
}
let mut properties = CakeLikeProperties::from_state_id(state_id, block);
match properties.bites {
0..=5 => {
player
.increment_stat(
pumpkin_data::statistic::StatisticCategory::Custom,
pumpkin_data::statistic::CustomStatistic::EatCakeSlice as i32,
1,
)
.await;
properties.bites += 1;
world
.set_block_state(
location,
properties.to_state_id(block),
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
}
6 => {
player
.increment_stat(
pumpkin_data::statistic::StatisticCategory::Custom,
pumpkin_data::statistic::CustomStatistic::EatCakeSlice as i32,
1,
)
.await;
world
.set_block_state(
location,
Block::AIR.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
BlockActionResult::Consume
}
_ => BlockActionResult::Pass,
}
}
}
impl BlockBehaviour for CakeBlock {
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if !can_place_at(args.world, args.position) {
return Block::AIR.default_state.id;
}
Block::CAKE.default_state.id
})
}
fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool {
can_place_at(args.block_accessor, args.position)
}
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position);
let properties = CakeLikeProperties::from_state_id(state_id, args.block);
let item = args.item_stack.item;
match item.id {
id if (Item::CANDLE.id..=Item::BLACK_CANDLE.id).contains(&id) => {
if properties.bites != 0 {
return Self::consume_if_hungry(
args.world,
args.player,
args.block,
args.position,
state_id,
)
.await;
}
if args.player.gamemode.load() != GameMode::Creative {
args.item_stack.decrement(1);
}
args.world
.set_block_state(
args.position,
cake_from_candle(item).default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
let seed: f64 = rng().random();
args.player
.play_sound(
Sound::BlockCakeAddCandle as u16,
SoundCategory::Blocks,
&args.position.to_f64(),
1.0,
1.0,
seed,
)
.await;
BlockActionResult::Consume
}
_ => {
return Self::consume_if_hungry(
args.world,
args.player,
args.block,
args.position,
state_id,
)
.await;
}
}
})
}
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position);
Self::consume_if_hungry(args.world, args.player, args.block, args.position, state_id)
.await
})
}
fn on_scheduled_tick<'a>(&'a self, args: OnScheduledTickArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if !can_place_at(args.world.as_ref(), args.position) {
args.world
.break_block(args.position, None, BlockFlags::empty())
.await;
}
})
}
fn get_state_for_neighbor_update<'a>(
&'a self,
args: GetStateForNeighborUpdateArgs<'a>,
) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
if !can_place_at(args.world, args.position) {
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal);
}
args.state_id
})
}
fn get_comparator_output<'a>(
&'a self,
args: GetComparatorOutputArgs<'a>,
) -> BlockFuture<'a, Option<u8>> {
Box::pin(async move {
let state_id = args.world.get_block_state_id(args.position);
let properties = CakeLikeProperties::from_state_id(state_id, args.block);
if properties.bites <= 6 {
Some((7 - properties.bites) * 2)
} else {
Some(0)
}
})
}
}
fn can_place_at(world: &dyn BlockAccessor, position: &BlockPos) -> bool {
let state = world.get_block_state(&position.down());
state.is_solid()
}