mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-06 01:46:04 +00:00
Removed pocketmine subdirectory, map PSR-4 style
This commit is contained in:
138
src/crafting/CraftingGrid.php
Normal file
138
src/crafting/CraftingGrid.php
Normal file
@ -0,0 +1,138 @@
|
||||
<?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\inventory\BaseInventory;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\player\Player;
|
||||
use function max;
|
||||
use function min;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
class CraftingGrid extends BaseInventory{
|
||||
public const SIZE_SMALL = 2;
|
||||
public const SIZE_BIG = 3;
|
||||
|
||||
/** @var Player */
|
||||
protected $holder;
|
||||
/** @var int */
|
||||
private $gridWidth;
|
||||
|
||||
/** @var int|null */
|
||||
private $startX;
|
||||
/** @var int|null */
|
||||
private $xLen;
|
||||
/** @var int|null */
|
||||
private $startY;
|
||||
/** @var int|null */
|
||||
private $yLen;
|
||||
|
||||
public function __construct(Player $holder, int $gridWidth){
|
||||
$this->holder = $holder;
|
||||
$this->gridWidth = $gridWidth;
|
||||
parent::__construct($this->getGridWidth() ** 2);
|
||||
}
|
||||
|
||||
public function getGridWidth() : int{
|
||||
return $this->gridWidth;
|
||||
}
|
||||
|
||||
public function setItem(int $index, Item $item, bool $send = true) : void{
|
||||
parent::setItem($index, $item, $send);
|
||||
$this->seekRecipeBounds();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player
|
||||
*/
|
||||
public function getHolder(){
|
||||
return $this->holder;
|
||||
}
|
||||
|
||||
private function seekRecipeBounds() : void{
|
||||
$minX = PHP_INT_MAX;
|
||||
$maxX = 0;
|
||||
|
||||
$minY = PHP_INT_MAX;
|
||||
$maxY = 0;
|
||||
|
||||
$empty = true;
|
||||
|
||||
for($y = 0; $y < $this->gridWidth; ++$y){
|
||||
for($x = 0; $x < $this->gridWidth; ++$x){
|
||||
if(!$this->isSlotEmpty($y * $this->gridWidth + $x)){
|
||||
$minX = min($minX, $x);
|
||||
$maxX = max($maxX, $x);
|
||||
|
||||
$minY = min($minY, $y);
|
||||
$maxY = max($maxY, $y);
|
||||
|
||||
$empty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!$empty){
|
||||
$this->startX = $minX;
|
||||
$this->xLen = $maxX - $minX + 1;
|
||||
$this->startY = $minY;
|
||||
$this->yLen = $maxY - $minY + 1;
|
||||
}else{
|
||||
$this->startX = $this->xLen = $this->startY = $this->yLen = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item at offset x,y, offset by where the starts of the recipe rectangle are.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
*
|
||||
* @return Item
|
||||
*/
|
||||
public function getIngredient(int $x, int $y) : Item{
|
||||
if($this->startX !== null and $this->startY !== null){
|
||||
return $this->getItem(($y + $this->startY) * $this->gridWidth + ($x + $this->startX));
|
||||
}
|
||||
|
||||
throw new \InvalidStateException("No ingredients found in grid");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the recipe we're trying to craft, based on items currently in the grid.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getRecipeWidth() : int{
|
||||
return $this->xLen ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the height of the recipe we're trying to craft, based on items currently in the grid.
|
||||
* @return int
|
||||
*/
|
||||
public function getRecipeHeight() : int{
|
||||
return $this->yLen ?? 0;
|
||||
}
|
||||
}
|
297
src/crafting/CraftingManager.php
Normal file
297
src/crafting/CraftingManager.php
Normal file
@ -0,0 +1,297 @@
|
||||
<?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\network\mcpe\compression\CompressBatchPromise;
|
||||
use pocketmine\network\mcpe\compression\Zlib;
|
||||
use pocketmine\network\mcpe\PacketBatch;
|
||||
use pocketmine\network\mcpe\protocol\CraftingDataPacket;
|
||||
use pocketmine\timings\Timings;
|
||||
use function array_map;
|
||||
use function file_get_contents;
|
||||
use function json_decode;
|
||||
use function json_encode;
|
||||
use function usort;
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
class CraftingManager{
|
||||
/** @var ShapedRecipe[][] */
|
||||
protected $shapedRecipes = [];
|
||||
/** @var ShapelessRecipe[][] */
|
||||
protected $shapelessRecipes = [];
|
||||
/** @var FurnaceRecipe[] */
|
||||
protected $furnaceRecipes = [];
|
||||
|
||||
/** @var CompressBatchPromise */
|
||||
private $craftingDataCache;
|
||||
|
||||
public function __construct(){
|
||||
$this->init();
|
||||
}
|
||||
|
||||
public function init() : void{
|
||||
$recipes = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla" . DIRECTORY_SEPARATOR . "recipes.json"), true);
|
||||
|
||||
$itemDeserializerFunc = \Closure::fromCallable([Item::class, 'jsonDeserialize']);
|
||||
foreach($recipes as $recipe){
|
||||
switch($recipe["type"]){
|
||||
case "shapeless":
|
||||
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
|
||||
break;
|
||||
}
|
||||
$this->registerShapelessRecipe(new ShapelessRecipe(
|
||||
array_map($itemDeserializerFunc, $recipe["input"]),
|
||||
array_map($itemDeserializerFunc, $recipe["output"])
|
||||
));
|
||||
break;
|
||||
case "shaped":
|
||||
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
|
||||
break;
|
||||
}
|
||||
$this->registerShapedRecipe(new ShapedRecipe(
|
||||
$recipe["shape"],
|
||||
array_map($itemDeserializerFunc, $recipe["input"]),
|
||||
array_map($itemDeserializerFunc, $recipe["output"])
|
||||
));
|
||||
break;
|
||||
case "smelting":
|
||||
if($recipe["block"] !== "furnace"){ //TODO: filter others out for now to avoid breaking economics
|
||||
break;
|
||||
}
|
||||
$this->registerFurnaceRecipe(new FurnaceRecipe(
|
||||
Item::jsonDeserialize($recipe["output"]),
|
||||
Item::jsonDeserialize($recipe["input"]))
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->buildCraftingDataCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuilds the cached CraftingDataPacket.
|
||||
*/
|
||||
public function buildCraftingDataCache() : void{
|
||||
Timings::$craftingDataCacheRebuildTimer->startTiming();
|
||||
$pk = new CraftingDataPacket();
|
||||
$pk->cleanRecipes = true;
|
||||
|
||||
foreach($this->shapelessRecipes as $list){
|
||||
foreach($list as $recipe){
|
||||
$pk->addShapelessRecipe($recipe);
|
||||
}
|
||||
}
|
||||
foreach($this->shapedRecipes as $list){
|
||||
foreach($list as $recipe){
|
||||
$pk->addShapedRecipe($recipe);
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->furnaceRecipes as $recipe){
|
||||
$pk->addFurnaceRecipe($recipe);
|
||||
}
|
||||
|
||||
$this->craftingDataCache = new CompressBatchPromise();
|
||||
$this->craftingDataCache->resolve(Zlib::compress(PacketBatch::fromPackets($pk)->getBuffer()));
|
||||
|
||||
Timings::$craftingDataCacheRebuildTimer->stopTiming();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a pre-compressed CraftingDataPacket for sending to players. Rebuilds the cache if it is not found.
|
||||
*
|
||||
* @return CompressBatchPromise
|
||||
*/
|
||||
public function getCraftingDataPacket() : CompressBatchPromise{
|
||||
if($this->craftingDataCache === null){
|
||||
$this->buildCraftingDataCache();
|
||||
}
|
||||
|
||||
return $this->craftingDataCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function used to arrange Shapeless Recipe ingredient lists into a consistent order.
|
||||
*
|
||||
* @param Item $i1
|
||||
* @param Item $i2
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public static function sort(Item $i1, Item $i2) : int{
|
||||
//Use spaceship operator to compare each property, then try the next one if they are equivalent.
|
||||
($retval = $i1->getId() <=> $i2->getId()) === 0 && ($retval = $i1->getMeta() <=> $i2->getMeta()) === 0 && ($retval = $i1->getCount() <=> $i2->getCount());
|
||||
|
||||
return $retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] $items
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
private static function pack(array $items) : array{
|
||||
/** @var Item[] $result */
|
||||
$result = [];
|
||||
|
||||
foreach($items as $i => $item){
|
||||
foreach($result as $otherItem){
|
||||
if($item->equals($otherItem)){
|
||||
$otherItem->setCount($otherItem->getCount() + $item->getCount());
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
//No matching item found
|
||||
$result[] = clone $item;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function hashOutputs(array $outputs) : string{
|
||||
$outputs = self::pack($outputs);
|
||||
usort($outputs, [self::class, "sort"]);
|
||||
foreach($outputs as $o){
|
||||
//this reduces accuracy of hash, but it's necessary to deal with recipe book shift-clicking stupidity
|
||||
$o->setCount(1);
|
||||
}
|
||||
|
||||
return json_encode($outputs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShapelessRecipe[][]
|
||||
*/
|
||||
public function getShapelessRecipes() : array{
|
||||
return $this->shapelessRecipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ShapedRecipe[][]
|
||||
*/
|
||||
public function getShapedRecipes() : array{
|
||||
return $this->shapedRecipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FurnaceRecipe[]
|
||||
*/
|
||||
public function getFurnaceRecipes() : array{
|
||||
return $this->furnaceRecipes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShapedRecipe $recipe
|
||||
*/
|
||||
public function registerShapedRecipe(ShapedRecipe $recipe) : void{
|
||||
$this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
|
||||
|
||||
$this->craftingDataCache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ShapelessRecipe $recipe
|
||||
*/
|
||||
public function registerShapelessRecipe(ShapelessRecipe $recipe) : void{
|
||||
$this->shapelessRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
|
||||
|
||||
$this->craftingDataCache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FurnaceRecipe $recipe
|
||||
*/
|
||||
public function registerFurnaceRecipe(FurnaceRecipe $recipe) : void{
|
||||
$input = $recipe->getInput();
|
||||
$this->furnaceRecipes[$input->getId() . ":" . ($input->hasAnyDamageValue() ? "?" : $input->getMeta())] = $recipe;
|
||||
$this->craftingDataCache = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingGrid $grid
|
||||
* @param Item[] $outputs
|
||||
*
|
||||
* @return CraftingRecipe|null
|
||||
*/
|
||||
public function matchRecipe(CraftingGrid $grid, array $outputs) : ?CraftingRecipe{
|
||||
//TODO: try to match special recipes before anything else (first they need to be implemented!)
|
||||
|
||||
$outputHash = self::hashOutputs($outputs);
|
||||
|
||||
if(isset($this->shapedRecipes[$outputHash])){
|
||||
foreach($this->shapedRecipes[$outputHash] as $recipe){
|
||||
if($recipe->matchesCraftingGrid($grid)){
|
||||
return $recipe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($this->shapelessRecipes[$outputHash])){
|
||||
foreach($this->shapelessRecipes[$outputHash] as $recipe){
|
||||
if($recipe->matchesCraftingGrid($grid)){
|
||||
return $recipe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item[] $outputs
|
||||
*
|
||||
* @return CraftingRecipe[]|\Generator
|
||||
*/
|
||||
public function matchRecipeByOutputs(array $outputs) : \Generator{
|
||||
//TODO: try to match special recipes before anything else (first they need to be implemented!)
|
||||
|
||||
$outputHash = self::hashOutputs($outputs);
|
||||
|
||||
if(isset($this->shapedRecipes[$outputHash])){
|
||||
foreach($this->shapedRecipes[$outputHash] as $recipe){
|
||||
yield $recipe;
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($this->shapelessRecipes[$outputHash])){
|
||||
foreach($this->shapelessRecipes[$outputHash] as $recipe){
|
||||
yield $recipe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item $input
|
||||
*
|
||||
* @return FurnaceRecipe|null
|
||||
*/
|
||||
public function matchFurnaceRecipe(Item $input) : ?FurnaceRecipe{
|
||||
return $this->furnaceRecipes[$input->getId() . ":" . $input->getMeta()] ?? $this->furnaceRecipes[$input->getId() . ":?"] ?? null;
|
||||
}
|
||||
}
|
53
src/crafting/CraftingRecipe.php
Normal file
53
src/crafting/CraftingRecipe.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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 CraftingRecipe{
|
||||
/**
|
||||
* Returns a list of items needed to craft this recipe. This MUST NOT include Air items or items with a zero count.
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getIngredientList() : array;
|
||||
|
||||
/**
|
||||
* Returns a list of results this recipe will produce when the inputs in the given crafting grid are consumed.
|
||||
*
|
||||
* @param CraftingGrid $grid
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getResultsFor(CraftingGrid $grid) : array;
|
||||
|
||||
/**
|
||||
* Returns whether the given crafting grid meets the requirements to craft this recipe.
|
||||
*
|
||||
* @param CraftingGrid $grid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matchesCraftingGrid(CraftingGrid $grid) : bool;
|
||||
}
|
58
src/crafting/FurnaceRecipe.php
Normal file
58
src/crafting/FurnaceRecipe.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?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 FurnaceRecipe{
|
||||
|
||||
/** @var Item */
|
||||
private $output;
|
||||
|
||||
/** @var Item */
|
||||
private $ingredient;
|
||||
|
||||
/**
|
||||
* @param Item $result
|
||||
* @param Item $ingredient
|
||||
*/
|
||||
public function __construct(Item $result, Item $ingredient){
|
||||
$this->output = clone $result;
|
||||
$this->ingredient = clone $ingredient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getInput() : Item{
|
||||
return clone $this->ingredient;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getResult() : Item{
|
||||
return clone $this->output;
|
||||
}
|
||||
}
|
47
src/crafting/MultiRecipe.php
Normal file
47
src/crafting/MultiRecipe.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?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\utils\UUID;
|
||||
|
||||
class MultiRecipe{
|
||||
public const TYPE_REPAIR_ITEM = "00000000-0000-0000-0000-000000000001";
|
||||
public const TYPE_MAP_EXTENDING = "D392B075-4BA1-40AE-8789-AF868D56F6CE";
|
||||
public const TYPE_MAP_EXTENDING_CARTOGRAPHY = "8B36268C-1829-483C-A0F1-993B7156A8F2";
|
||||
public const TYPE_MAP_CLONING = "85939755-BA10-4D9D-A4CC-EFB7A8E943C4";
|
||||
public const TYPE_MAP_CLONING_CARTOGRAPHY = "442D85ED-8272-4543-A6F1-418F90DED05D";
|
||||
public const TYPE_MAP_UPGRADING = "AECD2294-4B94-434B-8667-4499BB2C9327";
|
||||
public const TYPE_MAP_UPGRADING_CARTOGRAPHY = "98C84B38-1085-46BD-B1CE-DD38C159E6CC";
|
||||
public const TYPE_BOOK_CLONING = "D1CA6B84-338E-4F2F-9C6B-76CC8B4BD98D";
|
||||
public const TYPE_BANNER_DUPLICATE = "B5C5D105-75A2-4076-AF2B-923EA2BF4BF0";
|
||||
public const TYPE_BANNER_ADD_PATTERN = "D81AAEAF-E172-4440-9225-868DF030D27B";
|
||||
public const TYPE_FIREWORKS = "00000000-0000-0000-0000-000000000002";
|
||||
public const TYPE_MAP_LOCKING_CARTOGRAPHY = "602234E4-CAC1-4353-8BB7-B1EBFF70024B";
|
||||
|
||||
private $uuid;
|
||||
|
||||
public function __construct(UUID $uuid){
|
||||
$this->uuid = $uuid;
|
||||
}
|
||||
}
|
210
src/crafting/ShapedRecipe.php
Normal file
210
src/crafting/ShapedRecipe.php
Normal file
@ -0,0 +1,210 @@
|
||||
<?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;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function implode;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
|
||||
class ShapedRecipe implements CraftingRecipe{
|
||||
/** @var string[] */
|
||||
private $shape = [];
|
||||
/** @var Item[] char => Item map */
|
||||
private $ingredientList = [];
|
||||
/** @var Item[] */
|
||||
private $results = [];
|
||||
|
||||
/** @var int */
|
||||
private $height;
|
||||
/** @var int */
|
||||
private $width;
|
||||
|
||||
/**
|
||||
* Constructs a ShapedRecipe instance.
|
||||
*
|
||||
* @param string[] $shape <br>
|
||||
* Array of 1, 2, or 3 strings representing the rows of the recipe.
|
||||
* This accepts an array of 1, 2 or 3 strings. Each string should be of the same length and must be at most 3
|
||||
* characters long. Each character represents a unique type of ingredient. Spaces are interpreted as air.
|
||||
* @param Item[] $ingredients <br>
|
||||
* Char => Item map of items to be set into the shape.
|
||||
* This accepts an array of Items, indexed by character. Every unique character (except space) in the shape
|
||||
* array MUST have a corresponding item in this list. Space character is automatically treated as air.
|
||||
* @param Item[] $results List of items that this recipe produces when crafted.
|
||||
*
|
||||
* Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns.
|
||||
*/
|
||||
public function __construct(array $shape, array $ingredients, array $results){
|
||||
$this->height = count($shape);
|
||||
if($this->height > 3 or $this->height <= 0){
|
||||
throw new \InvalidArgumentException("Shaped recipes may only have 1, 2 or 3 rows, not $this->height");
|
||||
}
|
||||
|
||||
$shape = array_values($shape);
|
||||
|
||||
$this->width = strlen($shape[0]);
|
||||
if($this->width > 3 or $this->width <= 0){
|
||||
throw new \InvalidArgumentException("Shaped recipes may only have 1, 2 or 3 columns, not $this->width");
|
||||
}
|
||||
|
||||
foreach($shape as $y => $row){
|
||||
if(strlen($row) !== $this->width){
|
||||
throw new \InvalidArgumentException("Shaped recipe rows must all have the same length (expected $this->width, got " . strlen($row) . ")");
|
||||
}
|
||||
|
||||
for($x = 0; $x < $this->width; ++$x){
|
||||
if($row[$x] !== ' ' and !isset($ingredients[$row[$x]])){
|
||||
throw new \InvalidArgumentException("No item specified for symbol '" . $row[$x] . "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->shape = $shape;
|
||||
|
||||
foreach($ingredients as $char => $i){
|
||||
if(strpos(implode($this->shape), $char) === false){
|
||||
throw new \InvalidArgumentException("Symbol '$char' does not appear in the recipe shape");
|
||||
}
|
||||
|
||||
$this->ingredientList[$char] = clone $i;
|
||||
}
|
||||
|
||||
$this->results = Utils::cloneObjectArray($results);
|
||||
}
|
||||
|
||||
public function getWidth() : int{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
public function getHeight() : int{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getResults() : array{
|
||||
return Utils::cloneObjectArray($this->results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingGrid $grid
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getResultsFor(CraftingGrid $grid) : array{
|
||||
return $this->getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[][]
|
||||
*/
|
||||
public function getIngredientMap() : array{
|
||||
$ingredients = [];
|
||||
|
||||
for($y = 0; $y < $this->height; ++$y){
|
||||
for($x = 0; $x < $this->width; ++$x){
|
||||
$ingredients[$y][$x] = $this->getIngredient($x, $y);
|
||||
}
|
||||
}
|
||||
|
||||
return $ingredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
$ingredients = [];
|
||||
|
||||
for($y = 0; $y < $this->height; ++$y){
|
||||
for($x = 0; $x < $this->width; ++$x){
|
||||
$ingredient = $this->getIngredient($x, $y);
|
||||
if(!$ingredient->isNull()){
|
||||
$ingredients[] = $ingredient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $ingredients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
*
|
||||
* @return Item
|
||||
*/
|
||||
public function getIngredient(int $x, int $y) : Item{
|
||||
$exists = $this->ingredientList[$this->shape[$y]{$x}] ?? null;
|
||||
return $exists !== null ? clone $exists : ItemFactory::air();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of strings containing characters representing the recipe's shape.
|
||||
* @return string[]
|
||||
*/
|
||||
public function getShape() : array{
|
||||
return $this->shape;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingGrid $grid
|
||||
* @param bool $reverse
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function matchInputMap(CraftingGrid $grid, bool $reverse) : bool{
|
||||
for($y = 0; $y < $this->height; ++$y){
|
||||
for($x = 0; $x < $this->width; ++$x){
|
||||
|
||||
$given = $grid->getIngredient($reverse ? $this->width - $x - 1 : $x, $y);
|
||||
$required = $this->getIngredient($x, $y);
|
||||
if(!$required->equals($given, !$required->hasAnyDamageValue(), $required->hasNamedTag()) or $required->getCount() > $given->getCount()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingGrid $grid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matchesCraftingGrid(CraftingGrid $grid) : bool{
|
||||
if($this->width !== $grid->getRecipeWidth() or $this->height !== $grid->getRecipeHeight()){
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->matchInputMap($grid, false) or $this->matchInputMap($grid, true);
|
||||
}
|
||||
}
|
104
src/crafting/ShapelessRecipe.php
Normal file
104
src/crafting/ShapelessRecipe.php
Normal file
@ -0,0 +1,104 @@
|
||||
<?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\utils\Utils;
|
||||
use function count;
|
||||
|
||||
class ShapelessRecipe implements CraftingRecipe{
|
||||
/** @var Item[] */
|
||||
private $ingredients = [];
|
||||
/** @var Item[] */
|
||||
private $results;
|
||||
|
||||
/**
|
||||
* @param Item[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
|
||||
* @param Item[] $results List of result items created by this recipe.
|
||||
*/
|
||||
public function __construct(array $ingredients, array $results){
|
||||
foreach($ingredients as $item){
|
||||
//Ensure they get split up properly
|
||||
if(count($this->ingredients) + $item->getCount() > 9){
|
||||
throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients");
|
||||
}
|
||||
|
||||
while($item->getCount() > 0){
|
||||
$this->ingredients[] = $item->pop();
|
||||
}
|
||||
}
|
||||
|
||||
$this->results = Utils::cloneObjectArray($results);
|
||||
}
|
||||
|
||||
public function getResults() : array{
|
||||
return Utils::cloneObjectArray($this->results);
|
||||
}
|
||||
|
||||
public function getResultsFor(CraftingGrid $grid) : array{
|
||||
return $this->getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
return Utils::cloneObjectArray($this->ingredients);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getIngredientCount() : int{
|
||||
$count = 0;
|
||||
foreach($this->ingredients as $ingredient){
|
||||
$count += $ingredient->getCount();
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingGrid $grid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function matchesCraftingGrid(CraftingGrid $grid) : bool{
|
||||
//don't pack the ingredients - shapeless recipes require that each ingredient be in a separate slot
|
||||
$input = $grid->getContents();
|
||||
|
||||
foreach($this->ingredients as $needItem){
|
||||
foreach($input as $j => $haveItem){
|
||||
if($haveItem->equals($needItem, !$needItem->hasAnyDamageValue(), $needItem->hasNamedTag()) and $haveItem->getCount() >= $needItem->getCount()){
|
||||
unset($input[$j]);
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
return false; //failed to match the needed item to a given item
|
||||
}
|
||||
|
||||
return empty($input); //crafting grid should be empty apart from the given ingredient stacks
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user