mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-07-13 21:35:21 +00:00
feat: Implement Brewing (#4413)
The following API constants have been added: - tile\BrewingStand::BREW_TIME_TICKS The following public API methods have been added: - utils\BrewingStandSlot->getSlotNumber() : int - CraftingManager->getPotionTypeRecipes() : array<string, array<string, PotionTypeRecipe>> - CraftingManager->getPotionContainerChangeRecipes() : array<int, array<string, PotionContainerChangeRecipe>> - CraftingManager->registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void - CraftingManager->registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void The following classes have been added: - BrewingRecipe - PotionTypeRecipe - PotionContainerChangeRecipe - BrewItemEvent - BrewingFuelUseEvent - PotionFinishBrewingSound
This commit is contained in:
parent
b2630a0920
commit
e0da99a973
@ -125,6 +125,24 @@ class BrewingStand extends Transparent{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onScheduledUpdate() : void{
|
public function onScheduledUpdate() : void{
|
||||||
//TODO
|
$brewing = $this->position->getWorld()->getTile($this->position);
|
||||||
|
if($brewing instanceof TileBrewingStand){
|
||||||
|
if($brewing->onUpdate()){
|
||||||
|
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$changed = false;
|
||||||
|
foreach(BrewingStandSlot::getAll() as $slot){
|
||||||
|
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
|
||||||
|
if($occupied !== $this->hasSlot($slot)){
|
||||||
|
$this->setSlot($slot, $occupied);
|
||||||
|
$changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($changed){
|
||||||
|
$this->position->getWorld()->setBlock($this->position, $this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,16 +24,29 @@ declare(strict_types=1);
|
|||||||
namespace pocketmine\block\tile;
|
namespace pocketmine\block\tile;
|
||||||
|
|
||||||
use pocketmine\block\inventory\BrewingStandInventory;
|
use pocketmine\block\inventory\BrewingStandInventory;
|
||||||
|
use pocketmine\crafting\BrewingRecipe;
|
||||||
|
use pocketmine\event\block\BrewingFuelUseEvent;
|
||||||
|
use pocketmine\event\block\BrewItemEvent;
|
||||||
use pocketmine\inventory\CallbackInventoryListener;
|
use pocketmine\inventory\CallbackInventoryListener;
|
||||||
use pocketmine\inventory\Inventory;
|
use pocketmine\inventory\Inventory;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\item\VanillaItems;
|
||||||
use pocketmine\math\Vector3;
|
use pocketmine\math\Vector3;
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
|
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
|
||||||
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\world\sound\PotionFinishBrewingSound;
|
||||||
use pocketmine\world\World;
|
use pocketmine\world\World;
|
||||||
|
use function array_map;
|
||||||
|
use function count;
|
||||||
|
|
||||||
class BrewingStand extends Spawnable implements Container, Nameable{
|
class BrewingStand extends Spawnable implements Container, Nameable{
|
||||||
|
use NameableTrait {
|
||||||
|
addAdditionalSpawnData as addNameSpawnData;
|
||||||
|
}
|
||||||
use ContainerTrait;
|
use ContainerTrait;
|
||||||
use NameableTrait;
|
|
||||||
|
public const BREW_TIME_TICKS = 400; // Brew time in ticks
|
||||||
|
|
||||||
private const TAG_BREW_TIME = "BrewTime"; //TAG_Short
|
private const TAG_BREW_TIME = "BrewTime"; //TAG_Short
|
||||||
private const TAG_BREW_TIME_PE = "CookTime"; //TAG_Short
|
private const TAG_BREW_TIME_PE = "CookTime"; //TAG_Short
|
||||||
@ -41,15 +54,11 @@ class BrewingStand extends Spawnable implements Container, Nameable{
|
|||||||
private const TAG_REMAINING_FUEL_TIME = "Fuel"; //TAG_Byte
|
private const TAG_REMAINING_FUEL_TIME = "Fuel"; //TAG_Byte
|
||||||
private const TAG_REMAINING_FUEL_TIME_PE = "FuelAmount"; //TAG_Short
|
private const TAG_REMAINING_FUEL_TIME_PE = "FuelAmount"; //TAG_Short
|
||||||
|
|
||||||
/** @var BrewingStandInventory */
|
private BrewingStandInventory $inventory;
|
||||||
private $inventory;
|
|
||||||
|
|
||||||
/** @var int */
|
private int $brewTime = 0;
|
||||||
private $brewTime = 0;
|
private int $maxFuelTime = 0;
|
||||||
/** @var int */
|
private int $remainingFuelTime = 0;
|
||||||
private $maxFuelTime = 0;
|
|
||||||
/** @var int */
|
|
||||||
private $remainingFuelTime = 0;
|
|
||||||
|
|
||||||
public function __construct(World $world, Vector3 $pos){
|
public function __construct(World $world, Vector3 $pos){
|
||||||
parent::__construct($world, $pos);
|
parent::__construct($world, $pos);
|
||||||
@ -83,6 +92,14 @@ class BrewingStand extends Spawnable implements Container, Nameable{
|
|||||||
$nbt->setShort(self::TAG_REMAINING_FUEL_TIME_PE, $this->remainingFuelTime);
|
$nbt->setShort(self::TAG_REMAINING_FUEL_TIME_PE, $this->remainingFuelTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function addAdditionalSpawnData(CompoundTag $nbt) : void{
|
||||||
|
$this->addNameSpawnData($nbt);
|
||||||
|
|
||||||
|
$nbt->setShort(self::TAG_BREW_TIME_PE, $this->brewTime);
|
||||||
|
$nbt->setShort(self::TAG_MAX_FUEL_TIME, $this->maxFuelTime);
|
||||||
|
$nbt->setShort(self::TAG_REMAINING_FUEL_TIME_PE, $this->remainingFuelTime);
|
||||||
|
}
|
||||||
|
|
||||||
public function getDefaultName() : string{
|
public function getDefaultName() : string{
|
||||||
return "Brewing Stand";
|
return "Brewing Stand";
|
||||||
}
|
}
|
||||||
@ -108,4 +125,135 @@ class BrewingStand extends Spawnable implements Container, Nameable{
|
|||||||
public function getRealInventory(){
|
public function getRealInventory(){
|
||||||
return $this->inventory;
|
return $this->inventory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function checkFuel(Item $item) : void{
|
||||||
|
$ev = new BrewingFuelUseEvent($this);
|
||||||
|
if(!$item->equals(VanillaItems::BLAZE_POWDER(), true, false)){
|
||||||
|
$ev->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
$ev->call();
|
||||||
|
if($ev->isCancelled()){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$item->pop();
|
||||||
|
$this->inventory->setItem(BrewingStandInventory::SLOT_FUEL, $item);
|
||||||
|
|
||||||
|
$this->maxFuelTime = $this->remainingFuelTime = $ev->getFuelTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return BrewingRecipe[]
|
||||||
|
* @phpstan-return array<int, BrewingRecipe>
|
||||||
|
*/
|
||||||
|
private function getBrewableRecipes() : array{
|
||||||
|
if($this->inventory->getItem(BrewingStandInventory::SLOT_INGREDIENT)->isNull()){
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$recipes = [];
|
||||||
|
foreach([BrewingStandInventory::SLOT_BOTTLE_LEFT, BrewingStandInventory::SLOT_BOTTLE_MIDDLE, BrewingStandInventory::SLOT_BOTTLE_RIGHT] as $slot){
|
||||||
|
$input = $this->inventory->getItem($slot);
|
||||||
|
if($input->isNull()){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(($recipe = $this->position->getWorld()->getServer()->getCraftingManager()->matchBrewingRecipe($input, $this->inventory->getItem(BrewingStandInventory::SLOT_INGREDIENT))) !== null){
|
||||||
|
$recipes[$slot] = $recipe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $recipes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onUpdate() : bool{
|
||||||
|
if($this->closed){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->timings->startTiming();
|
||||||
|
|
||||||
|
$prevBrewTime = $this->brewTime;
|
||||||
|
$prevRemainingFuelTime = $this->remainingFuelTime;
|
||||||
|
$prevMaxFuelTime = $this->maxFuelTime;
|
||||||
|
|
||||||
|
$ret = false;
|
||||||
|
|
||||||
|
$fuel = $this->inventory->getItem(BrewingStandInventory::SLOT_FUEL);
|
||||||
|
$ingredient = $this->inventory->getItem(BrewingStandInventory::SLOT_INGREDIENT);
|
||||||
|
|
||||||
|
$recipes = $this->getBrewableRecipes();
|
||||||
|
$canBrew = count($recipes) !== 0;
|
||||||
|
|
||||||
|
if($this->remainingFuelTime <= 0 && $canBrew){
|
||||||
|
$this->checkFuel($fuel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->remainingFuelTime > 0){
|
||||||
|
if($canBrew){
|
||||||
|
if($this->brewTime === 0){
|
||||||
|
$this->brewTime = self::BREW_TIME_TICKS;
|
||||||
|
--$this->remainingFuelTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
--$this->brewTime;
|
||||||
|
|
||||||
|
if($this->brewTime <= 0){
|
||||||
|
$anythingBrewed = false;
|
||||||
|
foreach($recipes as $slot => $recipe){
|
||||||
|
$input = $this->inventory->getItem($slot);
|
||||||
|
$output = $recipe->getResultFor($input);
|
||||||
|
if($output === null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ev = new BrewItemEvent($this, $slot, $input, $output, $recipe);
|
||||||
|
$ev->call();
|
||||||
|
if($ev->isCancelled()){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->inventory->setItem($slot, $ev->getResult());
|
||||||
|
$anythingBrewed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($anythingBrewed){
|
||||||
|
$this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new PotionFinishBrewingSound());
|
||||||
|
}
|
||||||
|
|
||||||
|
$ingredient->pop();
|
||||||
|
$this->inventory->setItem(BrewingStandInventory::SLOT_INGREDIENT, $ingredient);
|
||||||
|
|
||||||
|
$this->brewTime = 0;
|
||||||
|
}else{
|
||||||
|
$ret = true;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$this->brewTime = 0;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
$this->brewTime = $this->remainingFuelTime = $this->maxFuelTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewers = array_map(fn(Player $p) => $p->getNetworkSession()->getInvManager(), $this->inventory->getViewers());
|
||||||
|
foreach($viewers as $v){
|
||||||
|
if($v === null){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if($prevBrewTime !== $this->brewTime){
|
||||||
|
$v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_BREWING_STAND_BREW_TIME, $this->brewTime);
|
||||||
|
}
|
||||||
|
if($prevRemainingFuelTime !== $this->remainingFuelTime){
|
||||||
|
$v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_BREWING_STAND_FUEL_AMOUNT, $this->remainingFuelTime);
|
||||||
|
}
|
||||||
|
if($prevMaxFuelTime !== $this->maxFuelTime){
|
||||||
|
$v->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_BREWING_STAND_FUEL_TOTAL, $this->maxFuelTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->timings->stopTiming();
|
||||||
|
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace pocketmine\block\utils;
|
namespace pocketmine\block\utils;
|
||||||
|
|
||||||
|
use pocketmine\block\inventory\BrewingStandInventory;
|
||||||
use pocketmine\utils\EnumTrait;
|
use pocketmine\utils\EnumTrait;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -36,13 +37,24 @@ use pocketmine\utils\EnumTrait;
|
|||||||
* @method static BrewingStandSlot SOUTHWEST()
|
* @method static BrewingStandSlot SOUTHWEST()
|
||||||
*/
|
*/
|
||||||
final class BrewingStandSlot{
|
final class BrewingStandSlot{
|
||||||
use EnumTrait;
|
use EnumTrait {
|
||||||
|
__construct as Enum___construct;
|
||||||
|
}
|
||||||
|
|
||||||
protected static function setup() : void{
|
protected static function setup() : void{
|
||||||
self::registerAll(
|
self::registerAll(
|
||||||
new self("east"),
|
new self("east", BrewingStandInventory::SLOT_BOTTLE_LEFT),
|
||||||
new self("northwest"),
|
new self("northwest", BrewingStandInventory::SLOT_BOTTLE_MIDDLE),
|
||||||
new self("southwest")
|
new self("southwest", BrewingStandInventory::SLOT_BOTTLE_RIGHT)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function __construct(string $enumName, private int $slotNumber){
|
||||||
|
$this->Enum___construct($enumName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the brewing stand inventory slot number associated with this visual slot.
|
||||||
|
*/
|
||||||
|
public function getSlotNumber() : int{ return $this->slotNumber; }
|
||||||
}
|
}
|
||||||
|
30
src/crafting/BrewingRecipe.php
Normal file
30
src/crafting/BrewingRecipe.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\crafting;
|
||||||
|
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
|
||||||
|
interface BrewingRecipe{
|
||||||
|
public function getResultFor(Item $input) : ?Item;
|
||||||
|
}
|
@ -45,6 +45,18 @@ class CraftingManager{
|
|||||||
*/
|
*/
|
||||||
protected $furnaceRecipeManagers;
|
protected $furnaceRecipeManagers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PotionTypeRecipe[][]
|
||||||
|
* @phpstan-var array<string, array<string, PotionTypeRecipe>>
|
||||||
|
*/
|
||||||
|
protected $potionTypeRecipes = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var PotionContainerChangeRecipe[][]
|
||||||
|
* @phpstan-var array<int, array<string, PotionContainerChangeRecipe>>
|
||||||
|
*/
|
||||||
|
protected $potionContainerChangeRecipes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ObjectSet
|
* @var ObjectSet
|
||||||
* @phpstan-var ObjectSet<\Closure() : void>
|
* @phpstan-var ObjectSet<\Closure() : void>
|
||||||
@ -140,6 +152,22 @@ class CraftingManager{
|
|||||||
return $this->furnaceRecipeManagers[$furnaceType->id()];
|
return $this->furnaceRecipeManagers[$furnaceType->id()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PotionTypeRecipe[][]
|
||||||
|
* @phpstan-return array<string, array<string, PotionTypeRecipe>>
|
||||||
|
*/
|
||||||
|
public function getPotionTypeRecipes() : array{
|
||||||
|
return $this->potionTypeRecipes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PotionContainerChangeRecipe[][]
|
||||||
|
* @phpstan-return array<int, array<string, PotionContainerChangeRecipe>>
|
||||||
|
*/
|
||||||
|
public function getPotionContainerChangeRecipes() : array{
|
||||||
|
return $this->potionContainerChangeRecipes;
|
||||||
|
}
|
||||||
|
|
||||||
public function registerShapedRecipe(ShapedRecipe $recipe) : void{
|
public function registerShapedRecipe(ShapedRecipe $recipe) : void{
|
||||||
$this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
|
$this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
|
||||||
|
|
||||||
@ -156,6 +184,25 @@ class CraftingManager{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
|
||||||
|
$input = $recipe->getInput();
|
||||||
|
$ingredient = $recipe->getIngredient();
|
||||||
|
$this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . ($ingredient->hasAnyDamageValue() ? "?" : $ingredient->getMeta())] = $recipe;
|
||||||
|
|
||||||
|
foreach($this->recipeRegisteredCallbacks as $callback){
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
|
||||||
|
$ingredient = $recipe->getIngredient();
|
||||||
|
$this->potionContainerChangeRecipes[$recipe->getInputItemId()][$ingredient->getId() . ":" . ($ingredient->hasAnyDamageValue() ? "?" : $ingredient->getMeta())] = $recipe;
|
||||||
|
|
||||||
|
foreach($this->recipeRegisteredCallbacks as $callback){
|
||||||
|
$callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Item[] $outputs
|
* @param Item[] $outputs
|
||||||
*/
|
*/
|
||||||
@ -206,4 +253,11 @@ class CraftingManager{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
|
||||||
|
return $this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . $ingredient->getMeta()] ??
|
||||||
|
$this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":?"] ??
|
||||||
|
$this->potionContainerChangeRecipes[$input->getId()][$ingredient->getId() . ":" . $ingredient->getMeta()] ??
|
||||||
|
$this->potionContainerChangeRecipes[$input->getId()][$ingredient->getId() . ":?"] ?? null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,20 @@ final class CraftingManagerFromDataHelper{
|
|||||||
Item::jsonDeserialize($recipe["input"]))
|
Item::jsonDeserialize($recipe["input"]))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
foreach($recipes["potion_type"] as $recipe){
|
||||||
|
$result->registerPotionTypeRecipe(new PotionTypeRecipe(
|
||||||
|
Item::jsonDeserialize($recipe["input"]),
|
||||||
|
Item::jsonDeserialize($recipe["ingredient"]),
|
||||||
|
Item::jsonDeserialize($recipe["output"])
|
||||||
|
));
|
||||||
|
}
|
||||||
|
foreach($recipes["potion_container_change"] as $recipe){
|
||||||
|
$result->registerPotionContainerChangeRecipe(new PotionContainerChangeRecipe(
|
||||||
|
$recipe["input_item_id"],
|
||||||
|
Item::jsonDeserialize($recipe["ingredient"]),
|
||||||
|
$recipe["output_item_id"]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
54
src/crafting/PotionContainerChangeRecipe.php
Normal file
54
src/crafting/PotionContainerChangeRecipe.php
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\crafting;
|
||||||
|
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\item\ItemFactory;
|
||||||
|
|
||||||
|
class PotionContainerChangeRecipe implements BrewingRecipe{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private int $inputItemId,
|
||||||
|
private Item $ingredient,
|
||||||
|
private int $outputItemId
|
||||||
|
){
|
||||||
|
$this->ingredient = clone $ingredient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInputItemId() : int{
|
||||||
|
return $this->inputItemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIngredient() : Item{
|
||||||
|
return clone $this->ingredient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOutputItemId() : int{
|
||||||
|
return $this->outputItemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResultFor(Item $input) : ?Item{
|
||||||
|
return $input->getId() === $this->getInputItemId() ? ItemFactory::getInstance()->get($this->getOutputItemId(), $input->getMeta()) : null;
|
||||||
|
}
|
||||||
|
}
|
55
src/crafting/PotionTypeRecipe.php
Normal file
55
src/crafting/PotionTypeRecipe.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\crafting;
|
||||||
|
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
|
||||||
|
class PotionTypeRecipe implements BrewingRecipe{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private Item $input,
|
||||||
|
private Item $ingredient,
|
||||||
|
private Item $output
|
||||||
|
){
|
||||||
|
$this->input = clone $input;
|
||||||
|
$this->ingredient = clone $ingredient;
|
||||||
|
$this->output = clone $output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInput() : Item{
|
||||||
|
return clone $this->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIngredient() : Item{
|
||||||
|
return clone $this->ingredient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOutput() : Item{
|
||||||
|
return clone $this->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResultFor(Item $input) : ?Item{
|
||||||
|
return $input->equals($this->input, true, false) ? $this->getOutput() : null;
|
||||||
|
}
|
||||||
|
}
|
71
src/event/block/BrewItemEvent.php
Normal file
71
src/event/block/BrewItemEvent.php
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\event\block;
|
||||||
|
|
||||||
|
use pocketmine\block\tile\BrewingStand;
|
||||||
|
use pocketmine\crafting\BrewingRecipe;
|
||||||
|
use pocketmine\event\Cancellable;
|
||||||
|
use pocketmine\event\CancellableTrait;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
|
||||||
|
class BrewItemEvent extends BlockEvent implements Cancellable{
|
||||||
|
use CancellableTrait;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private BrewingStand $brewingStand,
|
||||||
|
private int $slot,
|
||||||
|
private Item $input,
|
||||||
|
private Item $result,
|
||||||
|
private BrewingRecipe $recipe
|
||||||
|
){
|
||||||
|
parent::__construct($brewingStand->getBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBrewingStand() : BrewingStand{
|
||||||
|
return $this->brewingStand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns which slot of the brewing stand's inventory the potion is in.
|
||||||
|
*/
|
||||||
|
public function getSlot() : int{
|
||||||
|
return $this->slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInput() : Item{
|
||||||
|
return clone $this->input;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResult() : Item{
|
||||||
|
return clone $this->result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setResult(Item $result) : void{
|
||||||
|
$this->result = clone $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRecipe() : BrewingRecipe{
|
||||||
|
return $this->recipe;
|
||||||
|
}
|
||||||
|
}
|
64
src/event/block/BrewingFuelUseEvent.php
Normal file
64
src/event/block/BrewingFuelUseEvent.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\event\block;
|
||||||
|
|
||||||
|
use pocketmine\block\tile\BrewingStand;
|
||||||
|
use pocketmine\event\Cancellable;
|
||||||
|
use pocketmine\event\CancellableTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a brewing stand consumes a new fuel item.
|
||||||
|
*/
|
||||||
|
class BrewingFuelUseEvent extends BlockEvent implements Cancellable{
|
||||||
|
use CancellableTrait;
|
||||||
|
|
||||||
|
private int $fuelTime = 20;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private BrewingStand $brewingStand
|
||||||
|
){
|
||||||
|
parent::__construct($brewingStand->getBlock());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBrewingStand() : BrewingStand{
|
||||||
|
return $this->brewingStand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns how many times the fuel can be used for potion brewing before it runs out.
|
||||||
|
*/
|
||||||
|
public function getFuelTime() : int{
|
||||||
|
return $this->fuelTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets how many times the fuel can be used for potion brewing before it runs out.
|
||||||
|
*/
|
||||||
|
public function setFuelTime(int $fuelTime) : void{
|
||||||
|
if($fuelTime <= 0){
|
||||||
|
throw new \InvalidArgumentException("Fuel time must be positive");
|
||||||
|
}
|
||||||
|
$this->fuelTime = $fuelTime;
|
||||||
|
}
|
||||||
|
}
|
37
src/network/mcpe/cache/CraftingDataCache.php
vendored
37
src/network/mcpe/cache/CraftingDataCache.php
vendored
@ -26,12 +26,15 @@ namespace pocketmine\network\mcpe\cache;
|
|||||||
use pocketmine\crafting\CraftingManager;
|
use pocketmine\crafting\CraftingManager;
|
||||||
use pocketmine\crafting\FurnaceType;
|
use pocketmine\crafting\FurnaceType;
|
||||||
use pocketmine\item\Item;
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\network\mcpe\convert\ItemTranslator;
|
||||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||||
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
||||||
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
|
use pocketmine\network\mcpe\protocol\types\inventory\ItemStack;
|
||||||
use pocketmine\network\mcpe\protocol\types\recipe\CraftingRecipeBlockName;
|
use pocketmine\network\mcpe\protocol\types\recipe\CraftingRecipeBlockName;
|
||||||
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe as ProtocolFurnaceRecipe;
|
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe as ProtocolFurnaceRecipe;
|
||||||
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipeBlockName;
|
use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipeBlockName;
|
||||||
|
use pocketmine\network\mcpe\protocol\types\recipe\PotionContainerChangeRecipe as ProtocolPotionContainerChangeRecipe;
|
||||||
|
use pocketmine\network\mcpe\protocol\types\recipe\PotionTypeRecipe as ProtocolPotionTypeRecipe;
|
||||||
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient;
|
use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient;
|
||||||
use pocketmine\network\mcpe\protocol\types\recipe\ShapedRecipe as ProtocolShapedRecipe;
|
use pocketmine\network\mcpe\protocol\types\recipe\ShapedRecipe as ProtocolShapedRecipe;
|
||||||
use pocketmine\network\mcpe\protocol\types\recipe\ShapelessRecipe as ProtocolShapelessRecipe;
|
use pocketmine\network\mcpe\protocol\types\recipe\ShapelessRecipe as ProtocolShapelessRecipe;
|
||||||
@ -137,7 +140,39 @@ final class CraftingDataCache{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$potionTypeRecipes = [];
|
||||||
|
foreach($manager->getPotionTypeRecipes() as $recipes){
|
||||||
|
foreach($recipes as $recipe){
|
||||||
|
$input = $converter->coreItemStackToNet($recipe->getInput());
|
||||||
|
$ingredient = $converter->coreItemStackToNet($recipe->getIngredient());
|
||||||
|
$output = $converter->coreItemStackToNet($recipe->getOutput());
|
||||||
|
$potionTypeRecipes[] = new ProtocolPotionTypeRecipe(
|
||||||
|
$input->getId(),
|
||||||
|
$input->getMeta(),
|
||||||
|
$ingredient->getId(),
|
||||||
|
$ingredient->getMeta(),
|
||||||
|
$output->getId(),
|
||||||
|
$output->getMeta()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$potionContainerChangeRecipes = [];
|
||||||
|
$itemTranslator = ItemTranslator::getInstance();
|
||||||
|
foreach($manager->getPotionContainerChangeRecipes() as $recipes){
|
||||||
|
foreach($recipes as $recipe){
|
||||||
|
$input = $itemTranslator->toNetworkId($recipe->getInputItemId(), 0);
|
||||||
|
$ingredient = $itemTranslator->toNetworkId($recipe->getIngredient()->getId(), 0);
|
||||||
|
$output = $itemTranslator->toNetworkId($recipe->getOutputItemId(), 0);
|
||||||
|
$potionContainerChangeRecipes[] = new ProtocolPotionContainerChangeRecipe(
|
||||||
|
$input[0],
|
||||||
|
$ingredient[0],
|
||||||
|
$output[0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Timings::$craftingDataCacheRebuild->stopTiming();
|
Timings::$craftingDataCacheRebuild->stopTiming();
|
||||||
return CraftingDataPacket::create($recipesWithTypeIds, [], [], [], true);
|
return CraftingDataPacket::create($recipesWithTypeIds, $potionTypeRecipes, $potionContainerChangeRecipes, [], true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
35
src/world/sound/PotionFinishBrewingSound.php
Normal file
35
src/world/sound/PotionFinishBrewingSound.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\world\sound;
|
||||||
|
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||||
|
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||||
|
|
||||||
|
final class PotionFinishBrewingSound implements Sound{
|
||||||
|
|
||||||
|
public function encode(Vector3 $pos) : array{
|
||||||
|
return [LevelSoundEventPacket::nonActorSound(LevelSoundEvent::POTION_BREWED, $pos, false)];
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user