Added CraftItemEvent, Crafting system now uses Transactions

This commit is contained in:
Shoghi Cervantes
2014-05-27 17:49:22 +02:00
parent 3fc1be1262
commit 6746987ce5
14 changed files with 348 additions and 105 deletions

View File

@ -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);
}
}

View File

@ -41,6 +41,8 @@ abstract class ContainerInventory extends BaseInventory{
}
$who->dataPacket($pk);
$this->sendContents($who);
}
public function onClose(Player $who){

View File

@ -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
*/

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}