Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
025440f
Working brewing
VixikHD Aug 29, 2021
23359e7
Send spawn data
VixikHD Aug 29, 2021
13a0197
Some fixes
VixikHD Aug 29, 2021
576810b
Better documentation
VixikHD Aug 29, 2021
cf08647
Seconds -> ticks
VixikHD Aug 29, 2021
086b86e
Some updates
VixikHD Sep 6, 2021
a9054df
Use constants for brewing inventory slots
VixikHD Sep 6, 2021
8d0921f
Merge branch 'master' into feature/brewing
VixikHD Sep 6, 2021
4cfcd9a
Fixes
VixikHD Sep 10, 2021
9874f8d
Merge branch 'master' into feature/brewing
dktapps Sep 30, 2021
a42ec88
...
dktapps Sep 30, 2021
f304840
Fix formatting issues
dktapps Sep 30, 2021
90ab0bd
CraftingDataCache: use ItemTranslator directly
dktapps Sep 30, 2021
67719ed
Remove unnecessary nullsafe operator
dktapps Sep 30, 2021
ef48115
Consistently use results terminology
dktapps Sep 30, 2021
0135acb
BrewItemEvent: clone items
dktapps Sep 30, 2021
83ba253
Remove unnecessary check
dktapps Sep 30, 2021
c3e1d3b
Moved brewing events to a more appropriate namespace
dktapps Sep 30, 2021
d02b081
Merge branch 'next-minor' into feature/brewing
dktapps Jan 22, 2022
8a8642a
fix
dktapps Jan 22, 2022
a9f7a2b
don't use deprecated operators
dktapps Jan 22, 2022
e46aa1b
documentation improvement
dktapps Jan 22, 2022
453364c
Enforce positive brewing time in events
dktapps Jan 22, 2022
3fd90b4
Fixed block slots not being updated on inventory content change
dktapps Jan 22, 2022
56e08d0
Added missing sound
dktapps Jan 22, 2022
c5c9b3f
fuck you PhpStorm
dktapps Jan 22, 2022
19d64ac
improved documentation
dktapps Jan 22, 2022
961e95c
rename const
dktapps Jan 22, 2022
9aec177
Enhance return types of CraftingManager methods
dktapps Jan 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion src/block/BrewingStand.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,24 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player
}

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);
}
}
}
}
168 changes: 158 additions & 10 deletions src/block/tile/BrewingStand.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,41 @@
namespace pocketmine\block\tile;

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\Inventory;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\math\Vector3;
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 function array_map;
use function count;

class BrewingStand extends Spawnable implements Container, Nameable{

use NameableTrait {
addAdditionalSpawnData as addNameSpawnData;
}
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_PE = "CookTime"; //TAG_Short
private const TAG_MAX_FUEL_TIME = "FuelTotal"; //TAG_Short
private const TAG_REMAINING_FUEL_TIME = "Fuel"; //TAG_Byte
private const TAG_REMAINING_FUEL_TIME_PE = "FuelAmount"; //TAG_Short

/** @var BrewingStandInventory */
private $inventory;
private BrewingStandInventory $inventory;

/** @var int */
private $brewTime = 0;
/** @var int */
private $maxFuelTime = 0;
/** @var int */
private $remainingFuelTime = 0;
private int $brewTime = 0;
private int $maxFuelTime = 0;
private int $remainingFuelTime = 0;

public function __construct(World $world, Vector3 $pos){
parent::__construct($world, $pos);
Expand Down Expand Up @@ -83,6 +92,14 @@ protected function writeSaveData(CompoundTag $nbt) : void{
$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{
return "Brewing Stand";
}
Expand All @@ -108,4 +125,135 @@ public function getInventory(){
public function getRealInventory(){
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;
}
}
20 changes: 16 additions & 4 deletions src/block/utils/BrewingStandSlot.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

namespace pocketmine\block\utils;

use pocketmine\block\inventory\BrewingStandInventory;
use pocketmine\utils\EnumTrait;

/**
Expand All @@ -36,13 +37,24 @@
* @method static BrewingStandSlot SOUTHWEST()
*/
final class BrewingStandSlot{
use EnumTrait;
use EnumTrait {
__construct as Enum___construct;
}

protected static function setup() : void{
self::registerAll(
new self("east"),
new self("northwest"),
new self("southwest")
new self("east", BrewingStandInventory::SLOT_BOTTLE_LEFT),
new self("northwest", BrewingStandInventory::SLOT_BOTTLE_MIDDLE),
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 changes: 30 additions & 0 deletions src/crafting/BrewingRecipe.php
Original file line number Diff line number Diff line change
@@ -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;
}
54 changes: 54 additions & 0 deletions src/crafting/CraftingManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ class CraftingManager{
*/
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
* @phpstan-var ObjectSet<\Closure() : void>
Expand Down Expand Up @@ -140,6 +152,22 @@ public function getFurnaceRecipeManager(FurnaceType $furnaceType) : FurnaceRecip
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{
$this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;

Expand All @@ -156,6 +184,25 @@ public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
}
}

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
*/
Expand Down Expand Up @@ -206,4 +253,11 @@ public function matchRecipeByOutputs(array $outputs) : \Generator{
}
}
}

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;
}
}
14 changes: 14 additions & 0 deletions src/crafting/CraftingManagerFromDataHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ public static function make(string $filePath) : CraftingManager{
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;
}
Expand Down
Loading