From 2ffc38c835466ee9b202448b456fb99860aac9c3 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Sun, 7 Jul 2024 23:01:34 +0300 Subject: [PATCH] Implement campfire & soul campfire (#4696) --- src/block/BlockTypeIds.php | 4 +- src/block/Campfire.php | 277 ++++++++++++++++++ src/block/SoulCampfire.php | 48 +++ src/block/VanillaBlocks.php | 8 + src/block/inventory/CampfireInventory.php | 40 +++ src/block/tile/Campfire.php | 143 +++++++++ src/block/tile/TileFactory.php | 2 +- .../CraftingManagerFromDataHelper.php | 3 +- src/crafting/FurnaceType.php | 6 + .../convert/BlockObjectToStateSerializer.php | 12 + .../BlockStateToObjectDeserializer.php | 10 + .../ItemSerializerDeserializerRegistrar.php | 2 + src/event/block/CampfireCookEvent.php | 63 ++++ src/item/StringToItemParser.php | 2 + src/network/mcpe/cache/CraftingDataCache.php | 2 + src/world/sound/CampfireSound.php | 35 +++ tests/phpstan/configs/actual-problems.neon | 5 + .../block_factory_consistency_check.json | 2 + 18 files changed, 661 insertions(+), 3 deletions(-) create mode 100644 src/block/Campfire.php create mode 100644 src/block/SoulCampfire.php create mode 100644 src/block/inventory/CampfireInventory.php create mode 100644 src/block/tile/Campfire.php create mode 100644 src/event/block/CampfireCookEvent.php create mode 100644 src/world/sound/CampfireSound.php diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index dec31eff1..59c358489 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -745,8 +745,10 @@ final class BlockTypeIds{ public const PITCHER_PLANT = 10715; public const PITCHER_CROP = 10716; public const DOUBLE_PITCHER_CROP = 10717; + public const CAMPFIRE = 10718; + public const SOUL_CAMPFIRE = 10719; - public const FIRST_UNUSED_BLOCK_ID = 10718; + public const FIRST_UNUSED_BLOCK_ID = 10720; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/Campfire.php b/src/block/Campfire.php new file mode 100644 index 000000000..ce759ee87 --- /dev/null +++ b/src/block/Campfire.php @@ -0,0 +1,277 @@ + ticks + * @phpstan-var array + */ + protected array $cookingTimes = []; + + protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ + $this->encodeFacingState($w); + $this->encodeLitState($w); + } + + public function readStateFromWorld() : Block{ + parent::readStateFromWorld(); + $tile = $this->position->getWorld()->getTile($this->position); + if($tile instanceof TileCampfire){ + $this->inventory = $tile->getInventory(); + $this->cookingTimes = $tile->getCookingTimes(); + }else{ + $this->inventory = new CampfireInventory($this->position); + } + + return $this; + } + + public function writeStateToWorld() : void{ + parent::writeStateToWorld(); + $tile = $this->position->getWorld()->getTile($this->position); + if($tile instanceof TileCampfire){ + $tile->setCookingTimes($this->cookingTimes); + } + } + + public function hasEntityCollision() : bool{ + return true; + } + + public function getLightLevel() : int{ + return $this->lit ? 15 : 0; + } + + public function isAffectedBySilkTouch() : bool{ + return true; + } + + public function getDropsForCompatibleTool(Item $item) : array{ + return [ + VanillaItems::CHARCOAL()->setCount(2) + ]; + } + + public function getSupportType(int $facing) : SupportType{ + return SupportType::NONE; + } + + protected function recalculateCollisionBoxes() : array{ + return [AxisAlignedBB::one()->trim(Facing::UP, 9 / 16)]; + } + + public function getInventory() : CampfireInventory{ + return $this->inventory; + } + + protected function getFurnaceType() : FurnaceType{ + return FurnaceType::CAMPFIRE; + } + + protected function getEntityCollisionDamage() : int{ + return 1; + } + + /** + * Sets the number of ticks during the item in the given slot has been cooked. + */ + public function setCookingTime(int $slot, int $time) : void{ + if($slot < 0 || $slot > 3){ + throw new \InvalidArgumentException("Slot must be in range 0-3"); + } + if($time < 0 || $time > $this->getFurnaceType()->getCookDurationTicks()){ + throw new \InvalidArgumentException("CookingTime must be in range 0-" . $this->getFurnaceType()->getCookDurationTicks()); + } + $this->cookingTimes[$slot] = $time; + } + + /** + * Returns the number of ticks during the item in the given slot has been cooked. + */ + public function getCookingTime(int $slot) : int{ + return $this->cookingTimes[$slot] ?? 0; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if($this->getSide(Facing::DOWN) instanceof Campfire){ + return false; + } + if($player !== null){ + $this->facing = $player->getHorizontalFacing(); + } + $this->lit = true; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if(!$this->lit){ + if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE){ + $item->pop(); + $this->ignite(); + $this->position->getWorld()->addSound($this->position, new BlazeShootSound()); + return true; + }elseif($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){ + if($item instanceof Durable){ + $item->applyDamage(1); + } + $this->ignite(); + return true; + } + }elseif($item instanceof Shovel){ + $item->applyDamage(1); + $this->extinguish(); + return true; + } + + if($this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($this->getFurnaceType())->match($item) !== null){ + $ingredient = clone $item; + $ingredient->setCount(1); + if(count($this->inventory->addItem($ingredient)) === 0){ + $item->pop(); + $this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound()); + return true; + } + } + return false; + } + + public function onNearbyBlockChange() : void{ + if($this->lit && $this->getSide(Facing::UP)->getTypeId() === BlockTypeIds::WATER){ + $this->extinguish(); + //TODO: Waterlogging + } + } + + public function onEntityInside(Entity $entity) : bool{ + if(!$this->lit){ + if($entity->isOnFire()){ + $this->ignite(); + return false; + } + }elseif($entity instanceof Living){ + $entity->attack(new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, $this->getEntityCollisionDamage())); + } + return true; + } + + public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{ + if($this->lit && $projectile instanceof SplashPotion && $projectile->getPotionType() === PotionType::WATER){ + $this->extinguish(); + } + } + + public function onScheduledUpdate() : void{ + if($this->lit){ + $items = $this->inventory->getContents(); + $furnaceType = $this->getFurnaceType(); + $maxCookDuration = $furnaceType->getCookDurationTicks(); + foreach($items as $slot => $item){ + $this->setCookingTime($slot, min($maxCookDuration, $this->getCookingTime($slot) + self::UPDATE_INTERVAL_TICKS)); + if($this->getCookingTime($slot) >= $maxCookDuration){ + $result = + ($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->getFurnaceRecipeManager($furnaceType)->match($item)) instanceof FurnaceRecipe ? + $recipe->getResult() : + VanillaItems::AIR(); + + $ev = new CampfireCookEvent($this, $slot, $item, $result); + $ev->call(); + + if ($ev->isCancelled()){ + continue; + } + + $this->inventory->setItem($slot, VanillaItems::AIR()); + $this->setCookingTime($slot, 0); + $this->position->getWorld()->dropItem($this->position->add(0.5, 1, 0.5), $ev->getResult()); + } + } + if(count($items) > 0){ + $this->position->getWorld()->setBlock($this->position, $this); + } + if(mt_rand(1, 6) === 1){ + $this->position->getWorld()->addSound($this->position, $furnaceType->getCookSound()); + } + $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS); + } + } + + private function extinguish() : void{ + $this->position->getWorld()->addSound($this->position, new FireExtinguishSound()); + $this->position->getWorld()->setBlock($this->position, $this->setLit(false)); + } + + private function ignite() : void{ + $this->position->getWorld()->addSound($this->position, new FlintSteelSound()); + $this->position->getWorld()->setBlock($this->position, $this->setLit(true)); + $this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, self::UPDATE_INTERVAL_TICKS); + } +} diff --git a/src/block/SoulCampfire.php b/src/block/SoulCampfire.php new file mode 100644 index 000000000..a9c8fc918 --- /dev/null +++ b/src/block/SoulCampfire.php @@ -0,0 +1,48 @@ +lit ? 10 : 0; + } + + public function getDropsForCompatibleTool(Item $item) : array{ + return [ + VanillaBlocks::SOUL_SOIL()->asItem() + ]; + } + + protected function getEntityCollisionDamage() : int{ + return 2; + } + + protected function getFurnaceType() : FurnaceType{ + return FurnaceType::SOUL_CAMPFIRE; + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 9c0e7d3b7..7733f4359 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -36,6 +36,7 @@ use pocketmine\block\tile\Bed as TileBed; use pocketmine\block\tile\Bell as TileBell; use pocketmine\block\tile\BlastFurnace as TileBlastFurnace; use pocketmine\block\tile\BrewingStand as TileBrewingStand; +use pocketmine\block\tile\Campfire as TileCampfire; use pocketmine\block\tile\Cauldron as TileCauldron; use pocketmine\block\tile\Chest as TileChest; use pocketmine\block\tile\ChiseledBookshelf as TileChiseledBookshelf; @@ -154,6 +155,7 @@ use function strtolower; * @method static CakeWithCandle CAKE_WITH_CANDLE() * @method static CakeWithDyedCandle CAKE_WITH_DYED_CANDLE() * @method static Opaque CALCITE() + * @method static Campfire CAMPFIRE() * @method static Candle CANDLE() * @method static Carpet CARPET() * @method static Carrot CARROTS() @@ -685,6 +687,7 @@ use function strtolower; * @method static Slab SMOOTH_STONE_SLAB() * @method static Snow SNOW() * @method static SnowLayer SNOW_LAYER() + * @method static SoulCampfire SOUL_CAMPFIRE() * @method static SoulFire SOUL_FIRE() * @method static Lantern SOUL_LANTERN() * @method static SoulSand SOUL_SAND() @@ -826,6 +829,11 @@ final class VanillaBlocks{ self::register("brown_mushroom", new BrownMushroom(new BID(Ids::BROWN_MUSHROOM), "Brown Mushroom", new Info(BreakInfo::instant(), [Tags::POTTABLE_PLANTS]))); self::register("cactus", new Cactus(new BID(Ids::CACTUS), "Cactus", new Info(new BreakInfo(0.4), [Tags::POTTABLE_PLANTS]))); self::register("cake", new Cake(new BID(Ids::CAKE), "Cake", new Info(new BreakInfo(0.5)))); + + $campfireBreakInfo = new Info(BreakInfo::axe(2.0)); + self::register("campfire", new Campfire(new BID(Ids::CAMPFIRE, TileCampfire::class), "Campfire", $campfireBreakInfo)); + self::register("soul_campfire", new SoulCampfire(new BID(Ids::SOUL_CAMPFIRE, TileCampfire::class), "Soul Campfire", $campfireBreakInfo)); + self::register("carrots", new Carrot(new BID(Ids::CARROTS), "Carrot Block", new Info(BreakInfo::instant()))); $chestBreakInfo = new Info(BreakInfo::axe(2.5)); diff --git a/src/block/inventory/CampfireInventory.php b/src/block/inventory/CampfireInventory.php new file mode 100644 index 000000000..ae762473e --- /dev/null +++ b/src/block/inventory/CampfireInventory.php @@ -0,0 +1,40 @@ +holder = $holder; + parent::__construct(4); + } + + public function getMaxStackSize() : int{ + return 1; + } +} diff --git a/src/block/tile/Campfire.php b/src/block/tile/Campfire.php new file mode 100644 index 000000000..ad4a193d7 --- /dev/null +++ b/src/block/tile/Campfire.php @@ -0,0 +1,143 @@ + */ + private array $cookingTimes = []; + + public function __construct(World $world, Vector3 $pos){ + parent::__construct($world, $pos); + $this->inventory = new CampfireInventory($this->position); + $this->inventory->getListeners()->add(CallbackInventoryListener::onAnyChange( + static function(Inventory $unused) use ($world, $pos) : void{ + $block = $world->getBlock($pos); + if($block instanceof BlockCampfire){ + $world->setBlock($pos, $block); + } + }) + ); + } + + public function getInventory() : CampfireInventory{ + return $this->inventory; + } + + public function getRealInventory() : CampfireInventory{ + return $this->inventory; + } + + /** + * @return int[] + * @phpstan-return array + */ + public function getCookingTimes() : array{ + return $this->cookingTimes; + } + + /** + * @param int[] $cookingTimes + * @phpstan-param array $cookingTimes + */ + public function setCookingTimes(array $cookingTimes) : void{ + $this->cookingTimes = $cookingTimes; + } + + public function readSaveData(CompoundTag $nbt) : void{ + $items = []; + $listeners = $this->inventory->getListeners()->toArray(); + $this->inventory->getListeners()->remove(...$listeners); //prevent any events being fired by initialization + + foreach([ + [0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME], + [1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME], + [2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME], + [3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME], + ] as [$slot, $itemTag, $cookingTimeTag]){ + if(($tag = $nbt->getTag($itemTag)) instanceof CompoundTag){ + $items[$slot] = Item::nbtDeserialize($tag); + } + if(($tag = $nbt->getTag($cookingTimeTag)) instanceof IntTag){ + $this->cookingTimes[$slot] = $tag->getValue(); + } + } + $this->inventory->setContents($items); + $this->inventory->getListeners()->add(...$listeners); + } + + protected function writeSaveData(CompoundTag $nbt) : void{ + foreach([ + [0, self::TAG_FIRST_INPUT_ITEM, self::TAG_FIRST_COOKING_TIME], + [1, self::TAG_SECOND_INPUT_ITEM, self::TAG_SECOND_COOKING_TIME], + [2, self::TAG_THIRD_INPUT_ITEM, self::TAG_THIRD_COOKING_TIME], + [3, self::TAG_FOURTH_INPUT_ITEM, self::TAG_FOURTH_COOKING_TIME], + ] as [$slot, $itemTag, $cookingTimeTag]){ + $item = $this->inventory->getItem($slot); + if(!$item->isNull()){ + $nbt->setTag($itemTag, $item->nbtSerialize()); + if(isset($this->cookingTimes[$slot])){ + $nbt->setInt($cookingTimeTag, $this->cookingTimes[$slot]); + } + } + } + } + + protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ + foreach([ + 0 => self::TAG_FIRST_INPUT_ITEM, + 1 => self::TAG_SECOND_INPUT_ITEM, + 2 => self::TAG_THIRD_INPUT_ITEM, + 3 => self::TAG_FOURTH_INPUT_ITEM + ] as $slot => $tag){ + $item = $this->inventory->getItem($slot); + if(!$item->isNull()){ + $nbt->setTag($tag, TypeConverter::getInstance()->getItemTranslator()->toNetworkNbt($item)); + } + } + } +} diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php index 6e87b72ac..515dd8c63 100644 --- a/src/block/tile/TileFactory.php +++ b/src/block/tile/TileFactory.php @@ -57,6 +57,7 @@ final class TileFactory{ $this->register(Bell::class, ["Bell", "minecraft:bell"]); $this->register(BlastFurnace::class, ["BlastFurnace", "minecraft:blast_furnace"]); $this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]); + $this->register(Campfire::class, ["Campfire", "minecraft:campfire"]); $this->register(Cauldron::class, ["Cauldron", "minecraft:cauldron"]); $this->register(Chest::class, ["Chest", "minecraft:chest"]); $this->register(ChiseledBookshelf::class, ["ChiseledBookshelf", "minecraft:chiseled_bookshelf"]); @@ -79,7 +80,6 @@ final class TileFactory{ $this->register(MobHead::class, ["Skull", "minecraft:skull"]); $this->register(GlowingItemFrame::class, ["GlowItemFrame"]); - //TODO: Campfire //TODO: ChalkboardBlock //TODO: ChemistryTable //TODO: CommandBlock diff --git a/src/crafting/CraftingManagerFromDataHelper.php b/src/crafting/CraftingManagerFromDataHelper.php index 6e5bbcd1f..616c2a4bd 100644 --- a/src/crafting/CraftingManagerFromDataHelper.php +++ b/src/crafting/CraftingManagerFromDataHelper.php @@ -275,7 +275,8 @@ final class CraftingManagerFromDataHelper{ "furnace" => FurnaceType::FURNACE, "blast_furnace" => FurnaceType::BLAST_FURNACE, "smoker" => FurnaceType::SMOKER, - //TODO: campfire + "campfire" => FurnaceType::CAMPFIRE, + "soul_campfire" => FurnaceType::SOUL_CAMPFIRE, default => null }; if($furnaceType === null){ diff --git a/src/crafting/FurnaceType.php b/src/crafting/FurnaceType.php index 0ce5b72ce..89834c821 100644 --- a/src/crafting/FurnaceType.php +++ b/src/crafting/FurnaceType.php @@ -25,6 +25,7 @@ namespace pocketmine\crafting; use pocketmine\utils\LegacyEnumShimTrait; use pocketmine\world\sound\BlastFurnaceSound; +use pocketmine\world\sound\CampfireSound; use pocketmine\world\sound\FurnaceSound; use pocketmine\world\sound\SmokerSound; use pocketmine\world\sound\Sound; @@ -35,8 +36,10 @@ use function spl_object_id; * These are retained for backwards compatibility only. * * @method static FurnaceType BLAST_FURNACE() + * @method static FurnaceType CAMPFIRE() * @method static FurnaceType FURNACE() * @method static FurnaceType SMOKER() + * @method static FurnaceType SOUL_CAMPFIRE() * * @phpstan-type TMetadata array{0: int, 1: Sound} */ @@ -46,6 +49,8 @@ enum FurnaceType{ case FURNACE; case BLAST_FURNACE; case SMOKER; + case CAMPFIRE; + case SOUL_CAMPFIRE; /** * @phpstan-return TMetadata @@ -58,6 +63,7 @@ enum FurnaceType{ self::FURNACE => [200, new FurnaceSound()], self::BLAST_FURNACE => [100, new BlastFurnaceSound()], self::SMOKER => [100, new SmokerSound()], + self::CAMPFIRE, self::SOUL_CAMPFIRE => [600, new CampfireSound()] }; } diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index ab98e75f3..a8ca150ba 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -43,6 +43,7 @@ use pocketmine\block\Cactus; use pocketmine\block\Cake; use pocketmine\block\CakeWithCandle; use pocketmine\block\CakeWithDyedCandle; +use pocketmine\block\Campfire; use pocketmine\block\Candle; use pocketmine\block\Carpet; use pocketmine\block\Carrot; @@ -124,6 +125,7 @@ use pocketmine\block\SimplePressurePlate; use pocketmine\block\Slab; use pocketmine\block\SmallDripleaf; use pocketmine\block\SnowLayer; +use pocketmine\block\SoulCampfire; use pocketmine\block\Sponge; use pocketmine\block\StainedGlass; use pocketmine\block\StainedGlassPane; @@ -1160,6 +1162,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return Writer::create(Ids::CAKE) ->writeInt(StateNames::BITE_COUNTER, $block->getBites()); }); + $this->map(Blocks::CAMPFIRE(), function(Campfire $block) : Writer{ + return Writer::create(Ids::CAMPFIRE) + ->writeCardinalHorizontalFacing($block->getFacing()) + ->writeBool(StateNames::EXTINGUISHED, !$block->isLit()); + }); $this->map(Blocks::CARROTS(), fn(Carrot $block) => Helper::encodeCrops($block, new Writer(Ids::CARROTS))); $this->map(Blocks::CARVED_PUMPKIN(), function(CarvedPumpkin $block) : Writer{ return Writer::create(Ids::CARVED_PUMPKIN) @@ -1638,6 +1645,11 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ ->writeBool(StateNames::COVERED_BIT, false) ->writeInt(StateNames::HEIGHT, $block->getLayers() - 1); }); + $this->map(Blocks::SOUL_CAMPFIRE(), function(SoulCampfire $block) : Writer{ + return Writer::create(Ids::SOUL_CAMPFIRE) + ->writeCardinalHorizontalFacing($block->getFacing()) + ->writeBool(StateNames::EXTINGUISHED, !$block->isLit()); + }); $this->map(Blocks::SOUL_FIRE(), function() : Writer{ return Writer::create(Ids::SOUL_FIRE) ->writeInt(StateNames::AGE, 0); //useless for soul fire, we don't track it diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 4c9ec4b79..40478f197 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -1039,6 +1039,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return Blocks::CAKE() ->setBites($in->readBoundedInt(StateNames::BITE_COUNTER, 0, 6)); }); + $this->map(Ids::CAMPFIRE, function(Reader $in) : Block{ + return Blocks::CAMPFIRE() + ->setFacing($in->readCardinalHorizontalFacing()) + ->setLit(!$in->readBool(StateNames::EXTINGUISHED)); + }); $this->map(Ids::CARROTS, fn(Reader $in) => Helper::decodeCrops(Blocks::CARROTS(), $in)); $this->map(Ids::CARVED_PUMPKIN, function(Reader $in) : Block{ return Blocks::CARVED_PUMPKIN() @@ -1525,6 +1530,11 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ $in->ignored(StateNames::COVERED_BIT); //seems to be useless return Blocks::SNOW_LAYER()->setLayers($in->readBoundedInt(StateNames::HEIGHT, 0, 7) + 1); }); + $this->map(Ids::SOUL_CAMPFIRE, function(Reader $in) : Block{ + return Blocks::SOUL_CAMPFIRE() + ->setFacing($in->readCardinalHorizontalFacing()) + ->setLit(!$in->readBool(StateNames::EXTINGUISHED)); + }); $this->map(Ids::SOUL_FIRE, function(Reader $in) : Block{ $in->ignored(StateNames::AGE); //this is useless for soul fire, since it doesn't have the logic associated return Blocks::SOUL_FIRE(); diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 7a720559e..de9b5ae5e 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -133,6 +133,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Block(Ids::BIRCH_DOOR, Blocks::BIRCH_DOOR()); $this->map1to1Block(Ids::BREWING_STAND, Blocks::BREWING_STAND()); $this->map1to1Block(Ids::CAKE, Blocks::CAKE()); + $this->map1to1Block(Ids::CAMPFIRE, Blocks::CAMPFIRE()); $this->map1to1Block(Ids::CAULDRON, Blocks::CAULDRON()); $this->map1to1Block(Ids::CHAIN, Blocks::CHAIN()); $this->map1to1Block(Ids::CHERRY_DOOR, Blocks::CHERRY_DOOR()); @@ -148,6 +149,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Block(Ids::MANGROVE_DOOR, Blocks::MANGROVE_DOOR()); $this->map1to1Block(Ids::NETHER_WART, Blocks::NETHER_WART()); $this->map1to1Block(Ids::REPEATER, Blocks::REDSTONE_REPEATER()); + $this->map1to1Block(Ids::SOUL_CAMPFIRE, Blocks::SOUL_CAMPFIRE()); $this->map1to1Block(Ids::SPRUCE_DOOR, Blocks::SPRUCE_DOOR()); $this->map1to1Block(Ids::SUGAR_CANE, Blocks::SUGARCANE()); $this->map1to1Block(Ids::WARPED_DOOR, Blocks::WARPED_DOOR()); diff --git a/src/event/block/CampfireCookEvent.php b/src/event/block/CampfireCookEvent.php new file mode 100644 index 000000000..3762f5848 --- /dev/null +++ b/src/event/block/CampfireCookEvent.php @@ -0,0 +1,63 @@ +input = clone $input; + } + + public function getCampfire() : Campfire{ + return $this->campfire; + } + + public function getSlot() : int{ + return $this->slot; + } + + public function getInput() : Item{ + return $this->input; + } + + public function getResult() : Item{ + return $this->result; + } + + public function setResult(Item $result) : void{ + $this->result = $result; + } +} diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 91fd2ab4a..6a1c1d2e3 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -205,6 +205,7 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("cake", fn() => Blocks::CAKE()); $result->registerBlock("cake_block", fn() => Blocks::CAKE()); $result->registerBlock("calcite", fn() => Blocks::CALCITE()); + $result->registerBlock("campfire", fn() => Blocks::CAMPFIRE()); $result->registerBlock("candle", fn() => Blocks::CANDLE()); $result->registerBlock("carpet", fn() => Blocks::CARPET()); $result->registerBlock("carrot_block", fn() => Blocks::CARROTS()); @@ -1003,6 +1004,7 @@ final class StringToItemParser extends StringToTParser{ $result->registerBlock("snow", fn() => Blocks::SNOW()); $result->registerBlock("snow_block", fn() => Blocks::SNOW()); $result->registerBlock("snow_layer", fn() => Blocks::SNOW_LAYER()); + $result->registerBlock("soul_campfire", fn() => Blocks::SOUL_CAMPFIRE()); $result->registerBlock("soul_lantern", fn() => Blocks::SOUL_LANTERN()); $result->registerBlock("soul_sand", fn() => Blocks::SOUL_SAND()); $result->registerBlock("soul_soil", fn() => Blocks::SOUL_SOIL()); diff --git a/src/network/mcpe/cache/CraftingDataCache.php b/src/network/mcpe/cache/CraftingDataCache.php index 29f37590e..14523f74c 100644 --- a/src/network/mcpe/cache/CraftingDataCache.php +++ b/src/network/mcpe/cache/CraftingDataCache.php @@ -130,6 +130,8 @@ final class CraftingDataCache{ FurnaceType::FURNACE => FurnaceRecipeBlockName::FURNACE, FurnaceType::BLAST_FURNACE => FurnaceRecipeBlockName::BLAST_FURNACE, FurnaceType::SMOKER => FurnaceRecipeBlockName::SMOKER, + FurnaceType::CAMPFIRE => FurnaceRecipeBlockName::CAMPFIRE, + FurnaceType::SOUL_CAMPFIRE => FurnaceRecipeBlockName::SOUL_CAMPFIRE }; foreach($manager->getFurnaceRecipeManager($furnaceType)->getAll() as $recipe){ $input = $converter->coreRecipeIngredientToNet($recipe->getInput())->getDescriptor(); diff --git a/src/world/sound/CampfireSound.php b/src/world/sound/CampfireSound.php new file mode 100644 index 000000000..7e01342ef --- /dev/null +++ b/src/world/sound/CampfireSound.php @@ -0,0 +1,35 @@ +