Pumpkin/pumpkin/src/block/blocks/candle_cakes.rs
Alexander Medvedev 5bfa6528da chore: changed block registration
only require block ids, should make bin size smaller and let the compiler better optimize, should also be faster performance wise
2026-01-23 23:32:17 +01:00

153 lines
5.0 KiB
Rust

use std::sync::Arc;
use pumpkin_data::{Block, item::Item};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::{GameMode, math::position::BlockPos};
use pumpkin_world::{
BlockStateId,
item::ItemStack,
tick::TickPriority,
world::{BlockAccessor, BlockFlags},
};
use crate::{
block::{
BlockBehaviour, BlockFuture, GetStateForNeighborUpdateArgs, NormalUseArgs,
OnScheduledTickArgs, UseWithItemArgs, blocks::cake::CakeBlock, registry::BlockActionResult,
},
entity::player::Player,
world::World,
};
const CANDLE_MAP: [(&Item, &Block); 17] = [
(&Item::CANDLE, &Block::CANDLE_CAKE),
(&Item::WHITE_CANDLE, &Block::WHITE_CANDLE_CAKE),
(&Item::ORANGE_CANDLE, &Block::ORANGE_CANDLE_CAKE),
(&Item::MAGENTA_CANDLE, &Block::MAGENTA_CANDLE_CAKE),
(&Item::LIGHT_BLUE_CANDLE, &Block::LIGHT_BLUE_CANDLE_CAKE),
(&Item::YELLOW_CANDLE, &Block::YELLOW_CANDLE_CAKE),
(&Item::LIME_CANDLE, &Block::LIME_CANDLE_CAKE),
(&Item::PINK_CANDLE, &Block::PINK_CANDLE_CAKE),
(&Item::GRAY_CANDLE, &Block::GRAY_CANDLE_CAKE),
(&Item::LIGHT_GRAY_CANDLE, &Block::LIGHT_GRAY_CANDLE_CAKE),
(&Item::CYAN_CANDLE, &Block::CYAN_CANDLE_CAKE),
(&Item::PURPLE_CANDLE, &Block::PURPLE_CANDLE_CAKE),
(&Item::BLUE_CANDLE, &Block::BLUE_CANDLE_CAKE),
(&Item::BROWN_CANDLE, &Block::BROWN_CANDLE_CAKE),
(&Item::GREEN_CANDLE, &Block::GREEN_CANDLE_CAKE),
(&Item::RED_CANDLE, &Block::RED_CANDLE_CAKE),
(&Item::BLACK_CANDLE, &Block::BLACK_CANDLE_CAKE),
];
#[must_use]
pub fn cake_from_candle(item: &Item) -> &'static Block {
CANDLE_MAP
.binary_search_by_key(&item.id, |(key, _)| key.id)
.map_or_else(
|_| panic!("Expected a candle item, got {}", item.id),
|index| CANDLE_MAP[index].1,
)
}
#[must_use]
pub fn candle_from_cake(block: &Block) -> &'static Item {
CANDLE_MAP
.binary_search_by_key(&block.id, |(_, value)| value.id)
.map_or_else(
|_| panic!("Expected a candle cake block, got {}", block.id),
|index| CANDLE_MAP[index].0,
)
}
#[pumpkin_block_from_tag("minecraft:candle_cakes")]
pub struct CandleCakeBlock;
impl CandleCakeBlock {
async fn consume_and_drop_candle(
block: &Block,
player: &Player,
location: &BlockPos,
world: &Arc<World>,
) -> BlockActionResult {
match player.gamemode.load() {
GameMode::Survival | GameMode::Adventure => {
if player.hunger_manager.level.load() >= 20 {
return BlockActionResult::Pass;
}
}
GameMode::Creative => {}
GameMode::Spectator => return BlockActionResult::Pass,
}
let candle_item = candle_from_cake(block);
let item_stack = ItemStack::new(1, candle_item);
world.drop_stack(location, item_stack).await;
world
.set_block_state(
location,
Block::CAKE.default_state.id,
BlockFlags::NOTIFY_ALL,
)
.await;
let (block, state) = world.get_block_and_state_id(location).await;
CakeBlock::consume_if_hungry(world, player, block, location, state).await
}
}
impl BlockBehaviour for CandleCakeBlock {
fn use_with_item<'a>(
&'a self,
args: UseWithItemArgs<'a>,
) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
let item_id = args.item_stack.lock().await.item.id;
match item_id {
id if id == Item::FIRE_CHARGE.id || id == Item::FLINT_AND_STEEL.id => {
BlockActionResult::Pass
} // Item::FIRE_CHARGE | Item::FLINT_AND_STEEL
_ => BlockActionResult::PassToDefaultBlockAction,
}
})
}
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
Box::pin(async move {
Self::consume_and_drop_candle(args.block, args.player, args.position, args.world).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).await {
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).await {
args.world
.schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal)
.await;
}
args.state_id
})
}
}
async fn can_place_at(world: &dyn BlockAccessor, position: &BlockPos) -> bool {
let state = world.get_block_state(&position.down()).await;
state.is_solid()
}