mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Added CraftItemEvent, Crafting system now uses Transactions
This commit is contained in:
@ -390,7 +390,11 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
foreach($target as $player){
|
||||
$pk->windowid = $player->getWindowId($this);
|
||||
if(($id = $player->getWindowId($this)) === -1){
|
||||
$this->close($player);
|
||||
continue;
|
||||
}
|
||||
$pk->windowid = $id;
|
||||
$player->dataPacket(clone $pk);
|
||||
}
|
||||
}
|
||||
@ -409,7 +413,11 @@ abstract class BaseInventory implements Inventory{
|
||||
$pk->item = clone $this->getItem($index);
|
||||
|
||||
foreach($target as $player){
|
||||
$pk->windowid = $player->getWindowId($this);
|
||||
if(($id = $player->getWindowId($this)) === -1){
|
||||
$this->close($player);
|
||||
continue;
|
||||
}
|
||||
$pk->windowid = $id;
|
||||
$player->dataPacket(clone $pk);
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ abstract class ContainerInventory extends BaseInventory{
|
||||
}
|
||||
|
||||
$who->dataPacket($pk);
|
||||
|
||||
$this->sendContents($who);
|
||||
}
|
||||
|
||||
public function onClose(Player $who){
|
||||
|
@ -21,19 +21,55 @@
|
||||
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\block\Planks;
|
||||
use pocketmine\block\Wood;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class CraftingManager{
|
||||
|
||||
/** @var Recipe[] */
|
||||
public $recipes = [];
|
||||
|
||||
/** @var Recipe[][] */
|
||||
protected $recipeLookup = [];
|
||||
|
||||
/** @var FurnaceRecipe[] */
|
||||
public $furnaceRecipes = [];
|
||||
|
||||
public function __construct(){
|
||||
//TODO: add crafting recipes
|
||||
$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::OAK, 4)))->addIngredient(Item::get(Item::WOOD, Wood::OAK, 1)));
|
||||
$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::SPRUCE, 4)))->addIngredient(Item::get(Item::WOOD, Wood::SPRUCE, 1)));
|
||||
$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::BIRCH, 4)))->addIngredient(Item::get(Item::WOOD, Wood::BIRCH, 1)));
|
||||
$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::JUNGLE, 4)))->addIngredient(Item::get(Item::WOOD, Wood::JUNGLE, 1)));
|
||||
//$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::ACACIA, 4)))->addIngredient(Item::get(Item::WOOD2, Wood2::ACACIA, 1)));
|
||||
//$this->registerRecipe((new ShapelessRecipe(Item::get(Item::WOODEN_PLANK, Planks::DARK_OAK, 4)))->addIngredient(Item::get(Item::WOOD2, Wood2::DARK_OAK, 1)));
|
||||
}
|
||||
|
||||
public function sort(){
|
||||
//TODO: recipe sort
|
||||
public function sort(Item $i1, Item $i2){
|
||||
if($i1->getID() > $i2->getID()){
|
||||
return 1;
|
||||
}elseif($i1->getID() < $i2->getID()){
|
||||
return -1;
|
||||
}elseif($i1->getDamage() > $i2->getDamage()){
|
||||
return 1;
|
||||
}elseif($i1->getDamage() < $i2->getDamage()){
|
||||
return -1;
|
||||
}elseif($i1->getCount() > $i2->getCount()){
|
||||
return 1;
|
||||
}elseif($i1->getCount() < $i2->getCount()){
|
||||
return -1;
|
||||
}else{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return Recipe[]
|
||||
*/
|
||||
public function getRecipes(){
|
||||
return $this->recipes;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -47,7 +83,15 @@ class CraftingManager{
|
||||
* @param ShapelessRecipe $recipe
|
||||
*/
|
||||
public function registerShapelessRecipe(ShapelessRecipe $recipe){
|
||||
|
||||
$result = $recipe->getResult();
|
||||
$this->recipes[spl_object_hash($recipe)] = $recipe;
|
||||
$hash = "";
|
||||
$ingredients = $recipe->getIngredientList();
|
||||
usort($ingredients, array($this, "sort"));
|
||||
foreach($ingredients as $item){
|
||||
$hash .= $item->getID().":".($item->getDamage() === null ? "?":$item->getDamage())."x".$item->getCount().",";
|
||||
}
|
||||
$this->recipeLookup[$result->getID().":".$result->getDamage()][$hash] = $recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -57,6 +101,38 @@ class CraftingManager{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CraftingTransactionGroup $ts
|
||||
*
|
||||
* @return Recipe
|
||||
*/
|
||||
public function matchTransaction(CraftingTransactionGroup $ts){
|
||||
$result = $ts->getResult();
|
||||
|
||||
if(!($result instanceof Item)){
|
||||
return false;
|
||||
}
|
||||
$k = $result->getID().":".$result->getDamage();
|
||||
|
||||
if(!isset($this->recipeLookup[$k])){
|
||||
return false;
|
||||
}
|
||||
$hash = "";
|
||||
$input = $ts->getRecipe();
|
||||
usort($input, array($this, "sort"));
|
||||
foreach($input as $item){
|
||||
$hash .= $item->getID().":".($item->getDamage() === null ? "?":$item->getDamage())."x".$item->getCount().",";
|
||||
}
|
||||
if(!isset($this->recipeLookup[$k][$hash])){
|
||||
return false;
|
||||
}
|
||||
$checkResult = $this->recipeLookup[$k][$hash]->getResult();
|
||||
if($checkResult->equals($result, true) and $checkResult->getCount() === $result->getCount()){
|
||||
return $this->recipeLookup[$k][$hash];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Recipe $recipe
|
||||
*/
|
||||
|
@ -21,16 +21,88 @@
|
||||
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
class CraftingTransactionGroup extends SimpleTransactionGroup{
|
||||
use pocketmine\event\inventory\CraftItemEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Server;
|
||||
|
||||
class CraftingTransactionGroup extends SimpleTransactionGroup{
|
||||
/** @var Item[] */
|
||||
protected $input = [];
|
||||
/** @var Item[] */
|
||||
protected $output = [];
|
||||
public function __construct(TransactionGroup $group){
|
||||
parent::__construct();
|
||||
$this->transactions = $group->getTransactions();
|
||||
$this->inventories = $group->getInventories();
|
||||
|
||||
$this->matchItems($this->output, $this->input);
|
||||
/*$input = "";
|
||||
$output = "";
|
||||
foreach($this->input as $item){
|
||||
$input .= $item->getID().":".$item->getDamage()."(".$item->getCount()."), ";
|
||||
}
|
||||
foreach($this->output as $item){
|
||||
$output .= $item->getID().":".$item->getDamage()."(".$item->getCount()."), ";
|
||||
}
|
||||
console("craft_tx#".spl_object_hash($this).": ".substr($input, 0, -2)." => ".substr($output, 0, -2));*/
|
||||
}
|
||||
|
||||
public function addTransaction(Transaction $transaction){
|
||||
parent::addTransaction($transaction);
|
||||
$this->input = [];
|
||||
$this->output = [];
|
||||
$this->matchItems($this->output, $this->input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Items that have been used
|
||||
*
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getRecipe(){
|
||||
return $this->input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getResult(){
|
||||
reset($this->output);
|
||||
return current($this->output);
|
||||
}
|
||||
|
||||
public function canExecute(){
|
||||
//TODO: search in crafting recipes
|
||||
return false;
|
||||
if(count($this->output) !== 1 or count($this->input) === 0){
|
||||
return false;
|
||||
}
|
||||
return $this->getMatchingRecipe() instanceof Recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Recipe
|
||||
*/
|
||||
public function getMatchingRecipe(){
|
||||
return Server::getInstance()->getCraftingManager()->matchTransaction($this);
|
||||
}
|
||||
|
||||
public function execute(){
|
||||
if($this->hasExecuted() or !$this->canExecute()){
|
||||
return false;
|
||||
}
|
||||
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new CraftItemEvent($this, $this->getMatchingRecipe()));
|
||||
if($ev->isCancelled()){
|
||||
foreach($this->inventories as $inventory){
|
||||
$inventory->sendContents($inventory->getViewers());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($this->transactions as $transaction){
|
||||
$transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem());
|
||||
}
|
||||
$this->hasExecuted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Server;
|
||||
|
||||
class FurnaceRecipe implements Recipe{
|
||||
/** @var Item */
|
||||
@ -60,55 +61,7 @@ class FurnaceRecipe implements Recipe{
|
||||
return clone $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item $item
|
||||
*
|
||||
* @returns ShapelessRecipe
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function addIngredient(Item $item){
|
||||
if(count($this->ingredients) >= 9){
|
||||
throw new \Exception("Shapeless recipes cannot have more than 9 ingredients");
|
||||
}
|
||||
|
||||
$it = clone $item;
|
||||
$it->setCount(1);
|
||||
|
||||
while($item->getCount() > 0){
|
||||
$this->ingredients[] = clone $it;
|
||||
$item->setCount($item->getCount() - 1);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item $item
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function removeIngredient(Item $item){
|
||||
foreach($this->ingredients as $index => $ingredient){
|
||||
if($item->getCount() <= 0){
|
||||
break;
|
||||
}
|
||||
if($ingredient->equals($item, $item->getDamage() === null ? false : true)){
|
||||
unset($this->ingredients[$index]);
|
||||
$item->setCount($item->getCount() - 1);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
*/
|
||||
public function getIngredientList(){
|
||||
$ingredients = [];
|
||||
foreach($this->ingredients as $ingredient){
|
||||
$ingredients[] = clone $ingredient;
|
||||
}
|
||||
return $ingredients;
|
||||
public function registerToCraftingManager(){
|
||||
Server::getInstance()->getCraftingManager()->registerFurnaceRecipe($this);
|
||||
}
|
||||
}
|
@ -335,7 +335,11 @@ class PlayerInventory extends BaseInventory{
|
||||
$pk->hotbar[] = $index <= -1 ? -1 : $index + 9;
|
||||
}
|
||||
}
|
||||
$pk->windowid = $player->getWindowId($this);
|
||||
if(($id = $player->getWindowId($this)) === -1){
|
||||
$this->close($player);
|
||||
continue;
|
||||
}
|
||||
$pk->windowid = $id;
|
||||
$player->dataPacket(clone $pk);
|
||||
}
|
||||
}
|
||||
@ -355,10 +359,15 @@ class PlayerInventory extends BaseInventory{
|
||||
|
||||
foreach($target as $player){
|
||||
if($player === $this->getHolder()){
|
||||
/** @var Player $player */
|
||||
//TODO: Check if Mojang has implemented this (for the player inventory) on Minecraft: PE 0.9.0
|
||||
$this->sendContents($player);
|
||||
}else{
|
||||
$pk->windowid = $player->getWindowId($this);
|
||||
if(($id = $player->getWindowId($this)) === -1){
|
||||
$this->close($player);
|
||||
continue;
|
||||
}
|
||||
$pk->windowid = $id;
|
||||
$player->dataPacket(clone $pk);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Server;
|
||||
|
||||
class ShapedRecipe implements Recipe{
|
||||
/** @var Item */
|
||||
@ -98,4 +99,8 @@ class ShapedRecipe implements Recipe{
|
||||
public function getShape(){
|
||||
return $this->rows;
|
||||
}
|
||||
|
||||
public function registerToCraftingManager(){
|
||||
Server::getInstance()->getCraftingManager()->registerShapedRecipe($this);
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\inventory;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\Server;
|
||||
|
||||
class ShapelessRecipe implements Recipe{
|
||||
/** @var Item */
|
||||
@ -89,4 +90,8 @@ class ShapelessRecipe implements Recipe{
|
||||
}
|
||||
return $ingredients;
|
||||
}
|
||||
|
||||
public function registerToCraftingManager(){
|
||||
Server::getInstance()->getCraftingManager()->registerShapelessRecipe($this);
|
||||
}
|
||||
}
|
@ -59,19 +59,23 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
$this->inventories[spl_object_hash($transaction->getInventory())] = $transaction->getInventory();
|
||||
}
|
||||
|
||||
public function canExecute(){
|
||||
/** @var Item[] $needItems */
|
||||
$needItems = [];
|
||||
/** @var Item[] $haveItems */
|
||||
$haveItems = [];
|
||||
/**
|
||||
* @param Item[] $needItems
|
||||
* @param Item[] $haveItems
|
||||
*/
|
||||
protected function matchItems(array &$needItems, array &$haveItems){
|
||||
foreach($this->transactions as $key => $ts){
|
||||
$needItems[] = $ts->getTargetItem();
|
||||
if($ts->getTargetItem()->getID() !== Item::AIR){
|
||||
$needItems[] = $ts->getTargetItem();
|
||||
}
|
||||
$checkSourceItem = $ts->getInventory()->getItem($ts->getSlot());
|
||||
$sourceItem = $ts->getSourceItem();
|
||||
if(!$checkSourceItem->equals($sourceItem, true) or $sourceItem->getCount() !== $checkSourceItem->getCount()){
|
||||
return false;
|
||||
}
|
||||
$haveItems[] = $sourceItem;
|
||||
if($sourceItem->getID() !== Item::AIR){
|
||||
$haveItems[] = $sourceItem;
|
||||
}
|
||||
}
|
||||
|
||||
foreach($needItems as $i => $needItem){
|
||||
@ -90,7 +94,12 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function canExecute(){
|
||||
$haveItems = [];
|
||||
$needItems = [];
|
||||
$this->matchItems($haveItems, $needItems);
|
||||
return count($haveItems) === 0 and count($needItems) === 0 and count($this->transactions) > 0;
|
||||
}
|
||||
|
||||
@ -111,6 +120,8 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
$transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem());
|
||||
}
|
||||
|
||||
$this->hasExecuted = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user